aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs
diff options
context:
space:
mode:
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);