qemu
發表時間:2020-11-13
發布人:葵宇科技
浏覽次數:39
Linux客戶機 virtio設備初始化
virtio設備物理上連接在pci物理總線上,邏輯上連接在virtio虛拟總線。做為pci設備便于資(zī)源分配與配置,邏輯設備模型中(zhōng),便于管理與組織。
1.qemu-kvm提供的virtio pci設備
virtio-blk(硬盤),virtio-net(網絡),virtio-balloon(氣球)等pci設備,這些設備連接在pci總線上。代碼位于qemu: hw/virtio-pci.c
static PCIDeviceInfo virtio_info[] = {
{
.qdev.name = "virtio-blk-pci",
},{
.qdev.name = "virtio-net-pci",
},{
.qdev.name = "virtio-serial-pci",
},{
.qdev.name = "virtio-balloon-pci",
},
}
static void virtio_pci_register_devices(void)
{
pci_qdev_register_many(virtio_info);
}
2.客戶機PCI設備進行枚舉和(hé)資(zī)源分配
當Linux客戶機系統啟動(dòng)時,對PCI設備進行枚舉和(hé)資(zī)源分配(配置PCI的配置空間),通(tōng)常由BIOS完成。不過對Linux系統提供方式,一種由BIOS實現,另一種自己實現枚舉和(hé)資(zī)源分配功能。代碼位于kernel:arch/x86/pci/init.c
static __init int pci_arch_init(void)
{
#ifdef CONFIG_PCI_DIRECT
int type = 0;
type = pci_direct_probe();
#endif
#endif
#ifdef CONFIG_PCI_BIOS
pci_pcbios_init();
#endif
}
真正設備枚舉和(hé)資(zī)源分配由這裡開始
static int __init pci_legacy_init(void)
{
printk("PCI: Probing PCI hardware\n");
pci_root_bus = pcibios_scan_root(0);
if (pci_root_bus)
pci_bus_add_devices(pci_root_bus);
return 0;
}
pcibios_scan_root()---->pci_scan_bus_parented()---->pci_scan_child_bus()--->pci_scan_slot()--->pci_scan_single_device()----->pci_device_add()
将PCI總線上的設備添加到鍊表
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.
*/
down_write(&pci_bus_sem);
list_add_tail(&dev->bus_list, &bus->devices);
up_write(&pci_bus_sem);
}
上述過程執行完成,在/sys/devices/pci0000:00目錄下(xià),創建virtio pci設備。并且在/sys/bus/pci/devices/目錄下(xià),創建相應對于pci設備的符号連接,同時在/sys/bus/pci/drivers/目錄下(xià),創建virtio-pci目錄,目錄下(xià)存在支持設備符号連接文(wén)件。
3.virtio總線定義與注冊,virtio總線為虛拟的總線,目的為了設備管理與組織需要。代碼位于:
static struct bus_type virtio_bus = {
.name = "virtio",
.match = virtio_dev_match,
.dev_attrs = virtio_dev_attrs,
.uevent = virtio_uevent,
.probe = virtio_dev_probe,
.remove = virtio_dev_remove,
};
static int virtio_init(void)
{
if (bus_register(&virtio_bus) != 0)
panic("virtio bus registration failed");
return 0;
}
上述注冊函數調用執行完成,在/sys/bus/目錄下(xià),創建了一個(gè)新的目錄virtio,在該目錄下(xià)同時創建了兩個(gè)文(wén)件夾為devices和(hé)drivers。表示創建virtio總線,總線支持設備與驅動(dòng)devices和(hé)drivers目錄下(xià)。
4. virtio-pci設備驅動(dòng)加載
static struct pci_driver virtio_pci_driver = {
.name = "virtio-pci",
.id_table = virtio_pci_id_table,
.probe = virtio_pci_probe,
.remove = virtio_pci_remove,
#ifdef CONFIG_PM
.suspend = virtio_pci_suspend,
.resume = virtio_pci_resume,
#endif
};
static int __init virtio_pci_init(void)
{
virtio_pci_root = root_device_register("virtio-pci");
err = pci_register_driver(&virtio_pci_driver);
return err;
}
上述注冊函數調用執行完成,在/sys/bus/pci/drivers和(hé)/sys/devices目錄下(xià)創建了virtio-pci文(wén)件夾
5,virtio總線子(zǐ)設備注冊
上面步驟2,對PCI設備進行枚舉和(hé)資(zī)源分配中(zhōng)介紹了,枚舉的設備,已經關(guān)聯到總線鍊表中(zhōng)。對函數調用pci_register_driver(&virtio_pci_driver)就是對鍊表的每一個(gè)pci設備進行探測,該驅動(dòng)是否支持該設備,如(rú)果支持進,調用驅動(dòng)probe函數,完成啟用該pci設備,同時在virtio總線進行注冊設備。
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
if (drv->probe) {
ret = drv->probe(dev);
}
static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
{
struct virtio_pci_device *vp_dev;
int err;
/* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */
if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)
return -ENODEV;
/* allocate our structure and fill it out */
vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL);
if (vp_dev == NULL)
return -ENOMEM;
vp_dev->vdev.dev.parent = virtio_pci_root;
vp_dev->vdev.dev.release = virtio_pci_release_dev;
vp_dev->vdev.config = &virtio_pci_config_ops;
vp_dev->pci_dev = pci_dev;
INIT_LIST_HEAD(&vp_dev->virtqueues);
spin_lock_init(&vp_dev->lock);
/* Disable MSI/MSIX to bring device to a known good state. */
pci_msi_off(pci_dev);
/* enable the device */
err = pci_enable_device(pci_dev);
if (err)
goto out;
err = pci_request_regions(pci_dev, "virtio-pci");
if (err)
goto out_enable_device;
vp_dev->ioaddr = pci_iomap(pci_dev, 0, 0);
if (vp_dev->ioaddr == NULL)
goto out_req_regions;
pci_set_drvdata(pci_dev, vp_dev);
/* we use the subsystem vendor/device id as the virtio vendor/device
* id. this allows us to use the same PCI vendor/device id for all
* virtio devices and to identify the particular virtio driver by
* the subsytem ids */
vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
vp_dev->vdev.id.device = pci_dev->subsystem_device;
/* finally register the virtio device */
err = register_virtio_device(&vp_dev->vdev);
if (err)
goto out_set_drvdata;
return 0;
}
上述注冊函數調用執行完成,/sys/devices/virtio-pci/創建相應子(zǐ)設備{virtio1,virtio2,virtio3},同時在/sys/bus/virtio/devices下(xià)面創建三個(gè)符号連接文(wén)件{virtio1,virtio2,virtio3}
6. virtio總線子(zǐ)設備驅動(dòng)注冊。
當virtio總線進行注冊設備register_virtio_device,将調用virtio總線的probe函數:virtio_dev_probe()。該函數遍曆驅動(dòng),找到支持驅動(dòng)關(guān)聯到該設備。
register_virtio_device()--->bus_probe_device()---->device_attach();
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
}
static int virtio_dev_probe(struct device *_d)
{
int err, i;
struct virtio_device *dev = container_of(_d,struct virtio_device,dev);
struct virtio_driver *drv = container_of(dev->dev.driver,
struct virtio_driver, driver);
u32 device_features;
/* We have a driver! */
add_status(dev, VIRTIO_CONFIG_S_DRIVER);
/* Figure out what features the device supports. */
device_features = dev->config->get_features(dev);
/* Features supported by both device and driver into dev->features. */
memset(dev->features, 0, sizeof(dev->features));
for (i = 0; i < drv->feature_table_size; i++) {
unsigned int f = drv->feature_table[i];
BUG_ON(f >= 32);
if (device_features & (1 << f))
set_bit(f, dev->features);
}
/* Transport features always preserved to pass to finalize_features. */
for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
if (device_features & (1 << i))
set_bit(i, dev->features);
dev->config->finalize_features(dev);
err = drv->probe(dev);
if (err)
add_status(dev, VIRTIO_CONFIG_S_FAILED);
else
add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
return err;
}
//virtio_balloon設備驅動(dòng)實例
static struct virtio_driver virtio_balloon_driver = {
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtballoon_probe,
.remove = __devexit_p(virtballoon_remove),
.config_changed = virtballoon_changed,
};
static int __init init(void)
{
return register_virtio_driver(&virtio_balloon_driver);
}
同時在/sys/bus/virtio/drivers下(xià)面創建三個(gè)文(wén)件{virtio_balloon,virtio_blk,virtio_console},并且與設備發生關(guān)聯
//////////////////////////////
熱插拔事件的産生往往是由總線驅動(dòng)級的邏輯處理,所以總線一般提供事件發送函數。例如(rú)virtio總線事件函數virtio_uevent。
static int virtio_uevent(struct device *_dv, struct kobj_uevent_env *env)
{
struct virtio_device *dev = container_of(_dv,struct virtio_device,dev);
return add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X",
dev->id.device, dev->id.vendor);
}
下(xià)面函數工作流程如(rú)下(xià):
1.由設備對象往上查找,直到找到包含kset的kobject(總線包含着kset)
2.判斷kobject對象是否提供filter,name,uevent函數,如(rú)果提供,調用它。
3.分配一個(gè)kobj_uevent_env,并開始填充env環境變量:ACTION,DEVPATH,SUBSYSTEM,SEQNUM,MODALIAS
4.通(tōng)過netlink發送到用戶空間
register_virtio_device()---->device_register()---->device_add()---->kobject_uevent()---->kobject_uevent_env()
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
/* originating subsystem */
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);
else
subsystem = kobject_name(&kset->kobj);
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, "%s", envp_ext[i]);
if (retval)
goto exit;
}
}
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d\n", kobject_name(kobj), kobj,
__func__, retval);
goto exit;
}
}
/*
* Mark "add" and "remove" events in the object to ensure proper
* events to userspace during automatic cleanup. If the object did
* send an "add" event, "remove" will automatically generated by
* the core, if not already done by the caller.
*/
if (action == KOBJ_ADD)
kobj->state_add_uevent_sent = 1;
else if (action == KOBJ_REMOVE)
kobj->state_remove_uevent_sent = 1;
/* we will send an event, so request a new sequence number */
spin_lock(&sequence_lock);
seq = ++uevent_seqnum;
spin_unlock(&sequence_lock);
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
if (retval)
goto exit;
/* send netlink message */
if (uevent_sock) {
struct sk_buff *skb;
size_t len;
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
retval = netlink_broadcast(uevent_sock, skb, 0, 1,
GFP_KERNEL);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS)
retval = 0;
} else
retval = -ENOMEM;
}
用戶空間
當發送信息達到了用戶空間,用戶空間的udevd守護進程,接受到此信息。在udev規則文(wén)件裡匹配,相應的規則。
virtio設備物理上連接在pci物理總線上,邏輯上連接在virtio虛拟總線。做為pci設備便于資(zī)源分配與配置,邏輯設備模型中(zhōng),便于管理與組織。
1.qemu-kvm提供的virtio pci設備
virtio-blk(硬盤),virtio-net(網絡),virtio-balloon(氣球)等pci設備,這些設備連接在pci總線上。代碼位于qemu: hw/virtio-pci.c
static PCIDeviceInfo virtio_info[] = {
{
.qdev.name = "virtio-blk-pci",
},{
.qdev.name = "virtio-net-pci",
},{
.qdev.name = "virtio-serial-pci",
},{
.qdev.name = "virtio-balloon-pci",
},
}
static void virtio_pci_register_devices(void)
{
pci_qdev_register_many(virtio_info);
}
2.客戶機PCI設備進行枚舉和(hé)資(zī)源分配
當Linux客戶機系統啟動(dòng)時,對PCI設備進行枚舉和(hé)資(zī)源分配(配置PCI的配置空間),通(tōng)常由BIOS完成。不過對Linux系統提供方式,一種由BIOS實現,另一種自己實現枚舉和(hé)資(zī)源分配功能。代碼位于kernel:arch/x86/pci/init.c
static __init int pci_arch_init(void)
{
#ifdef CONFIG_PCI_DIRECT
int type = 0;
type = pci_direct_probe();
#endif
#endif
#ifdef CONFIG_PCI_BIOS
pci_pcbios_init();
#endif
}
真正設備枚舉和(hé)資(zī)源分配由這裡開始
static int __init pci_legacy_init(void)
{
printk("PCI: Probing PCI hardware\n");
pci_root_bus = pcibios_scan_root(0);
if (pci_root_bus)
pci_bus_add_devices(pci_root_bus);
return 0;
}
pcibios_scan_root()---->pci_scan_bus_parented()---->pci_scan_child_bus()--->pci_scan_slot()--->pci_scan_single_device()----->pci_device_add()
将PCI總線上的設備添加到鍊表
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.
*/
down_write(&pci_bus_sem);
list_add_tail(&dev->bus_list, &bus->devices);
up_write(&pci_bus_sem);
}
上述過程執行完成,在/sys/devices/pci0000:00目錄下(xià),創建virtio pci設備。并且在/sys/bus/pci/devices/目錄下(xià),創建相應對于pci設備的符号連接,同時在/sys/bus/pci/drivers/目錄下(xià),創建virtio-pci目錄,目錄下(xià)存在支持設備符号連接文(wén)件。
3.virtio總線定義與注冊,virtio總線為虛拟的總線,目的為了設備管理與組織需要。代碼位于:
static struct bus_type virtio_bus = {
.name = "virtio",
.match = virtio_dev_match,
.dev_attrs = virtio_dev_attrs,
.uevent = virtio_uevent,
.probe = virtio_dev_probe,
.remove = virtio_dev_remove,
};
static int virtio_init(void)
{
if (bus_register(&virtio_bus) != 0)
panic("virtio bus registration failed");
return 0;
}
上述注冊函數調用執行完成,在/sys/bus/目錄下(xià),創建了一個(gè)新的目錄virtio,在該目錄下(xià)同時創建了兩個(gè)文(wén)件夾為devices和(hé)drivers。表示創建virtio總線,總線支持設備與驅動(dòng)devices和(hé)drivers目錄下(xià)。
4. virtio-pci設備驅動(dòng)加載
static struct pci_driver virtio_pci_driver = {
.name = "virtio-pci",
.id_table = virtio_pci_id_table,
.probe = virtio_pci_probe,
.remove = virtio_pci_remove,
#ifdef CONFIG_PM
.suspend = virtio_pci_suspend,
.resume = virtio_pci_resume,
#endif
};
static int __init virtio_pci_init(void)
{
virtio_pci_root = root_device_register("virtio-pci");
err = pci_register_driver(&virtio_pci_driver);
return err;
}
上述注冊函數調用執行完成,在/sys/bus/pci/drivers和(hé)/sys/devices目錄下(xià)創建了virtio-pci文(wén)件夾
5,virtio總線子(zǐ)設備注冊
上面步驟2,對PCI設備進行枚舉和(hé)資(zī)源分配中(zhōng)介紹了,枚舉的設備,已經關(guān)聯到總線鍊表中(zhōng)。對函數調用pci_register_driver(&virtio_pci_driver)就是對鍊表的每一個(gè)pci設備進行探測,該驅動(dòng)是否支持該設備,如(rú)果支持進,調用驅動(dòng)probe函數,完成啟用該pci設備,同時在virtio總線進行注冊設備。
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
if (drv->probe) {
ret = drv->probe(dev);
}
static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
{
struct virtio_pci_device *vp_dev;
int err;
/* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */
if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)
return -ENODEV;
/* allocate our structure and fill it out */
vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL);
if (vp_dev == NULL)
return -ENOMEM;
vp_dev->vdev.dev.parent = virtio_pci_root;
vp_dev->vdev.dev.release = virtio_pci_release_dev;
vp_dev->vdev.config = &virtio_pci_config_ops;
vp_dev->pci_dev = pci_dev;
INIT_LIST_HEAD(&vp_dev->virtqueues);
spin_lock_init(&vp_dev->lock);
/* Disable MSI/MSIX to bring device to a known good state. */
pci_msi_off(pci_dev);
/* enable the device */
err = pci_enable_device(pci_dev);
if (err)
goto out;
err = pci_request_regions(pci_dev, "virtio-pci");
if (err)
goto out_enable_device;
vp_dev->ioaddr = pci_iomap(pci_dev, 0, 0);
if (vp_dev->ioaddr == NULL)
goto out_req_regions;
pci_set_drvdata(pci_dev, vp_dev);
/* we use the subsystem vendor/device id as the virtio vendor/device
* id. this allows us to use the same PCI vendor/device id for all
* virtio devices and to identify the particular virtio driver by
* the subsytem ids */
vp_dev->vdev.id.vendor = pci_dev->subsystem_vendor;
vp_dev->vdev.id.device = pci_dev->subsystem_device;
/* finally register the virtio device */
err = register_virtio_device(&vp_dev->vdev);
if (err)
goto out_set_drvdata;
return 0;
}
上述注冊函數調用執行完成,/sys/devices/virtio-pci/創建相應子(zǐ)設備{virtio1,virtio2,virtio3},同時在/sys/bus/virtio/devices下(xià)面創建三個(gè)符号連接文(wén)件{virtio1,virtio2,virtio3}
6. virtio總線子(zǐ)設備驅動(dòng)注冊。
當virtio總線進行注冊設備register_virtio_device,将調用virtio總線的probe函數:virtio_dev_probe()。該函數遍曆驅動(dòng),找到支持驅動(dòng)關(guān)聯到該設備。
register_virtio_device()--->bus_probe_device()---->device_attach();
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
}
static int virtio_dev_probe(struct device *_d)
{
int err, i;
struct virtio_device *dev = container_of(_d,struct virtio_device,dev);
struct virtio_driver *drv = container_of(dev->dev.driver,
struct virtio_driver, driver);
u32 device_features;
/* We have a driver! */
add_status(dev, VIRTIO_CONFIG_S_DRIVER);
/* Figure out what features the device supports. */
device_features = dev->config->get_features(dev);
/* Features supported by both device and driver into dev->features. */
memset(dev->features, 0, sizeof(dev->features));
for (i = 0; i < drv->feature_table_size; i++) {
unsigned int f = drv->feature_table[i];
BUG_ON(f >= 32);
if (device_features & (1 << f))
set_bit(f, dev->features);
}
/* Transport features always preserved to pass to finalize_features. */
for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
if (device_features & (1 << i))
set_bit(i, dev->features);
dev->config->finalize_features(dev);
err = drv->probe(dev);
if (err)
add_status(dev, VIRTIO_CONFIG_S_FAILED);
else
add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
return err;
}
//virtio_balloon設備驅動(dòng)實例
static struct virtio_driver virtio_balloon_driver = {
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtballoon_probe,
.remove = __devexit_p(virtballoon_remove),
.config_changed = virtballoon_changed,
};
static int __init init(void)
{
return register_virtio_driver(&virtio_balloon_driver);
}
同時在/sys/bus/virtio/drivers下(xià)面創建三個(gè)文(wén)件{virtio_balloon,virtio_blk,virtio_console},并且與設備發生關(guān)聯
//////////////////////////////
熱插拔事件的産生往往是由總線驅動(dòng)級的邏輯處理,所以總線一般提供事件發送函數。例如(rú)virtio總線事件函數virtio_uevent。
static int virtio_uevent(struct device *_dv, struct kobj_uevent_env *env)
{
struct virtio_device *dev = container_of(_dv,struct virtio_device,dev);
return add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X",
dev->id.device, dev->id.vendor);
}
下(xià)面函數工作流程如(rú)下(xià):
1.由設備對象往上查找,直到找到包含kset的kobject(總線包含着kset)
2.判斷kobject對象是否提供filter,name,uevent函數,如(rú)果提供,調用它。
3.分配一個(gè)kobj_uevent_env,并開始填充env環境變量:ACTION,DEVPATH,SUBSYSTEM,SEQNUM,MODALIAS
4.通(tōng)過netlink發送到用戶空間
register_virtio_device()---->device_register()---->device_add()---->kobject_uevent()---->kobject_uevent_env()
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
/* originating subsystem */
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);
else
subsystem = kobject_name(&kset->kobj);
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, "%s", envp_ext[i]);
if (retval)
goto exit;
}
}
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d\n", kobject_name(kobj), kobj,
__func__, retval);
goto exit;
}
}
/*
* Mark "add" and "remove" events in the object to ensure proper
* events to userspace during automatic cleanup. If the object did
* send an "add" event, "remove" will automatically generated by
* the core, if not already done by the caller.
*/
if (action == KOBJ_ADD)
kobj->state_add_uevent_sent = 1;
else if (action == KOBJ_REMOVE)
kobj->state_remove_uevent_sent = 1;
/* we will send an event, so request a new sequence number */
spin_lock(&sequence_lock);
seq = ++uevent_seqnum;
spin_unlock(&sequence_lock);
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
if (retval)
goto exit;
/* send netlink message */
if (uevent_sock) {
struct sk_buff *skb;
size_t len;
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
retval = netlink_broadcast(uevent_sock, skb, 0, 1,
GFP_KERNEL);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS)
retval = 0;
} else
retval = -ENOMEM;
}
用戶空間
當發送信息達到了用戶空間,用戶空間的udevd守護進程,接受到此信息。在udev規則文(wén)件裡匹配,相應的規則。