diff options
author | Tejun Heo <htejun@gmail.com> | 2007-06-13 14:45:16 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-11 19:09:06 -0400 |
commit | dbde0fcf9f8f6d477af3c32d9979e789ee680cde (patch) | |
tree | 12a1240213d59c061fec60325c7d5ebb1edebcd7 /fs/sysfs | |
parent | 198a2a847015805c6f57d8cc732bdaaccb494007 (diff) |
sysfs: reimplement sysfs_drop_dentry()
This patch reimplements sysfs_drop_dentry() such that remove_dir() can
use it to drop dentry instead of using a separate mechanism. With
this change, making directories reclaimable is much easier.
This patch used to contain fixes for two race conditions around
sd->s_dentry but that part has been separated out and included into
mainline early as commit 6aa054aadfea613a437ad0b15d38eca2b963fc0a and
dd14cbc994709a1c5a64ed3621f583c49a27e521.
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.c | 18 | ||||
-rw-r--r-- | fs/sysfs/inode.c | 82 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 2 |
3 files changed, 67 insertions, 35 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 01eeb4b954b1..bc11a263aa53 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -372,22 +372,19 @@ const struct inode_operations sysfs_dir_inode_operations = { | |||
372 | 372 | ||
373 | static void remove_dir(struct dentry * d) | 373 | static void remove_dir(struct dentry * d) |
374 | { | 374 | { |
375 | struct dentry * parent = dget(d->d_parent); | 375 | struct dentry *parent = d->d_parent; |
376 | struct sysfs_dirent * sd; | 376 | struct sysfs_dirent *sd = d->d_fsdata; |
377 | 377 | ||
378 | mutex_lock(&parent->d_inode->i_mutex); | 378 | mutex_lock(&parent->d_inode->i_mutex); |
379 | d_delete(d); | 379 | |
380 | sd = d->d_fsdata; | ||
381 | list_del_init(&sd->s_sibling); | 380 | list_del_init(&sd->s_sibling); |
382 | if (d->d_inode) | ||
383 | simple_rmdir(parent->d_inode,d); | ||
384 | 381 | ||
385 | pr_debug(" o %s removing done (%d)\n",d->d_name.name, | 382 | pr_debug(" o %s removing done (%d)\n",d->d_name.name, |
386 | atomic_read(&d->d_count)); | 383 | atomic_read(&d->d_count)); |
387 | 384 | ||
388 | mutex_unlock(&parent->d_inode->i_mutex); | 385 | mutex_unlock(&parent->d_inode->i_mutex); |
389 | dput(parent); | ||
390 | 386 | ||
387 | sysfs_drop_dentry(sd); | ||
391 | sysfs_deactivate(sd); | 388 | sysfs_deactivate(sd); |
392 | sysfs_put(sd); | 389 | sysfs_put(sd); |
393 | } | 390 | } |
@@ -404,7 +401,6 @@ static void __sysfs_remove_dir(struct dentry *dentry) | |||
404 | struct sysfs_dirent * parent_sd; | 401 | struct sysfs_dirent * parent_sd; |
405 | struct sysfs_dirent * sd, * tmp; | 402 | struct sysfs_dirent * sd, * tmp; |
406 | 403 | ||
407 | dget(dentry); | ||
408 | if (!dentry) | 404 | if (!dentry) |
409 | return; | 405 | return; |
410 | 406 | ||
@@ -415,21 +411,17 @@ static void __sysfs_remove_dir(struct dentry *dentry) | |||
415 | if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) | 411 | if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) |
416 | continue; | 412 | continue; |
417 | list_move(&sd->s_sibling, &removed); | 413 | list_move(&sd->s_sibling, &removed); |
418 | sysfs_drop_dentry(sd, dentry); | ||
419 | } | 414 | } |
420 | mutex_unlock(&dentry->d_inode->i_mutex); | 415 | mutex_unlock(&dentry->d_inode->i_mutex); |
421 | 416 | ||
422 | list_for_each_entry_safe(sd, tmp, &removed, s_sibling) { | 417 | list_for_each_entry_safe(sd, tmp, &removed, s_sibling) { |
423 | list_del_init(&sd->s_sibling); | 418 | list_del_init(&sd->s_sibling); |
419 | sysfs_drop_dentry(sd); | ||
424 | sysfs_deactivate(sd); | 420 | sysfs_deactivate(sd); |
425 | sysfs_put(sd); | 421 | sysfs_put(sd); |
426 | } | 422 | } |
427 | 423 | ||
428 | remove_dir(dentry); | 424 | remove_dir(dentry); |
429 | /** | ||
430 | * Drop reference from dget() on entrance. | ||
431 | */ | ||
432 | dput(dentry); | ||
433 | } | 425 | } |
434 | 426 | ||
435 | /** | 427 | /** |
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 88857a399d0c..6ad47c13b94d 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c | |||
@@ -191,13 +191,25 @@ int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, int mode, | |||
191 | return error; | 191 | return error; |
192 | } | 192 | } |
193 | 193 | ||
194 | /* | 194 | /** |
195 | * Unhashes the dentry corresponding to given sysfs_dirent | 195 | * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent |
196 | * Called with parent inode's i_mutex held. | 196 | * @sd: target sysfs_dirent |
197 | * | ||
198 | * Drop dentry for @sd. @sd must have been unlinked from its | ||
199 | * parent on entry to this function such that it can't be looked | ||
200 | * up anymore. | ||
201 | * | ||
202 | * @sd->s_dentry which is protected with sysfs_lock points to the | ||
203 | * currently associated dentry but we're not holding a reference | ||
204 | * to it and racing with dput(). Grab dcache_lock and verify | ||
205 | * dentry before dropping it. If @sd->s_dentry is NULL or dput() | ||
206 | * beats us, no need to bother. | ||
197 | */ | 207 | */ |
198 | void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | 208 | void sysfs_drop_dentry(struct sysfs_dirent *sd) |
199 | { | 209 | { |
200 | struct dentry *dentry = NULL; | 210 | struct dentry *dentry = NULL, *parent = NULL; |
211 | struct inode *dir; | ||
212 | struct timespec curtime; | ||
201 | 213 | ||
202 | /* We're not holding a reference to ->s_dentry dentry but the | 214 | /* We're not holding a reference to ->s_dentry dentry but the |
203 | * field will stay valid as long as sysfs_lock is held. | 215 | * field will stay valid as long as sysfs_lock is held. |
@@ -205,30 +217,57 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | |||
205 | spin_lock(&sysfs_lock); | 217 | spin_lock(&sysfs_lock); |
206 | spin_lock(&dcache_lock); | 218 | spin_lock(&dcache_lock); |
207 | 219 | ||
208 | /* dget dentry if it's still alive */ | 220 | if (sd->s_dentry && sd->s_dentry->d_inode) { |
209 | if (sd->s_dentry && sd->s_dentry->d_inode) | 221 | /* get dentry if it's there and dput() didn't kill it yet */ |
210 | dentry = dget_locked(sd->s_dentry); | 222 | dentry = dget_locked(sd->s_dentry); |
223 | parent = dentry->d_parent; | ||
224 | } else if (sd->s_parent->s_dentry->d_inode) { | ||
225 | /* We need to update the parent even if dentry for the | ||
226 | * victim itself doesn't exist. | ||
227 | */ | ||
228 | parent = dget_locked(sd->s_parent->s_dentry); | ||
229 | } | ||
230 | |||
231 | /* drop */ | ||
232 | if (dentry) { | ||
233 | spin_lock(&dentry->d_lock); | ||
234 | __d_drop(dentry); | ||
235 | spin_unlock(&dentry->d_lock); | ||
236 | } | ||
211 | 237 | ||
212 | spin_unlock(&dcache_lock); | 238 | spin_unlock(&dcache_lock); |
213 | spin_unlock(&sysfs_lock); | 239 | spin_unlock(&sysfs_lock); |
214 | 240 | ||
215 | /* drop dentry */ | 241 | /* nothing to do if the parent isn't in dcache */ |
242 | if (!parent) | ||
243 | return; | ||
244 | |||
245 | /* adjust nlink and update timestamp */ | ||
246 | dir = parent->d_inode; | ||
247 | mutex_lock(&dir->i_mutex); | ||
248 | |||
249 | curtime = CURRENT_TIME; | ||
250 | |||
251 | dir->i_ctime = dir->i_mtime = curtime; | ||
252 | |||
216 | if (dentry) { | 253 | if (dentry) { |
217 | spin_lock(&dcache_lock); | 254 | dentry->d_inode->i_ctime = curtime; |
218 | spin_lock(&dentry->d_lock); | 255 | drop_nlink(dentry->d_inode); |
219 | if (!d_unhashed(dentry) && dentry->d_inode) { | 256 | if (sd->s_type & SYSFS_DIR) { |
220 | dget_locked(dentry); | 257 | drop_nlink(dentry->d_inode); |
221 | __d_drop(dentry); | 258 | drop_nlink(dir); |
222 | spin_unlock(&dentry->d_lock); | 259 | /* XXX: unpin if directory, this will go away soon */ |
223 | spin_unlock(&dcache_lock); | 260 | dput(dentry); |
224 | simple_unlink(parent->d_inode, dentry); | ||
225 | } else { | ||
226 | spin_unlock(&dentry->d_lock); | ||
227 | spin_unlock(&dcache_lock); | ||
228 | } | 261 | } |
262 | } | ||
263 | |||
264 | mutex_unlock(&dir->i_mutex); | ||
229 | 265 | ||
266 | /* bye bye */ | ||
267 | if (dentry) | ||
230 | dput(dentry); | 268 | dput(dentry); |
231 | } | 269 | else |
270 | dput(parent); | ||
232 | } | 271 | } |
233 | 272 | ||
234 | int sysfs_hash_and_remove(struct dentry * dir, const char * name) | 273 | int sysfs_hash_and_remove(struct dentry * dir, const char * name) |
@@ -251,7 +290,6 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) | |||
251 | continue; | 290 | continue; |
252 | if (!strcmp(sd->s_name, name)) { | 291 | if (!strcmp(sd->s_name, name)) { |
253 | list_del_init(&sd->s_sibling); | 292 | list_del_init(&sd->s_sibling); |
254 | sysfs_drop_dentry(sd, dir); | ||
255 | found = 1; | 293 | found = 1; |
256 | break; | 294 | break; |
257 | } | 295 | } |
@@ -261,7 +299,9 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) | |||
261 | if (!found) | 299 | if (!found) |
262 | return -ENOENT; | 300 | return -ENOENT; |
263 | 301 | ||
302 | sysfs_drop_dentry(sd); | ||
264 | sysfs_deactivate(sd); | 303 | sysfs_deactivate(sd); |
265 | sysfs_put(sd); | 304 | sysfs_put(sd); |
305 | |||
266 | return 0; | 306 | return 0; |
267 | } | 307 | } |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 9fa77d648aa5..fc6aa863b947 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -76,7 +76,7 @@ extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * na | |||
76 | extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); | 76 | extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); |
77 | extern void sysfs_remove_subdir(struct dentry *); | 77 | extern void sysfs_remove_subdir(struct dentry *); |
78 | 78 | ||
79 | extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); | 79 | extern void sysfs_drop_dentry(struct sysfs_dirent *sd); |
80 | extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); | 80 | extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); |
81 | 81 | ||
82 | extern spinlock_t sysfs_lock; | 82 | extern spinlock_t sysfs_lock; |