aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wright <chrisw@sous-sol.org>2009-02-24 00:52:23 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-03-20 13:48:11 -0400
commit0994375e9614f78657031e04e30019b9cdb62795 (patch)
tree471d1eb44d5d9c6351bb55cb6187fb30c1112316
parent13bf75766966e1bcc71fae536988caec312eef8f (diff)
PCI: add remove_id sysfs entry
This adds a remove_id sysfs entry to allow users of new_id to later remove the added dynid. One use case is management tools that want to dynamically bind/unbind devices to pci-stub driver while devices are assigned to KVM guests. Rather than having to track which driver was originally bound to the driver, a mangement tool can simply: Guest uses device Signed-off-by: Chris Wright <chrisw@sous-sol.org> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci16
-rw-r--r--drivers/pci/pci-driver.c80
2 files changed, 94 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index e638e15a8895..3d29793a0ea2 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -41,6 +41,22 @@ Description:
41 for the device and attempt to bind to it. For example: 41 for the device and attempt to bind to it. For example:
42 # echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id 42 # echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id
43 43
44What: /sys/bus/pci/drivers/.../remove_id
45Date: February 2009
46Contact: Chris Wright <chrisw@sous-sol.org>
47Description:
48 Writing a device ID to this file will remove an ID
49 that was dynamically added via the new_id sysfs entry.
50 The format for the device ID is:
51 VVVV DDDD SVVV SDDD CCCC MMMM. That is Vendor ID, Device
52 ID, Subsystem Vendor ID, Subsystem Device ID, Class,
53 and Class Mask. The Vendor ID and Device ID fields are
54 required, the rest are optional. After successfully
55 removing an ID, the driver will no longer support the
56 device. This is useful to ensure auto probing won't
57 match the driver to the device. For example:
58 # echo "8086 10f5" > /sys/bus/pci/drivers/foo/remove_id
59
44What: /sys/bus/pci/devices/.../vpd 60What: /sys/bus/pci/devices/.../vpd
45Date: February 2008 61Date: February 2008
46Contact: Ben Hutchings <bhutchings@solarflare.com> 62Contact: Ben Hutchings <bhutchings@solarflare.com>
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 93eac1423585..87a5ddbb324f 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -99,6 +99,52 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
99} 99}
100static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); 100static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
101 101
102/**
103 * store_remove_id - remove a PCI device ID from this driver
104 * @driver: target device driver
105 * @buf: buffer for scanning device ID data
106 * @count: input size
107 *
108 * Removes a dynamic pci device ID to this driver.
109 */
110static ssize_t
111store_remove_id(struct device_driver *driver, const char *buf, size_t count)
112{
113 struct pci_dynid *dynid, *n;
114 struct pci_driver *pdrv = to_pci_driver(driver);
115 __u32 vendor, device, subvendor = PCI_ANY_ID,
116 subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
117 int fields = 0;
118 int retval = -ENODEV;
119
120 fields = sscanf(buf, "%x %x %x %x %x %x",
121 &vendor, &device, &subvendor, &subdevice,
122 &class, &class_mask);
123 if (fields < 2)
124 return -EINVAL;
125
126 spin_lock(&pdrv->dynids.lock);
127 list_for_each_entry_safe(dynid, n, &pdrv->dynids.list, node) {
128 struct pci_device_id *id = &dynid->id;
129 if ((id->vendor == vendor) &&
130 (id->device == device) &&
131 (subvendor == PCI_ANY_ID || id->subvendor == subvendor) &&
132 (subdevice == PCI_ANY_ID || id->subdevice == subdevice) &&
133 !((id->class ^ class) & class_mask)) {
134 list_del(&dynid->node);
135 kfree(dynid);
136 retval = 0;
137 break;
138 }
139 }
140 spin_unlock(&pdrv->dynids.lock);
141
142 if (retval)
143 return retval;
144 return count;
145}
146static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
147
102static void 148static void
103pci_free_dynids(struct pci_driver *drv) 149pci_free_dynids(struct pci_driver *drv)
104{ 150{
@@ -125,6 +171,20 @@ static void pci_remove_newid_file(struct pci_driver *drv)
125{ 171{
126 driver_remove_file(&drv->driver, &driver_attr_new_id); 172 driver_remove_file(&drv->driver, &driver_attr_new_id);
127} 173}
174
175static int
176pci_create_removeid_file(struct pci_driver *drv)
177{
178 int error = 0;
179 if (drv->probe != NULL)
180 error = driver_create_file(&drv->driver,&driver_attr_remove_id);
181 return error;
182}
183
184static void pci_remove_removeid_file(struct pci_driver *drv)
185{
186 driver_remove_file(&drv->driver, &driver_attr_remove_id);
187}
128#else /* !CONFIG_HOTPLUG */ 188#else /* !CONFIG_HOTPLUG */
129static inline void pci_free_dynids(struct pci_driver *drv) {} 189static inline void pci_free_dynids(struct pci_driver *drv) {}
130static inline int pci_create_newid_file(struct pci_driver *drv) 190static inline int pci_create_newid_file(struct pci_driver *drv)
@@ -132,6 +192,11 @@ static inline int pci_create_newid_file(struct pci_driver *drv)
132 return 0; 192 return 0;
133} 193}
134static inline void pci_remove_newid_file(struct pci_driver *drv) {} 194static inline void pci_remove_newid_file(struct pci_driver *drv) {}
195static inline int pci_create_removeid_file(struct pci_driver *drv)
196{
197 return 0;
198}
199static inline void pci_remove_removeid_file(struct pci_driver *drv) {}
135#endif 200#endif
136 201
137/** 202/**
@@ -852,13 +917,23 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
852 /* register with core */ 917 /* register with core */
853 error = driver_register(&drv->driver); 918 error = driver_register(&drv->driver);
854 if (error) 919 if (error)
855 return error; 920 goto out;
856 921
857 error = pci_create_newid_file(drv); 922 error = pci_create_newid_file(drv);
858 if (error) 923 if (error)
859 driver_unregister(&drv->driver); 924 goto out_newid;
860 925
926 error = pci_create_removeid_file(drv);
927 if (error)
928 goto out_removeid;
929out:
861 return error; 930 return error;
931
932out_removeid:
933 pci_remove_newid_file(drv);
934out_newid:
935 driver_unregister(&drv->driver);
936 goto out;
862} 937}
863 938
864/** 939/**
@@ -874,6 +949,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
874void 949void
875pci_unregister_driver(struct pci_driver *drv) 950pci_unregister_driver(struct pci_driver *drv)
876{ 951{
952 pci_remove_removeid_file(drv);
877 pci_remove_newid_file(drv); 953 pci_remove_newid_file(drv);
878 driver_unregister(&drv->driver); 954 driver_unregister(&drv->driver);
879 pci_free_dynids(drv); 955 pci_free_dynids(drv);