Merge tag 'stable/for-linus-3.7-x86-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / xen / xen-pciback / pci_stub.c
index 92ff01d..961d664 100644 (file)
@@ -362,6 +362,7 @@ static int __devinit pcistub_init_device(struct pci_dev *dev)
        else {
                dev_dbg(&dev->dev, "reseting (FLR, D3, etc) the device\n");
                __pci_reset_function_locked(dev);
+               pci_restore_state(dev);
        }
        /* Now disable the device (this also ensures some private device
         * data is setup before we export)
@@ -681,14 +682,14 @@ static pci_ers_result_t xen_pcibk_slot_reset(struct pci_dev *dev)
                dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
                        " by HVM, kill it\n");
                kill_domain_by_device(psdev);
-               goto release;
+               goto end;
        }
 
        if (!test_bit(_XEN_PCIB_AERHANDLER,
                (unsigned long *)&psdev->pdev->sh_info->flags)) {
                dev_err(&dev->dev,
                        "guest with no AER driver should have been killed\n");
-               goto release;
+               goto end;
        }
        result = common_process(psdev, 1, XEN_PCI_OP_aer_slotreset, result);
 
@@ -698,9 +699,9 @@ static pci_ers_result_t xen_pcibk_slot_reset(struct pci_dev *dev)
                        "No AER slot_reset service or disconnected!\n");
                kill_domain_by_device(psdev);
        }
-release:
-       pcistub_device_put(psdev);
 end:
+       if (psdev)
+               pcistub_device_put(psdev);
        up_write(&pcistub_sem);
        return result;
 
@@ -739,14 +740,14 @@ static pci_ers_result_t xen_pcibk_mmio_enabled(struct pci_dev *dev)
                dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
                        " by HVM, kill it\n");
                kill_domain_by_device(psdev);
-               goto release;
+               goto end;
        }
 
        if (!test_bit(_XEN_PCIB_AERHANDLER,
                (unsigned long *)&psdev->pdev->sh_info->flags)) {
                dev_err(&dev->dev,
                        "guest with no AER driver should have been killed\n");
-               goto release;
+               goto end;
        }
        result = common_process(psdev, 1, XEN_PCI_OP_aer_mmio, result);
 
@@ -756,9 +757,9 @@ static pci_ers_result_t xen_pcibk_mmio_enabled(struct pci_dev *dev)
                        "No AER mmio_enabled service or disconnected!\n");
                kill_domain_by_device(psdev);
        }
-release:
-       pcistub_device_put(psdev);
 end:
+       if (psdev)
+               pcistub_device_put(psdev);
        up_write(&pcistub_sem);
        return result;
 }
@@ -797,7 +798,7 @@ static pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
                dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
                        " by HVM, kill it\n");
                kill_domain_by_device(psdev);
-               goto release;
+               goto end;
        }
 
        /*Guest owns the device yet no aer handler regiested, kill guest*/
@@ -805,7 +806,7 @@ static pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
                (unsigned long *)&psdev->pdev->sh_info->flags)) {
                dev_dbg(&dev->dev, "guest may have no aer driver, kill it\n");
                kill_domain_by_device(psdev);
-               goto release;
+               goto end;
        }
        result = common_process(psdev, error, XEN_PCI_OP_aer_detected, result);
 
@@ -815,9 +816,9 @@ static pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
                        "No AER error_detected service or disconnected!\n");
                kill_domain_by_device(psdev);
        }
-release:
-       pcistub_device_put(psdev);
 end:
+       if (psdev)
+               pcistub_device_put(psdev);
        up_write(&pcistub_sem);
        return result;
 }
@@ -851,7 +852,7 @@ static void xen_pcibk_error_resume(struct pci_dev *dev)
                dev_err(&dev->dev, DRV_NAME " device is not connected or owned"
                        " by HVM, kill it\n");
                kill_domain_by_device(psdev);
-               goto release;
+               goto end;
        }
 
        if (!test_bit(_XEN_PCIB_AERHANDLER,
@@ -859,13 +860,13 @@ static void xen_pcibk_error_resume(struct pci_dev *dev)
                dev_err(&dev->dev,
                        "guest with no AER driver should have been killed\n");
                kill_domain_by_device(psdev);
-               goto release;
+               goto end;
        }
        common_process(psdev, 1, XEN_PCI_OP_aer_resume,
                       PCI_ERS_RESULT_RECOVERED);
-release:
-       pcistub_device_put(psdev);
 end:
+       if (psdev)
+               pcistub_device_put(psdev);
        up_write(&pcistub_sem);
        return;
 }
@@ -897,17 +898,41 @@ static inline int str_to_slot(const char *buf, int *domain, int *bus,
                              int *slot, int *func)
 {
        int err;
+       char wc = '*';
 
        err = sscanf(buf, " %x:%x:%x.%x", domain, bus, slot, func);
-       if (err == 4)
+       switch (err) {
+       case 3:
+               *func = -1;
+               err = sscanf(buf, " %x:%x:%x.%c", domain, bus, slot, &wc);
+               break;
+       case 2:
+               *slot = *func = -1;
+               err = sscanf(buf, " %x:%x:*.%c", domain, bus, &wc);
+               if (err >= 2)
+                       ++err;
+               break;
+       }
+       if (err == 4 && wc == '*')
                return 0;
        else if (err < 0)
                return -EINVAL;
 
        /* try again without domain */
        *domain = 0;
+       wc = '*';
        err = sscanf(buf, " %x:%x.%x", bus, slot, func);
-       if (err == 3)
+       switch (err) {
+       case 2:
+               *func = -1;
+               err = sscanf(buf, " %x:%x.%c", bus, slot, &wc);
+               break;
+       case 1:
+               *slot = *func = -1;
+               err = sscanf(buf, " %x:*.%c", bus, &wc) + 1;
+               break;
+       }
+       if (err == 3 && wc == '*')
                return 0;
 
        return -EINVAL;
@@ -930,6 +955,19 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func)
 {
        struct pcistub_device_id *pci_dev_id;
        unsigned long flags;
+       int rc = 0;
+
+       if (slot < 0) {
+               for (slot = 0; !rc && slot < 32; ++slot)
+                       rc = pcistub_device_id_add(domain, bus, slot, func);
+               return rc;
+       }
+
+       if (func < 0) {
+               for (func = 0; !rc && func < 8; ++func)
+                       rc = pcistub_device_id_add(domain, bus, slot, func);
+               return rc;
+       }
 
        pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
        if (!pci_dev_id)
@@ -952,15 +990,15 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func)
 static int pcistub_device_id_remove(int domain, int bus, int slot, int func)
 {
        struct pcistub_device_id *pci_dev_id, *t;
-       int devfn = PCI_DEVFN(slot, func);
        int err = -ENOENT;
        unsigned long flags;
 
        spin_lock_irqsave(&device_ids_lock, flags);
        list_for_each_entry_safe(pci_dev_id, t, &pcistub_device_ids,
                                 slot_list) {
-               if (pci_dev_id->domain == domain
-                   && pci_dev_id->bus == bus && pci_dev_id->devfn == devfn) {
+               if (pci_dev_id->domain == domain && pci_dev_id->bus == bus
+                   && (slot < 0 || PCI_SLOT(pci_dev_id->devfn) == slot)
+                   && (func < 0 || PCI_FUNC(pci_dev_id->devfn) == func)) {
                        /* Don't break; here because it's possible the same
                         * slot could be in the list more than once
                         */
@@ -987,7 +1025,7 @@ static int pcistub_reg_add(int domain, int bus, int slot, int func, int reg,
        struct config_field *field;
 
        psdev = pcistub_device_find(domain, bus, slot, func);
-       if (!psdev || !psdev->dev) {
+       if (!psdev) {
                err = -ENODEV;
                goto out;
        }
@@ -1011,6 +1049,8 @@ static int pcistub_reg_add(int domain, int bus, int slot, int func, int reg,
        if (err)
                kfree(field);
 out:
+       if (psdev)
+               pcistub_device_put(psdev);
        return err;
 }
 
@@ -1115,10 +1155,9 @@ static ssize_t pcistub_irq_handler_switch(struct device_driver *drv,
 
        err = str_to_slot(buf, &domain, &bus, &slot, &func);
        if (err)
-               goto out;
+               return err;
 
        psdev = pcistub_device_find(domain, bus, slot, func);
-
        if (!psdev)
                goto out;
 
@@ -1134,6 +1173,8 @@ static ssize_t pcistub_irq_handler_switch(struct device_driver *drv,
        if (dev_data->isr_on)
                dev_data->ack_intr = 1;
 out:
+       if (psdev)
+               pcistub_device_put(psdev);
        if (!err)
                err = count;
        return err;
@@ -1216,15 +1257,16 @@ static ssize_t permissive_add(struct device_driver *drv, const char *buf,
        err = str_to_slot(buf, &domain, &bus, &slot, &func);
        if (err)
                goto out;
+       if (slot < 0 || func < 0) {
+               err = -EINVAL;
+               goto out;
+       }
        psdev = pcistub_device_find(domain, bus, slot, func);
        if (!psdev) {
                err = -ENODEV;
                goto out;
        }
-       if (!psdev->dev) {
-               err = -ENODEV;
-               goto release;
-       }
+
        dev_data = pci_get_drvdata(psdev->dev);
        /* the driver data for a device should never be null at this point */
        if (!dev_data) {
@@ -1297,17 +1339,51 @@ static int __init pcistub_init(void)
 
        if (pci_devs_to_hide && *pci_devs_to_hide) {
                do {
+                       char wc = '*';
+
                        parsed = 0;
 
                        err = sscanf(pci_devs_to_hide + pos,
                                     " (%x:%x:%x.%x) %n",
                                     &domain, &bus, &slot, &func, &parsed);
-                       if (err != 4) {
+                       switch (err) {
+                       case 3:
+                               func = -1;
+                               err = sscanf(pci_devs_to_hide + pos,
+                                            " (%x:%x:%x.%c) %n",
+                                            &domain, &bus, &slot, &wc,
+                                            &parsed);
+                               break;
+                       case 2:
+                               slot = func = -1;
+                               err = sscanf(pci_devs_to_hide + pos,
+                                            " (%x:%x:*.%c) %n",
+                                            &domain, &bus, &wc, &parsed) + 1;
+                               break;
+                       }
+
+                       if (err != 4 || wc != '*') {
                                domain = 0;
+                               wc = '*';
                                err = sscanf(pci_devs_to_hide + pos,
                                             " (%x:%x.%x) %n",
                                             &bus, &slot, &func, &parsed);
-                               if (err != 3)
+                               switch (err) {
+                               case 2:
+                                       func = -1;
+                                       err = sscanf(pci_devs_to_hide + pos,
+                                                    " (%x:%x.%c) %n",
+                                                    &bus, &slot, &wc,
+                                                    &parsed);
+                                       break;
+                               case 1:
+                                       slot = func = -1;
+                                       err = sscanf(pci_devs_to_hide + pos,
+                                                    " (%x:*.%c) %n",
+                                                    &bus, &wc, &parsed) + 1;
+                                       break;
+                               }
+                               if (err != 3 || wc != '*')
                                        goto parse_error;
                        }