diff options
Diffstat (limited to 'fs/autofs4/expire.c')
-rw-r--r-- | fs/autofs4/expire.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c new file mode 100644 index 000000000000..31540a6404d9 --- /dev/null +++ b/fs/autofs4/expire.c | |||
@@ -0,0 +1,358 @@ | |||
1 | /* -*- c -*- --------------------------------------------------------------- * | ||
2 | * | ||
3 | * linux/fs/autofs/expire.c | ||
4 | * | ||
5 | * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved | ||
6 | * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> | ||
7 | * Copyright 2001-2003 Ian Kent <raven@themaw.net> | ||
8 | * | ||
9 | * This file is part of the Linux kernel and is made available under | ||
10 | * the terms of the GNU General Public License, version 2, or at your | ||
11 | * option, any later version, incorporated herein by reference. | ||
12 | * | ||
13 | * ------------------------------------------------------------------------- */ | ||
14 | |||
15 | #include "autofs_i.h" | ||
16 | |||
17 | static unsigned long now; | ||
18 | |||
19 | /* Check if a dentry can be expired return 1 if it can else return 0 */ | ||
20 | static inline int autofs4_can_expire(struct dentry *dentry, | ||
21 | unsigned long timeout, int do_now) | ||
22 | { | ||
23 | struct autofs_info *ino = autofs4_dentry_ino(dentry); | ||
24 | |||
25 | /* dentry in the process of being deleted */ | ||
26 | if (ino == NULL) | ||
27 | return 0; | ||
28 | |||
29 | /* No point expiring a pending mount */ | ||
30 | if (dentry->d_flags & DCACHE_AUTOFS_PENDING) | ||
31 | return 0; | ||
32 | |||
33 | if (!do_now) { | ||
34 | /* Too young to die */ | ||
35 | if (time_after(ino->last_used + timeout, now)) | ||
36 | return 0; | ||
37 | |||
38 | /* update last_used here :- | ||
39 | - obviously makes sense if it is in use now | ||
40 | - less obviously, prevents rapid-fire expire | ||
41 | attempts if expire fails the first time */ | ||
42 | ino->last_used = now; | ||
43 | } | ||
44 | |||
45 | return 1; | ||
46 | } | ||
47 | |||
48 | /* Check a mount point for busyness return 1 if not busy, otherwise */ | ||
49 | static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry) | ||
50 | { | ||
51 | int status = 0; | ||
52 | |||
53 | DPRINTK("dentry %p %.*s", | ||
54 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | ||
55 | |||
56 | mntget(mnt); | ||
57 | dget(dentry); | ||
58 | |||
59 | if (!follow_down(&mnt, &dentry)) | ||
60 | goto done; | ||
61 | |||
62 | while (d_mountpoint(dentry) && follow_down(&mnt, &dentry)) | ||
63 | ; | ||
64 | |||
65 | /* This is an autofs submount, we can't expire it */ | ||
66 | if (is_autofs4_dentry(dentry)) | ||
67 | goto done; | ||
68 | |||
69 | /* The big question */ | ||
70 | if (may_umount_tree(mnt) == 0) | ||
71 | status = 1; | ||
72 | done: | ||
73 | DPRINTK("returning = %d", status); | ||
74 | mntput(mnt); | ||
75 | dput(dentry); | ||
76 | return status; | ||
77 | } | ||
78 | |||
79 | /* Check a directory tree of mount points for busyness | ||
80 | * The tree is not busy iff no mountpoints are busy | ||
81 | * Return 1 if the tree is busy or 0 otherwise | ||
82 | */ | ||
83 | static int autofs4_check_tree(struct vfsmount *mnt, | ||
84 | struct dentry *top, | ||
85 | unsigned long timeout, | ||
86 | int do_now) | ||
87 | { | ||
88 | struct dentry *this_parent = top; | ||
89 | struct list_head *next; | ||
90 | |||
91 | DPRINTK("parent %p %.*s", | ||
92 | top, (int)top->d_name.len, top->d_name.name); | ||
93 | |||
94 | /* Negative dentry - give up */ | ||
95 | if (!simple_positive(top)) | ||
96 | return 0; | ||
97 | |||
98 | /* Timeout of a tree mount is determined by its top dentry */ | ||
99 | if (!autofs4_can_expire(top, timeout, do_now)) | ||
100 | return 0; | ||
101 | |||
102 | spin_lock(&dcache_lock); | ||
103 | repeat: | ||
104 | next = this_parent->d_subdirs.next; | ||
105 | resume: | ||
106 | while (next != &this_parent->d_subdirs) { | ||
107 | struct dentry *dentry = list_entry(next, struct dentry, d_child); | ||
108 | |||
109 | /* Negative dentry - give up */ | ||
110 | if (!simple_positive(dentry)) { | ||
111 | next = next->next; | ||
112 | continue; | ||
113 | } | ||
114 | |||
115 | DPRINTK("dentry %p %.*s", | ||
116 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | ||
117 | |||
118 | if (!simple_empty_nolock(dentry)) { | ||
119 | this_parent = dentry; | ||
120 | goto repeat; | ||
121 | } | ||
122 | |||
123 | dentry = dget(dentry); | ||
124 | spin_unlock(&dcache_lock); | ||
125 | |||
126 | if (d_mountpoint(dentry)) { | ||
127 | /* First busy => tree busy */ | ||
128 | if (!autofs4_check_mount(mnt, dentry)) { | ||
129 | dput(dentry); | ||
130 | return 0; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | dput(dentry); | ||
135 | spin_lock(&dcache_lock); | ||
136 | next = next->next; | ||
137 | } | ||
138 | |||
139 | if (this_parent != top) { | ||
140 | next = this_parent->d_child.next; | ||
141 | this_parent = this_parent->d_parent; | ||
142 | goto resume; | ||
143 | } | ||
144 | spin_unlock(&dcache_lock); | ||
145 | |||
146 | return 1; | ||
147 | } | ||
148 | |||
149 | static struct dentry *autofs4_check_leaves(struct vfsmount *mnt, | ||
150 | struct dentry *parent, | ||
151 | unsigned long timeout, | ||
152 | int do_now) | ||
153 | { | ||
154 | struct dentry *this_parent = parent; | ||
155 | struct list_head *next; | ||
156 | |||
157 | DPRINTK("parent %p %.*s", | ||
158 | parent, (int)parent->d_name.len, parent->d_name.name); | ||
159 | |||
160 | spin_lock(&dcache_lock); | ||
161 | repeat: | ||
162 | next = this_parent->d_subdirs.next; | ||
163 | resume: | ||
164 | while (next != &this_parent->d_subdirs) { | ||
165 | struct dentry *dentry = list_entry(next, struct dentry, d_child); | ||
166 | |||
167 | /* Negative dentry - give up */ | ||
168 | if (!simple_positive(dentry)) { | ||
169 | next = next->next; | ||
170 | continue; | ||
171 | } | ||
172 | |||
173 | DPRINTK("dentry %p %.*s", | ||
174 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | ||
175 | |||
176 | if (!list_empty(&dentry->d_subdirs)) { | ||
177 | this_parent = dentry; | ||
178 | goto repeat; | ||
179 | } | ||
180 | |||
181 | dentry = dget(dentry); | ||
182 | spin_unlock(&dcache_lock); | ||
183 | |||
184 | if (d_mountpoint(dentry)) { | ||
185 | /* Can we expire this guy */ | ||
186 | if (!autofs4_can_expire(dentry, timeout, do_now)) | ||
187 | goto cont; | ||
188 | |||
189 | /* Can we umount this guy */ | ||
190 | if (autofs4_check_mount(mnt, dentry)) | ||
191 | return dentry; | ||
192 | |||
193 | } | ||
194 | cont: | ||
195 | dput(dentry); | ||
196 | spin_lock(&dcache_lock); | ||
197 | next = next->next; | ||
198 | } | ||
199 | |||
200 | if (this_parent != parent) { | ||
201 | next = this_parent->d_child.next; | ||
202 | this_parent = this_parent->d_parent; | ||
203 | goto resume; | ||
204 | } | ||
205 | spin_unlock(&dcache_lock); | ||
206 | |||
207 | return NULL; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Find an eligible tree to time-out | ||
212 | * A tree is eligible if :- | ||
213 | * - it is unused by any user process | ||
214 | * - it has been unused for exp_timeout time | ||
215 | */ | ||
216 | static struct dentry *autofs4_expire(struct super_block *sb, | ||
217 | struct vfsmount *mnt, | ||
218 | struct autofs_sb_info *sbi, | ||
219 | int how) | ||
220 | { | ||
221 | unsigned long timeout; | ||
222 | struct dentry *root = sb->s_root; | ||
223 | struct dentry *expired = NULL; | ||
224 | struct list_head *next; | ||
225 | int do_now = how & AUTOFS_EXP_IMMEDIATE; | ||
226 | int exp_leaves = how & AUTOFS_EXP_LEAVES; | ||
227 | |||
228 | if ( !sbi->exp_timeout || !root ) | ||
229 | return NULL; | ||
230 | |||
231 | now = jiffies; | ||
232 | timeout = sbi->exp_timeout; | ||
233 | |||
234 | spin_lock(&dcache_lock); | ||
235 | next = root->d_subdirs.next; | ||
236 | |||
237 | /* On exit from the loop expire is set to a dgot dentry | ||
238 | * to expire or it's NULL */ | ||
239 | while ( next != &root->d_subdirs ) { | ||
240 | struct dentry *dentry = list_entry(next, struct dentry, d_child); | ||
241 | |||
242 | /* Negative dentry - give up */ | ||
243 | if ( !simple_positive(dentry) ) { | ||
244 | next = next->next; | ||
245 | continue; | ||
246 | } | ||
247 | |||
248 | dentry = dget(dentry); | ||
249 | spin_unlock(&dcache_lock); | ||
250 | |||
251 | /* Case 1: indirect mount or top level direct mount */ | ||
252 | if (d_mountpoint(dentry)) { | ||
253 | DPRINTK("checking mountpoint %p %.*s", | ||
254 | dentry, (int)dentry->d_name.len, dentry->d_name.name); | ||
255 | |||
256 | /* Can we expire this guy */ | ||
257 | if (!autofs4_can_expire(dentry, timeout, do_now)) | ||
258 | goto next; | ||
259 | |||
260 | /* Can we umount this guy */ | ||
261 | if (autofs4_check_mount(mnt, dentry)) { | ||
262 | expired = dentry; | ||
263 | break; | ||
264 | } | ||
265 | goto next; | ||
266 | } | ||
267 | |||
268 | if ( simple_empty(dentry) ) | ||
269 | goto next; | ||
270 | |||
271 | /* Case 2: tree mount, expire iff entire tree is not busy */ | ||
272 | if (!exp_leaves) { | ||
273 | if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { | ||
274 | expired = dentry; | ||
275 | break; | ||
276 | } | ||
277 | /* Case 3: direct mount, expire individual leaves */ | ||
278 | } else { | ||
279 | expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); | ||
280 | if (expired) { | ||
281 | dput(dentry); | ||
282 | break; | ||
283 | } | ||
284 | } | ||
285 | next: | ||
286 | dput(dentry); | ||
287 | spin_lock(&dcache_lock); | ||
288 | next = next->next; | ||
289 | } | ||
290 | |||
291 | if ( expired ) { | ||
292 | DPRINTK("returning %p %.*s", | ||
293 | expired, (int)expired->d_name.len, expired->d_name.name); | ||
294 | spin_lock(&dcache_lock); | ||
295 | list_del(&expired->d_parent->d_subdirs); | ||
296 | list_add(&expired->d_parent->d_subdirs, &expired->d_child); | ||
297 | spin_unlock(&dcache_lock); | ||
298 | return expired; | ||
299 | } | ||
300 | spin_unlock(&dcache_lock); | ||
301 | |||
302 | return NULL; | ||
303 | } | ||
304 | |||
305 | /* Perform an expiry operation */ | ||
306 | int autofs4_expire_run(struct super_block *sb, | ||
307 | struct vfsmount *mnt, | ||
308 | struct autofs_sb_info *sbi, | ||
309 | struct autofs_packet_expire __user *pkt_p) | ||
310 | { | ||
311 | struct autofs_packet_expire pkt; | ||
312 | struct dentry *dentry; | ||
313 | |||
314 | memset(&pkt,0,sizeof pkt); | ||
315 | |||
316 | pkt.hdr.proto_version = sbi->version; | ||
317 | pkt.hdr.type = autofs_ptype_expire; | ||
318 | |||
319 | if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL) | ||
320 | return -EAGAIN; | ||
321 | |||
322 | pkt.len = dentry->d_name.len; | ||
323 | memcpy(pkt.name, dentry->d_name.name, pkt.len); | ||
324 | pkt.name[pkt.len] = '\0'; | ||
325 | dput(dentry); | ||
326 | |||
327 | if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) | ||
328 | return -EFAULT; | ||
329 | |||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | /* Call repeatedly until it returns -EAGAIN, meaning there's nothing | ||
334 | more to be done */ | ||
335 | int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, | ||
336 | struct autofs_sb_info *sbi, int __user *arg) | ||
337 | { | ||
338 | struct dentry *dentry; | ||
339 | int ret = -EAGAIN; | ||
340 | int do_now = 0; | ||
341 | |||
342 | if (arg && get_user(do_now, arg)) | ||
343 | return -EFAULT; | ||
344 | |||
345 | if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { | ||
346 | struct autofs_info *de_info = autofs4_dentry_ino(dentry); | ||
347 | |||
348 | /* This is synchronous because it makes the daemon a | ||
349 | little easier */ | ||
350 | de_info->flags |= AUTOFS_INF_EXPIRING; | ||
351 | ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); | ||
352 | de_info->flags &= ~AUTOFS_INF_EXPIRING; | ||
353 | dput(dentry); | ||
354 | } | ||
355 | |||
356 | return ret; | ||
357 | } | ||
358 | |||