aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
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
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')
-rw-r--r--fs/sysfs/dir.c250
-rw-r--r--fs/sysfs/file.c17
-rw-r--r--fs/sysfs/inode.c46
-rw-r--r--fs/sysfs/symlink.c20
-rw-r--r--fs/sysfs/sysfs.h20
5 files changed, 229 insertions, 124 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
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index d0deed3e60b5..69bacf1db596 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -416,6 +416,7 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
416 int type) 416 int type)
417{ 417{
418 umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; 418 umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;
419 struct sysfs_addrm_cxt acxt;
419 struct sysfs_dirent *sd; 420 struct sysfs_dirent *sd;
420 421
421 sd = sysfs_new_dirent(attr->name, mode, type); 422 sd = sysfs_new_dirent(attr->name, mode, type);
@@ -423,20 +424,18 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
423 return -ENOMEM; 424 return -ENOMEM;
424 sd->s_elem.attr.attr = (void *)attr; 425 sd->s_elem.attr.attr = (void *)attr;
425 426
426 mutex_lock(&sysfs_mutex); 427 sysfs_addrm_start(&acxt, dir_sd);
427 428
428 if (!sysfs_find_dirent(dir_sd, attr->name)) { 429 if (!sysfs_find_dirent(dir_sd, attr->name)) {
429 sysfs_attach_dirent(sd, dir_sd, NULL); 430 sysfs_add_one(&acxt, sd);
430 sd = NULL; 431 sysfs_link_sibling(sd);
431 } 432 }
432 433
433 mutex_unlock(&sysfs_mutex); 434 if (sysfs_addrm_finish(&acxt))
435 return 0;
434 436
435 if (sd) { 437 sysfs_put(sd);
436 sysfs_put(sd); 438 return -EEXIST;
437 return -EEXIST;
438 }
439 return 0;
440} 439}
441 440
442 441
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index d439c0b4bfce..f95966847a81 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -191,15 +191,9 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode)
191{ 191{
192 BUG_ON(!dentry || dentry->d_inode); 192 BUG_ON(!dentry || dentry->d_inode);
193 193
194 if (inode->i_state & I_NEW) { 194 if (inode->i_state & I_NEW)
195 unlock_new_inode(inode); 195 unlock_new_inode(inode);
196 196
197 if (dentry->d_parent && dentry->d_parent->d_inode) {
198 struct inode *p_inode = dentry->d_parent->d_inode;
199 p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME;
200 }
201 }
202
203 d_instantiate(dentry, inode); 197 d_instantiate(dentry, inode);
204} 198}
205 199
@@ -220,7 +214,6 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode)
220void sysfs_drop_dentry(struct sysfs_dirent *sd) 214void sysfs_drop_dentry(struct sysfs_dirent *sd)
221{ 215{
222 struct dentry *dentry = NULL; 216 struct dentry *dentry = NULL;
223 struct timespec curtime;
224 struct inode *inode; 217 struct inode *inode;
225 218
226 /* We're not holding a reference to ->s_dentry dentry but the 219 /* We're not holding a reference to ->s_dentry dentry but the
@@ -246,13 +239,11 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd)
246 dput(dentry); 239 dput(dentry);
247 240
248 /* adjust nlink and update timestamp */ 241 /* adjust nlink and update timestamp */
249 curtime = CURRENT_TIME;
250
251 inode = ilookup(sysfs_sb, sd->s_ino); 242 inode = ilookup(sysfs_sb, sd->s_ino);
252 if (inode) { 243 if (inode) {
253 mutex_lock(&inode->i_mutex); 244 mutex_lock(&inode->i_mutex);
254 245
255 inode->i_ctime = curtime; 246 inode->i_ctime = CURRENT_TIME;
256 drop_nlink(inode); 247 drop_nlink(inode);
257 if (sysfs_type(sd) == SYSFS_DIR) 248 if (sysfs_type(sd) == SYSFS_DIR)
258 drop_nlink(inode); 249 drop_nlink(inode);
@@ -260,30 +251,17 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd)
260 mutex_unlock(&inode->i_mutex); 251 mutex_unlock(&inode->i_mutex);
261 iput(inode); 252 iput(inode);
262 } 253 }
263
264 /* adjust nlink and udpate timestamp of the parent */
265 inode = ilookup(sysfs_sb, sd->s_parent->s_ino);
266 if (inode) {
267 mutex_lock(&inode->i_mutex);
268
269 inode->i_ctime = inode->i_mtime = curtime;
270 if (sysfs_type(sd) == SYSFS_DIR)
271 drop_nlink(inode);
272
273 mutex_unlock(&inode->i_mutex);
274 iput(inode);
275 }
276} 254}
277 255
278int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) 256int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name)
279{ 257{
258 struct sysfs_addrm_cxt acxt;
280 struct sysfs_dirent **pos, *sd; 259 struct sysfs_dirent **pos, *sd;
281 int found = 0;
282 260
283 if (!dir_sd) 261 if (!dir_sd)
284 return -ENOENT; 262 return -ENOENT;
285 263
286 mutex_lock(&sysfs_mutex); 264 sysfs_addrm_start(&acxt, dir_sd);
287 265
288 for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { 266 for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) {
289 sd = *pos; 267 sd = *pos;
@@ -291,22 +269,14 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name)
291 if (!sysfs_type(sd)) 269 if (!sysfs_type(sd))
292 continue; 270 continue;
293 if (!strcmp(sd->s_name, name)) { 271 if (!strcmp(sd->s_name, name)) {
294 sd->s_flags |= SYSFS_FLAG_REMOVED;
295 *pos = sd->s_sibling; 272 *pos = sd->s_sibling;
296 sd->s_sibling = NULL; 273 sd->s_sibling = NULL;
297 found = 1; 274 sysfs_remove_one(&acxt, sd);
298 break; 275 break;
299 } 276 }
300 } 277 }
301 278
302 mutex_unlock(&sysfs_mutex); 279 if (sysfs_addrm_finish(&acxt))
303 280 return 0;
304 if (!found) 281 return -ENOENT;
305 return -ENOENT;
306
307 sysfs_drop_dentry(sd);
308 sysfs_deactivate(sd);
309 sysfs_put(sd);
310
311 return 0;
312} 282}
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index 683316f0aa96..2f86e0422290 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -55,6 +55,7 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char
55 struct sysfs_dirent *parent_sd = NULL; 55 struct sysfs_dirent *parent_sd = NULL;
56 struct sysfs_dirent *target_sd = NULL; 56 struct sysfs_dirent *target_sd = NULL;
57 struct sysfs_dirent *sd = NULL; 57 struct sysfs_dirent *sd = NULL;
58 struct sysfs_addrm_cxt acxt;
58 int error; 59 int error;
59 60
60 BUG_ON(!name); 61 BUG_ON(!name);
@@ -87,17 +88,18 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char
87 goto out_put; 88 goto out_put;
88 sd->s_elem.symlink.target_sd = target_sd; 89 sd->s_elem.symlink.target_sd = target_sd;
89 90
90 mutex_lock(&sysfs_mutex); 91 sysfs_addrm_start(&acxt, parent_sd);
91 error = -EEXIST;
92 if (sysfs_find_dirent(parent_sd, name))
93 goto out_unlock;
94 sysfs_attach_dirent(sd, parent_sd, NULL);
95 mutex_unlock(&sysfs_mutex);
96 92
97 return 0; 93 if (!sysfs_find_dirent(parent_sd, name)) {
94 sysfs_add_one(&acxt, sd);
95 sysfs_link_sibling(sd);
96 }
98 97
99 out_unlock: 98 if (sysfs_addrm_finish(&acxt))
100 mutex_unlock(&sysfs_mutex); 99 return 0;
100
101 error = -EEXIST;
102 /* fall through */
101 out_put: 103 out_put:
102 sysfs_put(target_sd); 104 sysfs_put(target_sd);
103 sysfs_put(sd); 105 sysfs_put(sd);
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 26051616ed11..3e9a5ee38233 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -44,14 +44,29 @@ struct sysfs_dirent {
44 44
45#define SD_DEACTIVATED_BIAS INT_MIN 45#define SD_DEACTIVATED_BIAS INT_MIN
46 46
47struct sysfs_addrm_cxt {
48 struct sysfs_dirent *parent_sd;
49 struct inode *parent_inode;
50 struct sysfs_dirent *removed;
51 int cnt;
52};
53
47extern struct vfsmount * sysfs_mount; 54extern struct vfsmount * sysfs_mount;
48extern struct kmem_cache *sysfs_dir_cachep; 55extern struct kmem_cache *sysfs_dir_cachep;
49 56
57extern void sysfs_link_sibling(struct sysfs_dirent *sd);
58extern void sysfs_unlink_sibling(struct sysfs_dirent *sd);
50extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); 59extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd);
51extern void sysfs_put_active(struct sysfs_dirent *sd); 60extern void sysfs_put_active(struct sysfs_dirent *sd);
52extern struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd); 61extern struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd);
53extern void sysfs_put_active_two(struct sysfs_dirent *sd); 62extern void sysfs_put_active_two(struct sysfs_dirent *sd);
54extern void sysfs_deactivate(struct sysfs_dirent *sd); 63extern void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
64 struct sysfs_dirent *parent_sd);
65extern void sysfs_add_one(struct sysfs_addrm_cxt *acxt,
66 struct sysfs_dirent *sd);
67extern void sysfs_remove_one(struct sysfs_addrm_cxt *acxt,
68 struct sysfs_dirent *sd);
69extern int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt);
55 70
56extern void sysfs_delete_inode(struct inode *inode); 71extern void sysfs_delete_inode(struct inode *inode);
57extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode); 72extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode);
@@ -65,9 +80,6 @@ extern struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
65 const unsigned char *name); 80 const unsigned char *name);
66extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, 81extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode,
67 int type); 82 int type);
68extern void sysfs_attach_dirent(struct sysfs_dirent *sd,
69 struct sysfs_dirent *parent_sd,
70 struct dentry *dentry);
71 83
72extern int sysfs_add_file(struct sysfs_dirent *dir_sd, 84extern int sysfs_add_file(struct sysfs_dirent *dir_sd,
73 const struct attribute *attr, int type); 85 const struct attribute *attr, int type);