先看一下pci_stub 是如何使用的,通过下面这段code 可以将0000:00:19.0的driver和device 分来
* # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id
* # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
* # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind
* # ls -l /sys/bus/pci/devices/0000:00:19.0/driver
* .../0000:00:19.0/driver -> ../../../bus/pci/drivers/pci-stub
从这里能看出如果往driver的unbind写0000:00:19.0的话,就可以将device和driver 分开。这样0000:00:19.0 对应的的driver就能和pci_stub 绑定而和0000:00:19.0对应的硬件解帮
使用PCI Stub ,能够将这个pci设备跟目前绑定的驱动分离,暂时由PCI Stub driver接管,最后交给虚拟机。
static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
dev_info(&dev->dev, "claimed by stub\n");
return 0;
}
static struct pci_driver stub_driver = {
.name = "pci-stub",
.id_table = NULL, /* only dynamic id's */
.probe = pci_stub_probe,
};
static int __init pci_stub_init(void)
{
char *p, *id;
int rc;
//注册pci_stub 对应的driver
rc = pci_register_driver(&stub_driver);
if (rc)
return rc;
/* no ids passed actually */
if (ids[0] == '\0')
return 0;
/* add ids specified in the module parameter */
p = ids;
while ((id = strsep(&p, ","))) {
unsigned int vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
int fields;
if (!strlen(id))
continue;
//从ids中拿到vendor,device等
fields = sscanf(id, "%x:%x:%x:%x:%x:%x",
&vendor, &device, &subvendor, &subdevice,
&class, &class_mask);
if (fields < 2) {
printk(KERN_WARNING
"pci-stub: invalid id string \"%s\"\n", id);
continue;
}
printk(KERN_INFO
"pci-stub: add %04X:%04X sub=%04X:%04X cls=%08X/%08X\n",
vendor, device, subvendor, subdevice, class, class_mask);
rc = pci_add_dynid(&stub_driver, vendor, device,
subvendor, subdevice, class, class_mask, 0);
if (rc)
printk(KERN_WARNING
"pci-stub: failed to add dynamic id (%d)\n", rc);
}
return 0;
}
从pci_stub的看如果把pci_stub build 成ko的话,就可以在insmod ko的时候通过ids制定参数。而一般我们是把pci_stub buildin的,这样ids就是NULL。这时候while循环的条件while ((id = strsep(&p, ","))) 就不成立,就直接可以退出了,这样pci_stub 的probe函数就只注册了stub_driver。
这时候就要通过echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id 来写如id。
而这个new_id是在pci-driver中实现的
static ssize_t store_new_id(struct device_driver *driver, const char *buf,
size_t count)
{
struct pci_driver *pdrv = to_pci_driver(driver);
const struct pci_device_id *ids = pdrv->id_table;
__u32 vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
unsigned long driver_data = 0;
int fields = 0;
int retval = 0;
//从/sys/bus/pci/drivers/pci-stub/new_id 中parse出来vendor/device 等变量,本例中"8086 10f5",对应vendor和//device
fields = sscanf(buf, "%x %x %x %x %x %x %lx",
&vendor, &device, &subvendor, &subdevice,
&class, &class_mask, &driver_data);
if (fields < 2)
return -EINVAL;
//field 不等于7 说明没有指定特定的pci设备,需要调用pci_match_id 来看哪个pci设备最匹配.
if (fields != 7) {
struct pci_dev *pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
if (!pdev)
return -ENOMEM;
pdev->vendor = vendor;
pdev->device = device;
pdev->subsystem_vendor = subvendor;
pdev->subsystem_device = subdevice;
pdev->class = class;
if (pci_match_id(pdrv->id_table, pdev))
retval = -EEXIST;
kfree(pdev);
if (retval)
return retval;
}
/* Only accept driver_data values that match an existing id_table
entry */
if (ids) {
retval = -EINVAL;
while (ids->vendor || ids->subvendor || ids->class_mask) {
if (driver_data == ids->driver_data) {
retval = 0;
break;
}
ids++;
}
if (retval) /* No match */
return retval;
}
// 将8086 10f5 这个pci设备和pci_stub 绑定
retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice,
class, class_mask, driver_data);
if (retval)
return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
总结一下:每个pci 设备都有new_id 这个属性,所以每个pci设备都可以在解帮后再和pci_stub 帮订.
* # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id
* # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
* # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind
* # ls -l /sys/bus/pci/devices/0000:00:19.0/driver
* .../0000:00:19.0/driver -> ../../../bus/pci/drivers/pci-stub
从这里能看出如果往driver的unbind写0000:00:19.0的话,就可以将device和driver 分开。这样0000:00:19.0 对应的的driver就能和pci_stub 绑定而和0000:00:19.0对应的硬件解帮
使用PCI Stub ,能够将这个pci设备跟目前绑定的驱动分离,暂时由PCI Stub driver接管,最后交给虚拟机。
static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
dev_info(&dev->dev, "claimed by stub\n");
return 0;
}
static struct pci_driver stub_driver = {
.name = "pci-stub",
.id_table = NULL, /* only dynamic id's */
.probe = pci_stub_probe,
};
static int __init pci_stub_init(void)
{
char *p, *id;
int rc;
//注册pci_stub 对应的driver
rc = pci_register_driver(&stub_driver);
if (rc)
return rc;
/* no ids passed actually */
if (ids[0] == '\0')
return 0;
/* add ids specified in the module parameter */
p = ids;
while ((id = strsep(&p, ","))) {
unsigned int vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
int fields;
if (!strlen(id))
continue;
//从ids中拿到vendor,device等
fields = sscanf(id, "%x:%x:%x:%x:%x:%x",
&vendor, &device, &subvendor, &subdevice,
&class, &class_mask);
if (fields < 2) {
printk(KERN_WARNING
"pci-stub: invalid id string \"%s\"\n", id);
continue;
}
printk(KERN_INFO
"pci-stub: add %04X:%04X sub=%04X:%04X cls=%08X/%08X\n",
vendor, device, subvendor, subdevice, class, class_mask);
rc = pci_add_dynid(&stub_driver, vendor, device,
subvendor, subdevice, class, class_mask, 0);
if (rc)
printk(KERN_WARNING
"pci-stub: failed to add dynamic id (%d)\n", rc);
}
return 0;
}
从pci_stub的看如果把pci_stub build 成ko的话,就可以在insmod ko的时候通过ids制定参数。而一般我们是把pci_stub buildin的,这样ids就是NULL。这时候while循环的条件while ((id = strsep(&p, ","))) 就不成立,就直接可以退出了,这样pci_stub 的probe函数就只注册了stub_driver。
这时候就要通过echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id 来写如id。
而这个new_id是在pci-driver中实现的
static ssize_t store_new_id(struct device_driver *driver, const char *buf,
size_t count)
{
struct pci_driver *pdrv = to_pci_driver(driver);
const struct pci_device_id *ids = pdrv->id_table;
__u32 vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
unsigned long driver_data = 0;
int fields = 0;
int retval = 0;
//从/sys/bus/pci/drivers/pci-stub/new_id 中parse出来vendor/device 等变量,本例中"8086 10f5",对应vendor和//device
fields = sscanf(buf, "%x %x %x %x %x %x %lx",
&vendor, &device, &subvendor, &subdevice,
&class, &class_mask, &driver_data);
if (fields < 2)
return -EINVAL;
//field 不等于7 说明没有指定特定的pci设备,需要调用pci_match_id 来看哪个pci设备最匹配.
if (fields != 7) {
struct pci_dev *pdev = kzalloc(sizeof(*pdev), GFP_KERNEL);
if (!pdev)
return -ENOMEM;
pdev->vendor = vendor;
pdev->device = device;
pdev->subsystem_vendor = subvendor;
pdev->subsystem_device = subdevice;
pdev->class = class;
if (pci_match_id(pdrv->id_table, pdev))
retval = -EEXIST;
kfree(pdev);
if (retval)
return retval;
}
/* Only accept driver_data values that match an existing id_table
entry */
if (ids) {
retval = -EINVAL;
while (ids->vendor || ids->subvendor || ids->class_mask) {
if (driver_data == ids->driver_data) {
retval = 0;
break;
}
ids++;
}
if (retval) /* No match */
return retval;
}
// 将8086 10f5 这个pci设备和pci_stub 绑定
retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice,
class, class_mask, driver_data);
if (retval)
return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
总结一下:每个pci 设备都有new_id 这个属性,所以每个pci设备都可以在解帮后再和pci_stub 帮订.