Whamcloud - gitweb
- landing b_fid.
[fs/lustre-release.git] / lustre / smfs / smfs_lib.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  lustre/smfs/super.c
5  *  Lustre filesystem abstraction routines
6  *
7  *  Copyright (C) 2004 Cluster File Systems, Inc.
8  *
9  *   This file is part of Lustre, http://www.lustre.org.
10  *
11  *   Lustre is free software; you can redistribute it and/or
12  *   modify it under the terms of version 2 of the GNU General Public
13  *   License as published by the Free Software Foundation.
14  *
15  *   Lustre is distributed in the hope that it will be useful,
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *   GNU General Public License for more details.
19  *
20  *   You should have received a copy of the GNU General Public License
21  *   along with Lustre; if not, write to the Free Software
22  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #define DEBUG_SUBSYSTEM S_SM
26
27 #include <linux/config.h>
28 #include <linux/module.h>
29 #include <linux/kmod.h>
30 #include <linux/init.h>
31 #include <linux/fs.h>
32 #include <linux/string.h>
33 #include <linux/mm.h>
34 #include <linux/utime.h>
35 #include <linux/file.h>
36 #include <linux/slab.h>
37 #include <linux/dcache.h>
38 #include <linux/loop.h>
39 #include <linux/errno.h>
40 #include <linux/obd_class.h>
41 #include <linux/obd_support.h>
42 #include <linux/lustre_lib.h>
43 #include <linux/lustre_idl.h>
44 #include <linux/lustre_fsfilt.h>
45 #include <linux/lustre_smfs.h>
46 #include "smfs_internal.h"
47
48 static char *smfs_options(char *data, char **devstr, 
49                           char **namestr, char *opts, 
50                           int *flags)  
51 {
52         struct option *opt_value = NULL;
53         char   *pos;
54         
55         LASSERT(opts && flags);
56
57         while (!(get_opt(&opt_value, &pos))) {
58                 if (!strcmp(opt_value->opt, "dev")) {
59                         if (devstr != NULL)
60                                 *devstr = opt_value->value;
61                 } else if (!strcmp(opt_value->opt, "type")) {
62                         if (namestr != NULL)
63                                 *namestr = opt_value->value;
64                 } else if (!strcmp(opt_value->opt, "kml")) {
65                         *flags |= SM_DO_REC;
66                 } else if (!strcmp(opt_value->opt, "cache")) {
67                         *flags |= SM_CACHE_HOOK;
68                 } else if (!strcmp(opt_value->opt, "snap")) {
69                         *flags |= SM_DO_COW;
70                 } else if (!strcmp(opt_value->opt, "options")) {
71                         if (strlen(opts) == 0)
72                                 sprintf((char *)opts + strlen(opts), "%s",
73                                         opt_value->value);
74                         else  
75                                 sprintf((char *)opts + strlen(opts), ",%s",
76                                         opt_value->value);
77                 } else {
78                         /* FIXME-WANGDI: how about the opt_value->value */
79                         if (strlen(opts) == 0)
80                                 sprintf((char *)opts + strlen(opts), "%s",
81                                         opt_value->opt);
82                         else  
83                                 sprintf((char *)opts + strlen(opts), ",%s",
84                                         opt_value->opt);
85                 }
86         }
87         return pos;
88 }
89
90 struct super_block *smfs_get_sb_by_path(char *path, int len)
91 {
92         struct super_block *sb;
93         struct nameidata nd;
94         int error = 0;
95
96         ENTRY;
97
98 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
99         if (path_init(path, LOOKUP_FOLLOW, &nd)) {
100 #else
101         if (path_lookup(path, LOOKUP_FOLLOW, &nd)) {
102 #endif
103                 error = path_walk(path, &nd);
104                 if (error) {
105                         path_release(&nd);
106                         RETURN(NULL);
107                 }
108         } else {
109                 RETURN(NULL);
110         }
111
112         /* FIXME-WANGDI: add some check code here. */
113         sb = nd.dentry->d_sb;
114         path_release(&nd);
115         RETURN(sb);
116 }
117
118 static struct smfs_super_info *smfs_init_smb(struct super_block *sb)
119 {
120         struct smfs_super_info *smb;
121         ENTRY;
122
123         OBD_ALLOC(smb, sizeof(*smb));
124         if (!smb)
125                 RETURN(NULL);        
126         
127         S2FSI(sb) = smb;
128         RETURN(smb);        
129 }
130
131 static int smfs_init_fsfilt_ops(struct smfs_super_info *smb)
132 {
133         ENTRY;
134         if (!smb->sm_cache_fsfilt) {
135                 smb->sm_cache_fsfilt =
136                         fsfilt_get_ops(smb->smsi_cache_ftype);
137                 if (!smb->sm_cache_fsfilt) {
138                         CERROR("Can not get %s fsfilt ops needed by kml\n",
139                                smb->smsi_cache_ftype);
140                         RETURN(-EINVAL);
141                 }
142         }
143         if (!smb->sm_fsfilt) {
144                 smb->sm_fsfilt =
145                         fsfilt_get_ops(smb->smsi_ftype);
146                 if (!smb->sm_fsfilt) {
147                         CERROR("Can not get %s fsfilt ops needed by kml\n",
148                                smb->smsi_ftype);
149                         RETURN(-EINVAL);
150                 }
151         }
152         RETURN(0);
153 }
154
155 void smfs_cleanup_fsfilt_ops(struct smfs_super_info *smb)
156 {
157         if (smb->sm_cache_fsfilt)
158                 fsfilt_put_ops(smb->sm_cache_fsfilt);
159         if (smb->sm_fsfilt)
160                 fsfilt_put_ops(smb->sm_fsfilt);
161 }
162
163 static int smfs_mount_cache(struct smfs_super_info *smb, char *devstr, 
164                             char *typestr, char *opts)
165 {
166         int err = 0, typelen;
167         struct vfsmount *mnt;
168         ENTRY;
169
170         typelen = strlen(typestr);
171
172         printk("smfs: mounting %s at %s\n", typestr, devstr);
173         mnt = do_kern_mount(typestr, 0, devstr, (void *)opts);
174         if (IS_ERR(mnt)) {
175                 CERROR("do_kern_mount failed: rc = %ld\n", 
176                        PTR_ERR(mnt));
177                 GOTO(err_out, err = PTR_ERR(mnt));
178         }
179
180         smb->smsi_sb = mnt->mnt_sb;
181         smb->smsi_mnt = mnt;
182
183         smfs_init_sm_ops(smb);
184
185         OBD_ALLOC(smb->smsi_cache_ftype, strlen(typestr) + 1);
186         memcpy(smb->smsi_cache_ftype, typestr, strlen(typestr));
187
188         OBD_ALLOC(smb->smsi_ftype, strlen(SMFS_TYPE) + 1);
189         memcpy(smb->smsi_ftype, SMFS_TYPE, strlen(SMFS_TYPE));
190         
191         err = smfs_init_fsfilt_ops(smb);
192 err_out:
193         RETURN(err);
194 }
195
196 static int smfs_umount_cache(struct smfs_super_info *smb)
197 {
198         struct dentry *root = smb->smsi_sb->s_root;
199         
200         dput(root);
201         if (atomic_read(&root->d_inode->i_count) == 0)
202                 igrab(root->d_inode); 
203         
204         mntput(smb->smsi_mnt);
205         smfs_cleanup_sm_ops(smb);
206         smfs_cleanup_fsfilt_ops(smb);
207
208         if (smb->smsi_cache_ftype)
209                 OBD_FREE(smb->smsi_cache_ftype,
210                          strlen(smb->smsi_cache_ftype) + 1);
211         if (smb->smsi_ftype)
212                 OBD_FREE(smb->smsi_ftype, strlen(smb->smsi_ftype) + 1);
213                
214         return 0;
215 }
216
217 static int smfs_init_hook_ops(struct smfs_super_info *smb)
218 {
219         ENTRY;
220         INIT_LIST_HEAD(&smb->smsi_hook_list);
221         RETURN(0); 
222 }
223
224 static void smfs_cleanup_hook_ops(struct smfs_super_info *smb)
225 {
226         struct list_head *hlist = &smb->smsi_hook_list;
227         ENTRY;
228
229         while (!list_empty(hlist)) {
230                 struct smfs_hook_ops *smfs_hops;
231                 
232                 smfs_hops = list_entry(hlist->next, struct smfs_hook_ops, 
233                                        smh_list);
234                 CERROR("Unregister %s hook ops\n", smfs_hops->smh_name);         
235                 
236                 smfs_unregister_hook_ops(smb, smfs_hops->smh_name);
237                 smfs_free_hook_ops(smfs_hops); 
238         } 
239         EXIT;
240 }
241
242 static void smfs_cleanup_smb(struct super_block *sb)
243 {
244         struct smfs_super_info *smb;
245         ENTRY;
246
247         smb = S2SMI(sb);
248         if (smb) 
249                 OBD_FREE(smb, sizeof(*smb));
250         EXIT;
251 }
252
253 void smfs_cleanup_hooks(struct smfs_super_info *smb)
254 {
255         
256         if (SMFS_CACHE_HOOK(smb))
257                 cache_space_hook_exit(smb);
258         if (SMFS_DO_REC(smb))
259                 smfs_rec_cleanup(smb);
260 #if CONFIG_SNAPFS
261         if (SMFS_DO_COW(smb))
262                 smfs_cow_cleanup(smb);
263 #endif  
264         smfs_cleanup_hook_ops(smb);
265 }
266
267 void smfs_put_super(struct super_block *sb)
268 {
269         struct smfs_super_info *smfs_info = S2SMI(sb);
270
271         smfs_cleanup_hooks(smfs_info);
272         
273         if (sb)
274                 smfs_umount_cache(smfs_info);
275         smfs_cleanup_smb(sb); 
276 }
277
278 static int smfs_init_hooks(struct super_block *sb)
279
280         ENTRY;
281  
282         if (SMFS_DO_REC(S2SMI(sb))) 
283                 smfs_rec_init(sb);
284         if (SMFS_CACHE_HOOK(S2SMI(sb))) 
285                 cache_space_hook_init(sb);
286 #if CONFIG_SNAPFS
287         if (SMFS_DO_COW(S2SMI(sb))) 
288                 smfs_cow_init(sb);
289 #endif
290         RETURN(0);
291 }
292
293 int smfs_fill_super(struct super_block *sb, void *data, int silent)
294 {
295         struct inode *root_inode = NULL;
296         struct smfs_super_info *smb = NULL;
297         char *devstr = NULL, *typestr = NULL; 
298         char *opts = NULL, *cache_data = NULL;
299         unsigned long page;
300         int err = 0; 
301         ino_t root_ino;
302
303         ENTRY;
304
305         CDEBUG(D_SUPER, "mount opts: %s\n", data ? 
306                (char *)data : "(none)");
307
308         smb = smfs_init_smb(sb);
309         if (!smb)
310                 RETURN(-ENOMEM);
311  
312         page = __get_free_page(GFP_KERNEL);
313         if (!page)
314                 GOTO(out_err, err = -ENOMEM);
315         
316         memset((void *)page, 0, PAGE_SIZE);
317         opts = (char *)page;
318
319         init_option(data);
320         cache_data = smfs_options(data, &devstr, &typestr, opts, 
321                                   &smb->smsi_flags); 
322         if (*cache_data)
323                 CWARN("smfs_fill_super(): options parsing stoped at "
324                       "option %s\n", cache_data);
325
326         if (!typestr || !devstr) {
327                 CERROR("mount options name and dev are mandatory\n");
328                 free_page(page);
329                 GOTO(out_err, err = -EINVAL);
330         }
331         
332         err = smfs_mount_cache(smb, devstr, typestr, opts);
333         free_page(page);
334         
335         if (err) {
336                 CERROR("Can not mount %s as %s\n", devstr, typestr);
337                 GOTO(out_err, err);
338         }
339
340         duplicate_sb(sb, smb->smsi_sb);
341         sm_set_sb_ops(smb->smsi_sb, sb);
342
343         err = smfs_init_hook_ops(smb);
344         if (err) {
345                 CERROR("Can not init super hook ops err %d\n", err);
346                 smfs_umount_cache(smb);
347                 GOTO(out_err, err);
348         }
349         
350         /* init the root_inode of smfs. */ 
351         dget(S2CSB(sb)->s_root);
352         root_ino = S2CSB(sb)->s_root->d_inode->i_ino;
353         root_inode = smfs_get_inode(sb, root_ino, NULL, 0);
354
355         CDEBUG(D_SUPER, "readinode %p, root ino %ld, root inode at %p\n",
356                sb->s_op->read_inode, root_ino, root_inode);
357
358         sb->s_root = d_alloc_root(root_inode);
359
360         if (!sb->s_root) {
361                 smfs_umount_cache(smb);
362                 GOTO(out_err, err = -ENOMEM);
363         }
364         
365         err = smfs_init_hooks(sb);  
366         if (err) {
367                 smfs_umount_cache(smb);
368                 GOTO(out_err, err);
369         }       
370 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
371         CDEBUG(D_SUPER, "sb %lx, &sb->u.generic_sbp: %lx\n",
372                (ulong)sb, (ulong)&sb->u.generic_sbp);
373 #else
374         CDEBUG(D_SUPER, "sb %lx, &sb->s_fs_info: %lx\n",
375                (ulong)sb, (ulong)&sb->s_fs_info);
376 #endif
377 out_err:
378         cleanup_option();
379         if (err)
380                 smfs_cleanup_smb(sb);
381         return err;
382 }
383
384 struct smfs_hook_ops *smfs_alloc_hook_ops(char *name, smfs_hook_func pre_hook, 
385                                           smfs_hook_func post_hook)
386 {
387         struct smfs_hook_ops *smfs_hops = NULL;
388         
389         ENTRY;
390         OBD_ALLOC(smfs_hops, sizeof(struct smfs_hook_ops));
391
392         if (!smfs_hops)
393                 RETURN(NULL);
394  
395         OBD_ALLOC(smfs_hops->smh_name, strlen(name) + 1);
396         
397         if (!smfs_hops->smh_name) { 
398                 OBD_FREE(smfs_hops, sizeof(struct smfs_hook_ops));
399                 RETURN(NULL);
400         }
401         
402         memcpy(smfs_hops->smh_name, name, strlen(name));  
403        
404         smfs_hops->smh_post_op = post_hook;  
405         smfs_hops->smh_pre_op = pre_hook;  
406         
407         RETURN(smfs_hops); 
408 }
409
410 void smfs_free_hook_ops(struct smfs_hook_ops *hops)
411 {
412         if (hops) {
413                 if (hops->smh_name){
414                         OBD_FREE(hops->smh_name, strlen(hops->smh_name) + 1);
415                 }
416                 OBD_FREE(hops, sizeof(struct smfs_hook_ops));
417         }
418 }
419
420 int smfs_register_hook_ops(struct smfs_super_info *smb, 
421                            struct smfs_hook_ops *smh_ops)
422 {
423         struct list_head *hlist = &smb->smsi_hook_list;
424         struct list_head *p;
425         ENTRY;
426  
427         list_for_each(p, hlist) {
428                 struct smfs_hook_ops *found;               
429                 found = list_entry(p, struct smfs_hook_ops, smh_list);
430                 if (!strcmp(found->smh_name, smh_ops->smh_name)) {
431                         CWARN("hook ops %s list  reregister\n", smh_ops->smh_name);
432                         RETURN(0);
433                 }
434         }
435         list_add(&smh_ops->smh_list, hlist);
436         RETURN(0);
437
438
439 struct smfs_hook_ops *smfs_unregister_hook_ops(struct smfs_super_info *smb, 
440                                                char *name)
441 {
442         struct list_head *hlist = &smb->smsi_hook_list;
443         struct list_head *p;
444         ENTRY;      
445  
446         list_for_each(p, hlist) {
447                 struct smfs_hook_ops *found;
448
449                 found = list_entry(p, typeof(*found), smh_list);
450                 if (!memcmp(found->smh_name, name, strlen(name))) {
451                         list_del(p);
452                         RETURN(found);
453                 }
454         } 
455         RETURN(NULL);
456 }
457
458 void *smfs_trans_start(struct inode *inode, int op, void *desc_private)
459 {
460         struct fsfilt_operations *fsfilt = S2SMI(inode->i_sb)->sm_fsfilt;
461
462         CDEBUG(D_INFO, "trans start %p\n", fsfilt->fs_start);
463
464         SMFS_TRANS_OP(inode, op);
465         
466         /* There are some problem here. fs_start in fsfilt is used by lustre
467          * the journal blocks of write rec are not counted in FIXME later */
468         if (fsfilt->fs_start)
469                 return fsfilt->fs_start(inode, op, desc_private, 0);
470         return NULL;
471 }
472
473 void smfs_trans_commit(struct inode *inode, void *handle, int force_sync)
474 {
475         struct fsfilt_operations *fsfilt = S2SMI(inode->i_sb)->sm_fsfilt;
476
477         if (!handle)
478                 return;
479
480         CDEBUG(D_INFO, "trans commit %p\n", fsfilt->fs_commit);
481
482         if (fsfilt->fs_commit)
483                 fsfilt->fs_commit(inode->i_sb, inode, handle, force_sync);
484 }
485