1235537Sgber/*- 2235537Sgber * Copyright (c) 2010-2012 Semihalf 3235537Sgber * Copyright (c) 2008, 2009 Reinoud Zandijk 4235537Sgber * All rights reserved. 5235537Sgber * 6235537Sgber * Redistribution and use in source and binary forms, with or without 7235537Sgber * modification, are permitted provided that the following conditions 8235537Sgber * are met: 9235537Sgber * 1. Redistributions of source code must retain the above copyright 10235537Sgber * notice, this list of conditions and the following disclaimer. 11235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 12235537Sgber * notice, this list of conditions and the following disclaimer in the 13235537Sgber * documentation and/or other materials provided with the distribution. 14235537Sgber * 15235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16235537Sgber * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17235537Sgber * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18235537Sgber * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19235537Sgber * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20235537Sgber * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21235537Sgber * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22235537Sgber * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23235537Sgber * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24235537Sgber * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25235537Sgber * 26235537Sgber * From: NetBSD: nilfs_vfsops.c,v 1.1 2009/07/18 16:31:42 reinoud Exp 27235537Sgber */ 28235537Sgber 29235537Sgber#include <sys/cdefs.h> 30235537Sgber__FBSDID("$FreeBSD$"); 31235537Sgber 32235537Sgber#include <sys/param.h> 33235537Sgber#include <sys/systm.h> 34235537Sgber#include <sys/fcntl.h> 35235537Sgber#include <sys/kernel.h> 36235537Sgber#include <sys/lock.h> 37235537Sgber#include <sys/malloc.h> 38235537Sgber#include <sys/mount.h> 39235537Sgber#include <sys/namei.h> 40235537Sgber#include <sys/proc.h> 41235537Sgber#include <sys/priv.h> 42235537Sgber#include <sys/vnode.h> 43235537Sgber#include <sys/buf.h> 44235537Sgber#include <sys/sysctl.h> 45235537Sgber#include <sys/libkern.h> 46235537Sgber 47235537Sgber#include <geom/geom.h> 48235537Sgber#include <geom/geom_vfs.h> 49235537Sgber 50235537Sgber#include <machine/_inttypes.h> 51235537Sgber 52235537Sgber#include <fs/nandfs/nandfs_mount.h> 53235537Sgber#include <fs/nandfs/nandfs.h> 54235537Sgber#include <fs/nandfs/nandfs_subr.h> 55235537Sgber 56235537Sgberstatic MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount structure"); 57235537Sgber 58235537Sgber#define NANDFS_SET_SYSTEMFILE(vp) { \ 59235537Sgber (vp)->v_vflag |= VV_SYSTEM; \ 60235537Sgber vref(vp); \ 61235537Sgber vput(vp); } 62235537Sgber 63235537Sgber#define NANDFS_UNSET_SYSTEMFILE(vp) { \ 64235537Sgber VOP_LOCK(vp, LK_EXCLUSIVE); \ 65235537Sgber MPASS(vp->v_bufobj.bo_dirty.bv_cnt == 0); \ 66235537Sgber (vp)->v_vflag &= ~VV_SYSTEM; \ 67235537Sgber vgone(vp); \ 68235537Sgber vput(vp); } 69235537Sgber 70235537Sgber/* Globals */ 71235537Sgberstruct _nandfs_devices nandfs_devices; 72235537Sgber 73235537Sgber/* Parameters */ 74235537Sgberint nandfs_verbose = 0; 75235537Sgber 76235537Sgberstatic void 77235537Sgbernandfs_tunable_init(void *arg) 78235537Sgber{ 79235537Sgber 80235537Sgber TUNABLE_INT_FETCH("vfs.nandfs.verbose", &nandfs_verbose); 81235537Sgber} 82235537SgberSYSINIT(nandfs_tunables, SI_SUB_VFS, SI_ORDER_ANY, nandfs_tunable_init, NULL); 83235537Sgber 84235537Sgberstatic SYSCTL_NODE(_vfs, OID_AUTO, nandfs, CTLFLAG_RD, 0, "NAND filesystem"); 85235537Sgberstatic SYSCTL_NODE(_vfs_nandfs, OID_AUTO, mount, CTLFLAG_RD, 0, 86235537Sgber "NANDFS mountpoints"); 87235537SgberSYSCTL_INT(_vfs_nandfs, OID_AUTO, verbose, CTLFLAG_RW, &nandfs_verbose, 0, ""); 88235537Sgber 89235537Sgber#define NANDFS_CONSTR_INTERVAL 5 90235537Sgberint nandfs_sync_interval = NANDFS_CONSTR_INTERVAL; /* sync every 5 seconds */ 91235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, sync_interval, CTLFLAG_RW, 92235537Sgber &nandfs_sync_interval, 0, ""); 93235537Sgber 94235537Sgber#define NANDFS_MAX_DIRTY_SEGS 5 95235537Sgberint nandfs_max_dirty_segs = NANDFS_MAX_DIRTY_SEGS; /* sync when 5 dirty seg */ 96235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, max_dirty_segs, CTLFLAG_RW, 97235537Sgber &nandfs_max_dirty_segs, 0, ""); 98235537Sgber 99235537Sgber#define NANDFS_CPS_BETWEEN_SBLOCKS 5 100235537Sgberint nandfs_cps_between_sblocks = NANDFS_CPS_BETWEEN_SBLOCKS; /* write superblock every 5 checkpoints */ 101235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cps_between_sblocks, CTLFLAG_RW, 102235537Sgber &nandfs_cps_between_sblocks, 0, ""); 103235537Sgber 104235537Sgber#define NANDFS_CLEANER_ENABLE 1 105235537Sgberint nandfs_cleaner_enable = NANDFS_CLEANER_ENABLE; 106235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_enable, CTLFLAG_RW, 107235537Sgber &nandfs_cleaner_enable, 0, ""); 108235537Sgber 109235537Sgber#define NANDFS_CLEANER_INTERVAL 5 110235537Sgberint nandfs_cleaner_interval = NANDFS_CLEANER_INTERVAL; 111235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_interval, CTLFLAG_RW, 112235537Sgber &nandfs_cleaner_interval, 0, ""); 113235537Sgber 114235537Sgber#define NANDFS_CLEANER_SEGMENTS 5 115235537Sgberint nandfs_cleaner_segments = NANDFS_CLEANER_SEGMENTS; 116235537SgberSYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_segments, CTLFLAG_RW, 117235537Sgber &nandfs_cleaner_segments, 0, ""); 118235537Sgber 119235537Sgberstatic int nandfs_mountfs(struct vnode *devvp, struct mount *mp); 120235537Sgberstatic vfs_mount_t nandfs_mount; 121235537Sgberstatic vfs_root_t nandfs_root; 122235537Sgberstatic vfs_statfs_t nandfs_statfs; 123235537Sgberstatic vfs_unmount_t nandfs_unmount; 124235537Sgberstatic vfs_vget_t nandfs_vget; 125235537Sgberstatic vfs_sync_t nandfs_sync; 126235537Sgberstatic const char *nandfs_opts[] = { 127235537Sgber "snap", "from", "noatime", NULL 128235537Sgber}; 129235537Sgber 130235537Sgber/* System nodes */ 131235537Sgberstatic int 132235537Sgbernandfs_create_system_nodes(struct nandfs_device *nandfsdev) 133235537Sgber{ 134235537Sgber int error; 135235537Sgber 136235537Sgber error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_DAT_INO, 137235537Sgber &nandfsdev->nd_super_root.sr_dat, &nandfsdev->nd_dat_node); 138235537Sgber if (error) 139235537Sgber goto errorout; 140235537Sgber 141235537Sgber error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_CPFILE_INO, 142235537Sgber &nandfsdev->nd_super_root.sr_cpfile, &nandfsdev->nd_cp_node); 143235537Sgber if (error) 144235537Sgber goto errorout; 145235537Sgber 146235537Sgber error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_SUFILE_INO, 147235537Sgber &nandfsdev->nd_super_root.sr_sufile, &nandfsdev->nd_su_node); 148235537Sgber if (error) 149235537Sgber goto errorout; 150235537Sgber 151235537Sgber error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_GC_INO, 152235537Sgber NULL, &nandfsdev->nd_gc_node); 153235537Sgber if (error) 154235537Sgber goto errorout; 155235537Sgber 156235537Sgber NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node)); 157235537Sgber NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node)); 158235537Sgber NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node)); 159235537Sgber NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node)); 160235537Sgber 161235537Sgber DPRINTF(VOLUMES, ("System vnodes: dat: %p cp: %p su: %p\n", 162235537Sgber NTOV(nandfsdev->nd_dat_node), NTOV(nandfsdev->nd_cp_node), 163235537Sgber NTOV(nandfsdev->nd_su_node))); 164235537Sgber return (0); 165235537Sgber 166235537Sgbererrorout: 167235537Sgber nandfs_dispose_node(&nandfsdev->nd_gc_node); 168235537Sgber nandfs_dispose_node(&nandfsdev->nd_dat_node); 169235537Sgber nandfs_dispose_node(&nandfsdev->nd_cp_node); 170235537Sgber nandfs_dispose_node(&nandfsdev->nd_su_node); 171235537Sgber 172235537Sgber return (error); 173235537Sgber} 174235537Sgber 175235537Sgberstatic void 176235537Sgbernandfs_release_system_nodes(struct nandfs_device *nandfsdev) 177235537Sgber{ 178235537Sgber 179235537Sgber if (!nandfsdev) 180235537Sgber return; 181235537Sgber if (nandfsdev->nd_refcnt > 0) 182235537Sgber return; 183235537Sgber 184235537Sgber if (nandfsdev->nd_gc_node) 185235537Sgber NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node)); 186235537Sgber if (nandfsdev->nd_dat_node) 187235537Sgber NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node)); 188235537Sgber if (nandfsdev->nd_cp_node) 189235537Sgber NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node)); 190235537Sgber if (nandfsdev->nd_su_node) 191235537Sgber NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node)); 192235537Sgber} 193235537Sgber 194235537Sgberstatic int 195235537Sgbernandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata) 196235537Sgber{ 197235537Sgber uint32_t fsdata_crc, comp_crc; 198235537Sgber 199235537Sgber if (fsdata->f_magic != NANDFS_FSDATA_MAGIC) 200235537Sgber return (0); 201235537Sgber 202235537Sgber /* Preserve CRC */ 203235537Sgber fsdata_crc = fsdata->f_sum; 204235537Sgber 205235537Sgber /* Calculate */ 206235537Sgber fsdata->f_sum = (0); 207235537Sgber comp_crc = crc32((uint8_t *)fsdata, fsdata->f_bytes); 208235537Sgber 209235537Sgber /* Restore */ 210235537Sgber fsdata->f_sum = fsdata_crc; 211235537Sgber 212235537Sgber /* Check CRC */ 213235537Sgber return (fsdata_crc == comp_crc); 214235537Sgber} 215235537Sgber 216235537Sgberstatic int 217235537Sgbernandfs_check_superblock_crc(struct nandfs_fsdata *fsdata, 218235537Sgber struct nandfs_super_block *super) 219235537Sgber{ 220235537Sgber uint32_t super_crc, comp_crc; 221235537Sgber 222235537Sgber /* Check super block magic */ 223235537Sgber if (super->s_magic != NANDFS_SUPER_MAGIC) 224235537Sgber return (0); 225235537Sgber 226235537Sgber /* Preserve CRC */ 227235537Sgber super_crc = super->s_sum; 228235537Sgber 229235537Sgber /* Calculate */ 230235537Sgber super->s_sum = (0); 231235537Sgber comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes); 232235537Sgber 233235537Sgber /* Restore */ 234235537Sgber super->s_sum = super_crc; 235235537Sgber 236235537Sgber /* Check CRC */ 237235537Sgber return (super_crc == comp_crc); 238235537Sgber} 239235537Sgber 240235537Sgberstatic void 241235537Sgbernandfs_calc_superblock_crc(struct nandfs_fsdata *fsdata, 242235537Sgber struct nandfs_super_block *super) 243235537Sgber{ 244235537Sgber uint32_t comp_crc; 245235537Sgber 246235537Sgber /* Calculate */ 247235537Sgber super->s_sum = 0; 248235537Sgber comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes); 249235537Sgber 250235537Sgber /* Restore */ 251235537Sgber super->s_sum = comp_crc; 252235537Sgber} 253235537Sgber 254235537Sgberstatic int 255235537Sgbernandfs_is_empty(u_char *area, int size) 256235537Sgber{ 257235537Sgber int i; 258235537Sgber 259235537Sgber for (i = 0; i < size; i++) 260235537Sgber if (area[i] != 0xff) 261235537Sgber return (0); 262235537Sgber 263235537Sgber return (1); 264235537Sgber} 265235537Sgber 266235537Sgberstatic __inline int 267235537Sgbernandfs_sblocks_in_esize(struct nandfs_device *fsdev) 268235537Sgber{ 269235537Sgber 270235537Sgber return ((fsdev->nd_erasesize - NANDFS_SBLOCK_OFFSET_BYTES) / 271235537Sgber sizeof(struct nandfs_super_block)); 272235537Sgber} 273235537Sgber 274235537Sgberstatic __inline int 275235537Sgbernandfs_max_sblocks(struct nandfs_device *fsdev) 276235537Sgber{ 277235537Sgber 278235537Sgber return (NANDFS_NFSAREAS * nandfs_sblocks_in_esize(fsdev)); 279235537Sgber} 280235537Sgber 281235537Sgberstatic __inline int 282235537Sgbernandfs_sblocks_in_block(struct nandfs_device *fsdev) 283235537Sgber{ 284235537Sgber 285235537Sgber return (fsdev->nd_devblocksize / sizeof(struct nandfs_super_block)); 286235537Sgber} 287235537Sgber 288235537Sgberstatic __inline int 289235537Sgbernandfs_sblocks_in_first_block(struct nandfs_device *fsdev) 290235537Sgber{ 291235537Sgber int n; 292235537Sgber 293235537Sgber n = nandfs_sblocks_in_block(fsdev) - 294235537Sgber NANDFS_SBLOCK_OFFSET_BYTES / sizeof(struct nandfs_super_block); 295235537Sgber if (n < 0) 296235537Sgber n = 0; 297235537Sgber 298235537Sgber return (n); 299235537Sgber} 300235537Sgber 301235537Sgberstatic int 302235537Sgbernandfs_write_superblock_at(struct nandfs_device *fsdev, 303235537Sgber struct nandfs_fsarea *fstp) 304235537Sgber{ 305235537Sgber struct nandfs_super_block *super, *supert; 306235537Sgber struct buf *bp; 307235537Sgber int sb_per_sector, sbs_in_fsd, read_block; 308235537Sgber int index, pos, error; 309235537Sgber off_t offset; 310235537Sgber 311235537Sgber DPRINTF(SYNC, ("%s: last_used %d nandfs_sblocks_in_esize %d\n", 312235537Sgber __func__, fstp->last_used, nandfs_sblocks_in_esize(fsdev))); 313235537Sgber if (fstp->last_used == nandfs_sblocks_in_esize(fsdev) - 1) 314235537Sgber index = 0; 315235537Sgber else 316235537Sgber index = fstp->last_used + 1; 317235537Sgber 318235537Sgber super = &fsdev->nd_super; 319235537Sgber supert = NULL; 320235537Sgber 321235537Sgber sb_per_sector = nandfs_sblocks_in_block(fsdev); 322235537Sgber sbs_in_fsd = sizeof(struct nandfs_fsdata) / 323235537Sgber sizeof(struct nandfs_super_block); 324235537Sgber index += sbs_in_fsd; 325235537Sgber offset = fstp->offset; 326235537Sgber 327235537Sgber DPRINTF(SYNC, ("%s: offset %#jx s_last_pseg %#jx s_last_cno %#jx " 328235537Sgber "s_last_seq %#jx wtime %jd index %d\n", __func__, offset, 329235537Sgber super->s_last_pseg, super->s_last_cno, super->s_last_seq, 330235537Sgber super->s_wtime, index)); 331235537Sgber 332235537Sgber read_block = btodb(offset + ((index / sb_per_sector) * sb_per_sector) 333235537Sgber * sizeof(struct nandfs_super_block)); 334235537Sgber 335235537Sgber DPRINTF(SYNC, ("%s: read_block %#x\n", __func__, read_block)); 336235537Sgber 337235537Sgber if (index == sbs_in_fsd) { 338235537Sgber error = nandfs_erase(fsdev, offset, fsdev->nd_erasesize); 339235537Sgber if (error) 340235537Sgber return (error); 341235537Sgber 342235537Sgber error = bread(fsdev->nd_devvp, btodb(offset), 343235537Sgber fsdev->nd_devblocksize, NOCRED, &bp); 344235537Sgber if (error) { 345235537Sgber printf("NANDFS: couldn't read initial data: %d\n", 346235537Sgber error); 347235537Sgber brelse(bp); 348235537Sgber return (error); 349235537Sgber } 350235537Sgber memcpy(bp->b_data, &fsdev->nd_fsdata, sizeof(fsdev->nd_fsdata)); 351235537Sgber /* 352235537Sgber * 0xff-out the rest. This bp could be cached, so potentially 353235537Sgber * b_data contains stale super blocks. 354235537Sgber * 355235537Sgber * We don't mind cached bp since most of the time we just add 356235537Sgber * super blocks to already 0xff-out b_data and don't need to 357235537Sgber * perform actual read. 358235537Sgber */ 359235537Sgber if (fsdev->nd_devblocksize > sizeof(fsdev->nd_fsdata)) 360235537Sgber memset(bp->b_data + sizeof(fsdev->nd_fsdata), 0xff, 361235537Sgber fsdev->nd_devblocksize - sizeof(fsdev->nd_fsdata)); 362235537Sgber error = bwrite(bp); 363235537Sgber if (error) { 364235537Sgber printf("NANDFS: cannot rewrite initial data at %jx\n", 365235537Sgber offset); 366235537Sgber return (error); 367235537Sgber } 368235537Sgber } 369235537Sgber 370235537Sgber error = bread(fsdev->nd_devvp, read_block, fsdev->nd_devblocksize, 371235537Sgber NOCRED, &bp); 372235537Sgber if (error) { 373235537Sgber brelse(bp); 374235537Sgber return (error); 375235537Sgber } 376235537Sgber 377235537Sgber supert = (struct nandfs_super_block *)(bp->b_data); 378235537Sgber pos = index % sb_per_sector; 379235537Sgber 380235537Sgber DPRINTF(SYNC, ("%s: storing at %d\n", __func__, pos)); 381235537Sgber memcpy(&supert[pos], super, sizeof(struct nandfs_super_block)); 382235537Sgber 383235537Sgber /* 384235537Sgber * See comment above in code that performs erase. 385235537Sgber */ 386235537Sgber if (pos == 0) 387235537Sgber memset(&supert[1], 0xff, 388235537Sgber (sb_per_sector - 1) * sizeof(struct nandfs_super_block)); 389235537Sgber 390235537Sgber error = bwrite(bp); 391235537Sgber if (error) { 392235537Sgber printf("NANDFS: cannot update superblock at %jx\n", offset); 393235537Sgber return (error); 394235537Sgber } 395235537Sgber 396235537Sgber DPRINTF(SYNC, ("%s: fstp->last_used %d -> %d\n", __func__, 397235537Sgber fstp->last_used, index - sbs_in_fsd)); 398235537Sgber fstp->last_used = index - sbs_in_fsd; 399235537Sgber 400235537Sgber return (0); 401235537Sgber} 402235537Sgber 403235537Sgberint 404235537Sgbernandfs_write_superblock(struct nandfs_device *fsdev) 405235537Sgber{ 406235537Sgber struct nandfs_super_block *super; 407235537Sgber struct timespec ts; 408235537Sgber int error; 409235537Sgber int i, j; 410235537Sgber 411235537Sgber vfs_timestamp(&ts); 412235537Sgber 413235537Sgber super = &fsdev->nd_super; 414235537Sgber 415235537Sgber super->s_last_pseg = fsdev->nd_last_pseg; 416235537Sgber super->s_last_cno = fsdev->nd_last_cno; 417235537Sgber super->s_last_seq = fsdev->nd_seg_sequence; 418235537Sgber super->s_wtime = ts.tv_sec; 419235537Sgber 420235537Sgber nandfs_calc_superblock_crc(&fsdev->nd_fsdata, super); 421235537Sgber 422235537Sgber error = 0; 423235537Sgber for (i = 0, j = fsdev->nd_last_fsarea; i < NANDFS_NFSAREAS; 424235537Sgber i++, j = (j + 1 % NANDFS_NFSAREAS)) { 425235537Sgber if (fsdev->nd_fsarea[j].flags & NANDFS_FSSTOR_FAILED) { 426235537Sgber DPRINTF(SYNC, ("%s: skipping %d\n", __func__, j)); 427235537Sgber continue; 428235537Sgber } 429235537Sgber error = nandfs_write_superblock_at(fsdev, &fsdev->nd_fsarea[j]); 430235537Sgber if (error) { 431235537Sgber printf("NANDFS: writing superblock at offset %d failed:" 432235537Sgber "%d\n", j * fsdev->nd_erasesize, error); 433235537Sgber fsdev->nd_fsarea[j].flags |= NANDFS_FSSTOR_FAILED; 434235537Sgber } else 435235537Sgber break; 436235537Sgber } 437235537Sgber 438235537Sgber if (i == NANDFS_NFSAREAS) { 439235537Sgber printf("NANDFS: superblock was not written\n"); 440235537Sgber /* 441235537Sgber * TODO: switch to read-only? 442235537Sgber */ 443235537Sgber return (error); 444235537Sgber } else 445235537Sgber fsdev->nd_last_fsarea = (j + 1) % NANDFS_NFSAREAS; 446235537Sgber 447235537Sgber return (0); 448235537Sgber} 449235537Sgber 450235537Sgberstatic int 451235537Sgbernandfs_select_fsdata(struct nandfs_device *fsdev, 452235537Sgber struct nandfs_fsdata *fsdatat, struct nandfs_fsdata **fsdata, int nfsds) 453235537Sgber{ 454235537Sgber int i; 455235537Sgber 456235537Sgber *fsdata = NULL; 457235537Sgber for (i = 0; i < nfsds; i++) { 458235537Sgber DPRINTF(VOLUMES, ("%s: i %d f_magic %x f_crc %x\n", __func__, 459235537Sgber i, fsdatat[i].f_magic, fsdatat[i].f_sum)); 460235537Sgber if (!nandfs_check_fsdata_crc(&fsdatat[i])) 461235537Sgber continue; 462235537Sgber *fsdata = &fsdatat[i]; 463235537Sgber break; 464235537Sgber } 465235537Sgber 466235537Sgber return (*fsdata != NULL ? 0 : EINVAL); 467235537Sgber} 468235537Sgber 469235537Sgberstatic int 470235537Sgbernandfs_select_sb(struct nandfs_device *fsdev, 471235537Sgber struct nandfs_super_block *supert, struct nandfs_super_block **super, 472235537Sgber int nsbs) 473235537Sgber{ 474235537Sgber int i; 475235537Sgber 476235537Sgber *super = NULL; 477235537Sgber for (i = 0; i < nsbs; i++) { 478235537Sgber if (!nandfs_check_superblock_crc(&fsdev->nd_fsdata, &supert[i])) 479235537Sgber continue; 480235537Sgber DPRINTF(SYNC, ("%s: i %d s_last_cno %jx s_magic %x " 481235537Sgber "s_wtime %jd\n", __func__, i, supert[i].s_last_cno, 482235537Sgber supert[i].s_magic, supert[i].s_wtime)); 483235537Sgber if (*super == NULL || supert[i].s_last_cno > 484235537Sgber (*super)->s_last_cno) 485235537Sgber *super = &supert[i]; 486235537Sgber } 487235537Sgber 488235537Sgber return (*super != NULL ? 0 : EINVAL); 489235537Sgber} 490235537Sgber 491235537Sgberstatic int 492235537Sgbernandfs_read_structures_at(struct nandfs_device *fsdev, 493235537Sgber struct nandfs_fsarea *fstp, struct nandfs_fsdata *fsdata, 494235537Sgber struct nandfs_super_block *super) 495235537Sgber{ 496235537Sgber struct nandfs_super_block *tsuper, *tsuperd; 497235537Sgber struct buf *bp; 498235537Sgber int error, read_size; 499235537Sgber int i; 500235537Sgber int offset; 501235537Sgber 502235537Sgber offset = fstp->offset; 503235537Sgber 504235537Sgber if (fsdev->nd_erasesize > MAXBSIZE) 505235537Sgber read_size = MAXBSIZE; 506235537Sgber else 507235537Sgber read_size = fsdev->nd_erasesize; 508235537Sgber 509235537Sgber error = bread(fsdev->nd_devvp, btodb(offset), read_size, NOCRED, &bp); 510235537Sgber if (error) { 511235537Sgber printf("couldn't read: %d\n", error); 512235537Sgber brelse(bp); 513235537Sgber fstp->flags |= NANDFS_FSSTOR_FAILED; 514235537Sgber return (error); 515235537Sgber } 516235537Sgber 517235537Sgber tsuper = super; 518235537Sgber 519235537Sgber memcpy(fsdata, bp->b_data, sizeof(struct nandfs_fsdata)); 520235537Sgber memcpy(tsuper, (bp->b_data + sizeof(struct nandfs_fsdata)), 521235537Sgber read_size - sizeof(struct nandfs_fsdata)); 522235537Sgber brelse(bp); 523235537Sgber 524235537Sgber tsuper += (read_size - sizeof(struct nandfs_fsdata)) / 525235537Sgber sizeof(struct nandfs_super_block); 526235537Sgber 527235537Sgber for (i = 1; i < fsdev->nd_erasesize / read_size; i++) { 528235537Sgber error = bread(fsdev->nd_devvp, btodb(offset + i * read_size), 529235537Sgber read_size, NOCRED, &bp); 530235537Sgber if (error) { 531235537Sgber printf("couldn't read: %d\n", error); 532235537Sgber brelse(bp); 533235537Sgber fstp->flags |= NANDFS_FSSTOR_FAILED; 534235537Sgber return (error); 535235537Sgber } 536235537Sgber memcpy(tsuper, bp->b_data, read_size); 537235537Sgber tsuper += read_size / sizeof(struct nandfs_super_block); 538235537Sgber brelse(bp); 539235537Sgber } 540235537Sgber 541235537Sgber tsuper -= 1; 542235537Sgber fstp->last_used = nandfs_sblocks_in_esize(fsdev) - 1; 543235537Sgber for (tsuperd = super - 1; (tsuper != tsuperd); tsuper -= 1) { 544235537Sgber if (nandfs_is_empty((u_char *)tsuper, sizeof(*tsuper))) 545235537Sgber fstp->last_used--; 546235537Sgber else 547235537Sgber break; 548235537Sgber } 549235537Sgber 550235537Sgber DPRINTF(VOLUMES, ("%s: last_used %d\n", __func__, fstp->last_used)); 551235537Sgber 552235537Sgber return (0); 553235537Sgber} 554235537Sgber 555235537Sgberstatic int 556235537Sgbernandfs_read_structures(struct nandfs_device *fsdev) 557235537Sgber{ 558235537Sgber struct nandfs_fsdata *fsdata, *fsdatat; 559235537Sgber struct nandfs_super_block *sblocks, *ssblock; 560235537Sgber int nsbs, nfsds, i; 561235537Sgber int error = 0; 562235537Sgber int nrsbs; 563235537Sgber 564235537Sgber nfsds = NANDFS_NFSAREAS; 565235537Sgber nsbs = nandfs_max_sblocks(fsdev); 566235537Sgber 567235537Sgber fsdatat = malloc(sizeof(struct nandfs_fsdata) * nfsds, M_NANDFSTEMP, 568235537Sgber M_WAITOK | M_ZERO); 569235537Sgber sblocks = malloc(sizeof(struct nandfs_super_block) * nsbs, M_NANDFSTEMP, 570235537Sgber M_WAITOK | M_ZERO); 571235537Sgber 572235537Sgber nrsbs = 0; 573235537Sgber for (i = 0; i < NANDFS_NFSAREAS; i++) { 574235537Sgber fsdev->nd_fsarea[i].offset = i * fsdev->nd_erasesize; 575235537Sgber error = nandfs_read_structures_at(fsdev, &fsdev->nd_fsarea[i], 576235537Sgber &fsdatat[i], sblocks + nrsbs); 577235537Sgber if (error) 578235537Sgber continue; 579235537Sgber nrsbs += (fsdev->nd_fsarea[i].last_used + 1); 580235537Sgber if (fsdev->nd_fsarea[fsdev->nd_last_fsarea].last_used > 581235537Sgber fsdev->nd_fsarea[i].last_used) 582235537Sgber fsdev->nd_last_fsarea = i; 583235537Sgber } 584235537Sgber 585235537Sgber if (nrsbs == 0) { 586235537Sgber printf("nandfs: no valid superblocks found\n"); 587235537Sgber error = EINVAL; 588235537Sgber goto out; 589235537Sgber } 590235537Sgber 591235537Sgber error = nandfs_select_fsdata(fsdev, fsdatat, &fsdata, nfsds); 592235537Sgber if (error) 593235537Sgber goto out; 594235537Sgber memcpy(&fsdev->nd_fsdata, fsdata, sizeof(struct nandfs_fsdata)); 595235537Sgber 596235537Sgber error = nandfs_select_sb(fsdev, sblocks, &ssblock, nsbs); 597235537Sgber if (error) 598235537Sgber goto out; 599235537Sgber 600235537Sgber memcpy(&fsdev->nd_super, ssblock, sizeof(struct nandfs_super_block)); 601235537Sgberout: 602235537Sgber free(fsdatat, M_NANDFSTEMP); 603235537Sgber free(sblocks, M_NANDFSTEMP); 604235537Sgber 605235537Sgber if (error == 0) 606235537Sgber DPRINTF(VOLUMES, ("%s: selected sb with w_time %jd " 607235537Sgber "last_pseg %#jx\n", __func__, fsdev->nd_super.s_wtime, 608235537Sgber fsdev->nd_super.s_last_pseg)); 609235537Sgber 610235537Sgber return (error); 611235537Sgber} 612235537Sgber 613235537Sgberstatic void 614235537Sgbernandfs_unmount_base(struct nandfs_device *nandfsdev) 615235537Sgber{ 616235537Sgber int error; 617235537Sgber 618235537Sgber if (!nandfsdev) 619235537Sgber return; 620235537Sgber 621235537Sgber /* Remove all our information */ 622235537Sgber error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0); 623235537Sgber if (error) { 624235537Sgber /* 625235537Sgber * Flushing buffers failed when fs was umounting, can't do 626235537Sgber * much now, just printf error and continue with umount. 627235537Sgber */ 628235537Sgber nandfs_error("%s(): error:%d when umounting FS\n", 629235537Sgber __func__, error); 630235537Sgber } 631235537Sgber 632235537Sgber /* Release the device's system nodes */ 633235537Sgber nandfs_release_system_nodes(nandfsdev); 634235537Sgber} 635235537Sgber 636235537Sgberstatic void 637235537Sgbernandfs_get_ncleanseg(struct nandfs_device *nandfsdev) 638235537Sgber{ 639235537Sgber struct nandfs_seg_stat nss; 640235537Sgber 641235537Sgber nandfs_get_seg_stat(nandfsdev, &nss); 642235537Sgber nandfsdev->nd_clean_segs = nss.nss_ncleansegs; 643235537Sgber DPRINTF(VOLUMES, ("nandfs_mount: clean segs: %jx\n", 644235537Sgber (uintmax_t)nandfsdev->nd_clean_segs)); 645235537Sgber} 646235537Sgber 647235537Sgber 648235537Sgberstatic int 649235537Sgbernandfs_mount_base(struct nandfs_device *nandfsdev, struct mount *mp, 650235537Sgber struct nandfs_args *args) 651235537Sgber{ 652235537Sgber uint32_t log_blocksize; 653235537Sgber int error; 654235537Sgber 655235537Sgber /* Flush out any old buffers remaining from a previous use. */ 656235537Sgber if ((error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0))) 657235537Sgber return (error); 658235537Sgber 659235537Sgber error = nandfs_read_structures(nandfsdev); 660235537Sgber if (error) { 661235537Sgber printf("nandfs: could not get valid filesystem structures\n"); 662235537Sgber return (error); 663235537Sgber } 664235537Sgber 665235537Sgber if (nandfsdev->nd_fsdata.f_rev_level != NANDFS_CURRENT_REV) { 666235537Sgber printf("nandfs: unsupported file system revision: %d " 667235537Sgber "(supported is %d).\n", nandfsdev->nd_fsdata.f_rev_level, 668235537Sgber NANDFS_CURRENT_REV); 669235537Sgber return (EINVAL); 670235537Sgber } 671235537Sgber 672235537Sgber if (nandfsdev->nd_fsdata.f_erasesize != nandfsdev->nd_erasesize) { 673235537Sgber printf("nandfs: erasesize mismatch (device %#x, fs %#x)\n", 674235537Sgber nandfsdev->nd_erasesize, nandfsdev->nd_fsdata.f_erasesize); 675235537Sgber return (EINVAL); 676235537Sgber } 677235537Sgber 678235537Sgber /* Get our blocksize */ 679235537Sgber log_blocksize = nandfsdev->nd_fsdata.f_log_block_size; 680235537Sgber nandfsdev->nd_blocksize = (uint64_t) 1 << (log_blocksize + 10); 681235537Sgber DPRINTF(VOLUMES, ("%s: blocksize:%x\n", __func__, 682235537Sgber nandfsdev->nd_blocksize)); 683235537Sgber 684235537Sgber DPRINTF(VOLUMES, ("%s: accepted super block with cp %#jx\n", __func__, 685235537Sgber (uintmax_t)nandfsdev->nd_super.s_last_cno)); 686235537Sgber 687235537Sgber /* Calculate dat structure parameters */ 688235537Sgber nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_dat_mdt, 689235537Sgber nandfsdev->nd_fsdata.f_dat_entry_size); 690235537Sgber nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_ifile_mdt, 691235537Sgber nandfsdev->nd_fsdata.f_inode_size); 692235537Sgber 693235537Sgber /* Search for the super root and roll forward when needed */ 694235537Sgber if (nandfs_search_super_root(nandfsdev)) { 695235537Sgber printf("Cannot find valid SuperRoot\n"); 696235537Sgber return (EINVAL); 697235537Sgber } 698235537Sgber 699235537Sgber nandfsdev->nd_mount_state = nandfsdev->nd_super.s_state; 700235537Sgber if (nandfsdev->nd_mount_state != NANDFS_VALID_FS) { 701235537Sgber printf("FS is seriously damaged, needs repairing\n"); 702235537Sgber printf("aborting mount\n"); 703235537Sgber return (EINVAL); 704235537Sgber } 705235537Sgber 706235537Sgber /* 707235537Sgber * FS should be ok now. The superblock and the last segsum could be 708235537Sgber * updated from the repair so extract running values again. 709235537Sgber */ 710235537Sgber nandfsdev->nd_last_pseg = nandfsdev->nd_super.s_last_pseg; 711235537Sgber nandfsdev->nd_seg_sequence = nandfsdev->nd_super.s_last_seq; 712235537Sgber nandfsdev->nd_seg_num = nandfs_get_segnum_of_block(nandfsdev, 713235537Sgber nandfsdev->nd_last_pseg); 714235537Sgber nandfsdev->nd_next_seg_num = nandfs_get_segnum_of_block(nandfsdev, 715235537Sgber nandfsdev->nd_last_segsum.ss_next); 716235537Sgber nandfsdev->nd_ts.tv_sec = nandfsdev->nd_last_segsum.ss_create; 717235537Sgber nandfsdev->nd_last_cno = nandfsdev->nd_super.s_last_cno; 718235537Sgber nandfsdev->nd_fakevblk = 1; 719235537Sgber nandfsdev->nd_last_ino = NANDFS_USER_INO; 720235537Sgber DPRINTF(VOLUMES, ("%s: last_pseg %#jx last_cno %#jx last_seq %#jx\n" 721235537Sgber "fsdev: last_seg: seq %#jx num %#jx, next_seg_num %#jx\n", 722235537Sgber __func__, (uintmax_t)nandfsdev->nd_last_pseg, 723235537Sgber (uintmax_t)nandfsdev->nd_last_cno, 724235537Sgber (uintmax_t)nandfsdev->nd_seg_sequence, 725235537Sgber (uintmax_t)nandfsdev->nd_seg_sequence, 726235537Sgber (uintmax_t)nandfsdev->nd_seg_num, 727235537Sgber (uintmax_t)nandfsdev->nd_next_seg_num)); 728235537Sgber 729235537Sgber DPRINTF(VOLUMES, ("nandfs_mount: accepted super root\n")); 730235537Sgber 731235537Sgber /* Create system vnodes for DAT, CP and SEGSUM */ 732235537Sgber error = nandfs_create_system_nodes(nandfsdev); 733235537Sgber if (error) 734235537Sgber nandfs_unmount_base(nandfsdev); 735235537Sgber 736235537Sgber nandfs_get_ncleanseg(nandfsdev); 737235537Sgber 738235537Sgber return (error); 739235537Sgber} 740235537Sgber 741235537Sgberstatic void 742235537Sgbernandfs_unmount_device(struct nandfs_device *nandfsdev) 743235537Sgber{ 744235537Sgber 745235537Sgber /* Is there anything? */ 746235537Sgber if (nandfsdev == NULL) 747235537Sgber return; 748235537Sgber 749235537Sgber /* Remove the device only if we're the last reference */ 750235537Sgber nandfsdev->nd_refcnt--; 751235537Sgber if (nandfsdev->nd_refcnt >= 1) 752235537Sgber return; 753235537Sgber 754235537Sgber MPASS(nandfsdev->nd_syncer == NULL); 755235537Sgber MPASS(nandfsdev->nd_cleaner == NULL); 756235537Sgber MPASS(nandfsdev->nd_free_base == NULL); 757235537Sgber 758235537Sgber /* Unmount our base */ 759235537Sgber nandfs_unmount_base(nandfsdev); 760235537Sgber 761235537Sgber /* Remove from our device list */ 762235537Sgber SLIST_REMOVE(&nandfs_devices, nandfsdev, nandfs_device, nd_next_device); 763235537Sgber 764235537Sgber DROP_GIANT(); 765235537Sgber g_topology_lock(); 766235537Sgber g_vfs_close(nandfsdev->nd_gconsumer); 767235537Sgber g_topology_unlock(); 768235537Sgber PICKUP_GIANT(); 769235537Sgber 770235537Sgber DPRINTF(VOLUMES, ("closing device\n")); 771235537Sgber 772235537Sgber /* Clear our mount reference and release device node */ 773235537Sgber vrele(nandfsdev->nd_devvp); 774235537Sgber 775235537Sgber dev_rel(nandfsdev->nd_devvp->v_rdev); 776235537Sgber 777235537Sgber /* Free our device info */ 778235537Sgber cv_destroy(&nandfsdev->nd_sync_cv); 779235537Sgber mtx_destroy(&nandfsdev->nd_sync_mtx); 780235537Sgber cv_destroy(&nandfsdev->nd_clean_cv); 781235537Sgber mtx_destroy(&nandfsdev->nd_clean_mtx); 782235537Sgber mtx_destroy(&nandfsdev->nd_mutex); 783235537Sgber lockdestroy(&nandfsdev->nd_seg_const); 784235537Sgber free(nandfsdev, M_NANDFSMNT); 785235537Sgber} 786235537Sgber 787235537Sgberstatic int 788235537Sgbernandfs_check_mounts(struct nandfs_device *nandfsdev, struct mount *mp, 789235537Sgber struct nandfs_args *args) 790235537Sgber{ 791235537Sgber struct nandfsmount *nmp; 792235537Sgber uint64_t last_cno; 793235537Sgber 794235537Sgber /* no double-mounting of the same checkpoint */ 795235537Sgber STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) { 796235537Sgber if (nmp->nm_mount_args.cpno == args->cpno) 797235537Sgber return (EBUSY); 798235537Sgber } 799235537Sgber 800235537Sgber /* Allow readonly mounts without questioning here */ 801235537Sgber if (mp->mnt_flag & MNT_RDONLY) 802235537Sgber return (0); 803235537Sgber 804235537Sgber /* Read/write mount */ 805235537Sgber STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) { 806235537Sgber /* Only one RW mount on this device! */ 807235537Sgber if ((nmp->nm_vfs_mountp->mnt_flag & MNT_RDONLY)==0) 808235537Sgber return (EROFS); 809235537Sgber /* RDONLY on last mountpoint is device busy */ 810235537Sgber last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno; 811235537Sgber if (nmp->nm_mount_args.cpno == last_cno) 812235537Sgber return (EBUSY); 813235537Sgber } 814235537Sgber 815235537Sgber /* OK for now */ 816235537Sgber return (0); 817235537Sgber} 818235537Sgber 819235537Sgberstatic int 820235537Sgbernandfs_mount_device(struct vnode *devvp, struct mount *mp, 821235537Sgber struct nandfs_args *args, struct nandfs_device **nandfsdev_p) 822235537Sgber{ 823235537Sgber struct nandfs_device *nandfsdev; 824235537Sgber struct g_provider *pp; 825235537Sgber struct g_consumer *cp; 826235537Sgber struct cdev *dev; 827235537Sgber uint32_t erasesize; 828235537Sgber int error, size; 829235537Sgber int ronly; 830235537Sgber 831235537Sgber DPRINTF(VOLUMES, ("Mounting NANDFS device\n")); 832235537Sgber 833235537Sgber ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 834235537Sgber 835235537Sgber /* Look up device in our nandfs_mountpoints */ 836235537Sgber *nandfsdev_p = NULL; 837235537Sgber SLIST_FOREACH(nandfsdev, &nandfs_devices, nd_next_device) 838235537Sgber if (nandfsdev->nd_devvp == devvp) 839235537Sgber break; 840235537Sgber 841235537Sgber if (nandfsdev) { 842235537Sgber DPRINTF(VOLUMES, ("device already mounted\n")); 843235537Sgber error = nandfs_check_mounts(nandfsdev, mp, args); 844235537Sgber if (error) 845235537Sgber return error; 846235537Sgber nandfsdev->nd_refcnt++; 847235537Sgber *nandfsdev_p = nandfsdev; 848235537Sgber 849235537Sgber if (!ronly) { 850235537Sgber DROP_GIANT(); 851235537Sgber g_topology_lock(); 852235537Sgber error = g_access(nandfsdev->nd_gconsumer, 0, 1, 0); 853235537Sgber g_topology_unlock(); 854235537Sgber PICKUP_GIANT(); 855235537Sgber } 856235537Sgber return (error); 857235537Sgber } 858235537Sgber 859235537Sgber vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 860235537Sgber dev = devvp->v_rdev; 861235537Sgber dev_ref(dev); 862235537Sgber DROP_GIANT(); 863235537Sgber g_topology_lock(); 864235537Sgber error = g_vfs_open(devvp, &cp, "nandfs", ronly ? 0 : 1); 865235537Sgber pp = g_dev_getprovider(dev); 866235537Sgber g_topology_unlock(); 867235537Sgber PICKUP_GIANT(); 868235537Sgber VOP_UNLOCK(devvp, 0); 869235537Sgber if (error) { 870235537Sgber dev_rel(dev); 871235537Sgber return (error); 872235537Sgber } 873235537Sgber 874235537Sgber nandfsdev = malloc(sizeof(struct nandfs_device), M_NANDFSMNT, M_WAITOK | M_ZERO); 875235537Sgber 876235537Sgber /* Initialise */ 877235537Sgber nandfsdev->nd_refcnt = 1; 878235537Sgber nandfsdev->nd_devvp = devvp; 879235537Sgber nandfsdev->nd_syncing = 0; 880235537Sgber nandfsdev->nd_cleaning = 0; 881235537Sgber nandfsdev->nd_gconsumer = cp; 882235537Sgber cv_init(&nandfsdev->nd_sync_cv, "nandfssync"); 883235537Sgber mtx_init(&nandfsdev->nd_sync_mtx, "nffssyncmtx", NULL, MTX_DEF); 884235537Sgber cv_init(&nandfsdev->nd_clean_cv, "nandfsclean"); 885235537Sgber mtx_init(&nandfsdev->nd_clean_mtx, "nffscleanmtx", NULL, MTX_DEF); 886235537Sgber mtx_init(&nandfsdev->nd_mutex, "nandfsdev lock", NULL, MTX_DEF); 887235537Sgber lockinit(&nandfsdev->nd_seg_const, PVFS, "nffssegcon", VLKTIMEOUT, 888235537Sgber LK_CANRECURSE); 889235537Sgber STAILQ_INIT(&nandfsdev->nd_mounts); 890235537Sgber 891235537Sgber nandfsdev->nd_devsize = pp->mediasize; 892235537Sgber nandfsdev->nd_devblocksize = pp->sectorsize; 893235537Sgber 894235537Sgber size = sizeof(erasesize); 895235537Sgber error = g_io_getattr("NAND::blocksize", nandfsdev->nd_gconsumer, &size, 896235537Sgber &erasesize); 897235537Sgber if (error) { 898235537Sgber DPRINTF(VOLUMES, ("couldn't get erasesize: %d\n", error)); 899235537Sgber 900235537Sgber if (error == ENOIOCTL || error == EOPNOTSUPP) { 901235537Sgber /* 902235537Sgber * We conclude that this is not NAND storage 903235537Sgber */ 904235537Sgber nandfsdev->nd_erasesize = NANDFS_DEF_ERASESIZE; 905235537Sgber nandfsdev->nd_is_nand = 0; 906235537Sgber } else { 907235537Sgber DROP_GIANT(); 908235537Sgber g_topology_lock(); 909235537Sgber g_vfs_close(nandfsdev->nd_gconsumer); 910235537Sgber g_topology_unlock(); 911235537Sgber PICKUP_GIANT(); 912235537Sgber dev_rel(dev); 913235537Sgber free(nandfsdev, M_NANDFSMNT); 914235537Sgber return (error); 915235537Sgber } 916235537Sgber } else { 917235537Sgber nandfsdev->nd_erasesize = erasesize; 918235537Sgber nandfsdev->nd_is_nand = 1; 919235537Sgber } 920235537Sgber 921235537Sgber DPRINTF(VOLUMES, ("%s: erasesize %x\n", __func__, 922235537Sgber nandfsdev->nd_erasesize)); 923235537Sgber 924235537Sgber /* Register nandfs_device in list */ 925235537Sgber SLIST_INSERT_HEAD(&nandfs_devices, nandfsdev, nd_next_device); 926235537Sgber 927235537Sgber error = nandfs_mount_base(nandfsdev, mp, args); 928235537Sgber if (error) { 929235537Sgber /* Remove all our information */ 930235537Sgber nandfs_unmount_device(nandfsdev); 931235537Sgber return (EINVAL); 932235537Sgber } 933235537Sgber 934235537Sgber nandfsdev->nd_maxfilesize = nandfs_get_maxfilesize(nandfsdev); 935235537Sgber 936235537Sgber *nandfsdev_p = nandfsdev; 937235537Sgber DPRINTF(VOLUMES, ("NANDFS device mounted ok\n")); 938235537Sgber 939235537Sgber return (0); 940235537Sgber} 941235537Sgber 942235537Sgberstatic int 943235537Sgbernandfs_mount_checkpoint(struct nandfsmount *nmp) 944235537Sgber{ 945235537Sgber struct nandfs_cpfile_header *cphdr; 946235537Sgber struct nandfs_checkpoint *cp; 947235537Sgber struct nandfs_inode ifile_inode; 948235537Sgber struct nandfs_node *cp_node; 949235537Sgber struct buf *bp; 950235537Sgber uint64_t ncp, nsn, cpno, fcpno, blocknr, last_cno; 951235537Sgber uint32_t off, dlen; 952235537Sgber int cp_per_block, error; 953235537Sgber 954235537Sgber cpno = nmp->nm_mount_args.cpno; 955235537Sgber if (cpno == 0) 956235537Sgber cpno = nmp->nm_nandfsdev->nd_super.s_last_cno; 957235537Sgber 958235537Sgber DPRINTF(VOLUMES, ("%s: trying to mount checkpoint number %"PRIu64"\n", 959235537Sgber __func__, cpno)); 960235537Sgber 961235537Sgber cp_node = nmp->nm_nandfsdev->nd_cp_node; 962235537Sgber 963235537Sgber VOP_LOCK(NTOV(cp_node), LK_SHARED); 964235537Sgber /* Get cpfile header from 1st block of cp file */ 965235537Sgber error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); 966235537Sgber if (error) { 967235537Sgber brelse(bp); 968235537Sgber VOP_UNLOCK(NTOV(cp_node), 0); 969235537Sgber return (error); 970235537Sgber } 971235537Sgber 972235537Sgber cphdr = (struct nandfs_cpfile_header *) bp->b_data; 973235537Sgber ncp = cphdr->ch_ncheckpoints; 974235537Sgber nsn = cphdr->ch_nsnapshots; 975235537Sgber 976235537Sgber brelse(bp); 977235537Sgber 978235537Sgber DPRINTF(VOLUMES, ("mount_nandfs: checkpoint header read in\n")); 979235537Sgber DPRINTF(VOLUMES, ("\tNumber of checkpoints %"PRIu64"\n", ncp)); 980235537Sgber DPRINTF(VOLUMES, ("\tNumber of snapshots %"PRIu64"\n", nsn)); 981235537Sgber 982235537Sgber /* Read in our specified checkpoint */ 983235537Sgber dlen = nmp->nm_nandfsdev->nd_fsdata.f_checkpoint_size; 984235537Sgber cp_per_block = nmp->nm_nandfsdev->nd_blocksize / dlen; 985235537Sgber 986235537Sgber fcpno = cpno + NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1; 987235537Sgber blocknr = fcpno / cp_per_block; 988235537Sgber off = (fcpno % cp_per_block) * dlen; 989235537Sgber error = nandfs_bread(cp_node, blocknr, NOCRED, 0, &bp); 990235537Sgber if (error) { 991235537Sgber brelse(bp); 992235537Sgber VOP_UNLOCK(NTOV(cp_node), 0); 993235537Sgber printf("mount_nandfs: couldn't read cp block %"PRIu64"\n", 994235537Sgber fcpno); 995235537Sgber return (EINVAL); 996235537Sgber } 997235537Sgber 998235537Sgber /* Needs to be a valid checkpoint */ 999235537Sgber cp = (struct nandfs_checkpoint *) ((uint8_t *) bp->b_data + off); 1000235537Sgber if (cp->cp_flags & NANDFS_CHECKPOINT_INVALID) { 1001235537Sgber printf("mount_nandfs: checkpoint marked invalid\n"); 1002235537Sgber brelse(bp); 1003235537Sgber VOP_UNLOCK(NTOV(cp_node), 0); 1004235537Sgber return (EINVAL); 1005235537Sgber } 1006235537Sgber 1007235537Sgber /* Is this really the checkpoint we want? */ 1008235537Sgber if (cp->cp_cno != cpno) { 1009235537Sgber printf("mount_nandfs: checkpoint file corrupt? " 1010235537Sgber "expected cpno %"PRIu64", found cpno %"PRIu64"\n", 1011235537Sgber cpno, cp->cp_cno); 1012235537Sgber brelse(bp); 1013235537Sgber VOP_UNLOCK(NTOV(cp_node), 0); 1014235537Sgber return (EINVAL); 1015235537Sgber } 1016235537Sgber 1017235537Sgber /* Check if it's a snapshot ! */ 1018235537Sgber last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno; 1019235537Sgber if (cpno != last_cno) { 1020235537Sgber /* Only allow snapshots if not mounting on the last cp */ 1021235537Sgber if ((cp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) == 0) { 1022235537Sgber printf( "mount_nandfs: checkpoint %"PRIu64" is not a " 1023235537Sgber "snapshot\n", cpno); 1024235537Sgber brelse(bp); 1025235537Sgber VOP_UNLOCK(NTOV(cp_node), 0); 1026235537Sgber return (EINVAL); 1027235537Sgber } 1028235537Sgber } 1029235537Sgber 1030235537Sgber ifile_inode = cp->cp_ifile_inode; 1031235537Sgber brelse(bp); 1032235537Sgber 1033235537Sgber /* Get ifile inode */ 1034235537Sgber error = nandfs_get_node_raw(nmp->nm_nandfsdev, NULL, NANDFS_IFILE_INO, 1035235537Sgber &ifile_inode, &nmp->nm_ifile_node); 1036235537Sgber if (error) { 1037235537Sgber printf("mount_nandfs: can't read ifile node\n"); 1038235537Sgber VOP_UNLOCK(NTOV(cp_node), 0); 1039235537Sgber return (EINVAL); 1040235537Sgber } 1041235537Sgber 1042235537Sgber NANDFS_SET_SYSTEMFILE(NTOV(nmp->nm_ifile_node)); 1043235537Sgber VOP_UNLOCK(NTOV(cp_node), 0); 1044235537Sgber /* Get root node? */ 1045235537Sgber 1046235537Sgber return (0); 1047235537Sgber} 1048235537Sgber 1049235537Sgberstatic void 1050235537Sgberfree_nandfs_mountinfo(struct mount *mp) 1051235537Sgber{ 1052235537Sgber struct nandfsmount *nmp = VFSTONANDFS(mp); 1053235537Sgber 1054235537Sgber if (nmp == NULL) 1055235537Sgber return; 1056235537Sgber 1057235537Sgber free(nmp, M_NANDFSMNT); 1058235537Sgber} 1059235537Sgber 1060235537Sgbervoid 1061235537Sgbernandfs_wakeup_wait_sync(struct nandfs_device *nffsdev, int reason) 1062235537Sgber{ 1063235537Sgber char *reasons[] = { 1064235537Sgber "umount", 1065235537Sgber "vfssync", 1066235537Sgber "bdflush", 1067235537Sgber "fforce", 1068235537Sgber "fsync", 1069235537Sgber "ro_upd" 1070235537Sgber }; 1071235537Sgber 1072235537Sgber DPRINTF(SYNC, ("%s: %s\n", __func__, reasons[reason])); 1073235537Sgber mtx_lock(&nffsdev->nd_sync_mtx); 1074235537Sgber if (nffsdev->nd_syncing) 1075235537Sgber cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx); 1076235537Sgber if (reason == SYNCER_UMOUNT) 1077235537Sgber nffsdev->nd_syncer_exit = 1; 1078235537Sgber nffsdev->nd_syncing = 1; 1079235537Sgber wakeup(&nffsdev->nd_syncing); 1080235537Sgber cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx); 1081235537Sgber 1082235537Sgber mtx_unlock(&nffsdev->nd_sync_mtx); 1083235537Sgber} 1084235537Sgber 1085235537Sgberstatic void 1086235537Sgbernandfs_gc_finished(struct nandfs_device *nffsdev, int exit) 1087235537Sgber{ 1088235537Sgber int error; 1089235537Sgber 1090235537Sgber mtx_lock(&nffsdev->nd_sync_mtx); 1091235537Sgber nffsdev->nd_syncing = 0; 1092235537Sgber DPRINTF(SYNC, ("%s: cleaner finish\n", __func__)); 1093235537Sgber cv_broadcast(&nffsdev->nd_sync_cv); 1094235537Sgber mtx_unlock(&nffsdev->nd_sync_mtx); 1095235537Sgber if (!exit) { 1096235537Sgber error = tsleep(&nffsdev->nd_syncing, PRIBIO, "-", 1097235537Sgber hz * nandfs_sync_interval); 1098235537Sgber DPRINTF(SYNC, ("%s: cleaner waked up: %d\n", 1099235537Sgber __func__, error)); 1100235537Sgber } 1101235537Sgber} 1102235537Sgber 1103235537Sgberstatic void 1104235537Sgbernandfs_syncer(struct nandfsmount *nmp) 1105235537Sgber{ 1106235537Sgber struct nandfs_device *nffsdev; 1107235537Sgber struct mount *mp; 1108235537Sgber int flags, error; 1109235537Sgber 1110235537Sgber mp = nmp->nm_vfs_mountp; 1111235537Sgber nffsdev = nmp->nm_nandfsdev; 1112235537Sgber tsleep(&nffsdev->nd_syncing, PRIBIO, "-", hz * nandfs_sync_interval); 1113235537Sgber 1114235537Sgber while (!nffsdev->nd_syncer_exit) { 1115235537Sgber DPRINTF(SYNC, ("%s: syncer run\n", __func__)); 1116235537Sgber nffsdev->nd_syncing = 1; 1117235537Sgber 1118235537Sgber flags = (nmp->nm_flags & (NANDFS_FORCE_SYNCER | NANDFS_UMOUNT)); 1119235537Sgber 1120235537Sgber error = nandfs_segment_constructor(nmp, flags); 1121235537Sgber if (error) 1122235537Sgber nandfs_error("%s: error:%d when creating segments\n", 1123235537Sgber __func__, error); 1124235537Sgber 1125235537Sgber nmp->nm_flags &= ~flags; 1126235537Sgber 1127235537Sgber nandfs_gc_finished(nffsdev, 0); 1128235537Sgber } 1129235537Sgber 1130235537Sgber MPASS(nffsdev->nd_cleaner == NULL); 1131235537Sgber error = nandfs_segment_constructor(nmp, 1132235537Sgber NANDFS_FORCE_SYNCER | NANDFS_UMOUNT); 1133235537Sgber if (error) 1134235537Sgber nandfs_error("%s: error:%d when creating segments\n", 1135235537Sgber __func__, error); 1136235537Sgber nandfs_gc_finished(nffsdev, 1); 1137235537Sgber nffsdev->nd_syncer = NULL; 1138235537Sgber MPASS(nffsdev->nd_free_base == NULL); 1139235537Sgber 1140235537Sgber DPRINTF(SYNC, ("%s: exiting\n", __func__)); 1141235537Sgber kthread_exit(); 1142235537Sgber} 1143235537Sgber 1144235537Sgberstatic int 1145235537Sgberstart_syncer(struct nandfsmount *nmp) 1146235537Sgber{ 1147235537Sgber int error; 1148235537Sgber 1149235537Sgber MPASS(nmp->nm_nandfsdev->nd_syncer == NULL); 1150235537Sgber 1151235537Sgber DPRINTF(SYNC, ("%s: start syncer\n", __func__)); 1152235537Sgber 1153235537Sgber nmp->nm_nandfsdev->nd_syncer_exit = 0; 1154235537Sgber 1155235537Sgber error = kthread_add((void(*)(void *))nandfs_syncer, nmp, NULL, 1156235537Sgber &nmp->nm_nandfsdev->nd_syncer, 0, 0, "nandfs_syncer"); 1157235537Sgber 1158235537Sgber if (error) 1159235537Sgber printf("nandfs: could not start syncer: %d\n", error); 1160235537Sgber 1161235537Sgber return (error); 1162235537Sgber} 1163235537Sgber 1164235537Sgberstatic int 1165235537Sgberstop_syncer(struct nandfsmount *nmp) 1166235537Sgber{ 1167235537Sgber 1168235537Sgber MPASS(nmp->nm_nandfsdev->nd_syncer != NULL); 1169235537Sgber 1170235537Sgber nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_UMOUNT); 1171235537Sgber 1172235537Sgber DPRINTF(SYNC, ("%s: stop syncer\n", __func__)); 1173235537Sgber return (0); 1174235537Sgber} 1175235537Sgber 1176235537Sgber/* 1177235537Sgber * Mount null layer 1178235537Sgber */ 1179235537Sgberstatic int 1180235537Sgbernandfs_mount(struct mount *mp) 1181235537Sgber{ 1182235537Sgber struct nandfsmount *nmp; 1183235537Sgber struct vnode *devvp; 1184235537Sgber struct nameidata nd; 1185235537Sgber struct vfsoptlist *opts; 1186235537Sgber struct thread *td; 1187235537Sgber char *from; 1188235537Sgber int error = 0, flags; 1189235537Sgber 1190235537Sgber DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp)); 1191235537Sgber 1192235537Sgber td = curthread; 1193235537Sgber opts = mp->mnt_optnew; 1194235537Sgber 1195235537Sgber if (vfs_filteropt(opts, nandfs_opts)) 1196235537Sgber return (EINVAL); 1197235537Sgber 1198235537Sgber /* 1199235537Sgber * Update is a no-op 1200235537Sgber */ 1201235537Sgber if (mp->mnt_flag & MNT_UPDATE) { 1202235537Sgber nmp = VFSTONANDFS(mp); 1203235537Sgber if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) { 1204235537Sgber return (error); 1205235537Sgber } 1206235537Sgber if (!(nmp->nm_ronly) && vfs_flagopt(opts, "ro", NULL, 0)) { 1207235537Sgber vn_start_write(NULL, &mp, V_WAIT); 1208235537Sgber error = VFS_SYNC(mp, MNT_WAIT); 1209235537Sgber if (error) 1210235537Sgber return (error); 1211235537Sgber vn_finished_write(mp); 1212235537Sgber 1213235537Sgber flags = WRITECLOSE; 1214235537Sgber if (mp->mnt_flag & MNT_FORCE) 1215235537Sgber flags |= FORCECLOSE; 1216235537Sgber 1217235537Sgber nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, 1218235537Sgber SYNCER_ROUPD); 1219235537Sgber error = vflush(mp, 0, flags, td); 1220235537Sgber if (error) 1221235537Sgber return (error); 1222235537Sgber 1223235537Sgber nandfs_stop_cleaner(nmp->nm_nandfsdev); 1224235537Sgber stop_syncer(nmp); 1225235537Sgber DROP_GIANT(); 1226235537Sgber g_topology_lock(); 1227235537Sgber g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1, 0); 1228235537Sgber g_topology_unlock(); 1229235537Sgber PICKUP_GIANT(); 1230235537Sgber MNT_ILOCK(mp); 1231235537Sgber mp->mnt_flag |= MNT_RDONLY; 1232235537Sgber MNT_IUNLOCK(mp); 1233235537Sgber nmp->nm_ronly = 1; 1234235537Sgber 1235235537Sgber } else if ((nmp->nm_ronly) && 1236235537Sgber !vfs_flagopt(opts, "ro", NULL, 0)) { 1237235537Sgber /* 1238235537Sgber * Don't allow read-write snapshots. 1239235537Sgber */ 1240235537Sgber if (nmp->nm_mount_args.cpno != 0) 1241235537Sgber return (EROFS); 1242235537Sgber /* 1243235537Sgber * If upgrade to read-write by non-root, then verify 1244235537Sgber * that user has necessary permissions on the device. 1245235537Sgber */ 1246235537Sgber devvp = nmp->nm_nandfsdev->nd_devvp; 1247235537Sgber vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 1248235537Sgber error = VOP_ACCESS(devvp, VREAD | VWRITE, 1249235537Sgber td->td_ucred, td); 1250235537Sgber if (error) { 1251235537Sgber error = priv_check(td, PRIV_VFS_MOUNT_PERM); 1252235537Sgber if (error) { 1253235537Sgber VOP_UNLOCK(devvp, 0); 1254235537Sgber return (error); 1255235537Sgber } 1256235537Sgber } 1257235537Sgber 1258235537Sgber VOP_UNLOCK(devvp, 0); 1259235537Sgber DROP_GIANT(); 1260235537Sgber g_topology_lock(); 1261235537Sgber error = g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, 1, 1262235537Sgber 0); 1263235537Sgber g_topology_unlock(); 1264235537Sgber PICKUP_GIANT(); 1265235537Sgber if (error) 1266235537Sgber return (error); 1267235537Sgber 1268235537Sgber MNT_ILOCK(mp); 1269235537Sgber mp->mnt_flag &= ~MNT_RDONLY; 1270235537Sgber MNT_IUNLOCK(mp); 1271235537Sgber error = start_syncer(nmp); 1272235537Sgber if (error == 0) 1273235537Sgber error = nandfs_start_cleaner(nmp->nm_nandfsdev); 1274235537Sgber if (error) { 1275235537Sgber DROP_GIANT(); 1276235537Sgber g_topology_lock(); 1277235537Sgber g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1, 1278235537Sgber 0); 1279235537Sgber g_topology_unlock(); 1280235537Sgber PICKUP_GIANT(); 1281235537Sgber return (error); 1282235537Sgber } 1283235537Sgber 1284235537Sgber nmp->nm_ronly = 0; 1285235537Sgber } 1286235537Sgber return (0); 1287235537Sgber } 1288235537Sgber 1289235537Sgber from = vfs_getopts(opts, "from", &error); 1290235537Sgber if (error) 1291235537Sgber return (error); 1292235537Sgber 1293235537Sgber /* 1294235537Sgber * Find device node 1295235537Sgber */ 1296235537Sgber NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, from, curthread); 1297235537Sgber error = namei(&nd); 1298235537Sgber if (error) 1299235537Sgber return (error); 1300235537Sgber NDFREE(&nd, NDF_ONLY_PNBUF); 1301235537Sgber 1302235537Sgber devvp = nd.ni_vp; 1303235537Sgber 1304235537Sgber if (!vn_isdisk(devvp, &error)) { 1305235537Sgber vput(devvp); 1306235537Sgber return (error); 1307235537Sgber } 1308235537Sgber 1309235537Sgber /* Check the access rights on the mount device */ 1310235537Sgber error = VOP_ACCESS(devvp, VREAD, curthread->td_ucred, curthread); 1311235537Sgber if (error) 1312235537Sgber error = priv_check(curthread, PRIV_VFS_MOUNT_PERM); 1313235537Sgber if (error) { 1314235537Sgber vput(devvp); 1315235537Sgber return (error); 1316235537Sgber } 1317235537Sgber 1318235537Sgber vfs_getnewfsid(mp); 1319235537Sgber 1320235537Sgber error = nandfs_mountfs(devvp, mp); 1321235537Sgber if (error) 1322235537Sgber return (error); 1323235537Sgber vfs_mountedfrom(mp, from); 1324235537Sgber 1325235537Sgber return (0); 1326235537Sgber} 1327235537Sgber 1328235537Sgberstatic int 1329235537Sgbernandfs_mountfs(struct vnode *devvp, struct mount *mp) 1330235537Sgber{ 1331235537Sgber struct nandfsmount *nmp = NULL; 1332235537Sgber struct nandfs_args *args = NULL; 1333235537Sgber struct nandfs_device *nandfsdev; 1334235537Sgber char *from; 1335235537Sgber int error, ronly; 1336235537Sgber char *cpno; 1337235537Sgber 1338235537Sgber ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 1339235537Sgber 1340235537Sgber if (devvp->v_rdev->si_iosize_max != 0) 1341235537Sgber mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; 1342235537Sgber VOP_UNLOCK(devvp, 0); 1343235537Sgber 1344235537Sgber if (mp->mnt_iosize_max > MAXPHYS) 1345235537Sgber mp->mnt_iosize_max = MAXPHYS; 1346235537Sgber 1347235537Sgber from = vfs_getopts(mp->mnt_optnew, "from", &error); 1348235537Sgber if (error) 1349235537Sgber goto error; 1350235537Sgber 1351235537Sgber error = vfs_getopt(mp->mnt_optnew, "snap", (void **)&cpno, NULL); 1352235537Sgber if (error == ENOENT) 1353235537Sgber cpno = NULL; 1354235537Sgber else if (error) 1355235537Sgber goto error; 1356235537Sgber 1357235537Sgber args = (struct nandfs_args *)malloc(sizeof(struct nandfs_args), 1358235537Sgber M_NANDFSMNT, M_WAITOK | M_ZERO); 1359235537Sgber 1360235537Sgber if (cpno != NULL) 1361235537Sgber args->cpno = strtoul(cpno, (char **)NULL, 10); 1362235537Sgber else 1363235537Sgber args->cpno = 0; 1364235537Sgber args->fspec = from; 1365235537Sgber 1366235537Sgber if (args->cpno != 0 && !ronly) { 1367235537Sgber error = EROFS; 1368235537Sgber goto error; 1369235537Sgber } 1370235537Sgber 1371235537Sgber printf("WARNING: NANDFS is considered to be a highly experimental " 1372235537Sgber "feature in FreeBSD.\n"); 1373235537Sgber 1374235537Sgber error = nandfs_mount_device(devvp, mp, args, &nandfsdev); 1375235537Sgber if (error) 1376235537Sgber goto error; 1377235537Sgber 1378235537Sgber nmp = (struct nandfsmount *) malloc(sizeof(struct nandfsmount), 1379235537Sgber M_NANDFSMNT, M_WAITOK | M_ZERO); 1380235537Sgber 1381235537Sgber mp->mnt_data = nmp; 1382235537Sgber nmp->nm_vfs_mountp = mp; 1383235537Sgber nmp->nm_ronly = ronly; 1384235537Sgber MNT_ILOCK(mp); 1385235537Sgber mp->mnt_flag |= MNT_LOCAL; 1386235537Sgber MNT_IUNLOCK(mp); 1387235537Sgber nmp->nm_nandfsdev = nandfsdev; 1388235537Sgber /* Add our mountpoint */ 1389235537Sgber STAILQ_INSERT_TAIL(&nandfsdev->nd_mounts, nmp, nm_next_mount); 1390235537Sgber 1391235537Sgber if (args->cpno > nandfsdev->nd_last_cno) { 1392235537Sgber printf("WARNING: supplied checkpoint number (%jd) is greater " 1393235537Sgber "than last known checkpoint on filesystem (%jd). Mounting" 1394235537Sgber " checkpoint %jd\n", (uintmax_t)args->cpno, 1395235537Sgber (uintmax_t)nandfsdev->nd_last_cno, 1396235537Sgber (uintmax_t)nandfsdev->nd_last_cno); 1397235537Sgber args->cpno = nandfsdev->nd_last_cno; 1398235537Sgber } 1399235537Sgber 1400235537Sgber /* Setting up other parameters */ 1401235537Sgber nmp->nm_mount_args = *args; 1402235537Sgber free(args, M_NANDFSMNT); 1403235537Sgber error = nandfs_mount_checkpoint(nmp); 1404235537Sgber if (error) { 1405235537Sgber nandfs_unmount(mp, MNT_FORCE); 1406235537Sgber goto unmounted; 1407235537Sgber } 1408235537Sgber 1409235537Sgber if (!ronly) { 1410235537Sgber error = start_syncer(nmp); 1411235537Sgber if (error == 0) 1412235537Sgber error = nandfs_start_cleaner(nmp->nm_nandfsdev); 1413235537Sgber if (error) 1414235537Sgber nandfs_unmount(mp, MNT_FORCE); 1415235537Sgber } 1416235537Sgber 1417235537Sgber return (0); 1418235537Sgber 1419235537Sgbererror: 1420235537Sgber if (args != NULL) 1421235537Sgber free(args, M_NANDFSMNT); 1422235537Sgber 1423235537Sgber if (nmp != NULL) { 1424235537Sgber free(nmp, M_NANDFSMNT); 1425235537Sgber mp->mnt_data = NULL; 1426235537Sgber } 1427235537Sgberunmounted: 1428235537Sgber return (error); 1429235537Sgber} 1430235537Sgber 1431235537Sgberstatic int 1432235537Sgbernandfs_unmount(struct mount *mp, int mntflags) 1433235537Sgber{ 1434235537Sgber struct nandfs_device *nandfsdev; 1435235537Sgber struct nandfsmount *nmp; 1436235537Sgber int error; 1437235537Sgber int flags = 0; 1438235537Sgber 1439235537Sgber DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp)); 1440235537Sgber 1441235537Sgber if (mntflags & MNT_FORCE) 1442235537Sgber flags |= FORCECLOSE; 1443235537Sgber 1444235537Sgber nmp = mp->mnt_data; 1445235537Sgber nandfsdev = nmp->nm_nandfsdev; 1446235537Sgber 1447235537Sgber error = vflush(mp, 0, flags | SKIPSYSTEM, curthread); 1448235537Sgber if (error) 1449235537Sgber return (error); 1450235537Sgber 1451235537Sgber if (!(nmp->nm_ronly)) { 1452235537Sgber nandfs_stop_cleaner(nandfsdev); 1453235537Sgber stop_syncer(nmp); 1454235537Sgber } 1455235537Sgber 1456235537Sgber if (nmp->nm_ifile_node) 1457235537Sgber NANDFS_UNSET_SYSTEMFILE(NTOV(nmp->nm_ifile_node)); 1458235537Sgber 1459235537Sgber /* Remove our mount point */ 1460235537Sgber STAILQ_REMOVE(&nandfsdev->nd_mounts, nmp, nandfsmount, nm_next_mount); 1461235537Sgber 1462235537Sgber /* Unmount the device itself when we're the last one */ 1463235537Sgber nandfs_unmount_device(nandfsdev); 1464235537Sgber 1465235537Sgber free_nandfs_mountinfo(mp); 1466235537Sgber 1467235537Sgber /* 1468235537Sgber * Finally, throw away the null_mount structure 1469235537Sgber */ 1470235537Sgber mp->mnt_data = 0; 1471235537Sgber MNT_ILOCK(mp); 1472235537Sgber mp->mnt_flag &= ~MNT_LOCAL; 1473235537Sgber MNT_IUNLOCK(mp); 1474235537Sgber 1475235537Sgber return (0); 1476235537Sgber} 1477235537Sgber 1478235537Sgberstatic int 1479235537Sgbernandfs_statfs(struct mount *mp, struct statfs *sbp) 1480235537Sgber{ 1481235537Sgber struct nandfsmount *nmp; 1482235537Sgber struct nandfs_device *nandfsdev; 1483235537Sgber struct nandfs_fsdata *fsdata; 1484235537Sgber struct nandfs_super_block *sb; 1485235537Sgber struct nandfs_block_group_desc *groups; 1486235537Sgber struct nandfs_node *ifile; 1487235537Sgber struct nandfs_mdt *mdt; 1488235537Sgber struct buf *bp; 1489235537Sgber int i, error; 1490235537Sgber uint32_t entries_per_group; 1491235537Sgber uint64_t files = 0; 1492235537Sgber 1493235537Sgber nmp = mp->mnt_data; 1494235537Sgber nandfsdev = nmp->nm_nandfsdev; 1495235537Sgber fsdata = &nandfsdev->nd_fsdata; 1496235537Sgber sb = &nandfsdev->nd_super; 1497235537Sgber ifile = nmp->nm_ifile_node; 1498235537Sgber mdt = &nandfsdev->nd_ifile_mdt; 1499235537Sgber entries_per_group = mdt->entries_per_group; 1500235537Sgber 1501235537Sgber VOP_LOCK(NTOV(ifile), LK_SHARED); 1502235537Sgber error = nandfs_bread(ifile, 0, NOCRED, 0, &bp); 1503235537Sgber if (error) { 1504235537Sgber brelse(bp); 1505235537Sgber VOP_UNLOCK(NTOV(ifile), 0); 1506235537Sgber return (error); 1507235537Sgber } 1508235537Sgber 1509235537Sgber groups = (struct nandfs_block_group_desc *)bp->b_data; 1510235537Sgber 1511235537Sgber for (i = 0; i < mdt->groups_per_desc_block; i++) 1512235537Sgber files += (entries_per_group - groups[i].bg_nfrees); 1513235537Sgber 1514235537Sgber brelse(bp); 1515235537Sgber VOP_UNLOCK(NTOV(ifile), 0); 1516235537Sgber 1517235537Sgber sbp->f_bsize = nandfsdev->nd_blocksize; 1518235537Sgber sbp->f_iosize = sbp->f_bsize; 1519235537Sgber sbp->f_blocks = fsdata->f_blocks_per_segment * fsdata->f_nsegments; 1520235537Sgber sbp->f_bfree = sb->s_free_blocks_count; 1521235537Sgber sbp->f_bavail = sbp->f_bfree; 1522235537Sgber sbp->f_files = files; 1523235537Sgber sbp->f_ffree = 0; 1524235537Sgber return (0); 1525235537Sgber} 1526235537Sgber 1527235537Sgberstatic int 1528235537Sgbernandfs_root(struct mount *mp, int flags, struct vnode **vpp) 1529235537Sgber{ 1530235537Sgber struct nandfsmount *nmp = VFSTONANDFS(mp); 1531235537Sgber struct nandfs_node *node; 1532235537Sgber int error; 1533235537Sgber 1534235537Sgber error = nandfs_get_node(nmp, NANDFS_ROOT_INO, &node); 1535235537Sgber if (error) 1536235537Sgber return (error); 1537235537Sgber 1538235537Sgber KASSERT(NTOV(node)->v_vflag & VV_ROOT, 1539235537Sgber ("root_vp->v_vflag & VV_ROOT")); 1540235537Sgber 1541235537Sgber *vpp = NTOV(node); 1542235537Sgber 1543235537Sgber return (error); 1544235537Sgber} 1545235537Sgber 1546235537Sgberstatic int 1547235537Sgbernandfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) 1548235537Sgber{ 1549235537Sgber struct nandfsmount *nmp = VFSTONANDFS(mp); 1550235537Sgber struct nandfs_node *node; 1551235537Sgber int error; 1552235537Sgber 1553235537Sgber error = nandfs_get_node(nmp, ino, &node); 1554235537Sgber if (node) 1555235537Sgber *vpp = NTOV(node); 1556235537Sgber 1557235537Sgber return (error); 1558235537Sgber} 1559235537Sgber 1560235537Sgberstatic int 1561235537Sgbernandfs_sync(struct mount *mp, int waitfor) 1562235537Sgber{ 1563235537Sgber struct nandfsmount *nmp = VFSTONANDFS(mp); 1564235537Sgber 1565235537Sgber DPRINTF(SYNC, ("%s: mp %p waitfor %d\n", __func__, mp, waitfor)); 1566235537Sgber 1567235537Sgber /* 1568235537Sgber * XXX: A hack to be removed soon 1569235537Sgber */ 1570235537Sgber if (waitfor == MNT_LAZY) 1571235537Sgber return (0); 1572235537Sgber if (waitfor == MNT_SUSPEND) 1573235537Sgber return (0); 1574235537Sgber nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_VFS_SYNC); 1575235537Sgber return (0); 1576235537Sgber} 1577235537Sgber 1578235537Sgberstatic struct vfsops nandfs_vfsops = { 1579235537Sgber .vfs_init = nandfs_init, 1580235537Sgber .vfs_mount = nandfs_mount, 1581235537Sgber .vfs_root = nandfs_root, 1582235537Sgber .vfs_statfs = nandfs_statfs, 1583235537Sgber .vfs_uninit = nandfs_uninit, 1584235537Sgber .vfs_unmount = nandfs_unmount, 1585235537Sgber .vfs_vget = nandfs_vget, 1586235537Sgber .vfs_sync = nandfs_sync, 1587235537Sgber}; 1588235537Sgber 1589235537SgberVFS_SET(nandfs_vfsops, nandfs, VFCF_LOOPBACK); 1590