1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
5  *  Copyright (C) 2000 Stelias Computing, Inc.
6  *  Copyright (C) 2000 Red Hat, Inc.
7  *
8  *   This file is part of InterMezzo, http://www.inter-mezzo.org.
9  *
10  *   InterMezzo is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   InterMezzo is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with InterMezzo; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  *  presto's super.c
24  */
25 
26 static char rcsid[] __attribute ((unused)) = "$Id: super.c,v 1.42 2003/09/30 15:51:52 sunsetyang Exp $";
27 #define INTERMEZZO_VERSION "$Revision: 1.42 $"
28 
29 #include <stdarg.h>
30 
31 #include <asm/bitops.h>
32 #include <asm/uaccess.h>
33 #include <asm/system.h>
34 
35 #include <linux/errno.h>
36 #include <linux/fs.h>
37 #include <linux/ext2_fs.h>
38 #include <linux/slab.h>
39 #include <linux/vmalloc.h>
40 #include <linux/sched.h>
41 #include <linux/stat.h>
42 #include <linux/string.h>
43 #include <linux/locks.h>
44 #include <linux/blkdev.h>
45 #include <linux/init.h>
46 #include <linux/devfs_fs_kernel.h>
47 #define __NO_VERSION__
48 #include <linux/module.h>
49 
50 #include <linux/intermezzo_fs.h>
51 #include <linux/intermezzo_psdev.h>
52 
53 #ifdef PRESTO_DEBUG
54 long presto_vmemory = 0;
55 long presto_kmemory = 0;
56 #endif
57 
58 /* returns an allocated string, copied out from data if opt is found */
opt_read(const char * opt,char * data)59 static char *opt_read(const char *opt, char *data)
60 {
61         char *value;
62         char *retval;
63 
64         CDEBUG(D_SUPER, "option: %s, data %s\n", opt, data);
65         if ( strncmp(opt, data, strlen(opt)) )
66                 return NULL;
67 
68         if ( (value = strchr(data, '=')) == NULL )
69                 return NULL;
70 
71         value++;
72         PRESTO_ALLOC(retval, strlen(value) + 1);
73         if ( !retval ) {
74                 CERROR("InterMezzo: Out of memory!\n");
75                 return NULL;
76         }
77 
78         strcpy(retval, value);
79         CDEBUG(D_SUPER, "Assigned option: %s, value %s\n", opt, retval);
80         return retval;
81 }
82 
opt_store(char ** dst,char * opt)83 static void opt_store(char **dst, char *opt)
84 {
85         if (!dst)
86                 CERROR("intermezzo: store_opt, error dst == NULL\n");
87 
88         if (*dst)
89                 PRESTO_FREE(*dst, strlen(*dst) + 1);
90         *dst = opt;
91 }
92 
opt_set_default(char ** dst,char * defval)93 static void opt_set_default(char **dst, char *defval)
94 {
95         if (!dst)
96                 CERROR("intermezzo: store_opt, error dst == NULL\n");
97 
98         if (*dst)
99                 PRESTO_FREE(*dst, strlen(*dst) + 1);
100         if (defval) {
101                 char *def_alloced;
102                 PRESTO_ALLOC(def_alloced, strlen(defval)+1);
103                 if (!def_alloced) {
104                         CERROR("InterMezzo: Out of memory!\n");
105                         return ;
106                 }
107                 strcpy(def_alloced, defval);
108                 *dst = def_alloced;
109         }
110 }
111 
112 
113 /* Find the options for InterMezzo in "options", saving them into the
114  * passed pointers.  If the pointer is null, the option is discarded.
115  * Copy out all non-InterMezzo options into cache_data (to be passed
116  * to the read_super operation of the cache).  The return value will
117  * be a pointer to the end of the cache_data.
118  */
presto_options(struct super_block * sb,char * options,char * cache_data,char ** cache_type,char ** fileset,char ** channel)119 static char *presto_options(struct super_block *sb,
120                             char *options, char *cache_data,
121                             char **cache_type, char **fileset,
122                             char **channel)
123 {
124         char *this_char;
125         char *cache_data_end = cache_data;
126 
127         /* set the defaults */
128         if (strcmp(sb->s_type->name, "intermezzo") == 0)
129             opt_set_default(cache_type, "ext3");
130         else
131             opt_set_default(cache_type, "tmpfs");
132 
133         if (!options || !cache_data)
134                 return cache_data_end;
135 
136 
137         CDEBUG(D_SUPER, "parsing options\n");
138         for (this_char = strtok (options, ",");
139              this_char != NULL;
140              this_char = strtok (NULL, ",")) {
141                 char *opt;
142                 CDEBUG(D_SUPER, "this_char %s\n", this_char);
143 
144                 if ( (opt = opt_read("fileset", this_char)) ) {
145                         opt_store(fileset, opt);
146                         continue;
147                 }
148                 if ( (opt = opt_read("cache_type", this_char)) ) {
149                         opt_store(cache_type, opt);
150                         continue;
151                 }
152                 if ( (opt = opt_read("channel", this_char)) ) {
153                         opt_store(channel, opt);
154                         continue;
155                 }
156 
157                 cache_data_end +=
158                         sprintf(cache_data_end, "%s%s",
159                                 cache_data_end != cache_data ? ",":"",
160                                 this_char);
161         }
162 
163         return cache_data_end;
164 }
165 
presto_set_channel(struct presto_cache * cache,char * channel)166 static int presto_set_channel(struct presto_cache *cache, char *channel)
167 {
168         int minor;
169 
170         ENTRY;
171         if (!channel) {
172                 minor = izo_psdev_get_free_channel();
173         } else {
174                 minor = simple_strtoul(channel, NULL, 0);
175 				PRESTO_FREE(channel, strlen(channel) + 1);
176         }
177         if (minor < 0 || minor >= MAX_CHANNEL) {
178                 CERROR("all channels in use or channel too large %d\n",
179                        minor);
180                 return -EINVAL;
181         }
182 
183         cache->cache_psdev = &(izo_channels[minor]);
184         list_add(&cache->cache_channel_list,
185                  &cache->cache_psdev->uc_cache_list);
186 
187         EXIT;
188         return minor;
189 }
190 
191 /* We always need to remove the presto options before passing
192    mount options to cache FS */
presto_read_super(struct super_block * sb,void * data,int silent)193 struct super_block * presto_read_super(struct super_block * sb,
194                                        void * data, int silent)
195 {
196         struct file_system_type *fstype;
197         struct presto_cache *cache = NULL;
198         char *cache_data = NULL;
199         char *cache_data_end;
200         char *cache_type = NULL;
201         char *fileset = NULL;
202         char *channel = NULL;
203         int err;
204         unsigned int minor;
205 
206         ENTRY;
207 
208         /* reserve space for the cache's data */
209         PRESTO_ALLOC(cache_data, PAGE_SIZE);
210         if ( !cache_data ) {
211                 CERROR("presto_read_super: Cannot allocate data page.\n");
212                 EXIT;
213                 goto out_err;
214         }
215 
216         /* read and validate options */
217         cache_data_end = presto_options(sb, data, cache_data, &cache_type,
218                                         &fileset, &channel);
219 
220         /* was there anything for the cache filesystem in the data? */
221         if (cache_data_end == cache_data) {
222                 PRESTO_FREE(cache_data, PAGE_SIZE);
223                 cache_data = NULL;
224         } else {
225                 CDEBUG(D_SUPER, "cache_data at %p is: %s\n", cache_data,
226                        cache_data);
227         }
228 
229         /* set up the cache */
230         cache = presto_cache_init();
231         if ( !cache ) {
232                 CERROR("presto_read_super: failure allocating cache.\n");
233                 EXIT;
234                 goto out_err;
235         }
236         cache->cache_type = cache_type;
237 
238         /* link cache to channel */
239         minor = presto_set_channel(cache, channel);
240         if (minor < 0) {
241                 EXIT;
242                 goto out_err;
243         }
244 
245         CDEBUG(D_SUPER, "Presto: type=%s, fset=%s, dev= %d, flags %x\n",
246                cache_type, fileset?fileset:"NULL", minor, cache->cache_flags);
247 
248         MOD_INC_USE_COUNT;
249 
250         /* get the filter for the cache */
251         fstype = get_fs_type(cache_type);
252         cache->cache_filter = filter_get_filter_fs((const char *)cache_type);
253         if ( !fstype || !cache->cache_filter) {
254                 CERROR("Presto: unrecognized fs type or cache type\n");
255                 MOD_DEC_USE_COUNT;
256                 EXIT;
257                 goto out_err;
258         }
259 
260         /* can we in fact mount the cache */
261         if ((fstype->fs_flags & FS_REQUIRES_DEV) && !sb->s_bdev) {
262                 CERROR("filesystem \"%s\" requires a valid block device\n",
263                                 cache_type);
264                 MOD_DEC_USE_COUNT;
265                 EXIT;
266                 goto out_err;
267         }
268 
269         sb = fstype->read_super(sb, cache_data, silent);
270 
271         /* this might have been freed above */
272         if (cache_data) {
273                 PRESTO_FREE(cache_data, PAGE_SIZE);
274                 cache_data = NULL;
275         }
276 
277         if ( !sb ) {
278                 CERROR("InterMezzo: cache mount failure.\n");
279                 MOD_DEC_USE_COUNT;
280                 EXIT;
281                 goto out_err;
282         }
283 
284         cache->cache_sb = sb;
285         cache->cache_root = dget(sb->s_root);
286 
287         /* we now know the dev of the cache: hash the cache */
288         presto_cache_add(cache, sb->s_dev);
289         err = izo_prepare_fileset(sb->s_root, fileset);
290 
291         if (fileset)
292                 PRESTO_FREE(fileset, strlen(fileset) + 1);
293 
294         filter_setup_journal_ops(cache->cache_filter, cache->cache_type);
295 
296         /* make sure we have our own super operations: sb
297            still contains the cache operations */
298         filter_setup_super_ops(cache->cache_filter, sb->s_op,
299                                &presto_super_ops);
300         sb->s_op = filter_c2usops(cache->cache_filter);
301 
302         /* get izo directory operations: sb->s_root->d_inode exists now */
303         filter_setup_dir_ops(cache->cache_filter, sb->s_root->d_inode,
304                              &presto_dir_iops, &presto_dir_fops);
305         filter_setup_dentry_ops(cache->cache_filter, sb->s_root->d_op,
306                                 &presto_dentry_ops);
307         sb->s_root->d_inode->i_op = filter_c2udiops(cache->cache_filter);
308         sb->s_root->d_inode->i_fop = filter_c2udfops(cache->cache_filter);
309         sb->s_root->d_op = filter_c2udops(cache->cache_filter);
310 
311         EXIT;
312         return sb;
313 
314  out_err:
315         CDEBUG(D_SUPER, "out_err called\n");
316         if (cache)
317                 PRESTO_FREE(cache, sizeof(struct presto_cache));
318         if (cache_data)
319                 PRESTO_FREE(cache_data, PAGE_SIZE);
320         if (fileset)
321                 PRESTO_FREE(fileset, strlen(fileset) + 1);
322         if (channel)
323                 PRESTO_FREE(channel, strlen(channel) + 1);
324         if (cache_type)
325                 PRESTO_FREE(cache_type, strlen(cache_type) + 1);
326 
327         CDEBUG(D_MALLOC, "mount error exit: kmem %ld, vmem %ld\n",
328                presto_kmemory, presto_vmemory);
329         return NULL;
330 }
331 
332 
333 
334 #ifdef PRESTO_DEVEL
335 static DECLARE_FSTYPE(presto_fs_type, "izo", presto_read_super, FS_REQUIRES_DEV);
336 static DECLARE_FSTYPE(vpresto_fs_type, "vintermezzo", presto_read_super, FS_LITTER);
337 #else
338 static DECLARE_FSTYPE(vpresto_fs_type, "vintermezzo", presto_read_super, FS_LITTER);
339 static DECLARE_FSTYPE(presto_fs_type, "intermezzo", presto_read_super, FS_REQUIRES_DEV);
340 #endif
341 
342 
343 
init_intermezzo_fs(void)344 int __init init_intermezzo_fs(void)
345 {
346         int status;
347 
348         printk(KERN_INFO "InterMezzo Kernel/Intersync communications " INTERMEZZO_VERSION
349                " info@clusterfs.com\n");
350 
351         status = presto_psdev_init();
352         if ( status ) {
353                 CERROR("Problem (%d) in init_intermezzo_psdev\n", status);
354                 return status;
355         }
356 
357         status = init_intermezzo_sysctl();
358         if (status) {
359                 CERROR("presto: failed in init_intermezzo_sysctl!\n");
360         }
361 
362         presto_cache_init_hash();
363 
364         if (!presto_init_ddata_cache()) {
365                 CERROR("presto out of memory!\n");
366                 return -ENOMEM;
367         }
368 
369         status = register_filesystem(&presto_fs_type);
370         if (status) {
371                 CERROR("presto: failed in register_filesystem!\n");
372         }
373         status = register_filesystem(&vpresto_fs_type);
374         if (status) {
375                 CERROR("vpresto: failed in register_filesystem!\n");
376         }
377         return status;
378 }
379 
exit_intermezzo_fs(void)380 void __exit exit_intermezzo_fs(void)
381 {
382         int err;
383 
384         ENTRY;
385 
386         if ( (err = unregister_filesystem(&presto_fs_type)) != 0 ) {
387                 CERROR("presto: failed to unregister filesystem\n");
388         }
389         if ( (err = unregister_filesystem(&vpresto_fs_type)) != 0 ) {
390                 CERROR("vpresto: failed to unregister filesystem\n");
391         }
392 
393         presto_psdev_cleanup();
394         cleanup_intermezzo_sysctl();
395         presto_cleanup_ddata_cache();
396         CERROR("after cleanup: kmem %ld, vmem %ld\n",
397                presto_kmemory, presto_vmemory);
398 }
399 
400 
401 MODULE_AUTHOR("Cluster Filesystems Inc. <info@clusterfs.com>");
402 MODULE_DESCRIPTION("InterMezzo Kernel/Intersync communications " INTERMEZZO_VERSION);
403 MODULE_LICENSE("GPL");
404 
405 module_init(init_intermezzo_fs)
406 module_exit(exit_intermezzo_fs)
407