diff options
author | Chris Wright <chrisw@sous-sol.org> | 2009-02-24 00:52:23 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-03-20 13:48:11 -0400 |
commit | 0994375e9614f78657031e04e30019b9cdb62795 (patch) | |
tree | 471d1eb44d5d9c6351bb55cb6187fb30c1112316 | |
parent | 13bf75766966e1bcc71fae536988caec312eef8f (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-pci | 16 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 80 |
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 | ||
44 | What: /sys/bus/pci/drivers/.../remove_id | ||
45 | Date: February 2009 | ||
46 | Contact: Chris Wright <chrisw@sous-sol.org> | ||
47 | Description: | ||
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 | |||
44 | What: /sys/bus/pci/devices/.../vpd | 60 | What: /sys/bus/pci/devices/.../vpd |
45 | Date: February 2008 | 61 | Date: February 2008 |
46 | Contact: Ben Hutchings <bhutchings@solarflare.com> | 62 | Contact: 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 | } |
100 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); | 100 | static 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 | */ | ||
110 | static ssize_t | ||
111 | store_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 | } | ||
146 | static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); | ||
147 | |||
102 | static void | 148 | static void |
103 | pci_free_dynids(struct pci_driver *drv) | 149 | pci_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 | |||
175 | static int | ||
176 | pci_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 | |||
184 | static 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 */ |
129 | static inline void pci_free_dynids(struct pci_driver *drv) {} | 189 | static inline void pci_free_dynids(struct pci_driver *drv) {} |
130 | static inline int pci_create_newid_file(struct pci_driver *drv) | 190 | static 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 | } |
134 | static inline void pci_remove_newid_file(struct pci_driver *drv) {} | 194 | static inline void pci_remove_newid_file(struct pci_driver *drv) {} |
195 | static inline int pci_create_removeid_file(struct pci_driver *drv) | ||
196 | { | ||
197 | return 0; | ||
198 | } | ||
199 | static 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; | ||
929 | out: | ||
861 | return error; | 930 | return error; |
931 | |||
932 | out_removeid: | ||
933 | pci_remove_newid_file(drv); | ||
934 | out_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, | |||
874 | void | 949 | void |
875 | pci_unregister_driver(struct pci_driver *drv) | 950 | pci_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); |