aboutsummaryrefslogtreecommitdiffstats
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:06 -0400
commitdbde0fcf9f8f6d477af3c32d9979e789ee680cde (patch)
tree12a1240213d59c061fec60325c7d5ebb1edebcd7
parent198a2a847015805c6f57d8cc732bdaaccb494007 (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>
-rw-r--r--fs/sysfs/dir.c18
-rw-r--r--fs/sysfs/inode.c82
-rw-r--r--fs/sysfs/sysfs.h2
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
373static void remove_dir(struct dentry * d) 373static 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 */
198void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) 208void 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
234int sysfs_hash_and_remove(struct dentry * dir, const char * name) 273int 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
76extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); 76extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
77extern void sysfs_remove_subdir(struct dentry *); 77extern void sysfs_remove_subdir(struct dentry *);
78 78
79extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); 79extern void sysfs_drop_dentry(struct sysfs_dirent *sd);
80extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); 80extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
81 81
82extern spinlock_t sysfs_lock; 82extern spinlock_t sysfs_lock;