本文將對Linux系統中的sysfs進行簡單的分析,要分析sysfs就必須分析內核的driver-model(驅動模型),兩者是緊密聯繫的。在分析過程中,本文將以platform總線和spi主控制器的platform驅動爲例來進行講解。其實,platform機制是基於driver-model的,通過本文,也會對platform機制有個簡單的瞭解。
內核版本:2.6.30
1. What is sysfs?
個人理解:sysfs向用戶空間展示了驅動設備的層次結構。我們都知道設備和對應的驅動都是由內核管理的,這些對於用戶空間是不可見的。現在通過sysfs,可以在用戶空間直觀的瞭解設備驅動的層次結構。
我們來看看sysfs的文件結構:
[root@yj423 /sys]#ls
block class devices fs module
bus dev firmware kernel power
block:塊設備
bus:系統中的總線
class: 設備類型,比如輸入設備
dev:系統中已註冊的設備節點的視圖,有兩個子目錄char和block。
devices:系統中所有設備拓撲結構視圖
fireware:固件
fs:文件系統
kernel:內核配置選項和狀態信息
module:模塊
power:系統的電源管理數據
2. kobject ,kset和ktype
要分析sysfs,首先就要分析kobject和kset,因爲驅動設備的層次結構的構成就是由這兩個東東來完成的。
2.1 kobject
kobject是一個對象的抽象,它用於管理對象。每個kobject對應着sysfs中的一個目錄。
kobject用struct kobject來描述。
- struct kobject {
- const char *name; /*在sysfs建立目錄的名字*/
- struct list_head entry; /*用於連接到所屬kset的鏈表中*/
- struct kobject *parent; /*父對象*/
- struct kset *kset; /*屬於哪個kset*/
- struct kobj_type *ktype; /*類型*/
- struct sysfs_dirent *sd; /*sysfs中與該對象對應的文件節點*/
- struct kref kref; /*對象的應用計數*/
- unsigned int state_initialized:1;
- unsigned int state_in_sysfs:1;
- unsigned int state_add_uevent_sent:1;
- unsigned int state_remove_uevent_sent:1;
- unsigned int uevent_suppress:1;
- };
2.2 kset
kset是一些kobject的集合,這些kobject可以有相同的ktype,也可以不同。同時,kset自己也包含一個kobject。在sysfs中,kset也是對應這一個目錄,但是目錄下面包含着其他的kojbect。
kset使用struct kset來描述。
- /**
- * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
- *
- * A kset defines a group of kobjects. They can be individually
- * different "types" but overall these kobjects all want to be grouped
- * together and operated on in the same manner. ksets are used to
- * define the attribute callbacks and other common events that happen to
- * a kobject.
- *
- * @list: the list of all kobjects for this kset
- * @list_lock: a lock for iterating over the kobjects
- * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
- * @uevent_ops: the set of uevent operations for this kset. These are
- * called whenever a kobject has something happen to it so that the kset
- * can add new environment variables, or filter out the uevents if so
- * desired.
- */
- struct kset {
- struct list_head list; /*屬於該kset的kobject鏈表*/
- spinlock_t list_lock;
- struct kobject kobj; /*該kset內嵌的kobj*/
- struct kset_uevent_ops *uevent_ops;
- };
2.3 ktype
每個kobject對象都內嵌有一個ktype,該結構定義了kobject在創建和刪除時所採取的行爲。
- struct kobj_type {
- void (*release)(struct kobject *kobj);
- struct sysfs_ops *sysfs_ops;
- struct attribute **default_attrs;
- };
- struct sysfs_ops {
- ssize_t (*show)(struct kobject *, struct attribute *,char *);
- ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
- };
- /* FIXME
- * The *owner field is no longer used.
- * x86 tree has been cleaned up. The owner
- * attribute is still left for other arches.
- */
- struct attribute {
- const char *name;
- struct module *owner;
- mode_t mode;
- };
attribute爲屬性,每個屬性在sysfs中都有對應的屬性文件。
sysfs_op的兩個方法用於實現讀取和寫入屬性文件時應該採取的行爲。
2.4 kobject與kset的關係
下面這張圖非常經典。最下面的kobj都屬於一個kset,同時這些kobj的父對象就是kset內嵌的kobj。通過鏈表,kset可以獲取所有屬於它的kobj。
從sysfs角度而言,kset代表一個文件夾,而下面的kobj就是這個文件夾裏面的內容,而內容有可能是文件也有可能是文件夾。
3.舉例
在上一節中,我們知道sys下有一個bus目錄,這一將分析如何通過kobject創建bus目錄。
下面代碼位於drivers/base/bus.c
- int __init buses_init(void)
- {
- bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
- if (!bus_kset)
- return -ENOMEM;
- return 0;
- }
- static struct kset_uevent_ops bus_uevent_ops = {
- .filter = bus_uevent_filter,
- };
- static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
- {
- struct kobj_type *ktype = get_ktype(kobj);
- if (ktype == &bus_ktype)
- return 1;
- return 0;
- }
下面代碼位於drivers/base/kobject.c
- /**
- * kset_create_and_add - create a struct kset dynamically and add it to sysfs
- *
- * @name: the name for the kset
- * @uevent_ops: a struct kset_uevent_ops for the kset
- * @parent_kobj: the parent kobject of this kset, if any.
- *
- * This function creates a kset structure dynamically and registers it
- * with sysfs. When you are finished with this structure, call
- * kset_unregister() and the structure will be dynamically freed when it
- * is no longer being used.
- *
- * If the kset was not able to be created, NULL will be returned.
- */
- struct kset *kset_create_and_add(const char *name,
- struct kset_uevent_ops *uevent_ops,
- struct kobject *parent_kobj)
- {
- struct kset *kset;
- int error;
- kset = kset_create(name, uevent_ops, parent_kobj); /*建立kset,設置某些字段*/
- if (!kset)
- return NULL;
- error = kset_register(kset); /*添加kset到sysfs*/
- if (error) {
- kfree(kset);
- return NULL;
- }
- return kset;
- }
這裏主要調用了兩個函數,接下分別來看下。
3.1 kset_create函數
下面代碼位於drivers/base/kobject.c
- /**
- * kset_create - create a struct kset dynamically
- *
- * @name: the name for the kset
- * @uevent_ops: a struct kset_uevent_ops for the kset
- * @parent_kobj: the parent kobject of this kset, if any.
- *
- * This function creates a kset structure dynamically. This structure can
- * then be registered with the system and show up in sysfs with a call to
- * kset_register(). When you are finished with this structure, if
- * kset_register() has been called, call kset_unregister() and the
- * structure will be dynamically freed when it is no longer being used.
- *
- * If the kset was not able to be created, NULL will be returned.
- */
- static struct kset *kset_create(const char *name,
- struct kset_uevent_ops *uevent_ops,
- struct kobject *parent_kobj)
- {
- struct kset *kset;
- kset = kzalloc(sizeof(*kset), GFP_KERNEL);/*分配kset*/
- if (!kset)
- return NULL;
- kobject_set_name(&kset->kobj, name);/*設置kobj->name*/
- kset->uevent_ops = uevent_ops;
- kset->kobj.parent = parent_kobj; /*設置父對象*/
- /*
- * The kobject of this kset will have a type of kset_ktype and belong to
- * no kset itself. That way we can properly free it when it is
- * finished being used.
- */
- kset->kobj.ktype = &kset_ktype;
- kset->kobj.kset = NULL; /*本keset不屬於任何kset*/
- return kset;
- }
這個函數中,動態分配了kset結構,調用kobject_set_name設置kset->kobj->name爲bus,也就是我們要創建的目錄bus。同時這裏kset->kobj.parent爲NULL,
也就是沒有父對象。因爲要創建的bus目錄是在sysfs所在的根目錄創建的,自然沒有父對象。
隨後簡要看下由kobject_set_name函數調用引發的一系列調用。
- /**
- * kobject_set_name - Set the name of a kobject
- * @kobj: struct kobject to set the name of
- * @fmt: format string used to build the name
- *
- * This sets the name of the kobject. If you have already added the
- * kobject to the system, you must call kobject_rename() in order to
- * change the name of the kobject.
- */
- int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
- {
- va_list vargs;
- int retval;
- va_start(vargs, fmt);
- retval = kobject_set_name_vargs(kobj, fmt, vargs);
- va_end(vargs);
- return retval;
- }
- /**
- * kobject_set_name_vargs - Set the name of an kobject
- * @kobj: struct kobject to set the name of
- * @fmt: format string used to build the name
- * @vargs: vargs to format the string.
- */
- int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
- va_list vargs)
- {
- const char *old_name = kobj->name;
- char *s;
- if (kobj->name && !fmt)
- return 0;
- kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
- if (!kobj->name)
- return -ENOMEM;
- /* ewww... some of these buggers have '/' in the name ... */
- while ((s = strchr(kobj->name, '/')))
- s[0] = '!';
- kfree(old_name);
- return 0;
- }
- /* Simplified asprintf. */
- char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
- {
- unsigned int len;
- char *p;
- va_list aq;
- va_copy(aq, ap);
- len = vsnprintf(NULL, 0, fmt, aq);
- va_end(aq);
- p = kmalloc(len+1, gfp);
- if (!p)
- return NULL;
- vsnprintf(p, len+1, fmt, ap);
- return p;
- }
3.2 kset_register
下面代碼位於drivers/base/kobject.c。
- /**
- * kset_register - initialize and add a kset.
- * @k: kset.
- */
- int kset_register(struct kset *k)
- {
- int err;
- if (!k)
- return -EINVAL;
- kset_init(k); /*初始化kset*/
- err = kobject_add_internal(&k->kobj); /*在sysfs中建立目錄*/
- if (err)
- return err;
- kobject_uevent(&k->kobj, KOBJ_ADD);
- return 0;
- }
這裏面調用了3個函數。這裏先介紹前兩個函數。
3.2.1 kset_init
該函數用於初始化kset。
下面代碼位於drivers/base/kobject.c。
- /**
- * kset_init - initialize a kset for use
- * @k: kset
- */
- void kset_init(struct kset *k)
- {
- kobject_init_internal(&k->kobj);/*初始化kobject的某些字段*/
- INIT_LIST_HEAD(&k->list); /*初始化鏈表頭*/
- spin_lock_init(&k->list_lock); /*初始化自旋鎖*/
- }
- static void kobject_init_internal(struct kobject *kobj)
- {
- if (!kobj)
- return;
- kref_init(&kobj->kref); /*初始化引用基計數*/
- INIT_LIST_HEAD(&kobj->entry); /*初始化鏈表頭*/
- kobj->state_in_sysfs = 0;
- kobj->state_add_uevent_sent = 0;
- kobj->state_remove_uevent_sent = 0;
- kobj->state_initialized = 1;
- }
3.2.2 kobject_add_internal
該函數將在sysfs中建立目錄。
下面代碼位於drivers/base/kobject.c。
- static int kobject_add_internal(struct kobject *kobj)
- {
- int error = 0;
- struct kobject *parent;
- if (!kobj)
- return -ENOENT;
- /*檢查name字段是否存在*/
- if (!kobj->name || !kobj->name[0]) {
- WARN(1, "kobject: (%p): attempted to be registered with empty "
- "name!\n", kobj);
- return -EINVAL;
- }
- parent = kobject_get(kobj->parent); /*有父對象則增加父對象引用計數*/
- /* join kset if set, use it as parent if we do not already have one */
- if (kobj->kset) {
- if (!parent)
- /*kobj屬於某個kset,但是該kobj沒有父對象,則以kset的kobj作爲父對象*/
- parent = kobject_get(&kobj->kset->kobj);
- kobj_kset_join(kobj); /*將kojbect添加到kset結構中的鏈表當中*/
- kobj->parent = parent;
- }
- pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
- kobject_name(kobj), kobj, __func__,
- parent ? kobject_name(parent) : "<NULL>",
- kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
- error = create_dir(kobj); /*根據kobj->name在sys中建立目錄*/
- if (error) {
- kobj_kset_leave(kobj); /*刪除鏈表項*/
- kobject_put(parent); /*減少引用計數*/
- kobj->parent = NULL;
- /* be noisy on error issues */
- if (error == -EEXIST)
- printk(KERN_ERR "%s failed for %s with "
- "-EEXIST, don't try to register things with "
- "the same name in the same directory.\n",
- __func__, kobject_name(kobj));
- else
- printk(KERN_ERR "%s failed for %s (%d)\n",
- __func__, kobject_name(kobj), error);
- dump_stack();
- } else
- kobj->state_in_sysfs = 1;
- return error;
- }
在上面的kset_create中有kset->kobj.kset = NULL,因此if (kobj->kset)條件不滿足。因此在這個函數中,對name進行了必要的檢查之後,調用了create_dir在sysfs中創建目錄。
在create_dir執行完成以後會在sysfs的根目錄(/sys/)建立文件夾bus。該函數的詳細分析將在後面給出。
至此,對bus目錄的建立有了簡單而直觀的瞭解。我們可以看出kset其實就是表示一個文件夾,而kset本身也含有一個kobject,而該kobject的name字段即爲該目錄的名字,本例中爲bus。
4. driver model
第2節所介紹的是最底層,最核心的內容。下面開始將描述較爲高層的內容。
Linux設備模型使用了三個數據結構分別來描述總線、設備和驅動。所有的設備和對應的驅動都必須掛載在某一個總線上,通過總線,可以綁定設備和驅動。
這個屬於分離的思想,將設備和驅動分開管理。
同時驅動程序可以瞭解到所有它所支持的設備,同樣的,設備也能知道它對應驅動程序。
4.1 bus
總線是處理器與一個設備或者多個設備之間的通道。在設備模型中,所有的設備都掛載在某一個總線上。總線使用struct bus_type來表述。
下列代碼位於include/linux/device.h。
- <strong><span style="font-family:System;font-size:12px;">struct bus_type {
- const char *name;
- struct bus_attribute *bus_attrs;
- struct device_attribute *dev_attrs;
- struct driver_attribute *drv_attrs;
- int (*match)(struct device *dev, struct device_driver *drv);
- int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
- int (*probe)(struct device *dev);
- int (*remove)(struct device *dev);
- void (*shutdown)(struct device *dev);
- int (*suspend)(struct device *dev, pm_message_t state);
- int (*suspend_late)(struct device *dev, pm_message_t state);
- int (*resume_early)(struct device *dev);
- int (*resume)(struct device *dev);
- struct dev_pm_ops *pm;
- struct bus_type_private *p;
- };
- /**
- * struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure.
- *
- * @subsys - the struct kset that defines this bus. This is the main kobject
- * @drivers_kset - the list of drivers associated with this bus
- * @devices_kset - the list of devices associated with this bus
- * @klist_devices - the klist to iterate over the @devices_kset
- * @klist_drivers - the klist to iterate over the @drivers_kset
- * @bus_notifier - the bus notifier list for anything that cares about things
- * on this bus.
- * @bus - pointer back to the struct bus_type that this structure is associated
- * with.
- *
- * This structure is the one that is the actual kobject allowing struct
- * bus_type to be statically allocated safely. Nothing outside of the driver
- * core should ever touch these fields.
- */
- struct bus_type_private {
- struct kset subsys;
- struct kset *drivers_kset;
- struct kset *devices_kset;
- struct klist klist_devices;
- struct klist klist_drivers;
- struct blocking_notifier_head bus_notifier;
- unsigned int drivers_autoprobe:1;
- struct bus_type *bus;
- };</span></strong>
drivers_kset和devices_kset對應着兩個目錄,該兩個目錄下將包含該總線上的設備和相應的驅動程序。
同時總線上的設備和驅動將分別保存在兩個鏈表中:klist_devices和klist_drivers。
4.2 device
設備對象在driver-model中使用struct device來表示。
下列代碼位於include/linux/device.h。- struct device {
- struct device *parent;
- struct device_private *p;
- struct kobject kobj;
- const char *init_name; /* initial name of the device */
- struct device_type *type;
- struct semaphore sem; /* semaphore to synchronize calls to
- * its driver.
- */
- struct bus_type *bus; /* type of bus device is on */
- struct device_driver *driver; /* which driver has allocated this
- device */
- void *driver_data; /* data private to the driver */
- void *platform_data; /* Platform specific data, device
- core doesn't touch it */
- struct dev_pm_info power;
- #ifdef CONFIG_NUMA
- int numa_node; /* NUMA node this device is close to */
- #endif
- u64 *dma_mask; /* dma mask (if dma'able device) */
- u64 coherent_dma_mask;/* Like dma_mask, but for
- alloc_coherent mappings as
- not all hardware supports
- 64 bit addresses for consistent
- allocations such descriptors. */
- struct device_dma_parameters *dma_parms;
- struct list_head dma_pools; /* dma pools (if dma'ble) */
- struct dma_coherent_mem *dma_mem; /* internal for coherent mem
- override */
- /* arch specific additions */
- struct dev_archdata archdata;
- dev_t devt; /* dev_t, creates the sysfs "dev" */
- spinlock_t devres_lock;
- struct list_head devres_head;
- struct klist_node knode_class;
- struct class *class;
- struct attribute_group **groups; /* optional groups */
- void (*release)(struct device *dev);
- };
- /**
- * struct device_private - structure to hold the private to the driver core portions of the device structure.
- *
- * @klist_children - klist containing all children of this device
- * @knode_parent - node in sibling list
- * @knode_driver - node in driver list
- * @knode_bus - node in bus list
- * @device - pointer back to the struct class that this structure is
- * associated with.
- *
- * Nothing outside of the driver core should ever touch these fields.
- */
- struct device_private {
- struct klist klist_children;
- struct klist_node knode_parent;
- struct klist_node knode_driver;
- struct klist_node knode_bus;
- struct device *device;
- };
device本身包含一個kobject,也就是說這個device在sysfs的某個地方有着一個對應的目錄。
該device所掛載的bus由knode_bus指定。
該device所對應的設備驅動由knode_driver指定。
4.3 driver
設備設備對象在driver-model中使用struct device_driver來表示。
下列代碼位於include/linux/device.h。
- struct device_driver {
- const char *name;
- struct bus_type *bus;
- struct module *owner;
- const char *mod_name; /* used for built-in modules */
- int (*probe) (struct device *dev);
- int (*remove) (struct device *dev);
- void (*shutdown) (struct device *dev);
- int (*suspend) (struct device *dev, pm_message_t state);
- int (*resume) (struct device *dev);
- struct attribute_group **groups;
- struct dev_pm_ops *pm;
- struct driver_private *p;
- };
- struct driver_private {
- struct kobject kobj;
- struct klist klist_devices;
- struct klist_node knode_bus;
- struct module_kobject *mkobj;
- struct device_driver *driver;
- };
該設備驅動所支持的設備由klist_devices指定。
該設備驅動所掛載的總線由knode_bus制定。
5. Bus舉例
本節我們將以platform總線爲例,來看看,/sys/bus/platform是如何建立的。
platform總線的註冊是由platform_bus_init函數完成的。該函數在內核啓動階段被調用,我們來簡單看下調用過程:
start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。
注:kernel_init()是在rest_init函數中創建內核線程來執行的。
- int __init platform_bus_init(void)
- {
- int error;
- early_platform_cleanup();
- error = device_register(&platform_bus);
- if (error)
- return error;
- error = bus_register(&platform_bus_type);
- if (error)
- device_unregister(&platform_bus);
- return error;
- }
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = PLATFORM_PM_OPS_PTR,
- };
- EXPORT_SYMBOL_GPL(platform_bus_type);
調用了兩個函數,我們只關注bus_register函數。
- /**
- * bus_register - register a bus with the system.
- * @bus: bus.
- *
- * Once we have that, we registered the bus with the kobject
- * infrastructure, then register the children subsystems it has:
- * the devices and drivers that belong to the bus.
- */
- int bus_register(struct bus_type *bus)
- {
- int retval;
- struct bus_type_private *priv;
- priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- /*互相保存*/
- priv->bus = bus;
- bus->p = priv;
- BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
- /*設定kobject->name*/
- retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
- if (retval)
- goto out;
- priv->subsys.kobj.kset = bus_kset;
- priv->subsys.kobj.ktype = &bus_ktype;
- priv->drivers_autoprobe = 1;
- /*註冊kset,在bus/建立目錄XXX,XXX爲bus->name*/
- retval = kset_register(&priv->subsys);
- if (retval)
- goto out;
- /*創建屬性,在bus/XXX/建立文件uevent*/
- retval = bus_create_file(bus, &bus_attr_uevent);
- if (retval)
- goto bus_uevent_fail;
- /*創建kset,在bus/XXX/建立目錄devices*/
- priv->devices_kset = kset_create_and_add("devices", NULL,
- &priv->subsys.kobj);
- if (!priv->devices_kset) {
- retval = -ENOMEM;
- goto bus_devices_fail;
- }
- /*創建kset,在bus/XXX/建立目錄drivers*/
- priv->drivers_kset = kset_create_and_add("drivers", NULL,
- &priv->subsys.kobj);
- if (!priv->drivers_kset) {
- retval = -ENOMEM;
- goto bus_drivers_fail;
- }
- /*初始化2個內核鏈表,*/
- klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
- klist_init(&priv->klist_drivers, NULL, NULL);
- /*創建屬性,在bus/XXX/建立文件drivers_autoprobe和drivers_probe*/
- retval = add_probe_files(bus);
- if (retval)
- goto bus_probe_files_fail;
- /*根據bus->bus_attribute創建屬性,在bus/XXX/下建立相應的文件d*/
- retval = bus_add_attrs(bus);
- if (retval)
- goto bus_attrs_fail;
- pr_debug("bus: '%s': registered\n", bus->name);
- return 0;
- bus_attrs_fail:
- remove_probe_files(bus);
- bus_probe_files_fail:
- kset_unregister(bus->p->drivers_kset);
- bus_drivers_fail:
- kset_unregister(bus->p->devices_kset);
- bus_devices_fail:
- bus_remove_file(bus, &bus_attr_uevent);
- bus_uevent_fail:
- kset_unregister(&bus->p->subsys);
- kfree(bus->p);
- out:
- bus->p = NULL;
- return retval;
- }
- EXPORT_SYMBOL_GPL(bus_register);
函數中,首先調用kobject_set_name設置了bus對象的subsys.kobject->name 爲 platform,也就是說會建立一個名爲platform的目錄。kobject_set_name函數在3.1小節中已經給出。
在這裏還用到了bus_kset這個變量,這個變量就是在第3節buses_init函數中建立bus目錄所對應的kset對象。
接着,priv->subsys.kobj.kset = bus_kset,設置subsys的kobj在bus_kset對象包含的集合中,也就是說bus目錄下將包含subsys對象所對應的目錄,即platform。
緊接着調用了kset_register,參數爲&priv->subsys。該函數在3.2節中以給出。在該函數的調用過程中,將調用kobj_kset_join函數,該函數將kobject添加到kobject->kset的鏈表中。
- /* add the kobject to its kset's list */
- static void kobj_kset_join(struct kobject *kobj)
- {
- if (!kobj->kset)
- return;
- kset_get(kobj->kset); /*增加kset引用計數*/
- spin_lock(&kobj->kset->list_lock);
- list_add_tail(&kobj->entry, &kobj->kset->list); /*將kojbect添加到kset結構中的鏈表當中*/
- spin_unlock(&kobj->kset->list_lock);
- }
然後,調用了bus_create_file函數在/sys/bus/platform/下建立文件uevent。
- int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
- {
- int error;
- if (bus_get(bus)) {
- error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
- bus_put(bus);
- } else
- error = -EINVAL;
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_create_file);
接着調用了2次kset_create_and_add,分別在/sys/bus/platform/下建立了文件夾devices和drivers。該函數位於第3節開始處。
這裏和第3節調用kset_create_and_add時的最主要一個區別就是:此時的parent參數不爲NULL,而是&priv->subsys.kobj。
也就是說,將要創建的kset的kobject->parent = &priv->subsys.kobj,也即新建的kset被包含在platform文件夾對應的kset中。
我們來看下關係圖:
隨後,調用了add_probe_files創建了屬性文件drivers_autoprobe和drivers_probe。
- static int add_probe_files(struct bus_type *bus)
- {
- int retval;
- retval = bus_create_file(bus, &bus_attr_drivers_probe);
- if (retval)
- goto out;
- retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
- if (retval)
- bus_remove_file(bus, &bus_attr_drivers_probe);
- out:
- return retval;
- }
最後調用bus_add_attrs創建總線相關的屬性文件。
- /**
- * bus_add_attrs - Add default attributes for this bus.
- * @bus: Bus that has just been registered.
- */
- static int bus_add_attrs(struct bus_type *bus)
- {
- int error = 0;
- int i;
- if (bus->bus_attrs) {
- for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
- error = bus_create_file(bus, &bus->bus_attrs[i]);
- if (error)
- goto err;
- }
- }
- done:
- return error;
- err:
- while (--i >= 0)
- bus_remove_file(bus, &bus->bus_attrs[i]);
- goto done;
- }
好了,整個bus_register調用完成了,我們來看下sysfs中實際的情況。
[root@yj423 platform]#pwd
/sys/bus/platform
[root@yj423 platform]#ls
devices drivers drivers_autoprobe drivers_probe uevent
最後,我們對整個bus_register的過程進行一個小結。
6. device舉例
本節將首先講述如何在/sys/devices下建立虛擬的platform設備,然後再講述如何在/sys/devices/platform/下建立子設備。
6.1 虛擬的platform設備
之所以叫虛擬是因爲這個platform並不代表任何實際存在的設備,但是platform將是所有具體設備的父設備。
在第5節,platform_bus_init函數中還調用了device_register,現在對其做出分析。
- int __init platform_bus_init(void)
- {
- int error;
- early_platform_cleanup();
- error = device_register(&platform_bus);
- if (error)
- return error;
- error = bus_register(&platform_bus_type);
- if (error)
- device_unregister(&platform_bus);
- return error;
- }
- struct device platform_bus = {
- .init_name = "platform",
- };
- EXPORT_SYMBOL_GPL(platform_bus)
- /**
- * device_register - register a device with the system.
- * @dev: pointer to the device structure
- *
- * This happens in two clean steps - initialize the device
- * and add it to the system. The two steps can be called
- * separately, but this is the easiest and most common.
- * I.e. you should only call the two helpers separately if
- * have a clearly defined need to use and refcount the device
- * before it is added to the hierarchy.
- *
- * NOTE: _Never_ directly free @dev after calling this function, even
- * if it returned an error! Always use put_device() to give up the
- * reference initialized in this function instead.
- */
- int device_register(struct device *dev)
- {
- device_initialize(dev); /*初始化dev的某些字段*/
- return device_add(dev); /*將設備添加到系統中*/
- }
一個設備的註冊分成兩部,每步通過調用一個函數函數。首先先看第一步:
下列函數位於drivers/base/core.c。
- /**
- * device_initialize - init device structure.
- * @dev: device.
- *
- * This prepares the device for use by other layers by initializing
- * its fields.
- * It is the first half of device_register(), if called by
- * that function, though it can also be called separately, so one
- * may use @dev's fields. In particular, get_device()/put_device()
- * may be used for reference counting of @dev after calling this
- * function.
- *
- * NOTE: Use put_device() to give up your reference instead of freeing
- * @dev directly once you have called this function.
- */
- void device_initialize(struct device *dev)
- {
- dev->kobj.kset = devices_kset; /*設置kobj屬於哪個kset,/sys/devices/*/
- kobject_init(&dev->kobj, &device_ktype);/*初始化dev->kobj*/
- INIT_LIST_HEAD(&dev->dma_pools); /*初始化鏈表頭*/
- init_MUTEX(&dev->sem); /*初始化互斥體*/
- spin_lock_init(&dev->devres_lock); /*初始化自旋鎖*/
- INIT_LIST_HEAD(&dev->devres_head); /*初始化鏈表頭*/
- device_init_wakeup(dev, 0); /*設置該device不能喚醒*/
- device_pm_init(dev); /*設置該device可操作*/
- set_dev_node(dev, -1); /*設置NUMA節點*/
- }
6.1.1 有關devices_kset
首先其中用到了devices_kset對象,這個對象和第3節當中的bus_kset是同樣的性質,也就是說該對象表示一個目錄。
該對象的建立是在devices_init函數中完成的。
- int __init devices_init(void)
- {
- devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
- if (!devices_kset)
- return -ENOMEM;
- dev_kobj = kobject_create_and_add("dev", NULL);
- if (!dev_kobj)
- goto dev_kobj_err;
- sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
- if (!sysfs_dev_block_kobj)
- goto block_kobj_err;
- sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
- if (!sysfs_dev_char_kobj)
- goto char_kobj_err;
- return 0;
- char_kobj_err:
- kobject_put(sysfs_dev_block_kobj);
- block_kobj_err:
- kobject_put(dev_kobj);
- dev_kobj_err:
- kset_unregister(devices_kset);
- return -ENOMEM;
- }
6.1.2 kobject_init
下列函數位於lib/kojbect.c。
- /**
- * kobject_init - initialize a kobject structure
- * @kobj: pointer to the kobject to initialize
- * @ktype: pointer to the ktype for this kobject.
- *
- * This function will properly initialize a kobject such that it can then
- * be passed to the kobject_add() call.
- *
- * After this function is called, the kobject MUST be cleaned up by a call
- * to kobject_put(), not by a call to kfree directly to ensure that all of
- * the memory is cleaned up properly.
- */
- void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
- {
- char *err_str;
- if (!kobj) {
- err_str = "invalid kobject pointer!";
- goto error;
- }
- if (!ktype) {
- err_str = "must have a ktype to be initialized properly!\n";
- goto error;
- }
- if (kobj->state_initialized) {
- /* do not error out as sometimes we can recover */
- printk(KERN_ERR "kobject (%p): tried to init an initialized "
- "object, something is seriously wrong.\n", kobj);
- dump_stack();
- }
- kobject_init_internal(kobj);
- kobj->ktype = ktype;
- return;
- error:
- printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
- dump_stack();
- }
- EXPORT_SYMBOL(kobject_init);
- static void kobject_init_internal(struct kobject *kobj)
- {
- if (!kobj)
- return;
- kref_init(&kobj->kref); /*初始化引用基計數*/
- INIT_LIST_HEAD(&kobj->entry); /*初始化鏈表頭*/
- kobj->state_in_sysfs = 0;
- kobj->state_add_uevent_sent = 0;
- kobj->state_remove_uevent_sent = 0;
- kobj->state_initialized = 1;
- }
6.1.3 device_init_wakeup
參數val爲0,設置該device不能夠喚醒。
- #ifdef CONFIG_PM
- /* changes to device_may_wakeup take effect on the next pm state change.
- * by default, devices should wakeup if they can.
- */
- static inline void device_init_wakeup(struct device *dev, int val)
- {
- dev->power.can_wakeup = dev->power.should_wakeup = !!val;
- }
- 。。。。。。
- #else /* !CONFIG_PM */
- /* For some reason the next two routines work even without CONFIG_PM */
- static inline void device_init_wakeup(struct device *dev, int val)
- {
- dev->power.can_wakeup = !!val;
- }
- 。。。。。。
- #endif
6.1.4 device_pm_init
設置電源的狀態。
- static inline void device_pm_init(struct device *dev)
- {
- dev->power.status = DPM_ON; /*該device被認爲可操作*/
- }
6.1.5 set_dev_node
如果使用NUMA,則設置NUMA節點。
- #ifdef CONFIG_NUMA
- 。。。。。。
- static inline void set_dev_node(struct device *dev, int node)
- {
- dev->numa_node = node;
- }
- #else
- 。。。。。。
- static inline void set_dev_node(struct device *dev, int node)
- {
- }
- #endif
6.2 device_add
接下來是註冊的第二步:調用device_add。
- /**
- * device_add - add device to device hierarchy.
- * @dev: device.
- *
- * This is part 2 of device_register(), though may be called
- * separately _iff_ device_initialize() has been called separately.
- *
- * This adds @dev to the kobject hierarchy via kobject_add(), adds it
- * to the global and sibling lists for the device, then
- * adds it to the other relevant subsystems of the driver model.
- *
- * NOTE: _Never_ directly free @dev after calling this function, even
- * if it returned an error! Always use put_device() to give up your
- * reference instead.
- */
- int device_add(struct device *dev)
- {
- struct device *parent = NULL;
- struct class_interface *class_intf;
- int error = -EINVAL;
- dev = get_device(dev); /*增加引用計數*/
- if (!dev)
- goto done;
- dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); /*分配device_private結構*/
- if (!dev->p) {
- error = -ENOMEM;
- goto done;
- }
- dev->p->device = dev; /*保存dev*/
- klist_init(&dev->p->klist_children, klist_children_get, /*初始化內核鏈表*/
- klist_children_put);
- /*
- * for statically allocated devices, which should all be converted
- * some day, we need to initialize the name. We prevent reading back
- * the name, and force the use of dev_name()
- */
- if (dev->init_name) {
- dev_set_name(dev, dev->init_name); /*dev->kobject->name = dev->init_name*/
- dev->init_name = NULL;
- }
- if (!dev_name(dev)) /*檢查dev->kobject->name*/
- goto name_error;
- pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
- parent = get_device(dev->parent); /*增加父設備引用計數*/
- setup_parent(dev, parent); /*設置dev->kobject->parent*/
- /* use parent numa_node */
- if (parent)
- set_dev_node(dev, dev_to_node(parent));
- /* first, register with generic layer. */
- /* we require the name to be set before, and pass NULL */
- /* 執行完以後,將在/sys/devices/下建立目錄XXX,目錄名XXX爲dev->kobj->name*/
- error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
- if (error)
- goto Error;
- /* notify platform of device entry */
- if (platform_notify)
- platform_notify(dev);
- /*在XXX下建立文件uevent*/
- error = device_create_file(dev, &uevent_attr);
- if (error)
- goto attrError;
- if (MAJOR(dev->devt)) {/*主設備號不爲0*/
- error = device_create_file(dev, &devt_attr);/*創建屬性文件dev*/
- if (error)
- goto ueventattrError;
- /* 在sys/dev/char/下建立symlink,名字爲主設備號:次設備號,該鏈接指向XXX */
- error = device_create_sys_dev_entry(dev);
- if (error)
- goto devtattrError;
- }
- error = device_add_class_symlinks(dev);
- if (error)
- goto SymlinkError;
- error = device_add_attrs(dev); /*添加類設備屬型文件和屬性組*/
- if (error)
- goto AttrsError;
- error = bus_add_device(dev); /*添加3個symlink*/
- if (error)
- goto BusError;
- error = dpm_sysfs_add(dev); /*創建power子目錄,並在其下添加電源管理的屬性組文件*/
- if (error)
- goto DPMError;
- device_pm_add(dev); /*將該device添加到電源管理鏈表中*/
- /* Notify clients of device addition. This call must come
- * after dpm_sysf_add() and before kobject_uevent().
- */
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_ADD_DEVICE, dev);
- kobject_uevent(&dev->kobj, KOBJ_ADD); /*通知用戶層*/
- bus_attach_device(dev); /*將設備添加到總線的設備鏈表中,並嘗試獲取驅動*/
- if (parent)
- klist_add_tail(&dev->p->knode_parent, /*有父設備,則將該設備添加到父設備的兒子鏈表中*/
- &parent->p->klist_children);
- if (dev->class) { /*該設備屬於某個設備類*/
- mutex_lock(&dev->class->p->class_mutex);
- /* tie the class to the device */
- klist_add_tail(&dev->knode_class, /*將device添加到class的類設備鏈表中*/
- &dev->class->p->class_devices);
- /* notify any interfaces that the device is here */
- list_for_each_entry(class_intf,
- &dev->class->p->class_interfaces, node)
- if (class_intf->add_dev)
- class_intf->add_dev(dev, class_intf);
- mutex_unlock(&dev->class->p->class_mutex);
- }
- done:
- put_device(dev);
- return error;
- DPMError:
- bus_remove_device(dev);
- BusError:
- device_remove_attrs(dev);
- AttrsError:
- device_remove_class_symlinks(dev);
- SymlinkError:
- if (MAJOR(dev->devt))
- device_remove_sys_dev_entry(dev);
- devtattrError:
- if (MAJOR(dev->devt))
- device_remove_file(dev, &devt_attr);
- ueventattrError:
- device_remove_file(dev, &uevent_attr);
- attrError:
- kobject_uevent(&dev->kobj, KOBJ_REMOVE);
- kobject_del(&dev->kobj);
- Error:
- cleanup_device_parent(dev);
- if (parent)
- put_device(parent);
- name_error:
- kfree(dev->p);
- dev->p = NULL;
- goto done;
- }
該函數調用了非常多的其他函數,接下來對主要的函數做出分析。
6.2.1 setup_parent函數
下列代碼位於drivers/base/core.c。
- static void setup_parent(struct device *dev, struct device *parent)
- {
- struct kobject *kobj;
- kobj = get_device_parent(dev, parent);
- if (kobj)
- dev->kobj.parent = kobj;
- }
- static struct kobject *get_device_parent(struct device *dev,
- struct device *parent)
- {
- /* class devices without a parent live in /sys/class/<classname>/ */
- if (dev->class && (!parent || parent->class != dev->class))
- return &dev->class->p->class_subsys.kobj;
- /* all other devices keep their parent */
- else if (parent)
- return &parent->kobj;
- return NULL;
- }
6.2.2 kobject_add函數
下列代碼位於lib/kobject.c。
- /**
- * kobject_add - the main kobject add function
- * @kobj: the kobject to add
- * @parent: pointer to the parent of the kobject.
- * @fmt: format to name the kobject with.
- *
- * The kobject name is set and added to the kobject hierarchy in this
- * function.
- *
- * If @parent is set, then the parent of the @kobj will be set to it.
- * If @parent is NULL, then the parent of the @kobj will be set to the
- * kobject associted with the kset assigned to this kobject. If no kset
- * is assigned to the kobject, then the kobject will be located in the
- * root of the sysfs tree.
- *
- * If this function returns an error, kobject_put() must be called to
- * properly clean up the memory associated with the object.
- * Under no instance should the kobject that is passed to this function
- * be directly freed with a call to kfree(), that can leak memory.
- *
- * Note, no "add" uevent will be created with this call, the caller should set
- * up all of the necessary sysfs files for the object and then call
- * kobject_uevent() with the UEVENT_ADD parameter to ensure that
- * userspace is properly notified of this kobject's creation.
- */
- int kobject_add(struct kobject *kobj, struct kobject *parent,
- const char *fmt, ...)
- {
- va_list args;
- int retval;
- if (!kobj)
- return -EINVAL;
- if (!kobj->state_initialized) {
- printk(KERN_ERR "kobject '%s' (%p): tried to add an "
- "uninitialized object, something is seriously wrong.\n",
- kobject_name(kobj), kobj);
- dump_stack();
- return -EINVAL;
- }
- va_start(args, fmt);
- retval = kobject_add_varg(kobj, parent, fmt, args);
- va_end(args);
- return retval;
- }
- EXPORT_SYMBOL(kobject_add);
- static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
- const char *fmt, va_list vargs)
- {
- int retval;
- retval = kobject_set_name_vargs(kobj, fmt, vargs);
- if (retval) {
- printk(KERN_ERR "kobject: can not set name properly!\n");
- return retval;
- }
- kobj->parent = parent;
- return kobject_add_internal(kobj);
- }
- static int kobject_add_internal(struct kobject *kobj)
- {
- int error = 0;
- struct kobject *parent;
- if (!kobj)
- return -ENOENT;
- /*檢查name字段是否存在*/
- if (!kobj->name || !kobj->name[0]) {
- WARN(1, "kobject: (%p): attempted to be registered with empty "
- "name!\n", kobj);
- return -EINVAL;
- }
- parent = kobject_get(kobj->parent); /*有父對象則增加父對象引用計數*/
- /* join kset if set, use it as parent if we do not already have one */
- if (kobj->kset) {
- if (!parent)
- /*kobj屬於某個kset,但是該kobj沒有父對象,則以kset的kobj作爲父對象*/
- parent = kobject_get(&kobj->kset->kobj);
- kobj_kset_join(kobj); /*將kojbect添加到kset結構中的鏈表當中*/
- kobj->parent = parent;
- }
- pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
- kobject_name(kobj), kobj, __func__,
- parent ? kobject_name(parent) : "<NULL>",
- kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
- error = create_dir(kobj); /*根據kobj->name在sys中建立目錄*/
- if (error) {
- kobj_kset_leave(kobj); /*刪除鏈表項*/
- kobject_put(parent); /*減少引用計數*/
- kobj->parent = NULL;
- /* be noisy on error issues */
- if (error == -EEXIST)
- printk(KERN_ERR "%s failed for %s with "
- "-EEXIST, don't try to register things with "
- "the same name in the same directory.\n",
- __func__, kobject_name(kobj));
- else
- printk(KERN_ERR "%s failed for %s (%d)\n",
- __func__, kobject_name(kobj), error);
- dump_stack();
- } else
- kobj->state_in_sysfs = 1;
- return error;
- }
而devices_kset對應着/sys/devices目錄,因此該函數調用完成後將在/sys/devices目錄下生成目錄platform。
但是這裏比較奇怪的是,爲什麼platform目錄沒有對應的kset對象???
6.2.3 device_create_sys_dev_entry函數
在調用該函數之前,會在/sys/devices/platform/下生成屬性文件。接着如果該device的設備號不爲0,則創建屬性文件dev,並調用本函數。
但是,在本例中設備號devt從未設置過,顯然爲0,那麼本函數實際並未執行。
下列代碼位於drivers/base/core.c。
- static int device_create_sys_dev_entry(struct device *dev)
- {
- struct kobject *kobj = device_to_dev_kobj(dev);
- int error = 0;
- char devt_str[15];
- if (kobj) {
- format_dev_t(devt_str, dev->devt);
- error = sysfs_create_link(kobj, &dev->kobj, devt_str);
- }
- return error;
- }
- /**
- * device_to_dev_kobj - select a /sys/dev/ directory for the device
- * @dev: device
- *
- * By default we select char/ for new entries. Setting class->dev_obj
- * to NULL prevents an entry from being created. class->dev_kobj must
- * be set (or cleared) before any devices are registered to the class
- * otherwise device_create_sys_dev_entry() and
- * device_remove_sys_dev_entry() will disagree about the the presence
- * of the link.
- */
- static struct kobject *device_to_dev_kobj(struct device *dev)
- {
- struct kobject *kobj;
- if (dev->class)
- kobj = dev->class->dev_kobj;
- else
- kobj = sysfs_dev_char_kobj;
- return kobj;
- }
6.2.4 device_add_class_symlinks函數
由於dev->class爲NULL,本函數其實沒做任何工作。
下列代碼位於drivers/base/core.c。
- static int device_add_class_symlinks(struct device *dev)
- {
- int error;
- if (!dev->class)
- return 0;
- error = sysfs_create_link(&dev->kobj,
- &dev->class->p->class_subsys.kobj,
- "subsystem");
- if (error)
- goto out;
- #ifdef CONFIG_SYSFS_DEPRECATED
- /* stacked class devices need a symlink in the class directory */
- if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
- device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_subsys;
- }
- if (dev->parent && device_is_not_partition(dev)) {
- struct device *parent = dev->parent;
- char *class_name;
- /*
- * stacked class devices have the 'device' link
- * pointing to the bus device instead of the parent
- */
- while (parent->class && !parent->bus && parent->parent)
- parent = parent->parent;
- error = sysfs_create_link(&dev->kobj,
- &parent->kobj,
- "device");
- if (error)
- goto out_busid;
- class_name = make_class_name(dev->class->name,
- &dev->kobj);
- if (class_name)
- error = sysfs_create_link(&dev->parent->kobj,
- &dev->kobj, class_name);
- kfree(class_name);
- if (error)
- goto out_device;
- }
- return 0;
- out_device:
- if (dev->parent && device_is_not_partition(dev))
- sysfs_remove_link(&dev->kobj, "device");
- out_busid:
- if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
- device_is_not_partition(dev))
- sysfs_remove_link(&dev->class->p->class_subsys.kobj,
- dev_name(dev));
- #else
- /* link in the class directory pointing to the device */
- error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_subsys;
- if (dev->parent && device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
- "device");
- if (error)
- goto out_busid;
- }
- return 0;
- out_busid:
- sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev));
- #endif
- out_subsys:
- sysfs_remove_link(&dev->kobj, "subsystem");
- out:
- return error;
- }
6.2.5 device_add_attrs函數
同樣dev->class爲空,什麼都沒幹。
下列代碼位於drivers/base/core.c。
- static int device_add_attrs(struct device *dev)
- {
- struct class *class = dev->class;
- struct device_type *type = dev->type;
- int error;
- if (class) {
- error = device_add_attributes(dev, class->dev_attrs);
- if (error)
- return error;
- }
- if (type) {
- error = device_add_groups(dev, type->groups);
- if (error)
- goto err_remove_class_attrs;
- }
- error = device_add_groups(dev, dev->groups);
- if (error)
- goto err_remove_type_groups;
- return 0;
- err_remove_type_groups:
- if (type)
- device_remove_groups(dev, type->groups);
- err_remove_class_attrs:
- if (class)
- device_remove_attributes(dev, class->dev_attrs);
- return error;
- }
6.2.6 bus_add_device函數
由於dev->bus未指定,因此這個函數什麼都沒幹。
該函數將創建三個symlink,在sysfs中建立總線和設備間的關係。
下列代碼位於drivers/base/bus.c。
- /**
- * bus_add_device - add device to bus
- * @dev: device being added
- *
- * - Add the device to its bus's list of devices.
- * - Create link to device's bus.
- */
- int bus_add_device(struct device *dev)
- {
- struct bus_type *bus = bus_get(dev->bus);
- int error = 0;
- if (bus) {
- pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
- error = device_add_attrs(bus, dev);
- if (error)
- goto out_put;
- /*在sys/bus/XXX/devices下建立symlink,名字爲設備名,該鏈接指向/sys/devices/下的某個目錄*/
- error = sysfs_create_link(&bus->p->devices_kset->kobj,
- &dev->kobj, dev_name(dev));
- if (error)
- goto out_id;
- /*在sys/devices/的某個目錄下建立symlink,名字爲subsystem,該鏈接指向/sys/bus/下的某個目錄*/
- error = sysfs_create_link(&dev->kobj,
- &dev->bus->p->subsys.kobj, "subsystem");
- if (error)
- goto out_subsys;
- /*在sys/devices/的某個目錄下建立symlink,名字爲bus,該鏈接指向/sys/bus/下的某個目錄*/
- error = make_deprecated_bus_links(dev);
- if (error)
- goto out_deprecated;
- }
- return 0;
- out_deprecated:
- sysfs_remove_link(&dev->kobj, "subsystem");
- out_subsys:
- sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
- out_id:
- device_remove_attrs(bus, dev);
- out_put:
- bus_put(dev->bus);
- return error;
- }
6.2.7 dpm_sysfs_add函數
下列代碼位於drivers/base/power/sysfs.c。
- int dpm_sysfs_add(struct device * dev)
- {
- return sysfs_create_group(&dev->kobj, &pm_attr_group);
- }
- static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
- static struct attribute * power_attrs[] = {
- &dev_attr_wakeup.attr,
- NULL,
- };
- static struct attribute_group pm_attr_group = {
- .name = "power",
- .attrs = power_attrs,
- };
該函數將在XXX目錄下建立power子目錄,並在該子目錄下建立屬性文件wakeup。
在本例中,將在/sys/bus/platform下建立子目錄power並在子目錄下建立wakeup文件。
6.2.8 device_pm_add函數
下列代碼位於drivers/base/power/main.c。
- /**
- * device_pm_add - add a device to the list of active devices
- * @dev: Device to be added to the list
- */
- void device_pm_add(struct device *dev)
- {
- pr_debug("PM: Adding info for %s:%s\n",
- dev->bus ? dev->bus->name : "No Bus",
- kobject_name(&dev->kobj));
- mutex_lock(&dpm_list_mtx);
- if (dev->parent) {
- if (dev->parent->power.status >= DPM_SUSPENDING)
- dev_warn(dev, "parent %s should not be sleeping\n",
- dev_name(dev->parent));
- } else if (transition_started) {
- /*
- * We refuse to register parentless devices while a PM
- * transition is in progress in order to avoid leaving them
- * unhandled down the road
- */
- dev_WARN(dev, "Parentless device registered during a PM transaction\n");
- }
- list_add_tail(&dev->power.entry, &dpm_list); /*將該設備添加到鏈表中*/
- mutex_unlock(&dpm_list_mtx);
- }
該函數只是將設備添加到電源管理鏈表中。
6.2.9 bus_attach_device函數
在本例中,由於bus未指定,該函數實際不做任何工作。
下列代碼位於drivers/base/bus.c。
- /**
- * bus_attach_device - add device to bus
- * @dev: device tried to attach to a driver
- *
- * - Add device to bus's list of devices.
- * - Try to attach to driver.
- */
- void bus_attach_device(struct device *dev)
- {
- struct bus_type *bus = dev->bus;
- int ret = 0;
- if (bus) {
- if (bus->p->drivers_autoprobe)
- ret = device_attach(dev); /*嘗試獲取驅動*/
- WARN_ON(ret < 0);
- if (ret >= 0) /*將設備掛在到總線中*/
- klist_add_tail(&dev->p->knode_bus,
- &bus->p->klist_devices);
- }
- }
- /**
- * device_attach - try to attach device to a driver.
- * @dev: device.
- *
- * Walk the list of drivers that the bus has and call
- * driver_probe_device() for each pair. If a compatible
- * pair is found, break out and return.
- *
- * Returns 1 if the device was bound to a driver;
- * 0 if no matching device was found;
- * -ENODEV if the device is not registered.
- *
- * When called for a USB interface, @dev->parent->sem must be held.
- */
- int device_attach(struct device *dev)
- {
- int ret = 0;
- down(&dev->sem);
- if (dev->driver) { /*如果已指定驅動,即已綁定*/
- ret = device_bind_driver(dev); /*在sysfs中建立鏈接關係*/
- if (ret == 0)
- ret = 1;
- else {
- dev->driver = NULL;
- ret = 0;
- }
- } else { /*尚未綁定,嘗試綁定,遍歷該總線上的所有驅動*/
- ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
- }
- up(&dev->sem);
- return ret;
- }
- EXPORT_SYMBOL_GPL(device_attach);
如果bus存在的話,將會調用device_attach函數進行綁定工作。該函數首先判斷dev->driver,如果非0,表示該設備已經綁定了驅動,只要在sysfs中建立鏈接關係即可。
爲0表示沒有綁定,接着調用bus_for_each_drv,注意作爲參數傳入的__device_attach,這是個函數,後面會調用它。
我們來看下bus_for_each_drv:
- /**
- * bus_for_each_drv - driver iterator
- * @bus: bus we're dealing with.
- * @start: driver to start iterating on.
- * @data: data to pass to the callback.
- * @fn: function to call for each driver.
- *
- * This is nearly identical to the device iterator above.
- * We iterate over each driver that belongs to @bus, and call
- * @fn for each. If @fn returns anything but 0, we break out
- * and return it. If @start is not NULL, we use it as the head
- * of the list.
- *
- * NOTE: we don't return the driver that returns a non-zero
- * value, nor do we leave the reference count incremented for that
- * driver. If the caller needs to know that info, it must set it
- * in the callback. It must also be sure to increment the refcount
- * so it doesn't disappear before returning to the caller.
- */
- int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
- void *data, int (*fn)(struct device_driver *, void *))
- {
- struct klist_iter i;
- struct device_driver *drv;
- int error = 0;
- if (!bus)
- return -EINVAL;
- klist_iter_init_node(&bus->p->klist_drivers, &i,
- start ? &start->p->knode_bus : NULL);
- while ((drv = next_driver(&i)) && !error)
- error = fn(drv, data);
- klist_iter_exit(&i);
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_for_each_drv);
- static int __device_attach(struct device_driver *drv, void *data)
- {
- struct device *dev = data;
- if (!driver_match_device(drv, dev)) /*進行匹配工作*/
- return 0;
- return driver_probe_device(drv, dev);
- }
- static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;
- }
- /**
- * driver_probe_device - attempt to bind device & driver together
- * @drv: driver to bind a device to
- * @dev: device to try to bind to the driver
- *
- * This function returns -ENODEV if the device is not registered,
- * 1 if the device is bound sucessfully and 0 otherwise.
- *
- * This function must be called with @dev->sem held. When called for a
- * USB interface, @dev->parent->sem must be held as well.
- */
- int driver_probe_device(struct device_driver *drv, struct device *dev)
- {
- int ret = 0;
- if (!device_is_registered(dev)) /*該device是否已在sysfs中*/
- return -ENODEV;
- pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- ret = really_probe(dev, drv);/*device已在sysfs,調用really_probe*/
- return ret;
- }
該函數首先調用driver_match_device函數,後者將會調用總線的match方法,如果有的話,來進行匹配工作。如果沒有該方法,則返回1,表示匹配成功。
我們這裏是針對platform總線,該總線的方法將在7.6.2節中看到。
隨後,又調用了driver_probe_device函數。該函數將首先判斷該device是否已在sysfs中,如果在則調用really_probe,否則返回出錯。
really_probe將會調用驅動的probe並完成綁定的工作。該函數將在7.6.2節中分析。
6.2.10 小結
在本例中,當device_register調用完成以後,將在/sys/devices/下建立目錄platform,並在platfrom下建立屬性文件uevent和子目錄power,最後在power子目錄下建立wakeup屬性文件。
最後以函數調用過程的總結來結束第6.2小結。
6.3 spi主控制器的平臺設備
本節對一個特定的platform設備進行講解,那就是spi主控制器的平臺設備。
在內核的啓動階段,platform設備將被註冊進內核。我們來看下。
下列代碼位於arch/arm/mach-s3c2440/mach-smdk2440.c
- static struct resource s3c_spi0_resource[] = {
- [0] = {
- .start = S3C24XX_PA_SPI,
- .end = S3C24XX_PA_SPI + 0x1f,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_SPI0,
- .end = IRQ_SPI0,
- .flags = IORESOURCE_IRQ,
- }
- };
- static u64 s3c_device_spi0_dmamask = 0xffffffffUL;
- struct platform_device s3c_device_spi0 = {
- .name = "s3c2410-spi",
- .id = 0,
- .num_resources = ARRAY_SIZE(s3c_spi0_resource),
- .resource = s3c_spi0_resource,
- .dev = {
- .dma_mask = &s3c_device_spi0_dmamask,
- .coherent_dma_mask = 0xffffffffUL
- }
- };
- static struct platform_device *smdk2440_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_lcd,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_iis,
- &s3c_device_spi0,
- };
- static void __init smdk2440_machine_init(void)
- {
- s3c24xx_fb_set_platdata(&smdk2440_fb_info);
- s3c_i2c0_set_platdata(NULL);
- platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
- smdk_machine_init();
- }
在smdk2440_machine_init函數中,通過調用platform_add_devices將設備註冊到內核中。接着來看下該函數。
6.3.1 platform_add_devices
- /**
- * platform_add_devices - add a numbers of platform devices
- * @devs: array of platform devices to add
- * @num: number of platform devices in array
- */
- int platform_add_devices(struct platform_device **devs, int num)
- {
- int i, ret = 0;
- for (i = 0; i < num; i++) {
- ret = platform_device_register(devs[i]);
- if (ret) {
- while (--i >= 0)
- platform_device_unregister(devs[i]);
- break;
- }
- }
- return ret;
- }
- EXPORT_SYMBOL_GPL(platform_add_devices);
6.3.2 platform_device_register
- /**
- * platform_device_register - add a platform-level device
- * @pdev: platform device we're adding
- */
- int platform_device_register(struct platform_device *pdev)
- {
- device_initialize(&pdev->dev);
- return platform_device_add(pdev);
- }
- EXPORT_SYMBOL_GPL(platform_device_register);
6.3.2 platform_device_register
- /**
- * platform_device_add - add a platform device to device hierarchy
- * @pdev: platform device we're adding
- *
- * This is part 2 of platform_device_register(), though may be called
- * separately _iff_ pdev was allocated by platform_device_alloc().
- */
- int platform_device_add(struct platform_device *pdev)
- {
- int i, ret = 0;
- if (!pdev)
- return -EINVAL;
- if (!pdev->dev.parent)
- pdev->dev.parent = &platform_bus; /*該設備的父設備是platform設備,/sys/devices/platform*/
- pdev->dev.bus = &platform_bus_type; /*設備掛載到platform總線上*/
- if (pdev->id != -1)
- dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
- else
- dev_set_name(&pdev->dev, pdev->name);/*pdev->dev->kobj->name = pdev->name*/
- /*遍歷平臺設備的資源,並將資源添加到資源樹中*/
- for (i = 0; i < pdev->num_resources; i++) {
- struct resource *p, *r = &pdev->resource[i];
- if (r->name == NULL)
- r->name = dev_name(&pdev->dev); /*獲取dev->kobject->name*/
- p = r->parent;
- if (!p) { /*p空*/
- if (resource_type(r) == IORESOURCE_MEM)
- p = &iomem_resource;
- else if (resource_type(r) == IORESOURCE_IO)
- p = &ioport_resource;
- }
- if (p && insert_resource(p, r)) { /*將資源添加到資源樹中*/
- printk(KERN_ERR
- "%s: failed to claim resource %d\n",
- dev_name(&pdev->dev), i);
- ret = -EBUSY;
- goto failed;
- }
- }
- pr_debug("Registering platform device '%s'. Parent at %s\n",
- dev_name(&pdev->dev), dev_name(pdev->dev.parent));
- ret = device_add(&pdev->dev); /*添加設備*/
- if (ret == 0)
- return ret;
- failed:
- while (--i >= 0) {
- struct resource *r = &pdev->resource[i];
- unsigned long type = resource_type(r);
- if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
- release_resource(r);
- }
- return ret;
- }
- EXPORT_SYMBOL_GPL(platform_device_add);
本節的platform_device_register函數,首先也是調用了device_initialize,但是隨後他做了一些其他的工作,最後調用了device_add。
那麼這個"其他的工作"幹了些什麼呢?
首先,它將該SPI主控制對應的平臺設備的父設備設爲虛擬的platform設備(platform_bus),然後將該平臺設備掛在至platform總線(platform_bus_type)上,這兩步尤爲重要,後面我們將看到。
然後,調用了dev_set_name設置了pdev->dev-kobj.name,也就是該設備對象的名字,這裏的名字爲s3c2410-spi.0,這個名字將被用來建立一個目錄。
最後,將平臺的相關資源添加到資源樹中。這不是本篇文章討論的重點所在,所以不做過多說明。
在"其他的工作""幹完之後,調用了device_add函數。那麼後面的函數調用過程將和6.2小結的一致。
由於“其他的工作”的原因,實際執行的過程和結果將有所區別。我們來分析下。
6.3.3 不一樣device_add調用結果
首先,在device_add被調用之前,有若干個非常重要的條件已經被設置了。如下:
pdev->dev->kobj.kset = devices_kset
pdev->dev-.parent = &platform_bus
pdev->dev.bus = &platform_bus_type
set_up函數執行時,由於參數parent爲&platform_bus,因此最後將設置pdev->dev->kobj.parent = platform_bus.kobj。平臺設備對象的父對象爲虛擬的platform設備。
kobject_add函數執行時,由於參數parent的存在,將在parent對象所對應的目錄下創建另一個目錄。parent對象代表目錄/sys/devices/下的platform,因此將在/sys/devices/platform下建立目錄s3c2410-spi.0。
device_create_file建立屬性文件uevent。
bus_add_device函數執行時,由於dev.bus 爲&platform_bus_type,因此將建立三個symlink。
/sys/devices/platform/s3c2410-spi.0下建立鏈接subsystem和bus,他們指向/sys/bus/platform。
/sys/bus/platform/devices/下建立鏈接s3c2410-spi.0,指向/sys/devices/platform/s3c2410-spi.0。
dpm_sysfs_add函數在/sys/devices/platform/s3c2410-spi.0下建立子目錄power,並在該子目錄下建立屬性文件wakeup。
執行到這裏時,sysfs已將內核中新添加的SPI主控制器平臺設備呈現出來了,我們來驗證下。
[root@yj423 s3c2410-spi.0]#pwd/sys/devices/platform/s3c2410-spi.0[root@yj423 s3c2410-spi.0]#lllrwxrwxrwx 1 root root 0 Jan 1 00:29 bus -> ../../../bus/platformlrwxrwxrwx 1 root root 0 Jan 1 00:29 driver -> ../../../bus/platform/drivers/s3c2410-spi-r--r--r-- 1 root root 4096 Jan 1 00:29 modaliasdrwxr-xr-x 2 root root 0 Jan 1 00:29 powerdrwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.0drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.1lrwxrwxrwx 1 root root 0 Jan 1 00:29 spi_master:spi0 -> ../../../class/spi_master/spi0lrwxrwxrwx 1 root root 0 Jan 1 00:29 subsystem -> ../../../bus/platform-rw-r--r-- 1 root root 4096 Jan 1 00:29 uevent
[root@yj423 devices]#pwd/sys/bus/platform/devices[root@yj423 devices]#ll s3c2410-spi.0 lrwxrwxrwx 1 root root 0 Jan 1 00:44 s3c2410-spi.0 -> ../../../devices/platform/s3c2410-spi.0
通過sysfs將設備驅動的模型層次呈現在用戶空間以後,將更新內核的設備模型之間的關係,這是通過修改鏈表的指向來完成的。
bus_attach_device函數執行時,將設備添加到總線的設備鏈表中,同時也會嘗試綁定驅動,不過會失敗。
接着,由於dev->parent的存在,將SPI主控制器設備添加到父設備platform虛擬設備的兒子鏈表中。
7. driver舉例
我們已經介紹過platform總線的註冊,也講述了SPI主控制器設備作爲平臺設備的註冊過程,在本節,將描述SPI主控制器的platform驅動是如何註冊的。
7.1 s3c24xx_spi_init
下列代碼位於drivers/spi/spi_s3c24xx.c。
- MODULE_ALIAS("platform:s3c2410-spi");
- static struct platform_driver s3c24xx_spi_driver = {
- .remove = __exit_p(s3c24xx_spi_remove),
- .suspend = s3c24xx_spi_suspend,
- .resume = s3c24xx_spi_resume,
- .driver = {
- .name = "s3c2410-spi",
- .owner = THIS_MODULE,
- },
- };
- static int __init s3c24xx_spi_init(void)
- {
- return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);//設備不可熱插拔,所以使用該函數,而不是platform_driver_register
- }
注意:driver.name字段使用來匹配設備的,該字段必須和6.3節一開始給出的pdev.name字段相同。
7.2 platform_driver_probe
下列代碼位於drivers/base/platform.c。
- /**
- * platform_driver_probe - register driver for non-hotpluggable device
- * @drv: platform driver structure
- * @probe: the driver probe routine, probably from an __init section
- *
- * Use this instead of platform_driver_register() when you know the device
- * is not hotpluggable and has already been registered, and you want to
- * remove its run-once probe() infrastructure from memory after the driver
- * has bound to the device.
- *
- * One typical use for this would be with drivers for controllers integrated
- * into system-on-chip processors, where the controller devices have been
- * configured as part of board setup.
- *
- * Returns zero if the driver registered and bound to a device, else returns
- * a negative error code and with the driver not registered.
- */
- int __init_or_module platform_driver_probe(struct platform_driver *drv,
- int (*probe)(struct platform_device *))
- {
- int retval, code;
- /* temporary section violation during probe() */
- drv->probe = probe;
- retval = code = platform_driver_register(drv); /*註冊platform驅動*/
- /* Fixup that section violation, being paranoid about code scanning
- * the list of drivers in order to probe new devices. Check to see
- * if the probe was successful, and make sure any forced probes of
- * new devices fail.
- */
- spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
- drv->probe = NULL;
- if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
- retval = -ENODEV;
- drv->driver.probe = platform_drv_probe_fail;
- spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
- if (code != retval)
- platform_driver_unregister(drv);
- return retval;
- }
- EXPORT_SYMBOL_GPL(platform_driver_probe);
7.3 platform_driver_register
- /**
- * platform_driver_register
- * @drv: platform driver structure
- */
- int platform_driver_register(struct platform_driver *drv)
- {
- drv->driver.bus = &platform_bus_type;
- if (drv->probe)
- drv->driver.probe = platform_drv_probe;
- if (drv->remove)
- drv->driver.remove = platform_drv_remove;
- if (drv->shutdown)
- drv->driver.shutdown = platform_drv_shutdown;
- if (drv->suspend)
- drv->driver.suspend = platform_drv_suspend;
- if (drv->resume)
- drv->driver.resume = platform_drv_resume;
- return driver_register(&drv->driver); /*驅動註冊*/
- }
- EXPORT_SYMBOL_GPL(platform_driver_register);
7.4 driver_register
下列代碼位於drivers/base/driver.c。
- /**
- * driver_register - register driver with bus
- * @drv: driver to register
- *
- * We pass off most of the work to the bus_add_driver() call,
- * since most of the things we have to do deal with the bus
- * structures.
- */
- int driver_register(struct device_driver *drv)
- {
- int ret;
- struct device_driver *other;
- BUG_ON(!drv->bus->p);
- if ((drv->bus->probe && drv->probe) ||
- (drv->bus->remove && drv->remove) ||
- (drv->bus->shutdown && drv->shutdown))
- printk(KERN_WARNING "Driver '%s' needs updating - please use "
- "bus_type methods\n", drv->name);
- other = driver_find(drv->name, drv->bus);/*用驅動名字來搜索在該總線上驅動是否已經存在*/
- if (other) { /*存在則報錯*/
- put_driver(other);
- printk(KERN_ERR "Error: Driver '%s' is already registered, "
- "aborting...\n", drv->name);
- return -EEXIST;
- }
- ret = bus_add_driver(drv); /*將驅動添加到一個總線中*/
- if (ret)
- return ret;
- ret = driver_add_groups(drv, drv->groups); /*建立屬性組文件*/
- if (ret)
- bus_remove_driver(drv);
- return ret;
- }
- EXPORT_SYMBOL_GPL(driver_register);
接下來就分析這兩個函數。
7.5 driver_find
下列代碼位於drivers/base/driver.c。
- /**
- * driver_find - locate driver on a bus by its name.
- * @name: name of the driver.
- * @bus: bus to scan for the driver.
- *
- * Call kset_find_obj() to iterate over list of drivers on
- * a bus to find driver by name. Return driver if found.
- *
- * Note that kset_find_obj increments driver's reference count.
- */
- struct device_driver *driver_find(const char *name, struct bus_type *bus)
- {
- struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
- struct driver_private *priv;
- if (k) {
- priv = to_driver(k);
- return priv->driver;
- }
- return NULL;
- }
- EXPORT_SYMBOL_GPL(driver_find);
- /**
- * kset_find_obj - search for object in kset.
- * @kset: kset we're looking in.
- * @name: object's name.
- *
- * Lock kset via @kset->subsys, and iterate over @kset->list,
- * looking for a matching kobject. If matching object is found
- * take a reference and return the object.
- */
- struct kobject *kset_find_obj(struct kset *kset, const char *name)
- {
- struct kobject *k;
- struct kobject *ret = NULL;
- spin_lock(&kset->list_lock);
- list_for_each_entry(k, &kset->list, entry) {
- if (kobject_name(k) && !strcmp(kobject_name(k), name)) {
- ret = kobject_get(k);
- break;
- }
- }
- spin_unlock(&kset->list_lock);
- return ret;
- }
這裏調用了kset_find_obj函數,傳入的實參bus->p->drivers_kset,它對應的就是/sys/bus/platform/下的drivers目錄,然後通過鏈表,它將搜索該目錄下的所有文件,來尋找是否有名爲s3c2410-spi的文件。還記得嗎? kobject就是一個文件對象。如果沒有找到將返回NULL,接着將調用bus_add_driver把驅動註冊進內核。
7.6 bus_add_driver
下列代碼位於drivers/base/bus.c
- /**
- * bus_add_driver - Add a driver to the bus.
- * @drv: driver.
- */
- int bus_add_driver(struct device_driver *drv)
- {
- struct bus_type *bus;
- struct driver_private *priv;
- int error = 0;
- bus = bus_get(drv->bus); /*增加引用計數獲取bus_type*/
- if (!bus)
- return -EINVAL;
- pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
- priv = kzalloc(sizeof(*priv), GFP_KERNEL); /*分配driver_private結構體*/
- if (!priv) {
- error = -ENOMEM;
- goto out_put_bus;
- }
- /*初始化內核鏈表*/
- klist_init(&priv->klist_devices, NULL, NULL);
- /*相互保存*/
- priv->driver = drv;
- drv->p = priv;
- /*設置該kobj屬於那個kset*/
- priv->kobj.kset = bus->p->drivers_kset;
- error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, /*parent=NULL*/
- "%s", drv->name); /*執行完以後,會在bus/總線名/drivers/下建立名爲drv->name的目錄*/
- if (error)
- goto out_unregister;
- if (drv->bus->p->drivers_autoprobe) {
- error = driver_attach(drv); /*嘗試綁定驅動和設備*/
- if (error)
- goto out_unregister;
- }
- /*添加該驅動到bus的內核鏈表中*/
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
- module_add_driver(drv->owner, drv);/*?????????*/
- /*創建屬性,在bus/總線名/drivers/驅動名/下建立文件uevent*/
- error = driver_create_file(drv, &driver_attr_uevent);
- if (error) {
- printk(KERN_ERR "%s: uevent attr (%s) failed\n",
- __func__, drv->name);
- }
- /*利用bus->drv_attrs創建屬性,位於bus/總線名/drivers/驅動名/*/
- error = driver_add_attrs(bus, drv);
- if (error) {
- /* How the hell do we get out of this pickle? Give up */
- printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
- __func__, drv->name);
- }
- /*創建屬性,在bus/總線名/drivers/驅動名/下建立文件bind和unbind*/
- error = add_bind_files(drv);
- if (error) {
- /* Ditto */
- printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
- __func__, drv->name);
- }
- /*通知用戶空間???*/
- kobject_uevent(&priv->kobj, KOBJ_ADD);
- return 0;
- out_unregister:
- kfree(drv->p);
- drv->p = NULL;
- kobject_put(&priv->kobj);
- out_put_bus:
- bus_put(bus);
- return error;
- }
7.6.1 kobject_init_and_add
下列代碼位於lib/kobject.c。
- /**
- * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
- * @kobj: pointer to the kobject to initialize
- * @ktype: pointer to the ktype for this kobject.
- * @parent: pointer to the parent of this kobject.
- * @fmt: the name of the kobject.
- *
- * This function combines the call to kobject_init() and
- * kobject_add(). The same type of error handling after a call to
- * kobject_add() and kobject lifetime rules are the same here.
- */
- int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
- struct kobject *parent, const char *fmt, ...)
- {
- va_list args;
- int retval;
- kobject_init(kobj, ktype);
- va_start(args, fmt);
- retval = kobject_add_varg(kobj, parent, fmt, args);
- va_end(args);
- return retval;
- }
- EXPORT_SYMBOL_GPL(kobject_init_and_add);
調用該函數時由於parent爲NULL,但kobj.kset爲drivers目錄,所以將在/sys/bus/platform/drivers/下建立目錄,名爲s3c2410-spi。
我們來驗證下:
[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
接着由於drivers_autoprobe在bus_register執行的時候已經置1,將調用driver_attach。
7.6.2 driver_attach
下列代碼位於drivers/base/dd.c。
- /**
- * driver_attach - try to bind driver to devices.
- * @drv: driver.
- *
- * Walk the list of devices that the bus has on it and try to
- * match the driver with each one. If driver_probe_device()
- * returns 0 and the @dev->driver is set, we've found a
- * compatible pair.
- */
- int driver_attach(struct device_driver *drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }
- EXPORT_SYMBOL_GPL(driver_attach);
這裏需要注意的是最後一個參數__driver_attach,這是一個函數名,後面將會調用它。
- /**
- * bus_for_each_dev - device iterator.
- * @bus: bus type.
- * @start: device to start iterating from.
- * @data: data for the callback.
- * @fn: function to be called for each device.
- *
- * Iterate over @bus's list of devices, and call @fn for each,
- * passing it @data. If @start is not NULL, we use that device to
- * begin iterating from.
- *
- * We check the return of @fn each time. If it returns anything
- * other than 0, we break out and return that value.
- *
- * NOTE: The device that returns a non-zero value is not retained
- * in any way, nor is its refcount incremented. If the caller needs
- * to retain this data, it should do, and increment the reference
- * count in the supplied callback.
- */
- int bus_for_each_dev(struct bus_type *bus, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct klist_iter i;
- struct device *dev;
- int error = 0;
- if (!bus)
- return -EINVAL;
- klist_iter_init_node(&bus->p->klist_devices, &i,
- (start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error)
- error = fn(dev, data);
- klist_iter_exit(&i);
- return error;
- }
- EXPORT_SYMBOL_GPL(bus_for_each_dev);
- static int __driver_attach(struct device *dev, void *data)
- {
- struct device_driver *drv = data;
- /*
- * Lock device and try to bind to it. We drop the error
- * here and always return 0, because we need to keep trying
- * to bind to devices and some drivers will return an error
- * simply if it didn't support the device.
- *
- * driver_probe_device() will spit a warning if there
- * is an error.
- */
- if (!driver_match_device(drv, dev))
- return 0;
- if (dev->parent) /* Needed for USB */
- down(&dev->parent->sem);
- down(&dev->sem);
- if (!dev->driver)
- driver_probe_device(drv, dev);
- up(&dev->sem);
- if (dev->parent)
- up(&dev->parent->sem);
- return 0;
- }
- static inline int driver_match_device(struct device_driver *drv,
- struct device *dev)
- {
- return drv->bus->match ? drv->bus->match(dev, drv) : 1;
- }
這裏直接調用了platform總線的match方法,我們來看下這個方法。
- /**
- * platform_match - bind platform device to platform driver.
- * @dev: device.
- * @drv: driver.
- *
- * Platform device IDs are assumed to be encoded like this:
- * "<name><instance>", where <name> is a short description of the type of
- * device, like "pci" or "floppy", and <instance> is the enumerated
- * instance of the device, like '0' or '42'. Driver IDs are simply
- * "<name>". So, extract the <name> from the platform_device structure,
- * and compare it against the name of the driver. Return whether they match
- * or not.
- */
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
- /* match against the id table first */
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
- /* fall-back to driver name match */
- return (strcmp(pdev->name, drv->name) == 0);
- }
在本例中兩者同爲s3c2410-spi。因此匹配完成,返回1。
返回後,由於dev->driver爲NULL,將調用driver_probe_device函數。我們來看下:
- /**
- * driver_probe_device - attempt to bind device & driver together
- * @drv: driver to bind a device to
- * @dev: device to try to bind to the driver
- *
- * This function returns -ENODEV if the device is not registered,
- * 1 if the device is bound sucessfully and 0 otherwise.
- *
- * This function must be called with @dev->sem held. When called for a
- * USB interface, @dev->parent->sem must be held as well.
- */
- int driver_probe_device(struct device_driver *drv, struct device *dev)
- {
- int ret = 0;
- if (!device_is_registered(dev))
- return -ENODEV;
- pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- ret = really_probe(dev, drv);
- return ret;
- }
- static inline int device_is_registered(struct device *dev)
- {
- return dev->kobj.state_in_sysfs;
- }
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
- int ret = 0;
- atomic_inc(&probe_count);
- pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
- drv->bus->name, __func__, drv->name, dev_name(dev));
- WARN_ON(!list_empty(&dev->devres_head));
- dev->driver = drv;
- if (driver_sysfs_add(dev)) { /*創建兩個symlink,更新sysfs*/
- printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
- __func__, dev_name(dev));
- goto probe_failed;
- }
- if (dev->bus->probe) {
- ret = dev->bus->probe(dev);/*調用總線的probe方法*/
- if (ret)
- goto probe_failed;
- } else if (drv->probe) {
- ret = drv->probe(dev); /*調用驅動的probe方法*/
- if (ret)
- goto probe_failed;
- }
- driver_bound(dev); /*綁定設備和驅動*/
- ret = 1;
- pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
- drv->bus->name, __func__, dev_name(dev), drv->name);
- goto done;
- probe_failed:
- devres_release_all(dev);
- driver_sysfs_remove(dev);
- dev->driver = NULL;
- if (ret != -ENODEV && ret != -ENXIO) {
- /* driver matched but the probe failed */
- printk(KERN_WARNING
- "%s: probe of %s failed with error %d\n",
- drv->name, dev_name(dev), ret);
- }
- /*
- * Ignore errors returned by ->probe so that the next driver can try
- * its luck.
- */
- ret = 0;
- done:
- atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
- return ret;
- }
在這個函數中調用4個函數。
第一個函數driver_sysfs_add將更新sysfs。
- static int driver_sysfs_add(struct device *dev)
- {
- int ret;
- /* 在/sys/bus/XXX/drivers/XXX目錄下建立symlink,鏈接名爲kobj->name,
- 鏈接指向/sys/devices/platform/XXX */
- ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
- kobject_name(&dev->kobj));
- if (ret == 0) {
- /* 在/sys/devices/platform/XXX/下建立symlink,鏈接名爲driver,
- 指向/sys/bus/xxx/drivers目錄下的某個目錄*/
- ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
- "driver");
- if (ret)
- sysfs_remove_link(&dev->driver->p->kobj,
- kobject_name(&dev->kobj));
- }
- return ret;
- }
執行完以後,建立了兩個鏈接。
在/sys/bus/platform/drivers/s3c2410-spi下建立鏈接,指向/sys/devices/platform/s3c2410-spi.0
在/sys/devices/platform/s3c2410-spi.0下建立鏈接,指向/sys/devices/platform/s3c2410-spi.0。
這樣就在用戶空間呈現出驅動和設備的關係了。我們來驗證下。
[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
[root@yj423 s3c2410-spi]#ll s3c2410-spi.0
lrwxrwxrwx 1 root root 0 Jan 1 02:28 s3c2410-spi.0 -> ../../../../devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#pwd
/sys/devices/platform/s3c2410-spi.0
[root@yj423 s3c2410-spi.0]#ll driver
lrwxrwxrwx 1 root root 0 Jan 1 02:26 driver -> ../../../bus/platform/drivers/s3c2410-spi
第2個函數執行總線的probe方法,由於platform總線沒有提供probe方法,因此不執行。
第3個函數執行驅動的probe方法,驅動提供了probe,因此調用它,該函數的細節超過了本文的討論內容,所以略過。
第4個函數執行driver_bound,用來綁定設備和驅動,來看下這個函數。
- static void driver_bound(struct device *dev)
- {
- if (klist_node_attached(&dev->p->knode_driver)) {
- printk(KERN_WARNING "%s: device %s already bound\n",
- __func__, kobject_name(&dev->kobj));
- return;
- }
- pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
- __func__, dev->driver->name);
- if (dev->bus)
- blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
- BUS_NOTIFY_BOUND_DRIVER, dev);
- klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
- }
至此,通過內核鏈表,這個platform device 和platform driver 已經綁定完成,將繼續遍歷內核鏈表嘗試匹配和綁定,直到鏈表結束。
在driver_attach執行完畢以後,bus_add_driver函數還有些剩餘工作要完成。
首先,將驅動添加到總線的驅動列表中。
接着,如果定義了驅動屬性文件,則創建。
最後,在/sys/bus/platform/drivers/s3c2410-spi/下建立屬性文件uevent,並在同一目錄下建立文件bind和unbind。
我們來驗證下:
[root@yj423 s3c2410-spi]#pwd
/sys/bus/platform/drivers/s3c2410-spi
[root@yj423 s3c2410-spi]#ls
bind s3c2410-spi.0 uevent unbind
7.7 小結
在本節中,我們看到了platform driver是如何註冊到內核中,在註冊過程中,通過更新了sysfs,向用戶空間展示總線,設備和驅動之間的關係。
同時,還更新了鏈表的指向,在內核中體現了同樣的關係。
最後以platform driver的註冊過程結束本章。
8. sysfs底層函數
下面講述的內容將基於VFS,有關VFS的基本內容超過本文的範圍,請參考<<深入理解Linux內核>>一書的第12章。
在前面講述的過程中,我們知道設備驅動模型是如何通過kobject將總線,設備和驅動間的層次關係在用戶空間呈現出來的。事實上,就是通過目錄,文件和symlink來呈現相互之間的關係。在前面的敘述中,我們並沒有對目錄,文件和symlink的創建進行 講解,本章就對這些底層函數進行講解。在講解這些函數之前,我們先來看下,sysfs文件系統是如何註冊的。
8.1 註冊sysfs文件系統
sysfs文件系統的註冊是調用sysfs_init函數來完成的,該函數在內核啓動階段被調用,我們來看下大致函數調用流程,這裏不作分析。
start_kernel( ) -> vfs_caches_init( ) -> mnt_init( ) -> mnt_init( ) -> sysfs_init( )。
- int __init sysfs_init(void)
- {
- int err = -ENOMEM;
- /*建立cache,名字爲sysfs_dir_cache*/
- sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
- sizeof(struct sysfs_dirent),
- 0, 0, NULL);
- if (!sysfs_dir_cachep)
- goto out;
- err = sysfs_inode_init();
- if (err)
- goto out_err;
- /*註冊文件系統*/
- err = register_filesystem(&sysfs_fs_type);
- if (!err) {
- /*註冊成功,加載文件系統*/
- sysfs_mount = kern_mount(&sysfs_fs_type);
- if (IS_ERR(sysfs_mount)) {
- printk(KERN_ERR "sysfs: could not mount!\n");
- err = PTR_ERR(sysfs_mount);
- sysfs_mount = NULL;
- unregister_filesystem(&sysfs_fs_type);
- goto out_err;
- }
- } else
- goto out_err;
- out:
- return err;
- out_err:
- kmem_cache_destroy(sysfs_dir_cachep);
- sysfs_dir_cachep = NULL;
- goto out;
- }
- static struct file_system_type sysfs_fs_type = {
- .name = "sysfs",
- .get_sb = sysfs_get_sb,
- .kill_sb = kill_anon_super,
- };
8.1.1 register_filesystem
下列代碼位於fs/filesystems.c。
- /**
- * register_filesystem - register a new filesystem
- * @fs: the file system structure
- *
- * Adds the file system passed to the list of file systems the kernel
- * is aware of for mount and other syscalls. Returns 0 on success,
- * or a negative errno code on an error.
- *
- * The &struct file_system_type that is passed is linked into the kernel
- * structures and must not be freed until the file system has been
- * unregistered.
- */
- int register_filesystem(struct file_system_type * fs)
- {
- int res = 0;
- struct file_system_type ** p;
- BUG_ON(strchr(fs->name, '.'));
- if (fs->next)
- return -EBUSY;
- INIT_LIST_HEAD(&fs->fs_supers);
- write_lock(&file_systems_lock);
- p = find_filesystem(fs->name, strlen(fs->name)); /*查找要住的文件是同是否存在,返回位置*/
- if (*p)
- res = -EBUSY; /*該文件系統已存在,返回error*/
- else
- *p = fs; /*將新的文件系統加入到鏈表中*/
- write_unlock(&file_systems_lock);
- return res;
- }
- static struct file_system_type **find_filesystem(const char *name, unsigned len)
- {
- struct file_system_type **p;
- for (p=&file_systems; *p; p=&(*p)->next)
- if (strlen((*p)->name) == len &&
- strncmp((*p)->name, name, len) == 0)
- break;
- return p;
- }
該函數將調用函數file_system_type,此函數根據name字段(sysfs)來查找要註冊的文件系統是否已經存在。
如果不存在,表示還未註冊,則將新的fs添加到鏈表中,鏈表的第一項爲全局變量file_systems。
該全局變量爲單項鍊表,所有已註冊的文件系統都被插入到這個鏈表當中。
8.1.2 kern_mount函數
下列代碼位於include/linux/fs.h
- #define kern_mount(type) kern_mount_data(type, NULL)
- struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
- {
- return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
- }
- EXPORT_SYMBOL_GPL(kern_mount_data);
kern_mount實際上最後是調用了vfs_kern_mount函數。我們來看下:
- struct vfsmount *
- vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
- {
- struct vfsmount *mnt;
- char *secdata = NULL;
- int error;
- if (!type)
- return ERR_PTR(-ENODEV);
- error = -ENOMEM;
- mnt = alloc_vfsmnt(name); /*分配struct vfsmount*/
- if (!mnt)
- goto out;
- if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
- secdata = alloc_secdata();
- if (!secdata)
- goto out_mnt;
- error = security_sb_copy_data(data, secdata);
- if (error)
- goto out_free_secdata;
- }
- /*get_sb方法,分配superblock對象,並初始化*/
- error = type->get_sb(type, flags, name, data, mnt);
- if (error < 0)
- goto out_free_secdata;
- BUG_ON(!mnt->mnt_sb);
- error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
- if (error)
- goto out_sb;
- mnt->mnt_mountpoint = mnt->mnt_root;/*設置掛載點的dentry*/
- mnt->mnt_parent = mnt; /*設置所掛載的fs爲自己本身*/
- up_write(&mnt->mnt_sb->s_umount);
- free_secdata(secdata);
- return mnt;
- out_sb:
- dput(mnt->mnt_root);
- deactivate_locked_super(mnt->mnt_sb);
- out_free_secdata:
- free_secdata(secdata);
- out_mnt:
- free_vfsmnt(mnt);
- out:
- return ERR_PTR(error);
- }
該函數在首先調用alloc_vfsmnt來分配struct vfsmount結構,並做了一些初試化工作。
下列函數位於fs/super.c
- struct vfsmount *alloc_vfsmnt(const char *name)
- {
- struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
- if (mnt) {
- int err;
- err = mnt_alloc_id(mnt); /*設置mnt->mnt_id*/
- if (err)
- goto out_free_cache;
- if (name) {
- mnt->mnt_devname = kstrdup(name, GFP_KERNEL); /*拷貝name,並賦值*/
- if (!mnt->mnt_devname)
- goto out_free_id;
- }
- atomic_set(&mnt->mnt_count, 1);
- INIT_LIST_HEAD(&mnt->mnt_hash);
- INIT_LIST_HEAD(&mnt->mnt_child);
- INIT_LIST_HEAD(&mnt->mnt_mounts);
- INIT_LIST_HEAD(&mnt->mnt_list);
- INIT_LIST_HEAD(&mnt->mnt_expire);
- INIT_LIST_HEAD(&mnt->mnt_share);
- INIT_LIST_HEAD(&mnt->mnt_slave_list);
- INIT_LIST_HEAD(&mnt->mnt_slave);
- atomic_set(&mnt->__mnt_writers, 0);
- }
- return mnt;
- out_free_id:
- mnt_free_id(mnt);
- out_free_cache:
- kmem_cache_free(mnt_cache, mnt);
- return NULL;
- }
下列函數位於fs/sysfs/mount.c。
- static int sysfs_get_sb(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data, struct vfsmount *mnt)
- {
- return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);
- }
該函數將分配sysfs文件系統的superblock,獲取文件系統根目錄的inode和dentry。
該函數的執行過程相當複雜,在下一節單獨講述。
8.2 get_sb_single函數
下列函數位於fs/sysfs/mount.c。
- int get_sb_single(struct file_system_type *fs_type,
- int flags, void *data,
- int (*fill_super)(struct super_block *, void *, int),
- struct vfsmount *mnt)
- {
- struct super_block *s;
- int error;
- /*查找或者創建super_block*/
- s = sget(fs_type, compare_single, set_anon_super, NULL);
- if (IS_ERR(s))
- return PTR_ERR(s);
- if (!s->s_root) { /*沒有根目錄dentry*/
- s->s_flags = flags;
- /*獲取root( / )的 inode和dentry*/
- error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
- if (error) {
- deactivate_locked_super(s);
- return error;
- }
- s->s_flags |= MS_ACTIVE;
- }
- do_remount_sb(s, flags, data, 0);
- simple_set_mnt(mnt, s); /*設置vfsmount的superblock和根dentry*/
- return 0;
- }
- EXPORT_SYMBOL(get_sb_single);
8.2.1 sget函數
首先調用了sget函數來查找是否
下列函數位於fs/super.c。
- /**
- * sget - find or create a superblock
- * @type: filesystem type superblock should belong to
- * @test: comparison callback
- * @set: setup callback
- * @data: argument to each of them
- */
- struct super_block *sget(struct file_system_type *type,
- int (*test)(struct super_block *,void *),
- int (*set)(struct super_block *,void *),
- void *data)
- {
- struct super_block *s = NULL;
- struct super_block *old;
- int err;
- retry:
- spin_lock(&sb_lock);
- if (test) {
- /*遍歷所有屬於該文件系統的super_block*/
- list_for_each_entry(old, &type->fs_supers, s_instances) {
- if (!test(old, data))
- continue;
- if (!grab_super(old))
- goto retry;
- if (s) {
- up_write(&s->s_umount);
- destroy_super(s);
- }
- return old;
- }
- }
- if (!s) {
- spin_unlock(&sb_lock);
- s = alloc_super(type); /*創建新的super_block並初始化*/
- if (!s)
- return ERR_PTR(-ENOMEM);
- goto retry;
- }
- err = set(s, data); /*設置s->s_dev */
- if (err) {
- spin_unlock(&sb_lock);
- up_write(&s->s_umount);
- destroy_super(s);
- return ERR_PTR(err);
- }
- s->s_type = type;
- strlcpy(s->s_id, type->name, sizeof(s->s_id)); /*拷貝name*/
- list_add_tail(&s->s_list, &super_blocks); /*將新的super_block添加到鏈表頭super_blocks中*/
- list_add(&s->s_instances, &type->fs_supers); /*將新的super_block添加到相應的文件系統類型的鏈表中*/
- spin_unlock(&sb_lock);
- get_filesystem(type);
- return s;
- }
- EXPORT_SYMBOL(sget);
然後調用alloc_super函數來創建新的struct super_block。
下列函數位於fs/super.c。
- /**
- * alloc_super - create new superblock
- * @type: filesystem type superblock should belong to
- *
- * Allocates and initializes a new &struct super_block. alloc_super()
- * returns a pointer new superblock or %NULL if allocation had failed.
- */
- static struct super_block *alloc_super(struct file_system_type *type)
- {
- struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);/*分配並清0super_block*/
- static struct super_operations default_op;
- if (s) {
- if (security_sb_alloc(s)) {
- kfree(s);
- s = NULL;
- goto out;
- }
- INIT_LIST_HEAD(&s->s_dirty);
- INIT_LIST_HEAD(&s->s_io);
- INIT_LIST_HEAD(&s->s_more_io);
- INIT_LIST_HEAD(&s->s_files);
- INIT_LIST_HEAD(&s->s_instances);
- INIT_HLIST_HEAD(&s->s_anon);
- INIT_LIST_HEAD(&s->s_inodes);
- INIT_LIST_HEAD(&s->s_dentry_lru);
- INIT_LIST_HEAD(&s->s_async_list);
- init_rwsem(&s->s_umount);
- mutex_init(&s->s_lock);
- lockdep_set_class(&s->s_umount, &type->s_umount_key);
- /*
- * The locking rules for s_lock are up to the
- * filesystem. For example ext3fs has different
- * lock ordering than usbfs:
- */
- lockdep_set_class(&s->s_lock, &type->s_lock_key);
- /*
- * sget() can have s_umount recursion.
- *
- * When it cannot find a suitable sb, it allocates a new
- * one (this one), and tries again to find a suitable old
- * one.
- *
- * In case that succeeds, it will acquire the s_umount
- * lock of the old one. Since these are clearly distrinct
- * locks, and this object isn't exposed yet, there's no
- * risk of deadlocks.
- *
- * Annotate this by putting this lock in a different
- * subclass.
- */
- down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING);
- s->s_count = S_BIAS;
- atomic_set(&s->s_active, 1);
- mutex_init(&s->s_vfs_rename_mutex);
- mutex_init(&s->s_dquot.dqio_mutex);
- mutex_init(&s->s_dquot.dqonoff_mutex);
- init_rwsem(&s->s_dquot.dqptr_sem);
- init_waitqueue_head(&s->s_wait_unfrozen);
- s->s_maxbytes = MAX_NON_LFS;
- s->dq_op = sb_dquot_ops;
- s->s_qcop = sb_quotactl_ops;
- s->s_op = &default_op;
- s->s_time_gran = 1000000000;
- }
- out:
- return s;
- }
下列函數位於fs/super.c。
- int set_anon_super(struct super_block *s, void *data)
- {
- int dev;
- int error;
- retry:
- if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0)/*分配ID號*/
- return -ENOMEM;
- spin_lock(&unnamed_dev_lock);
- error = ida_get_new(&unnamed_dev_ida, &dev);/*獲取ID號,保存在dev中*/
- spin_unlock(&unnamed_dev_lock);
- if (error == -EAGAIN)
- /* We raced and lost with another CPU. */
- goto retry;
- else if (error)
- return -EAGAIN;
- if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
- spin_lock(&unnamed_dev_lock);
- ida_remove(&unnamed_dev_ida, dev);
- spin_unlock(&unnamed_dev_lock);
- return -EMFILE;
- }
- s->s_dev = MKDEV(0, dev & MINORMASK); /*構建設備號*/
- return 0;
- }
8.2.2 sysfs_fill_super函數
分配了super_block之後,將判斷該super_block是否有root dentry。本例中,顯然沒有。然後調用形參fill_super指向的函數,也就是sysfs_fill_super函數。
下列函數位於fs/sysfs/mount.c。
- struct super_block * sysfs_sb = NULL;
- static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
- {
- struct inode *inode;
- struct dentry *root;
- sb->s_blocksize = PAGE_CACHE_SIZE; /*4KB*/
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT; /*4KB*/
- sb->s_magic = SYSFS_MAGIC; /*0x62656572*/
- sb->s_op = &sysfs_ops;
- sb->s_time_gran = 1;
- sysfs_sb = sb; /*sysfs_sb即爲sysfs的super_block*/
- /* get root inode, initialize and unlock it */
- mutex_lock(&sysfs_mutex);
- inode = sysfs_get_inode(&sysfs_root); /*sysfs_root即爲sysfs所在的根目錄的dirent,,獲取inode*/
- mutex_unlock(&sysfs_mutex);
- if (!inode) {
- pr_debug("sysfs: could not get root inode\n");
- return -ENOMEM;
- }
- /* instantiate and link root dentry */
- root = d_alloc_root(inode); /*爲獲得的根inode分配root(/) dentry*/
- if (!root) {
- pr_debug("%s: could not get root dentry!\n",__func__);
- iput(inode);
- return -ENOMEM;
- }
- root->d_fsdata = &sysfs_root;
- sb->s_root = root; /*保存superblock的根dentry*/
- return 0;
- }
- struct sysfs_dirent sysfs_root = { /*sysfs_root即爲sysfs所在的根目錄的dirent*/
- .s_name = "",
- .s_count = ATOMIC_INIT(1),
- .s_flags = SYSFS_DIR,
- .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
- .s_ino = 1,
- };
在設置了一些字段後,設置了sysfs_sb這個全局變量,該全局變量表示的就是sysfs的super_block。
隨後,調用了sysfs_get_inode函數,來獲取sysfs的根目錄的dirent。該函數的參數sysfs_root爲全局變量,表示sysfs的根目錄的sysfs_dirent。
我們看些這個sysfs_dirent數據結構:
- /*
- * sysfs_dirent - the building block of sysfs hierarchy. Each and
- * every sysfs node is represented by single sysfs_dirent.
- *
- * As long as s_count reference is held, the sysfs_dirent itself is
- * accessible. Dereferencing s_elem or any other outer entity
- * requires s_active reference.
- */
- struct sysfs_dirent {
- atomic_t s_count;
- atomic_t s_active;
- struct sysfs_dirent *s_parent;
- struct sysfs_dirent *s_sibling;
- const char *s_name;
- union {
- struct sysfs_elem_dir s_dir;
- struct sysfs_elem_symlink s_symlink;
- struct sysfs_elem_attr s_attr;
- struct sysfs_elem_bin_attr s_bin_attr;
- };
- unsigned int s_flags;
- ino_t s_ino;
- umode_t s_mode;
- struct iattr *s_iattr;
- };
另外,sysfs_dirent將最爲dentry的fs專有數據被保存下來,這一點會在下面中看到。
接着,在來看下sysfs_get_inode函數:
下列函數位於fs/sysfs/inode.c。
- /**
- * sysfs_get_inode - get inode for sysfs_dirent
- * @sd: sysfs_dirent to allocate inode for
- *
- * Get inode for @sd. If such inode doesn't exist, a new inode
- * is allocated and basics are initialized. New inode is
- * returned locked.
- *
- * LOCKING:
- * Kernel thread context (may sleep).
- *
- * RETURNS:
- * Pointer to allocated inode on success, NULL on failure.
- */
- struct inode * sysfs_get_inode(struct sysfs_dirent *sd)
- {
- struct inode *inode;
- inode = iget_locked(sysfs_sb, sd->s_ino); /*在inode cache查找inode是否存在,不存在側創建一個*/
- if (inode && (inode->i_state & I_NEW)) /*如果是新創建的inode,則包含I_NEW*/
- sysfs_init_inode(sd, inode);
- return inode;
- }
- /**
- * iget_locked - obtain an inode from a mounted file system
- * @sb: super block of file system
- * @ino: inode number to get
- *
- * iget_locked() uses ifind_fast() to search for the inode specified by @ino in
- * the inode cache and if present it is returned with an increased reference
- * count. This is for file systems where the inode number is sufficient for
- * unique identification of an inode.
- *
- * If the inode is not in cache, get_new_inode_fast() is called to allocate a
- * new inode and this is returned locked, hashed, and with the I_NEW flag set.
- * The file system gets to fill it in before unlocking it via
- * unlock_new_inode().
- */
- struct inode *iget_locked(struct super_block *sb, unsigned long ino)
- {
- struct hlist_head *head = inode_hashtable + hash(sb, ino);
- struct inode *inode;
- inode = ifind_fast(sb, head, ino);/*在inode cache查找該inode*/
- if (inode)
- return inode; /*找到了該inode*/
- /*
- * get_new_inode_fast() will do the right thing, re-trying the search
- * in case it had to block at any point.
- */
- return get_new_inode_fast(sb, head, ino); /*分配一個新的inode*/
- }
- EXPORT_SYMBOL(iget_locked);
- static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
- {
- struct bin_attribute *bin_attr;
- inode->i_private = sysfs_get(sd);
- inode->i_mapping->a_ops = &sysfs_aops;
- inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
- inode->i_op = &sysfs_inode_operations;
- inode->i_ino = sd->s_ino;
- lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
- if (sd->s_iattr) {
- /* sysfs_dirent has non-default attributes
- * get them for the new inode from persistent copy
- * in sysfs_dirent
- */
- set_inode_attr(inode, sd->s_iattr);
- } else
- set_default_inode_attr(inode, sd->s_mode);/*設置inode屬性*/
- /* initialize inode according to type */
- switch (sysfs_type(sd)) {
- case SYSFS_DIR:
- inode->i_op = &sysfs_dir_inode_operations;
- inode->i_fop = &sysfs_dir_operations;
- inode->i_nlink = sysfs_count_nlink(sd);
- break;
- case SYSFS_KOBJ_ATTR:
- inode->i_size = PAGE_SIZE;
- inode->i_fop = &sysfs_file_operations;
- break;
- case SYSFS_KOBJ_BIN_ATTR:
- bin_attr = sd->s_bin_attr.bin_attr;
- inode->i_size = bin_attr->size;
- inode->i_fop = &bin_fops;
- break;
- case SYSFS_KOBJ_LINK:
- inode->i_op = &sysfs_symlink_inode_operations;
- break;
- default:
- BUG();
- }
- unlock_new_inode(inode);
- }
再獲取了根目錄的inode和sysfs_dirent後,調用d_alloc_root來獲得dirent。
- /**
- * d_alloc_root - allocate root dentry
- * @root_inode: inode to allocate the root for
- *
- * Allocate a root ("/") dentry for the inode given. The inode is
- * instantiated and returned. %NULL is returned if there is insufficient
- * memory or the inode passed is %NULL.
- */
- struct dentry * d_alloc_root(struct inode * root_inode)
- {
- struct dentry *res = NULL;
- if (root_inode) {
- static const struct qstr name = { .name = "/", .len = 1 };
- res = d_alloc(NULL, &name); /*分配struct dentry,沒有父dentry*/
- if (res) {
- res->d_sb = root_inode->i_sb;
- res->d_parent = res;
- d_instantiated_instantiate(res, root_inode); /*綁定inode和dentry之間的關係*/
- }
- }
- return res;
- }
- /**
- * d_alloc - allocate a dcache entry
- * @parent: parent of entry to allocate
- * @name: qstr of the name
- *
- * Allocates a dentry. It returns %NULL if there is insufficient memory
- * available. On a success the dentry is returned. The name passed in is
- * copied and the copy passed in may be reused after this call.
- */
- struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
- {
- struct dentry *dentry;
- char *dname;
- dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);/*分配struct dentry*/
- if (!dentry)
- return NULL;
- if (name->len > DNAME_INLINE_LEN-1) {
- dname = kmalloc(name->len + 1, GFP_KERNEL);
- if (!dname) {
- kmem_cache_free(dentry_cache, dentry);
- return NULL;
- }
- } else {
- dname = dentry->d_iname;
- }
- dentry->d_name.name = dname;
- dentry->d_name.len = name->len;
- dentry->d_name.hash = name->hash;
- memcpy(dname, name->name, name->len);
- dname[name->len] = 0;
- atomic_set(&dentry->d_count, 1);
- dentry->d_flags = DCACHE_UNHASHED;
- spin_lock_init(&dentry->d_lock);
- dentry->d_inode = NULL;
- dentry->d_parent = NULL;
- dentry->d_sb = NULL;
- dentry->d_op = NULL;
- dentry->d_fsdata = NULL;
- dentry->d_mounted = 0;
- INIT_HLIST_NODE(&dentry->d_hash);
- INIT_LIST_HEAD(&dentry->d_lru);
- INIT_LIST_HEAD(&dentry->d_subdirs);
- INIT_LIST_HEAD(&dentry->d_alias);
- if (parent) { /*有父目錄,則設置指針來表示關係*/
- dentry->d_parent = dget(parent);
- dentry->d_sb = parent->d_sb; /*根dentry的父對象爲自己*/
- } else {
- INIT_LIST_HEAD(&dentry->d_u.d_child);
- }
- spin_lock(&dcache_lock);
- if (parent) /*有父目錄,則添加到父目錄的兒子鏈表中*/
- list_add(&dentry->d_u.d_child, &parent->d_subdirs);
- dentry_stat.nr_dentry++;
- spin_unlock(&dcache_lock);
- return dentry;
- }
- /**
- * d_instantiate - fill in inode information for a dentry
- * @entry: dentry to complete
- * @inode: inode to attach to this dentry
- *
- * Fill in inode information in the entry.
- *
- * This turns negative dentries into productive full members
- * of society.
- *
- * NOTE! This assumes that the inode count has been incremented
- * (or otherwise set) by the caller to indicate that it is now
- * in use by the dcache.
- */
- void d_instantiate(struct dentry *entry, struct inode * inode)
- {
- BUG_ON(!list_empty(&entry->d_alias));
- spin_lock(&dcache_lock);
- __d_instantiate(entry, inode);
- spin_unlock(&dcache_lock);
- security_d_instantiate(entry, inode);
- }
- /* the caller must hold dcache_lock */
- static void __d_instantiate(struct dentry *dentry, struct inode *inode)
- {
- if (inode)
- list_add(&dentry->d_alias, &inode->i_dentry);/*將dentry添加到inode的鏈表中*/
- dentry->d_inode = inode; /*保存dentry對應的inode*/
- fsnotify_d_instantiate(dentry, inode);
- }
該函數首先調用了d_alloc來創建struct dentry,參數parent爲NULL,既然是爲根( / )建立dentry,自然沒有父對象。
接着調用d_instantiate來綁定inode和dentry之間的關係。
在sysfs_fill_super函數執行的最後,將sysfs_root保存到了dentry->d_fsdata。
可見,在sysfs中用sysfs_dirent來表示目錄,但是對於VFS,還是要使用dentry來表示目錄。
8.2.3 do_remount_sb
下列代碼位於fs/super.c。
- /**
- * do_remount_sb - asks filesystem to change mount options.
- * @sb: superblock in question
- * @flags: numeric part of options
- * @data: the rest of options
- * @force: whether or not to force the change
- *
- * Alters the mount options of a mounted file system.
- */
- int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
- {
- int retval;
- int remount_rw;
- #ifdef CONFIG_BLOCK
- if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev))
- return -EACCES;
- #endif
- if (flags & MS_RDONLY)
- acct_auto_close(sb);
- shrink_dcache_sb(sb);
- fsync_super(sb);
- /* If we are remounting RDONLY and current sb is read/write,
- make sure there are no rw files opened */
- if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) {
- if (force)
- mark_files_ro(sb);
- else if (!fs_may_remount_ro(sb))
- return -EBUSY;
- retval = vfs_dq_off(sb, 1);
- if (retval < 0 && retval != -ENOSYS)
- return -EBUSY;
- }
- remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
- if (sb->s_op->remount_fs) {
- lock_super(sb);
- retval = sb->s_op->remount_fs(sb, &flags, data);
- unlock_super(sb);
- if (retval)
- return retval;
- }
- sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
- if (remount_rw)
- vfs_dq_quota_on_remount(sb);
- return 0;
- }
這個函數用來修改掛在選項,這個函數就不分析了,不是重點。
8.2.4simple_set_mnt
下列函數位於fs/namespace.c。
- void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb)
- {
- mnt->mnt_sb = sb;
- mnt->mnt_root = dget(sb->s_root);
- }
8.2.5 小結
這裏,對sysfs的註冊過程做一個總結。
sysfs_init函數調用過程示意圖如下:
在整個過程中,先後使用和創建了許多struct
第一,根據file_system_type表示的sysfs文件系統的類型註冊了sysfs。
第二,建立了vfsmount。
第三,創建了超級塊super_block。
第四,根據sysfs_dirent表示的根目錄,建立了inode。
最後,根據剛纔建立的inode創建了dentry。
除了sysfs_dirent,其他5個結構體都是VFS中基本的數據結構,而sysfs_dirent則是特定於sysfs文件系統的數據結構。
8.3 創建目錄
在前面的描述中,使用sysfs_create_dir在sysfs下建立一個目錄。我們來看下這個函數是如何來建立目錄的。
下列代碼位於fs/sysfs/dir.c。
- /**
- * sysfs_create_dir - create a directory for an object.
- * @kobj: object we're creating directory for.
- */
- int sysfs_create_dir(struct kobject * kobj)
- {
- struct sysfs_dirent *parent_sd, *sd;
- int error = 0;
- BUG_ON(!kobj);
- if (kobj->parent) /*如果有parent,獲取parent對應的sys目錄*/
- parent_sd = kobj->parent->sd;
- else /*沒有則是在sys根目錄*/
- parent_sd = &sysfs_root;
- error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
- if (!error)
- kobj->sd = sd;
- return error;
- }
函數中,首先獲取待建目錄的父sysfs_dirent,然後將它作爲參數 來調用create_dir函數。
很明顯,就是要在父sysfs_dirent下建立新的sysfs_dirent,新建立的sysfs_dirent將保存到參數sd中。
下列代碼位於fs/sysfs/dir.c。
- static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
- const char *name, struct sysfs_dirent **p_sd)
- {
- umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
- struct sysfs_addrm_cxt acxt;
- struct sysfs_dirent *sd;
- int rc;
- /* allocate */ /*分配sysfs_dirent並初始化*/
- sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
- if (!sd)
- return -ENOMEM;
- sd->s_dir.kobj = kobj; /*保存kobject對象*/
- /* link in */
- sysfs_addrm_start(&acxt, parent_sd);/*尋找父sysfs_dirent對應的inode*/
- rc = sysfs_add_one(&acxt, sd); /*檢查父sysfs_dirent下是否已有有該sysfs_dirent,沒有則添加到父sysfs_dirent中*/
- sysfs_addrm_finish(&acxt); /*收尾工作*/
- if (rc == 0) /*rc爲0表示創建成功*/
- *p_sd = sd;
- else
- sysfs_put(sd); /*增加引用計數*/
- return rc;
- }
這裏要注意一下mode變量,改變了使用了宏定義SYSFS_DIR,這個就表示要創建的是一個目錄。
mode還有幾個宏定義可以使用,如下:
- #define SYSFS_KOBJ_ATTR 0x0002
- #define SYSFS_KOBJ_BIN_ATTR 0x0004
- #define SYSFS_KOBJ_LINK 0x0008
- #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK)
8.3.1 sysfs_new_dirent
在create_dir函數中,首先調用了sysfs_new_dirent來建立一個新的sysfs_dirent結構體。
下列代碼位於fs/sysfs/dir.c。
- struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
- {
- char *dup_name = NULL;
- struct sysfs_dirent *sd;
- if (type & SYSFS_COPY_NAME) {
- name = dup_name = kstrdup(name, GFP_KERNEL);
- if (!name)
- return NULL;
- }
- /*分配sysfs_dirent並清0*/
- sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);
- if (!sd)
- goto err_out1;
- if (sysfs_alloc_ino(&sd->s_ino)) /*分配ID號*/
- goto err_out2;
- atomic_set(&sd->s_count, 1);
- atomic_set(&sd->s_active, 0);
- sd->s_name = name;
- sd->s_mode = mode;
- sd->s_flags = type;
- return sd;
- err_out2:
- kmem_cache_free(sysfs_dir_cachep, sd);
- err_out1:
- kfree(dup_name);
- return NULL;
- }
8.3.2 有關sysfs_dirent中的聯合體
分配了sysfs_dirent後,設置了該結構中的聯合體數據。先來看下聯合體中的四個數據結構。
- /* type-specific structures for sysfs_dirent->s_* union members */
- struct sysfs_elem_dir {
- struct kobject *kobj;
- /* children list starts here and goes through sd->s_sibling */
- struct sysfs_dirent *children;
- };
- struct sysfs_elem_symlink {
- struct sysfs_dirent *target_sd;
- };
- struct sysfs_elem_attr {
- struct attribute *attr;
- struct sysfs_open_dirent *open;
- };
- struct sysfs_elem_bin_attr {
- struct bin_attribute *bin_attr;
- struct hlist_head buffers;
- };
在本例中要創建的是目錄,自然使用sysfs_elem_dir結構體,然後保存了kobject對象。
在8.4和8.5中我們將分別看到sysfs_elem_attr和sysfs_elem_symlink的使用。
8.3.3 sysfs_addrm_start
在獲取了父sysfs_dirent,調用sysfs_addrm_start來獲取與之對應的inode。
下列代碼位於fs/sysfs/dir.c。
- /**
- * sysfs_addrm_start - prepare for sysfs_dirent add/remove
- * @acxt: pointer to sysfs_addrm_cxt to be used
- * @parent_sd: parent sysfs_dirent
- *
- * This function is called when the caller is about to add or
- * remove sysfs_dirent under @parent_sd. This function acquires
- * sysfs_mutex, grabs inode for @parent_sd if available and lock
- * i_mutex of it. @acxt is used to keep and pass context to
- * other addrm functions.
- *
- * LOCKING:
- * Kernel thread context (may sleep). sysfs_mutex is locked on
- * return. i_mutex of parent inode is locked on return if
- * available.
- */
- void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
- struct sysfs_dirent *parent_sd)
- {
- struct inode *inode;
- memset(acxt, 0, sizeof(*acxt));
- acxt->parent_sd = parent_sd;
- /* Lookup parent inode. inode initialization is protected by
- * sysfs_mutex, so inode existence can be determined by
- * looking up inode while holding sysfs_mutex.
- */
- mutex_lock(&sysfs_mutex);
- /*根據parent_sd來尋找父inode*/
- inode = ilookup5(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
- parent_sd);
- if (inode) {
- WARN_ON(inode->i_state & I_NEW);
- /* parent inode available */
- acxt->parent_inode = inode; /*保存找到的父inode*/
- /* sysfs_mutex is below i_mutex in lock hierarchy.
- * First, trylock i_mutex. If fails, unlock
- * sysfs_mutex and lock them in order.
- */
- if (!mutex_trylock(&inode->i_mutex)) {
- mutex_unlock(&sysfs_mutex);
- mutex_lock(&inode->i_mutex);
- mutex_lock(&sysfs_mutex);
- }
- }
- }
- /*
- * Context structure to be used while adding/removing nodes.
- */
- struct sysfs_addrm_cxt {
- struct sysfs_dirent *parent_sd;
- struct inode *parent_inode;
- struct sysfs_dirent *removed;
- int cnt;
- };
8.3.4 sysfs_add_one
下列代碼位於fs/sysfs/dir.c。
- /**
- * sysfs_add_one - add sysfs_dirent to parent
- * @acxt: addrm context to use
- * @sd: sysfs_dirent to be added
- *
- * Get @acxt->parent_sd and set sd->s_parent to it and increment
- * nlink of parent inode if @sd is a directory and link into the
- * children list of the parent.
- *
- * This function should be called between calls to
- * sysfs_addrm_start() and sysfs_addrm_finish() and should be
- * passed the same @acxt as passed to sysfs_addrm_start().
- *
- * LOCKING:
- * Determined by sysfs_addrm_start().
- *
- * RETURNS:
- * 0 on success, -EEXIST if entry with the given name already
- * exists.
- */
- int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
- {
- int ret;
- ret = __sysfs_add_one(acxt, sd);
- if (ret == -EEXIST) {
- char *path = kzalloc(PATH_MAX, GFP_KERNEL);
- WARN(1, KERN_WARNING
- "sysfs: cannot create duplicate filename '%s'\n",
- (path == NULL) ? sd->s_name :
- strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"),
- sd->s_name));
- kfree(path);
- }
- return ret;
- }
- /**
- * __sysfs_add_one - add sysfs_dirent to parent without warning
- * @acxt: addrm context to use
- * @sd: sysfs_dirent to be added
- *
- * Get @acxt->parent_sd and set sd->s_parent to it and increment
- * nlink of parent inode if @sd is a directory and link into the
- * children list of the parent.
- *
- * This function should be called between calls to
- * sysfs_addrm_start() and sysfs_addrm_finish() and should be
- * passed the same @acxt as passed to sysfs_addrm_start().
- *
- * LOCKING:
- * Determined by sysfs_addrm_start().
- *
- * RETURNS:
- * 0 on success, -EEXIST if entry with the given name already
- * exists.
- */
- int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
- {
- /*查找該parent_sd下有無將要建立的sd,沒有返回NULL*/
- if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
- return -EEXIST;
- sd->s_parent = sysfs_get(acxt->parent_sd); /*設置父sysfs_dirent,增加父sysfs_dirent的引用計數*/
- if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode) /*如果要創建的是目錄或文件,並且有父inode*/
- inc_nlink(acxt->parent_inode); /*inode->i_nlink加1*/
- acxt->cnt++;
- sysfs_link_sibling(sd);
- return 0;
- }
- /**
- * sysfs_find_dirent - find sysfs_dirent with the given name
- * @parent_sd: sysfs_dirent to search under
- * @name: name to look for
- *
- * Look for sysfs_dirent with name @name under @parent_sd.
- *
- * LOCKING:
- * mutex_lock(sysfs_mutex)
- *
- * RETURNS:
- * Pointer to sysfs_dirent if found, NULL if not.
- */
- struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
- const unsigned char *name)
- {
- struct sysfs_dirent *sd;
- for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
- if (!strcmp(sd->s_name, name))
- return sd;
- return NULL;
- }
- /**
- * sysfs_link_sibling - link sysfs_dirent into sibling list
- * @sd: sysfs_dirent of interest
- *
- * Link @sd into its sibling list which starts from
- * sd->s_parent->s_dir.children.
- *
- * Locking:
- * mutex_lock(sysfs_mutex)
- */
- static void sysfs_link_sibling(struct sysfs_dirent *sd)
- {
- struct sysfs_dirent *parent_sd = sd->s_parent;
- struct sysfs_dirent **pos;
- BUG_ON(sd->s_sibling);
- /* Store directory entries in order by ino. This allows
- * readdir to properly restart without having to add a
- * cursor into the s_dir.children list.
- */
- /*children鏈表根據s_ino按升序排列,現在將sd插入到正確的兒子鏈表中*/
- for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {
- if (sd->s_ino < (*pos)->s_ino)
- break;
- }
- /*插入鏈表*/
- sd->s_sibling = *pos;
- *pos = sd;
- }
8.3.5 sysfs_addrm_finish
下列代碼位於fs/sysfs/dir.c。
- /**
- * sysfs_addrm_finish - finish up sysfs_dirent add/remove
- * @acxt: addrm context to finish up
- *
- * Finish up sysfs_dirent add/remove. Resources acquired by
- * sysfs_addrm_start() are released and removed sysfs_dirents are
- * cleaned up. Timestamps on the parent inode are updated.
- *
- * LOCKING:
- * All mutexes acquired by sysfs_addrm_start() are released.
- */
- void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
- {
- /* release resources acquired by sysfs_addrm_start() */
- mutex_unlock(&sysfs_mutex);
- if (acxt->parent_inode) {
- struct inode *inode = acxt->parent_inode;
- /* if added/removed, update timestamps on the parent */
- if (acxt->cnt)
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;/*更新父inode的時間*/
- mutex_unlock(&inode->i_mutex);
- iput(inode);
- }
- /* kill removed sysfs_dirents */
- while (acxt->removed) {
- struct sysfs_dirent *sd = acxt->removed;
- acxt->removed = sd->s_sibling;
- sd->s_sibling = NULL;
- sysfs_drop_dentry(sd);
- sysfs_deactivate(sd);
- unmap_bin_file(sd);
- sysfs_put(sd);
- }
- }
該函數結束了添加sysfs_dirent的工作,這個就不多做說明了。
至此,添加一個目錄的工作已經完成了,添加目錄的工作其實就是創建了一個新的sysfs_dirent,並把它添加到父sysfs_dirent中。
下面我們看下如何添加屬性文件。
8.4 創建屬性文件
添加屬性文件使用sysfs_create_file函數。
下列函數位於fs/sysfs/file.c。
- /**
- * sysfs_create_file - create an attribute file for an object.
- * @kobj: object we're creating for.
- * @attr: attribute descriptor.
- */
- int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
- {
- BUG_ON(!kobj || !kobj->sd || !attr);
- return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
- }
- int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
- int type)
- {
- return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
- }
- int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
- const struct attribute *attr, int type, mode_t amode)
- {
- umode_t mode = (amode & S_IALLUGO) | S_IFREG;
- struct sysfs_addrm_cxt acxt;
- struct sysfs_dirent *sd;
- int rc;
- /*分配sysfs_dirent並初始化*/
- sd = sysfs_new_dirent(attr->name, mode, type);
- if (!sd)
- return -ENOMEM;
- sd->s_attr.attr = (void *)attr;
- sysfs_addrm_start(&acxt, dir_sd); /*尋找父sysfs_dirent對應的inode*/
- rc = sysfs_add_one(&acxt, sd); /*檢查父sysfs_dirent下是否已有有該sysfs_dirent,沒有則創建*/
- sysfs_addrm_finish(&acxt); /*收尾工作*/
- if (rc) /*0表示創建成功*/
- sysfs_put(sd);
- return rc;
- }
sysfs_create_file用參數SYSFS_KOBJ_ATTR(表示建立屬性文件)來調用了sysfs_add_file,後者又直接調用了sysfs_add_file_mode。
sysfs_add_file_mode函數的執行和8.3節的create_dir函數非常類似,只不過它並沒有保存kobject對象,也就是說該sysfs_dirent並沒有一個對應的kobject對象。
需要注意的是,這裏是建立屬性文件,因此使用了聯合體中的結構體s_attr。
8.5 創建symlink
最後,來看下symlink的建立。
- /**
- * sysfs_create_link - create symlink between two objects.
- * @kobj: object whose directory we're creating the link in.
- * @target: object we're pointing to.
- * @name: name of the symlink.
- */
- int sysfs_create_link(struct kobject *kobj, struct kobject *target,
- const char *name)
- {
- return sysfs_do_create_link(kobj, target, name, 1);
- }
- static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
- const char *name, int warn)
- {
- struct sysfs_dirent *parent_sd = NULL;
- struct sysfs_dirent *target_sd = NULL;
- struct sysfs_dirent *sd = NULL;
- struct sysfs_addrm_cxt acxt;
- int error;
- BUG_ON(!name);
- if (!kobj) /*kobj爲空,表示在sysyfs跟目錄下建立symlink*/
- parent_sd = &sysfs_root;
- else /*有父sysfs_dirent*/
- parent_sd = kobj->sd;
- error = -EFAULT;
- if (!parent_sd)
- goto out_put;
- /* target->sd can go away beneath us but is protected with
- * sysfs_assoc_lock. Fetch target_sd from it.
- */
- spin_lock(&sysfs_assoc_lock);
- if (target->sd)
- target_sd = sysfs_get(target->sd); 、/*獲取目標對象的sysfs_dirent*/
- spin_unlock(&sysfs_assoc_lock);
- error = -ENOENT;
- if (!target_sd)
- goto out_put;
- error = -ENOMEM;
- /*分配sysfs_dirent並初始化*/
- sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
- if (!sd)
- goto out_put;
- sd->s_symlink.target_sd = target_sd;/*保存目標sysfs_dirent*/
- target_sd = NULL; /* reference is now owned by the symlink */
- sysfs_addrm_start(&acxt, parent_sd);/*尋找父sysfs_dirent對應的inode*/
- if (warn)
- error = sysfs_add_one(&acxt, sd);/*檢查父sysfs_dirent下是否已有有該sysfs_dirent,沒有則創建*/
- else
- error = __sysfs_add_one(&acxt, sd);
- sysfs_addrm_finish(&acxt); /*收尾工作*/
- if (error)
- goto out_put;
- return 0;
- out_put:
- sysfs_put(target_sd);
- sysfs_put(sd);
- return error;
- }
這個函數的執行也和8.3節的create_dir函數非常類似。其次,symlink同樣沒有對應的kobject對象。
因爲sysfs_dirent表示的是symlink,這裏使用了聯合體中的s_symlink。同時設置了s_symlink.target_sd指向的目標sysfs_dirent爲參數targed_sd。
8.6 小結
本節首先對syfs這一特殊的文件系統的註冊過程進行了分析。接着對目錄,屬性文件和symlink的建立進行了分析。這三者的建立過程基本一致,但是目錄
有kobject對象,而剩餘兩個沒有。其次,這三者的每個sysfs_dirent中,都使用了自己的聯合體數據。
9 總結
本文首先對sysfs的核心數據kobject,kset等數據結構做出了分析,正是通過它們才能向用戶空間呈現出設備驅動模型。
接着,以/sys/bus目錄的建立爲例,來說明如何通過kobject和kset來建立該bus目錄。
隨後,介紹了驅動模型中表示總線,設備和驅動的三個數據結構。
然後,介紹了platform總線(bus/platform)的註冊,再介紹了虛擬的platform設備(devices/platform)的添加過程。
之後 ,以spi主控制器的platform設備爲例,介紹了該platform設備和相應的驅動的註冊過程。
最後,介紹了底層sysfs文件系統的註冊過程和如何建立目錄,屬性文件和symlink的過程。
更新說明:
2012.09.14 在6.2.9中,添加分析 bus_for_each_drv。