aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-06-13 14:45:18 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-11 19:09:07 -0400
commit8619f979898397582e366877fd5feeba7560d70c (patch)
tree7d0e92d4c17d92a00dfeba2b840bf89657b30607 /fs/sysfs/dir.c
parentb6b4a4399c2a83d1af77c99dee0d0b5cc15ec268 (diff)
sysfs: slim down sysfs_dirent->s_active
Make sysfs_dirent->s_active an atomic_t instead of rwsem. This reduces the size of sysfs_dirent from 136 to 104 on 64bit and from 76 to 60 on 32bit with lock debugging turned off. With lock debugging turned on the reduction is much larger. s_active starts at zero and each active reference increments s_active. Putting a reference decrements s_active. Deactivation subtracts SD_DEACTIVATED_BIAS which is currently INT_MIN and assumed to be small enough to make s_active negative. If s_active is negative, sysfs_get() no longer grants new references. Deactivation succeeds immediately if there is no active user; otherwise, it waits using a completion for the last put. Due to the removal of lockdep tricks, this change makes things less trickier in release_sysfs_dirent(). As all the complexity is contained in three s_active functions, I think it's more readable this way. 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.c74
1 files changed, 48 insertions, 26 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index f5f0b936f181..40596a0eee52 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -10,6 +10,7 @@
10#include <linux/kobject.h> 10#include <linux/kobject.h>
11#include <linux/namei.h> 11#include <linux/namei.h>
12#include <linux/idr.h> 12#include <linux/idr.h>
13#include <linux/completion.h>
13#include <asm/semaphore.h> 14#include <asm/semaphore.h>
14#include "sysfs.h" 15#include "sysfs.h"
15 16
@@ -32,11 +33,24 @@ static DEFINE_IDA(sysfs_ino_ida);
32 */ 33 */
33struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) 34struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
34{ 35{
35 if (sd) { 36 if (unlikely(!sd))
36 if (unlikely(!down_read_trylock(&sd->s_active))) 37 return NULL;
37 sd = NULL; 38
39 while (1) {
40 int v, t;
41
42 v = atomic_read(&sd->s_active);
43 if (unlikely(v < 0))
44 return NULL;
45
46 t = atomic_cmpxchg(&sd->s_active, v, v + 1);
47 if (likely(t == v))
48 return sd;
49 if (t < 0)
50 return NULL;
51
52 cpu_relax();
38 } 53 }
39 return sd;
40} 54}
41 55
42/** 56/**
@@ -48,8 +62,21 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
48 */ 62 */
49void sysfs_put_active(struct sysfs_dirent *sd) 63void sysfs_put_active(struct sysfs_dirent *sd)
50{ 64{
51 if (sd) 65 struct completion *cmpl;
52 up_read(&sd->s_active); 66 int v;
67
68 if (unlikely(!sd))
69 return;
70
71 v = atomic_dec_return(&sd->s_active);
72 if (likely(v != SD_DEACTIVATED_BIAS))
73 return;
74
75 /* atomic_dec_return() is a mb(), we'll always see the updated
76 * sd->s_sibling.next.
77 */
78 cmpl = (void *)sd->s_sibling.next;
79 complete(cmpl);
53} 80}
54 81
55/** 82/**
@@ -95,17 +122,25 @@ void sysfs_put_active_two(struct sysfs_dirent *sd)
95 * sysfs_deactivate - deactivate sysfs_dirent 122 * sysfs_deactivate - deactivate sysfs_dirent
96 * @sd: sysfs_dirent to deactivate 123 * @sd: sysfs_dirent to deactivate
97 * 124 *
98 * Deny new active references and drain existing ones. s_active 125 * Deny new active references and drain existing ones.
99 * will be unlocked when the sysfs_dirent is released.
100 */ 126 */
101void sysfs_deactivate(struct sysfs_dirent *sd) 127void sysfs_deactivate(struct sysfs_dirent *sd)
102{ 128{
103 down_write_nested(&sd->s_active, SYSFS_S_ACTIVE_DEACTIVATE); 129 DECLARE_COMPLETION_ONSTACK(wait);
130 int v;
131
132 BUG_ON(!list_empty(&sd->s_sibling));
133 sd->s_sibling.next = (void *)&wait;
104 134
105 /* s_active will be unlocked by the thread doing the final put 135 /* atomic_add_return() is a mb(), put_active() will always see
106 * on @sd. Lie to lockdep. 136 * the updated sd->s_sibling.next.
107 */ 137 */
108 rwsem_release(&sd->s_active.dep_map, 1, _RET_IP_); 138 v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active);
139
140 if (v != SD_DEACTIVATED_BIAS)
141 wait_for_completion(&wait);
142
143 INIT_LIST_HEAD(&sd->s_sibling);
109} 144}
110 145
111static int sysfs_alloc_ino(ino_t *pino) 146static int sysfs_alloc_ino(ino_t *pino)
@@ -141,19 +176,6 @@ void release_sysfs_dirent(struct sysfs_dirent * sd)
141 repeat: 176 repeat:
142 parent_sd = sd->s_parent; 177 parent_sd = sd->s_parent;
143 178
144 /* If @sd is being released after deletion, s_active is write
145 * locked. If @sd is cursor for directory walk or being
146 * released prematurely, s_active has no reader or writer.
147 *
148 * sysfs_deactivate() lies to lockdep that s_active is
149 * unlocked immediately. Lie one more time to cover the
150 * previous lie.
151 */
152 if (!down_write_trylock(&sd->s_active))
153 rwsem_acquire(&sd->s_active.dep_map,
154 SYSFS_S_ACTIVE_DEACTIVATE, 0, _RET_IP_);
155 up_write(&sd->s_active);
156
157 if (sd->s_type & SYSFS_KOBJ_LINK) 179 if (sd->s_type & SYSFS_KOBJ_LINK)
158 sysfs_put(sd->s_elem.symlink.target_sd); 180 sysfs_put(sd->s_elem.symlink.target_sd);
159 if (sd->s_type & SYSFS_COPY_NAME) 181 if (sd->s_type & SYSFS_COPY_NAME)
@@ -213,8 +235,8 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
213 goto err_out; 235 goto err_out;
214 236
215 atomic_set(&sd->s_count, 1); 237 atomic_set(&sd->s_count, 1);
238 atomic_set(&sd->s_active, 0);
216 atomic_set(&sd->s_event, 1); 239 atomic_set(&sd->s_event, 1);
217 init_rwsem(&sd->s_active);
218 INIT_LIST_HEAD(&sd->s_children); 240 INIT_LIST_HEAD(&sd->s_children);
219 INIT_LIST_HEAD(&sd->s_sibling); 241 INIT_LIST_HEAD(&sd->s_sibling);
220 242