aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-06-13 15:27:24 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-11 19:09:09 -0400
commitfb6896da37f19be4b75154c14d1cd79231255b17 (patch)
treed1480206694a0642067dc7dc5b40bd63b038cac0 /fs/sysfs/dir.c
parent3007e997de91ec59af39a3f9c91595b31ae6e08b (diff)
sysfs: restructure add/remove paths and fix inode update
The original add/remove code had the following problems. * parent's timestamps are updated on dentry instantiation. this is incorrect with reclaimable files. * updating parent's timestamps isn't synchronized. * parent nlink update assumes the inode is accessible which won't be true once directory dentries are made reclaimable. This patch restructures add/remove paths to resolve the above problems. Add/removal are done in the following steps. 1. sysfs_addrm_start() : acquire locks including sysfs_mutex and other resources. 2-a. sysfs_add_one() : add new sd. linking the new sd into the children list is caller's responsibility. 2-b. sysfs_remove_one() : remove a sd. unlinking the sd from the children list is caller's responsibility. 3. sysfs_addrm_finish() : release all resources and clean up. Steps 2-a and/or 2-b can be repeated multiple times. Parent's inode is looked up during sysfs_addrm_start(). If available (always at the moment), it's pinned and nlink is updated as sd's are added and removed. Timestamps are updated during finish if any sd has been added or removed. If parent's inode is not available during start, sysfs_mutex ensures that parent inode is not created till add/remove is complete. All the complexity is contained inside the helper functions. Especially, dentry/inode handling is properly hidden from the rest of sysfs which now mostly operate on sysfs_dirents. As an added bonus, codes which use these helpers to add and remove sysfs_dirents are now more structured and simpler. Signed-off-by: Tejun Heo <htejun@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r--fs/sysfs/dir.c250
1 files changed, 186 insertions, 64 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 9fc8558fd86c..edb30621b82f 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -30,7 +30,7 @@ static DEFINE_IDA(sysfs_ino_ida);
30 * Locking: 30 * Locking:
31 * mutex_lock(sysfs_mutex) 31 * mutex_lock(sysfs_mutex)
32 */ 32 */
33static void sysfs_link_sibling(struct sysfs_dirent *sd) 33void sysfs_link_sibling(struct sysfs_dirent *sd)
34{ 34{
35 struct sysfs_dirent *parent_sd = sd->s_parent; 35 struct sysfs_dirent *parent_sd = sd->s_parent;
36 36
@@ -49,7 +49,7 @@ static void sysfs_link_sibling(struct sysfs_dirent *sd)
49 * Locking: 49 * Locking:
50 * mutex_lock(sysfs_mutex) 50 * mutex_lock(sysfs_mutex)
51 */ 51 */
52static void sysfs_unlink_sibling(struct sysfs_dirent *sd) 52void sysfs_unlink_sibling(struct sysfs_dirent *sd)
53{ 53{
54 struct sysfs_dirent **pos; 54 struct sysfs_dirent **pos;
55 55
@@ -165,7 +165,7 @@ void sysfs_put_active_two(struct sysfs_dirent *sd)
165 * 165 *
166 * Deny new active references and drain existing ones. 166 * Deny new active references and drain existing ones.
167 */ 167 */
168void sysfs_deactivate(struct sysfs_dirent *sd) 168static void sysfs_deactivate(struct sysfs_dirent *sd)
169{ 169{
170 DECLARE_COMPLETION_ONSTACK(wait); 170 DECLARE_COMPLETION_ONSTACK(wait);
171 int v; 171 int v;
@@ -318,27 +318,164 @@ static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry)
318 d_rehash(dentry); 318 d_rehash(dentry);
319} 319}
320 320
321static int sysfs_ilookup_test(struct inode *inode, void *arg)
322{
323 struct sysfs_dirent *sd = arg;
324 return inode->i_ino == sd->s_ino;
325}
326
321/** 327/**
322 * sysfs_attach_dirent - attach sysfs_dirent to its parent and dentry 328 * sysfs_addrm_start - prepare for sysfs_dirent add/remove
323 * @sd: sysfs_dirent to attach 329 * @acxt: pointer to sysfs_addrm_cxt to be used
324 * @parent_sd: parent to attach to (optional) 330 * @parent_sd: parent sysfs_dirent
325 * @dentry: dentry to be associated to @sd (optional)
326 * 331 *
327 * Attach @sd to @parent_sd and/or @dentry. Both are optional. 332 * This function is called when the caller is about to add or
333 * remove sysfs_dirent under @parent_sd. This function acquires
334 * sysfs_mutex, grabs inode for @parent_sd if available and lock
335 * i_mutex of it. @acxt is used to keep and pass context to
336 * other addrm functions.
328 * 337 *
329 * LOCKING: 338 * LOCKING:
330 * mutex_lock(sysfs_mutex) 339 * Kernel thread context (may sleep). sysfs_mutex is locked on
340 * return. i_mutex of parent inode is locked on return if
341 * available.
331 */ 342 */
332void sysfs_attach_dirent(struct sysfs_dirent *sd, 343void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
333 struct sysfs_dirent *parent_sd, struct dentry *dentry) 344 struct sysfs_dirent *parent_sd)
334{ 345{
335 if (dentry) 346 struct inode *inode;
336 sysfs_attach_dentry(sd, dentry);
337 347
338 if (parent_sd) { 348 memset(acxt, 0, sizeof(*acxt));
339 sd->s_parent = sysfs_get(parent_sd); 349 acxt->parent_sd = parent_sd;
340 sysfs_link_sibling(sd); 350
351 /* Lookup parent inode. inode initialization and I_NEW
352 * clearing are protected by sysfs_mutex. By grabbing it and
353 * looking up with _nowait variant, inode state can be
354 * determined reliably.
355 */
356 mutex_lock(&sysfs_mutex);
357
358 inode = ilookup5_nowait(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
359 parent_sd);
360
361 if (inode && !(inode->i_state & I_NEW)) {
362 /* parent inode available */
363 acxt->parent_inode = inode;
364
365 /* sysfs_mutex is below i_mutex in lock hierarchy.
366 * First, trylock i_mutex. If fails, unlock
367 * sysfs_mutex and lock them in order.
368 */
369 if (!mutex_trylock(&inode->i_mutex)) {
370 mutex_unlock(&sysfs_mutex);
371 mutex_lock(&inode->i_mutex);
372 mutex_lock(&sysfs_mutex);
373 }
374 } else
375 iput(inode);
376}
377
378/**
379 * sysfs_add_one - add sysfs_dirent to parent
380 * @acxt: addrm context to use
381 * @sd: sysfs_dirent to be added
382 *
383 * Get @acxt->parent_sd and set sd->s_parent to it and increment
384 * nlink of parent inode if @sd is a directory. @sd is NOT
385 * linked into the children list of the parent. The caller
386 * should invoke sysfs_link_sibling() after this function
387 * completes if @sd needs to be on the children list.
388 *
389 * This function should be called between calls to
390 * sysfs_addrm_start() and sysfs_addrm_finish() and should be
391 * passed the same @acxt as passed to sysfs_addrm_start().
392 *
393 * LOCKING:
394 * Determined by sysfs_addrm_start().
395 */
396void sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
397{
398 sd->s_parent = sysfs_get(acxt->parent_sd);
399
400 if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
401 inc_nlink(acxt->parent_inode);
402
403 acxt->cnt++;
404}
405
406/**
407 * sysfs_remove_one - remove sysfs_dirent from parent
408 * @acxt: addrm context to use
409 * @sd: sysfs_dirent to be added
410 *
411 * Mark @sd removed and drop nlink of parent inode if @sd is a
412 * directory. @sd is NOT unlinked from the children list of the
413 * parent. The caller is repsonsible for removing @sd from the
414 * children list before calling this function.
415 *
416 * This function should be called between calls to
417 * sysfs_addrm_start() and sysfs_addrm_finish() and should be
418 * passed the same @acxt as passed to sysfs_addrm_start().
419 *
420 * LOCKING:
421 * Determined by sysfs_addrm_start().
422 */
423void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
424{
425 BUG_ON(sd->s_sibling || (sd->s_flags & SYSFS_FLAG_REMOVED));
426
427 sd->s_flags |= SYSFS_FLAG_REMOVED;
428 sd->s_sibling = acxt->removed;
429 acxt->removed = sd;
430
431 if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
432 drop_nlink(acxt->parent_inode);
433
434 acxt->cnt++;
435}
436
437/**
438 * sysfs_addrm_finish - finish up sysfs_dirent add/remove
439 * @acxt: addrm context to finish up
440 *
441 * Finish up sysfs_dirent add/remove. Resources acquired by
442 * sysfs_addrm_start() are released and removed sysfs_dirents are
443 * cleaned up. Timestamps on the parent inode are updated.
444 *
445 * LOCKING:
446 * All mutexes acquired by sysfs_addrm_start() are released.
447 *
448 * RETURNS:
449 * Number of added/removed sysfs_dirents since sysfs_addrm_start().
450 */
451int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
452{
453 /* release resources acquired by sysfs_addrm_start() */
454 mutex_unlock(&sysfs_mutex);
455 if (acxt->parent_inode) {
456 struct inode *inode = acxt->parent_inode;
457
458 /* if added/removed, update timestamps on the parent */
459 if (acxt->cnt)
460 inode->i_ctime = inode->i_mtime = CURRENT_TIME;
461
462 mutex_unlock(&inode->i_mutex);
463 iput(inode);
464 }
465
466 /* kill removed sysfs_dirents */
467 while (acxt->removed) {
468 struct sysfs_dirent *sd = acxt->removed;
469
470 acxt->removed = sd->s_sibling;
471 sd->s_sibling = NULL;
472
473 sysfs_drop_dentry(sd);
474 sysfs_deactivate(sd);
475 sysfs_put(sd);
341 } 476 }
477
478 return acxt->cnt;
342} 479}
343 480
344/** 481/**
@@ -396,19 +533,20 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
396 const char *name, struct sysfs_dirent **p_sd) 533 const char *name, struct sysfs_dirent **p_sd)
397{ 534{
398 struct dentry *parent = parent_sd->s_dentry; 535 struct dentry *parent = parent_sd->s_dentry;
536 struct sysfs_addrm_cxt acxt;
399 int error; 537 int error;
400 umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; 538 umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
401 struct dentry *dentry; 539 struct dentry *dentry;
402 struct inode *inode; 540 struct inode *inode;
403 struct sysfs_dirent *sd; 541 struct sysfs_dirent *sd;
404 542
405 mutex_lock(&parent->d_inode->i_mutex); 543 sysfs_addrm_start(&acxt, parent_sd);
406 544
407 /* allocate */ 545 /* allocate */
408 dentry = lookup_one_len(name, parent, strlen(name)); 546 dentry = lookup_one_len(name, parent, strlen(name));
409 if (IS_ERR(dentry)) { 547 if (IS_ERR(dentry)) {
410 error = PTR_ERR(dentry); 548 error = PTR_ERR(dentry);
411 goto out_unlock; 549 goto out_finish;
412 } 550 }
413 551
414 error = -EEXIST; 552 error = -EEXIST;
@@ -433,23 +571,18 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
433 } 571 }
434 572
435 /* link in */ 573 /* link in */
436 mutex_lock(&sysfs_mutex);
437
438 error = -EEXIST; 574 error = -EEXIST;
439 if (sysfs_find_dirent(parent_sd, name)) { 575 if (sysfs_find_dirent(parent_sd, name))
440 mutex_unlock(&sysfs_mutex);
441 goto out_iput; 576 goto out_iput;
442 }
443 577
578 sysfs_add_one(&acxt, sd);
579 sysfs_link_sibling(sd);
444 sysfs_instantiate(dentry, inode); 580 sysfs_instantiate(dentry, inode);
445 inc_nlink(parent->d_inode); 581 sysfs_attach_dentry(sd, dentry);
446 sysfs_attach_dirent(sd, parent_sd, dentry);
447
448 mutex_unlock(&sysfs_mutex);
449 582
450 *p_sd = sd; 583 *p_sd = sd;
451 error = 0; 584 error = 0;
452 goto out_unlock; /* pin directory dentry in core */ 585 goto out_finish; /* pin directory dentry in core */
453 586
454 out_iput: 587 out_iput:
455 iput(inode); 588 iput(inode);
@@ -459,8 +592,8 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
459 d_drop(dentry); 592 d_drop(dentry);
460 out_dput: 593 out_dput:
461 dput(dentry); 594 dput(dentry);
462 out_unlock: 595 out_finish:
463 mutex_unlock(&parent->d_inode->i_mutex); 596 sysfs_addrm_finish(&acxt);
464 return error; 597 return error;
465} 598}
466 599
@@ -561,16 +694,12 @@ const struct inode_operations sysfs_dir_inode_operations = {
561 694
562static void remove_dir(struct sysfs_dirent *sd) 695static void remove_dir(struct sysfs_dirent *sd)
563{ 696{
564 mutex_lock(&sysfs_mutex); 697 struct sysfs_addrm_cxt acxt;
565 sysfs_unlink_sibling(sd);
566 sd->s_flags |= SYSFS_FLAG_REMOVED;
567 mutex_unlock(&sysfs_mutex);
568 698
569 pr_debug(" o %s removing done\n", sd->s_name); 699 sysfs_addrm_start(&acxt, sd->s_parent);
570 700 sysfs_unlink_sibling(sd);
571 sysfs_drop_dentry(sd); 701 sysfs_remove_one(&acxt, sd);
572 sysfs_deactivate(sd); 702 sysfs_addrm_finish(&acxt);
573 sysfs_put(sd);
574} 703}
575 704
576void sysfs_remove_subdir(struct sysfs_dirent *sd) 705void sysfs_remove_subdir(struct sysfs_dirent *sd)
@@ -581,38 +710,26 @@ void sysfs_remove_subdir(struct sysfs_dirent *sd)
581 710
582static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) 711static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
583{ 712{
584 struct sysfs_dirent *removed = NULL; 713 struct sysfs_addrm_cxt acxt;
585 struct sysfs_dirent **pos; 714 struct sysfs_dirent **pos;
586 715
587 if (!dir_sd) 716 if (!dir_sd)
588 return; 717 return;
589 718
590 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); 719 pr_debug("sysfs %s: removing dir\n", dir_sd->s_name);
591 mutex_lock(&sysfs_mutex); 720 sysfs_addrm_start(&acxt, dir_sd);
592 pos = &dir_sd->s_children; 721 pos = &dir_sd->s_children;
593 while (*pos) { 722 while (*pos) {
594 struct sysfs_dirent *sd = *pos; 723 struct sysfs_dirent *sd = *pos;
595 724
596 if (sysfs_type(sd) && (sysfs_type(sd) & SYSFS_NOT_PINNED)) { 725 if (sysfs_type(sd) && (sysfs_type(sd) & SYSFS_NOT_PINNED)) {
597 sd->s_flags |= SYSFS_FLAG_REMOVED;
598 *pos = sd->s_sibling; 726 *pos = sd->s_sibling;
599 sd->s_sibling = removed; 727 sd->s_sibling = NULL;
600 removed = sd; 728 sysfs_remove_one(&acxt, sd);
601 } else 729 } else
602 pos = &(*pos)->s_sibling; 730 pos = &(*pos)->s_sibling;
603 } 731 }
604 mutex_unlock(&sysfs_mutex); 732 sysfs_addrm_finish(&acxt);
605
606 while (removed) {
607 struct sysfs_dirent *sd = removed;
608
609 removed = sd->s_sibling;
610 sd->s_sibling = NULL;
611
612 sysfs_drop_dentry(sd);
613 sysfs_deactivate(sd);
614 sysfs_put(sd);
615 }
616 733
617 remove_dir(dir_sd); 734 remove_dir(dir_sd);
618} 735}
@@ -772,7 +889,8 @@ static int sysfs_dir_open(struct inode *inode, struct file *file)
772 sd = sysfs_new_dirent("_DIR_", 0, 0); 889 sd = sysfs_new_dirent("_DIR_", 0, 0);
773 if (sd) { 890 if (sd) {
774 mutex_lock(&sysfs_mutex); 891 mutex_lock(&sysfs_mutex);
775 sysfs_attach_dirent(sd, parent_sd, NULL); 892 sd->s_parent = sysfs_get(parent_sd);
893 sysfs_link_sibling(sd);
776 mutex_unlock(&sysfs_mutex); 894 mutex_unlock(&sysfs_mutex);
777 } 895 }
778 896
@@ -957,6 +1075,7 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj)
957 struct sysfs_dirent *parent_sd = parent->d_fsdata; 1075 struct sysfs_dirent *parent_sd = parent->d_fsdata;
958 struct dentry *shadow; 1076 struct dentry *shadow;
959 struct sysfs_dirent *sd; 1077 struct sysfs_dirent *sd;
1078 struct sysfs_addrm_cxt acxt;
960 1079
961 sd = ERR_PTR(-EINVAL); 1080 sd = ERR_PTR(-EINVAL);
962 if (!sysfs_is_shadowed_inode(inode)) 1081 if (!sysfs_is_shadowed_inode(inode))
@@ -970,15 +1089,18 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj)
970 if (!sd) 1089 if (!sd)
971 goto nomem; 1090 goto nomem;
972 sd->s_elem.dir.kobj = kobj; 1091 sd->s_elem.dir.kobj = kobj;
973 /* point to parent_sd but don't attach to it */
974 sd->s_parent = sysfs_get(parent_sd);
975 mutex_lock(&sysfs_mutex);
976 sysfs_attach_dirent(sd, NULL, shadow);
977 mutex_unlock(&sysfs_mutex);
978 1092
1093 sysfs_addrm_start(&acxt, parent_sd);
1094
1095 /* add but don't link into children list */
1096 sysfs_add_one(&acxt, sd);
1097
1098 /* attach and instantiate dentry */
1099 sysfs_attach_dentry(sd, shadow);
979 d_instantiate(shadow, igrab(inode)); 1100 d_instantiate(shadow, igrab(inode));
980 inc_nlink(inode); 1101 inc_nlink(inode); /* tj: synchronization? */
981 inc_nlink(parent->d_inode); 1102
1103 sysfs_addrm_finish(&acxt);
982 1104
983 dget(shadow); /* Extra count - pin the dentry in core */ 1105 dget(shadow); /* Extra count - pin the dentry in core */
984 1106