在sysfs文件系统中创建目录
时间:2010-08-09 来源:tq08g2z
在sysfs文件系统中创建目录
文接上回,来讨论在sysfs文件系统中创建目录的问题。话说,一个kobject在sysfs层次体系中对应于一个目录。在kset_register()和kobject_add_varg()中会调用kobject_add_internal(struct kobject *kobj)函数来完成最终在sysfs文件系统中添加目录的工作。回想一些,之前的工作都对kobject做了些什么:kobject的ktype字段肯定是经过了适当的设置了,还有parent字段和name字段也一样。接着,没多久就见到了kobject_add_internal()的出场。这个函数的定义为:
---------------------------------------------------------------------
lib/kobject.c
158 static int kobject_add_internal(struct kobject *kobj)
159 {
160 int error = 0;
161 struct kobject *parent;
162
163 if (!kobj)
164 return -ENOENT;
165
166 if (!kobj->name || !kobj->name[0]) {
167 WARN(1, "kobject: (%p): attempted to be registered with empty "
168 "name!\n", kobj);
169 return -EINVAL;
170 }
171
172 parent = kobject_get(kobj->parent);
173
174 /* join kset if set, use it as parent if we do not already have one */
175 if (kobj->kset) {
176 if (!parent)
177 parent = kobject_get(&kobj->kset->kobj);
178 kobj_kset_join(kobj);
179 kobj->parent = parent;
180 }
181
182 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set:
183 '%s'\n", kobject_name(kobj), kobj, __func__,
184 parent ? kobject_name(parent) : "<NULL>",
185 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
186
187 error = create_dir(kobj);
188 if (error) {
189 kobj_kset_leave(kobj);
190 kobject_put(parent);
191 kobj->parent = NULL;
192
193 /* be noisy on error issues */
194 if (error == -EEXIST)
195 printk(KERN_ERR "%s failed for %s with "
196 "-EEXIST, don't try to register things with "
197 "the same name in the same directory.\n",
198 __func__, kobject_name(kobj));
199 else
200 printk(KERN_ERR "%s failed for %s (%d)\n",
201 __func__, kobject_name(kobj), error);
202 dump_stack();
203 } else
204 kobj->state_in_sysfs = 1;
205
206 return error;
207 }
---------------------------------------------------------------------
这个函数完成如下操作:
1、检查传递的kobject的有效性,若为NULL,则返回-ENOENT。
2、检查kobject名称的有效性,若名称无效,则返回-EINVAL。
3、若kobject是属于某一个kset的,则检查kobject的parent字段是否指向有效的kobject,若没有,则将kobject加入到它的kset的链表里,并使kobject的parent字段指向它所属的kset的内嵌kobject。若kobject的parent指向有效的kobject,则仅仅将kobject加入到它的kset的链表里。
4、调用create_dir(kobj)函数来在sysfs文件系统层次结构中为kobject创建目录。create_dir()函数定义如下:
---------------------------------------------------------------------
lib/kobject.c
47 static int create_dir(struct kobject *kobj)
48 {
49 int error = 0;
50 if (kobject_name(kobj)) {
51 error = sysfs_create_dir(kobj);
52 if (!error) {
53 error = populate_dir(kobj);
54 if (error)
55 sysfs_remove_dir(kobj);
56 }
57 }
58 return error;
59 }
---------------------------------------------------------------------
这个函数接受唯一的一个参数,也就是要为其创建目录的kobject的地址。这个函数的实现也是多么的简洁啊。它完成的操作如下:
a.检查传递进来kobject对象的name的有效性,若无效,则返回0。
b.若有效,则调用sysfs_create_dir(kobj)来在sysfs中创建目录。sysfs_create_dir()的定义如下:
---------------------------------------------------------------------
fs/sysfs/dir.c
611 int sysfs_create_dir(struct kobject * kobj)
612 {
613 struct sysfs_dirent *parent_sd, *sd;
614 int error = 0;
615
616 BUG_ON(!kobj);
617
618 if (kobj->parent)
619 parent_sd = kobj->parent->sd;
620 else
621 parent_sd = &sysfs_root;
622
623 error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
624 if (!error)
625 kobj->sd = sd;
626 return error;
627 }
---------------------------------------------------------------------
这个函数首先会找到要为其创建目录的kobject的sysfs_dirent对象的父sysfs_dirent,通常是由父kobject的sd字段所指向。若父kobject为NULL,则设为sysfs跟目录的sysfs_dirent。
然后调用create_dir(kobj, parent_sd, kobject_name(kobj) , &sd)来创建目录。随后详细来看创建目录的过程。
最后,若成功,设置kobject的sd字段指向为其新创建的sysfs_dirent,并返回0。若失败则返回错误码。
创建目录的这个函数和前面的那个函数同名,不过此create_dir()非彼create_dir()。所定义的位置自是不同,所需要的参数也迥异。这个create_dir()的定义为:
---------------------------------------------------------------------
fs/sysfs/dir.c
574 static int create_dir(struct kobject *kobj, struct sysfs_dirent
575 *parent_sd, const char *name, struct sysfs_dirent **p_sd)
576 {
577 umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
578 struct sysfs_addrm_cxt acxt;
579 struct sysfs_dirent *sd;
580 int rc;
581
582 /* allocate */
583 sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
584 if (!sd)
585 return -ENOMEM;
586 sd->s_dir.kobj = kobj;
587
588 /* link in */
589 sysfs_addrm_start(&acxt, parent_sd);
590 rc = sysfs_add_one(&acxt, sd);
591 sysfs_addrm_finish(&acxt);
592
593 if (rc == 0)
594 *p_sd = sd;
595 else
596 sysfs_put(sd);
597
598 return rc;
599 }
---------------------------------------------------------------------
先来对这个函数的几个参数做一些说明,kobj为要为其创建目录的kobject的地址,parent_sd为父sysfs_dirent的地址,name为名字,还有一个sysfs_dirent的二级指针p_sd,它主要用于保存创建的sysfs_dirent对象的地址。这个函数完成如下操作:
(1).调用sysfs_new_dirent(name, mode, SYSFS_DIR)来为kobject创建sysfs_dirent对象。并由局部变量sd指向它。sysfs_new_dirent()函数的定义为:
---------------------------------------------------------------------
fs/sysfs/dir.c
302 struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
303 {
304 char *dup_name = NULL;
305 struct sysfs_dirent *sd;
306
307 if (type & SYSFS_COPY_NAME) {
308 name = dup_name = kstrdup(name, GFP_KERNEL);
309 if (!name)
310 return NULL;
311 }
312
313 sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);
314 if (!sd)
315 goto err_out1;
316
317 if (sysfs_alloc_ino(&sd->s_ino))
318 goto err_out2;
319
320 atomic_set(&sd->s_count, 1);
321 atomic_set(&sd->s_active, 0);
322
323 sd->s_name = name;
324 sd->s_mode = mode;
325 sd->s_flags = type;
326
327 return sd;
328
329 err_out2:
330 kmem_cache_free(sysfs_dir_cachep, sd);
331 err_out1:
332 kfree(dup_name);
333 return NULL;
334 }
---------------------------------------------------------------------
这个sysfs_new_dirent()函数完成的工作还算清晰。调用kstrdup(name, GFP_KERNEL)为sysfs_dirent的名字分配内核缓冲区,并将传递进来的kobject名字复制到该缓冲区,由局部变量dup_name和参数name指向这个缓冲区。调用kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL)来在sysfs_dirent的缓存区sysfs_dir_cachep中分配sysfs_dirent对象,由局部变量sd指向它。调用sysfs_alloc_ino(&sd->s_ino)来为sysfs_dirent分配索引节点号。设置sd->s_name = name、sd->s_mode = mode及sd->s_flags = type。最后返回sysfs_dirent对象地址。
这个地方,分配inode节点号的sysfs_alloc_ino()也是颇有意思的,回头再好好看下这个函数。
(2).设置新分配的sysfs_dirent对象的sd->s_dir.kobj字段指向kobject。
(3).调用sysfs_addrm_start(&acxt, parent_sd)来为sysfs_dirent的添加做准备。这个函数定义为:
---------------------------------------------------------------------
fs/sysfs/dir.c
336 /**
337 * sysfs_addrm_start - prepare for sysfs_dirent add/remove
338 * @acxt: pointer to sysfs_addrm_cxt to be used
339 * @parent_sd: parent sysfs_dirent
340 *
341 * This function is called when the caller is about to add or
342 * remove sysfs_dirent under @parent_sd. This function
343 * acquires sysfs_mutex. @acxt is used to keep and pass context
344 * to other addrm functions.
345 *
346 * LOCKING:
347 * Kernel thread context (may sleep). sysfs_mutex is locked on
348 * return.
349 */
350 void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
351 struct sysfs_dirent *parent_sd)
352 {
353 memset(acxt, 0, sizeof(*acxt));
354 acxt->parent_sd = parent_sd;
355
356 mutex_lock(&sysfs_mutex);
357 }
---------------------------------------------------------------------
它完成的工作主要就是设置传递进来的sysfs_addrm_cxt指针所指向的对象的parent_sd字段指向sysfs_dirent,并获得sysfs_mutex锁。
(4).调用sysfs_add_one(&acxt, sd)来向父sysfs_dirent添加sysfs_dirent。这个函数定义为:
---------------------------------------------------------------------
fs/sysfs/dir.c
359 /**
360 * __sysfs_add_one - add sysfs_dirent to parent without warning
361 * @acxt: addrm context to use
362 * @sd: sysfs_dirent to be added
363 *
364 * Get @acxt->parent_sd and set sd->s_parent to it and increment
365 * nlink of parent inode if @sd is a directory and link into the
366 * children list of the parent.
367 *
368 * This function should be called between calls to
369 * sysfs_addrm_start() and sysfs_addrm_finish() and should be
370 * passed the same @acxt as passed to sysfs_addrm_start().
371 *
372 * LOCKING:
373 * Determined by sysfs_addrm_start().
374 *
375 * RETURNS:
376 * 0 on success, -EEXIST if entry with the given name already
377 * exists.
378 */
379 int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
380 {
381 struct sysfs_inode_attrs *ps_iattr;
382
383 if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
384 return -EEXIST;
385
386 sd->s_parent = sysfs_get(acxt->parent_sd);
387
388 sysfs_link_sibling(sd);
389
390 /* Update timestamps on the parent */
391 ps_iattr = acxt->parent_sd->s_iattr;
392 if (ps_iattr) {
393 struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
394 ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
395 }
396
397 return 0;
398 }
399
400 /**
401 * sysfs_pathname - return full path to sysfs dirent
402 * @sd: sysfs_dirent whose path we want
403 * @path: caller allocated buffer
404 *
405 * Gives the name "/" to the sysfs_root entry; any path returned
406 * is relative to wherever sysfs is mounted.
407 *
408 * XXX: does no error checking on @path size
409 */
410 static char *sysfs_pathname(struct sysfs_dirent *sd, char *path)
411 {
412 if (sd->s_parent) {
413 sysfs_pathname(sd->s_parent, path);
414 strcat(path, "/");
415 }
416 strcat(path, sd->s_name);
417 return path;
418 }
419
420 /**
421 * sysfs_add_one - add sysfs_dirent to parent
422 * @acxt: addrm context to use
423 * @sd: sysfs_dirent to be added
424 *
425 * Get @acxt->parent_sd and set sd->s_parent to it and increment
426 * nlink of parent inode if @sd is a directory and link into the
427 * children list of the parent.
428 *
429 * This function should be called between calls to
430 * sysfs_addrm_start() and sysfs_addrm_finish() and should be
431 * passed the same @acxt as passed to sysfs_addrm_start().
432 *
433 * LOCKING:
434 * Determined by sysfs_addrm_start().
435 *
436 * RETURNS:
437 * 0 on success, -EEXIST if entry with the given name already
438 * exists.
439 */
440 int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
441 {
442 int ret;
443
444 ret = __sysfs_add_one(acxt, sd);
445 if (ret == -EEXIST) {
446 char *path = kzalloc(PATH_MAX, GFP_KERNEL);
447 WARN(1, KERN_WARNING
448 "sysfs: cannot create duplicate filename
449 '%s'\n", (path == NULL) ? sd->s_name :
450 strcat(strcat(sysfs_pathname(acxt->parent_sd,
451 path), "/"), sd->s_name));
452 kfree(path);
453 }
454
455 return ret;
456 }
---------------------------------------------------------------------
我们看到,上面这个函数主要是设置sysfs_dirent的s_parent指向其父sysfs_dirent,并用sysfs_dirent的s_sibling字段把它连接到其父sysfs_diren的s_dir.children链表中。
(5).调用sysfs_addrm_finish(&acxt)来结束sysfs_dirent的添加。这个函数定义为:
---------------------------------------------------------------------
fs/sysfs/dir.c
493 /**
494 * sysfs_addrm_finish - finish up sysfs_dirent add/remove
495 * @acxt: addrm context to finish up
496 *
497 * Finish up sysfs_dirent add/remove. Resources acquired by
498 * sysfs_addrm_start() are released and removed sysfs_dirents are
499 * cleaned up.
500 *
501 * LOCKING:
502 * sysfs_mutex is released.
503 */
504 void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
505 {
506 /* release resources acquired by sysfs_addrm_start() */
507 mutex_unlock(&sysfs_mutex);
508
509 /* kill removed sysfs_dirents */
510 while (acxt->removed) {
511 struct sysfs_dirent *sd = acxt->removed;
512
513 acxt->removed = sd->s_sibling;
514 sd->s_sibling = NULL;
515
516 sysfs_deactivate(sd);
517 unmap_bin_file(sd);
518 sysfs_put(sd);
519 }
520 }
---------------------------------------------------------------------
这个函数的主要工作就是释放锁。另外就是在移除sysfs_dirent时会完成一些工作。
(6).使用sysfs_dirent的二级指针p_sd来将创建的sysfs_dirent对象的地址返回给调用者。
5、设置kobject的state_in_sysfs字段为1,以此说明该kobject已经被添加进了sysfs文件系统。
这整个过程主要是为kobject创建并初始化sysfs_dirent,将它和它的父sysfs_dirent及兄弟sysfs_dirent连接起来。
未明了的问题:对于一个目录来说的inode对象的创建。