Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | /* | |
11 | * VFS operations for Linux | |
12 | * | |
13 | * super_block operations should return negated errno to Linux. | |
14 | */ | |
15 | #include <afsconfig.h> | |
16 | #include "afs/param.h" | |
17 | ||
18 | ||
19 | #define __NO_VERSION__ /* don't define kernel_version in module.h */ | |
20 | #include <linux/module.h> /* early to avoid printf->printk mapping */ | |
21 | #include "afs/sysincludes.h" | |
22 | #include "afsincludes.h" | |
23 | #include "afs/afs_stats.h" | |
24 | ||
25 | #include "osi_compat.h" | |
26 | ||
27 | struct vcache *afs_globalVp = 0; | |
28 | struct vfs *afs_globalVFS = 0; | |
29 | struct vfsmount *afs_cacheMnt; | |
30 | int afs_was_mounted = 0; /* Used to force reload if mount/unmount/mount */ | |
31 | ||
32 | extern struct super_operations afs_sops; | |
33 | #if !defined(AFS_NONFSTRANS) | |
34 | extern struct export_operations afs_export_ops; | |
35 | #endif | |
36 | extern afs_rwlock_t afs_xvcache; | |
37 | extern struct afs_q VLRU; | |
38 | ||
39 | extern struct dentry_operations afs_dentry_operations; | |
40 | ||
41 | /* Forward declarations */ | |
42 | static int afs_root(struct super_block *afsp); | |
43 | int afs_fill_super(struct super_block *sb, void *data, int silent); | |
44 | ||
45 | ||
46 | /* | |
47 | * afs_mount (2.6.37+) and afs_get_sb (2.6.36-) are the entry | |
48 | * points from the vfs when mounting afs. The super block | |
49 | * structure is setup in the afs_fill_super callback function. | |
50 | */ | |
51 | ||
52 | #if defined(STRUCT_FILE_SYSTEM_TYPE_HAS_MOUNT) | |
53 | static struct dentry * | |
54 | afs_mount(struct file_system_type *fs_type, int flags, | |
55 | const char *dev_name, void *data) { | |
56 | return mount_nodev(fs_type, flags, data, afs_fill_super); | |
57 | } | |
58 | #elif defined(GET_SB_HAS_STRUCT_VFSMOUNT) | |
59 | static int | |
60 | afs_get_sb(struct file_system_type *fs_type, int flags, | |
61 | const char *dev_name, void *data, struct vfsmount *mnt) { | |
62 | return get_sb_nodev(fs_type, flags, data, afs_fill_super, mnt); | |
63 | } | |
64 | #else | |
65 | static struct super_block * | |
66 | afs_get_sb(struct file_system_type *fs_type, int flags, | |
67 | const char *dev_name, void *data) { | |
68 | return get_sb_nodev(fs_type, flags, data, afs_fill_super); | |
69 | } | |
70 | #endif | |
71 | ||
72 | struct file_system_type afs_fs_type = { | |
73 | .owner = THIS_MODULE, | |
74 | .name = "afs", | |
75 | #if defined(STRUCT_FILE_SYSTEM_TYPE_HAS_MOUNT) | |
76 | .mount = afs_mount, | |
77 | #else | |
78 | .get_sb = afs_get_sb, | |
79 | #endif | |
80 | .kill_sb = kill_anon_super, | |
81 | .fs_flags = FS_BINARY_MOUNTDATA, | |
82 | }; | |
83 | ||
84 | struct backing_dev_info *afs_backing_dev_info; | |
85 | ||
86 | int | |
87 | afs_fill_super(struct super_block *sb, void *data, int silent) | |
88 | { | |
89 | int code = 0; | |
90 | #if defined(HAVE_LINUX_BDI_INIT) | |
91 | int bdi_init_done = 0; | |
92 | #endif | |
93 | ||
94 | AFS_GLOCK(); | |
95 | if (afs_was_mounted) { | |
96 | printf | |
97 | ("You must reload the AFS kernel extensions before remounting AFS.\n"); | |
98 | AFS_GUNLOCK(); | |
99 | return -EINVAL; | |
100 | } | |
101 | afs_was_mounted = 1; | |
102 | ||
103 | /* Set basics of super_block */ | |
104 | __module_get(THIS_MODULE); | |
105 | ||
106 | afs_globalVFS = sb; | |
107 | #if defined(SB_NOATIME) | |
108 | sb->s_flags |= SB_NOATIME; | |
109 | #else | |
110 | sb->s_flags |= MS_NOATIME; | |
111 | #endif | |
112 | sb->s_blocksize = 1024; | |
113 | sb->s_blocksize_bits = 10; | |
114 | sb->s_magic = AFS_VFSMAGIC; | |
115 | sb->s_op = &afs_sops; /* Super block (vfs) ops */ | |
116 | ||
117 | #if defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP) | |
118 | sb->s_d_op = &afs_dentry_operations; | |
119 | #endif | |
120 | #if defined(HAVE_LINUX_SUPER_SETUP_BDI) | |
121 | code = super_setup_bdi(sb); | |
122 | if (code) | |
123 | goto out; | |
124 | sb->s_bdi->name = "openafs"; | |
125 | sb->s_bdi->ra_pages = 32; | |
126 | #else | |
127 | /* used for inodes backing_dev_info field, also */ | |
128 | afs_backing_dev_info = kzalloc(sizeof(struct backing_dev_info), GFP_NOFS); | |
129 | #if defined(HAVE_LINUX_BDI_INIT) | |
130 | code = bdi_init(afs_backing_dev_info); | |
131 | if (code) | |
132 | goto out; | |
133 | bdi_init_done = 1; | |
134 | #endif | |
135 | #if defined(STRUCT_BACKING_DEV_INFO_HAS_NAME) | |
136 | afs_backing_dev_info->name = "openafs"; | |
137 | #endif | |
138 | afs_backing_dev_info->ra_pages = 32; | |
139 | #if defined (STRUCT_SUPER_BLOCK_HAS_S_BDI) | |
140 | sb->s_bdi = afs_backing_dev_info; | |
141 | /* The name specified here will appear in the flushing thread name - flush-afs */ | |
142 | bdi_register(afs_backing_dev_info, NULL, "afs"); | |
143 | #endif | |
144 | #endif /* HAVE_LINUX_SUPER_SETUP_BDI */ | |
145 | #if !defined(AFS_NONFSTRANS) | |
146 | sb->s_export_op = &afs_export_ops; | |
147 | #endif | |
148 | #if defined(MAX_NON_LFS) | |
149 | #ifdef AFS_64BIT_CLIENT | |
150 | #if !defined(MAX_LFS_FILESIZE) | |
151 | #if BITS_PER_LONG==32 | |
152 | #define MAX_LFS_FILESIZE (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1) | |
153 | #elif BITS_PER_LONG==64 | |
154 | #define MAX_LFS_FILESIZE 0x7fffffffffffffff | |
155 | #endif | |
156 | #endif | |
157 | sb->s_maxbytes = MAX_LFS_FILESIZE; | |
158 | #else | |
159 | sb->s_maxbytes = MAX_NON_LFS; | |
160 | #endif | |
161 | #endif | |
162 | code = afs_root(sb); | |
163 | out: | |
164 | if (code) { | |
165 | afs_globalVFS = NULL; | |
166 | afs_FlushAllVCaches(); | |
167 | #if defined(HAVE_LINUX_BDI_INIT) | |
168 | if (bdi_init_done) | |
169 | bdi_destroy(afs_backing_dev_info); | |
170 | #endif | |
171 | #if !defined(HAVE_LINUX_SUPER_SETUP_BDI) | |
172 | kfree(afs_backing_dev_info); | |
173 | #endif | |
174 | module_put(THIS_MODULE); | |
175 | } | |
176 | ||
177 | AFS_GUNLOCK(); | |
178 | return code ? -EINVAL : 0; | |
179 | } | |
180 | ||
181 | ||
182 | /* afs_root - stat the root of the file system. AFS global held on entry. */ | |
183 | static int | |
184 | afs_root(struct super_block *afsp) | |
185 | { | |
186 | afs_int32 code = 0; | |
187 | struct vcache *tvp = 0; | |
188 | ||
189 | AFS_STATCNT(afs_root); | |
190 | if (afs_globalVp && (afs_globalVp->f.states & CStatd)) { | |
191 | tvp = afs_globalVp; | |
192 | } else { | |
193 | struct vrequest *treq = NULL; | |
194 | cred_t *credp = crref(); | |
195 | ||
196 | if (afs_globalVp) { | |
197 | afs_PutVCache(afs_globalVp); | |
198 | afs_globalVp = NULL; | |
199 | } | |
200 | ||
201 | if (!(code = afs_CreateReq(&treq, credp)) && !(code = afs_CheckInit())) { | |
202 | tvp = afs_GetVCache(&afs_rootFid, treq, NULL, NULL); | |
203 | if (tvp) { | |
204 | struct inode *ip = AFSTOV(tvp); | |
205 | struct vattr *vattr = NULL; | |
206 | ||
207 | code = afs_CreateAttr(&vattr); | |
208 | if (!code) { | |
209 | afs_getattr(tvp, vattr, credp); | |
210 | afs_fill_inode(ip, vattr); | |
211 | ||
212 | /* setup super_block and mount point inode. */ | |
213 | afs_globalVp = tvp; | |
214 | #if defined(HAVE_LINUX_D_MAKE_ROOT) | |
215 | afsp->s_root = d_make_root(ip); | |
216 | #else | |
217 | afsp->s_root = d_alloc_root(ip); | |
218 | #endif | |
219 | #if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP) | |
220 | afsp->s_root->d_op = &afs_dentry_operations; | |
221 | #endif | |
222 | afs_DestroyAttr(vattr); | |
223 | } | |
224 | } else | |
225 | code = EIO; | |
226 | } | |
227 | crfree(credp); | |
228 | afs_DestroyReq(treq); | |
229 | } | |
230 | ||
231 | afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, afs_globalVp, | |
232 | ICL_TYPE_INT32, code); | |
233 | return code; | |
234 | } | |
235 | ||
236 | /* super_operations */ | |
237 | ||
238 | #if defined(STRUCT_SUPER_OPERATIONS_HAS_ALLOC_INODE) | |
239 | static afs_kmem_cache_t *afs_inode_cachep; | |
240 | ||
241 | static struct inode * | |
242 | afs_alloc_inode(struct super_block *sb) | |
243 | { | |
244 | struct vcache *vcp; | |
245 | ||
246 | vcp = (struct vcache *) kmem_cache_alloc(afs_inode_cachep, KALLOC_TYPE); | |
247 | if (!vcp) | |
248 | return NULL; | |
249 | ||
250 | return AFSTOV(vcp); | |
251 | } | |
252 | ||
253 | static void | |
254 | afs_destroy_inode(struct inode *inode) | |
255 | { | |
256 | kmem_cache_free(afs_inode_cachep, inode); | |
257 | } | |
258 | ||
259 | void | |
260 | init_once(void * foo) | |
261 | { | |
262 | struct vcache *vcp = (struct vcache *) foo; | |
263 | ||
264 | inode_init_once(AFSTOV(vcp)); | |
265 | } | |
266 | ||
267 | int | |
268 | afs_init_inodecache(void) | |
269 | { | |
270 | #if defined(KMEM_CACHE_TAKES_DTOR) | |
271 | afs_inode_cachep = kmem_cache_create("afs_inode_cache", | |
272 | sizeof(struct vcache), 0, | |
273 | SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT, init_once_func, NULL); | |
274 | #else | |
275 | afs_inode_cachep = kmem_cache_create("afs_inode_cache", | |
276 | sizeof(struct vcache), 0, | |
277 | SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT, init_once_func); | |
278 | #endif | |
279 | if (afs_inode_cachep == NULL) | |
280 | return -ENOMEM; | |
281 | return 0; | |
282 | } | |
283 | ||
284 | void | |
285 | afs_destroy_inodecache(void) | |
286 | { | |
287 | if (afs_inode_cachep) | |
288 | (void) kmem_cache_destroy(afs_inode_cachep); | |
289 | } | |
290 | #else | |
291 | int | |
292 | afs_init_inodecache(void) | |
293 | { | |
294 | return 0; | |
295 | } | |
296 | ||
297 | void | |
298 | afs_destroy_inodecache(void) | |
299 | { | |
300 | return; | |
301 | } | |
302 | #endif | |
303 | ||
304 | #if defined(STRUCT_SUPER_OPERATIONS_HAS_EVICT_INODE) | |
305 | static void | |
306 | afs_evict_inode(struct inode *ip) | |
307 | { | |
308 | struct vcache *vcp = VTOAFS(ip); | |
309 | ||
310 | if (vcp->vlruq.prev || vcp->vlruq.next) | |
311 | osi_Panic("inode freed while on LRU"); | |
312 | if (vcp->hnext) | |
313 | osi_Panic("inode freed while still hashed"); | |
314 | ||
315 | truncate_inode_pages(&ip->i_data, 0); | |
316 | #if defined(HAVE_LINUX_CLEAR_INODE) | |
317 | clear_inode(ip); | |
318 | #else | |
319 | end_writeback(ip); | |
320 | #endif | |
321 | ||
322 | #if !defined(STRUCT_SUPER_OPERATIONS_HAS_ALLOC_INODE) | |
323 | afs_osi_Free(ip->u.generic_ip, sizeof(struct vcache)); | |
324 | #endif | |
325 | } | |
326 | #else | |
327 | static void | |
328 | afs_clear_inode(struct inode *ip) | |
329 | { | |
330 | struct vcache *vcp = VTOAFS(ip); | |
331 | ||
332 | if (vcp->vlruq.prev || vcp->vlruq.next) | |
333 | osi_Panic("inode freed while on LRU"); | |
334 | if (vcp->hnext) | |
335 | osi_Panic("inode freed while still hashed"); | |
336 | ||
337 | #if !defined(STRUCT_SUPER_OPERATIONS_HAS_ALLOC_INODE) | |
338 | afs_osi_Free(ip->u.generic_ip, sizeof(struct vcache)); | |
339 | #endif | |
340 | } | |
341 | #endif | |
342 | ||
343 | /* afs_put_super | |
344 | * Called from unmount to release super_block. */ | |
345 | static void | |
346 | afs_put_super(struct super_block *sbp) | |
347 | { | |
348 | AFS_GLOCK(); | |
349 | AFS_STATCNT(afs_unmount); | |
350 | ||
351 | afs_globalVFS = 0; | |
352 | afs_globalVp = 0; | |
353 | ||
354 | afs_shutdown(); | |
355 | mntput(afs_cacheMnt); | |
356 | ||
357 | osi_linux_verify_alloced_memory(); | |
358 | #if defined(HAVE_LINUX_BDI_INIT) | |
359 | bdi_destroy(afs_backing_dev_info); | |
360 | #endif | |
361 | kfree(afs_backing_dev_info); | |
362 | AFS_GUNLOCK(); | |
363 | ||
364 | sbp->s_dev = 0; | |
365 | module_put(THIS_MODULE); | |
366 | } | |
367 | ||
368 | ||
369 | /* afs_statfs | |
370 | * statp is in user space, so we need to cobble together a statfs, then | |
371 | * copy it. | |
372 | */ | |
373 | int | |
374 | #if defined(STATFS_TAKES_DENTRY) | |
375 | afs_statfs(struct dentry *dentry, struct kstatfs *statp) | |
376 | #else | |
377 | afs_statfs(struct super_block *sbp, struct kstatfs *statp) | |
378 | #endif | |
379 | { | |
380 | memset(statp, 0, sizeof(*statp)); | |
381 | ||
382 | AFS_STATCNT(afs_statfs); | |
383 | ||
384 | /* hardcode in case that which is giveth is taken away */ | |
385 | statp->f_type = 0x5346414F; | |
386 | #if defined(STATFS_TAKES_DENTRY) | |
387 | statp->f_bsize = dentry->d_sb->s_blocksize; | |
388 | #else | |
389 | statp->f_bsize = sbp->s_blocksize; | |
390 | #endif | |
391 | statp->f_blocks = statp->f_bfree = statp->f_bavail = statp->f_files = | |
392 | statp->f_ffree = AFS_VFS_FAKEFREE; | |
393 | statp->f_fsid.val[0] = AFS_VFSMAGIC; | |
394 | statp->f_fsid.val[1] = AFS_VFSFSID; | |
395 | statp->f_namelen = 256; | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | struct super_operations afs_sops = { | |
401 | #if defined(STRUCT_SUPER_OPERATIONS_HAS_ALLOC_INODE) | |
402 | .alloc_inode = afs_alloc_inode, | |
403 | .destroy_inode = afs_destroy_inode, | |
404 | #endif | |
405 | #if defined(STRUCT_SUPER_OPERATIONS_HAS_EVICT_INODE) | |
406 | .evict_inode = afs_evict_inode, | |
407 | #else | |
408 | .clear_inode = afs_clear_inode, | |
409 | #endif | |
410 | .put_super = afs_put_super, | |
411 | .statfs = afs_statfs, | |
412 | }; |