Skip to content

Commit 0994375

Browse files
chriswrightjbarnes993
authored andcommitted
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 <[email protected]> Signed-off-by: Jesse Barnes <[email protected]>
1 parent 13bf757 commit 0994375

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

Documentation/ABI/testing/sysfs-bus-pci

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@ Description:
4141
for the device and attempt to bind to it. For example:
4242
# echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id
4343

44+
What: /sys/bus/pci/drivers/.../remove_id
45+
Date: February 2009
46+
Contact: Chris Wright <[email protected]>
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+
4460
What: /sys/bus/pci/devices/.../vpd
4561
Date: February 2008
4662
Contact: Ben Hutchings <[email protected]>

drivers/pci/pci-driver.c

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,52 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
9999
}
100100
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
101101

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+
102148
static void
103149
pci_free_dynids(struct pci_driver *drv)
104150
{
@@ -125,13 +171,32 @@ static void pci_remove_newid_file(struct pci_driver *drv)
125171
{
126172
driver_remove_file(&drv->driver, &driver_attr_new_id);
127173
}
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+
}
128188
#else /* !CONFIG_HOTPLUG */
129189
static inline void pci_free_dynids(struct pci_driver *drv) {}
130190
static inline int pci_create_newid_file(struct pci_driver *drv)
131191
{
132192
return 0;
133193
}
134194
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) {}
135200
#endif
136201

137202
/**
@@ -852,13 +917,23 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
852917
/* register with core */
853918
error = driver_register(&drv->driver);
854919
if (error)
855-
return error;
920+
goto out;
856921

857922
error = pci_create_newid_file(drv);
858923
if (error)
859-
driver_unregister(&drv->driver);
924+
goto out_newid;
860925

926+
error = pci_create_removeid_file(drv);
927+
if (error)
928+
goto out_removeid;
929+
out:
861930
return error;
931+
932+
out_removeid:
933+
pci_remove_newid_file(drv);
934+
out_newid:
935+
driver_unregister(&drv->driver);
936+
goto out;
862937
}
863938

864939
/**
@@ -874,6 +949,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
874949
void
875950
pci_unregister_driver(struct pci_driver *drv)
876951
{
952+
pci_remove_removeid_file(drv);
877953
pci_remove_newid_file(drv);
878954
driver_unregister(&drv->driver);
879955
pci_free_dynids(drv);

0 commit comments

Comments
 (0)