Whamcloud - gitweb
Add interface to extN filesystem which uses extended attributes to store
[fs/lustre-release.git] / lustre / mds / mds_extN.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  linux/mds/mds_extN.c
5  *
6  *  Lustre Metadata Server (mds) journal abstraction routines
7  *
8  *  Copyright (C) 2002  Cluster File Systems, Inc.
9  *  author: Andreas Dilger <adilger@clusterfs.com>
10  *
11  *  This code is issued under the GNU General Public License.
12  *  See the file COPYING in this distribution
13  *
14  */
15
16 #define DEBUG_SUBSYSTEM S_MDS
17
18 #include <linux/fs.h>
19 #include <linux/jbd.h>
20 #include <linux/extN_fs.h>
21 #include <linux/extN_jbd.h>
22 #include <linux/extN_xattr.h>
23 #include <linux/lustre_mds.h>
24
25 struct mds_fs_operations mds_extN_fs_ops;
26 static kmem_cache_t *jcb_cache;
27 static int jcb_cache_count;
28
29 struct mds_cb_data {
30         struct journal_callback cb_jcb;
31         struct mds_obd *cb_mds;
32         __u64 cb_last_rcvd;
33 };
34
35 struct mds_objid {
36         __u16 mo_magic;
37         __u8  mo_unused;
38         __u8  mo_ost;
39         __u64 mo_id;
40 };
41
42 #define EXTN_XATTR_INDEX_LUSTRE         5
43 #define XATTR_LUSTRE_MDS_OBJID          "system.lustre_mds_objid"
44
45 #define XATTR_MDS_MO_MAGIC              0x4711
46
47 int mds_extN_init(struct mds_obd *mds)
48 {
49         int rc;
50
51         /*
52          * Replace the client filesystem delete_inode method with our own,
53          * so that we can clear the object ID before the inode is deleted.
54          * The fs_delete_inode method will call cl_delete_inode for us.
55          *
56          * We need to do this for the MDS superblock only, hence we install
57          * a modified copy of the original superblock method table.
58          *
59          * We still assume that there is only a single MDS client filesystem
60          * type, as we don't have access to the mds struct in delete_inode
61          * and store the client delete_inode method in a global table.  This
62          * will only become a problem when multiple MDSs are running on a
63          * single host with different client filesystems.
64          */
65         OBD_ALLOC(mds->mds_sop, sizeof(*mds->mds_sop));
66         if (!mds->mds_sop)
67                 GOTO(out, rc = -ENOMEM);
68
69         memcpy(mds->mds_sop, mds->mds_sb->s_op, sizeof(*mds->mds_sop));
70         mds_extN_fs_ops.cl_delete_inode = mds->mds_sop->delete_inode;
71         mds->mds_sop->delete_inode = mds_extN_fs_ops.fs_delete_inode;
72         mds->mds_sb->s_op = mds->mds_sop;
73
74         //rc = extN_xattr_register();
75         jcb_cache = kmem_cache_create("mds_extN_jcb",
76                                       sizeof(struct mds_cb_data), 0,
77                                       SLAB_POISON, NULL, NULL);
78         if (!jcb_cache) {
79                 CERROR("error allocating MDS journal callback cache\n");
80                 GOTO(out_sop, rc = -ENOMEM);
81         }
82
83         return 0;
84
85 out_sop:
86         OBD_FREE(mds->mds_sop, sizeof(*mds->mds_sop));
87 out:
88         return rc;
89 }
90
91 int mds_extN_exit(struct mds_obd *mds)
92 {
93         int rc;
94
95         rc = kmem_cache_destroy(jcb_cache);
96
97         if (rc || jcb_cache_count) {
98                 CERROR("can't free MDS callback cache: count %d, rc = %d\n",
99                        jcb_cache_count, rc);
100         }
101
102         //rc = extN_xattr_unregister();
103         OBD_FREE(mds->mds_sop, sizeof(*mds->mds_sop));
104         return rc;
105 }
106
107 /*
108  * We don't currently need any additional blocks for rmdir and
109  * unlink transactions because we are storing the OST oa_id inside
110  * the inode (which we will be changing anyways as part of this
111  * transaction).
112  */
113 static void *mds_extN_start(struct inode *inode, int op)
114 {
115         /* For updates to the last recieved file */
116         int nblocks = EXTN_DATA_TRANS_BLOCKS;
117
118         switch(op) {
119         case MDS_FSOP_RMDIR:
120         case MDS_FSOP_UNLINK:
121                 nblocks += EXTN_DELETE_TRANS_BLOCKS;
122                 break;
123         case MDS_FSOP_RENAME:
124                 /* We may be modifying two directories */
125                 nblocks += EXTN_DATA_TRANS_BLOCKS;
126         case MDS_FSOP_SYMLINK:
127                 /* Possible new block + block bitmap + GDT for long symlink */
128                 nblocks += 3;
129         case MDS_FSOP_CREATE:
130         case MDS_FSOP_MKDIR:
131         case MDS_FSOP_MKNOD:
132                 /* New inode + block bitmap + GDT for new file */
133                 nblocks += 3;
134         case MDS_FSOP_LINK:
135                 /* Change parent directory */
136                 nblocks += EXTN_INDEX_EXTRA_TRANS_BLOCKS+EXTN_DATA_TRANS_BLOCKS;
137                 break;
138         case MDS_FSOP_SETATTR:
139                 /* Setattr on inode */
140                 nblocks += 1;
141                 break;
142         default: CERROR("unknown transaction start op %d\n", op);
143                  LBUG();
144         }
145
146         return journal_start(EXTN_JOURNAL(inode), nblocks);
147 }
148
149 static int mds_extN_commit(struct inode *inode, void *handle)
150 {
151         return journal_stop((handle_t *)handle);
152 }
153
154 static int mds_extN_setattr(struct dentry *dentry, void *handle,
155                             struct iattr *iattr)
156 {
157         struct inode *inode = dentry->d_inode;
158
159         if (inode->i_op->setattr)
160                 return inode->i_op->setattr(dentry, iattr);
161         else
162                 return inode_setattr(inode, iattr);
163 }
164
165 static int mds_extN_set_objid(struct inode *inode, void *handle, obd_id id)
166 {
167         struct mds_objid data;
168         int rc;
169
170         data.mo_magic = cpu_to_le16(XATTR_MDS_MO_MAGIC);
171         data.mo_unused = 0;
172         data.mo_ost = 0;  /* FIXME: store OST index here */
173         data.mo_id = cpu_to_le64(id);
174
175         lock_kernel();
176         down(&inode->i_sem);
177         if (id == 0) {
178                 rc = extN_xattr_set(handle, inode, EXTN_XATTR_INDEX_LUSTRE,
179                                     XATTR_LUSTRE_MDS_OBJID, NULL, 0, 0);
180         } else {
181                 rc = extN_xattr_set(handle, inode, EXTN_XATTR_INDEX_LUSTRE,
182                                     XATTR_LUSTRE_MDS_OBJID, &data,
183                                     sizeof(struct mds_objid), XATTR_CREATE);
184         }
185         up(&inode->i_sem);
186         unlock_kernel();
187
188         if (rc)
189                 CERROR("error adding objectid %Ld to inode %ld\n",
190                        (unsigned long long)id, inode->i_ino);
191         return rc;
192 }
193
194 static int mds_extN_get_objid(struct inode *inode, obd_id *id)
195 {
196         struct mds_objid data;
197         int rc;
198
199         lock_kernel();
200         down(&inode->i_sem);
201         rc = extN_xattr_get(inode, EXTN_XATTR_INDEX_LUSTRE,
202                             XATTR_LUSTRE_MDS_OBJID, &data,
203                             sizeof(struct mds_objid));
204         up(&inode->i_sem);
205         unlock_kernel();
206
207         if (rc < 0) {
208                 CERROR("error getting EA %s from MDS inode %ld: rc = %d\n",
209                        XATTR_LUSTRE_MDS_OBJID, inode->i_ino, rc);
210                 *id = 0;
211         } else if (data.mo_magic != cpu_to_le16(XATTR_MDS_MO_MAGIC)) {
212                 CERROR("MDS object id %Ld has bad magic %x\n",
213                        (unsigned long long)le64_to_cpu(data.mo_id),
214                        le16_to_cpu(data.mo_magic));
215                 rc = -EINVAL;
216         } else {
217                 *id = le64_to_cpu(data.mo_id);
218                 /* FIXME: will actually use data.mo_ost at some point */
219                 if (data.mo_ost)
220                         CERROR("MDS objid %Ld with ost index %d!\n",
221                                *id, data.mo_ost);
222         }
223
224         return rc;
225 }
226
227 static ssize_t mds_extN_readpage(struct file *file, char *buf, size_t count,
228                                  loff_t *offset)
229 {
230         struct inode *inode = file->f_dentry->d_inode;
231         int rc = 0;
232
233         if (S_ISREG(inode->i_mode))
234                 rc = file->f_op->read(file, buf, count, offset);
235         else {
236                 struct buffer_head *bh;
237
238                 /* FIXME: this assumes the blocksize == count, but the calling
239                  *        function will detect this as an error for now */
240                 bh = extN_bread(NULL, inode,
241                                 *offset >> inode->i_sb->s_blocksize_bits,
242                                 0, &rc);
243
244                 if (bh) {
245                         memcpy(buf, bh->b_data, inode->i_blksize);
246                         brelse(bh);
247                         rc = inode->i_blksize;
248                 }
249         }
250
251         return rc;
252 }
253
254 static void mds_extN_delete_inode(struct inode *inode)
255 {
256         if (S_ISREG(inode->i_mode)) {
257                 void *handle = mds_extN_start(inode, MDS_FSOP_UNLINK);
258
259                 if (IS_ERR(handle)) {
260                         CERROR("unable to start transaction");
261                         EXIT;
262                         return;
263                 }
264                 if (mds_extN_set_objid(inode, handle, 0))
265                         CERROR("error clearing objid on %ld\n", inode->i_ino);
266
267                 if (mds_extN_fs_ops.cl_delete_inode)
268                         mds_extN_fs_ops.cl_delete_inode(inode);
269
270                 if (mds_extN_commit(inode, handle))
271                         CERROR("error closing handle on %ld\n", inode->i_ino);
272         } else
273                 mds_extN_fs_ops.cl_delete_inode(inode);
274 }
275
276 static void mds_extN_callback_status(struct journal_callback *jcb, int error)
277 {
278         struct mds_cb_data *mcb = (struct mds_cb_data *)jcb;
279
280         CDEBUG(D_EXT2, "got callback for last_rcvd %Ld: rc = %d\n",
281                mcb->cb_last_rcvd, error);
282         if (!error && mcb->cb_last_rcvd > mcb->cb_mds->mds_last_committed)
283                 mcb->cb_mds->mds_last_committed = mcb->cb_last_rcvd;
284
285         kmem_cache_free(jcb_cache, jcb);
286         --jcb_cache_count;
287 }
288
289 #ifdef HAVE_JOURNAL_CALLBACK
290 static void mds_extN_callback_func(void *cb_data)
291 {
292         mds_extN_callback_status(cb_data, 0);
293 }
294 #endif
295
296 static int mds_extN_set_last_rcvd(struct mds_obd *mds, void *handle)
297 {
298         struct mds_cb_data *mcb;
299
300         mcb = kmem_cache_alloc(jcb_cache, GFP_NOFS);
301         if (!mcb)
302                 RETURN(-ENOMEM);
303
304         ++jcb_cache_count;
305         mcb->cb_mds = mds;
306         mcb->cb_last_rcvd = mds->mds_last_rcvd;
307
308 #ifdef HAVE_JOURNAL_CALLBACK_STATUS
309         CDEBUG(D_EXT2, "set callback for last_rcvd: %Ld\n",
310                (unsigned long long)mcb->cb_last_rcvd);
311         journal_callback_set(handle, mds_extN_callback_status,
312                              (struct journal_callback *)mcb);
313 #elif HAVE_JOURNAL_CALLBACK /* XXX original patch version - remove soon */
314 #warning "using old journal callback kernel patch, please update"
315         CDEBUG(D_EXT2, "set callback for last_rcvd: %Ld\n",
316                (unsigned long long)mcb->cb_last_rcvd);
317         journal_callback_set(handle, mds_extN_callback_func, mcb);
318 #else
319 #warning "no journal callback kernel patch, faking it..."
320         {
321         static long next = 0;
322
323         if (time_after(jiffies, next)) {
324                 CERROR("no journal callback kernel patch, faking it...\n");
325                 next = jiffies + 300 * HZ;
326         }
327         }
328         mds_extN_callback_status((struct journal_callback *)mcb, 0);
329 #endif
330
331         return 0;
332 }
333
334 static int mds_extN_journal_data(struct file *filp)
335 {
336         struct inode *inode = filp->f_dentry->d_inode;
337
338         EXTN_I(inode)->i_flags |= EXTN_JOURNAL_DATA_FL;
339
340         return 0;
341 }
342
343 struct mds_fs_operations mds_extN_fs_ops = {
344         fs_init:        mds_extN_init,
345         fs_exit:        mds_extN_exit,
346         fs_start:       mds_extN_start,
347         fs_commit:      mds_extN_commit,
348         fs_setattr:     mds_extN_setattr,
349         fs_set_objid:   mds_extN_set_objid,
350         fs_get_objid:   mds_extN_get_objid,
351         fs_readpage:    mds_extN_readpage,
352         fs_delete_inode:mds_extN_delete_inode,
353         cl_delete_inode:clear_inode,
354         fs_journal_data:mds_extN_journal_data,
355         fs_set_last_rcvd:mds_extN_set_last_rcvd,
356 };