Merge branches 'hammer2' and 'master' of ssh://crater.dragonflybsd.org/repository...
[dragonfly.git] / sys / vfs / hammer2 / hammer2_vfsops.c
1 /*-
2  * Copyright (c) 2011, 2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/nlookup.h>
39 #include <sys/vnode.h>
40 #include <sys/mount.h>
41 #include <sys/fcntl.h>
42 #include <sys/buf.h>
43 #include <sys/uuid.h>
44 #include <sys/vfsops.h>
45
46 #include "hammer2.h"
47 #include "hammer2_disk.h"
48 #include "hammer2_mount.h"
49
50 static int      hammer2_init(struct vfsconf *conf);
51 static int      hammer2_mount(struct mount *mp, char *path, caddr_t data,
52                               struct ucred *cred);
53 static int      hammer2_remount(struct mount *, char *, struct vnode *,
54                                 struct ucred *);
55 static int      hammer2_unmount(struct mount *mp, int mntflags);
56 static int      hammer2_root(struct mount *mp, struct vnode **vpp);
57 static int      hammer2_statfs(struct mount *mp, struct statfs *sbp,
58                                struct ucred *cred);
59 static int      hammer2_statvfs(struct mount *mp, struct statvfs *sbp,
60                                 struct ucred *cred);
61 static int      hammer2_sync(struct mount *mp, int waitfor);
62 static int      hammer2_vget(struct mount *mp, struct vnode *dvp,
63                              ino_t ino, struct vnode **vpp);
64 static int      hammer2_fhtovp(struct mount *mp, struct vnode *rootvp,
65                                struct fid *fhp, struct vnode **vpp);
66 static int      hammer2_vptofh(struct vnode *vp, struct fid *fhp);
67 static int      hammer2_checkexp(struct mount *mp, struct sockaddr *nam,
68                                  int *exflagsp, struct ucred **credanonp);
69
70 /*
71  * HAMMER2 vfs operations.
72  */
73 static struct vfsops hammer2_vfsops = {
74         .vfs_init       = hammer2_init,
75         .vfs_sync       = hammer2_sync,
76         .vfs_mount      = hammer2_mount,
77         .vfs_unmount    = hammer2_unmount,
78         .vfs_root       = hammer2_root,
79         .vfs_statfs     = hammer2_statfs,
80         .vfs_statvfs    = hammer2_statvfs,
81         .vfs_vget       = hammer2_vget,
82         .vfs_vptofh     = hammer2_vptofh,
83         .vfs_fhtovp     = hammer2_fhtovp,
84         .vfs_checkexp   = hammer2_checkexp
85 };
86
87 MALLOC_DEFINE(M_HAMMER2, "HAMMER2-mount", "");
88
89 VFS_SET(hammer2_vfsops, hammer2, 0);
90 MODULE_VERSION(hammer2, 1);
91
92 static int
93 hammer2_init(struct vfsconf *conf)
94 {
95         int error;
96
97         error = 0;
98
99         if (HAMMER2_BLOCKREF_BYTES != sizeof(struct hammer2_blockref))
100                 error = EINVAL;
101         if (HAMMER2_INODE_BYTES != sizeof(struct hammer2_inode_data))
102                 error = EINVAL;
103         if (HAMMER2_ALLOCREF_BYTES != sizeof(struct hammer2_allocref))
104                 error = EINVAL;
105         if (HAMMER2_VOLUME_BYTES != sizeof(struct hammer2_volume_data))
106                 error = EINVAL;
107
108         if (error)
109                 kprintf("HAMMER2 structure size mismatch; cannot continue.\n");
110
111         return (error);
112 }
113
114 /*
115  * Mount or remount HAMMER2 fileystem from physical media
116  *
117  *      mountroot
118  *              mp              mount point structure
119  *              path            NULL
120  *              data            <unused>
121  *              cred            <unused>
122  *
123  *      mount
124  *              mp              mount point structure
125  *              path            path to mount point
126  *              data            pointer to argument structure in user space
127  *                      volume  volume path (device@LABEL form)
128  *                      hflags  user mount flags
129  *              cred            user credentials
130  *
131  * RETURNS:     0       Success
132  *              !0      error number
133  */
134 static int
135 hammer2_mount(struct mount *mp, char *path, caddr_t data,
136               struct ucred *cred)
137 {
138         struct hammer2_mount_info info;
139         struct hammer2_mount *hmp;
140         struct vnode *devvp;
141         struct nlookupdata nd;
142         char devstr[MNAMELEN];
143         size_t size;
144         size_t done;
145         char *dev, *label;
146         int ronly;
147         int error;
148         int rc;
149
150         hmp = NULL;
151         dev = label = NULL;
152         devvp = NULL;
153
154         kprintf("hammer2_mount\n");
155
156         if (path == NULL) {
157                 /*
158                  * Root mount
159                  */
160
161                 return (EOPNOTSUPP);
162         } else {
163                 /*
164                  * Non-root mount or updating a mount
165                  */
166
167                 error = copyin(data, &info, sizeof(info));
168                 if (error)
169                         return (error);
170
171                 error = copyinstr(info.volume, devstr, MNAMELEN - 1, &done);
172                 if (error)
173                         return (error);
174
175                 /* Extract device and label */
176                 dev = devstr;
177                 label = strchr(devstr, '@');
178                 if (label == NULL ||
179                     ((label + 1) - dev) > done)
180                         return (EINVAL);
181                 *label = '\0';
182                 label++;
183                 if (*label == '\0')
184                         return (EINVAL);
185
186                 if (mp->mnt_flag & MNT_UPDATE) {
187                         /* Update mount */
188                         /* HAMMER2 implements NFS export via mountctl */
189                         hmp = MPTOH2(mp);
190                         devvp = hmp->hm_devvp;
191                         return hammer2_remount(mp, path, devvp, cred);
192                 }
193         }
194
195         /*
196          * New non-root mount
197          */
198         /* Lookup name and verify it refers to a block device */
199         error = nlookup_init(&nd, dev, UIO_SYSSPACE, NLC_FOLLOW);
200         if (error)
201                 return (error);
202         error = nlookup(&nd);
203         if (error)
204                 return (error);
205         error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
206         if (error)
207                 return (error);
208         nlookup_done(&nd);
209
210         if (!vn_isdisk(devvp, &error)) {
211                 vrele(devvp);
212                 return (error);
213         }
214
215         /*
216          * Common path for new root/non-root mounts;
217          * devvp is a ref-ed by not locked vnode referring to the fs device
218          */
219
220         error = vfs_mountedon(devvp);
221         if (error) {
222                 vrele(devvp);
223                 return (error);
224         }
225
226         if (vcount(devvp) > 0) {
227                 vrele(devvp);
228                 return (EBUSY);
229         }
230
231         /*
232          * Open the fs device
233          */
234         ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
235         vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
236         error = vinvalbuf(devvp, V_SAVE, 0, 0);
237         if (error) {
238                 vn_unlock(devvp);
239                 vrele(devvp);
240                 return (error);
241         }
242         /* This is correct; however due to an NFS quirk of my setup, FREAD
243          * is required... */
244         /*
245         error = VOP_OPEN(devvp, ronly ? FREAD : FREAD | FWRITE, FSCRED, NULL);
246          */
247
248         error = VOP_OPEN(devvp, FREAD, FSCRED, NULL);
249         vn_unlock(devvp);
250         if (error) {
251                 vrele(devvp);
252                 return (error);
253         }
254
255 #ifdef notyet
256         /* VOP_IOCTL(EXTENDED_DISK_INFO, devvp); */
257         /* if vn device, never use bdwrite(); */
258         /* check if device supports BUF_CMD_READALL; */
259         /* check if device supports BUF_CMD_WRITEALL; */
260 #endif
261
262         hmp = kmalloc(sizeof(*hmp), M_HAMMER2, M_WAITOK | M_ZERO);
263         mp->mnt_data = (qaddr_t) hmp;
264         hmp->hm_mp = mp;
265         hmp->hm_ronly = ronly;
266         hmp->hm_devvp = devvp;
267         lockinit(&hmp->hm_lk, "h2mp", 0, 0);
268         kmalloc_create(&hmp->hm_inodes, "HAMMER2-inodes");
269         kmalloc_create(&hmp->hm_ipstacks, "HAMMER2-ipstacks");
270         
271         mp->mnt_flag = MNT_LOCAL;
272
273         /*
274          * Filesystem subroutines are self-synchronized
275          */
276         mp->mnt_kern_flag |= MNTK_ALL_MPSAFE;
277
278         /* Setup root inode */
279         hmp->hm_iroot = alloci(hmp);
280         hmp->hm_iroot->type = HAMMER2_INODE_TYPE_DIR | HAMMER2_INODE_TYPE_ROOT;
281         hmp->hm_iroot->inum = 1;
282
283         /* currently rely on tmpfs routines */
284         vfs_getnewfsid(mp);
285         vfs_add_vnodeops(mp, &hammer2_vnode_vops, &mp->mnt_vn_norm_ops);
286         vfs_add_vnodeops(mp, &hammer2_spec_vops, &mp->mnt_vn_spec_ops);
287         vfs_add_vnodeops(mp, &hammer2_fifo_vops, &mp->mnt_vn_fifo_ops);
288
289         copystr(info.volume, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
290         bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
291         bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
292         copyinstr(path, mp->mnt_stat.f_mntonname,
293                   sizeof(mp->mnt_stat.f_mntonname) - 1,
294                   &size);
295
296         hammer2_statfs(mp, &mp->mnt_stat, cred);
297
298         hammer2_inode_unlock_ex(hmp->hm_iroot);
299
300         return 0;
301 }
302
303 static int
304 hammer2_remount(struct mount *mp, char *path, struct vnode *devvp,
305                 struct ucred *cred)
306 {
307         return (0);
308 }
309
310 static int
311 hammer2_unmount(struct mount *mp, int mntflags)
312 {
313         struct hammer2_mount *hmp;
314         int flags;
315         int error;
316
317         kprintf("hammer2_unmount\n");
318
319         hmp = MPTOH2(mp);
320         flags = 0;
321
322         if (mntflags & MNT_FORCE)
323                 flags |= FORCECLOSE;
324
325         hammer2_mount_exlock(hmp);
326
327         error = vflush(mp, 0, flags);
328
329         /*
330          * Work to do:
331          *      1) Wait on the flusher having no work; heat up if needed
332          *      2) Scan inode RB tree till all the inodes are free
333          *      3) Destroy the kmalloc inode zone
334          *      4) Free the mount point
335          */
336
337         kmalloc_destroy(&hmp->hm_inodes);
338         kmalloc_destroy(&hmp->hm_ipstacks);
339
340         hammer2_mount_unlock(hmp);
341
342         // Tmpfs does this
343         //kfree(hmp, M_HAMMER2);
344
345
346         return (error);
347 }
348
349 static int
350 hammer2_vget(struct mount *mp, struct vnode *dvp,
351              ino_t ino, struct vnode **vpp)
352 {
353         kprintf("hammer2_vget\n");
354         return (EOPNOTSUPP);
355 }
356
357 static int
358 hammer2_root(struct mount *mp, struct vnode **vpp)
359 {
360         struct hammer2_mount *hmp;
361         int error;
362         struct vnode *vp;
363
364         kprintf("hammer2_root\n");
365
366         hmp = MPTOH2(mp);
367         hammer2_mount_lock_ex(hmp);
368         if (hmp->hm_iroot == NULL) {
369                 *vpp = NULL;
370                 error = EINVAL;
371         } else {
372                 vp = igetv(hmp->hm_iroot, &error);
373                 *vpp = vp;
374                 if (vp == NULL)
375                         kprintf("vnodefail\n");
376         }
377         hammer2_mount_unlock(hmp);
378
379         return (error);
380 }
381
382 static int
383 hammer2_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
384 {
385         struct hammer2_mount *hmp;
386
387         kprintf("hammer2_statfs\n");
388
389         hmp = MPTOH2(mp);
390
391         sbp->f_iosize = PAGE_SIZE;
392         sbp->f_bsize = PAGE_SIZE;
393
394         sbp->f_blocks = 10;
395         sbp->f_bavail = 10;
396         sbp->f_bfree = 10;
397
398         sbp->f_files = 10;
399         sbp->f_ffree = 10;
400         sbp->f_owner = 0;
401
402         return (0);
403 }
404
405 static int
406 hammer2_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
407 {
408         kprintf("hammer2_statvfs\n");
409         return (EOPNOTSUPP);
410 }
411
412 /*
413  * Sync the entire filesystem; this is called from the filesystem syncer
414  * process periodically and whenever a user calls sync(1) on the hammer
415  * mountpoint.
416  *
417  * Currently is actually called from the syncer! \o/
418  *
419  * This task will have to snapshot the state of the dirty inode chain.
420  * From that, it will have to make sure all of the inodes on the dirty
421  * chain have IO initiated. We make sure that io is initiated for the root
422  * block.
423  *
424  * If waitfor is set, we wait for media to acknowledge the new rootblock.
425  *
426  * THINKS: side A vs side B, to have sync not stall all I/O?
427  */
428 static int
429 hammer2_sync(struct mount *mp, int waitfor)
430 {
431         struct hammer2_mount *hmp;
432         struct hammer2_inode *ip;
433
434         kprintf("hammer2_sync \n");
435
436 //      hmp = MPTOH2(mp);
437
438         return (0);
439 }
440
441 static int
442 hammer2_vptofh(struct vnode *vp, struct fid *fhp)
443 {
444         return (0);
445 }
446
447 static int
448 hammer2_fhtovp(struct mount *mp, struct vnode *rootvp,
449                struct fid *fhp, struct vnode **vpp)
450 {
451         return (0);
452 }
453
454 static int
455 hammer2_checkexp(struct mount *mp, struct sockaddr *nam,
456                  int *exflagsp, struct ucred **credanonp)
457 {
458         return (0);
459 }