不浮躁
分类: LINUX
2014-11-14 21:09:56
原文地址:在sysfs文件系统中创建目录 作者: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) :
"
185 kobj->kset ?
kobject_name(&kobj->kset->kobj) : "
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对象的创建。