Whamcloud - gitweb
land 0.5.20.3 b_devel onto HEAD (b_devel will remain)
[fs/lustre-release.git] / lustre / obdclass / fsfilt_ext3.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  lustre/lib/fsfilt_ext3.c
5  *  Lustre filesystem abstraction routines
6  *
7  *  Copyright (C) 2002 Cluster File Systems, Inc.
8  *   Author: Andreas Dilger <adilger@clusterfs.com>
9  *
10  *   This file is part of Lustre, http://www.lustre.org.
11  *
12  *   Lustre is free software; you can redistribute it and/or
13  *   modify it under the terms of version 2 of the GNU General Public
14  *   License as published by the Free Software Foundation.
15  *
16  *   Lustre is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with Lustre; if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25
26 //#error "FIXME: this needs to be updated to match fsfilt_extN.c"
27
28 #define DEBUG_SUBSYSTEM S_FILTER
29
30 #include <linux/fs.h>
31 #include <linux/jbd.h>
32 #include <linux/slab.h>
33 #include <linux/init.h>
34 #include <linux/ext3_fs.h>
35 #include <linux/ext3_jbd.h>
36 #include <linux/version.h>
37 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
38 # include <linux/ext3_xattr.h>
39 #else
40 # include <asm/statfs.h>
41 #endif
42 #include <linux/kp30.h>
43 #include <linux/lustre_fsfilt.h>
44 #include <linux/obd.h>
45 #include <linux/module.h>
46
47 static kmem_cache_t *fcb_cache;
48 static atomic_t fcb_cache_count = ATOMIC_INIT(0);
49
50 struct fsfilt_cb_data {
51         struct journal_callback cb_jcb; /* data private to jbd */
52         fsfilt_cb_t cb_func;            /* MDS/OBD completion function */
53         struct obd_device *cb_obd;      /* MDS/OBD completion device */
54         __u64 cb_last_rcvd;             /* MDS/OST last committed operation */
55 };
56
57 #define EXT3_XATTR_INDEX_LUSTRE         5
58 #define XATTR_LUSTRE_MDS_OBJID          "system.lustre_mds_objid"
59
60 /*
61  * We don't currently need any additional blocks for rmdir and
62  * unlink transactions because we are storing the OST oa_id inside
63  * the inode (which we will be changing anyways as part of this
64  * transaction).
65  */
66 static void *fsfilt_ext3_start(struct inode *inode, int op)
67 {
68         /* For updates to the last recieved file */
69         int nblocks = EXT3_DATA_TRANS_BLOCKS;
70         void *handle;
71
72         switch(op) {
73         case FSFILT_OP_RMDIR:
74         case FSFILT_OP_UNLINK:
75                 nblocks += EXT3_DELETE_TRANS_BLOCKS;
76                 break;
77         case FSFILT_OP_RENAME:
78                 /* We may be modifying two directories */
79                 nblocks += EXT3_DATA_TRANS_BLOCKS;
80         case FSFILT_OP_SYMLINK:
81                 /* Possible new block + block bitmap + GDT for long symlink */
82                 nblocks += 3;
83         case FSFILT_OP_CREATE:
84         case FSFILT_OP_MKDIR:
85         case FSFILT_OP_MKNOD:
86                 /* New inode + block bitmap + GDT for new file */
87                 nblocks += 3;
88         case FSFILT_OP_LINK:
89                 /* Change parent directory */
90                 nblocks += EXT3_INDEX_EXTRA_TRANS_BLOCKS+EXT3_DATA_TRANS_BLOCKS;
91                 break;
92         case FSFILT_OP_SETATTR:
93                 /* Setattr on inode */
94                 nblocks += 1;
95                 break;
96         default: CERROR("unknown transaction start op %d\n", op);
97                  LBUG();
98         }
99
100         lock_kernel();
101         handle = journal_start(EXT3_JOURNAL(inode), nblocks);
102         unlock_kernel();
103
104         return handle;
105 }
106
107 static int fsfilt_ext3_commit(struct inode *inode, void *handle)
108 {
109         int rc;
110
111         lock_kernel();
112         rc = journal_stop((handle_t *)handle);
113         unlock_kernel();
114
115         return rc;
116 }
117
118 static int fsfilt_ext3_setattr(struct dentry *dentry, void *handle,
119                                struct iattr *iattr)
120 {
121         struct inode *inode = dentry->d_inode;
122         int rc;
123
124         lock_kernel();
125         if (inode->i_op->setattr)
126                 rc = inode->i_op->setattr(dentry, iattr);
127         else
128                 rc = inode_setattr(inode, iattr);
129
130         unlock_kernel();
131
132         return rc;
133 }
134
135 static int fsfilt_ext3_set_md(struct inode *inode, void *handle,
136                               void *lmm, int lmm_size)
137 {
138         int rc;
139
140         down(&inode->i_sem);
141         lock_kernel();
142         rc = ext3_xattr_set(handle, inode, EXT3_XATTR_INDEX_LUSTRE,
143                             XATTR_LUSTRE_MDS_OBJID, lmm, lmm_size, 0);
144         unlock_kernel();
145         up(&inode->i_sem);
146
147         if (rc) {
148                 CERROR("error adding MD data to inode %lu: rc = %d\n",
149                        inode->i_ino, rc);
150                 if (rc != -ENOSPC) LBUG();
151         }
152         return rc;
153 }
154
155 static int fsfilt_ext3_get_md(struct inode *inode, void *lmm, int size)
156 {
157         int rc;
158
159         down(&inode->i_sem);
160         lock_kernel();
161         rc = ext3_xattr_get(inode, EXT3_XATTR_INDEX_LUSTRE,
162                             XATTR_LUSTRE_MDS_OBJID, lmm, size);
163         unlock_kernel();
164         up(&inode->i_sem);
165
166         /* This gives us the MD size */
167         if (lmm == NULL)
168                 return (rc == -ENODATA) ? 0 : rc;
169
170         if (rc < 0) {
171                 CDEBUG(D_INFO, "error getting EA %s from inode %lu: "
172                        "rc = %d\n", XATTR_LUSTRE_MDS_OBJID, inode->i_ino, rc);
173                 memset(lmm, 0, size);
174                 return (rc == -ENODATA) ? 0 : rc;
175         }
176
177         return rc;
178 }
179
180 static ssize_t fsfilt_ext3_readpage(struct file *file, char *buf, size_t count,
181                                     loff_t *offset)
182 {
183         struct inode *inode = file->f_dentry->d_inode;
184         int rc = 0;
185
186         if (S_ISREG(inode->i_mode))
187                 rc = file->f_op->read(file, buf, count, offset);
188         else {
189                 struct buffer_head *bh;
190
191                 /* FIXME: this assumes the blocksize == count, but the calling
192                  *        function will detect this as an error for now */
193                 bh = ext3_bread(NULL, inode,
194                                 *offset >> inode->i_sb->s_blocksize_bits,
195                                 0, &rc);
196
197                 if (bh) {
198                         memcpy(buf, bh->b_data, inode->i_blksize);
199                         brelse(bh);
200                         rc = inode->i_blksize;
201                 }
202         }
203
204         return rc;
205 }
206
207 static void fsfilt_ext3_cb_func(struct journal_callback *jcb, int error)
208 {
209         struct fsfilt_cb_data *fcb = (struct fsfilt_cb_data *)jcb;
210
211         fcb->cb_func(fcb->cb_obd, fcb->cb_last_rcvd, error);
212
213         kmem_cache_free(fcb_cache, fcb);
214         atomic_dec(&fcb_cache_count);
215 }
216
217 static int fsfilt_ext3_set_last_rcvd(struct obd_device *obd, __u64 last_rcvd,
218                                      void *handle, fsfilt_cb_t cb_func)
219 {
220 #ifdef HAVE_JOURNAL_CALLBACK_STATUS
221         struct fsfilt_cb_data *fcb;
222
223         fcb = kmem_cache_alloc(fcb_cache, GFP_NOFS);
224         if (!fcb)
225                 RETURN(-ENOMEM);
226
227         atomic_inc(&fcb_cache_count);
228         fcb->cb_func = cb_func;
229         fcb->cb_obd = obd;
230         fcb->cb_last_rcvd = last_rcvd;
231
232         CDEBUG(D_EXT2, "set callback for last_rcvd: "LPD64"\n", last_rcvd);
233         lock_kernel();
234         /* Note that an "incompatible pointer" warning here is OK for now */
235         journal_callback_set(handle, fsfilt_ext3_cb_func,
236                              (struct journal_callback *)fcb);
237         unlock_kernel();
238 #else
239 #warning "no journal callback kernel patch, faking it..."
240         static long next = 0;
241
242         if (time_after(jiffies, next)) {
243                 CERROR("no journal callback kernel patch, faking it...\n");
244                 next = jiffies + 300 * HZ;
245         }
246
247         cb_func(obd, last_rcvd, 0);
248 #endif
249
250         return 0;
251 }
252
253 static int fsfilt_ext3_journal_data(struct file *filp)
254 {
255         struct inode *inode = filp->f_dentry->d_inode;
256
257         EXT3_I(inode)->i_flags |= EXT3_JOURNAL_DATA_FL;
258
259         return 0;
260 }
261
262 /*
263  * We need to hack the return value for the free inode counts because
264  * the current EA code requires one filesystem block per inode with EAs,
265  * so it is possible to run out of blocks before we run out of inodes.
266  *
267  * This can be removed when the ext3 EA code is fixed.
268  */
269 static int fsfilt_ext3_statfs(struct super_block *sb, struct statfs *sfs)
270 {
271         int rc = vfs_statfs(sb, sfs);
272
273         if (!rc && sfs->f_bfree < sfs->f_ffree)
274                 sfs->f_ffree = sfs->f_bfree;
275
276         return rc;
277 }
278
279 static int fsfilt_ext3_sync(struct super_block *sb)
280 {
281         return ext3_force_commit(sb);
282 }
283
284 static struct fsfilt_operations fsfilt_ext3_ops = {
285         fs_type:                "ext3",
286         fs_owner:               THIS_MODULE,
287         fs_start:               fsfilt_ext3_start,
288         fs_commit:              fsfilt_ext3_commit,
289         fs_setattr:             fsfilt_ext3_setattr,
290         fs_set_md:              fsfilt_ext3_set_md,
291         fs_get_md:              fsfilt_ext3_get_md,
292         fs_readpage:            fsfilt_ext3_readpage,
293         fs_journal_data:        fsfilt_ext3_journal_data,
294         fs_set_last_rcvd:       fsfilt_ext3_set_last_rcvd,
295         fs_statfs:              fsfilt_ext3_statfs,
296         fs_sync:                fsfilt_ext3_sync,
297 };
298
299 static int __init fsfilt_ext3_init(void)
300 {
301         int rc;
302
303         //rc = ext3_xattr_register();
304         fcb_cache = kmem_cache_create("fsfilt_ext3_fcb",
305                                       sizeof(struct fsfilt_cb_data), 0,
306                                       0, NULL, NULL);
307         if (!fcb_cache) {
308                 CERROR("error allocating fsfilt journal callback cache\n");
309                 GOTO(out, rc = -ENOMEM);
310         }
311
312         rc = fsfilt_register_ops(&fsfilt_ext3_ops);
313
314         if (rc)
315                 kmem_cache_destroy(fcb_cache);
316 out:
317         return rc;
318 }
319
320 static void __exit fsfilt_ext3_exit(void)
321 {
322         int rc;
323
324         fsfilt_unregister_ops(&fsfilt_ext3_ops);
325         rc = kmem_cache_destroy(fcb_cache);
326
327         if (rc || atomic_read(&fcb_cache_count)) {
328                 CERROR("can't free fsfilt callback cache: count %d, rc = %d\n",
329                        atomic_read(&fcb_cache_count), rc);
330         }
331
332         //rc = ext3_xattr_unregister();
333 }
334
335 MODULE_AUTHOR("Cluster File Systems, Inc. <info@clusterfs.com>");
336 MODULE_DESCRIPTION("Lustre ext3 Filesystem Helper v0.1");
337 MODULE_LICENSE("GPL");
338
339 module_init(fsfilt_ext3_init);
340 module_exit(fsfilt_ext3_exit);