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