[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] [PATCH 8/8] Maintain a list of active PCI hostdevs and use it in pciResetDevice()



On Mon, Aug 17, 2009 at 03:10:21PM +0100, Mark McLoughlin wrote:
> As we start/shutdown guests, or hotplug/hot-unplug devices, we can add
> or delete devices as appropriate from a list of active devices.
> 
> Then, in pciReset(), we can use this to determine whether its safe to
> reset a device as a side effect of resetting another device.
> 
> * src/qemu_conf.h: add activePciHostdevs to qemud_driver
> 
> * src/qemu_driver.c: maintain the activePciHostdevs list, and pass it
>   to pciResetDevice()
> 
> * src/pci.[ch]: pass the activeDevs list to pciResetDevice() and use
>   it to determine whether a Secondary Bus Reset is safe
> ---
>  src/pci.c         |  102 ++++++++++++++++++++++++++------------------
>  src/pci.h         |    3 +-
>  src/qemu_conf.h   |    3 +
>  src/qemu_driver.c |  123 ++++++++++++++++++++++++++++++++++++++++++----------
>  src/xen_unified.c |    2 +-
>  5 files changed, 165 insertions(+), 68 deletions(-)
> 
> diff --git a/src/pci.c b/src/pci.c
> index a37eaf7..96e5d6d 100644
> --- a/src/pci.c
> +++ b/src/pci.c
> @@ -226,7 +226,7 @@ pciWrite32(pciDevice *dev, unsigned pos, uint32_t val)
>      pciWrite(dev, pos, &buf[0], sizeof(buf));
>  }
>  
> -typedef int (*pciIterPredicate)(pciDevice *, pciDevice *);
> +typedef int (*pciIterPredicate)(pciDevice *, pciDevice *, void *);
>  
>  /* Iterate over available PCI devices calling @predicate
>   * to compare each one to @dev.
> @@ -237,7 +237,8 @@ static int
>  pciIterDevices(virConnectPtr conn,
>                 pciIterPredicate predicate,
>                 pciDevice *dev,
> -               pciDevice **matched)
> +               pciDevice **matched,
> +               void *data)
>  {
>      DIR *dir;
>      struct dirent *entry;
> @@ -255,7 +256,7 @@ pciIterDevices(virConnectPtr conn,
>  
>      while ((entry = readdir(dir))) {
>          unsigned domain, bus, slot, function;
> -        pciDevice *try;
> +        pciDevice *check;
>  
>          /* Ignore '.' and '..' */
>          if (entry->d_name[0] == '.')
> @@ -267,18 +268,18 @@ pciIterDevices(virConnectPtr conn,
>              continue;
>          }
>  
> -        try = pciGetDevice(conn, domain, bus, slot, function);
> -        if (!try) {
> +        check = pciGetDevice(conn, domain, bus, slot, function);
> +        if (!check) {
>              ret = -1;
>              break;
>          }
>  
> -        if (predicate(try, dev)) {
> -            VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, try->name);
> -            *matched = try;
> +        if (predicate(dev, check, data)) {
> +            VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, check->name);
> +            *matched = check;
>              break;
>          }
> -        pciFreeDevice(conn, try);
> +        pciFreeDevice(conn, check);
>      }
>      closedir(dir);
>      return ret;
> @@ -380,63 +381,70 @@ pciDetectPowerManagementReset(pciDevice *dev)
>      return 0;
>  }
>  
> -/* Any devices other than the one supplied on the same domain/bus ? */
> +/* Any active devices other than the one supplied on the same domain/bus ? */
>  static int
> -pciSharesBus(pciDevice *a, pciDevice *b)
> +pciSharesBusWithActive(pciDevice *dev, pciDevice *check, void *data)
>  {
> -    return
> -        a->domain == b->domain &&
> -        a->bus == b->bus &&
> -        (a->slot != b->slot ||
> -         a->function != b->function);
> -}
> +    pciDeviceList *activeDevs = data;
>  
> -static int
> -pciBusContainsOtherDevices(virConnectPtr conn, pciDevice *dev)
> -{
> -    pciDevice *matched = NULL;
> -    if (pciIterDevices(conn, pciSharesBus, dev, &matched) < 0)
> -        return 1;
> -    if (!matched)
> +    if (dev->domain != check->domain ||
> +        dev->bus != check->bus ||
> +        (check->slot == check->slot &&
> +         check->function == check->function))
> +        return 0;
> +
> +    if (activeDevs && !pciDeviceListFind(activeDevs, check))
>          return 0;
> -    pciFreeDevice(conn, matched);
> +
>      return 1;
>  }
>  
> -/* Is @a the parent of @b ? */
> +static pciDevice *
> +pciBusContainsActiveDevices(virConnectPtr conn,
> +                            pciDevice *dev,
> +                            pciDeviceList *activeDevs)
> +{
> +    pciDevice *active = NULL;
> +    if (pciIterDevices(conn, pciSharesBusWithActive,
> +                       dev, &active, activeDevs) < 0)
> +        return NULL;
> +    return active;
> +}
> +
> +/* Is @check the parent of @dev ? */
>  static int
> -pciIsParent(pciDevice *a, pciDevice *b)
> +pciIsParent(pciDevice *dev, pciDevice *check, void *data ATTRIBUTE_UNUSED)
>  {
>      uint16_t device_class;
>      uint8_t header_type, secondary, subordinate;
>  
> -    if (a->domain != b->domain)
> +    if (dev->domain != check->domain)
>          return 0;
>  
>      /* Is it a bridge? */
> -    device_class = pciRead16(a, PCI_CLASS_DEVICE);
> +    device_class = pciRead16(check, PCI_CLASS_DEVICE);
>      if (device_class != PCI_CLASS_BRIDGE_PCI)
>          return 0;
>  
>      /* Is it a plane? */
> -    header_type = pciRead8(a, PCI_HEADER_TYPE);
> +    header_type = pciRead8(check, PCI_HEADER_TYPE);
>      if ((header_type & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_BRIDGE)
>          return 0;
>  
> -    secondary   = pciRead8(a, PCI_SECONDARY_BUS);
> -    subordinate = pciRead8(a, PCI_SUBORDINATE_BUS);
> +    secondary   = pciRead8(check, PCI_SECONDARY_BUS);
> +    subordinate = pciRead8(check, PCI_SUBORDINATE_BUS);
>  
> -    VIR_DEBUG("%s %s: found parent device %s\n", b->id, b->name, a->name);
> +    VIR_DEBUG("%s %s: found parent device %s\n", dev->id, dev->name, check->name);
>  
>      /* No, it's superman! */
> -    return (b->bus >= secondary && b->bus <= subordinate);
> +    return (dev->bus >= secondary && dev->bus <= subordinate);
>  }
>  
>  static pciDevice *
>  pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
>  {
>      pciDevice *parent = NULL;
> -    pciIterDevices(conn, pciIsParent, dev, &parent);
> +    pciIterDevices(conn, pciIsParent, dev, &parent, NULL);
>      return parent;
>  }
>  
> @@ -444,9 +452,11 @@ pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
>   * devices behind a bus.
>   */
>  static int
> -pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev)
> +pciTrySecondaryBusReset(virConnectPtr conn,
> +                        pciDevice *dev,
> +                        pciDeviceList *activeDevs)
>  {
> -    pciDevice *parent;
> +    pciDevice *parent, *conflict;
>      uint8_t config_space[PCI_CONF_LEN];
>      uint16_t ctl;
>      int ret = -1;
> @@ -456,10 +466,10 @@ pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev)
>       * In future, we could allow it so long as those devices
>       * are not in use by the host or other guests.
>       */
> -    if (pciBusContainsOtherDevices(conn, dev)) {
> +    if ((conflict = pciBusContainsActiveDevices(conn, dev, activeDevs))) {
>          pciReportError(conn, VIR_ERR_NO_SUPPORT,
> -                       _("Other devices on bus with %s, not doing bus reset"),
> -                       dev->name);
> +                       _("Active %s devices on bus with %s, not doing bus reset"),
> +                       conflict->name, dev->name);
>          return -1;
>      }
>  
> @@ -573,10 +583,18 @@ pciInitDevice(virConnectPtr conn, pciDevice *dev)
>  }
>  
>  int
> -pciResetDevice(virConnectPtr conn, pciDevice *dev)
> +pciResetDevice(virConnectPtr conn,
> +               pciDevice *dev,
> +               pciDeviceList *activeDevs)
>  {
>      int ret = -1;
>  
> +    if (activeDevs && pciDeviceListFind(activeDevs, dev)) {
> +        pciReportError(conn, VIR_ERR_INTERNAL_ERROR,
> +                       _("Not resetting active device %s"), dev->name);
> +        return -1;
> +    }
> +
>      if (!dev->initted && pciInitDevice(conn, dev) < 0)
>          return -1;
>  
> @@ -595,7 +613,7 @@ pciResetDevice(virConnectPtr conn, pciDevice *dev)
>  
>      /* Bus reset is not an option with the root bus */
>      if (ret < 0 && dev->bus != 0)
> -        ret = pciTrySecondaryBusReset(conn, dev);
> +        ret = pciTrySecondaryBusReset(conn, dev, activeDevs);
>  
>      if (ret < 0) {
>          virErrorPtr err = virGetLastError();
> diff --git a/src/pci.h b/src/pci.h
> index 75fbd51..685b0af 100644
> --- a/src/pci.h
> +++ b/src/pci.h
> @@ -44,7 +44,8 @@ int        pciDettachDevice  (virConnectPtr  conn,
>  int        pciReAttachDevice (virConnectPtr  conn,
>                                pciDevice     *dev);
>  int        pciResetDevice    (virConnectPtr  conn,
> -                              pciDevice     *dev);
> +                              pciDevice     *dev,
> +                              pciDeviceList *activeDevs);
>  void      pciDeviceSetManaged(pciDevice     *dev,
>                                unsigned       managed);
>  unsigned  pciDeviceGetManaged(pciDevice     *dev);
> diff --git a/src/qemu_conf.h b/src/qemu_conf.h
> index 8f9e1c1..a126dac 100644
> --- a/src/qemu_conf.h
> +++ b/src/qemu_conf.h
> @@ -35,6 +35,7 @@
>  #include "threads.h"
>  #include "security.h"
>  #include "cgroup.h"
> +#include "pci.h"
>  
>  #define qemudDebug(fmt, ...) do {} while(0)
>  
> @@ -111,6 +112,8 @@ struct qemud_driver {
>      virSecurityDriverPtr securityDriver;
>  
>      char *saveImageFormat;
> +
> +    pciDeviceList *activePciHostdevs;
>  };
>  
>  
> diff --git a/src/qemu_driver.c b/src/qemu_driver.c
> index f2b807b..70d3d55 100644
> --- a/src/qemu_driver.c
> +++ b/src/qemu_driver.c
> @@ -128,6 +128,9 @@ static int qemudDomainSetMemoryBalloon(virConnectPtr conn,
>  static int qemudDetectVcpuPIDs(virConnectPtr conn,
>                                 virDomainObjPtr vm);
>  
> +static int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
> +                                       virDomainDefPtr def);
> +
>  static struct qemud_driver *qemu_driver = NULL;
>  
>  static int qemuCgroupControllerActive(struct qemud_driver *driver,
> @@ -320,6 +323,10 @@ qemuReconnectDomain(struct qemud_driver *driver,
>          goto error;
>      }
>  
> +    if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) {
> +        goto error;
> +    }
> +
>      if (obj->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC &&
>          driver->securityDriver &&
>          driver->securityDriver->domainReserveSecurityLabel &&
> @@ -524,6 +531,9 @@ qemudStartup(int privileged) {
>      if ((qemu_driver->caps = qemudCapsInit(NULL)) == NULL)
>          goto out_of_memory;
>  
> +    if ((qemu_driver->activePciHostdevs = pciDeviceListNew(NULL)) == NULL)
> +        goto error;
> +
>      if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
>          goto error;
>      }
> @@ -648,6 +658,7 @@ qemudShutdown(void) {
>          return -1;
>  
>      qemuDriverLock(qemu_driver);
> +    pciDeviceListFree(NULL, qemu_driver->activePciHostdevs);
>      virCapabilitiesFree(qemu_driver->caps);
>  
>      virDomainObjListFree(&qemu_driver->domains);
> @@ -1371,7 +1382,38 @@ qemuGetPciHostDeviceList(virConnectPtr conn,
>  }
>  
>  static int
> -qemuPrepareHostDevices(virConnectPtr conn, virDomainDefPtr def)
> +qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
> +                            virDomainDefPtr def)
> +{
> +    pciDeviceList *pcidevs;
> +    int i, ret;
> +
> +    if (!def->nhostdevs)
> +        return 0;
> +
> +    if (!(pcidevs = qemuGetPciHostDeviceList(NULL, def)))
> +        return -1;
> +
> +    ret = 0;
> +
> +    for (i = 0; i < pcidevs->count; i++) {
> +        if (pciDeviceListAdd(NULL,
> +                             driver->activePciHostdevs,
> +                             pcidevs->devs[i]) < 0) {
> +            ret = -1;
> +            break;
> +        }
> +        pcidevs->devs[i] = NULL;
> +    }
> +
> +    pciDeviceListFree(NULL, pcidevs);
> +    return ret;
> +}
> +
> +static int
> +qemuPrepareHostDevices(virConnectPtr conn,
> +                       struct qemud_driver *driver,
> +                       virDomainDefPtr def)
>  {
>      pciDeviceList *pcidevs;
>      int i;
> @@ -1382,10 +1424,11 @@ qemuPrepareHostDevices(virConnectPtr conn, virDomainDefPtr def)
>      if (!(pcidevs = qemuGetPciHostDeviceList(conn, def)))
>          return -1;
>  
> -    /* We have to use 2 loops here. *All* devices must
> +    /* We have to use 3 loops here. *All* devices must
>       * be detached before we reset any of them, because
>       * in some cases you have to reset the whole PCI,
> -     * which impacts all devices on it
> +     * which impacts all devices on it. Also, all devices
> +     * must be reset before being marked as active.
>       */
>  
>      /* XXX validate that non-managed device isn't in use, eg
> @@ -1401,9 +1444,19 @@ qemuPrepareHostDevices(virConnectPtr conn, virDomainDefPtr def)
>      /* Now that all the PCI hostdevs have be dettached, we can safely
>       * reset them */
>      for (i = 0; i < pcidevs->count; i++)
> -        if (pciResetDevice(conn, pcidevs->devs[i]) < 0)
> +        if (pciResetDevice(conn, pcidevs->devs[i],
> +                           driver->activePciHostdevs) < 0)
>              goto error;
>  
> +    /* Now mark all the devices as active */
> +    for (i = 0; i < pcidevs->count; i++) {
> +        if (pciDeviceListAdd(conn,
> +                             driver->activePciHostdevs,
> +                             pcidevs->devs[i]) < 0)
> +            goto error;
> +        pcidevs->devs[i] = NULL;
> +    }
> +
>      pciDeviceListFree(conn, pcidevs);
>      return 0;
>  
> @@ -1413,7 +1466,9 @@ error:
>  }
>  
>  static void
> -qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
> +qemuDomainReAttachHostDevices(virConnectPtr conn,
> +                              struct qemud_driver *driver,
> +                              virDomainDefPtr def)
>  {
>      pciDeviceList *pcidevs;
>      int i;
> @@ -1429,10 +1484,15 @@ qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
>          return;
>      }
>  
> -    /* Again 2 loops; reset all the devices before re-attach */
> +    /* Again 3 loops; mark all devices as inactive before reset
> +     * them and reset all the devices before re-attach */
>  
>      for (i = 0; i < pcidevs->count; i++)
> -        if (pciResetDevice(conn, pcidevs->devs[i]) < 0) {
> +        pciDeviceListDel(conn, driver->activePciHostdevs, pcidevs->devs[i]);
> +
> +    for (i = 0; i < pcidevs->count; i++)
> +        if (pciResetDevice(conn, pcidevs->devs[i],
> +                           driver->activePciHostdevs) < 0) {
>              virErrorPtr err = virGetLastError();
>              VIR_ERROR(_("Failed to reset PCI device: %s\n"),
>                        err ? err->message : "");
> @@ -1976,7 +2036,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
>      if (qemuSetupCgroup(conn, driver, vm) < 0)
>          goto cleanup;
>  
> -    if (qemuPrepareHostDevices(conn, vm->def) < 0)
> +    if (qemuPrepareHostDevices(conn, driver, vm->def) < 0)
>          goto cleanup;
>  
>      if (VIR_ALLOC(vm->monitor_chr) < 0) {
> @@ -2158,7 +2218,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
>          VIR_WARN("Failed to restore all device ownership for %s",
>                   vm->def->name);
>  
> -    qemuDomainReAttachHostDevices(conn, vm->def);
> +    qemuDomainReAttachHostDevices(conn, driver, vm->def);
>  
>  retry:
>      if ((ret = qemuRemoveCgroup(conn, driver, vm)) < 0) {
> @@ -5279,6 +5339,7 @@ cleanup:
>  }
>  
>  static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
> +                                          struct qemud_driver *driver,
>                                            virDomainObjPtr vm,
>                                            virDomainDeviceDefPtr dev)
>  {
> @@ -5301,42 +5362,42 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
>          return -1;
>  
>      if ((hostdev->managed && pciDettachDevice(conn, pci) < 0) ||
> -        pciResetDevice(conn, pci) < 0) {
> +        pciResetDevice(conn, pci, driver->activePciHostdevs) < 0) {
> +        pciFreeDevice(conn, pci);
> +        return -1;
> +    }
> +
> +    if (pciDeviceListAdd(conn, driver->activePciHostdevs, pci) < 0) {
>          pciFreeDevice(conn, pci);
>          return -1;
>      }
>  
> -    pciFreeDevice(conn, pci);
> +    cmd = reply = NULL;
>  
>      if (virAsprintf(&cmd, "pci_add pci_addr=auto host host=%.2x:%.2x.%.1x",
>                      hostdev->source.subsys.u.pci.bus,
>                      hostdev->source.subsys.u.pci.slot,
>                      hostdev->source.subsys.u.pci.function) < 0) {
>          virReportOOMError(conn);
> -        return -1;
> +        goto error;
>      }
>  
>      if (qemudMonitorCommand(vm, cmd, &reply) < 0) {
>          qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
>                           "%s", _("cannot attach host pci device"));
> -        VIR_FREE(cmd);
> -        return -1;
> +        goto error;
>      }
>  
>      if (strstr(reply, "invalid type: host")) {
>          qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT, "%s",
>                           _("PCI device assignment is not supported by this version of qemu"));
> -        VIR_FREE(cmd);
> -        VIR_FREE(reply);
> -        return -1;
> +        goto error;
>      }
>  
>      if (qemudParsePciAddReply(vm, reply, &domain, &bus, &slot) < 0) {
>          qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
>                           _("parsing pci_add reply failed: %s"), reply);
> -        VIR_FREE(cmd);
> -        VIR_FREE(reply);
> -        return -1;
> +        goto error;
>      }
>  
>      hostdev->source.subsys.u.pci.guest_addr.domain = domain;
> @@ -5349,6 +5410,14 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
>      VIR_FREE(cmd);
>  
>      return 0;
> +
> +error:
> +    pciDeviceListDel(conn, driver->activePciHostdevs, pci);
> +
> +    VIR_FREE(reply);
> +    VIR_FREE(cmd);
> +
> +    return -1;
>  }
>  
>  static int qemudDomainAttachHostUsbDevice(virConnectPtr conn,
> @@ -5422,7 +5491,7 @@ static int qemudDomainAttachHostDevice(virConnectPtr conn,
>  
>      switch (hostdev->source.subsys.type) {
>      case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
> -        return qemudDomainAttachHostPciDevice(conn, vm, dev);
> +        return qemudDomainAttachHostPciDevice(conn, driver, vm, dev);
>      case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
>          return qemudDomainAttachHostUsbDevice(conn, vm, dev);
>      default:
> @@ -5749,6 +5818,7 @@ cleanup:
>  }
>  
>  static int qemudDomainDetachHostPciDevice(virConnectPtr conn,
> +                                          struct qemud_driver *driver,
>                                            virDomainObjPtr vm,
>                                            virDomainDeviceDefPtr dev)
>  {
> @@ -5832,7 +5902,8 @@ static int qemudDomainDetachHostPciDevice(virConnectPtr conn,
>      if (!pci)
>          ret = -1;
>      else {
> -        if (pciResetDevice(conn, pci) < 0)
> +        pciDeviceListDel(conn, driver->activePciHostdevs, pci);
> +        if (pciResetDevice(conn, pci, driver->activePciHostdevs) < 0)
>              ret = -1;
>          if (detach->managed && pciReAttachDevice(conn, pci) < 0)
>              ret = -1;
> @@ -5868,7 +5939,7 @@ static int qemudDomainDetachHostDevice(virConnectPtr conn,
>  
>      switch (hostdev->source.subsys.type) {
>      case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
> -        ret = qemudDomainDetachHostPciDevice(conn, vm, dev);
> +        ret = qemudDomainDetachHostPciDevice(conn, driver, vm, dev);
>          break;
>      default:
>          qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT,
> @@ -7092,6 +7163,7 @@ out:
>  static int
>  qemudNodeDeviceReset (virNodeDevicePtr dev)
>  {
> +    struct qemud_driver *driver = dev->conn->privateData;
>      pciDevice *pci;
>      unsigned domain, bus, slot, function;
>      int ret = -1;
> @@ -7103,11 +7175,14 @@ qemudNodeDeviceReset (virNodeDevicePtr dev)
>      if (!pci)
>          return -1;
>  
> -    if (pciResetDevice(dev->conn, pci) < 0)
> +    qemuDriverLock(driver);
> +
> +    if (pciResetDevice(dev->conn, pci, driver->activePciHostdevs) < 0)
>          goto out;
>  
>      ret = 0;
>  out:
> +    qemuDriverUnlock(driver);
>      pciFreeDevice(dev->conn, pci);
>      return ret;
>  }
> diff --git a/src/xen_unified.c b/src/xen_unified.c
> index f2ffc25..dfa9ca5 100644
> --- a/src/xen_unified.c
> +++ b/src/xen_unified.c
> @@ -1641,7 +1641,7 @@ xenUnifiedNodeDeviceReset (virNodeDevicePtr dev)
>      if (!pci)
>          return -1;
>  
> -    if (pciResetDevice(dev->conn, pci) < 0)
> +    if (pciResetDevice(dev->conn, pci, NULL) < 0)
>          goto out;
>  
>      ret = 0;

ACK

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]