/* * Find an eligible tree to time-out * A tree is eligible if :- * - it is unused by any user process * - it has been unused for exp_timeout time */ static struct dentry *autofs4_expire(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int how) { unsigned long timeout; struct dentry *root = sb->s_root; struct dentry *expired = NULL; struct list_head *next; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; if ( !sbi->exp_timeout || !root ) return NULL; now = jiffies; timeout = sbi->exp_timeout; spin_lock(&dcache_lock); next = root->d_subdirs.next; /* On exit from the loop expire is set to a dgot dentry * to expire or it's NULL */ while ( next != &root->d_subdirs ) { struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if ( !simple_positive(dentry) ) { next = next->next; continue; } dentry = dget(dentry); spin_unlock(&dcache_lock); /* Case 1: indirect mount or top level direct mount */ if (d_mountpoint(dentry)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); /* Can we expire this guy */ if (!autofs4_can_expire(dentry, timeout, do_now)) goto next; /* Can we umount this guy */ if (autofs4_check_mount(mnt, dentry)) { expired = dentry; break; } goto next; } if ( simple_empty(dentry) ) goto next; /* Case 2: tree mount, expire iff entire tree is not busy */ if (!exp_leaves) { /* Lock the tree as we must expire as a whole */ spin_lock(&sbi->fs_lock); if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { struct autofs_info *inf = autofs4_dentry_ino(dentry); /* Set this flag early to catch sys_chdir and the like */ inf->flags |= AUTOFS_INF_EXPIRING; spin_unlock(&sbi->fs_lock); expired = dentry; break; } spin_unlock(&sbi->fs_lock); /* Case 3: direct mount, expire individual leaves */ } else { expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); break; } } next: dput(dentry); spin_lock(&dcache_lock); next = next->next; } if ( expired ) { DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); spin_lock(&dcache_lock); list_del(&expired->d_parent->d_subdirs); list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&dcache_lock); return expired; } spin_unlock(&dcache_lock); return NULL; }
/* Check if 'dentry' should expire, or return a nearby * dentry that is suitable. * If returned dentry is different from arg dentry, * then a dget() reference was taken, else not. */ static struct dentry *should_expire(struct dentry *dentry, struct vfsmount *mnt, unsigned long timeout, int how) { int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino = autofs4_dentry_ino(dentry); unsigned int ino_count; /* No point expiring a pending mount */ if (ino->flags & AUTOFS_INF_PENDING) return NULL; /* * Case 1: (i) indirect mount or top level pseudo direct mount * (autofs-4.1). * (ii) indirect mount with offset mount, check the "/" * offset (autofs-5.0+). */ if (d_mountpoint(dentry)) { pr_debug("checking mountpoint %p %pd\n", dentry, dentry); /* Can we umount this guy */ if (autofs4_mount_busy(mnt, dentry)) return NULL; /* Can we expire this guy */ if (autofs4_can_expire(dentry, timeout, do_now)) return dentry; return NULL; } if (d_really_is_positive(dentry) && d_is_symlink(dentry)) { pr_debug("checking symlink %p %pd\n", dentry, dentry); /* * A symlink can't be "busy" in the usual sense so * just check last used for expire timeout. */ if (autofs4_can_expire(dentry, timeout, do_now)) return dentry; return NULL; } if (simple_empty(dentry)) return NULL; /* Case 2: tree mount, expire iff entire tree is not busy */ if (!exp_leaves) { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (d_count(dentry) > ino_count) return NULL; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) return dentry; /* * Case 3: pseudo direct mount, expire individual leaves * (autofs-4.1). */ } else { /* Path walk currently on this dentry? */ struct dentry *expired; ino_count = atomic_read(&ino->count) + 1; if (d_count(dentry) > ino_count) return NULL; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { if (expired == dentry) dput(dentry); return expired; } } return NULL; }
/* * Find an eligible tree to time-out * A tree is eligible if :- * - it is unused by any user process * - it has been unused for exp_timeout time */ struct dentry *autofs4_expire_indirect(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int how) { unsigned long timeout; struct dentry *root = sb->s_root; struct dentry *dentry; struct dentry *expired = NULL; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino; unsigned int ino_count; if (!root) return NULL; now = jiffies; timeout = sbi->exp_timeout; dentry = NULL; while ((dentry = get_next_positive_subdir(dentry, root))) { spin_lock(&sbi->fs_lock); ino = autofs4_dentry_ino(dentry); /* No point expiring a pending mount */ if (ino->flags & AUTOFS_INF_PENDING) goto next; /* * Case 1: (i) indirect mount or top level pseudo direct mount * (autofs-4.1). * (ii) indirect mount with offset mount, check the "/" * offset (autofs-5.0+). */ if (d_mountpoint(dentry)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 2; if (dentry->d_count > ino_count) goto next; /* Can we umount this guy */ if (autofs4_mount_busy(mnt, dentry)) goto next; /* Can we expire this guy */ if (autofs4_can_expire(dentry, timeout, do_now)) { expired = dentry; goto found; } goto next; } if (simple_empty(dentry)) goto next; /* Case 2: tree mount, expire iff entire tree is not busy */ if (!exp_leaves) { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (dentry->d_count > ino_count) goto next; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { expired = dentry; goto found; } /* * Case 3: pseudo direct mount, expire individual leaves * (autofs-4.1). */ } else { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (dentry->d_count > ino_count) goto next; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); goto found; } } next: spin_unlock(&sbi->fs_lock); } return NULL; found: DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); ino = autofs4_dentry_ino(expired); ino->flags |= AUTOFS_INF_EXPIRING; init_completion(&ino->expire_complete); spin_unlock(&sbi->fs_lock); spin_lock(&sbi->lookup_lock); spin_lock(&expired->d_parent->d_lock); spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); list_move(&expired->d_parent->d_subdirs, &expired->d_child); spin_unlock(&expired->d_lock); spin_unlock(&expired->d_parent->d_lock); spin_unlock(&sbi->lookup_lock); return expired; }
struct dentry *autofs4_expire_indirect(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int how) { unsigned long timeout; struct dentry *root = sb->s_root; struct dentry *dentry; struct dentry *expired = NULL; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino; unsigned int ino_count; if (!root) return NULL; now = jiffies; timeout = sbi->exp_timeout; dentry = NULL; while ((dentry = get_next_positive_subdir(dentry, root))) { spin_lock(&sbi->fs_lock); ino = autofs4_dentry_ino(dentry); if (ino->flags & AUTOFS_INF_PENDING) goto next; if (d_mountpoint(dentry)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); ino_count = atomic_read(&ino->count) + 2; if (dentry->d_count > ino_count) goto next; if (autofs4_mount_busy(mnt, dentry)) goto next; if (autofs4_can_expire(dentry, timeout, do_now)) { expired = dentry; goto found; } goto next; } if (simple_empty(dentry)) goto next; if (!exp_leaves) { ino_count = atomic_read(&ino->count) + 1; if (dentry->d_count > ino_count) goto next; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { expired = dentry; goto found; } } else { ino_count = atomic_read(&ino->count) + 1; if (dentry->d_count > ino_count) goto next; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); goto found; } } next: spin_unlock(&sbi->fs_lock); } return NULL; found: DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); ino = autofs4_dentry_ino(expired); ino->flags |= AUTOFS_INF_EXPIRING; init_completion(&ino->expire_complete); spin_unlock(&sbi->fs_lock); spin_lock(&sbi->lookup_lock); spin_lock(&expired->d_parent->d_lock); spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&expired->d_lock); spin_unlock(&expired->d_parent->d_lock); spin_unlock(&sbi->lookup_lock); return expired; }
/* * Find an eligible tree to time-out * A tree is eligible if :- * - it is unused by any user process * - it has been unused for exp_timeout time */ struct dentry *autofs4_expire_indirect(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int how) { unsigned long timeout; struct dentry *root = sb->s_root; struct dentry *expired = NULL; struct list_head *next; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino; unsigned int ino_count; if (!root) return NULL; now = jiffies; timeout = sbi->exp_timeout; spin_lock(&dcache_lock); next = root->d_subdirs.next; /* On exit from the loop expire is set to a dgot dentry * to expire or it's NULL */ while ( next != &root->d_subdirs ) { struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if (!simple_positive(dentry)) { next = next->next; continue; } dentry = dget(dentry); spin_unlock(&dcache_lock); spin_lock(&sbi->fs_lock); ino = autofs4_dentry_ino(dentry); /* * Case 1: (i) indirect mount or top level pseudo direct mount * (autofs-4.1). * (ii) indirect mount with offset mount, check the "/" * offset (autofs-5.0+). */ if (d_mountpoint(dentry)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 2; if (atomic_read(&dentry->d_count) > ino_count) goto next; /* Can we umount this guy */ if (autofs4_mount_busy(mnt, dentry)) goto next; /* Can we expire this guy */ if (autofs4_can_expire(dentry, timeout, do_now)) { expired = dentry; goto found; } goto next; } if (simple_empty(dentry)) goto next; /* Case 2: tree mount, expire iff entire tree is not busy */ if (!exp_leaves) { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (atomic_read(&dentry->d_count) > ino_count) goto next; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { expired = dentry; goto found; } /* * Case 3: pseudo direct mount, expire individual leaves * (autofs-4.1). */ } else { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (atomic_read(&dentry->d_count) > ino_count) goto next; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); goto found; } } next: spin_unlock(&sbi->fs_lock); dput(dentry); spin_lock(&dcache_lock); next = next->next; } spin_unlock(&dcache_lock); return NULL; found: DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); ino = autofs4_dentry_ino(expired); ino->flags |= AUTOFS_INF_EXPIRING; init_completion(&ino->expire_complete); spin_unlock(&sbi->fs_lock); spin_lock(&dcache_lock); list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&dcache_lock); return expired; }