Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tejun Heo and committed by
Greg Kroah-Hartman
dbde0fcf 198a2a84

+69 -37
+5 -13
fs/sysfs/dir.c
··· 372 372 373 373 static void remove_dir(struct dentry * d) 374 374 { 375 - struct dentry * parent = dget(d->d_parent); 376 - struct sysfs_dirent * sd; 375 + struct dentry *parent = d->d_parent; 376 + struct sysfs_dirent *sd = d->d_fsdata; 377 377 378 378 mutex_lock(&parent->d_inode->i_mutex); 379 - d_delete(d); 380 - sd = d->d_fsdata; 379 + 381 380 list_del_init(&sd->s_sibling); 382 - if (d->d_inode) 383 - simple_rmdir(parent->d_inode,d); 384 381 385 382 pr_debug(" o %s removing done (%d)\n",d->d_name.name, 386 383 atomic_read(&d->d_count)); 387 384 388 385 mutex_unlock(&parent->d_inode->i_mutex); 389 - dput(parent); 390 386 387 + sysfs_drop_dentry(sd); 391 388 sysfs_deactivate(sd); 392 389 sysfs_put(sd); 393 390 } ··· 401 404 struct sysfs_dirent * parent_sd; 402 405 struct sysfs_dirent * sd, * tmp; 403 406 404 - dget(dentry); 405 407 if (!dentry) 406 408 return; 407 409 ··· 411 415 if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) 412 416 continue; 413 417 list_move(&sd->s_sibling, &removed); 414 - sysfs_drop_dentry(sd, dentry); 415 418 } 416 419 mutex_unlock(&dentry->d_inode->i_mutex); 417 420 418 421 list_for_each_entry_safe(sd, tmp, &removed, s_sibling) { 419 422 list_del_init(&sd->s_sibling); 423 + sysfs_drop_dentry(sd); 420 424 sysfs_deactivate(sd); 421 425 sysfs_put(sd); 422 426 } 423 427 424 428 remove_dir(dentry); 425 - /** 426 - * Drop reference from dget() on entrance. 427 - */ 428 - dput(dentry); 429 429 } 430 430 431 431 /**
+63 -23
fs/sysfs/inode.c
··· 191 191 return error; 192 192 } 193 193 194 - /* 195 - * Unhashes the dentry corresponding to given sysfs_dirent 196 - * Called with parent inode's i_mutex held. 194 + /** 195 + * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent 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 214 /* We're not holding a reference to ->s_dentry dentry but the 203 215 * field will stay valid as long as sysfs_lock is held. ··· 217 205 spin_lock(&sysfs_lock); 218 206 spin_lock(&dcache_lock); 219 207 220 - /* dget dentry if it's still alive */ 221 - if (sd->s_dentry && sd->s_dentry->d_inode) 208 + if (sd->s_dentry && sd->s_dentry->d_inode) { 209 + /* get dentry if it's there and dput() didn't kill it yet */ 222 210 dentry = dget_locked(sd->s_dentry); 211 + parent = dentry->d_parent; 212 + } else if (sd->s_parent->s_dentry->d_inode) { 213 + /* We need to update the parent even if dentry for the 214 + * victim itself doesn't exist. 215 + */ 216 + parent = dget_locked(sd->s_parent->s_dentry); 217 + } 218 + 219 + /* drop */ 220 + if (dentry) { 221 + spin_lock(&dentry->d_lock); 222 + __d_drop(dentry); 223 + spin_unlock(&dentry->d_lock); 224 + } 223 225 224 226 spin_unlock(&dcache_lock); 225 227 spin_unlock(&sysfs_lock); 226 228 227 - /* drop dentry */ 228 - if (dentry) { 229 - spin_lock(&dcache_lock); 230 - spin_lock(&dentry->d_lock); 231 - if (!d_unhashed(dentry) && dentry->d_inode) { 232 - dget_locked(dentry); 233 - __d_drop(dentry); 234 - spin_unlock(&dentry->d_lock); 235 - spin_unlock(&dcache_lock); 236 - simple_unlink(parent->d_inode, dentry); 237 - } else { 238 - spin_unlock(&dentry->d_lock); 239 - spin_unlock(&dcache_lock); 240 - } 229 + /* nothing to do if the parent isn't in dcache */ 230 + if (!parent) 231 + return; 241 232 242 - dput(dentry); 233 + /* adjust nlink and update timestamp */ 234 + dir = parent->d_inode; 235 + mutex_lock(&dir->i_mutex); 236 + 237 + curtime = CURRENT_TIME; 238 + 239 + dir->i_ctime = dir->i_mtime = curtime; 240 + 241 + if (dentry) { 242 + dentry->d_inode->i_ctime = curtime; 243 + drop_nlink(dentry->d_inode); 244 + if (sd->s_type & SYSFS_DIR) { 245 + drop_nlink(dentry->d_inode); 246 + drop_nlink(dir); 247 + /* XXX: unpin if directory, this will go away soon */ 248 + dput(dentry); 249 + } 243 250 } 251 + 252 + mutex_unlock(&dir->i_mutex); 253 + 254 + /* bye bye */ 255 + if (dentry) 256 + dput(dentry); 257 + else 258 + dput(parent); 244 259 } 245 260 246 261 int sysfs_hash_and_remove(struct dentry * dir, const char * name) ··· 290 251 continue; 291 252 if (!strcmp(sd->s_name, name)) { 292 253 list_del_init(&sd->s_sibling); 293 - sysfs_drop_dentry(sd, dir); 294 254 found = 1; 295 255 break; 296 256 } ··· 299 261 if (!found) 300 262 return -ENOENT; 301 263 264 + sysfs_drop_dentry(sd); 302 265 sysfs_deactivate(sd); 303 266 sysfs_put(sd); 267 + 304 268 return 0; 305 269 }
+1 -1
fs/sysfs/sysfs.h
··· 76 76 extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); 77 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 80 extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); 81 81 82 82 extern spinlock_t sysfs_lock;