HAMMER 38D/Many: Undo/Synchronization and crash recovery
[dragonfly.git] / sys / vfs / hammer / hammer_vfsops.c
1 /*
2  * Copyright (c) 2007-2008 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  * $DragonFly: src/sys/vfs/hammer/hammer_vfsops.c,v 1.28 2008/04/26 02:54:00 dillon Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/vnode.h>
41 #include <sys/mount.h>
42 #include <sys/malloc.h>
43 #include <sys/nlookup.h>
44 #include <sys/fcntl.h>
45 #include <sys/sysctl.h>
46 #include <sys/buf.h>
47 #include <sys/buf2.h>
48 #include "hammer.h"
49
50 int hammer_debug_general;
51 int hammer_debug_locks;
52 int hammer_debug_btree;
53 int hammer_debug_tid;
54 int hammer_debug_recover;       /* -1 will disable, +1 will force */
55 int hammer_debug_recover_faults;
56 int hammer_count_inodes;
57 int hammer_count_records;
58 int hammer_count_record_datas;
59 int hammer_count_volumes;
60 int hammer_count_buffers;
61 int hammer_count_nodes;
62 int64_t hammer_contention_count;
63 int64_t hammer_zone_limit;
64
65 SYSCTL_NODE(_vfs, OID_AUTO, hammer, CTLFLAG_RW, 0, "HAMMER filesystem");
66 SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_general, CTLFLAG_RW,
67            &hammer_debug_general, 0, "");
68 SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_locks, CTLFLAG_RW,
69            &hammer_debug_locks, 0, "");
70 SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_btree, CTLFLAG_RW,
71            &hammer_debug_btree, 0, "");
72 SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_tid, CTLFLAG_RW,
73            &hammer_debug_tid, 0, "");
74 SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_recover, CTLFLAG_RW,
75            &hammer_debug_recover, 0, "");
76 SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_recover_faults, CTLFLAG_RW,
77            &hammer_debug_recover_faults, 0, "");
78 SYSCTL_INT(_vfs_hammer, OID_AUTO, count_inodes, CTLFLAG_RD,
79            &hammer_count_inodes, 0, "");
80 SYSCTL_INT(_vfs_hammer, OID_AUTO, count_records, CTLFLAG_RD,
81            &hammer_count_records, 0, "");
82 SYSCTL_INT(_vfs_hammer, OID_AUTO, count_record_datas, CTLFLAG_RD,
83            &hammer_count_record_datas, 0, "");
84 SYSCTL_INT(_vfs_hammer, OID_AUTO, count_volumes, CTLFLAG_RD,
85            &hammer_count_volumes, 0, "");
86 SYSCTL_INT(_vfs_hammer, OID_AUTO, count_buffers, CTLFLAG_RD,
87            &hammer_count_buffers, 0, "");
88 SYSCTL_INT(_vfs_hammer, OID_AUTO, count_nodes, CTLFLAG_RD,
89            &hammer_count_nodes, 0, "");
90 SYSCTL_QUAD(_vfs_hammer, OID_AUTO, zone_limit, CTLFLAG_RW,
91            &hammer_zone_limit, 0, "");
92 SYSCTL_QUAD(_vfs_hammer, OID_AUTO, contention_count, CTLFLAG_RW,
93            &hammer_contention_count, 0, "");
94
95 /*
96  * VFS ABI
97  */
98 static void     hammer_free_hmp(struct mount *mp);
99
100 static int      hammer_vfs_mount(struct mount *mp, char *path, caddr_t data,
101                                 struct ucred *cred);
102 static int      hammer_vfs_unmount(struct mount *mp, int mntflags);
103 static int      hammer_vfs_root(struct mount *mp, struct vnode **vpp);
104 static int      hammer_vfs_statfs(struct mount *mp, struct statfs *sbp,
105                                 struct ucred *cred);
106 static int      hammer_vfs_sync(struct mount *mp, int waitfor);
107 static int      hammer_vfs_vget(struct mount *mp, ino_t ino,
108                                 struct vnode **vpp);
109 static int      hammer_vfs_init(struct vfsconf *conf);
110 static int      hammer_vfs_fhtovp(struct mount *mp, struct fid *fhp,
111                                 struct vnode **vpp);
112 static int      hammer_vfs_vptofh(struct vnode *vp, struct fid *fhp);
113 static int      hammer_vfs_checkexp(struct mount *mp, struct sockaddr *nam,
114                                 int *exflagsp, struct ucred **credanonp);
115
116
117 static struct vfsops hammer_vfsops = {
118         .vfs_mount      = hammer_vfs_mount,
119         .vfs_unmount    = hammer_vfs_unmount,
120         .vfs_root       = hammer_vfs_root,
121         .vfs_statfs     = hammer_vfs_statfs,
122         .vfs_sync       = hammer_vfs_sync,
123         .vfs_vget       = hammer_vfs_vget,
124         .vfs_init       = hammer_vfs_init,
125         .vfs_vptofh     = hammer_vfs_vptofh,
126         .vfs_fhtovp     = hammer_vfs_fhtovp,
127         .vfs_checkexp   = hammer_vfs_checkexp
128 };
129
130 MALLOC_DEFINE(M_HAMMER, "hammer-mount", "hammer mount");
131
132 VFS_SET(hammer_vfsops, hammer, 0);
133 MODULE_VERSION(hammer, 1);
134
135 static int
136 hammer_vfs_init(struct vfsconf *conf)
137 {
138         /*hammer_init_alist_config();*/
139         return(0);
140 }
141
142 static int
143 hammer_vfs_mount(struct mount *mp, char *mntpt, caddr_t data,
144                  struct ucred *cred)
145 {
146         struct hammer_mount_info info;
147         hammer_mount_t hmp;
148         hammer_volume_t rootvol;
149         struct vnode *rootvp;
150         const char *upath;      /* volume name in userspace */
151         char *path;             /* volume name in system space */
152         int error;
153         int i;
154
155         if ((error = copyin(data, &info, sizeof(info))) != 0)
156                 return (error);
157         if (info.nvolumes <= 0 || info.nvolumes >= 32768)
158                 return (EINVAL);
159
160         /*
161          * Interal mount data structure
162          */
163         if (mp->mnt_flag & MNT_UPDATE) {
164                 hmp = (void *)mp->mnt_data;
165                 KKASSERT(hmp != NULL);
166         } else {
167                 hmp = kmalloc(sizeof(*hmp), M_HAMMER, M_WAITOK | M_ZERO);
168                 mp->mnt_data = (qaddr_t)hmp;
169                 hmp->mp = mp;
170                 hmp->zbuf = kmalloc(HAMMER_BUFSIZE, M_HAMMER, M_WAITOK|M_ZERO);
171                 hmp->namekey_iterator = mycpu->gd_time_seconds;
172                 /*TAILQ_INIT(&hmp->recycle_list);*/
173
174                 hmp->root_btree_beg.obj_id = -0x8000000000000000LL;
175                 hmp->root_btree_beg.key = -0x8000000000000000LL;
176                 hmp->root_btree_beg.create_tid = 1;
177                 hmp->root_btree_beg.delete_tid = 1;
178                 hmp->root_btree_beg.rec_type = 0;
179                 hmp->root_btree_beg.obj_type = 0;
180
181                 hmp->root_btree_end.obj_id = 0x7FFFFFFFFFFFFFFFLL;
182                 hmp->root_btree_end.key = 0x7FFFFFFFFFFFFFFFLL;
183                 hmp->root_btree_end.create_tid = 0xFFFFFFFFFFFFFFFFULL;
184                 hmp->root_btree_end.delete_tid = 0;   /* special case */
185                 hmp->root_btree_end.rec_type = 0xFFFFU;
186                 hmp->root_btree_end.obj_type = 0;
187                 lockinit(&hmp->blockmap_lock, "blkmap", 0, 0);
188
189                 TAILQ_INIT(&hmp->flush_list);
190
191                 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
192                         hmp->zone_limits[i] =
193                                 HAMMER_ZONE_ENCODE(i, HAMMER_ZONE_LIMIT);
194                         /*
195                          * Sysctl override for debugging (force the zone
196                          * the cycle more quickly then every 2^60 bytes).
197                          */
198                         if (hammer_zone_limit) {
199                                 hmp->zone_limits[i] =
200                                     HAMMER_ZONE_ENCODE(i, hammer_zone_limit);
201                         }
202                         hammer_init_holes(hmp, &hmp->holes[i]);
203                 }
204         }
205         hmp->hflags = info.hflags;
206         if (info.asof) {
207                 mp->mnt_flag |= MNT_RDONLY;
208                 hmp->asof = info.asof;
209         } else {
210                 hmp->asof = HAMMER_MAX_TID;
211         }
212
213         /*
214          * Re-open read-write if originally read-only, or vise-versa XXX
215          */
216         if (mp->mnt_flag & MNT_UPDATE) {
217                 if (hmp->ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
218                         kprintf("HAMMER read-write -> read-only XXX\n");
219                         hmp->ronly = 1;
220                 } else if (hmp->ronly && (mp->mnt_flag & MNT_RDONLY) == 0) {
221                         kprintf("HAMMER read-only -> read-write XXX\n");
222                         hmp->ronly = 0;
223                 }
224                 return(0);
225         }
226
227         RB_INIT(&hmp->rb_vols_root);
228         RB_INIT(&hmp->rb_inos_root);
229         RB_INIT(&hmp->rb_nods_root);
230         hmp->ronly = ((mp->mnt_flag & MNT_RDONLY) != 0);
231
232         TAILQ_INIT(&hmp->volu_list);
233         TAILQ_INIT(&hmp->undo_list);
234         TAILQ_INIT(&hmp->data_list);
235         TAILQ_INIT(&hmp->meta_list);
236         TAILQ_INIT(&hmp->lose_list);
237
238         /*
239          * Load volumes
240          */
241         path = objcache_get(namei_oc, M_WAITOK);
242         hmp->nvolumes = info.nvolumes;
243         for (i = 0; i < info.nvolumes; ++i) {
244                 error = copyin(&info.volumes[i], &upath, sizeof(char *));
245                 if (error == 0)
246                         error = copyinstr(upath, path, MAXPATHLEN, NULL);
247                 if (error == 0)
248                         error = hammer_install_volume(hmp, path);
249                 if (error)
250                         break;
251         }
252         objcache_put(namei_oc, path);
253
254         /*
255          * Make sure we found a root volume
256          */
257         if (error == 0 && hmp->rootvol == NULL) {
258                 kprintf("hammer_mount: No root volume found!\n");
259                 error = EINVAL;
260         }
261         if (error) {
262                 hammer_free_hmp(mp);
263                 return (error);
264         }
265
266         /*
267          * No errors, setup enough of the mount point so we can lookup the
268          * root vnode.
269          */
270         mp->mnt_iosize_max = MAXPHYS;
271         mp->mnt_kern_flag |= MNTK_FSMID;
272
273         /* 
274          * note: f_iosize is used by vnode_pager_haspage() when constructing
275          * its VOP_BMAP call.
276          */
277         mp->mnt_stat.f_iosize = HAMMER_BUFSIZE;
278         mp->mnt_stat.f_bsize = HAMMER_BUFSIZE;
279         mp->mnt_maxsymlinklen = 255;
280         mp->mnt_flag |= MNT_LOCAL;
281
282         vfs_add_vnodeops(mp, &hammer_vnode_vops, &mp->mnt_vn_norm_ops);
283         vfs_add_vnodeops(mp, &hammer_spec_vops, &mp->mnt_vn_spec_ops);
284         vfs_add_vnodeops(mp, &hammer_fifo_vops, &mp->mnt_vn_fifo_ops);
285
286         /*
287          * The root volume's ondisk pointer is only valid if we hold a
288          * reference to it.
289          */
290         rootvol = hammer_get_root_volume(hmp, &error);
291         if (error)
292                 goto failed;
293
294         /*
295          * Perform any necessary UNDO operations
296          */
297         error = hammer_recover(hmp, rootvol);
298         if (error) {
299                 kprintf("Failed to recover HAMMER filesystem on mount\n");
300                 goto done;
301         }
302
303         /*
304          * Finish setup now that we have a good root volume
305          */
306         ksnprintf(mp->mnt_stat.f_mntfromname,
307                   sizeof(mp->mnt_stat.f_mntfromname), "%s",
308                   rootvol->ondisk->vol_name);
309         mp->mnt_stat.f_fsid.val[0] =
310                 crc32((char *)&rootvol->ondisk->vol_fsid + 0, 8);
311         mp->mnt_stat.f_fsid.val[1] =
312                 crc32((char *)&rootvol->ondisk->vol_fsid + 8, 8);
313
314         hmp->next_tid = rootvol->ondisk->vol0_next_tid;
315         kprintf("on-disk next_tid %016llx\n", hmp->next_tid);
316
317         hammer_flusher_create(hmp);
318
319         /*
320          * Locate the root directory using the root cluster's B-Tree as a
321          * starting point.  The root directory uses an obj_id of 1.
322          *
323          * FUTURE: Leave the root directory cached referenced but unlocked
324          * in hmp->rootvp (need to flush it on unmount).
325          */
326         error = hammer_vfs_vget(mp, 1, &rootvp);
327         if (error)
328                 goto done;
329         vput(rootvp);
330         /*vn_unlock(hmp->rootvp);*/
331
332 done:
333         hammer_rel_volume(rootvol, 0);
334 failed:
335         /*
336          * Cleanup and return.
337          */
338         if (error)
339                 hammer_free_hmp(mp);
340         return (error);
341 }
342
343 static int
344 hammer_vfs_unmount(struct mount *mp, int mntflags)
345 {
346 #if 0
347         struct hammer_mount *hmp = (void *)mp->mnt_data;
348 #endif
349         int flags;
350         int error;
351
352         /*
353          * Clean out the vnodes
354          */
355         flags = 0;
356         if (mntflags & MNT_FORCE)
357                 flags |= FORCECLOSE;
358         if ((error = vflush(mp, 0, flags)) != 0)
359                 return (error);
360
361         /*
362          * Clean up the internal mount structure and related entities.  This
363          * may issue I/O.
364          */
365         hammer_free_hmp(mp);
366         return(0);
367 }
368
369 /*
370  * Clean up the internal mount structure and disassociate it from the mount.
371  * This may issue I/O.
372  */
373 static void
374 hammer_free_hmp(struct mount *mp)
375 {
376         struct hammer_mount *hmp = (void *)mp->mnt_data;
377         int i;
378
379 #if 0
380         /*
381          * Clean up the root vnode
382          */
383         if (hmp->rootvp) {
384                 vrele(hmp->rootvp);
385                 hmp->rootvp = NULL;
386         }
387 #endif
388         hammer_flusher_sync(hmp);
389         hammer_flusher_sync(hmp);
390         hammer_flusher_destroy(hmp);
391
392         KKASSERT(RB_EMPTY(&hmp->rb_inos_root));
393
394 #if 0
395         /*
396          * Unload & flush inodes
397          *
398          * XXX illegal to call this from here, it can only be done from
399          * the flusher.
400          */
401         RB_SCAN(hammer_ino_rb_tree, &hmp->rb_inos_root, NULL,
402                 hammer_unload_inode, (void *)MNT_WAIT);
403
404         /*
405          * Unload & flush volumes
406          */
407 #endif
408         /*
409          * Unload the volumes
410          */
411         RB_SCAN(hammer_vol_rb_tree, &hmp->rb_vols_root, NULL,
412                 hammer_unload_volume, NULL);
413
414         mp->mnt_data = NULL;
415         mp->mnt_flag &= ~MNT_LOCAL;
416         hmp->mp = NULL;
417         kfree(hmp->zbuf, M_HAMMER);
418         lockuninit(&hmp->blockmap_lock);
419
420         for (i = 0; i < HAMMER_MAX_ZONES; ++i)
421                 hammer_free_holes(hmp, &hmp->holes[i]);
422
423         kfree(hmp, M_HAMMER);
424 }
425
426 /*
427  * Obtain a vnode for the specified inode number.  An exclusively locked
428  * vnode is returned.
429  */
430 int
431 hammer_vfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
432 {
433         struct hammer_transaction trans;
434         struct hammer_mount *hmp = (void *)mp->mnt_data;
435         struct hammer_inode *ip;
436         int error;
437
438         hammer_simple_transaction(&trans, hmp);
439
440         /*
441          * Lookup the requested HAMMER inode.  The structure must be
442          * left unlocked while we manipulate the related vnode to avoid
443          * a deadlock.
444          */
445         ip = hammer_get_inode(&trans, NULL, ino, hmp->asof, 0, &error);
446         if (ip == NULL) {
447                 *vpp = NULL;
448                 return(error);
449         }
450         error = hammer_get_vnode(ip, LK_EXCLUSIVE, vpp);
451         hammer_rel_inode(ip, 0);
452         hammer_done_transaction(&trans);
453         return (error);
454 }
455
456 /*
457  * Return the root vnode for the filesystem.
458  *
459  * HAMMER stores the root vnode in the hammer_mount structure so
460  * getting it is easy.
461  */
462 static int
463 hammer_vfs_root(struct mount *mp, struct vnode **vpp)
464 {
465 #if 0
466         struct hammer_mount *hmp = (void *)mp->mnt_data;
467 #endif
468         int error;
469
470         error = hammer_vfs_vget(mp, 1, vpp);
471         return (error);
472 }
473
474 static int
475 hammer_vfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
476 {
477         struct hammer_mount *hmp = (void *)mp->mnt_data;
478         hammer_volume_t volume;
479         hammer_volume_ondisk_t ondisk;
480         int error;
481         int64_t bfree;
482
483         volume = hammer_get_root_volume(hmp, &error);
484         if (error)
485                 return(error);
486         ondisk = volume->ondisk;
487
488         /*
489          * Basic stats
490          */
491         mp->mnt_stat.f_files = ondisk->vol0_stat_inodes;
492         bfree = ondisk->vol0_stat_freebigblocks * HAMMER_LARGEBLOCK_SIZE;
493         hammer_rel_volume(volume, 0);
494
495         mp->mnt_stat.f_bfree = bfree / HAMMER_BUFSIZE;
496         mp->mnt_stat.f_bavail = mp->mnt_stat.f_bfree;
497         if (mp->mnt_stat.f_files < 0)
498                 mp->mnt_stat.f_files = 0;
499
500         *sbp = mp->mnt_stat;
501         return(0);
502 }
503
504 static int
505 hammer_vfs_sync(struct mount *mp, int waitfor)
506 {
507         struct hammer_mount *hmp = (void *)mp->mnt_data;
508         return(hammer_sync_hmp(hmp, waitfor));
509 }
510
511 /*
512  * Convert a vnode to a file handle.
513  */
514 static int
515 hammer_vfs_vptofh(struct vnode *vp, struct fid *fhp)
516 {
517         hammer_inode_t ip;
518
519         KKASSERT(MAXFIDSZ >= 16);
520         ip = VTOI(vp);
521         fhp->fid_len = offsetof(struct fid, fid_data[16]);
522         fhp->fid_reserved = 0;
523         bcopy(&ip->obj_id, fhp->fid_data + 0, sizeof(ip->obj_id));
524         bcopy(&ip->obj_asof, fhp->fid_data + 8, sizeof(ip->obj_asof));
525         return(0);
526 }
527
528
529 /*
530  * Convert a file handle back to a vnode.
531  */
532 static int
533 hammer_vfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
534 {
535         struct hammer_transaction trans;
536         struct hammer_inode *ip;
537         struct hammer_inode_info info;
538         int error;
539
540         bcopy(fhp->fid_data + 0, &info.obj_id, sizeof(info.obj_id));
541         bcopy(fhp->fid_data + 8, &info.obj_asof, sizeof(info.obj_asof));
542
543         hammer_simple_transaction(&trans, (void *)mp->mnt_data);
544
545         /*
546          * Get/allocate the hammer_inode structure.  The structure must be
547          * unlocked while we manipulate the related vnode to avoid a
548          * deadlock.
549          */
550         ip = hammer_get_inode(&trans, NULL, info.obj_id, info.obj_asof,
551                               0, &error);
552         if (ip == NULL) {
553                 *vpp = NULL;
554                 return(error);
555         }
556         error = hammer_get_vnode(ip, LK_EXCLUSIVE, vpp);
557         hammer_rel_inode(ip, 0);
558         hammer_done_transaction(&trans);
559         return (error);
560 }
561
562 static int
563 hammer_vfs_checkexp(struct mount *mp, struct sockaddr *nam,
564                     int *exflagsp, struct ucred **credanonp)
565 {
566         hammer_mount_t hmp = (void *)mp->mnt_data;
567         struct netcred *np;
568         int error;
569
570         np = vfs_export_lookup(mp, &hmp->export, nam);
571         if (np) {
572                 *exflagsp = np->netc_exflags;
573                 *credanonp = &np->netc_anon;
574                 error = 0;
575         } else {
576                 error = EACCES;
577         }
578         return (error);
579
580 }
581
582 int
583 hammer_vfs_export(struct mount *mp, int op, const struct export_args *export)
584 {
585         hammer_mount_t hmp = (void *)mp->mnt_data;
586         int error;
587
588         switch(op) {
589         case MOUNTCTL_SET_EXPORT:
590                 error = vfs_export(mp, &hmp->export, export);
591                 break;
592         default:
593                 error = EOPNOTSUPP;
594                 break;
595         }
596         return(error);
597 }
598