Linux内核 文档翻译汇总 下载本文

www.ourkernel.com 我们的内核

DEVICE_ATTR(power,0644,show_power,store_power);

上句声明了一个叫做\的设备属性结构体. 此结 构体可以用下列接口添加到设备或同设备删除:

代码:

int device_create_file(struct device *device, struct device_attribute * entry); void device_remove_file(struct device * dev, struct device_attribute * attr); 例:

代码:

device_create_file(dev,&dev_attr_power); device_remove_file(dev,&dev_attr_power);

文件名和模式将分别是\和0644(-rw-r--r--).

Devres

原文作者:Tejun Heo 翻 译者:Henry deargodzw@gmail.com 校 订者: 版本状态:已完成

=====================================

Devres - 设备资源管理 =====================

Tejun Heo

于2007年1月10号第一次起草

1.简介 : 嘿? Devres(设备资源)?

2.Devres(设备资源) : 果壳里的Devres(设备资源)

3.Devres(设备资源)组 : 聚集Devres(设备资源)和释放Devres(设备资源)

4.详细内容 : 生命周期规则,调用上下文, ...

5.费用 : 我们必须为此付出多大的代价?

6.管理级别的接口列表: 现有的已被实现的管理级别的接口

17

www.ourkernel.com 我们的内核

1.简介

------

当你正试着用IO映射来转化libata, devres(设备资源)就产生了。每个被IO映射的地址都应该被保存起来,而当驱动被卸载时都应该变成未映射的。举个例子来说,一个简单的SFF ATA控制器(就是那种很经典的PCI IDE)在本地模式需要使用五个PCI BARS,并且所有的PCI BARS都需要被维护。

就和大多数其他的设备驱动一样,在->remove和->probe失败路径里libata底层驱动也有很多的漏洞。是的,那也许是因为libata底层驱动开发者们都是一群懒家伙,但是是不是所有的底层开发者都是这样呢?在没有任何关于Braindamaged的文档的情况下,我花了一天的时间来折腾Braindamaged硬件,最后它成功的工作了。

出于某种原因,我们并没有像测试核心代码一样来测试底层驱动,并且一些在驱动卸载时发生的漏洞以及一些初始化失败也没能引起我们的注意。而关于初始化失败的路径则是更加糟糕,因为它需要应付多个入口点,更不用说完全遍历了。

所以,许多底层驱动都会在卸载时产生资源泄露,一半的失败原因都是在实现probe()时导致的,当它执行失败后就会产生泄露,也许还会产生更加意想不到的错误。IO映射添加了更多与probe()的结合,当然也包括msi和msix。

2. Devres(设备资源)

-------------------

设备资源从根本上说就是一个占着内存的由设备结构体连接而成的链表。每个设备资源入口都与一个释放资源的函数相关联。一个设备资源能通过多种方式来释放。不管是那一种方式,所有的设备资源入口都应该在驱动被卸载时而释放掉。释放资源时,相关联的释放函数就会被应用,并且设备资源入口也会被释放掉。

设备驱动使用资源时,管理级别接口就会被创建用来使用设备资源。举例来说,使用dma_alloc_coherent()来获得连续的DMA内存。这个管理级别的版本被称作dmam_alloc_coherent()。它和dma_alloc_coherent()功能大致一样,除了DMA内存分配时使用它来管理,并且在卸载时自动释放所分配的内存。实现看起来和下面相似: struct dma_devres{ size_t size; void *vaddr; dma_addr_t dma_handle; };

static void dmam_coherent_release(struct device *dev, void *res) {

struct dma_devres *this = res;

dma_free_coherent(dev, this->size, this->vaddr, this->dma_handle); }

18

www.ourkernel.com 我们的内核

dmam_alloc_coherent(dev, size, dma_handle, gfp) { struct dma_devres *dr;

/* alloc DMA memory as usual */ vaddr = dma_alloc_coherent(...); ...

dr = devres_alloc(dmam_coherent_release, sizeof(*dr), gfp); ...

void *vaddr;

/* record size, vaddr, dma_handle in dr */ dr->vaddr = vaddr;

return vaddr;

}

如果一个驱动使用dmam_alloc_coherent(), 那么被分配的区域都会保证被释放不管是否是中途初始化失败还是驱动被卸载。如果大多数的资源都是用管理级别的接口来获得,那么一个驱动在编写初始化和退出代码时就会显得更加简单。初始化路径看起来大概和下面很像:

my_init_one() { struct mydev *d;

...

devres_add(dev, dr);

d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM;

d->ring = dmam_alloc_coherent(...); if (!d->ring)

return -ENOMEM;

if (check something) ...

return register_to_upper_layer(d);

19

return -EINVAL;

www.ourkernel.com 我们的内核

}

退出时:

my_remove_one() {

unregister_from_upper_layer(d); shutdown_my_hardware(); }

就像上面写的那样,底层驱动在使用设备资源的时候更加简单化。在维护底层驱动时也会更加容易。当然,当初始化失败路径被退出路径所共享时,两者都需要更多的测试。

3. 设备资源组 -------------

设备资源入口可以用设备资源组来集中起来。当一个组被释放时,所有被包含的设备资源入口以及被嵌套的组都会被释放。一个习惯是当获取资源失败时就回滚到之前的状态。举例如下:

if (!devres_open_group(dev, NULL, GFP_KERNEL)) return -ENOMEM;

acquire A; if (failed) goto err;

acquire B; if (failed) goto err; ...

devres_remove_group(dev, NULL); return 0;

err:

devres_release_group(dev, NULL); return err_code;

通常当资源获取失败时就意味着probe失败,像上面的架构通常在中间层驱动(就像libata核心层)上是十分有用的,这样也不会在失败时引起所谓的边际效应。对于LLDs,在大多数例子中仅仅是返回错误代码就已经足够了。

20