当变量在结构中的结构中时如何使用 container_of()?

How to use container_of() when the variable is in a struct within a struct?

我浏览了很多关于 container_of() 的问题,但没有找到询问如何使用 container_of() 在结构中检索结构的问题。如果这个问题重复,请指出。抱歉给您带来不便。

我目前正在实现一个 PCI 设备驱动程序 remove() 函数结合另一个内核模块,具有以下设置(我删除了一些其他字段以使这个问题易于阅读):

struct my_device {
    struct mutex lock;
    
    struct my_dev {
        struct cdev cdev;
    } my_dev;
    
    struct pci {
        struct cdev cdev;
        struct pci_dev *pdev;
    } pci;
};

问题来了:当我们在struct pci_dev *类型的struct pci中有一个指向pdev的指针时,我们如何得到struct pci,然后得到struct my_device ? 请注意,在 probe(struct pci_dev *dev, const struct pci_device_id *id) 中,我已将 dev 分配给先前分配的 struct my_device *dev_ptr,即 dev_ptr->pci.pdev = dev;

我尝试了以下方法:

struct pci *pci_t;
pci_t = container_of(ptr, struct pci, pdev)

但这没有用。

报错信息如下:

error: call to '__compiletime_assert_243' declared with attribute error: pointer type mismatch in container_of()

如有任何想法,我们将不胜感激。


更新:

我尝试了评论中的方法。由于pci驱动的remove()函数有一个参数——struct pci_dev *dev,代表对应的pci设备,所以我做了如下操作:

struct my_device *mydev;
mydev = container_of(&dev, struct my_device, pci.pdev);

但是它没有return指向正确结构的指针mydev

这是 container_of() 宏的简化定义:

#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))

offsetof(type, member) 宏如 C 标准中所述,给出了从某些结构类型 type 开始到该类型某些成员开始 member 的字节数]. ptr 应该 与指向成员类型的指针兼容(但上面的简化版本不关心)。 (char *)(ptr) 将指针转换为指向成员的字节指针,以便指针减法 (char *)(ptr) - offsetof(type, member) 生成指向包含结构类型开头的字节指针。最后,强制转换 (type *) 将该指针转换为正确的类型。

用法示例。鉴于:

struct my_device {
    struct mutex lock;
    
    struct my_dev {
        struct cdev cdev;
    } my_dev;
    
    struct pci {
        struct cdev cdev;
        struct pci_dev *pdev;
    } pci;
};

和指向已分配 struct my_device:

的指针
    struct my_device *mydev = kzalloc(sizeof(*mydev), GFP_KERNEL);

和指向 pci.pdev 成员的指针:

    struct pci_dev **ppdev = &mydev->pci.pdev;

然后可以使用 container_of():

将其转换回指向包含 struct my_device 的指针
    struct my_device *myd = container_of(ppdev, struct my_device, pci.pdev);

OP 有这样的内容:

    struct my_device *mydev = kzalloc(sizeof(*mydev), GFP_KERNEL);
    mydev->pci.pdev = pcidev;

并试图从指针 pcidev 返回到 struct my_device,如下所示:

    struct my_device *mydev = container_of(&pcidev, struct my_device, pci.pdev); /* wrong! */

这不起作用,因为 &pcidevpcidev 变量的地址,而不是原始 mydev->pci.pdev 成员的地址。


Linux 内核允许 PCI 设备驱动程序将任意 void * 值与 PCI 设备相关联。这通常在 PCI 设备驱动程序的 probe() 处理程序中完成,如下所示:

    pci_set_drvdata(pcidev, mydev);

然后直到 PCI 设备从驱动程序中删除,或者驱动程序使用其他值调用 pci_set_drvdata,可以通过调用 pci_get_drvdata():

来检索指针
    struct my_device *mydev = pci_get_drvdata(pcidev);

更一般地说,pci_set_drvdata()pci_get_drvdata() 只是 dev_set_drvdata()dev_get_drvdata() 的包装,将任意 void * 值与 [=47= 相关联]. struct pci_device 结构定义包含一个成员 struct device dev;pci_set_drvdata(pcidev, ptr)等同于dev_set_drvdata(&pcidev->dev, ptr)pci_get_drvdata(pcidev)等同于dev_get_drvdata(&pcidev->dev).

通常,驱动程序代码有一个指向 struct device 的指针,它知道它嵌入在 struct pci_dev 中,并且它可以使用 container_of() 获取指向包含 struct pci_dev:

/* dptr is pointing the the struct device inside a struct pci_dev. */
static void foo(struct device *dptr)
{
    struct pci_dev *pcidev = container_of(dptr, struct pci_dev, dev);
    /* ... */
}

如果驱动之前使用过pci_set_drvdata()dev_set_drvdata(),可以使用pci_get_drvdata()dev_get_drvdata()取回设定值:

static void foo(struct device *dptr)
{
    struct pci_dev *pcidev = container_of(dptr, struct pci_dev, dev);
    struct my_device *mydev = pci_get_drvdata(pcidev);
    struct my_device *mydev1 = dev_get_drvdata(dptr);
    /* (mydev == mydev1) is true */
    struct pci_dev *pcidev1 = mydev->pci.pdev;
    /* (pcidev == pcidev1) is true assuming mydev->pci.pdev was previously set to pcidev */
    /* ... */
}

尽管它有效,但将 pci_set_drvdata() 与之前对 dev_get_drvdata() 的调用混合使用或将 pci_get_drvdata() 与之前对 dev_set_drvdata() 的调用混合使用可能是不好的风格。