aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-06-13 14:45:16 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-11 19:09:05 -0400
commit0ab66088c855eca68513bdd7442a426c4b374ced (patch)
tree7f931f7f984ac14701a33d123fa9e03d9048bf8f /fs/sysfs/dir.c
parenteb36165353d0e5ac32b063f555acedcbaf6d3b75 (diff)
sysfs: implement sysfs_dirent active reference and immediate disconnect
sysfs: implement sysfs_dirent active reference and immediate disconnect Opening a sysfs node references its associated kobject, so userland can arbitrarily prolong lifetime of a kobject which complicates lifetime rules in drivers. This patch implements active reference and makes the association between kobject and sysfs immediately breakable. Now each sysfs_dirent has two reference counts - s_count and s_active. s_count is a regular reference count which guarantees that the containing sysfs_dirent is accessible. As long as s_count reference is held, all sysfs internal fields in sysfs_dirent are accessible including s_parent and s_name. The newly added s_active is active reference count. This is acquired by invoking sysfs_get_active() and it's the caller's responsibility to ensure sysfs_dirent itself is accessible (should be holding s_count one way or the other). Dereferencing sysfs_dirent to access objects out of sysfs proper requires active reference. This includes access to the associated kobjects, attributes and ops. The active references can be drained and denied by calling sysfs_deactivate(). All active sysfs_dirents must be deactivated after deletion but before the default reference is dropped. This enables immediate disconnect of sysfs nodes. Once a sysfs_dirent is deleted, it won't access any entity external to sysfs proper. Because attr/bin_attr ops access both the node itself and its parent for kobject, they need to hold active references to both. sysfs_get/put_active_two() helpers are provided to help grabbing both references. Parent's is acquired first and released last. Unlike other operations, mmapped area lingers on after mmap() is finished and the module implement implementing it and kobj need to stay referenced till all the mapped pages are gone. This is accomplished by holding one set of active references to the bin_attr and its parent if there have been any mmap during lifetime of an openfile. The references are dropped when the openfile is released. This change makes sysfs lifetime rules independent from both kobject's and module's. It not only fixes several race conditions caused by sysfs not holding onto the proper module when referencing kobject, but also helps fixing and simplifying lifetime management in driver model and drivers by taking sysfs out of the equation. Please read the following message for more info. http://article.gmane.org/gmane.linux.kernel/510293 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.c28
1 files changed, 25 insertions, 3 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 2a94dc36d166..e0d377aaf2cc 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -53,6 +53,19 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
53 repeat: 53 repeat:
54 parent_sd = sd->s_parent; 54 parent_sd = sd->s_parent;
55 55
56 /* If @sd is being released after deletion, s_active is write
57 * locked. If @sd is cursor for directory walk or being
58 * released prematurely, s_active has no reader or writer.
59 *
60 * sysfs_deactivate() lies to lockdep that s_active is
61 * unlocked immediately. Lie one more time to cover the
62 * previous lie.
63 */
64 if (!down_write_trylock(&sd->s_active))
65 rwsem_acquire(&sd->s_active.dep_map,
66 SYSFS_S_ACTIVE_DEACTIVATE, 0, _RET_IP_);
67 up_write(&sd->s_active);
68
56 if (sd->s_type & SYSFS_KOBJ_LINK) 69 if (sd->s_type & SYSFS_KOBJ_LINK)
57 sysfs_put(sd->s_elem.symlink.target_sd); 70 sysfs_put(sd->s_elem.symlink.target_sd);
58 if (sd->s_type & SYSFS_COPY_NAME) 71 if (sd->s_type & SYSFS_COPY_NAME)
@@ -113,6 +126,7 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
113 126
114 atomic_set(&sd->s_count, 1); 127 atomic_set(&sd->s_count, 1);
115 atomic_set(&sd->s_event, 1); 128 atomic_set(&sd->s_event, 1);
129 init_rwsem(&sd->s_active);
116 INIT_LIST_HEAD(&sd->s_children); 130 INIT_LIST_HEAD(&sd->s_children);
117 INIT_LIST_HEAD(&sd->s_sibling); 131 INIT_LIST_HEAD(&sd->s_sibling);
118 132
@@ -371,7 +385,6 @@ static void remove_dir(struct dentry * d)
371 d_delete(d); 385 d_delete(d);
372 sd = d->d_fsdata; 386 sd = d->d_fsdata;
373 list_del_init(&sd->s_sibling); 387 list_del_init(&sd->s_sibling);
374 sysfs_put(sd);
375 if (d->d_inode) 388 if (d->d_inode)
376 simple_rmdir(parent->d_inode,d); 389 simple_rmdir(parent->d_inode,d);
377 390
@@ -380,6 +393,9 @@ static void remove_dir(struct dentry * d)
380 393
381 mutex_unlock(&parent->d_inode->i_mutex); 394 mutex_unlock(&parent->d_inode->i_mutex);
382 dput(parent); 395 dput(parent);
396
397 sysfs_deactivate(sd);
398 sysfs_put(sd);
383} 399}
384 400
385void sysfs_remove_subdir(struct dentry * d) 401void sysfs_remove_subdir(struct dentry * d)
@@ -390,6 +406,7 @@ void sysfs_remove_subdir(struct dentry * d)
390 406
391static void __sysfs_remove_dir(struct dentry *dentry) 407static void __sysfs_remove_dir(struct dentry *dentry)
392{ 408{
409 LIST_HEAD(removed);
393 struct sysfs_dirent * parent_sd; 410 struct sysfs_dirent * parent_sd;
394 struct sysfs_dirent * sd, * tmp; 411 struct sysfs_dirent * sd, * tmp;
395 412
@@ -403,12 +420,17 @@ static void __sysfs_remove_dir(struct dentry *dentry)
403 list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { 420 list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
404 if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) 421 if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED))
405 continue; 422 continue;
406 list_del_init(&sd->s_sibling); 423 list_move(&sd->s_sibling, &removed);
407 sysfs_drop_dentry(sd, dentry); 424 sysfs_drop_dentry(sd, dentry);
408 sysfs_put(sd);
409 } 425 }
410 mutex_unlock(&dentry->d_inode->i_mutex); 426 mutex_unlock(&dentry->d_inode->i_mutex);
411 427
428 list_for_each_entry_safe(sd, tmp, &removed, s_sibling) {
429 list_del_init(&sd->s_sibling);
430 sysfs_deactivate(sd);
431 sysfs_put(sd);
432 }
433
412 remove_dir(dentry); 434 remove_dir(dentry);
413 /** 435 /**
414 * Drop reference from dget() on entrance. 436 * Drop reference from dget() on entrance.