From 6f7c7353004e2ff9709b326a4008ce8ea63d9270 Mon Sep 17 00:00:00 2001 From: gber Date: Thu, 17 May 2012 10:11:18 +0000 Subject: Import work done under project/nand (@235533) into head. The NAND Flash environment consists of several distinct components: - NAND framework (drivers harness for NAND controllers and NAND chips) - NAND simulator (NANDsim) - NAND file system (NAND FS) - Companion tools and utilities - Documentation (manual pages) This work is still experimental. Please use with caution. Obtained from: Semihalf Supported by: FreeBSD Foundation, Juniper Networks --- etc/mtree/BSD.include.dist | 4 + include/Makefile | 20 +- lib/Makefile | 5 + lib/libnandfs/Makefile | 9 + lib/libnandfs/libnandfs.h | 65 + lib/libnandfs/nandfs.c | 247 ++++ lib/libstand/Makefile | 3 + lib/libstand/nandfs.c | 1041 +++++++++++++++ lib/libstand/stand.h | 1 + sbin/Makefile | 5 + sbin/nandfs/Makefile | 10 + sbin/nandfs/lssnap.c | 112 ++ sbin/nandfs/mksnap.c | 80 ++ sbin/nandfs/nandfs.8 | 75 ++ sbin/nandfs/nandfs.c | 74 ++ sbin/nandfs/nandfs.h | 40 + sbin/nandfs/rmsnap.c | 87 ++ sbin/newfs_nandfs/Makefile | 9 + sbin/newfs_nandfs/newfs_nandfs.8 | 77 ++ sbin/newfs_nandfs/newfs_nandfs.c | 1176 +++++++++++++++++ share/man/man4/Makefile | 2 + share/man/man4/nand.4 | 140 ++ share/man/man4/nandsim.4 | 184 +++ share/man/man5/Makefile | 4 + share/man/man5/nandfs.5 | 129 ++ share/mk/bsd.own.mk | 1 + sys/boot/arm/uboot/Makefile | 8 + sys/boot/arm/uboot/conf.c | 3 + sys/boot/arm/uboot/version | 1 + sys/boot/i386/loader/Makefile | 3 + sys/boot/i386/loader/conf.c | 3 + sys/conf/files | 29 + sys/conf/options | 1 + sys/dev/nand/nand.c | 832 ++++++++++++ sys/dev/nand/nand.h | 385 ++++++ sys/dev/nand/nand_bbt.c | 273 ++++ sys/dev/nand/nand_cdev.c | 413 ++++++ sys/dev/nand/nand_dev.h | 90 ++ sys/dev/nand/nand_ecc_pos.h | 56 + sys/dev/nand/nand_generic.c | 1320 +++++++++++++++++++ sys/dev/nand/nand_geom.c | 414 ++++++ sys/dev/nand/nand_id.c | 60 + sys/dev/nand/nand_if.m | 168 +++ sys/dev/nand/nandbus.c | 530 ++++++++ sys/dev/nand/nandbus.h | 49 + sys/dev/nand/nandbus_if.m | 100 ++ sys/dev/nand/nandsim.c | 665 ++++++++++ sys/dev/nand/nandsim.h | 175 +++ sys/dev/nand/nandsim_chip.c | 901 +++++++++++++ sys/dev/nand/nandsim_chip.h | 159 +++ sys/dev/nand/nandsim_ctrl.c | 396 ++++++ sys/dev/nand/nandsim_log.c | 186 +++ sys/dev/nand/nandsim_log.h | 52 + sys/dev/nand/nandsim_swap.c | 389 ++++++ sys/dev/nand/nandsim_swap.h | 64 + sys/dev/nand/nfc_if.m | 165 +++ sys/dev/nand/nfc_mv.c | 236 ++++ sys/fs/nandfs/bmap.c | 621 +++++++++ sys/fs/nandfs/bmap.h | 40 + sys/fs/nandfs/nandfs.h | 310 +++++ sys/fs/nandfs/nandfs_alloc.c | 364 ++++++ sys/fs/nandfs/nandfs_bmap.c | 230 ++++ sys/fs/nandfs/nandfs_buffer.c | 83 ++ sys/fs/nandfs/nandfs_cleaner.c | 621 +++++++++ sys/fs/nandfs/nandfs_cpfile.c | 776 +++++++++++ sys/fs/nandfs/nandfs_dat.c | 344 +++++ sys/fs/nandfs/nandfs_dir.c | 314 +++++ sys/fs/nandfs/nandfs_fs.h | 565 ++++++++ sys/fs/nandfs/nandfs_ifile.c | 213 +++ sys/fs/nandfs/nandfs_mount.h | 50 + sys/fs/nandfs/nandfs_segment.c | 1329 +++++++++++++++++++ sys/fs/nandfs/nandfs_subr.c | 1120 ++++++++++++++++ sys/fs/nandfs/nandfs_subr.h | 238 ++++ sys/fs/nandfs/nandfs_sufile.c | 569 ++++++++ sys/fs/nandfs/nandfs_vfsops.c | 1590 +++++++++++++++++++++++ sys/fs/nandfs/nandfs_vnops.c | 2455 +++++++++++++++++++++++++++++++++++ sys/modules/Makefile | 10 +- sys/modules/nandfs/Makefile | 12 + sys/modules/nandsim/Makefile | 11 + tools/build/options/WITHOUT_NAND | 2 + tools/build/options/WITH_NAND | 2 + usr.sbin/Makefile | 5 + usr.sbin/nandsim/Makefile | 8 + usr.sbin/nandsim/nandsim.8 | 230 ++++ usr.sbin/nandsim/nandsim.c | 1397 ++++++++++++++++++++ usr.sbin/nandsim/nandsim_cfgparse.c | 957 ++++++++++++++ usr.sbin/nandsim/nandsim_cfgparse.h | 86 ++ usr.sbin/nandsim/nandsim_rcfile.c | 440 +++++++ usr.sbin/nandsim/nandsim_rcfile.h | 70 + usr.sbin/nandsim/sample.conf | 174 +++ usr.sbin/nandtool/Makefile | 11 + usr.sbin/nandtool/nand_erase.c | 114 ++ usr.sbin/nandtool/nand_info.c | 86 ++ usr.sbin/nandtool/nand_read.c | 139 ++ usr.sbin/nandtool/nand_readoob.c | 111 ++ usr.sbin/nandtool/nand_write.c | 143 ++ usr.sbin/nandtool/nand_writeoob.c | 113 ++ usr.sbin/nandtool/nandtool.8 | 188 +++ usr.sbin/nandtool/nandtool.c | 283 ++++ usr.sbin/nandtool/nandtool.h | 57 + usr.sbin/nandtool/usage.h | 112 ++ 101 files changed, 28313 insertions(+), 6 deletions(-) create mode 100644 lib/libnandfs/Makefile create mode 100644 lib/libnandfs/libnandfs.h create mode 100644 lib/libnandfs/nandfs.c create mode 100644 lib/libstand/nandfs.c create mode 100644 sbin/nandfs/Makefile create mode 100644 sbin/nandfs/lssnap.c create mode 100644 sbin/nandfs/mksnap.c create mode 100644 sbin/nandfs/nandfs.8 create mode 100644 sbin/nandfs/nandfs.c create mode 100644 sbin/nandfs/nandfs.h create mode 100644 sbin/nandfs/rmsnap.c create mode 100644 sbin/newfs_nandfs/Makefile create mode 100644 sbin/newfs_nandfs/newfs_nandfs.8 create mode 100644 sbin/newfs_nandfs/newfs_nandfs.c create mode 100644 share/man/man4/nand.4 create mode 100644 share/man/man4/nandsim.4 create mode 100644 share/man/man5/nandfs.5 create mode 100644 sys/dev/nand/nand.c create mode 100644 sys/dev/nand/nand.h create mode 100644 sys/dev/nand/nand_bbt.c create mode 100644 sys/dev/nand/nand_cdev.c create mode 100644 sys/dev/nand/nand_dev.h create mode 100644 sys/dev/nand/nand_ecc_pos.h create mode 100644 sys/dev/nand/nand_generic.c create mode 100644 sys/dev/nand/nand_geom.c create mode 100644 sys/dev/nand/nand_id.c create mode 100644 sys/dev/nand/nand_if.m create mode 100644 sys/dev/nand/nandbus.c create mode 100644 sys/dev/nand/nandbus.h create mode 100644 sys/dev/nand/nandbus_if.m create mode 100644 sys/dev/nand/nandsim.c create mode 100644 sys/dev/nand/nandsim.h create mode 100644 sys/dev/nand/nandsim_chip.c create mode 100644 sys/dev/nand/nandsim_chip.h create mode 100644 sys/dev/nand/nandsim_ctrl.c create mode 100644 sys/dev/nand/nandsim_log.c create mode 100644 sys/dev/nand/nandsim_log.h create mode 100644 sys/dev/nand/nandsim_swap.c create mode 100644 sys/dev/nand/nandsim_swap.h create mode 100644 sys/dev/nand/nfc_if.m create mode 100644 sys/dev/nand/nfc_mv.c create mode 100644 sys/fs/nandfs/bmap.c create mode 100644 sys/fs/nandfs/bmap.h create mode 100644 sys/fs/nandfs/nandfs.h create mode 100644 sys/fs/nandfs/nandfs_alloc.c create mode 100644 sys/fs/nandfs/nandfs_bmap.c create mode 100644 sys/fs/nandfs/nandfs_buffer.c create mode 100644 sys/fs/nandfs/nandfs_cleaner.c create mode 100644 sys/fs/nandfs/nandfs_cpfile.c create mode 100644 sys/fs/nandfs/nandfs_dat.c create mode 100644 sys/fs/nandfs/nandfs_dir.c create mode 100644 sys/fs/nandfs/nandfs_fs.h create mode 100644 sys/fs/nandfs/nandfs_ifile.c create mode 100644 sys/fs/nandfs/nandfs_mount.h create mode 100644 sys/fs/nandfs/nandfs_segment.c create mode 100644 sys/fs/nandfs/nandfs_subr.c create mode 100644 sys/fs/nandfs/nandfs_subr.h create mode 100644 sys/fs/nandfs/nandfs_sufile.c create mode 100644 sys/fs/nandfs/nandfs_vfsops.c create mode 100644 sys/fs/nandfs/nandfs_vnops.c create mode 100644 sys/modules/nandfs/Makefile create mode 100644 sys/modules/nandsim/Makefile create mode 100644 tools/build/options/WITHOUT_NAND create mode 100644 tools/build/options/WITH_NAND create mode 100644 usr.sbin/nandsim/Makefile create mode 100644 usr.sbin/nandsim/nandsim.8 create mode 100644 usr.sbin/nandsim/nandsim.c create mode 100644 usr.sbin/nandsim/nandsim_cfgparse.c create mode 100644 usr.sbin/nandsim/nandsim_cfgparse.h create mode 100644 usr.sbin/nandsim/nandsim_rcfile.c create mode 100644 usr.sbin/nandsim/nandsim_rcfile.h create mode 100644 usr.sbin/nandsim/sample.conf create mode 100644 usr.sbin/nandtool/Makefile create mode 100644 usr.sbin/nandtool/nand_erase.c create mode 100644 usr.sbin/nandtool/nand_info.c create mode 100644 usr.sbin/nandtool/nand_read.c create mode 100644 usr.sbin/nandtool/nand_readoob.c create mode 100644 usr.sbin/nandtool/nand_write.c create mode 100644 usr.sbin/nandtool/nand_writeoob.c create mode 100644 usr.sbin/nandtool/nandtool.8 create mode 100644 usr.sbin/nandtool/nandtool.c create mode 100644 usr.sbin/nandtool/nandtool.h create mode 100644 usr.sbin/nandtool/usage.h diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 2de9e84..c2a0e91 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -122,6 +122,8 @@ mpilib .. .. + nand + .. ofw .. pbio @@ -154,6 +156,8 @@ .. msdosfs .. + nandfs + .. nfs .. ntfs diff --git a/include/Makefile b/include/Makefile index 2cbc9d2..c67dc75 100644 --- a/include/Makefile +++ b/include/Makefile @@ -43,7 +43,7 @@ LSUBDIRS= cam/ata cam/scsi \ dev/ic dev/iicbus ${_dev_ieee488} dev/io dev/lmc dev/mfi dev/ofw \ dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus \ dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \ - fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/ntfs fs/nullfs \ + fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/ntfs fs/nullfs \ ${_fs_nwfs} fs/portalfs fs/procfs fs/smbfs fs/udf fs/unionfs \ geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \ geom/mirror geom/mountver geom/multipath geom/nop \ @@ -157,7 +157,7 @@ copies: done .endif .endfor -.for i in ${LDIRS} ${LSUBDIRS:Ndev/acpica:Ndev/bktr} ${LSUBSUBDIRS} +.for i in ${LDIRS} ${LSUBDIRS:Ndev/acpica:Ndev/bktr:Ndev/nand} ${LSUBSUBDIRS} cd ${.CURDIR}/../sys; \ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 $i/*.h \ ${DESTDIR}${INCLUDEDIR}/$i @@ -168,6 +168,13 @@ copies: cd ${.CURDIR}/../sys/dev/bktr; \ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ioctl_*.h \ ${DESTDIR}${INCLUDEDIR}/dev/bktr +.if ${MK_NAND} != "no" + cd ${.CURDIR}/../sys/dev/nand; \ + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 nandsim.h \ + ${DESTDIR}${INCLUDEDIR}/dev/nand; \ + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 nand_dev.h \ + ${DESTDIR}${INCLUDEDIR}/dev/nand +.endif cd ${.CURDIR}/../sys/contrib/altq/altq; \ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 *.h \ ${DESTDIR}${INCLUDEDIR}/altq @@ -224,7 +231,7 @@ symlinks: ln -fs ../../../sys/$i/$$h ${DESTDIR}${INCLUDEDIR}/$i; \ done .endfor -.for i in ${LSUBDIRS:Ndev/acpica:Ndev/bktr} +.for i in ${LSUBDIRS:Ndev/acpica:Ndev/bktr:Ndev/nand} cd ${.CURDIR}/../sys/$i; \ for h in *.h; do \ ln -fs ../../../../sys/$i/$$h ${DESTDIR}${INCLUDEDIR}/$i; \ @@ -240,6 +247,13 @@ symlinks: ln -fs ../../../../sys/dev/bktr/$$h \ ${DESTDIR}${INCLUDEDIR}/dev/bktr; \ done +.if ${MK_NAND} != "no" + cd ${.CURDIR}/../sys/dev/nand; \ + for h in nandsim.h nand_dev.h; do \ + ln -fs ../../../../sys/dev/nand/$$h \ + ${DESTDIR}${INCLUDEDIR}/dev/nand; \ + done +.endif .for i in ${LSUBSUBDIRS} cd ${.CURDIR}/../sys/$i; \ for h in *.h; do \ diff --git a/lib/Makefile b/lib/Makefile index 2bff80a..a366f5f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -88,6 +88,7 @@ SUBDIR= ${SUBDIR_ORDERED} \ libmemstat \ ${_libmilter} \ ${_libmp} \ + ${_libnandfs} \ ${_libncp} \ ${_libngatm} \ libopie \ @@ -175,6 +176,10 @@ _libipx= libipx _libthr= libthr .endif +.if ${MK_NAND} != "no" +_libnandfs= libnandfs +.endif + .if ${MK_NETGRAPH} != "no" _libnetgraph= libnetgraph .endif diff --git a/lib/libnandfs/Makefile b/lib/libnandfs/Makefile new file mode 100644 index 0000000..d87573e --- /dev/null +++ b/lib/libnandfs/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +LIB= nandfs +SRCS+= nandfs.c +INCS= libnandfs.h + +CFLAGS += -I${.CURDIR} + +.include diff --git a/lib/libnandfs/libnandfs.h b/lib/libnandfs/libnandfs.h new file mode 100644 index 0000000..8338c63 --- /dev/null +++ b/lib/libnandfs/libnandfs.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LIBNANDFS_NANDFS_H +#define _LIBNANDFS_NANDFS_H + +struct nandfs { + struct nandfs_fsdata n_fsdata; + struct nandfs_super_block n_sb; + char n_ioc[MNAMELEN]; + char n_dev[MNAMELEN]; + int n_iocfd; + int n_devfd; + int n_flags; + char n_errmsg[120]; +}; + +int nandfs_iserror(struct nandfs *); +const char *nandfs_errmsg(struct nandfs *); + +void nandfs_init(struct nandfs *, const char *); +void nandfs_destroy(struct nandfs *); + +const char *nandfs_dev(struct nandfs *); + +int nandfs_open(struct nandfs *); +void nandfs_close(struct nandfs *); + +int nandfs_get_cpstat(struct nandfs *, struct nandfs_cpstat *); + +ssize_t nandfs_get_cp(struct nandfs *, uint64_t, + struct nandfs_cpinfo *, size_t); + +ssize_t nandfs_get_snap(struct nandfs *, uint64_t, + struct nandfs_cpinfo *, size_t); + +int nandfs_make_snap(struct nandfs *, uint64_t *); +int nandfs_delete_snap(struct nandfs *, uint64_t); + +#endif /* _LIBNANDFS_NANDFS_H */ diff --git a/lib/libnandfs/nandfs.c b/lib/libnandfs/nandfs.c new file mode 100644 index 0000000..fd59a92 --- /dev/null +++ b/lib/libnandfs/nandfs.c @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NANDFS_IS_VALID 0x1 +#define NANDFS_IS_OPENED 0x2 +#define NANDFS_IS_OPENED_DEV 0x4 +#define NANDFS_IS_ERROR 0x8 + +#define DEBUG +#undef DEBUG +#ifdef DEBUG +#define NANDFS_DEBUG(fmt, args...) do { \ + printf("libnandfs:" fmt "\n", ##args); } while (0) +#else +#define NANDFS_DEBUG(fmt, args...) +#endif + +#define NANDFS_ASSERT_VALID(fs) assert((fs)->n_flags & NANDFS_IS_VALID) +#define NANDFS_ASSERT_VALID_DEV(fs) \ + assert(((fs)->n_flags & (NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV)) == \ + (NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV)) + +int +nandfs_iserror(struct nandfs *fs) +{ + + NANDFS_ASSERT_VALID(fs); + + return (fs->n_flags & NANDFS_IS_ERROR); +} + +const char * +nandfs_errmsg(struct nandfs *fs) +{ + + NANDFS_ASSERT_VALID(fs); + + assert(nandfs_iserror(fs)); + assert(fs->n_errmsg); + return (fs->n_errmsg); +} + +static void +nandfs_seterr(struct nandfs *fs, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(fs->n_errmsg, sizeof(fs->n_errmsg), fmt, ap); + va_end(ap); + fs->n_flags |= NANDFS_IS_ERROR; +} + +const char * +nandfs_dev(struct nandfs *fs) +{ + + NANDFS_ASSERT_VALID(fs); + return (fs->n_dev); +} + +void +nandfs_init(struct nandfs *fs, const char *dir) +{ + + snprintf(fs->n_ioc, sizeof(fs->n_ioc), "%s/%s", dir, "."); + fs->n_iocfd = -1; + fs->n_flags = NANDFS_IS_VALID; +} + +void +nandfs_destroy(struct nandfs *fs) +{ + + assert(fs->n_iocfd == -1); + fs->n_flags &= + ~(NANDFS_IS_ERROR | NANDFS_IS_VALID); + assert(fs->n_flags == 0); +} + +int +nandfs_open(struct nandfs *fs) +{ + struct nandfs_fsinfo fsinfo; + + fs->n_flags |= NANDFS_IS_OPENED; + + fs->n_iocfd = open(fs->n_ioc, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP | S_IROTH | S_IWOTH); + if (fs->n_iocfd == -1) { + nandfs_seterr(fs, "couldn't open %s: %s", fs->n_ioc, + strerror(errno)); + return (-1); + } + + if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_FSINFO, &fsinfo) == -1) { + nandfs_seterr(fs, "couldn't fetch fsinfo: %s", + strerror(errno)); + return (-1); + } + + memcpy(&fs->n_fsdata, &fsinfo.fs_fsdata, sizeof(fs->n_fsdata)); + memcpy(&fs->n_sb, &fsinfo.fs_super, sizeof(fs->n_sb)); + snprintf(fs->n_dev, sizeof(fs->n_dev), "%s", fsinfo.fs_dev); + + return (0); +} + +void +nandfs_close(struct nandfs *fs) +{ + + NANDFS_ASSERT_VALID(fs); + assert(fs->n_flags & NANDFS_IS_OPENED); + + close(fs->n_iocfd); + fs->n_iocfd = -1; + fs->n_flags &= ~NANDFS_IS_OPENED; +} + +int +nandfs_get_cpstat(struct nandfs *fs, struct nandfs_cpstat *cpstat) +{ + + NANDFS_ASSERT_VALID(fs); + + if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPSTAT, cpstat) == -1) { + nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPSTAT: %s", + strerror(errno)); + return (-1); + } + + return (0); +} + +static ssize_t +nandfs_get_cpinfo(struct nandfs *fs, uint64_t cno, int mode, + struct nandfs_cpinfo *cpinfo, size_t nci) +{ + struct nandfs_argv args; + + NANDFS_ASSERT_VALID(fs); + + args.nv_base = (u_long)cpinfo; + args.nv_nmembs = nci; + args.nv_index = cno; + args.nv_flags = mode; + + if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPINFO, &args) == -1) { + nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPINFO: %s", + strerror(errno)); + return (-1); + } + + return (args.nv_nmembs); +} + +ssize_t +nandfs_get_cp(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo, + size_t nci) +{ + + return (nandfs_get_cpinfo(fs, cno, NANDFS_CHECKPOINT, cpinfo, nci)); +} + +ssize_t +nandfs_get_snap(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo, + size_t nci) +{ + + return (nandfs_get_cpinfo(fs, cno, NANDFS_SNAPSHOT, cpinfo, nci)); +} + +int +nandfs_make_snap(struct nandfs *fs, uint64_t *cno) +{ + + NANDFS_ASSERT_VALID(fs); + + if (ioctl(fs->n_iocfd, NANDFS_IOCTL_MAKE_SNAP, cno) == -1) { + nandfs_seterr(fs, "ioctl NANDFS_IOCTL_MAKE_SNAP: %s", + strerror(errno)); + return (-1); + } + + return (0); +} + +int +nandfs_delete_snap(struct nandfs *fs, uint64_t cno) +{ + + NANDFS_ASSERT_VALID(fs); + + if (ioctl(fs->n_iocfd, NANDFS_IOCTL_DELETE_SNAP, &cno) == -1) { + nandfs_seterr(fs, "ioctl NANDFS_IOCTL_DELETE_SNAP: %s", + strerror(errno)); + return (-1); + } + + return (0); +} diff --git a/lib/libstand/Makefile b/lib/libstand/Makefile index 567c886..0ad8fa5 100644 --- a/lib/libstand/Makefile +++ b/lib/libstand/Makefile @@ -150,6 +150,9 @@ SRCS+= bootp.c rarp.c bootparam.c SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c SRCS+= dosfs.c ext2fs.c SRCS+= splitfs.c +.if ${MK_NAND} != "no" +SRCS+= nandfs.c +.endif .include diff --git a/lib/libstand/nandfs.c b/lib/libstand/nandfs.c new file mode 100644 index 0000000..67e2fea --- /dev/null +++ b/lib/libstand/nandfs.c @@ -0,0 +1,1041 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include "stand.h" +#include "string.h" +#include "zlib.h" + +#define DEBUG +#undef DEBUG +#ifdef DEBUG +#define NANDFS_DEBUG(fmt, args...) do { \ + printf("NANDFS_DEBUG:" fmt "\n", ##args); } while (0) +#else +#define NANDFS_DEBUG(fmt, args...) +#endif + +struct nandfs_mdt { + uint32_t entries_per_block; + uint32_t entries_per_group; + uint32_t blocks_per_group; + uint32_t groups_per_desc_block; /* desc is super group */ + uint32_t blocks_per_desc_block; /* desc is super group */ +}; + +struct bmap_buf { + LIST_ENTRY(bmap_buf) list; + nandfs_daddr_t blknr; + uint64_t *map; +}; + +struct nandfs_node { + struct nandfs_inode *inode; + LIST_HEAD(, bmap_buf) bmap_bufs; +}; +struct nandfs { + int nf_blocksize; + int nf_sectorsize; + int nf_cpno; + + struct open_file *nf_file; + struct nandfs_node *nf_opened_node; + u_int nf_offset; + uint8_t *nf_buf; + int64_t nf_buf_blknr; + + struct nandfs_fsdata *nf_fsdata; + struct nandfs_super_block *nf_sb; + struct nandfs_segment_summary nf_segsum; + struct nandfs_checkpoint nf_checkpoint; + struct nandfs_super_root nf_sroot; + struct nandfs_node nf_ifile; + struct nandfs_node nf_datfile; + struct nandfs_node nf_cpfile; + struct nandfs_mdt nf_datfile_mdt; + struct nandfs_mdt nf_ifile_mdt; + + int nf_nindir[NIADDR]; +}; + +static int nandfs_open(const char *, struct open_file *); +static int nandfs_close(struct open_file *); +static int nandfs_read(struct open_file *, void *, size_t, size_t *); +static off_t nandfs_seek(struct open_file *, off_t, int); +static int nandfs_stat(struct open_file *, struct stat *); +static int nandfs_readdir(struct open_file *, struct dirent *); + +static int nandfs_buf_read(struct nandfs *, char **, size_t *); +static struct nandfs_node *nandfs_lookup_inode(struct nandfs *, nandfs_daddr_t); +static struct nandfs_node *nandfs_lookup_path(struct nandfs *, const char *); +static int nandfs_read_inode(struct nandfs *, struct nandfs_node *, + nandfs_lbn_t, u_int, void *, int); +static int nandfs_read_blk(struct nandfs *, nandfs_daddr_t, void *, int); +static int nandfs_bmap_lookup(struct nandfs *, struct nandfs_node *, + nandfs_lbn_t, nandfs_daddr_t *, int); +static int nandfs_get_checkpoint(struct nandfs *, uint64_t, + struct nandfs_checkpoint *); +static nandfs_daddr_t nandfs_vtop(struct nandfs *, nandfs_daddr_t); +static void nandfs_calc_mdt_consts(int, struct nandfs_mdt *, int); +static void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t, + nandfs_daddr_t *, uint32_t *); +static int ioread(struct open_file *, off_t, void *, u_int); +static int nandfs_probe_sectorsize(struct open_file *); + +struct fs_ops nandfs_fsops = { + "nandfs", + nandfs_open, + nandfs_close, + nandfs_read, + null_write, + nandfs_seek, + nandfs_stat, + nandfs_readdir +}; + +#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t)) + +static int +nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata) +{ + uint32_t fsdata_crc, comp_crc; + + if (fsdata->f_magic != NANDFS_FSDATA_MAGIC) + return (0); + + /* Preserve crc */ + fsdata_crc = fsdata->f_sum; + + /* Calculate */ + fsdata->f_sum = (0); + comp_crc = crc32(0, (uint8_t *)fsdata, fsdata->f_bytes); + + /* Restore */ + fsdata->f_sum = fsdata_crc; + + /* Check CRC */ + return (fsdata_crc == comp_crc); +} + +static int +nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata, + struct nandfs_super_block *super) +{ + uint32_t super_crc, comp_crc; + + /* Check super block magic */ + if (super->s_magic != NANDFS_SUPER_MAGIC) + return (0); + + /* Preserve CRC */ + super_crc = super->s_sum; + + /* Calculate */ + super->s_sum = (0); + comp_crc = crc32(0, (uint8_t *)super, fsdata->f_sbbytes); + + /* Restore */ + super->s_sum = super_crc; + + /* Check CRC */ + return (super_crc == comp_crc); +} + +static int +nandfs_find_super_block(struct nandfs *fs, struct open_file *f) +{ + struct nandfs_super_block *sb; + int i, j, n; + int sectors_to_read, error; + + sb = malloc(fs->nf_sectorsize); + if (sb == NULL) + return (ENOMEM); + + memset(fs->nf_sb, 0, sizeof(*fs->nf_sb)); + + sectors_to_read = (NANDFS_NFSAREAS * fs->nf_fsdata->f_erasesize) / + fs->nf_sectorsize; + for (i = 0; i < sectors_to_read; i++) { + NANDFS_DEBUG("reading i %d offset %d\n", i, + i * fs->nf_sectorsize); + error = ioread(f, i * fs->nf_sectorsize, (char *)sb, + fs->nf_sectorsize); + if (error) { + NANDFS_DEBUG("error %d\n", error); + continue; + } + n = fs->nf_sectorsize / sizeof(struct nandfs_super_block); + if ((i * fs->nf_sectorsize) % fs->nf_fsdata->f_erasesize == 0) { + if (fs->nf_sectorsize == sizeof(struct nandfs_fsdata)) + continue; + else { + sb += (sizeof(struct nandfs_fsdata) / + sizeof(struct nandfs_super_block)); + n -= (sizeof(struct nandfs_fsdata) / + sizeof(struct nandfs_super_block)); + } + } + + for (j = 0; j < n; j++) { + if (!nandfs_check_superblock_crc(fs->nf_fsdata, &sb[j])) + continue; + NANDFS_DEBUG("magic %x wtime %jd\n", sb->s_magic, + sb->s_wtime); + if (sb[j].s_wtime > fs->nf_sb->s_wtime) + memcpy(fs->nf_sb, &sb[j], sizeof(*fs->nf_sb)); + } + } + + free(sb); + + return (fs->nf_sb->s_magic != 0 ? 0 : EINVAL); +} + +static int +nandfs_find_fsdata(struct nandfs *fs, struct open_file *f) +{ + int offset, error, i; + + NANDFS_DEBUG("starting\n"); + + offset = 0; + for (i = 0; i < 64 * NANDFS_NFSAREAS; i++) { + error = ioread(f, offset, (char *)fs->nf_fsdata, + sizeof(struct nandfs_fsdata)); + if (error) + return (error); + if (fs->nf_fsdata->f_magic == NANDFS_FSDATA_MAGIC) { + NANDFS_DEBUG("found at %x, volume %s\n", offset, + fs->nf_fsdata->f_volume_name); + if (nandfs_check_fsdata_crc(fs->nf_fsdata)) + break; + } + offset += fs->nf_sectorsize; + } + + return (error); +} + +static int +nandfs_read_structures(struct nandfs *fs, struct open_file *f) +{ + int error; + + error = nandfs_find_fsdata(fs, f); + if (error) + return (error); + + error = nandfs_find_super_block(fs, f); + + if (error == 0) + NANDFS_DEBUG("selected sb with w_time %jd last_pseg %jx\n", + fs->nf_sb->s_wtime, fs->nf_sb->s_last_pseg); + + return (error); +} + +static int +nandfs_mount(struct nandfs *fs, struct open_file *f) +{ + int err = 0, level; + uint64_t last_pseg; + + fs->nf_fsdata = malloc(sizeof(struct nandfs_fsdata)); + fs->nf_sb = malloc(sizeof(struct nandfs_super_block)); + + err = nandfs_read_structures(fs, f); + if (err) { + free(fs->nf_fsdata); + free(fs->nf_sb); + return (err); + } + + fs->nf_blocksize = 1 << (fs->nf_fsdata->f_log_block_size + 10); + + NANDFS_DEBUG("using superblock with wtime %jd\n", fs->nf_sb->s_wtime); + + fs->nf_cpno = fs->nf_sb->s_last_cno; + last_pseg = fs->nf_sb->s_last_pseg; + + /* + * Calculate indirect block levels. + */ + nandfs_daddr_t mult; + + mult = 1; + for (level = 0; level < NIADDR; level++) { + mult *= NINDIR(fs); + fs->nf_nindir[level] = mult; + } + + nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_datfile_mdt, + fs->nf_fsdata->f_dat_entry_size); + + nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_ifile_mdt, + fs->nf_fsdata->f_inode_size); + + err = ioread(f, last_pseg * fs->nf_blocksize, &fs->nf_segsum, + sizeof(struct nandfs_segment_summary)); + if (err) { + free(fs->nf_sb); + free(fs->nf_fsdata); + return (err); + } + + err = ioread(f, (last_pseg + fs->nf_segsum.ss_nblocks - 1) * + fs->nf_blocksize, &fs->nf_sroot, sizeof(struct nandfs_super_root)); + if (err) { + free(fs->nf_sb); + free(fs->nf_fsdata); + return (err); + } + + fs->nf_datfile.inode = &fs->nf_sroot.sr_dat; + LIST_INIT(&fs->nf_datfile.bmap_bufs); + fs->nf_cpfile.inode = &fs->nf_sroot.sr_cpfile; + LIST_INIT(&fs->nf_cpfile.bmap_bufs); + + err = nandfs_get_checkpoint(fs, fs->nf_cpno, &fs->nf_checkpoint); + if (err) { + free(fs->nf_sb); + free(fs->nf_fsdata); + return (err); + } + + NANDFS_DEBUG("checkpoint cp_cno=%lld\n", fs->nf_checkpoint.cp_cno); + NANDFS_DEBUG("checkpoint cp_inodes_count=%lld\n", + fs->nf_checkpoint.cp_inodes_count); + NANDFS_DEBUG("checkpoint cp_ifile_inode.i_blocks=%lld\n", + fs->nf_checkpoint.cp_ifile_inode.i_blocks); + + fs->nf_ifile.inode = &fs->nf_checkpoint.cp_ifile_inode; + LIST_INIT(&fs->nf_ifile.bmap_bufs); + return (0); +} + +#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t)) + +static int +nandfs_open(const char *path, struct open_file *f) +{ + struct nandfs *fs; + struct nandfs_node *node; + int err, bsize, level; + + NANDFS_DEBUG("nandfs_open('%s', %p)\n", path, f); + + fs = malloc(sizeof(struct nandfs)); + f->f_fsdata = fs; + fs->nf_file = f; + + bsize = nandfs_probe_sectorsize(f); + if (bsize < 0) { + printf("Cannot probe medium sector size\n"); + return (EINVAL); + } + + fs->nf_sectorsize = bsize; + + /* + * Calculate indirect block levels. + */ + nandfs_daddr_t mult; + + mult = 1; + for (level = 0; level < NIADDR; level++) { + mult *= NINDIR(fs); + fs->nf_nindir[level] = mult; + } + + NANDFS_DEBUG("fs %p nf_sectorsize=%x\n", fs, fs->nf_sectorsize); + + err = nandfs_mount(fs, f); + if (err) { + NANDFS_DEBUG("Cannot mount nandfs: %s\n", strerror(err)); + return (err); + } + + node = nandfs_lookup_path(fs, path); + if (node == NULL) + return (EINVAL); + + fs->nf_offset = 0; + fs->nf_buf = NULL; + fs->nf_buf_blknr = -1; + fs->nf_opened_node = node; + LIST_INIT(&fs->nf_opened_node->bmap_bufs); + return (0); +} + +static int +nandfs_free_node(struct nandfs_node *node) +{ + struct bmap_buf *bmap, *tmp; + + free(node->inode); + LIST_FOREACH_SAFE(bmap, &node->bmap_bufs, list, tmp) { + LIST_REMOVE(bmap, list); + free(bmap->map); + free(bmap); + } + free(node); +} + +static int +nandfs_close(struct open_file *f) +{ + struct nandfs *fs = f->f_fsdata; + + NANDFS_DEBUG("nandfs_close(%p)\n", f); + + if (fs->nf_buf != NULL) + free(fs->nf_buf); + + nandfs_free_node(fs->nf_opened_node); + free(fs->nf_sb); + free(fs); +} + +static int +nandfs_read(struct open_file *f, void *addr, size_t size, size_t *resid) +{ + struct nandfs *fs = (struct nandfs *)f->f_fsdata; + size_t csize, buf_size; + uint8_t *buf; + int error = 0; + + NANDFS_DEBUG("nandfs_read(file=%p, addr=%p, size=%d)\n", f, addr, size); + + while (size != 0) { + if (fs->nf_offset >= fs->nf_opened_node->inode->i_size) + break; + + error = nandfs_buf_read(fs, (void *)&buf, &buf_size); + if (error) + break; + + csize = size; + if (csize > buf_size) + csize = buf_size; + + bcopy(buf, addr, csize); + + fs->nf_offset += csize; + addr = (char *)addr + csize; + size -= csize; + } + + if (resid) + *resid = size; + return (error); +} + +static off_t +nandfs_seek(struct open_file *f, off_t offset, int where) +{ + struct nandfs *fs = f->f_fsdata; + off_t off; + u_int size; + + NANDFS_DEBUG("nandfs_seek(file=%p, offset=%lld, where=%d)\n", f, + offset, where); + + size = fs->nf_opened_node->inode->i_size; + + switch (where) { + case SEEK_SET: + off = 0; + break; + case SEEK_CUR: + off = fs->nf_offset; + break; + case SEEK_END: + off = size; + break; + default: + errno = EINVAL; + return (-1); + } + + off += offset; + if (off < 0 || off > size) { + errno = EINVAL; + return(-1); + } + + fs->nf_offset = (u_int)off; + + return (off); +} + +static int +nandfs_stat(struct open_file *f, struct stat *sb) +{ + struct nandfs *fs = f->f_fsdata; + + NANDFS_DEBUG("nandfs_stat(file=%p, stat=%p)\n", f, sb); + + sb->st_size = fs->nf_opened_node->inode->i_size; + sb->st_mode = fs->nf_opened_node->inode->i_mode; + sb->st_uid = fs->nf_opened_node->inode->i_uid; + sb->st_gid = fs->nf_opened_node->inode->i_gid; + return (0); +} + +static int +nandfs_readdir(struct open_file *f, struct dirent *d) +{ + struct nandfs *fs = f->f_fsdata; + struct nandfs_dir_entry *dirent; + uint8_t *buf; + size_t buf_size; + + NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)\n", f, d); + + if (fs->nf_offset >= fs->nf_opened_node->inode->i_size) { + NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) ENOENT\n", + f, d); + return (ENOENT); + } + + if (nandfs_buf_read(fs, (void *)&buf, &buf_size)) { + NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)" + "buf_read failed\n", f, d); + return (EIO); + } + + NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) moving forward\n", + f, d); + + dirent = (struct nandfs_dir_entry *)buf; + fs->nf_offset += dirent->rec_len; + strncpy(d->d_name, dirent->name, dirent->name_len); + d->d_name[dirent->name_len] = '\0'; + d->d_type = dirent->file_type; + return (0); +} + +static int +nandfs_buf_read(struct nandfs *fs, char **buf_p, size_t *size_p) +{ + nandfs_daddr_t blknr, blkoff; + + blknr = fs->nf_offset / fs->nf_blocksize; + blkoff = fs->nf_offset % fs->nf_blocksize; + + if (blknr != fs->nf_buf_blknr) { + if (fs->nf_buf == NULL) + fs->nf_buf = malloc(fs->nf_blocksize); + + if (nandfs_read_inode(fs, fs->nf_opened_node, blknr, 1, + fs->nf_buf, 0)) + return (EIO); + + fs->nf_buf_blknr = blknr; + } + + *buf_p = fs->nf_buf + blkoff; + *size_p = fs->nf_blocksize - blkoff; + + NANDFS_DEBUG("nandfs_buf_read buf_p=%p size_p=%d\n", *buf_p, *size_p); + + if (*size_p > fs->nf_opened_node->inode->i_size - fs->nf_offset) + *size_p = fs->nf_opened_node->inode->i_size - fs->nf_offset; + + return (0); +} + +static struct nandfs_node * +nandfs_lookup_node(struct nandfs *fs, uint64_t ino) +{ + uint64_t blocknr; + int entrynr; + struct nandfs_inode *buffer; + struct nandfs_node *node; + struct nandfs_inode *inode; + + NANDFS_DEBUG("nandfs_lookup_node ino=%lld\n", ino); + + if (ino == 0) { + printf("nandfs_lookup_node: invalid inode requested\n"); + return (NULL); + } + + buffer = malloc(fs->nf_blocksize); + inode = malloc(sizeof(struct nandfs_inode)); + node = malloc(sizeof(struct nandfs_node)); + + nandfs_mdt_trans(&fs->nf_ifile_mdt, ino, &blocknr, &entrynr); + + if (nandfs_read_inode(fs, &fs->nf_ifile, blocknr, 1, buffer, 0)) + return (NULL); + + memcpy(inode, &buffer[entrynr], sizeof(struct nandfs_inode)); + node->inode = inode; + free(buffer); + return (node); +} + +static struct nandfs_node * +nandfs_lookup_path(struct nandfs *fs, const char *path) +{ + struct nandfs_node *node; + struct nandfs_dir_entry *dirent; + char *namebuf; + uint64_t i, j, done, counter, pinode, inode; + int nlinks = 0, len, link_len, nameidx; + uint8_t *buffer, *orig; + char *strp, *lpath; + + buffer = malloc(fs->nf_blocksize); + orig = buffer; + + namebuf = malloc(2 * MAXPATHLEN + 2); + strncpy(namebuf, path, MAXPATHLEN); + namebuf[MAXPATHLEN] = '\0'; + done = nameidx = 0; + lpath = namebuf; + + /* Get the root inode */ + node = nandfs_lookup_node(fs, NANDFS_ROOT_INO); + inode = NANDFS_ROOT_INO; + + while ((strp = strsep(&lpath, "/")) != NULL) { + if (*strp == '\0') + continue; + if ((node->inode->i_mode & IFMT) != IFDIR) { + nandfs_free_node(node); + node = NULL; + goto out; + } + + len = strlen(strp); + NANDFS_DEBUG("%s: looking for %s\n", __func__, strp); + for (i = 0; i < node->inode->i_blocks; i++) { + if (nandfs_read_inode(fs, node, i, 1, orig, 0)) { + node = NULL; + goto out; + } + + buffer = orig; + done = counter = 0; + while (1) { + dirent = (struct nandfs_dir_entry *)buffer; + NANDFS_DEBUG("%s: dirent.name = %s\n", + __func__, dirent->name); + NANDFS_DEBUG("%s: dirent.rec_len = %d\n", + __func__, dirent->rec_len); + NANDFS_DEBUG("%s: dirent.inode = %lld\n", + __func__, dirent->inode); + if (len == dirent->name_len && + (strncmp(strp, dirent->name, len) == 0) && + dirent->inode != 0) { + nandfs_free_node(node); + node = nandfs_lookup_node(fs, + dirent->inode); + pinode = inode; + inode = dirent->inode; + done = 1; + break; + } + + counter += dirent->rec_len; + buffer += dirent->rec_len; + + if (counter == fs->nf_blocksize) + break; + } + + if (done) + break; + } + + if (!done) { + node = NULL; + goto out; + } + + NANDFS_DEBUG("%s: %.*s has mode %o\n", __func__, + dirent->name_len, dirent->name, node->inode->i_mode); + + if ((node->inode->i_mode & IFMT) == IFLNK) { + NANDFS_DEBUG("%s: %.*s is symlink\n", + __func__, dirent->name_len, dirent->name); + link_len = node->inode->i_size; + + if (++nlinks > MAXSYMLINKS) { + nandfs_free_node(node); + node = NULL; + goto out; + } + + if (nandfs_read_inode(fs, node, 0, 1, orig, 0)) { + nandfs_free_node(node); + node = NULL; + goto out; + } + + NANDFS_DEBUG("%s: symlink is %.*s\n", + __func__, link_len, (char *)orig); + + nameidx = (nameidx == 0) ? MAXPATHLEN + 1 : 0; + bcopy((char *)orig, namebuf + nameidx, + (unsigned)link_len); + if (lpath != NULL) { + namebuf[nameidx + link_len++] = '/'; + strncpy(namebuf + nameidx + link_len, lpath, + MAXPATHLEN - link_len); + namebuf[nameidx + MAXPATHLEN] = '\0'; + } else + namebuf[nameidx + link_len] = '\0'; + + NANDFS_DEBUG("%s: strp=%s, lpath=%s, namebuf0=%s, " + "namebuf1=%s, idx=%d\n", __func__, strp, lpath, + namebuf + 0, namebuf + MAXPATHLEN + 1, nameidx); + + lpath = namebuf + nameidx; + + nandfs_free_node(node); + + /* + * If absolute pathname, restart at root. Otherwise + * continue with out parent inode. + */ + inode = (orig[0] == '/') ? NANDFS_ROOT_INO : pinode; + node = nandfs_lookup_node(fs, inode); + } + } + +out: + free(namebuf); + free(orig); + return (node); +} + +static int +nandfs_read_inode(struct nandfs *fs, struct nandfs_node *node, + nandfs_daddr_t blknr, u_int nblks, void *buf, int raw) +{ + int i; + uint64_t *pblks; + uint64_t *vblks; + int error; + + pblks = malloc(nblks * sizeof(uint64_t)); + vblks = malloc(nblks * sizeof(uint64_t)); + + NANDFS_DEBUG("nandfs_read_inode fs=%p node=%p blknr=%lld nblks=%d\n", + fs, node, blknr, nblks); + for (i = 0; i < nblks; i++) { + error = nandfs_bmap_lookup(fs, node, blknr + i, &vblks[i], raw); + if (error) { + free(pblks); + free(vblks); + return (error); + } + if (raw == 0) + pblks[i] = nandfs_vtop(fs, vblks[i]); + else + pblks[i] = vblks[i]; + } + + for (i = 0; i < nblks; i++) { + if (ioread(fs->nf_file, pblks[i] * fs->nf_blocksize, buf, + fs->nf_blocksize)) { + free(pblks); + free(vblks); + return (EIO); + } + + buf += fs->nf_blocksize; + } + + free(pblks); + free(vblks); + return (0); +} + +static int +nandfs_read_blk(struct nandfs *fs, nandfs_daddr_t blknr, void *buf, int phys) +{ + uint64_t pblknr; + + pblknr = (phys ? blknr : nandfs_vtop(fs, blknr)); + + return (ioread(fs->nf_file, pblknr * fs->nf_blocksize, buf, + fs->nf_blocksize)); +} + +static int +nandfs_get_checkpoint(struct nandfs *fs, uint64_t cpno, + struct nandfs_checkpoint *cp) +{ + uint64_t blocknr; + int blockoff, cp_per_block, dlen; + uint8_t *buf; + + NANDFS_DEBUG("nandfs_get_checkpoint(fs=%p cpno=%lld)\n", fs, cpno); + + buf = malloc(fs->nf_blocksize); + + cpno += NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1; + dlen = fs->nf_fsdata->f_checkpoint_size; + cp_per_block = fs->nf_blocksize / dlen; + blocknr = cpno / cp_per_block; + blockoff = (cpno % cp_per_block) * dlen; + + if (nandfs_read_inode(fs, &fs->nf_cpfile, blocknr, 1, buf, 0)) { + free(buf); + return (EINVAL); + } + + memcpy(cp, buf + blockoff, sizeof(struct nandfs_checkpoint)); + free(buf); + + return (0); +} + +static uint64_t * +nandfs_get_map(struct nandfs *fs, struct nandfs_node *node, nandfs_daddr_t blknr, + int phys) +{ + struct bmap_buf *bmap; + uint64_t *map; + + LIST_FOREACH(bmap, &node->bmap_bufs, list) { + if (bmap->blknr == blknr) + return (bmap->map); + } + + map = malloc(fs->nf_blocksize); + if (nandfs_read_blk(fs, blknr, map, phys)) { + free(map); + return (NULL); + } + + bmap = malloc(sizeof(struct bmap_buf)); + bmap->blknr = blknr; + bmap->map = map; + + LIST_INSERT_HEAD(&node->bmap_bufs, bmap, list); + + NANDFS_DEBUG("%s:(node=%p, map=%p)\n", __func__, node, map); + return (map); +} + +static int +nandfs_bmap_lookup(struct nandfs *fs, struct nandfs_node *node, + nandfs_lbn_t lblknr, nandfs_daddr_t *vblknr, int phys) +{ + struct nandfs_inode *ino; + nandfs_daddr_t ind_block_num; + uint64_t *map, *indir; + uint64_t idx0, idx1, vblk, tmp; + int idx; + int level; + + ino = node->inode; + + if (lblknr < NDADDR) { + *vblknr = ino->i_db[lblknr]; + return (0); + } + + lblknr -= NDADDR; + + /* + * nindir[0] = NINDIR + * nindir[1] = NINDIR**2 + * nindir[2] = NINDIR**3 + * etc + */ + for (level = 0; level < NIADDR; level++) { + NANDFS_DEBUG("lblknr=%jx fs->nf_nindir[%d]=%d\n", lblknr, level, fs->nf_nindir[level]); + if (lblknr < fs->nf_nindir[level]) + break; + lblknr -= fs->nf_nindir[level]; + } + + if (level == NIADDR) { + /* Block number too high */ + NANDFS_DEBUG("lblknr %jx too high\n", lblknr); + return (EFBIG); + } + + ind_block_num = ino->i_ib[level]; + + for (; level >= 0; level--) { + if (ind_block_num == 0) { + *vblknr = 0; /* missing */ + return (0); + } + + twiddle(); + NANDFS_DEBUG("calling get_map with %jx\n", ind_block_num); + map = nandfs_get_map(fs, node, ind_block_num, phys); + if (map == NULL) + return (EIO); + + if (level > 0) { + idx = lblknr / fs->nf_nindir[level - 1]; + lblknr %= fs->nf_nindir[level - 1]; + } else + idx = lblknr; + + ind_block_num = ((nandfs_daddr_t *)map)[idx]; + } + + *vblknr = ind_block_num; + + return (0); +} + +static nandfs_daddr_t +nandfs_vtop(struct nandfs *fs, nandfs_daddr_t vblocknr) +{ + nandfs_lbn_t blocknr; + nandfs_daddr_t pblocknr; + int entrynr; + struct nandfs_dat_entry *dat; + + dat = malloc(fs->nf_blocksize); + nandfs_mdt_trans(&fs->nf_datfile_mdt, vblocknr, &blocknr, &entrynr); + + if (nandfs_read_inode(fs, &fs->nf_datfile, blocknr, 1, dat, 1)) { + free(dat); + return (0); + } + + NANDFS_DEBUG("nandfs_vtop entrynr=%d vblocknr=%lld pblocknr=%lld\n", + entrynr, vblocknr, dat[entrynr].de_blocknr); + + pblocknr = dat[entrynr].de_blocknr; + free(dat); + return (pblocknr); +} + +static void +nandfs_calc_mdt_consts(int blocksize, struct nandfs_mdt *mdt, int entry_size) +{ + + mdt->entries_per_group = blocksize * 8; /* bits in sector */ + mdt->entries_per_block = blocksize / entry_size; + mdt->blocks_per_group = + (mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1; + mdt->groups_per_desc_block = + blocksize / sizeof(struct nandfs_block_group_desc); + mdt->blocks_per_desc_block = + mdt->groups_per_desc_block * mdt->blocks_per_group + 1; +} + +static void +nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index, + nandfs_daddr_t *blocknr, uint32_t *entry_in_block) +{ + nandfs_daddr_t blknr; + uint64_t group, group_offset, blocknr_in_group; + uint64_t desc_block, desc_offset; + + /* Calculate our offset in the file */ + group = index / mdt->entries_per_group; + group_offset = index % mdt->entries_per_group; + desc_block = group / mdt->groups_per_desc_block; + desc_offset = group % mdt->groups_per_desc_block; + blocknr_in_group = group_offset / mdt->entries_per_block; + + /* To descgroup offset */ + blknr = 1 + desc_block * mdt->blocks_per_desc_block; + + /* To group offset */ + blknr += desc_offset * mdt->blocks_per_group; + + /* To actual file block */ + blknr += 1 + blocknr_in_group; + + *blocknr = blknr; + *entry_in_block = group_offset % mdt->entries_per_block; +} + +static int +ioread(struct open_file *f, off_t pos, void *buf, u_int length) +{ + void *buffer; + int err; + int bsize = ((struct nandfs *)f->f_fsdata)->nf_sectorsize; + u_int off, nsec; + + off = pos % bsize; + pos /= bsize; + nsec = (length + (bsize - 1)) / bsize; + + NANDFS_DEBUG("pos=%lld length=%d off=%d nsec=%d\n", pos, length, + off, nsec); + + buffer = malloc(nsec * bsize); + + err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, pos, + nsec * bsize, buffer, NULL); + + memcpy(buf, buffer + off, length); + free(buffer); + + return (err); +} + +static int +nandfs_probe_sectorsize(struct open_file *f) +{ + void *buffer; + int i, err; + + buffer = malloc(16 * 1024); + + NANDFS_DEBUG("probing for sector size: "); + + for (i = 512; i < (16 * 1024); i <<= 1) { + NANDFS_DEBUG("%d ", i); + err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 0, i, + buffer, NULL); + + if (err == 0) { + NANDFS_DEBUG("found"); + free(buffer); + return (i); + } + } + + free(buffer); + NANDFS_DEBUG("not found\n"); + return (-1); +} diff --git a/lib/libstand/stand.h b/lib/libstand/stand.h index db0490f..10bb829 100644 --- a/lib/libstand/stand.h +++ b/lib/libstand/stand.h @@ -118,6 +118,7 @@ extern struct fs_ops ufs_fsops; extern struct fs_ops tftp_fsops; extern struct fs_ops nfs_fsops; extern struct fs_ops cd9660_fsops; +extern struct fs_ops nandfs_fsops; extern struct fs_ops gzipfs_fsops; extern struct fs_ops bzipfs_fsops; extern struct fs_ops dosfs_fsops; diff --git a/sbin/Makefile b/sbin/Makefile index 0cb421f..fcdb567 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -92,6 +92,11 @@ SUBDIR+= ipfw SUBDIR+= natd .endif +.if ${MK_NAND} != "no" +SUBDIR+= nandfs +SUBDIR+= newfs_nandfs +.endif + .if ${MK_PF} != "no" SUBDIR+= pfctl SUBDIR+= pflogd diff --git a/sbin/nandfs/Makefile b/sbin/nandfs/Makefile new file mode 100644 index 0000000..8474b09 --- /dev/null +++ b/sbin/nandfs/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +PROG= nandfs +SRCS= nandfs.c lssnap.c mksnap.c rmsnap.c +MAN= nandfs.8 + +DPADD= ${LIBNANDFS} +LDADD= -lnandfs + +.include diff --git a/sbin/nandfs/lssnap.c b/sbin/nandfs/lssnap.c new file mode 100644 index 0000000..fbee3cd --- /dev/null +++ b/sbin/nandfs/lssnap.c @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); +#include + +#include +#include +#include +#include + +#include +#include + +#include "nandfs.h" + +#define NCPINFO 512 + +static void +lssnap_usage(void) +{ + + fprintf(stderr, "usage:\n"); + fprintf(stderr, "\tlssnap node\n"); +} + +static void +print_cpinfo(struct nandfs_cpinfo *cpinfo) +{ + struct tm tm; + time_t t; + char timebuf[128]; + + t = (time_t)cpinfo->nci_create; + localtime_r(&t, &tm); + strftime(timebuf, sizeof(timebuf), "%F %T", &tm); + + printf("%20llu %s\n", (unsigned long long)cpinfo->nci_cno, timebuf); +} + +int +nandfs_lssnap(int argc, char **argv) +{ + struct nandfs_cpinfo *cpinfos; + struct nandfs fs; + uint64_t next; + int error, nsnap, i; + + if (argc != 1) { + lssnap_usage(); + return (EX_USAGE); + } + + cpinfos = malloc(sizeof(*cpinfos) * NCPINFO); + if (cpinfos == NULL) { + fprintf(stderr, "cannot allocate memory\n"); + return (-1); + } + + nandfs_init(&fs, argv[0]); + error = nandfs_open(&fs); + if (error == -1) { + fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs)); + goto out; + } + + for (next = 1; next != 0; next = cpinfos[nsnap - 1].nci_next) { + nsnap = nandfs_get_snap(&fs, next, cpinfos, NCPINFO); + if (nsnap < 1) + break; + + for (i = 0; i < nsnap; i++) + print_cpinfo(&cpinfos[i]); + } + + if (nsnap == -1) + fprintf(stderr, "nandfs_get_snap: %s\n", nandfs_errmsg(&fs)); + +out: + nandfs_close(&fs); + nandfs_destroy(&fs); + free(cpinfos); + return (error); +} diff --git a/sbin/nandfs/mksnap.c b/sbin/nandfs/mksnap.c new file mode 100644 index 0000000..cd2d130 --- /dev/null +++ b/sbin/nandfs/mksnap.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); +#include + +#include +#include + +#include +#include + +#include "nandfs.h" + +static void +mksnap_usage(void) +{ + + fprintf(stderr, "usage:\n"); + fprintf(stderr, "\tmksnap node\n"); +} + +int +nandfs_mksnap(int argc, char **argv) +{ + struct nandfs fs; + uint64_t cpno; + int error; + + if (argc != 1) { + mksnap_usage(); + return (EX_USAGE); + } + + nandfs_init(&fs, argv[0]); + error = nandfs_open(&fs); + if (error == -1) { + fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs)); + goto out; + } + + error = nandfs_make_snap(&fs, &cpno); + if (error == -1) + fprintf(stderr, "nandfs_make_snap: %s\n", nandfs_errmsg(&fs)); + else + printf("%jd\n", cpno); + +out: + nandfs_close(&fs); + nandfs_destroy(&fs); + return (error); +} diff --git a/sbin/nandfs/nandfs.8 b/sbin/nandfs/nandfs.8 new file mode 100644 index 0000000..b1cfa05 --- /dev/null +++ b/sbin/nandfs/nandfs.8 @@ -0,0 +1,75 @@ +.\" +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Semihalf under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd February 28, 2012 +.Dt NANDFS 8 +.Os +.Sh NAME +.Nm nandfs +.Nd manage mounted NAND FS +.Sh SYNOPSIS +.Nm +.Cm lssnap +.Ar node +.Nm +.Cm mksnap +.Ar node +.Nm +.Cm rmsnap +.Ar snapshot node +.Sh DESCRIPTION +The +.Nm +utility allows to manage snapshots of a mounted NAND FS. +.Sh EXAMPLES +.Pp +Create a snapshot of filesystem mounted on +.Em /nand . +.Bd -literal -offset 2n +.Li # Ic nandfs mksnap /nand +1 +.Ed +.Pp +List snapshots of filesystem mounted on +.Em /nand . +.Bd -literal -offset 2n +.Li # Ic nandfs lssnap /nand +1 2012-02-28 18:49:45 ss 138 2 +.Ed +.Pp +Remove snapshot 1 of filesystem mounted on +.Em /nand . +.Bd -literal -offset 2n +.Li # Ic nandfs rmsnap 1 /nand +.Ed +.Sh AUTHORS +This utility and manual page were written by +.An Mateusz Guzik . diff --git a/sbin/nandfs/nandfs.c b/sbin/nandfs/nandfs.c new file mode 100644 index 0000000..f7ddda1 --- /dev/null +++ b/sbin/nandfs/nandfs.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include "nandfs.h" + +static void +usage(void) +{ + + fprintf(stderr, "usage: nandfs [lssnap | mksnap | rmsnap ] " + "node\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + int error = 0; + char *cmd; + + if (argc < 2) + usage(); + + cmd = argv[1]; + argc -= 2; + argv += 2; + + if (strcmp(cmd, "lssnap") == 0) + error = nandfs_lssnap(argc, argv); + else if (strcmp(cmd, "mksnap") == 0) + error = nandfs_mksnap(argc, argv); + else if (strcmp(cmd, "rmsnap") == 0) + error = nandfs_rmsnap(argc, argv); + else + usage(); + + return (error); +} diff --git a/sbin/nandfs/nandfs.h b/sbin/nandfs/nandfs.h new file mode 100644 index 0000000..f544cc2 --- /dev/null +++ b/sbin/nandfs/nandfs.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef NANDFS_H +#define NANDFS_H + +int nandfs_lssnap(int, char **); +int nandfs_mksnap(int, char **); +int nandfs_rmsnap(int, char **); + +#endif /* !NANDFS_H */ diff --git a/sbin/nandfs/rmsnap.c b/sbin/nandfs/rmsnap.c new file mode 100644 index 0000000..df2cbd5 --- /dev/null +++ b/sbin/nandfs/rmsnap.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); +#include + +#include +#include +#include +#include + +#include +#include + +#include "nandfs.h" + +static void +rmsnap_usage(void) +{ + + fprintf(stderr, "usage:\n"); + fprintf(stderr, "\trmsnap snap node\n"); +} + +int +nandfs_rmsnap(int argc, char **argv) +{ + struct nandfs fs; + uint64_t cpno; + int error; + + if (argc != 2) { + rmsnap_usage(); + return (EX_USAGE); + } + + cpno = strtoll(argv[0], (char **)NULL, 10); + if (cpno == 0) { + fprintf(stderr, "%s must be a number greater than 0\n", + argv[0]); + return (EX_USAGE); + } + + nandfs_init(&fs, argv[1]); + error = nandfs_open(&fs); + if (error == -1) { + fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs)); + goto out; + } + + error = nandfs_delete_snap(&fs, cpno); + if (error == -1) + fprintf(stderr, "nandfs_delete_snap: %s\n", nandfs_errmsg(&fs)); + +out: + nandfs_close(&fs); + nandfs_destroy(&fs); + return (error); +} diff --git a/sbin/newfs_nandfs/Makefile b/sbin/newfs_nandfs/Makefile new file mode 100644 index 0000000..1d990ed --- /dev/null +++ b/sbin/newfs_nandfs/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +PROG= newfs_nandfs +MAN= newfs_nandfs.8 + +LDADD+= -lgeom +DPADD+= ${LIBGEOM} + +.include diff --git a/sbin/newfs_nandfs/newfs_nandfs.8 b/sbin/newfs_nandfs/newfs_nandfs.8 new file mode 100644 index 0000000..401b5c2 --- /dev/null +++ b/sbin/newfs_nandfs/newfs_nandfs.8 @@ -0,0 +1,77 @@ +.\" +.\" Copyright (c) 2010 Semihalf +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" + +.Dd April 11, 2009 +.Dt NEWFS_NANDFS 8 +.Os +.Sh NAME +.Nm newfs_nandfs +.Nd construct a new NAND FS file system +.Sh SYNOPSIS +.Nm +.Op Fl b Ar blocsize +.Op Fl B Ar blocks-per-segment +.Op Fl L Ar label +.Op Fl m Ar reserved-segment-percent +.Ar device +.Sh DESCRIPTION +The +.Nm +utility creates a NAND FS file system on device. +.Pp +The options are as follow: +.Bl -tag -width indent +.It Fl b Ar blocksize +Size of block (1024 if not specified). +.It Fl B Ar blocks_per_segment +Number of blocks per segment (2048 if not specified). +.It Fl L Ar label +Volume label (up to 16 characters). +.It Fl m Ar reserved_block_percent +Percentage of reserved blocks (5 if not specified). +.El +.Sh EXIT STATUS +Exit status is 0 on success and 1 on error. +.Sh EXAMPLES +Create a file system, using default parameters, on +.Pa /dev/ad0s1 : +.Bd -literal -offset indent +newfs_nandfs /dev/ad0s1 +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr fdisk 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 10.0 . +.Sh AUTHOR +.An Grzegorz Bernacki diff --git a/sbin/newfs_nandfs/newfs_nandfs.c b/sbin/newfs_nandfs/newfs_nandfs.c new file mode 100644 index 0000000..c996674 --- /dev/null +++ b/sbin/newfs_nandfs/newfs_nandfs.c @@ -0,0 +1,1176 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEBUG +#undef DEBUG +#ifdef DEBUG +#define debug(fmt, args...) do { \ + printf("nandfs:" fmt "\n", ##args); } while (0) +#else +#define debug(fmt, args...) +#endif + +#define NANDFS_FIRST_BLOCK nandfs_first_block() +#define NANDFS_FIRST_CNO 1 +#define NANDFS_BLOCK_BAD 1 +#define NANDFS_BLOCK_GOOD 0 + +struct file_info { + uint64_t ino; + const char *name; + uint32_t mode; + uint64_t size; + uint8_t nblocks; + uint32_t *blocks; + struct nandfs_inode *inode; +}; + +struct file_info user_files[] = +{ + {NANDFS_ROOT_INO, NULL, S_IFDIR | 0755, 0, 1, NULL, NULL}, +}; + +struct file_info ifile = {NANDFS_IFILE_INO, NULL, 0, 0, -1, NULL, NULL}; +struct file_info sufile = {NANDFS_SUFILE_INO, NULL, 0, 0, -1, NULL, NULL}; +struct file_info cpfile = {NANDFS_CPFILE_INO, NULL, 0, 0, -1, NULL, NULL}; +struct file_info datfile = {NANDFS_DAT_INO, NULL, 0, 0, -1, NULL, NULL}; + +struct nandfs_block { + LIST_ENTRY(nandfs_block) block_link; + uint32_t number; + uint64_t offset; + void *data; +}; + +static LIST_HEAD(, nandfs_block) block_head = LIST_HEAD_INITIALIZER(&block_head); + +/* Storage geometry */ +static off_t mediasize; +static ssize_t sectorsize; +static uint64_t nsegments; +static uint64_t erasesize; +static uint64_t segsize; + +struct nandfs_fsdata fsdata; +struct nandfs_super_block super_block; + +static int is_nand; + +/* Nandfs parameters */ +static size_t blocksize = NANDFS_DEF_BLOCKSIZE; +static long blocks_per_segment; +static long rsv_segment_percent = 5; +static time_t nandfs_time; +static uint32_t bad_segments_count = 0; +static uint32_t *bad_segments = NULL; +static uint8_t fsdata_blocks_state[NANDFS_NFSAREAS]; + +u_char *volumelabel = NULL; + +struct nandfs_super_root *sr; + +uint32_t nuserfiles; +uint32_t seg_segsum_size; +uint32_t seg_nblocks; +uint32_t seg_endblock; + +#define SIZE_TO_BLOCK(size) (((size) + (blocksize - 1)) / blocksize) + +static uint32_t +nandfs_first_block(void) +{ + uint32_t i, first_free, start_bad_segments = 0; + + for (i = 0; i < bad_segments_count; i++) { + if (i == bad_segments[i]) + start_bad_segments++; + else + break; + } + + first_free = SIZE_TO_BLOCK(NANDFS_DATA_OFFSET_BYTES(erasesize) + + (start_bad_segments * segsize)); + + if (first_free < (uint32_t)blocks_per_segment) + return (blocks_per_segment); + else + return (first_free); +} + +static void +usage(void) +{ + + fprintf(stderr, + "usage: newfs_nandfs [ -options ] device\n" + "where the options are:\n" + "\t-b block-size\n" + "\t-B blocks-per-segment\n" + "\t-L volume label\n" + "\t-m reserved-segments-percentage\n"); + exit(1); +} + +static int +nandfs_log2(unsigned n) +{ + unsigned count; + + /* + * N.B. this function will return 0 if supplied 0. + */ + for (count = 0; n/2; count++) + n /= 2; + return count; +} + +/* from NetBSD's src/sys/net/if_ethersubr.c */ +static uint32_t +crc32_le(uint32_t crc, const uint8_t *buf, size_t len) +{ + static const uint32_t crctab[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; + size_t i; + + crc = crc ^ ~0U; + + for (i = 0; i < len; i++) { + crc ^= buf[i]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + } + + return (crc ^ ~0U); +} + +static void * +get_block(uint32_t block_nr, uint64_t offset) +{ + struct nandfs_block *block, *new_block; + + LIST_FOREACH(block, &block_head, block_link) { + if (block->number == block_nr) + return block->data; + } + + debug("allocating block %x\n", block_nr); + + new_block = malloc(sizeof(*block)); + if (!new_block) + err(1, "cannot allocate block"); + + new_block->number = block_nr; + new_block->offset = offset; + new_block->data = malloc(blocksize); + if (!new_block->data) + err(1, "cannot allocate block data"); + + memset(new_block->data, 0, blocksize); + + LIST_INSERT_HEAD(&block_head, new_block, block_link); + + return (new_block->data); +} + +static int +nandfs_seg_usage_blk_offset(uint64_t seg, uint64_t *blk, uint64_t *offset) +{ + uint64_t off; + uint16_t seg_size; + + seg_size = sizeof(struct nandfs_segment_usage); + + off = roundup(sizeof(struct nandfs_sufile_header), seg_size); + off += (seg * seg_size); + + *blk = off / blocksize; + *offset = (off % blocksize) / seg_size; + return (0); +} + +static uint32_t +segment_size(void) +{ + u_int size; + + size = sizeof(struct nandfs_segment_summary ); + size += seg_nblocks * sizeof(struct nandfs_binfo_v); + + if (size > blocksize) + err(1, "segsum info bigger that blocksize"); + + return (size); +} + + +static void +prepare_blockgrouped_file(uint32_t block) +{ + struct nandfs_block_group_desc *desc; + uint32_t i, entries; + + desc = (struct nandfs_block_group_desc *)get_block(block, 0); + entries = blocksize / sizeof(struct nandfs_block_group_desc); + for (i = 0; i < entries; i++) + desc[i].bg_nfrees = blocksize * 8; +} + +static void +alloc_blockgrouped_file(uint32_t block, uint32_t entry) +{ + struct nandfs_block_group_desc *desc; + uint32_t desc_nr; + uint32_t *bitmap; + + desc = (struct nandfs_block_group_desc *)get_block(block, 0); + bitmap = (uint32_t *)get_block(block + 1, 1); + + bitmap += (entry >> 5); + if (*bitmap & (1 << (entry % 32))) { + printf("nandfs: blockgrouped entry %d already allocated\n", + entry); + } + *bitmap |= (1 << (entry % 32)); + + desc_nr = entry / (blocksize * 8); + desc[desc_nr].bg_nfrees--; +} + + +static uint64_t +count_su_blocks(void) +{ + uint64_t maxblk, blk, offset, i; + + maxblk = blk = 0; + + for (i = 0; i < bad_segments_count; i++) { + nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &offset); + debug("bad segment at block:%jx off: %jx", blk, offset); + if (blk > maxblk) + maxblk = blk; + } + + debug("bad segment needs %#jx", blk); + if (blk >= NDADDR) { + printf("nandfs: file too big (%jd > %d)\n", blk, NDADDR); + exit(2); + } + + sufile.size = (blk + 1) * blocksize; + return (blk + 1); +} + +static void +count_seg_blocks(void) +{ + uint32_t i; + + for (i = 0; i < nuserfiles; i++) + if (user_files[i].nblocks) { + seg_nblocks += user_files[i].nblocks; + user_files[i].blocks = malloc(user_files[i].nblocks * sizeof(uint32_t)); + } + + ifile.nblocks = 2 + + SIZE_TO_BLOCK(sizeof(struct nandfs_inode) * (NANDFS_USER_INO + 1)); + ifile.blocks = malloc(ifile.nblocks * sizeof(uint32_t)); + seg_nblocks += ifile.nblocks; + + cpfile.nblocks = + SIZE_TO_BLOCK((NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1) * + sizeof(struct nandfs_checkpoint)); + cpfile.blocks = malloc(cpfile.nblocks * sizeof(uint32_t)); + seg_nblocks += cpfile.nblocks; + + if (!bad_segments) { + sufile.nblocks = + SIZE_TO_BLOCK((NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET + 1) * + sizeof(struct nandfs_segment_usage)); + } else { + debug("bad blocks found: extra space for sufile"); + sufile.nblocks = count_su_blocks(); + } + + sufile.blocks = malloc(sufile.nblocks * sizeof(uint32_t)); + seg_nblocks += sufile.nblocks; + + datfile.nblocks = 2 + + SIZE_TO_BLOCK((seg_nblocks) * sizeof(struct nandfs_dat_entry)); + datfile.blocks = malloc(datfile.nblocks * sizeof(uint32_t)); + seg_nblocks += datfile.nblocks; +} + +static void +assign_file_blocks(uint64_t start_block) +{ + uint32_t i, j; + + for (i = 0; i < nuserfiles; i++) + for (j = 0; j < user_files[i].nblocks; j++) { + debug("user file %d at block %d at %#jx", + i, j, (uintmax_t)start_block); + user_files[i].blocks[j] = start_block++; + } + + for (j = 0; j < ifile.nblocks; j++) { + debug("ifile block %d at %#jx", j, (uintmax_t)start_block); + ifile.blocks[j] = start_block++; + } + + for (j = 0; j < cpfile.nblocks; j++) { + debug("cpfile block %d at %#jx", j, (uintmax_t)start_block); + cpfile.blocks[j] = start_block++; + } + + for (j = 0; j < sufile.nblocks; j++) { + debug("sufile block %d at %#jx", j, (uintmax_t)start_block); + sufile.blocks[j] = start_block++; + } + + for (j = 0; j < datfile.nblocks; j++) { + debug("datfile block %d at %#jx", j, (uintmax_t)start_block); + datfile.blocks[j] = start_block++; + } + + /* add one for superroot */ + debug("sr at block %#jx", (uintmax_t)start_block); + sr = (struct nandfs_super_root *)get_block(start_block++, 0); + seg_endblock = start_block; +} + +static void +save_datfile(void) +{ + + prepare_blockgrouped_file(datfile.blocks[0]); +} + +static uint64_t +update_datfile(uint64_t block) +{ + struct nandfs_dat_entry *dat; + static uint64_t vblock = 0; + uint64_t allocated, i, off; + + if (vblock == 0) { + alloc_blockgrouped_file(datfile.blocks[0], vblock); + vblock++; + } + allocated = vblock; + i = vblock / (blocksize / sizeof(*dat)); + off = vblock % (blocksize / sizeof(*dat)); + vblock++; + + dat = (struct nandfs_dat_entry *)get_block(datfile.blocks[2 + i], 2 + i); + + alloc_blockgrouped_file(datfile.blocks[0], allocated); + dat[off].de_blocknr = block; + dat[off].de_start = NANDFS_FIRST_CNO; + dat[off].de_end = UINTMAX_MAX; + + return (allocated); +} + +static union nandfs_binfo * +update_block_info(union nandfs_binfo *binfo, struct file_info *file) +{ + nandfs_daddr_t vblock; + uint32_t i; + + for (i = 0; i < file->nblocks; i++) { + debug("%s: blk %x", __func__, i); + if (file->ino != NANDFS_DAT_INO) { + vblock = update_datfile(file->blocks[i]); + binfo->bi_v.bi_vblocknr = vblock; + binfo->bi_v.bi_blkoff = i; + binfo->bi_v.bi_ino = file->ino; + file->inode->i_db[i] = vblock; + } else { + binfo->bi_dat.bi_blkoff = i; + binfo->bi_dat.bi_ino = file->ino; + file->inode->i_db[i] = datfile.blocks[i]; + } + binfo++; + } + + return (binfo); +} + +static void +save_segsum(struct nandfs_segment_summary *ss) +{ + union nandfs_binfo *binfo; + struct nandfs_block *block; + uint32_t sum_bytes, i; + uint8_t crc_data, crc_skip; + + sum_bytes = segment_size(); + ss->ss_magic = NANDFS_SEGSUM_MAGIC; + ss->ss_bytes = sizeof(struct nandfs_segment_summary); + ss->ss_flags = NANDFS_SS_LOGBGN | NANDFS_SS_LOGEND | NANDFS_SS_SR; + ss->ss_seq = 1; + ss->ss_create = nandfs_time; + + ss->ss_next = nandfs_first_block() + blocks_per_segment; + /* nblocks = segment blocks + segsum block + superroot */ + ss->ss_nblocks = seg_nblocks + 2; + ss->ss_nbinfos = seg_nblocks; + ss->ss_sumbytes = sum_bytes; + + crc_skip = sizeof(ss->ss_datasum) + sizeof(ss->ss_sumsum); + ss->ss_sumsum = crc32_le(0, (uint8_t *)ss + crc_skip, + sum_bytes - crc_skip); + crc_data = 0; + + binfo = (union nandfs_binfo *)(ss + 1); + for (i = 0; i < nuserfiles; i++) { + if (user_files[i].nblocks) + binfo = update_block_info(binfo, &user_files[i]); + } + + binfo = update_block_info(binfo, &ifile); + binfo = update_block_info(binfo, &cpfile); + binfo = update_block_info(binfo, &sufile); + update_block_info(binfo, &datfile); + + /* save superroot crc */ + crc_skip = sizeof(sr->sr_sum); + sr->sr_sum = crc32_le(0, (uint8_t *)sr + crc_skip, + NANDFS_SR_BYTES - crc_skip); + + /* segment checksup */ + crc_skip = sizeof(ss->ss_datasum); + LIST_FOREACH(block, &block_head, block_link) { + if (block->number < NANDFS_FIRST_BLOCK) + continue; + if (block->number == NANDFS_FIRST_BLOCK) + crc_data = crc32_le(0, + (uint8_t *)block->data + crc_skip, + blocksize - crc_skip); + else + crc_data = crc32_le(crc_data, (uint8_t *)block->data, + blocksize); + } + ss->ss_datasum = crc_data; +} + +static void +create_fsdata(void) +{ + + memset(&fsdata, 0, sizeof(struct nandfs_fsdata)); + + fsdata.f_magic = NANDFS_FSDATA_MAGIC; + fsdata.f_nsegments = nsegments; + fsdata.f_erasesize = erasesize; + fsdata.f_first_data_block = NANDFS_FIRST_BLOCK; + fsdata.f_blocks_per_segment = blocks_per_segment; + fsdata.f_r_segments_percentage = rsv_segment_percent; + fsdata.f_rev_level = NANDFS_CURRENT_REV; + fsdata.f_sbbytes = NANDFS_SB_BYTES; + fsdata.f_bytes = NANDFS_FSDATA_CRC_BYTES; + fsdata.f_ctime = nandfs_time; + fsdata.f_log_block_size = nandfs_log2(blocksize) - 10; + fsdata.f_errors = 1; + fsdata.f_inode_size = sizeof(struct nandfs_inode); + fsdata.f_dat_entry_size = sizeof(struct nandfs_dat_entry); + fsdata.f_checkpoint_size = sizeof(struct nandfs_checkpoint); + fsdata.f_segment_usage_size = sizeof(struct nandfs_segment_usage); + + uuidgen(&fsdata.f_uuid, 1); + + if (volumelabel) + memcpy(fsdata.f_volume_name, volumelabel, 16); + + fsdata.f_sum = crc32_le(0, (const uint8_t *)&fsdata, + NANDFS_FSDATA_CRC_BYTES); +} + +static void +save_fsdata(void *data) +{ + + memcpy(data, &fsdata, sizeof(fsdata)); +} + +static void +create_super_block(void) +{ + + memset(&super_block, 0, sizeof(struct nandfs_super_block)); + + super_block.s_magic = NANDFS_SUPER_MAGIC; + super_block.s_last_cno = NANDFS_FIRST_CNO; + super_block.s_last_pseg = NANDFS_FIRST_BLOCK; + super_block.s_last_seq = 1; + super_block.s_free_blocks_count = + (nsegments - bad_segments_count) * blocks_per_segment; + super_block.s_mtime = 0; + super_block.s_wtime = nandfs_time; + super_block.s_state = NANDFS_VALID_FS; + + super_block.s_sum = crc32_le(0, (const uint8_t *)&super_block, + NANDFS_SB_BYTES); +} + +static void +save_super_block(void *data) +{ + + memcpy(data, &super_block, sizeof(super_block)); +} + +static void +save_super_root(void) +{ + + sr->sr_bytes = NANDFS_SR_BYTES; + sr->sr_flags = 0; + sr->sr_nongc_ctime = nandfs_time; + datfile.inode = &sr->sr_dat; + cpfile.inode = &sr->sr_cpfile; + sufile.inode = &sr->sr_sufile; +} + +static struct nandfs_dir_entry * +add_de(void *block, struct nandfs_dir_entry *de, uint64_t ino, + const char *name, uint8_t type) +{ + uint16_t reclen; + + /* modify last de */ + de->rec_len = NANDFS_DIR_REC_LEN(de->name_len); + de = (void *)((uint8_t *)de + de->rec_len); + + reclen = blocksize - ((uintptr_t)de - (uintptr_t)block); + if (reclen < NANDFS_DIR_REC_LEN(strlen(name))) { + printf("nandfs: too many dir entries for one block\n"); + return (NULL); + } + + de->inode = ino; + de->rec_len = reclen; + de->name_len = strlen(name); + de->file_type = type; + memset(de->name, 0, + (strlen(name) + NANDFS_DIR_PAD - 1) & ~NANDFS_DIR_ROUND); + memcpy(de->name, name, strlen(name)); + + return (de); +} + +static struct nandfs_dir_entry * +make_dir(void *block, uint64_t ino, uint64_t parent_ino) +{ + struct nandfs_dir_entry *de = (struct nandfs_dir_entry *)block; + + /* create '..' entry */ + de->inode = parent_ino; + de->rec_len = NANDFS_DIR_REC_LEN(2); + de->name_len = 2; + de->file_type = DT_DIR; + memset(de->name, 0, NANDFS_DIR_NAME_LEN(2)); + memcpy(de->name, "..", 2); + + /* create '.' entry */ + de = (void *)((uint8_t *)block + NANDFS_DIR_REC_LEN(2)); + de->inode = ino; + de->rec_len = blocksize - NANDFS_DIR_REC_LEN(2); + de->name_len = 1; + de->file_type = DT_DIR; + memset(de->name, 0, NANDFS_DIR_NAME_LEN(1)); + memcpy(de->name, ".", 1); + + return (de); +} + +static void +save_root_dir(void) +{ + struct file_info *root = &user_files[0]; + struct nandfs_dir_entry *de; + uint32_t i; + void *block; + + block = get_block(root->blocks[0], 0); + + de = make_dir(block, root->ino, root->ino); + for (i = 1; i < nuserfiles; i++) + de = add_de(block, de, user_files[i].ino, user_files[i].name, + IFTODT(user_files[i].mode)); + + root->size = ((uintptr_t)de - (uintptr_t)block) + + NANDFS_DIR_REC_LEN(de->name_len); +} + +static void +save_sufile(void) +{ + struct nandfs_sufile_header *header; + struct nandfs_segment_usage *su; + uint64_t blk, i, off; + void *block; + int start; + + /* + * At the beginning just zero-out everything + */ + for (i = 0; i < sufile.nblocks; i++) + get_block(sufile.blocks[i], 0); + + start = 0; + + block = get_block(sufile.blocks[start], 0); + header = (struct nandfs_sufile_header *)block; + header->sh_ncleansegs = nsegments - bad_segments_count - 1; + header->sh_ndirtysegs = 1; + header->sh_last_alloc = 1; + + su = (struct nandfs_segment_usage *)header; + off = NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET; + /* Allocate data segment */ + su[off].su_lastmod = nandfs_time; + /* nblocks = segment blocks + segsum block + superroot */ + su[off].su_nblocks = seg_nblocks + 2; + su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY; + off++; + /* Allocate next segment */ + su[off].su_lastmod = nandfs_time; + su[off].su_nblocks = 0; + su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY; + for (i = 0; i < bad_segments_count; i++) { + nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &off); + debug("storing bad_segments[%jd]=%x at %jx off %jx\n", i, + bad_segments[i], blk, off); + block = get_block(sufile.blocks[blk], + off * sizeof(struct nandfs_segment_usage *)); + su = (struct nandfs_segment_usage *)block; + su[off].su_lastmod = nandfs_time; + su[off].su_nblocks = 0; + su[off].su_flags = NANDFS_SEGMENT_USAGE_ERROR; + } +} + +static void +save_cpfile(void) +{ + struct nandfs_cpfile_header *header; + struct nandfs_checkpoint *cp, *initial_cp; + int i, entries = blocksize / sizeof(struct nandfs_checkpoint); + uint64_t cno; + + header = (struct nandfs_cpfile_header *)get_block(cpfile.blocks[0], 0); + header->ch_ncheckpoints = 1; + header->ch_nsnapshots = 0; + + cp = (struct nandfs_checkpoint *)header; + + /* fill first checkpoint data*/ + initial_cp = &cp[NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET]; + initial_cp->cp_flags = 0; + initial_cp->cp_checkpoints_count = 0; + initial_cp->cp_cno = NANDFS_FIRST_CNO; + initial_cp->cp_create = nandfs_time; + initial_cp->cp_nblk_inc = seg_endblock - 1; + initial_cp->cp_blocks_count = seg_nblocks; + memset(&initial_cp->cp_snapshot_list, 0, + sizeof(struct nandfs_snapshot_list)); + + ifile.inode = &initial_cp->cp_ifile_inode; + + /* mark rest of cp as invalid */ + cno = NANDFS_FIRST_CNO + 1; + i = NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1; + for (; i < entries; i++) { + cp[i].cp_cno = cno++; + cp[i].cp_flags = NANDFS_CHECKPOINT_INVALID; + } +} + +static void +init_inode(struct nandfs_inode *inode, struct file_info *file) +{ + + inode->i_blocks = file->nblocks; + inode->i_ctime = nandfs_time; + inode->i_mtime = nandfs_time; + inode->i_mode = file->mode & 0xffff; + inode->i_links_count = 1; + + if (file->size > 0) + inode->i_size = file->size; + else + inode->i_size = 0; + + if (file->ino == NANDFS_USER_INO) + inode->i_flags = SF_NOUNLINK|UF_NOUNLINK; + else + inode->i_flags = 0; +} + +static void +save_ifile(void) +{ + struct nandfs_inode *inode; + struct file_info *file; + uint64_t ino, blk, off; + uint32_t i; + + prepare_blockgrouped_file(ifile.blocks[0]); + for (i = 0; i <= NANDFS_USER_INO; i++) + alloc_blockgrouped_file(ifile.blocks[0], i); + + for (i = 0; i < nuserfiles; i++) { + file = &user_files[i]; + ino = file->ino; + blk = ino / (blocksize / sizeof(*inode)); + off = ino % (blocksize / sizeof(*inode)); + inode = + (struct nandfs_inode *)get_block(ifile.blocks[2 + blk], 2 + blk); + file->inode = &inode[off]; + init_inode(file->inode, file); + } + + init_inode(ifile.inode, &ifile); + init_inode(cpfile.inode, &cpfile); + init_inode(sufile.inode, &sufile); + init_inode(datfile.inode, &datfile); +} + +static int +create_fs(void) +{ + uint64_t start_block; + uint32_t segsum_size; + char *data; + int i; + + nuserfiles = (sizeof(user_files) / sizeof(user_files[0])); + + /* Count and assign blocks */ + count_seg_blocks(); + segsum_size = segment_size(); + start_block = NANDFS_FIRST_BLOCK + SIZE_TO_BLOCK(segsum_size); + assign_file_blocks(start_block); + + /* Create super root structure */ + save_super_root(); + + /* Create root directory */ + save_root_dir(); + + /* Fill in file contents */ + save_sufile(); + save_cpfile(); + save_ifile(); + save_datfile(); + + /* Save fsdata and superblocks */ + create_fsdata(); + create_super_block(); + + for (i = 0; i < NANDFS_NFSAREAS; i++) { + if (fsdata_blocks_state[i] != NANDFS_BLOCK_GOOD) + continue; + + data = get_block((i * erasesize)/blocksize, 0); + save_fsdata(data); + + data = get_block((i * erasesize + NANDFS_SBLOCK_OFFSET_BYTES) / + blocksize, 0); + if (blocksize > NANDFS_SBLOCK_OFFSET_BYTES) + data += NANDFS_SBLOCK_OFFSET_BYTES; + save_super_block(data); + memset(data + sizeof(struct nandfs_super_block), 0xff, + (blocksize - sizeof(struct nandfs_super_block) - + NANDFS_SBLOCK_OFFSET_BYTES)); + } + + /* Save segment summary and CRCs */ + save_segsum(get_block(NANDFS_FIRST_BLOCK, 0)); + + return (0); +} + +static void +write_fs(int fda) +{ + struct nandfs_block *block; + char *data; + u_int ret; + + /* Overwrite next block with ff if not nand device */ + if (!is_nand) { + data = get_block(seg_endblock, 0); + memset(data, 0xff, blocksize); + } + + LIST_FOREACH(block, &block_head, block_link) { + lseek(fda, block->number * blocksize, SEEK_SET); + ret = write(fda, block->data, blocksize); + if (ret != blocksize) + err(1, "cannot write filesystem data"); + } +} + +static void +check_parameters(void) +{ + int i; + + /* check blocksize */ + if ((blocksize < NANDFS_MIN_BLOCKSIZE) || (blocksize > MAXBSIZE) || + ((blocksize - 1) & blocksize)) { + errx(1, "Bad blocksize (%zu). Must be in range [%u-%u] " + "and a power of two.", blocksize, NANDFS_MIN_BLOCKSIZE, + MAXBSIZE); + } + + /* check blocks per segments */ + if ((blocks_per_segment < NANDFS_SEG_MIN_BLOCKS) || + ((blocksize - 1) & blocksize)) + errx(1, "Bad blocks per segment (%lu). Must be greater than " + "%u and a power of two.", blocks_per_segment, + NANDFS_SEG_MIN_BLOCKS); + + /* check reserved segment percentage */ + if ((rsv_segment_percent < 1) && (rsv_segment_percent > 99)) + errx(1, "Bad reserved segment percentage. " + "Must in range 1..99."); + + /* check volume label */ + i = 0; + if (volumelabel) { + while (isalnum(volumelabel[++i])) + ; + + if (volumelabel[i] != '\0') { + errx(1, "bad volume label. " + "Valid characters are alphanumerics."); + } + + if (strlen(volumelabel) >= 16) + errx(1, "Bad volume label. Length is longer than %d.", + 16); + } + + nandfs_time = time(NULL); +} + +static void +print_parameters(void) +{ + + printf("filesystem parameters:\n"); + printf("blocksize: %#zx sectorsize: %#zx\n", blocksize, sectorsize); + printf("erasesize: %#jx mediasize: %#jx\n", erasesize, mediasize); + printf("segment size: %#jx blocks per segment: %#x\n", segsize, + (uint32_t)blocks_per_segment); +} + +/* + * Exit with error if file system is mounted. + */ +static void +check_mounted(const char *fname, mode_t mode) +{ + struct statfs *mp; + const char *s1, *s2; + size_t len; + int n, r; + + if (!(n = getmntinfo(&mp, MNT_NOWAIT))) + err(1, "getmntinfo"); + + len = strlen(_PATH_DEV); + s1 = fname; + if (!strncmp(s1, _PATH_DEV, len)) + s1 += len; + + r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; + + for (; n--; mp++) { + s2 = mp->f_mntfromname; + + if (!strncmp(s2, _PATH_DEV, len)) + s2 += len; + if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || + !strcmp(s1, s2)) + errx(1, "%s is mounted on %s", fname, mp->f_mntonname); + } +} + +static void +calculate_geometry(int fd) +{ + struct chip_param_io chip_params; + char ident[DISK_IDENT_SIZE]; + char medianame[MAXPATHLEN]; + + /* Check storage type */ + g_get_ident(fd, ident, DISK_IDENT_SIZE); + g_get_name(ident, medianame, MAXPATHLEN); + debug("device name: %s", medianame); + + is_nand = (strstr(medianame, "gnand") != NULL); + debug("is_nand = %d", is_nand); + + sectorsize = g_sectorsize(fd); + debug("sectorsize: %#zx", sectorsize); + + /* Get storage size */ + mediasize = g_mediasize(fd); + debug("mediasize: %#jx", mediasize); + + /* Get storage erase unit size */ + if (!is_nand) + erasesize = NANDFS_DEF_ERASESIZE; + else if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) + errx(1, "Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + else + erasesize = chip_params.page_size * chip_params.pages_per_block; + + debug("erasesize: %#jx", (uintmax_t)erasesize); + + if (blocks_per_segment == 0) { + if (erasesize >= NANDFS_MIN_SEGSIZE) + blocks_per_segment = erasesize / blocksize; + else + blocks_per_segment = NANDFS_MIN_SEGSIZE / blocksize; + } + + /* Calculate number of segments */ + segsize = blocksize * blocks_per_segment; + nsegments = ((mediasize - NANDFS_NFSAREAS * erasesize) / segsize) - 2; + debug("segsize: %#jx", segsize); + debug("nsegments: %#jx", nsegments); +} + +static void +erase_device(int fd) +{ + int rest, failed; + uint64_t i, nblocks; + off_t offset; + + failed = 0; + for (i = 0; i < NANDFS_NFSAREAS; i++) { + debug("Deleting %jx\n", i * erasesize); + if (g_delete(fd, i * erasesize, erasesize)) { + printf("cannot delete %jx\n", i * erasesize); + fsdata_blocks_state[i] = NANDFS_BLOCK_BAD; + failed++; + } else + fsdata_blocks_state[i] = NANDFS_BLOCK_GOOD; + } + + if (failed == NANDFS_NFSAREAS) { + printf("%d first blocks not usable. Unable to create " + "filesystem.\n", failed); + exit(1); + } + + for (i = 0; i < nsegments; i++) { + offset = NANDFS_NFSAREAS * erasesize + i * segsize; + if (g_delete(fd, offset, segsize)) { + printf("cannot delete segment %jx (offset %jd)\n", + i, offset); + bad_segments_count++; + bad_segments = realloc(bad_segments, + bad_segments_count * sizeof(uint32_t)); + bad_segments[bad_segments_count - 1] = i; + } + } + + if (bad_segments_count == nsegments) { + printf("no valid segments\n"); + exit(1); + } + + /* Delete remaining blocks at the end of device */ + rest = mediasize % segsize; + nblocks = rest / erasesize; + for (i = 0; i < nblocks; i++) { + offset = (segsize * nsegments) + (i * erasesize); + if (g_delete(fd, offset, erasesize)) { + printf("cannot delete space after last segment " + "- probably a bad block\n"); + } + } +} + +static void +erase_initial(int fd) +{ + char buf[512]; + u_int i; + + memset(buf, 0xff, sizeof(buf)); + + lseek(fd, 0, SEEK_SET); + for (i = 0; i < NANDFS_NFSAREAS * erasesize; i += sizeof(buf)) + write(fd, buf, sizeof(buf)); +} + +static void +create_nandfs(int fd) +{ + + create_fs(); + + write_fs(fd); +} + +static void +print_summary(void) +{ + + printf("filesystem created succesfully\n"); + printf("total segments: %#jx valid segments: %#jx\n", nsegments, + nsegments - bad_segments_count); + printf("total space: %ju MB free: %ju MB\n", + (nsegments * + blocks_per_segment * blocksize) / (1024 * 1024), + ((nsegments - bad_segments_count) * + blocks_per_segment * blocksize) / (1024 * 1024)); +} + +int +main(int argc, char *argv[]) +{ + struct stat sb; + char buf[MAXPATHLEN]; + const char opts[] = "b:B:L:m:"; + const char *fname; + int ch, fd; + + while ((ch = getopt(argc, argv, opts)) != -1) { + switch (ch) { + case 'b': + blocksize = strtol(optarg, (char **)NULL, 10); + if (blocksize == 0) + usage(); + break; + case 'B': + blocks_per_segment = strtol(optarg, (char **)NULL, 10); + if (blocks_per_segment == 0) + usage(); + break; + case 'L': + volumelabel = optarg; + break; + case 'm': + rsv_segment_percent = strtol(optarg, (char **)NULL, 10); + if (rsv_segment_percent == 0) + usage(); + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + if (argc < 1 || argc > 2) + usage(); + + /* construct proper device path */ + fname = *argv++; + if (!strchr(fname, '/')) { + snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); + if (!(fname = strdup(buf))) + err(1, NULL); + } + + fd = g_open(fname, 1); + if (fd == -1) + err(1, "Cannot open %s", fname); + + if (fstat(fd, &sb) == -1) + err(1, "Cannot stat %s", fname); + if (!S_ISCHR(sb.st_mode)) + warnx("%s is not a character device", fname); + + check_mounted(fname, sb.st_mode); + + calculate_geometry(fd); + + check_parameters(); + + print_parameters(); + + if (is_nand) + erase_device(fd); + else + erase_initial(fd); + + create_nandfs(fd); + + print_summary(); + + g_close(fd); + + return (0); +} + + diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index dffe304..2c23a9c 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -250,6 +250,8 @@ MAN= aac.4 \ mwlfw.4 \ mxge.4 \ my.4 \ + nand.4 \ + nandsim.4 \ natm.4 \ natmip.4 \ ncr.4 \ diff --git a/share/man/man4/nand.4 b/share/man/man4/nand.4 new file mode 100644 index 0000000..d23e292 --- /dev/null +++ b/share/man/man4/nand.4 @@ -0,0 +1,140 @@ +.\" +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Semihalf under sponsorship from +.\" the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 8, 2012 +.Dt NAND 4 +.Os +.Sh NAME +.Nm nand +.Nd NAND Flash framework +.Sh SYNOPSIS +.Cd "device nand" +.Sh DESCRIPTION +The +.Fx +.Nm +framework consists of a set of interfaces that aim to provide an extensible, +object oriented environement for NAND controllers and NAND Flash memory chips +from various hardware vendors, and to allow for uniform and flexible +management of the NAND devices. It comprises of the following major +components: +.Bl -bullet +.It +NAND Flash controller (NFC) interface. +.Pp +Defines methods which allow to send commands as well as send/receive data +between the controller and a NAND chip. Back-end drivers for specific NAND +controllers plug into this interface and implement low-level routines for a +given NAND controller. +.Pp +This layer implements basic functionality of a NAND Flash controller. It +allows to send command and address to chip, drive CS (chip select line), as +well as read/write to the selected NAND chip. This layer is independent of +NAND chip devices actually connected to the controller. +.It +NAND chip interface. +.Pp +Provides basic operations like read page, program page, erase block. Currently +three generic classes of drivers are available, which provide support for the +following chips: +.Bl -bullet +.It +large page +.It +small page +.It +ONFI-compliant +.El +.Pp +This layer implements basic operations to be performed on a NAND chip, like +read, program, erase, get status etc. Since these operations use specific +commands (depending on the vendor), each chip has potentially its own +implementation of the commands set. +.Pp +The framework is extensible so it is also possible to create a custom command +set for a non standard chip support. +.It +NANDbus. +.Pp +This layer is responsible for enumerating NAND chips in the system and +establishing the hierarchy between chips and their supervising controllers. +.Pp +Its main purpose is detecting type of NAND chips connected to a given chip +select (CS line). It also allows manages locking access to the NAND +controller. NANDbus passes requests from an active chip to the chip +controller. +.It +NAND character / GEOM device. +.Pp +For each NAND chip found in a system a character and GEOM devices are created +which allows to read / write directly to a device, as well as perform other +specific operations (like via ioctl). +.Pp +There are two GEOM devices created for each NAND chip: +.Bl -bullet +.It +raw device +.It +normal device +.El +.Pp +Raw device allows to bypass ECC checking when reading/writing to it, while +normal device always uses ECC algorithm to validate the read data. +.Pp +NAND character devices will be created for each NAND chip detected while +probing the NAND controller. +.El +.Sh SEE ALSO +.Xr libnandfs 3 , +.Xr gnand 4 , +.Xr nandsim 4 , +.Xr nandfs 5 , +.Xr makefs 8 , +.Xr mount_nandfs 8 , +.Xr nandfs 8 , +.Xr nandsim 8 , +.Xr nandtool 8 , +.Xr newfs_nandfs 8 , +.Xr umount_nandfs 8 +.Sh STANDARDS +Open NAND Flash Interface Working Group +.Pq Vt ONFI . +.Sh HISTORY +The +.Nm +framework support first appeared in +.Fx 10.0 . +.Sh AUTHOR +The +.Nm +framework was designed and developed by +.An Grzegorz Bernacki . +This manual page was written by +.An Rafal Jaworowski . diff --git a/share/man/man4/nandsim.4 b/share/man/man4/nandsim.4 new file mode 100644 index 0000000..154d1ff --- /dev/null +++ b/share/man/man4/nandsim.4 @@ -0,0 +1,184 @@ +.\" +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Semihalf under sponsorship from +.\" the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 8, 2012 +.Dt NANDSIM 4 +.Os +.Sh NAME +.Nm nandsim +.Nd NAND Flash simulator driver +.Sh SYNOPSIS +.Cd "device nand" +.Cd "device nandsim" +.Cd "options ALQ" +.Sh DESCRIPTION +The +.Nm +is part of the +.Fx +NAND framework +.Xr nand 4 +and can be characterized with the following highlights: +.Bl -bullet +.It +plugs into the +.Xr nand 4 +framework APIs as if it were a hardware controller (hanging on the nexus bus) +with real NAND chips connected to it +.It +physically part of the kernel code (either statically linked into the kernel +image or built as a module) +.It +controlled with a user space program +.Xr nandsim 8 +.El +.Pp +From the user perspective, the +.Nm +allows for imitating ONFI-compliant NAND Flash devices as if they were +attached to the system via a virtual controller. +.Pp +Some +.Nm +features rely on the ability to log contents to a file, which is achieved +through the +.Xr alq 9 +facility. +.Sh SEE ALSO +.Xr nand 4 , +.Xr nandsim.conf 5 , +.Xr nandsim 8 +.Sh STANDARDS +Open NAND Flash Interface Working Group +.Pq Vt ONFI . +.Sh HISTORY +The +.Nm +support first appeared in +.Fx 10.0 . +.Sh AUTHOR +The +.Nm +kernel driver was developed by +.An Grzegorz Bernacki . +This manual page was written by +.An Rafal Jaworowski . +.\" +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Semihalf under sponsorship from +.\" the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 8, 2012 +.Dt NANDSIM 4 +.Os +.Sh NAME +.Nm nandsim +.Nd NAND Flash simulator driver +.Sh SYNOPSIS +.Cd "device nand" +.Cd "device nandsim" +.Cd "options ALQ" +.Sh DESCRIPTION +The +.Nm +is part of the +.Fx +NAND framework +.Xr nand 4 +and can be characterized with the following highlights: +.Bl -bullet +.It +plugs into the +.Xr nand 4 +framework APIs as if it were a hardware controller (hanging on the nexus bus) +with real NAND chips connected to it +.It +physically part of the kernel code (either statically linked into the kernel +image or built as a module) +.It +controlled with a user space program +.Xr nandsim 8 +.El +.Pp +From the user perspective, the +.Nm +allows for imitating ONFI-compliant NAND Flash devices as if they were +attached to the system via a virtual controller. +.Pp +Some +.Nm +features rely on the ability to log contents to a file, which is achieved +through the +.Xr alq 9 +facility. +.Sh SEE ALSO +.Xr nand 4 , +.Xr nandsim.conf 5 , +.Xr nandsim 8 +.Sh STANDARDS +Open NAND Flash Interface Working Group +.Pq Vt ONFI . +.Sh HISTORY +The +.Nm +support first appeared in +.Fx 10.0 . +.Sh AUTHOR +The +.Nm +kernel driver was developed by +.An Grzegorz Bernacki . +This manual page was written by +.An Rafal Jaworowski . diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile index b05c1eb..c7a3c59 100644 --- a/share/man/man5/Makefile +++ b/share/man/man5/Makefile @@ -85,6 +85,10 @@ MLINKS+=resolver.5 resolv.conf.5 MAN+= hesiod.conf.5 .endif +.if ${MK_NAND} != "no" +MAN+= nandfs.5 +.endif + .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" _boot.config.5= boot.config.5 .endif diff --git a/share/man/man5/nandfs.5 b/share/man/man5/nandfs.5 new file mode 100644 index 0000000..43d586c --- /dev/null +++ b/share/man/man5/nandfs.5 @@ -0,0 +1,129 @@ +.\" +.\" Copyright (c) 2010 Semihalf +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd Nov 11, 2010 +.Dt NANDFS 5 +.Os +.Sh NAME +.Nm nandfs +.Nd NAND Flash file system +.Sh SYNOPSIS +To compile support for the +.Nm , +place the following in your kernel configuration file: +.Bd -ragged -offset indent +.Cd "options NANDFS" +.Ed +.Pp +Even though the NAND FS can be used with any storage media, it has been +optimized and designed towards NAND Flash devices, so typically the following +driver is used: +.Bd -ragged -offset indent +.Cd "device nand" +.Ed +.Sh DESCRIPTION +The +.Nm +driver enables +.Fx +with support for NAND-oriented file system. +.Pp +It is a log-structured style file system with the following major features and +characteristics: +.Bl -bullet +.It +Hard links, symbolic links support +.It +Block journaling +.It +Copy-On-Write +.It +Snapshots (continuous, taken automatically, simultaneously mountable) +.It +Quick crash recovery at mount time +.It +64-bit data structures; supports many files, large files and volumes +.It +POSIX file permissions +.It +Checksum / ECC +.El +.Sh EXAMPLES +The most common usage is mounting the file system: +.Pp +.Dl "mount -t nandfs /dev/ /mnt" +.Pp +or: +.Dl "mount_nandfs /dev/ /mnt" +.Pp +where +.Ar gnandN +is the GEOM device representing a Flash partition (slice) containing the +.Nm +structure, and +.Pa /mnt +is a mount point. +.Pp +.Pp +It is possible to define an entry in +.Pa /etc/fstab +for the +.Nm : +.Bd -literal +/dev/gnand0 /flash nandfs rw 0 0 +.Ed +.Pp +This will mount a +.Nm +partition at the specified mount point during system boot. +.Sh SEE ALSO +.Xr gnand 4 , +.Xr nand 4 , +.Xr mount_nandfs 8 , +.Xr nandfs 8 , +.Xr nandsim 8 , +.Xr nandtool 8 , +.Xr umount_nandfs 8 +.Sh HISTORY +The NAND FS concepts are based on NILFS principles and initial implementation +was derived from early NILFS NetBSD code (read only). Since then the NAND FS +code diverged significantly and is by no means compatible with NILFS. +.Pp +The NAND Flash file system first appeared in +.Fx 10.0 . +.Sh AUTHOR +The NAND FS was written by +.An Grzegorz Bernacki with the help of +.An Mateusz Guzik , +based on the NetBSD code created by +.An Reinoud Zandijk . +Additional help and support by +.An Lukasz Plachno , +.An Jan Sieka and +.An Lukasz Wojcik . +This manual page was written by +.An Rafal Jaworowski . diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 9802e79..b19253e 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -426,6 +426,7 @@ __DEFAULT_NO_OPTIONS = \ ICONV \ IDEA \ LIBCPLUSPLUS \ + NAND \ OFED \ SHARED_TOOLCHAIN diff --git a/sys/boot/arm/uboot/Makefile b/sys/boot/arm/uboot/Makefile index 906fc87..b69d38c 100644 --- a/sys/boot/arm/uboot/Makefile +++ b/sys/boot/arm/uboot/Makefile @@ -15,6 +15,11 @@ LOADER_DISK_SUPPORT?= yes LOADER_UFS_SUPPORT?= yes LOADER_CD9660_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= no +.if ${MK_NAND} != "no" +LOADER_NANDFS_SUPPORT?= yes +.else +LOADER_NANDFS_SUPPORT?= no +.endif LOADER_NET_SUPPORT?= yes LOADER_NFS_SUPPORT?= yes LOADER_TFTP_SUPPORT?= no @@ -38,6 +43,9 @@ CFLAGS+= -DLOADER_CD9660_SUPPORT .if ${LOADER_EXT2FS_SUPPORT} == "yes" CFLAGS+= -DLOADER_EXT2FS_SUPPORT .endif +.if ${LOADER_NANDFS_SUPPORT} == "yes" +CFLAGS+= -DLOADER_NANDFS_SUPPORT +.endif .if ${LOADER_GZIP_SUPPORT} == "yes" CFLAGS+= -DLOADER_GZIP_SUPPORT .endif diff --git a/sys/boot/arm/uboot/conf.c b/sys/boot/arm/uboot/conf.c index b797565..03dc641 100644 --- a/sys/boot/arm/uboot/conf.c +++ b/sys/boot/arm/uboot/conf.c @@ -56,6 +56,9 @@ struct fs_ops *file_system[] = { #if defined(LOADER_EXT2FS_SUPPORT) &ext2fs_fsops, #endif +#if defined(LOADER_NANDFS_SUPPORT) + &nandfs_fsops, +#endif #if defined(LOADER_NFS_SUPPORT) &nfs_fsops, #endif diff --git a/sys/boot/arm/uboot/version b/sys/boot/arm/uboot/version index 38c7eb6..486c412 100644 --- a/sys/boot/arm/uboot/version +++ b/sys/boot/arm/uboot/version @@ -3,6 +3,7 @@ $FreeBSD$ NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this file is important. Make sure the current version number is on line 6. +1.2: Extended with NAND FS support. 1.1: Flattened Device Tree blob support. 1.0: Added storage support. Booting from HDD, USB, etc. is now possible. 0.5: Initial U-Boot/arm version (netbooting only). diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile index e2f8892..d3f01b6 100644 --- a/sys/boot/i386/loader/Makefile +++ b/sys/boot/i386/loader/Makefile @@ -53,6 +53,9 @@ CFLAGS+= -DLOADER_GZIP_SUPPORT .if !defined(LOADER_NO_GPT_SUPPORT) CFLAGS+= -DLOADER_GPT_SUPPORT .endif +.if defined(LOADER_NANDFS_SUPPORT) +CFLAGS+= -DLOADER_NANDFS_SUPPORT +.endif # Always add MI sources .PATH: ${.CURDIR}/../../common diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c index 09d6a4e..dda2eac 100644 --- a/sys/boot/i386/loader/conf.c +++ b/sys/boot/i386/loader/conf.c @@ -74,6 +74,9 @@ struct fs_ops *file_system[] = { &ext2fs_fsops, &dosfs_fsops, &cd9660_fsops, +#if defined(LOADER_NANDFS_SUPPORT) + &nandfs_fsops, +#endif &splitfs_fsops, #if defined(LOADER_ZFS_SUPPORT) &zfs_fsops, diff --git a/sys/conf/files b/sys/conf/files index 3c84cf6..3a2b65e 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1660,6 +1660,21 @@ dev/mxge/mxge_ethp_z8e.c optional mxge pci dev/mxge/mxge_rss_eth_z8e.c optional mxge pci dev/mxge/mxge_rss_ethp_z8e.c optional mxge pci dev/my/if_my.c optional my +dev/nand/nand.c optional nand +dev/nand/nand_bbt.c optional nand +dev/nand/nand_cdev.c optional nand +dev/nand/nand_generic.c optional nand +dev/nand/nand_geom.c optional nand +dev/nand/nand_id.c optional nand +dev/nand/nandbus.c optional nand +dev/nand/nandbus_if.m optional nand +dev/nand/nand_if.m optional nand +dev/nand/nandsim.c optional nandsim nand +dev/nand/nandsim_chip.c optional nandsim nand +dev/nand/nandsim_ctrl.c optional nandsim nand +dev/nand/nandsim_log.c optional nandsim nand +dev/nand/nandsim_swap.c optional nandsim nand +dev/nand/nfc_if.m optional nand dev/ncv/ncr53c500.c optional ncv dev/ncv/ncr53c500_pccard.c optional ncv pccard dev/netmap/netmap.c optional netmap @@ -2252,6 +2267,20 @@ fs/msdosfs/msdosfs_iconv.c optional msdosfs_iconv fs/msdosfs/msdosfs_lookup.c optional msdosfs fs/msdosfs/msdosfs_vfsops.c optional msdosfs fs/msdosfs/msdosfs_vnops.c optional msdosfs +fs/nandfs/bmap.c optional nandfs +fs/nandfs/nandfs_alloc.c optional nandfs +fs/nandfs/nandfs_bmap.c optional nandfs +fs/nandfs/nandfs_buffer.c optional nandfs +fs/nandfs/nandfs_cleaner.c optional nandfs +fs/nandfs/nandfs_cpfile.c optional nandfs +fs/nandfs/nandfs_dat.c optional nandfs +fs/nandfs/nandfs_dir.c optional nandfs +fs/nandfs/nandfs_ifile.c optional nandfs +fs/nandfs/nandfs_segment.c optional nandfs +fs/nandfs/nandfs_subr.c optional nandfs +fs/nandfs/nandfs_sufile.c optional nandfs +fs/nandfs/nandfs_vfsops.c optional nandfs +fs/nandfs/nandfs_vnops.c optional nandfs fs/nfs/nfs_commonkrpc.c optional nfscl | nfsd fs/nfs/nfs_commonsubs.c optional nfscl | nfsd fs/nfs/nfs_commonport.c optional nfscl | nfsd diff --git a/sys/conf/options b/sys/conf/options index 4c95967..8f810f4 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -213,6 +213,7 @@ FDESCFS opt_dontuse.h FFS opt_dontuse.h HPFS opt_dontuse.h MSDOSFS opt_dontuse.h +NANDFS opt_dontuse.h NTFS opt_dontuse.h NULLFS opt_dontuse.h NWFS opt_dontuse.h diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c new file mode 100644 index 0000000..ad5bc40 --- /dev/null +++ b/sys/dev/nand/nand.c @@ -0,0 +1,832 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "nfc_if.h" +#include "nand_if.h" +#include "nandbus_if.h" +#include + +#define NAND_RESET_DELAY 1000 /* tRST */ +#define NAND_ERASE_DELAY 3000 /* tBERS */ +#define NAND_PROG_DELAY 700 /* tPROG */ +#define NAND_READ_DELAY 50 /* tR */ + +#define BIT0(x) ((x) & 0x1) +#define BIT1(x) (BIT0(x >> 1)) +#define BIT2(x) (BIT0(x >> 2)) +#define BIT3(x) (BIT0(x >> 3)) +#define BIT4(x) (BIT0(x >> 4)) +#define BIT5(x) (BIT0(x >> 5)) +#define BIT6(x) (BIT0(x >> 6)) +#define BIT7(x) (BIT0(x >> 7)) + +#define SOFTECC_SIZE 256 +#define SOFTECC_BYTES 3 + +int nand_debug_flag = 0; +SYSCTL_INT(_debug, OID_AUTO, nand_debug, CTLFLAG_RW, &nand_debug_flag, 0, + "NAND subsystem debug flag"); + +static void +nand_tunable_init(void *arg) +{ + + TUNABLE_INT_FETCH("debug.nand", &nand_debug_flag); +} + +SYSINIT(nand_tunables, SI_SUB_VFS, SI_ORDER_ANY, nand_tunable_init, NULL); + +MALLOC_DEFINE(M_NAND, "NAND", "NAND dynamic data"); + +static void calculate_ecc(const uint8_t *, uint8_t *); +static int correct_ecc(uint8_t *, uint8_t *, uint8_t *); + +void +nand_debug(int level, const char *fmt, ...) +{ + va_list ap; + + if (!(nand_debug_flag & level)) + return; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} + +void +nand_init(struct nand_softc *nand, device_t dev, int ecc_mode, + int ecc_bytes, int ecc_size, uint16_t *eccposition, char *cdev_name) +{ + + nand->ecc.eccmode = ecc_mode; + nand->chip_cdev_name = cdev_name; + + if (ecc_mode == NAND_ECC_SOFT) { + nand->ecc.eccbytes = SOFTECC_BYTES; + nand->ecc.eccsize = SOFTECC_SIZE; + } else if (ecc_mode != NAND_ECC_NONE) { + nand->ecc.eccbytes = ecc_bytes; + nand->ecc.eccsize = ecc_size; + if (eccposition) + nand->ecc.eccpositions = eccposition; + } +} + +void +nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params) +{ + struct chip_geom *cg; + + cg = &chip->chip_geom; + + init_chip_geom(cg, params->luns, params->blocks_per_lun, + params->pages_per_block, params->bytes_per_page, + params->spare_bytes_per_page); + chip->t_bers = params->t_bers; + chip->t_prog = params->t_prog; + chip->t_r = params->t_r; + chip->t_ccs = params->t_ccs; + + if (params->features & ONFI_FEAT_16BIT) + chip->flags |= NAND_16_BIT; +} + +void +nand_set_params(struct nand_chip *chip, struct nand_params *params) +{ + struct chip_geom *cg; + uint32_t blocks_per_chip; + + cg = &chip->chip_geom; + blocks_per_chip = (params->chip_size << 20) / + (params->page_size * params->pages_per_block); + + init_chip_geom(cg, 1, blocks_per_chip, + params->pages_per_block, params->page_size, + params->oob_size); + + chip->t_bers = NAND_ERASE_DELAY; + chip->t_prog = NAND_PROG_DELAY; + chip->t_r = NAND_READ_DELAY; + chip->t_ccs = 0; + + if (params->flags & NAND_16_BIT) + chip->flags |= NAND_16_BIT; +} + +int +nand_init_stat(struct nand_chip *chip) +{ + struct block_stat *blk_stat; + struct page_stat *pg_stat; + struct chip_geom *cg; + uint32_t blks, pgs; + + cg = &chip->chip_geom; + blks = cg->blks_per_lun * cg->luns; + blk_stat = malloc(sizeof(struct block_stat) * blks, M_NAND, + M_WAITOK | M_ZERO); + if (!blk_stat) + return (ENOMEM); + + pgs = blks * cg->pgs_per_blk; + pg_stat = malloc(sizeof(struct page_stat) * pgs, M_NAND, + M_WAITOK | M_ZERO); + if (!pg_stat) { + free(blk_stat, M_NAND); + return (ENOMEM); + } + + chip->blk_stat = blk_stat; + chip->pg_stat = pg_stat; + + return (0); +} + +void +nand_destroy_stat(struct nand_chip *chip) +{ + + free(chip->pg_stat, M_NAND); + free(chip->blk_stat, M_NAND); +} + +int +init_chip_geom(struct chip_geom *cg, uint32_t luns, uint32_t blks_per_lun, + uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size) +{ + int shift; + + if (!cg) + return (-1); + + cg->luns = luns; + cg->blks_per_lun = blks_per_lun; + cg->blks_per_chip = blks_per_lun * luns; + cg->pgs_per_blk = pgs_per_blk; + + cg->page_size = pg_size; + cg->oob_size = oob_size; + cg->block_size = cg->page_size * cg->pgs_per_blk; + cg->chip_size = cg->block_size * cg->blks_per_chip; + + shift = fls(cg->pgs_per_blk - 1); + cg->pg_mask = (1 << shift) - 1; + cg->blk_shift = shift; + + if (cg->blks_per_lun > 0) { + shift = fls(cg->blks_per_lun - 1); + cg->blk_mask = ((1 << shift) - 1) << cg->blk_shift; + } else { + shift = 0; + cg->blk_mask = 0; + } + + cg->lun_shift = shift + cg->blk_shift; + shift = fls(cg->luns - 1); + cg->lun_mask = ((1 << shift) - 1) << cg->lun_shift; + + nand_debug(NDBG_NAND, "Masks: lun 0x%x blk 0x%x page 0x%x\n" + "Shifts: lun %d blk %d", + cg->lun_mask, cg->blk_mask, cg->pg_mask, + cg->lun_shift, cg->blk_shift); + + return (0); +} + +int +nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun, + uint32_t *blk, uint32_t *pg) +{ + + if (!cg || !lun || !blk || !pg) + return (-1); + + if (row & ~(cg->lun_mask | cg->blk_mask | cg->pg_mask)) { + nand_debug(NDBG_NAND,"Address out of bounds\n"); + return (-1); + } + + *lun = (row & cg->lun_mask) >> cg->lun_shift; + *blk = (row & cg->blk_mask) >> cg->blk_shift; + *pg = (row & cg->pg_mask); + + nand_debug(NDBG_NAND,"address %x-%x-%x\n", *lun, *blk, *pg); + + return (0); +} + +int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row) +{ + uint32_t lun, block, pg_in_blk; + + if (!cg || !row) + return (-1); + + block = page / cg->pgs_per_blk; + pg_in_blk = page % cg->pgs_per_blk; + + lun = block / cg->blks_per_lun; + block = block % cg->blks_per_lun; + + *row = (lun << cg->lun_shift) & cg->lun_mask; + *row |= ((block << cg->blk_shift) & cg->blk_mask); + *row |= (pg_in_blk & cg->pg_mask); + + return (0); +} + +int +nand_check_page_boundary(struct nand_chip *chip, uint32_t page) +{ + struct chip_geom* cg; + + cg = &chip->chip_geom; + if (page >= (cg->pgs_per_blk * cg->blks_per_lun * cg->luns)) { + nand_debug(NDBG_GEN,"%s: page number too big %#x\n", + __func__, page); + return (1); + } + + return (0); +} + +void +nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param) +{ + struct chip_geom *cg; + + cg = &chip->chip_geom; + param->page_size = cg->page_size; + param->oob_size = cg->oob_size; + + param->blocks = cg->blks_per_lun * cg->luns; + param->pages_per_block = cg->pgs_per_blk; +} + +static uint16_t * +default_software_ecc_positions(struct nand_chip *chip) +{ + struct nand_ecc_data *eccd; + + eccd = &chip->nand->ecc; + + if (eccd->eccpositions) + return (eccd->eccpositions); + + switch (chip->chip_geom.oob_size) { + case 16: + return ((uint16_t *)&default_software_ecc_positions_16); + case 64: + return ((uint16_t *)&default_software_ecc_positions_64); + case 128: + return ((uint16_t *)&default_software_ecc_positions_128); + default: + return (NULL); /* No ecc bytes positions defs available */ + } + + return (NULL); +} + +static void +calculate_ecc(const uint8_t *buf, uint8_t *ecc) +{ + uint8_t p8, byte; + int i; + + memset(ecc, 0, 3); + + for (i = 0; i < 256; i++) { + byte = buf[i]; + ecc[0] ^= (BIT0(byte) ^ BIT2(byte) ^ BIT4(byte) ^ + BIT6(byte)) << 2; + ecc[0] ^= (BIT1(byte) ^ BIT3(byte) ^ BIT5(byte) ^ + BIT7(byte)) << 3; + ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT4(byte) ^ + BIT5(byte)) << 4; + ecc[0] ^= (BIT2(byte) ^ BIT3(byte) ^ BIT6(byte) ^ + BIT7(byte)) << 5; + ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^ + BIT3(byte)) << 6; + ecc[0] ^= (BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^ + BIT7(byte)) << 7; + + p8 = BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^ + BIT3(byte) ^ BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^ + BIT7(byte); + + if (p8) { + ecc[2] ^= (0x1 << BIT0(i)); + ecc[2] ^= (0x4 << BIT1(i)); + ecc[2] ^= (0x10 << BIT2(i)); + ecc[2] ^= (0x40 << BIT3(i)); + + ecc[1] ^= (0x1 << BIT4(i)); + ecc[1] ^= (0x4 << BIT5(i)); + ecc[1] ^= (0x10 << BIT6(i)); + ecc[1] ^= (0x40 << BIT7(i)); + } + } + ecc[0] = ~ecc[0]; + ecc[1] = ~ecc[1]; + ecc[2] = ~ecc[2]; + ecc[0] |= 3; +} + +static int +correct_ecc(uint8_t *buf, uint8_t *calc_ecc, uint8_t *read_ecc) +{ + uint8_t ecc0, ecc1, ecc2, onesnum, bit, byte; + uint16_t addr = 0; + + ecc0 = calc_ecc[0] ^ read_ecc[0]; + ecc1 = calc_ecc[1] ^ read_ecc[1]; + ecc2 = calc_ecc[2] ^ read_ecc[2]; + + if (!ecc0 && !ecc1 && !ecc2) + return (ECC_OK); + + addr = BIT3(ecc0) | (BIT5(ecc0) << 1) | (BIT7(ecc0) << 2); + addr |= (BIT1(ecc2) << 3) | (BIT3(ecc2) << 4) | + (BIT5(ecc2) << 5) | (BIT7(ecc2) << 6); + addr |= (BIT1(ecc1) << 7) | (BIT3(ecc1) << 8) | + (BIT5(ecc1) << 9) | (BIT7(ecc1) << 10); + + onesnum = 0; + while (ecc0 || ecc1 || ecc2) { + if (ecc0 & 1) + onesnum++; + if (ecc1 & 1) + onesnum++; + if (ecc2 & 1) + onesnum++; + + ecc0 >>= 1; + ecc1 >>= 1; + ecc2 >>= 1; + } + + if (onesnum == 11) { + /* Correctable error */ + bit = addr & 7; + byte = addr >> 3; + buf[byte] ^= (1 << bit); + return (ECC_CORRECTABLE); + } else if (onesnum == 1) { + /* ECC error */ + return (ECC_ERROR_ECC); + } else { + /* Uncorrectable error */ + return (ECC_UNCORRECTABLE); + } + + return (0); +} + +int +nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc) +{ + int steps = pagesize / SOFTECC_SIZE; + int i = 0, j = 0; + + for (; i < (steps * SOFTECC_BYTES); + i += SOFTECC_BYTES, j += SOFTECC_SIZE) { + calculate_ecc(&buf[j], &ecc[i]); + } + + return (0); +} + +int +nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize, + uint8_t *readecc, uint8_t *calcecc) +{ + int steps = pagesize / SOFTECC_SIZE; + int i = 0, j = 0, ret = 0; + + for (i = 0; i < (steps * SOFTECC_BYTES); + i += SOFTECC_BYTES, j += SOFTECC_SIZE) { + ret += correct_ecc(&buf[j], &calcecc[i], &readecc[i]); + if (ret < 0) + return (ret); + } + + return (ret); +} + +static int +offset_to_page(struct chip_geom *cg, uint32_t offset) +{ + + return (offset / cg->page_size); +} + +int +nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf, + uint32_t len) +{ + struct chip_geom *cg; + struct nand_ecc_data *eccd; + struct page_stat *pg_stat; + device_t nandbus; + void *oob = NULL; + uint8_t *ptr; + uint16_t *eccpos = NULL; + uint32_t page, num, steps = 0; + int i, retval = 0, needwrite; + + nand_debug(NDBG_NAND,"%p read page %x[%x]", chip, offset, len); + cg = &chip->chip_geom; + eccd = &chip->nand->ecc; + page = offset_to_page(cg, offset); + num = len / cg->page_size; + + if (eccd->eccmode != NAND_ECC_NONE) { + steps = cg->page_size / eccd->eccsize; + eccpos = default_software_ecc_positions(chip); + oob = malloc(cg->oob_size, M_NAND, M_WAITOK); + } + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + ptr = (uint8_t *)buf; + while (num--) { + pg_stat = &(chip->pg_stat[page]); + + if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) { + retval = ENXIO; + break; + } + + if (eccd->eccmode != NAND_ECC_NONE) { + if (NAND_GET_ECC(chip->dev, ptr, eccd->ecccalculated, + &needwrite)) { + retval = ENXIO; + break; + } + nand_debug(NDBG_ECC,"%s: ECC calculated:", + __func__); + if (nand_debug_flag & NDBG_ECC) + for (i = 0; i < (eccd->eccbytes * steps); i++) + printf("%x ", eccd->ecccalculated[i]); + + nand_debug(NDBG_ECC,"\n"); + + if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size, + 0)) { + retval = ENXIO; + break; + } + for (i = 0; i < (eccd->eccbytes * steps); i++) + eccd->eccread[i] = ((uint8_t *)oob)[eccpos[i]]; + + nand_debug(NDBG_ECC,"%s: ECC read:", __func__); + if (nand_debug_flag & NDBG_ECC) + for (i = 0; i < (eccd->eccbytes * steps); i++) + printf("%x ", eccd->eccread[i]); + nand_debug(NDBG_ECC,"\n"); + + retval = NAND_CORRECT_ECC(chip->dev, ptr, eccd->eccread, + eccd->ecccalculated); + + nand_debug(NDBG_ECC, "NAND_CORRECT_ECC() returned %d", + retval); + + if (retval == 0) + pg_stat->ecc_stat.ecc_succeded++; + else if (retval > 0) { + pg_stat->ecc_stat.ecc_corrected += retval; + retval = ECC_CORRECTABLE; + } else { + pg_stat->ecc_stat.ecc_failed++; + break; + } + } + + pg_stat->page_read++; + page++; + ptr += cg->page_size; + } + + NANDBUS_UNLOCK(nandbus); + + if (oob) + free(oob, M_NAND); + + return (retval); +} + +int +nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf, + uint32_t len) +{ + struct chip_geom *cg; + device_t nandbus; + uint8_t *ptr; + uint32_t page, num, end, begin = 0, begin_off; + int retval = 0; + + cg = &chip->chip_geom; + page = offset_to_page(cg, offset); + begin_off = offset - page * cg->page_size; + if (begin_off) { + begin = cg->page_size - begin_off; + len -= begin; + } + num = len / cg->page_size; + end = len % cg->page_size; + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + ptr = (uint8_t *)buf; + if (begin_off) { + if (NAND_READ_PAGE(chip->dev, page, ptr, begin, begin_off)) { + NANDBUS_UNLOCK(nandbus); + return (ENXIO); + } + + page++; + ptr += begin; + } + + while (num--) { + if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) { + NANDBUS_UNLOCK(nandbus); + return (ENXIO); + } + + page++; + ptr += cg->page_size; + } + + if (end) + if (NAND_READ_PAGE(chip->dev, page, ptr, end, 0)) { + NANDBUS_UNLOCK(nandbus); + return (ENXIO); + } + + NANDBUS_UNLOCK(nandbus); + + return (retval); +} + + +int +nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf, + uint32_t len) +{ + struct chip_geom *cg; + struct page_stat *pg_stat; + struct nand_ecc_data *eccd; + device_t nandbus; + uint32_t page, num; + uint8_t *oob = NULL; + uint16_t *eccpos = NULL; + int steps = 0, i, needwrite, err = 0; + + nand_debug(NDBG_NAND,"%p prog page %x[%x]", chip, offset, len); + + eccd = &chip->nand->ecc; + cg = &chip->chip_geom; + page = offset_to_page(cg, offset); + num = len / cg->page_size; + + if (eccd->eccmode != NAND_ECC_NONE) { + steps = cg->page_size / eccd->eccsize; + oob = malloc(cg->oob_size, M_NAND, M_WAITOK); + eccpos = default_software_ecc_positions(chip); + } + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + while (num--) { + if (NAND_PROGRAM_PAGE(chip->dev, page, buf, cg->page_size, 0)) { + err = ENXIO; + break; + } + + if (eccd->eccmode != NAND_ECC_NONE) { + if (NAND_GET_ECC(chip->dev, buf, &eccd->ecccalculated, + &needwrite)) { + err = ENXIO; + break; + } + nand_debug(NDBG_ECC,"ECC calculated:"); + if (nand_debug_flag & NDBG_ECC) + for (i = 0; i < (eccd->eccbytes * steps); i++) + printf("%x ", eccd->ecccalculated[i]); + + nand_debug(NDBG_ECC,"\n"); + + if (needwrite) { + if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size, + 0)) { + err = ENXIO; + break; + } + + for (i = 0; i < (eccd->eccbytes * steps); i++) + oob[eccpos[i]] = eccd->ecccalculated[i]; + + if (NAND_PROGRAM_OOB(chip->dev, page, oob, + cg->oob_size, 0)) { + err = ENXIO; + break; + } + } + } + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_written++; + + page++; + buf += cg->page_size; + } + + NANDBUS_UNLOCK(nandbus); + + if (oob) + free(oob, M_NAND); + + return (err); +} + +int +nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf, + uint32_t len) +{ + struct chip_geom *cg; + device_t nandbus; + uint8_t *ptr; + uint32_t page, num, end, begin = 0, begin_off; + int retval = 0; + + cg = &chip->chip_geom; + page = offset_to_page(cg, offset); + begin_off = offset - page * cg->page_size; + if (begin_off) { + begin = cg->page_size - begin_off; + len -= begin; + } + num = len / cg->page_size; + end = len % cg->page_size; + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + ptr = (uint8_t *)buf; + if (begin_off) { + if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, begin, begin_off)) { + NANDBUS_UNLOCK(nandbus); + return (ENXIO); + } + + page++; + ptr += begin; + } + + while (num--) { + if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, cg->page_size, 0)) { + NANDBUS_UNLOCK(nandbus); + return (ENXIO); + } + + page++; + ptr += cg->page_size; + } + + if (end) + retval = NAND_PROGRAM_PAGE(chip->dev, page, ptr, end, 0); + + NANDBUS_UNLOCK(nandbus); + + return (retval); +} + +int +nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf, + uint32_t len) +{ + device_t nandbus; + int retval = 0; + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + retval = NAND_READ_OOB(chip->dev, page, buf, len, 0); + + NANDBUS_UNLOCK(nandbus); + + return (retval); +} + + +int +nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf, + uint32_t len) +{ + device_t nandbus; + int retval = 0; + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + retval = NAND_PROGRAM_OOB(chip->dev, page, buf, len, 0); + + NANDBUS_UNLOCK(nandbus); + + return (retval); +} + +int +nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len) +{ + device_t nandbus; + struct chip_geom *cg; + uint32_t block, num_blocks; + int err = 0; + + cg = &chip->chip_geom; + if ((offset % cg->block_size) || (len % cg->block_size)) + return (EINVAL); + + block = offset / cg->block_size; + num_blocks = len / cg->block_size; + nand_debug(NDBG_NAND,"%p erase blocks %d[%d]", chip, block, num_blocks); + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + while (num_blocks--) { + if (!nand_check_bad_block(chip, block)) { + if (NAND_ERASE_BLOCK(chip->dev, block)) { + nand_debug(NDBG_NAND,"%p erase blocks %d error", + chip, block); + nand_mark_bad_block(chip, block); + err = ENXIO; + } + } else + err = ENXIO; + + block++; + }; + + NANDBUS_UNLOCK(nandbus); + + if (err) + nand_update_bbt(chip); + + return (err); +} diff --git a/sys/dev/nand/nand.h b/sys/dev/nand/nand.h new file mode 100644 index 0000000..05101ac --- /dev/null +++ b/sys/dev/nand/nand.h @@ -0,0 +1,385 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_NAND_H_ +#define _DEV_NAND_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MALLOC_DECLARE(M_NAND); + +/* Read commands */ +#define NAND_CMD_READ 0x00 +#define NAND_CMD_CHNG_READ_COL 0x05 +#define NAND_CMD_READ_END 0x30 +#define NAND_CMD_READ_CACHE 0x31 +#define NAND_CMD_READ_CPBK 0x35 +#define NAND_CMD_READ_CACHE_END 0x3F +#define NAND_CMD_CHNG_READ_COL_END 0xE0 + +/* Erase commands */ +#define NAND_CMD_ERASE 0x60 +#define NAND_CMD_ERASE_END 0xD0 +#define NAND_CMD_ERASE_INTLV 0xD1 + +/* Program commands */ +#define NAND_CMD_PROG 0x80 +#define NAND_CMD_CHNG_WRITE_COL 0x85 +#define NAND_CMD_PROG_END 0x10 +#define NAND_CMD_PROG_INTLV 0x11 +#define NAND_CMD_PROG_CACHE 0x15 + +/* Misc commands */ +#define NAND_CMD_STATUS 0x70 +#define NAND_CMD_STATUS_ENH 0x78 +#define NAND_CMD_READ_ID 0x90 +#define NAND_CMD_READ_PARAMETER 0xec +#define NAND_CMD_READ_UNIQUE_ID 0xed +#define NAND_CMD_GET_FEATURE 0xee +#define NAND_CMD_SET_FEATURE 0xef + +/* Reset commands */ +#define NAND_CMD_SYNCH_RESET 0xfc +#define NAND_CMD_RESET 0xff + +/* Small page flash commands */ +#define NAND_CMD_SMALLA 0x00 +#define NAND_CMD_SMALLB 0x01 +#define NAND_CMD_SMALLOOB 0x50 + +#define NAND_STATUS_FAIL 0x1 +#define NAND_STATUS_FAILC 0x2 +#define NAND_STATUS_ARDY 0x20 +#define NAND_STATUS_RDY 0x40 +#define NAND_STATUS_WP 0x80 + +#define NAND_LP_OOB_COLUMN_START 0x800 +#define NAND_LP_OOBSZ 0x40 +#define NAND_SP_OOB_COLUMN_START 0x200 +#define NAND_SP_OOBSZ 0x10 + +#define PAGE_PARAM_LENGTH 0x100 +#define PAGE_PARAMETER_DEF 0x0 +#define PAGE_PARAMETER_RED_1 0x100 +#define PAGE_PARAMETER_RED_2 0x200 + +#define ONFI_SIG_ADDR 0x20 + +#define NAND_MAX_CHIPS 0x4 +#define NAND_MAX_OOBSZ 512 +#define NAND_MAX_PAGESZ 16384 + +#define NAND_SMALL_PAGE_SIZE 0x200 + +#define NAND_16_BIT 0x00000001 + +#define NAND_ECC_NONE 0x0 +#define NAND_ECC_SOFT 0x1 +#define NAND_ECC_FULLHW 0x2 +#define NAND_ECC_PARTHW 0x4 +#define NAND_ECC_MODE_MASK 0x7 + +#define ECC_OK 0 +#define ECC_CORRECTABLE 1 +#define ECC_ERROR_ECC (-1) +#define ECC_UNCORRECTABLE (-2) + +#define NAND_MAN_SAMSUNG 0xec +#define NAND_MAN_HYNIX 0xad +#define NAND_MAN_STMICRO 0x20 + +struct nand_id { + uint8_t man_id; + uint8_t dev_id; +}; + +struct nand_params { + struct nand_id id; + char *name; + uint32_t chip_size; + uint32_t page_size; + uint32_t oob_size; + uint32_t pages_per_block; + uint32_t flags; +}; + +/* nand debug levels */ +#define NDBG_NAND 0x01 +#define NDBG_CDEV 0x02 +#define NDBG_GEN 0x04 +#define NDBG_GEOM 0x08 +#define NDBG_BUS 0x10 +#define NDBG_SIM 0x20 +#define NDBG_CTRL 0x40 +#define NDBG_DRV 0x80 +#define NDBG_ECC 0x100 + +/* nand_debug_function */ +void nand_debug(int level, const char *fmt, ...); +extern int nand_debug_flag; + +/* ONFI features bit*/ +#define ONFI_FEAT_16BIT 0x01 +#define ONFI_FEAT_MULT_LUN 0x02 +#define ONFI_FEAT_INTLV_OPS 0x04 +#define ONFI_FEAT_CPBK_RESTRICT 0x08 +#define ONFI_FEAT_SRC_SYNCH 0x10 + +/* ONFI optional commands bits */ +#define ONFI_OPTCOM_PROG_CACHE 0x01 +#define ONFI_OPTCOM_READ_CACHE 0x02 +#define ONFI_OPTCOM_GETSET_FEAT 0x04 +#define ONFI_OPTCOM_STATUS_ENH 0x08 +#define ONFI_OPTCOM_COPYBACK 0x10 +#define ONFI_OPTCOM_UNIQUE_ID 0x20 + + +/* Layout of parameter page is defined in ONFI */ +struct onfi_params { + char signature[4]; + uint16_t rev; + uint16_t features; + uint16_t optional_commands; + uint8_t res1[22]; + char manufacturer_name[12]; + char device_model[20]; + uint8_t manufacturer_id; + uint16_t date; + uint8_t res2[13]; + uint32_t bytes_per_page; + uint16_t spare_bytes_per_page; + uint32_t bytes_per_partial_page; + uint16_t spare_bytes_per_partial_page; + uint32_t pages_per_block; + uint32_t blocks_per_lun; + uint8_t luns; + uint8_t address_cycles; + uint8_t bits_per_cell; + uint16_t max_bad_block_per_lun; + uint16_t block_endurance; + uint8_t guaranteed_valid_blocks; + uint16_t valid_block_endurance; + uint8_t programs_per_page; + uint8_t partial_prog_attr; + uint8_t bits_of_ecc; + uint8_t interleaved_addr_bits; + uint8_t interleaved_oper_attr; + uint8_t res3[13]; + uint8_t pin_capacitance; + uint16_t asynch_timing_mode_support; + uint16_t asynch_prog_cache_timing_mode_support; + uint16_t t_prog; /* us, max page program time */ + uint16_t t_bers; /* us, max block erase time */ + uint16_t t_r; /* us, max page read time */ + uint16_t t_ccs; /* ns, min change column setup time */ + uint16_t source_synch_timing_mode_support; + uint8_t source_synch_feat; + uint16_t clk_input_capacitance; + uint16_t io_capacitance; + uint16_t input_capacitance; + uint8_t input_capacitance_max; + uint8_t driver_strength_support; + uint8_t res4[12]; + uint16_t vendor_rev; + uint8_t vendor_spec[8]; + uint16_t crc; +}; + +struct nand_ecc_data { + int eccsize; /* Number of data bytes per ECC step */ + int eccmode; + int eccbytes; /* Number of ECC bytes per step */ + + uint16_t *eccpositions; /* Positions of ecc bytes */ + uint8_t ecccalculated[NAND_MAX_OOBSZ]; + uint8_t eccread[NAND_MAX_OOBSZ]; +}; + +struct ecc_stat { + uint32_t ecc_succeded; + uint32_t ecc_corrected; + uint32_t ecc_failed; +}; + +struct page_stat { + struct ecc_stat ecc_stat; + uint32_t page_read; + uint32_t page_raw_read; + uint32_t page_written; + uint32_t page_raw_written; +}; + +struct block_stat { + uint32_t block_erased; +}; + +struct chip_geom { + uint32_t chip_size; + uint32_t block_size; + uint32_t page_size; + uint32_t oob_size; + + uint32_t luns; + uint32_t blks_per_lun; + uint32_t blks_per_chip; + uint32_t pgs_per_blk; + + uint32_t pg_mask; + uint32_t blk_mask; + uint32_t lun_mask; + uint8_t blk_shift; + uint8_t lun_shift; +}; + +struct nand_chip { + device_t dev; + struct nand_id id; + struct chip_geom chip_geom; + + uint16_t t_prog; /* us, max page program time */ + uint16_t t_bers; /* us, max block erase time */ + uint16_t t_r; /* us, max page read time */ + uint16_t t_ccs; /* ns, min change column setup time */ + uint8_t num; + uint8_t flags; + + struct page_stat *pg_stat; + struct block_stat *blk_stat; + struct nand_softc *nand; + struct nand_bbt *bbt; + struct nand_ops *ops; + struct cdev *cdev; + + struct disk *ndisk; + struct disk *rdisk; + struct bio_queue_head bioq; /* bio queue */ + struct mtx qlock; /* bioq lock */ + struct taskqueue *tq; /* private task queue for i/o request */ + struct task iotask; /* i/o processing */ + +}; + +struct nand_softc { + uint8_t flags; + + char *chip_cdev_name; + struct nand_ecc_data ecc; +}; + +/* NAND ops */ +int nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len); +int nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf, + uint32_t len); +int nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf, + uint32_t len); +int nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf, + uint32_t len); +int nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf, + uint32_t len); +int nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf, + uint32_t len); +int nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf, + uint32_t len); + +int nand_select_cs(device_t dev, uint8_t cs); + +int nand_read_parameter(struct nand_softc *nand, struct onfi_params *param); +int nand_synch_reset(struct nand_softc *nand); +int nand_chng_read_col(device_t dev, uint32_t col, void *buf, size_t len); +int nand_chng_write_col(device_t dev, uint32_t col, void *buf, size_t len); +int nand_get_feature(device_t dev, uint8_t feat, void* buf); +int nand_set_feature(device_t dev, uint8_t feat, void* buf); + + +int nand_erase_block_intlv(device_t dev, uint32_t block); +int nand_copyback_read(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len); +int nand_copyback_prog(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len); +int nand_copyback_prog_intlv(device_t dev, uint32_t page); +int nand_prog_cache(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len, uint8_t end); +int nand_prog_intlv(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len); +int nand_read_cache(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len, uint8_t end); + +int nand_write_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data); +int nand_read_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data); + +int nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc); +int nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize, + uint8_t *readecc, uint8_t *calcecc); + +/* Chip initialization */ +void nand_init(struct nand_softc *nand, device_t dev, int ecc_mode, + int ecc_bytes, int ecc_size, uint16_t* eccposition, char* cdev_name); +void nand_detach(struct nand_softc *nand); +struct nand_params *nand_get_params(struct nand_id *id); + +void nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params); +void nand_set_params(struct nand_chip *chip, struct nand_params *params); +int nand_init_stat(struct nand_chip *chip); +void nand_destroy_stat(struct nand_chip *chip); + +/* BBT */ +int nand_init_bbt(struct nand_chip *chip); +void nand_destroy_bbt(struct nand_chip *chip); +int nand_update_bbt(struct nand_chip *chip); +int nand_mark_bad_block(struct nand_chip* chip, uint32_t block_num); +int nand_check_bad_block(struct nand_chip* chip, uint32_t block_num); + +/* cdev creation/removal */ +int nand_make_dev(struct nand_chip* chip); +void nand_destroy_dev(struct nand_chip *chip); + +int create_geom_disk(struct nand_chip* chip); +int create_geom_raw_disk(struct nand_chip *chip); +void destroy_geom_disk(struct nand_chip *chip); +void destroy_geom_raw_disk(struct nand_chip *chip); + +int init_chip_geom(struct chip_geom* cg, uint32_t luns, uint32_t blks_per_lun, + uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size); +int nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun, + uint32_t *blk, uint32_t *pg); +int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row); +int nand_check_page_boundary(struct nand_chip *chip, uint32_t page); +void nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param); + +#endif /* _DEV_NAND_H_ */ diff --git a/sys/dev/nand/nand_bbt.c b/sys/dev/nand/nand_bbt.c new file mode 100644 index 0000000..d3f163a --- /dev/null +++ b/sys/dev/nand/nand_bbt.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "nand_if.h" + +#define BBT_PRIMARY_PATTERN 0x01020304 +#define BBT_SECONDARY_PATTERN 0x05060708 + +enum bbt_place { + BBT_NONE, + BBT_PRIMARY, + BBT_SECONDARY +}; + +struct nand_bbt { + struct nand_chip *chip; + uint32_t primary_map; + uint32_t secondary_map; + enum bbt_place active; + struct bbt_header *hdr; + uint32_t tab_len; + uint32_t *table; +}; + +struct bbt_header { + uint32_t pattern; + int32_t seq_nr; +}; + +static int nand_bbt_save(struct nand_bbt *); +static int nand_bbt_load_hdr(struct nand_bbt *, struct bbt_header *, int8_t); +static int nand_bbt_load_table(struct nand_bbt *); +static int nand_bbt_prescan(struct nand_bbt *); + +int +nand_init_bbt(struct nand_chip *chip) +{ + struct chip_geom *cg; + struct nand_bbt *bbt; + int err; + + cg = &chip->chip_geom; + + bbt = malloc(sizeof(struct nand_bbt), M_NAND, M_ZERO | M_WAITOK); + if (!bbt) { + device_printf(chip->dev, + "Cannot allocate memory for bad block struct"); + return (ENOMEM); + } + + bbt->chip = chip; + bbt->active = BBT_NONE; + bbt->primary_map = cg->chip_size - cg->block_size; + bbt->secondary_map = cg->chip_size - 2 * cg->block_size; + bbt->tab_len = cg->blks_per_chip * sizeof(uint32_t); + bbt->hdr = malloc(sizeof(struct bbt_header) + bbt->tab_len, M_NAND, + M_WAITOK); + if (!bbt->hdr) { + device_printf(chip->dev, "Cannot allocate %d bytes for BB " + "Table", bbt->tab_len); + free(bbt, M_NAND); + return (ENOMEM); + } + bbt->hdr->seq_nr = 0; + bbt->table = (uint32_t *)((uint8_t *)bbt->hdr + + sizeof(struct bbt_header)); + + err = nand_bbt_load_table(bbt); + if (err) { + free(bbt->table, M_NAND); + free(bbt, M_NAND); + return (err); + } + + chip->bbt = bbt; + if (bbt->active == BBT_NONE) { + bbt->active = BBT_PRIMARY; + memset(bbt->table, 0xff, bbt->tab_len); + nand_bbt_prescan(bbt); + nand_bbt_save(bbt); + } else + device_printf(chip->dev, "Found BBT table for chip\n"); + + return (0); +} + +void +nand_destroy_bbt(struct nand_chip *chip) +{ + + if (chip->bbt) { + nand_bbt_save(chip->bbt); + + free(chip->bbt->hdr, M_NAND); + free(chip->bbt, M_NAND); + chip->bbt = NULL; + } +} + +int +nand_update_bbt(struct nand_chip *chip) +{ + + nand_bbt_save(chip->bbt); + + return (0); +} + +static int +nand_bbt_save(struct nand_bbt *bbt) +{ + enum bbt_place next; + uint32_t addr; + int32_t err; + + if (bbt->active == BBT_PRIMARY) { + addr = bbt->secondary_map; + bbt->hdr->pattern = BBT_SECONDARY_PATTERN; + next = BBT_SECONDARY; + } else { + addr = bbt->primary_map; + bbt->hdr->pattern = BBT_PRIMARY_PATTERN; + next = BBT_PRIMARY; + } + + err = nand_erase_blocks(bbt->chip, addr, + bbt->chip->chip_geom.block_size); + if (err) + return (err); + + bbt->hdr->seq_nr++; + + err = nand_prog_pages_raw(bbt->chip, addr, bbt->hdr, + bbt->tab_len + sizeof(struct bbt_header)); + if (err) + return (err); + + bbt->active = next; + return (0); +} + +static int +nand_bbt_load_hdr(struct nand_bbt *bbt, struct bbt_header *hdr, int8_t primary) +{ + uint32_t addr; + + if (primary) + addr = bbt->primary_map; + else + addr = bbt->secondary_map; + + return (nand_read_pages_raw(bbt->chip, addr, hdr, + sizeof(struct bbt_header))); +} + +static int +nand_bbt_load_table(struct nand_bbt *bbt) +{ + struct bbt_header hdr1, hdr2; + uint32_t address = 0; + int err = 0; + + bzero(&hdr1, sizeof(hdr1)); + bzero(&hdr2, sizeof(hdr2)); + + nand_bbt_load_hdr(bbt, &hdr1, 1); + if (hdr1.pattern == BBT_PRIMARY_PATTERN) { + bbt->active = BBT_PRIMARY; + address = bbt->primary_map; + } else + bzero(&hdr1, sizeof(hdr1)); + + + nand_bbt_load_hdr(bbt, &hdr2, 0); + if ((hdr2.pattern == BBT_SECONDARY_PATTERN) && + (hdr2.seq_nr > hdr1.seq_nr)) { + bbt->active = BBT_SECONDARY; + address = bbt->secondary_map; + } else + bzero(&hdr2, sizeof(hdr2)); + + if (bbt->active != BBT_NONE) + err = nand_read_pages_raw(bbt->chip, address, bbt->hdr, + bbt->tab_len + sizeof(struct bbt_header)); + + return (err); +} + +static int +nand_bbt_prescan(struct nand_bbt *bbt) +{ + int32_t i; + uint8_t bad; + bool printed_hash = 0; + + device_printf(bbt->chip->dev, "No BBT found. Prescan chip...\n"); + for (i = 0; i < bbt->chip->chip_geom.blks_per_chip; i++) { + if (NAND_IS_BLK_BAD(bbt->chip->dev, i, &bad)) + return (ENXIO); + + if (bad) { + device_printf(bbt->chip->dev, "Bad block(%d)\n", i); + bbt->table[i] = 0x0FFFFFFF; + } + if (!(i % 100)) { + printf("#"); + printed_hash = 1; + } + } + + if (printed_hash) + printf("\n"); + + return (0); +} + +int +nand_check_bad_block(struct nand_chip *chip, uint32_t block_number) +{ + + if (!chip || !chip->bbt) + return (0); + + if ((chip->bbt->table[block_number] & 0xF0000000) == 0) + return (1); + + return (0); +} + +int +nand_mark_bad_block(struct nand_chip *chip, uint32_t block_number) +{ + + chip->bbt->table[block_number] = 0x0FFFFFFF; + + return (0); +} diff --git a/sys/dev/nand/nand_cdev.c b/sys/dev/nand/nand_cdev.c new file mode 100644 index 0000000..ac27ff3 --- /dev/null +++ b/sys/dev/nand/nand_cdev.c @@ -0,0 +1,413 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "nand_if.h" +#include "nandbus_if.h" + +static int nand_page_stat(struct nand_chip *, struct page_stat_io *); +static int nand_block_stat(struct nand_chip *, struct block_stat_io *); + +static d_ioctl_t nand_ioctl; +static d_open_t nand_open; +static d_strategy_t nand_strategy; + +static struct cdevsw nand_cdevsw = { + .d_version = D_VERSION, + .d_name = "nand", + .d_open = nand_open, + .d_read = physread, + .d_write = physwrite, + .d_ioctl = nand_ioctl, + .d_strategy = nand_strategy, +}; + +static int +offset_to_page(struct chip_geom *cg, uint32_t offset) +{ + + return (offset / cg->page_size); +} + +static int +offset_to_page_off(struct chip_geom *cg, uint32_t offset) +{ + + return (offset % cg->page_size); +} + +int +nand_make_dev(struct nand_chip *chip) +{ + struct nandbus_ivar *ivar; + device_t parent, nandbus; + int parent_unit, unit; + char *name; + + ivar = device_get_ivars(chip->dev); + nandbus = device_get_parent(chip->dev); + + if (ivar->chip_cdev_name) { + name = ivar->chip_cdev_name; + + /* + * If we got distinct name for chip device we can enumarete it + * based on contoller number. + */ + parent = device_get_parent(nandbus); + } else { + name = "nand"; + parent = nandbus; + } + + parent_unit = device_get_unit(parent); + unit = parent_unit * 4 + chip->num; + chip->cdev = make_dev(&nand_cdevsw, unit, UID_ROOT, GID_WHEEL, + 0666, "%s%d.%d", name, parent_unit, chip->num); + + if (chip->cdev == NULL) + return (ENXIO); + + if (bootverbose) + device_printf(chip->dev, "Created cdev %s%d.%d for chip " + "[0x%0x, 0x%0x]\n", name, parent_unit, chip->num, + ivar->man_id, ivar->dev_id); + + chip->cdev->si_drv1 = chip; + + return (0); +} + +void +nand_destroy_dev(struct nand_chip *chip) +{ + + if (chip->cdev) + destroy_dev(chip->cdev); +} + +static int +nand_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + + return (0); +} + +static int +nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len) +{ + struct chip_geom *cg; + device_t nandbus; + int start_page, count, off, err = 0; + uint8_t *ptr, *tmp; + + nand_debug(NDBG_CDEV, "Read from chip%d [%p] at %d\n", chip->num, + chip, offset); + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + cg = &chip->chip_geom; + start_page = offset_to_page(cg, offset); + off = offset_to_page_off(cg, offset); + count = (len > cg->page_size - off) ? cg->page_size - off : len; + + ptr = (uint8_t *)buf; + while (len > 0) { + if (len < cg->page_size) { + tmp = malloc(cg->page_size, M_NAND, M_WAITOK); + if (!tmp) { + err = ENOMEM; + break; + } + err = NAND_READ_PAGE(chip->dev, start_page, + tmp, cg->page_size, 0); + if (err) { + free(tmp, M_NAND); + break; + } + bcopy(tmp + off, ptr, count); + free(tmp, M_NAND); + } else { + err = NAND_READ_PAGE(chip->dev, start_page, + ptr, cg->page_size, 0); + if (err) + break; + } + + len -= count; + start_page++; + ptr += count; + count = (len > cg->page_size) ? cg->page_size : len; + off = 0; + } + + NANDBUS_UNLOCK(nandbus); + return (err); +} + +static int +nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len) +{ + struct chip_geom *cg; + device_t nandbus; + int off, start_page, err = 0; + uint8_t *ptr; + + nand_debug(NDBG_CDEV, "Write to chip %d [%p] at %d\n", chip->num, + chip, offset); + + nandbus = device_get_parent(chip->dev); + NANDBUS_LOCK(nandbus); + NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num); + + cg = &chip->chip_geom; + start_page = offset_to_page(cg, offset); + off = offset_to_page_off(cg, offset); + + if (off != 0 || (len % cg->page_size) != 0) { + printf("Not aligned write start [0x%08x] size [0x%08x]\n", + off, len); + NANDBUS_UNLOCK(nandbus); + return (EINVAL); + } + + ptr = (uint8_t *)buf; + while (len > 0) { + err = NAND_PROGRAM_PAGE(chip->dev, start_page, ptr, + cg->page_size, 0); + if (err) + break; + + len -= cg->page_size; + start_page++; + ptr += cg->page_size; + } + + NANDBUS_UNLOCK(nandbus); + return (err); +} + +static void +nand_strategy(struct bio *bp) +{ + struct nand_chip *chip; + struct cdev *dev; + int err = 0; + + dev = bp->bio_dev; + chip = dev->si_drv1; + + nand_debug(NDBG_CDEV, "Strategy %s on chip %d [%p]\n", + (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : "WRITE", + chip->num, chip); + + if ((bp->bio_cmd & BIO_READ) == BIO_READ) { + err = nand_read(chip, + bp->bio_offset & 0xffffffff, + bp->bio_data, bp->bio_bcount); + } else { + err = nand_write(chip, + bp->bio_offset & 0xffffffff, + bp->bio_data, bp->bio_bcount); + } + + if (err == 0) + bp->bio_resid = 0; + else { + bp->bio_error = EIO; + bp->bio_flags |= BIO_ERROR; + bp->bio_resid = bp->bio_bcount; + } + + biodone(bp); +} + +static int +nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset, + uint32_t len, uint8_t *data, uint8_t write) +{ + struct chip_geom *cg; + uint8_t *buf = NULL; + int ret = 0; + + cg = &chip->chip_geom; + + buf = malloc(cg->oob_size, M_NAND, M_WAITOK); + if (!buf) + return (ENOMEM); + + memset(buf, 0xff, cg->oob_size); + + if (!write) { + ret = nand_read_oob(chip, page, buf, cg->oob_size); + copyout(buf, data, len); + } else { + copyin(data, buf, len); + ret = nand_prog_oob(chip, page, buf, cg->oob_size); + } + + free(buf, M_NAND); + + return (ret); +} + +static int +nand_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, + struct thread *td) +{ + struct nand_chip *chip; + struct nand_oob_rw *oob_rw = NULL; + struct nand_raw_rw *raw_rw = NULL; + device_t nandbus; + uint8_t *buf = NULL; + int ret = 0; + uint8_t status; + + chip = (struct nand_chip *)dev->si_drv1; + nandbus = device_get_parent(chip->dev); + + if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) { + raw_rw = (struct nand_raw_rw *)data; + buf = malloc(raw_rw->len, M_NAND, M_WAITOK); + } + switch(cmd) { + case NAND_IO_ERASE: + ret = nand_erase_blocks(chip, ((off_t *)data)[0], + ((off_t *)data)[1]); + break; + + case NAND_IO_OOB_READ: + oob_rw = (struct nand_oob_rw *)data; + ret = nand_oob_access(chip, oob_rw->page, 0, + oob_rw->len, oob_rw->data, 0); + break; + + case NAND_IO_OOB_PROG: + oob_rw = (struct nand_oob_rw *)data; + ret = nand_oob_access(chip, oob_rw->page, 0, + oob_rw->len, oob_rw->data, 1); + break; + + case NAND_IO_GET_STATUS: + NANDBUS_LOCK(nandbus); + ret = NANDBUS_GET_STATUS(nandbus, &status); + if (ret == 0) + *(uint8_t *)data = status; + NANDBUS_UNLOCK(nandbus); + break; + + case NAND_IO_RAW_PROG: + ret = copyin(raw_rw->data, buf, raw_rw->len); + if (ret) + break; + ret = nand_prog_pages_raw(chip, raw_rw->off, buf, + raw_rw->len); + break; + + case NAND_IO_RAW_READ: + ret = nand_read_pages_raw(chip, raw_rw->off, buf, + raw_rw->len); + if (ret) + break; + ret = copyout(buf, raw_rw->data, raw_rw->len); + break; + + case NAND_IO_PAGE_STAT: + ret = nand_page_stat(chip, (struct page_stat_io *)data); + break; + + case NAND_IO_BLOCK_STAT: + ret = nand_block_stat(chip, (struct block_stat_io *)data); + break; + + case NAND_IO_GET_CHIP_PARAM: + nand_get_chip_param(chip, (struct chip_param_io *)data); + break; + + default: + printf("Unknown nand_ioctl request \n"); + ret = EIO; + } + + if (buf) + free(buf, M_NAND); + + return (ret); +} + +static int +nand_page_stat(struct nand_chip *chip, struct page_stat_io *page_stat) +{ + struct chip_geom *cg; + struct page_stat *stat; + int num_pages; + + cg = &chip->chip_geom; + num_pages = cg->pgs_per_blk * cg->blks_per_lun * cg->luns; + if (page_stat->page_num >= num_pages) + return (EINVAL); + + stat = &chip->pg_stat[page_stat->page_num]; + page_stat->page_read = stat->page_read; + page_stat->page_written = stat->page_written; + page_stat->page_raw_read = stat->page_raw_read; + page_stat->page_raw_written = stat->page_raw_written; + page_stat->ecc_succeded = stat->ecc_stat.ecc_succeded; + page_stat->ecc_corrected = stat->ecc_stat.ecc_corrected; + page_stat->ecc_failed = stat->ecc_stat.ecc_failed; + + return (0); +} + +static int +nand_block_stat(struct nand_chip *chip, struct block_stat_io *block_stat) +{ + struct chip_geom *cg; + uint32_t block_num = block_stat->block_num; + + cg = &chip->chip_geom; + if (block_num >= cg->blks_per_lun * cg->luns) + return (EINVAL); + + block_stat->block_erased = chip->blk_stat[block_num].block_erased; + + return (0); +} diff --git a/sys/dev/nand/nand_dev.h b/sys/dev/nand/nand_dev.h new file mode 100644 index 0000000..bc7d6c4 --- /dev/null +++ b/sys/dev/nand/nand_dev.h @@ -0,0 +1,90 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_NAND_CDEV_H_ +#define _DEV_NAND_CDEV_H_ + +#include +#include + +struct nand_raw_rw { + off_t off; + off_t len; + uint8_t *data; +}; + +struct nand_oob_rw { + uint32_t page; + off_t len; + uint8_t *data; +}; + +#define NAND_IOCTL_GROUP 'N' +#define NAND_IO_ERASE _IOWR(NAND_IOCTL_GROUP, 0x0, off_t[2]) + +#define NAND_IO_OOB_READ _IOWR(NAND_IOCTL_GROUP, 0x1, struct nand_oob_rw) + +#define NAND_IO_OOB_PROG _IOWR(NAND_IOCTL_GROUP, 0x2, struct nand_oob_rw) + +#define NAND_IO_RAW_READ _IOWR(NAND_IOCTL_GROUP, 0x3, struct nand_raw_rw) + +#define NAND_IO_RAW_PROG _IOWR(NAND_IOCTL_GROUP, 0x4, struct nand_raw_rw) + +#define NAND_IO_GET_STATUS _IOWR(NAND_IOCTL_GROUP, 0x5, uint8_t) + +struct page_stat_io { + uint32_t page_num; + uint32_t page_read; + uint32_t page_written; + uint32_t page_raw_read; + uint32_t page_raw_written; + uint32_t ecc_succeded; + uint32_t ecc_corrected; + uint32_t ecc_failed; +}; +#define NAND_IO_PAGE_STAT _IOWR(NAND_IOCTL_GROUP, 0x6, \ + struct page_stat_io) + +struct block_stat_io { + uint32_t block_num; + uint32_t block_erased; +}; +#define NAND_IO_BLOCK_STAT _IOWR(NAND_IOCTL_GROUP, 0x7, \ + struct block_stat_io) + +struct chip_param_io { + uint32_t page_size; + uint32_t oob_size; + + uint32_t blocks; + uint32_t pages_per_block; +}; +#define NAND_IO_GET_CHIP_PARAM _IOWR(NAND_IOCTL_GROUP, 0x8, \ + struct chip_param_io) + +#endif /* _DEV_NAND_CDEV_H_ */ diff --git a/sys/dev/nand/nand_ecc_pos.h b/sys/dev/nand/nand_ecc_pos.h new file mode 100644 index 0000000..f40415c --- /dev/null +++ b/sys/dev/nand/nand_ecc_pos.h @@ -0,0 +1,56 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_NAND_ECC_POS_H_ +#define _DEV_NAND_ECC_POS_H_ + +static uint16_t default_software_ecc_positions_16[] = {2, 0, 1, 7, 4, 6}; + +static uint16_t default_software_ecc_positions_64[] = { + + 42, 40, 41, 45, 43, 44, 48, 46, + 47, 51, 49, 50, 54, 52, 53, 57, + 55, 56, 60, 58, 59, 63, 61, 62 +}; + +static uint16_t default_software_ecc_positions_128[] = { + 8, 9, 10, 11, 12, 13, + 18, 19, 20, 21, 22, 23, + 28, 29, 30, 31, 32, 33, + 38, 39, 40, 41, 42, 43, + 48, 49, 50, 51, 52, 53, + 58, 59, 60, 61, 62, 63, + 68, 69, 70, 71, 72, 73, + 78, 79, 80, 81, 82, 83, + 88, 89, 90, 91, 92, 93, + 98, 99, 100, 101, 102, 103, + 108, 109, 110, 111, 112, 113, + 118, 119, 120, 121, 122, 123, +}; +#endif /* _DEV_NAND_ECC_POS_H_ */ + diff --git a/sys/dev/nand/nand_generic.c b/sys/dev/nand/nand_generic.c new file mode 100644 index 0000000..85e81be --- /dev/null +++ b/sys/dev/nand/nand_generic.c @@ -0,0 +1,1320 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Generic NAND driver */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "nfc_if.h" +#include "nand_if.h" +#include "nandbus_if.h" + + +static int onfi_nand_probe(device_t dev); +static int large_nand_probe(device_t dev); +static int small_nand_probe(device_t dev); +static int generic_nand_attach(device_t dev); +static int generic_nand_detach(device_t dev); + +static int generic_erase_block(device_t, uint32_t); +static int generic_erase_block_intlv(device_t, uint32_t); +static int generic_read_page (device_t, uint32_t, void *, uint32_t, uint32_t); +static int generic_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t); +static int generic_program_page(device_t, uint32_t, void *, uint32_t, uint32_t); +static int generic_program_page_intlv(device_t, uint32_t, void *, uint32_t, + uint32_t); +static int generic_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t); +static int generic_is_blk_bad(device_t, uint32_t, uint8_t *); +static int generic_get_ecc(device_t, void *, void *, int *); +static int generic_correct_ecc(device_t, void *, void *, void *); + +static int small_read_page(device_t, uint32_t, void *, uint32_t, uint32_t); +static int small_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t); +static int small_program_page(device_t, uint32_t, void *, uint32_t, uint32_t); +static int small_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t); + +static int onfi_is_blk_bad(device_t, uint32_t, uint8_t *); +static int onfi_read_parameter(struct nand_chip *, struct onfi_params *); + +static int nand_send_address(device_t, int32_t, int32_t, int8_t); + +static device_method_t onand_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, onfi_nand_probe), + DEVMETHOD(device_attach, generic_nand_attach), + DEVMETHOD(device_detach, generic_nand_detach), + + DEVMETHOD(nand_read_page, generic_read_page), + DEVMETHOD(nand_program_page, generic_program_page), + DEVMETHOD(nand_program_page_intlv, generic_program_page_intlv), + DEVMETHOD(nand_read_oob, generic_read_oob), + DEVMETHOD(nand_program_oob, generic_program_oob), + DEVMETHOD(nand_erase_block, generic_erase_block), + DEVMETHOD(nand_erase_block_intlv, generic_erase_block_intlv), + + DEVMETHOD(nand_is_blk_bad, onfi_is_blk_bad), + DEVMETHOD(nand_get_ecc, generic_get_ecc), + DEVMETHOD(nand_correct_ecc, generic_correct_ecc), + { 0, 0 } +}; + +static device_method_t lnand_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, large_nand_probe), + DEVMETHOD(device_attach, generic_nand_attach), + DEVMETHOD(device_detach, generic_nand_detach), + + DEVMETHOD(nand_read_page, generic_read_page), + DEVMETHOD(nand_program_page, generic_program_page), + DEVMETHOD(nand_read_oob, generic_read_oob), + DEVMETHOD(nand_program_oob, generic_program_oob), + DEVMETHOD(nand_erase_block, generic_erase_block), + + DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad), + DEVMETHOD(nand_get_ecc, generic_get_ecc), + DEVMETHOD(nand_correct_ecc, generic_correct_ecc), + { 0, 0 } +}; + +static device_method_t snand_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, small_nand_probe), + DEVMETHOD(device_attach, generic_nand_attach), + DEVMETHOD(device_detach, generic_nand_detach), + + DEVMETHOD(nand_read_page, small_read_page), + DEVMETHOD(nand_program_page, small_program_page), + DEVMETHOD(nand_read_oob, small_read_oob), + DEVMETHOD(nand_program_oob, small_program_oob), + DEVMETHOD(nand_erase_block, generic_erase_block), + + DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad), + DEVMETHOD(nand_get_ecc, generic_get_ecc), + DEVMETHOD(nand_correct_ecc, generic_correct_ecc), + { 0, 0 } +}; + +devclass_t onand_devclass; +devclass_t lnand_devclass; +devclass_t snand_devclass; + +driver_t onand_driver = { + "onand", + onand_methods, + sizeof(struct nand_chip) +}; + +driver_t lnand_driver = { + "lnand", + lnand_methods, + sizeof(struct nand_chip) +}; + +driver_t snand_driver = { + "snand", + snand_methods, + sizeof(struct nand_chip) +}; + +DRIVER_MODULE(onand, nandbus, onand_driver, onand_devclass, 0, 0); +DRIVER_MODULE(lnand, nandbus, lnand_driver, lnand_devclass, 0, 0); +DRIVER_MODULE(snand, nandbus, snand_driver, snand_devclass, 0, 0); + +static int +onfi_nand_probe(device_t dev) +{ + struct nandbus_ivar *ivar; + + ivar = device_get_ivars(dev); + if (ivar && ivar->is_onfi) { + device_set_desc(dev, "ONFI compliant NAND"); + return (BUS_PROBE_DEFAULT); + } + + return (ENODEV); +} + +static int +large_nand_probe(device_t dev) +{ + struct nandbus_ivar *ivar; + + ivar = device_get_ivars(dev); + if (ivar && !ivar->is_onfi && ivar->params->page_size >= 512) { + device_set_desc(dev, ivar->params->name); + return (BUS_PROBE_DEFAULT); + } + + return (ENODEV); +} + +static int +small_nand_probe(device_t dev) +{ + struct nandbus_ivar *ivar; + + ivar = device_get_ivars(dev); + if (ivar && !ivar->is_onfi && ivar->params->page_size == 512) { + device_set_desc(dev, ivar->params->name); + return (BUS_PROBE_DEFAULT); + } + + return (ENODEV); +} + +static int +generic_nand_attach(device_t dev) +{ + struct nand_chip *chip; + struct nandbus_ivar *ivar; + struct onfi_params *onfi_params; + device_t nandbus, nfc; + int err; + + chip = device_get_softc(dev); + chip->dev = dev; + + ivar = device_get_ivars(dev); + chip->id.man_id = ivar->man_id; + chip->id.dev_id = ivar->dev_id; + chip->num = ivar->cs; + + /* TODO remove when HW ECC supported */ + nandbus = device_get_parent(dev); + nfc = device_get_parent(nandbus); + + chip->nand = device_get_softc(nfc); + + if (ivar->is_onfi) { + onfi_params = malloc(sizeof(struct onfi_params), + M_NAND, M_WAITOK | M_ZERO); + if (onfi_params == NULL) + return (ENXIO); + + if (onfi_read_parameter(chip, onfi_params)) { + nand_debug(NDBG_GEN,"Could not read parameter page!\n"); + free(onfi_params, M_NAND); + return (ENXIO); + } + + nand_onfi_set_params(chip, onfi_params); + /* Set proper column and row cycles */ + ivar->cols = (onfi_params->address_cycles >> 4) & 0xf; + ivar->rows = onfi_params->address_cycles & 0xf; + free(onfi_params, M_NAND); + + } else { + + nand_set_params(chip, ivar->params); + } + + err = nand_init_stat(chip); + if (err) { + generic_nand_detach(dev); + return (err); + } + + err = nand_init_bbt(chip); + if (err) { + generic_nand_detach(dev); + return (err); + } + + err = nand_make_dev(chip); + if (err) { + generic_nand_detach(dev); + return (err); + } + + err = create_geom_disk(chip); + if (err) { + generic_nand_detach(dev); + return (err); + } + + return (0); +} + +static int +generic_nand_detach(device_t dev) +{ + struct nand_chip *chip; + + chip = device_get_softc(dev); + + nand_destroy_bbt(chip); + destroy_geom_disk(chip); + nand_destroy_dev(chip); + nand_destroy_stat(chip); + + return (0); +} + +static int +can_write(device_t nandbus) +{ + uint8_t status; + + if (NANDBUS_WAIT_READY(nandbus, &status)) + return (0); + + if (!(status & NAND_STATUS_WP)) { + nand_debug(NDBG_GEN,"Chip is write-protected"); + return (0); + } + + return (1); +} + +static int +check_fail(device_t nandbus) +{ + uint8_t status; + + NANDBUS_WAIT_READY(nandbus, &status); + if (status & NAND_STATUS_FAIL) { + nand_debug(NDBG_GEN,"Status failed %x", status); + return (ENXIO); + } + + return (0); +} + +static int +onfi_read_parameter(struct nand_chip *chip, struct onfi_params *params) +{ + device_t nandbus; + + nand_debug(NDBG_GEN,"read parameter"); + + nandbus = device_get_parent(chip->dev); + + NANDBUS_SELECT_CS(nandbus, chip->num); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ_PARAMETER)) + return (ENXIO); + + if (nand_send_address(chip->dev, -1, -1, PAGE_PARAMETER_DEF)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + NANDBUS_READ_BUFFER(nandbus, params, sizeof(struct onfi_params)); + + /* TODO */ + /* Check for signature */ + /* Check CRC */ + /* Use redundant page if necessary */ + + return (0); +} + +static int +send_read_page(device_t nand, uint8_t start_command, uint8_t end_command, + uint32_t row, uint32_t column) +{ + device_t nandbus = device_get_parent(nand); + + if (NANDBUS_SEND_COMMAND(nandbus, start_command)) + return (ENXIO); + + if (nand_send_address(nand, row, column, -1)) + return (ENXIO); + + if (NANDBUS_SEND_COMMAND(nandbus, end_command)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + return (0); +} + +static int +generic_read_page(device_t nand, uint32_t page, void *buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p raw read page %x[%x] at %x", nand, page, len, offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row, + offset)) + return (ENXIO); + + DELAY(chip->t_r); + + NANDBUS_READ_BUFFER(nandbus, buf, len); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_read++; + + return (0); +} + +static int +generic_read_oob(device_t nand, uint32_t page, void* buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p raw read oob %x[%x] at %x", nand, page, len, offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) { + nand_debug(NDBG_GEN,"page boundary check failed: %08x\n", page); + return (ENXIO); + } + + page_to_row(&chip->chip_geom, page, &row); + + offset += chip->chip_geom.page_size; + + if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row, + offset)) + return (ENXIO); + + DELAY(chip->t_r); + + NANDBUS_READ_BUFFER(nandbus, buf, len); + + if (check_fail(nandbus)) + return (ENXIO); + + return (0); +} + +static int +send_start_program_page(device_t nand, uint32_t row, uint32_t column) +{ + device_t nandbus = device_get_parent(nand); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_PROG)) + return (ENXIO); + + if (nand_send_address(nand, row, column, -1)) + return (ENXIO); + + return (0); +} + +static int +send_end_program_page(device_t nandbus, uint8_t end_command) +{ + + if (NANDBUS_SEND_COMMAND(nandbus, end_command)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + return (0); +} + +static int +generic_program_page(device_t nand, uint32_t page, void *buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len, + offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (!can_write(nandbus)) + return (ENXIO); + + if (send_start_program_page(nand, row, offset)) + return (ENXIO); + + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_written++; + + return (0); +} + +static int +generic_program_page_intlv(device_t nand, uint32_t page, void *buf, + uint32_t len, uint32_t offset) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len, offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (!can_write(nandbus)) + return (ENXIO); + + if (send_start_program_page(nand, row, offset)) + return (ENXIO); + + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_written++; + + return (0); +} + +static int +generic_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p raw prog oob %x[%x] at %x", nand, page, len, + offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + offset += chip->chip_geom.page_size; + + if (!can_write(nandbus)) + return (ENXIO); + + if (send_start_program_page(nand, row, offset)) + return (ENXIO); + + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + return (0); +} + +static int +send_erase_block(device_t nand, uint32_t row, uint8_t second_command) +{ + device_t nandbus = device_get_parent(nand); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_ERASE)) + return (ENXIO); + + if (nand_send_address(nand, row, -1, -1)) + return (ENXIO); + + if (NANDBUS_SEND_COMMAND(nandbus, second_command)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + return (0); +} + +static int +generic_erase_block(device_t nand, uint32_t block) +{ + struct block_stat *blk_stat; + struct nand_chip *chip; + device_t nandbus; + int row; + + nand_debug(NDBG_GEN,"%p erase block %x", nand, block); + nandbus = device_get_parent(nand); + chip = device_get_softc(nand); + + if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns)) + return (ENXIO); + + row = (block << chip->chip_geom.blk_shift) & + chip->chip_geom.blk_mask; + + nand_debug(NDBG_GEN,"%p erase block row %x", nand, row); + + if (!can_write(nandbus)) + return (ENXIO); + + send_erase_block(nand, row, NAND_CMD_ERASE_END); + + DELAY(chip->t_bers); + + if (check_fail(nandbus)) + return (ENXIO); + + blk_stat = &(chip->blk_stat[block]); + blk_stat->block_erased++; + + return (0); +} + +static int +generic_erase_block_intlv(device_t nand, uint32_t block) +{ + struct block_stat *blk_stat; + struct nand_chip *chip; + device_t nandbus; + int row; + + nand_debug(NDBG_GEN,"%p erase block %x", nand, block); + nandbus = device_get_parent(nand); + chip = device_get_softc(nand); + + if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns)) + return (ENXIO); + + row = (block << chip->chip_geom.blk_shift) & + chip->chip_geom.blk_mask; + + if (!can_write(nandbus)) + return (ENXIO); + + send_erase_block(nand, row, NAND_CMD_ERASE_INTLV); + + DELAY(chip->t_bers); + + if (check_fail(nandbus)) + return (ENXIO); + + blk_stat = &(chip->blk_stat[block]); + blk_stat->block_erased++; + + return (0); + +} + +static int +onfi_is_blk_bad(device_t device, uint32_t block_number, uint8_t *bad) +{ + struct nand_chip *chip; + int page_number, i, j, err; + uint8_t *oob; + + chip = device_get_softc(device); + + oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK); + if (!oob) { + device_printf(device, "%s: cannot allocate oob\n", __func__); + return (ENOMEM); + } + + page_number = block_number * chip->chip_geom.pgs_per_blk; + *bad = 0; + /* Check OOB of first and last page */ + for (i = 0; i < 2; i++, page_number+= chip->chip_geom.pgs_per_blk - 1) { + err = generic_read_oob(device, page_number, oob, + chip->chip_geom.oob_size, 0); + if (err) { + device_printf(device, "%s: cannot allocate oob\n", + __func__); + free(oob, M_NAND); + return (ENOMEM); + } + + for (j = 0; j < chip->chip_geom.oob_size; j++) { + if (!oob[j]) { + *bad = 1; + free(oob, M_NAND); + return (0); + } + } + } + + free(oob, M_NAND); + + return (0); +} + +static int +send_small_read_page(device_t nand, uint8_t start_command, + uint32_t row, uint32_t column) +{ + device_t nandbus = device_get_parent(nand); + + if (NANDBUS_SEND_COMMAND(nandbus, start_command)) + return (ENXIO); + + if (nand_send_address(nand, row, column, -1)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + return (0); +} + + +static int +small_read_page(device_t nand, uint32_t page, void *buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p small read page %x[%x] at %x", nand, page, len, offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (offset < 256) { + if (send_small_read_page(nand, NAND_CMD_SMALLA, row, offset)) + return (ENXIO); + } else { + offset -= 256; + if (send_small_read_page(nandbus, NAND_CMD_SMALLB, row, offset)) + return (ENXIO); + } + + DELAY(chip->t_r); + + NANDBUS_READ_BUFFER(nandbus, buf, len); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_read++; + + return (0); +} + +static int +small_read_oob(device_t nand, uint32_t page, void *buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p small read oob %x[%x] at %x", nand, page, len, offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (send_small_read_page(nand, NAND_CMD_SMALLOOB, row, 0)) + return (ENXIO); + + DELAY(chip->t_r); + + NANDBUS_READ_BUFFER(nandbus, buf, len); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_read++; + + return (0); +} + +static int +small_program_page(device_t nand, uint32_t page, void* buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p small prog page %x[%x] at %x", nand, page, len, offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (!can_write(nandbus)) + return (ENXIO); + + if (offset < 256) { + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLA)) + return (ENXIO); + } else { + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLB)) + return (ENXIO); + } + + if (send_start_program_page(nand, row, offset)) + return (ENXIO); + + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + return (0); +} + +static int +small_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len, + uint32_t offset) +{ + struct nand_chip *chip; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"%p small prog oob %x[%x] at %x", nand, page, len, offset); + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (!can_write(nandbus)) + return (ENXIO); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLOOB)) + return (ENXIO); + + if (send_start_program_page(nand, row, offset)) + return (ENXIO); + + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + return (0); +} + +int +nand_send_address(device_t nand, int32_t row, int32_t col, int8_t id) +{ + struct nandbus_ivar *ivar; + device_t nandbus; + uint8_t addr; + int err = 0; + int i; + + nandbus = device_get_parent(nand); + ivar = device_get_ivars(nand); + + if (id != -1) { + nand_debug(NDBG_GEN,"send_address: send id %02x", id); + err = NANDBUS_SEND_ADDRESS(nandbus, id); + } + + if (!err && col != -1) { + for (i = 0; i < ivar->cols; i++, col >>= 8) { + addr = (uint8_t)(col & 0xff); + nand_debug(NDBG_GEN,"send_address: send address column " + "%02x", addr); + err = NANDBUS_SEND_ADDRESS(nandbus, addr); + if (err) + break; + } + } + + if (!err && row != -1) { + for (i = 0; i < ivar->rows; i++, row >>= 8) { + addr = (uint8_t)(row & 0xff); + nand_debug(NDBG_GEN,"send_address: send address row " + "%02x", addr); + err = NANDBUS_SEND_ADDRESS(nandbus, addr); + if (err) + break; + } + } + + return (err); +} + +static int +generic_is_blk_bad(device_t dev, uint32_t block, uint8_t *bad) +{ + struct nand_chip *chip; + int page_number, err, i; + uint8_t *oob; + + chip = device_get_softc(dev); + + oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK); + if (!oob) { + device_printf(dev, "%s: cannot allocate OOB\n", __func__); + return (ENOMEM); + } + + page_number = block * chip->chip_geom.pgs_per_blk; + *bad = 0; + + /* Check OOB of first and second page */ + for (i = 0; i < 2; i++) { + err = NAND_READ_OOB(dev, page_number + i, oob, + chip->chip_geom.oob_size, 0); + if (err) { + device_printf(dev, "%s: cannot allocate OOB\n", + __func__); + free(oob, M_NAND); + return (ENOMEM); + } + + if (!oob[0]) { + *bad = 1; + free(oob, M_NAND); + return (0); + } + } + + free(oob, M_NAND); + + return (0); +} + +static int +generic_get_ecc(device_t dev, void *buf, void *ecc, int *needwrite) +{ + struct nand_chip *chip = device_get_softc(dev); + struct chip_geom *cg = &chip->chip_geom; + + return (NANDBUS_GET_ECC(device_get_parent(dev), buf, cg->page_size, + ecc, needwrite)); +} + +static int +generic_correct_ecc(device_t dev, void *buf, void *readecc, void *calcecc) +{ + struct nand_chip *chip = device_get_softc(dev); + struct chip_geom *cg = &chip->chip_geom; + + return (NANDBUS_CORRECT_ECC(device_get_parent(dev), buf, + cg->page_size, readecc, calcecc)); +} + + +#if 0 +int +nand_chng_read_col(device_t nand, uint32_t col, void *buf, size_t len) +{ + struct nand_chip *chip; + device_t nandbus; + + chip = device_get_softc(nand); + nandbus = device_get_parent(nand); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL)) + return (ENXIO); + + if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1)) + return (ENXIO); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + if (buf != NULL && len > 0) + NANDBUS_READ_BUFFER(nandbus, buf, len); + + return (0); +} + +int +nand_chng_write_col(device_t dev, uint32_t col, void *buf, + size_t len) +{ + struct nand_chip *chip; + device_t nandbus; + + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL)) + return (ENXIO); + + if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1)) + return (ENXIO); + + if (buf != NULL && len > 0) + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + return (0); +} + +int +nand_copyback_read(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN," raw read page %x[%x] at %x", page, col, len); + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_CPBK, row, 0)) + return (ENXIO); + + DELAY(chip->t_r); + if (check_fail(nandbus)) + return (ENXIO); + + if (buf != NULL && len > 0) + NANDBUS_READ_BUFFER(nandbus, buf, len); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_read++; + + return (0); +} + +int +nand_copyback_prog(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"copyback prog page %x[%x]", page, len); + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (!can_write(nandbus)) + return (ENXIO); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL)) + return (ENXIO); + + if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1)) + return (ENXIO); + + if (buf != NULL && len > 0) + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (send_end_program_page(nandbus, NAND_CMD_PROG_END)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_written++; + + return (0); +} + +int +nand_copyback_prog_intlv(device_t dev, uint32_t page) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + + nand_debug(NDBG_GEN,"cache prog page %x", page); + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (!can_write(nandbus)) + return (ENXIO); + + if (send_start_program_page(nand, row, 0)) + return (ENXIO); + + if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_written++; + + return (0); +} + +int +nand_prog_cache(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len, uint8_t end) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + uint8_t command; + + nand_debug(NDBG_GEN,"cache prog page %x[%x]", page, len); + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (!can_write(nandbus)) + return (ENXIO); + + if (send_start_program_page(dev, row, 0)) + return (ENXIO); + + NANDBUS_WRITE_BUFFER(nandbus, buf, len); + + if (end) + command = NAND_CMD_PROG_END; + else + command = NAND_CMD_PROG_CACHE; + + if (send_end_program_page(nandbus, command)) + return (ENXIO); + + DELAY(chip->t_prog); + + if (check_fail(nandbus)) + return (ENXIO); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_written++; + + return (0); +} + +int +nand_read_cache(device_t dev, uint32_t page, uint32_t col, + void *buf, size_t len, uint8_t end) +{ + struct nand_chip *chip; + struct page_stat *pg_stat; + device_t nandbus; + uint32_t row; + uint8_t command; + + nand_debug(NDBG_GEN,"cache read page %x[%x] ", page, len); + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (nand_check_page_boundary(chip, page)) + return (ENXIO); + + page_to_row(&chip->chip_geom, page, &row); + + if (page != -1) { + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ)) + return (ENXIO); + + if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1)) + return (ENXIO); + } + + if (end) + command = NAND_CMD_READ_CACHE_END; + else + command = NAND_CMD_READ_CACHE; + + if (NANDBUS_SEND_COMMAND(nandbus, command)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + DELAY(chip->t_r); + if (check_fail(nandbus)) + return (ENXIO); + + if (buf != NULL && len > 0) + NANDBUS_READ_BUFFER(nandbus, buf, len); + + pg_stat = &(chip->pg_stat[page]); + pg_stat->page_raw_read++; + + return (0); +} + +int +nand_get_feature(device_t dev, uint8_t feat, void *buf) +{ + struct nand_chip *chip; + device_t nandbus; + + nand_debug(NDBG_GEN,"nand get feature"); + + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_GET_FEATURE)) + return (ENXIO); + + if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat)) + return (ENXIO); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + DELAY(chip->t_r); + NANDBUS_READ_BUFFER(nandbus, buf, 4); + + return (0); +} + +int +nand_set_feature(device_t dev, uint8_t feat, void *buf) +{ + struct nand_chip *chip; + device_t nandbus; + + nand_debug(NDBG_GEN,"nand set feature"); + + chip = device_get_softc(dev); + nandbus = device_get_parent(dev); + + if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SET_FEATURE)) + return (ENXIO); + + if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat)) + return (ENXIO); + + NANDBUS_WRITE_BUFFER(nandbus, buf, 4); + + if (NANDBUS_START_COMMAND(nandbus)) + return (ENXIO); + + return (0); +} +#endif diff --git a/sys/dev/nand/nand_geom.c b/sys/dev/nand/nand_geom.c new file mode 100644 index 0000000..a8bdba2 --- /dev/null +++ b/sys/dev/nand/nand_geom.c @@ -0,0 +1,414 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "nand_if.h" +#include "nandbus_if.h" + +#define BIO_NAND_STD ((void *)1) +#define BIO_NAND_RAW ((void *)2) + +static disk_ioctl_t nand_ioctl; +static disk_getattr_t nand_getattr; +static disk_strategy_t nand_strategy; +static disk_strategy_t nand_strategy_raw; + +static int +nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len) +{ + + nand_debug(NDBG_GEOM, "Read from chip %d [%p] at %d", chip->num, chip, + offset); + + return (nand_read_pages(chip, offset, buf, len)); +} + +static int +nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len) +{ + + nand_debug(NDBG_GEOM, "Write to chip %d [%p] at %d", chip->num, chip, + offset); + + return (nand_prog_pages(chip, offset, buf, len)); +} + +static int +nand_read_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len) +{ + nand_debug(NDBG_GEOM, "Raw read from chip %d [%p] at %d", chip->num, + chip, offset); + + return (nand_read_pages_raw(chip, offset, buf, len)); +} + +static int +nand_write_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len) +{ + + nand_debug(NDBG_GEOM, "Raw write to chip %d [%p] at %d", chip->num, + chip, offset); + + return (nand_prog_pages_raw(chip, offset, buf, len)); +} + +static void +nand_strategy(struct bio *bp) +{ + struct nand_chip *chip; + + chip = (struct nand_chip *)bp->bio_disk->d_drv1; + + bp->bio_driver1 = BIO_NAND_STD; + + nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]", + (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : + ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" : + ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")), + chip->num, chip); + + mtx_lock(&chip->qlock); + bioq_insert_tail(&chip->bioq, bp); + mtx_unlock(&chip->qlock); + taskqueue_enqueue(chip->tq, &chip->iotask); +} + +static void +nand_strategy_raw(struct bio *bp) +{ + struct nand_chip *chip; + + chip = (struct nand_chip *)bp->bio_disk->d_drv1; + + /* Inform taskqueue that it's a raw access */ + bp->bio_driver1 = BIO_NAND_RAW; + + nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]", + (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : + ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" : + ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")), + chip->num, chip); + + mtx_lock(&chip->qlock); + bioq_insert_tail(&chip->bioq, bp); + mtx_unlock(&chip->qlock); + taskqueue_enqueue(chip->tq, &chip->iotask); +} + +static int +nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset, + uint32_t len, uint8_t *data, uint8_t write) +{ + struct chip_geom *cg; + int ret = 0; + + cg = &chip->chip_geom; + + if (!write) + ret = nand_read_oob(chip, page, data, cg->oob_size); + else + ret = nand_prog_oob(chip, page, data, cg->oob_size); + + return (ret); +} + +static int +nand_getattr(struct bio *bp) +{ + struct nand_chip *chip; + struct chip_geom *cg; + device_t dev; + + if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) + return (ENXIO); + + chip = (struct nand_chip *)bp->bio_disk->d_drv1; + cg = &(chip->chip_geom); + + dev = device_get_parent(chip->dev); + dev = device_get_parent(dev); + + do { + if (g_handleattr_int(bp, "NAND::oobsize", cg->oob_size)) + break; + else if (g_handleattr_int(bp, "NAND::pagesize", cg->page_size)) + break; + else if (g_handleattr_int(bp, "NAND::blocksize", + cg->block_size)) + break; + else if (g_handleattr(bp, "NAND::device", &(dev), + sizeof(device_t))) + break; + + return (ERESTART); + } while (0); + + return (EJUSTRETURN); +} + +static int +nand_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag, + struct thread *td) +{ + struct nand_chip *chip; + struct nand_oob_rw *oob_rw = NULL; + struct nand_raw_rw *raw_rw = NULL; + device_t nandbus; + uint8_t *buf = NULL; + int ret = 0; + uint8_t status; + + chip = (struct nand_chip *)ndisk->d_drv1; + nandbus = device_get_parent(chip->dev); + + if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) { + raw_rw = (struct nand_raw_rw *)data; + buf = malloc(raw_rw->len, M_NAND, M_WAITOK); + } + switch (cmd) { + case NAND_IO_ERASE: + ret = nand_erase_blocks(chip, ((off_t *)data)[0], + ((off_t *)data)[1]); + break; + + case NAND_IO_OOB_READ: + oob_rw = (struct nand_oob_rw *)data; + ret = nand_oob_access(chip, oob_rw->page, 0, + oob_rw->len, oob_rw->data, 0); + break; + + case NAND_IO_OOB_PROG: + oob_rw = (struct nand_oob_rw *)data; + ret = nand_oob_access(chip, oob_rw->page, 0, + oob_rw->len, oob_rw->data, 1); + break; + + case NAND_IO_GET_STATUS: + NANDBUS_LOCK(nandbus); + ret = NANDBUS_GET_STATUS(nandbus, &status); + if (ret == 0) + *(uint8_t *)data = status; + NANDBUS_UNLOCK(nandbus); + break; + + case NAND_IO_RAW_PROG: + copyin(raw_rw->data, buf, raw_rw->len); + ret = nand_prog_pages_raw(chip, raw_rw->off, buf, + raw_rw->len); + break; + + case NAND_IO_RAW_READ: + ret = nand_read_pages_raw(chip, raw_rw->off, buf, + raw_rw->len); + copyout(buf, raw_rw->data, raw_rw->len); + break; + + case NAND_IO_GET_CHIP_PARAM: + nand_get_chip_param(chip, (struct chip_param_io *)data); + break; + + default: + printf("Unknown nand_ioctl request \n"); + ret = EIO; + } + + if (buf) + free(buf, M_NAND); + + return (ret); +} + +static void +nand_io_proc(void *arg, int pending) +{ + struct nand_chip *chip = arg; + struct bio *bp; + int err = 0; + + for (;;) { + mtx_lock(&chip->qlock); + bp = bioq_takefirst(&chip->bioq); + mtx_unlock(&chip->qlock); + if (bp == NULL) + break; + + if (bp->bio_driver1 == BIO_NAND_STD) { + if ((bp->bio_cmd & BIO_READ) == BIO_READ) { + err = nand_read(chip, + bp->bio_offset & 0xffffffff, + bp->bio_data, bp->bio_bcount); + } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) { + err = nand_write(chip, + bp->bio_offset & 0xffffffff, + bp->bio_data, bp->bio_bcount); + } + } else if (bp->bio_driver1 == BIO_NAND_RAW) { + if ((bp->bio_cmd & BIO_READ) == BIO_READ) { + err = nand_read_raw(chip, + bp->bio_offset & 0xffffffff, + bp->bio_data, bp->bio_bcount); + } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) { + err = nand_write_raw(chip, + bp->bio_offset & 0xffffffff, + bp->bio_data, bp->bio_bcount); + } + } else + panic("Unknown access type in bio->bio_driver1\n"); + + if ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE) { + nand_debug(NDBG_GEOM, "Delete on chip%d offset %lld " + "length %ld\n", chip->num, bp->bio_offset, + bp->bio_bcount); + err = nand_erase_blocks(chip, + bp->bio_offset & 0xffffffff, + bp->bio_bcount); + } + + if (err == 0 || err == ECC_CORRECTABLE) + bp->bio_resid = 0; + else { + nand_debug(NDBG_GEOM,"nand_[read|write|erase_blocks] " + "error: %d\n", err); + + bp->bio_error = EIO; + bp->bio_flags |= BIO_ERROR; + bp->bio_resid = bp->bio_bcount; + } + biodone(bp); + } +} + +int +create_geom_disk(struct nand_chip *chip) +{ + struct disk *ndisk, *rdisk; + + /* Create the disk device */ + ndisk = disk_alloc(); + ndisk->d_strategy = nand_strategy; + ndisk->d_ioctl = nand_ioctl; + ndisk->d_getattr = nand_getattr; + ndisk->d_name = "gnand"; + ndisk->d_drv1 = chip; + ndisk->d_maxsize = chip->chip_geom.block_size; + ndisk->d_sectorsize = chip->chip_geom.page_size; + ndisk->d_mediasize = chip->chip_geom.chip_size; + ndisk->d_unit = chip->num + + 10 * device_get_unit(device_get_parent(chip->dev)); + + /* + * When using BBT, make two last blocks of device unavailable + * to user (because those are used to store BBT table). + */ + if (chip->bbt != NULL) + ndisk->d_mediasize -= (2 * chip->chip_geom.block_size); + + ndisk->d_flags = DISKFLAG_CANDELETE; + + snprintf(ndisk->d_ident, sizeof(ndisk->d_ident), + "nand: Man:0x%02x Dev:0x%02x", chip->id.man_id, chip->id.dev_id); + + disk_create(ndisk, DISK_VERSION); + + /* Create the RAW disk device */ + rdisk = disk_alloc(); + rdisk->d_strategy = nand_strategy_raw; + rdisk->d_ioctl = nand_ioctl; + rdisk->d_getattr = nand_getattr; + rdisk->d_name = "gnand.raw"; + rdisk->d_drv1 = chip; + rdisk->d_maxsize = chip->chip_geom.block_size; + rdisk->d_sectorsize = chip->chip_geom.page_size; + rdisk->d_mediasize = chip->chip_geom.chip_size; + rdisk->d_unit = chip->num + + 10 * device_get_unit(device_get_parent(chip->dev)); + + rdisk->d_flags = DISKFLAG_CANDELETE; + + snprintf(rdisk->d_ident, sizeof(rdisk->d_ident), + "nand_raw: Man:0x%02x Dev:0x%02x", chip->id.man_id, + chip->id.dev_id); + + disk_create(rdisk, DISK_VERSION); + + chip->ndisk = ndisk; + chip->rdisk = rdisk; + + mtx_init(&chip->qlock, "NAND I/O lock", NULL, MTX_DEF); + bioq_init(&chip->bioq); + + TASK_INIT(&chip->iotask, 0, nand_io_proc, chip); + chip->tq = taskqueue_create("nand_taskq", M_WAITOK, + taskqueue_thread_enqueue, &chip->tq); + taskqueue_start_threads(&chip->tq, 1, PI_DISK, "nand taskq"); + + if (bootverbose) + device_printf(chip->dev, "Created gnand%d for chip [0x%0x, " + "0x%0x]\n", ndisk->d_unit, chip->id.man_id, + chip->id.dev_id); + + return (0); +} + +void +destroy_geom_disk(struct nand_chip *chip) +{ + struct bio *bp; + + taskqueue_free(chip->tq); + disk_destroy(chip->ndisk); + disk_destroy(chip->rdisk); + + mtx_lock(&chip->qlock); + for (;;) { + bp = bioq_takefirst(&chip->bioq); + if (bp == NULL) + break; + bp->bio_error = EIO; + bp->bio_flags |= BIO_ERROR; + bp->bio_resid = bp->bio_bcount; + + biodone(bp); + } + mtx_unlock(&chip->qlock); + + mtx_destroy(&chip->qlock); +} diff --git a/sys/dev/nand/nand_id.c b/sys/dev/nand/nand_id.c new file mode 100644 index 0000000..75c5834 --- /dev/null +++ b/sys/dev/nand/nand_id.c @@ -0,0 +1,60 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +struct nand_params nand_ids[] = { + { { NAND_MAN_SAMSUNG, 0x75 }, "Samsung K9F5608U0B", + 0x20, 0x200, 0x10, 0x20, 0 }, + { { NAND_MAN_SAMSUNG, 0xd3 }, "Samsung NAND 1GiB 3,3V 8-bit", + 0x400, 0x800, 0x40, 0x40, 0 }, + { { NAND_MAN_HYNIX, 0x76 }, "Hynix NAND 64MiB 3,3V 8-bit", + 0x40, 0x200, 0x10, 0x20, 0 }, + { { NAND_MAN_HYNIX, 0xdc }, "Hynix NAND 512MiB 3,3V 8-bit", + 0x200, 0x800, 0x40, 0x40, 0 }, + { { NAND_MAN_HYNIX, 0x79 }, "NAND 128MB 3,3V 8-bit", + 0x80, 0x200, 0x10, 0x20, 0 }, + { { NAND_MAN_STMICRO, 0xf1 }, "STMicro 128MB 3,3V 8-bit", + 0x80, 2048, 64, 0x40, 0 }, +}; + +struct nand_params *nand_get_params(struct nand_id *id) +{ + int i; + + for (i = 0; i < sizeof(nand_ids) / sizeof(nand_ids[0]); i++) + if (nand_ids[i].id.man_id == id->man_id && + nand_ids[i].id.dev_id == id->dev_id) + return (&nand_ids[i]); + + return (NULL); +} diff --git a/sys/dev/nand/nand_if.m b/sys/dev/nand/nand_if.m new file mode 100644 index 0000000..49c8879 --- /dev/null +++ b/sys/dev/nand/nand_if.m @@ -0,0 +1,168 @@ +#- +# Copyright (C) 2009-2012 Semihalf +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# NAND chip interface description +# + +#include +#include + +INTERFACE nand; + +CODE { + static int nand_method_not_supported(device_t dev) + { + return (ENOENT); + } +}; + +# Read NAND page +# +# Return values: +# 0: Success +# +METHOD int read_page { + device_t dev; + uint32_t page; + void* buf; + uint32_t len; + uint32_t offset; +}; + +# Program NAND page +# +# Return values: +# 0: Success +# +METHOD int program_page { + device_t dev; + uint32_t page; + void* buf; + uint32_t len; + uint32_t offset; +}; + +# Program NAND page interleaved +# +# Return values: +# 0: Success +# +METHOD int program_page_intlv { + device_t dev; + uint32_t page; + void* buf; + uint32_t len; + uint32_t offset; +} DEFAULT nand_method_not_supported; + +# Read NAND oob +# +# Return values: +# 0: Success +# +METHOD int read_oob { + device_t dev; + uint32_t page; + void* buf; + uint32_t len; + uint32_t offset; +}; + +# Program NAND oob +# +# Return values: +# 0: Success +# +METHOD int program_oob { + device_t dev; + uint32_t page; + void* buf; + uint32_t len; + uint32_t offset; +}; + +# Erase NAND block +# +# Return values: +# 0: Success +# +METHOD int erase_block { + device_t dev; + uint32_t block; +}; + +# Erase NAND block interleaved +# +# Return values: +# 0: Success +# +METHOD int erase_block_intlv { + device_t dev; + uint32_t block; +} DEFAULT nand_method_not_supported; + +# NAND get status +# +# Return values: +# 0: Success +# +METHOD int get_status { + device_t dev; + uint8_t *status; +}; + +# NAND check if block is bad +# +# Return values: +# 0: Success +# +METHOD int is_blk_bad { + device_t dev; + uint32_t block_number; + uint8_t *bad; +}; + +# NAND get ECC +# +# +METHOD int get_ecc { + device_t dev; + void *buf; + void *ecc; + int *needwrite; +}; + +# NAND correct ECC +# +# +METHOD int correct_ecc { + device_t dev; + void *buf; + void *readecc; + void *calcecc; +}; + diff --git a/sys/dev/nand/nandbus.c b/sys/dev/nand/nandbus.c new file mode 100644 index 0000000..322d708 --- /dev/null +++ b/sys/dev/nand/nandbus.c @@ -0,0 +1,530 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "nand_if.h" +#include "nandbus_if.h" +#include "nfc_if.h" + +#define NAND_NCS 4 + +static int nandbus_probe(device_t dev); +static int nandbus_attach(device_t dev); +static int nandbus_detach(device_t dev); + +static int nandbus_child_location_str(device_t, device_t, char *, size_t); +static int nandbus_child_pnpinfo_str(device_t, device_t, char *, size_t); + +static int nandbus_get_status(device_t, uint8_t *); +static void nandbus_read_buffer(device_t, void *, uint32_t); +static int nandbus_select_cs(device_t, uint8_t); +static int nandbus_send_command(device_t, uint8_t); +static int nandbus_send_address(device_t, uint8_t); +static int nandbus_start_command(device_t); +static int nandbus_wait_ready(device_t, uint8_t *); +static void nandbus_write_buffer(device_t, void *, uint32_t); +static int nandbus_get_ecc(device_t, void *, uint32_t, void *, int *); +static int nandbus_correct_ecc(device_t, void *, int, void *, void *); +static void nandbus_lock(device_t); +static void nandbus_unlock(device_t); + +static int nand_readid(device_t, uint8_t *, uint8_t *); +static int nand_probe_onfi(device_t, uint8_t *); +static int nand_reset(device_t); + +struct nandbus_softc { + device_t dev; + struct cv nandbus_cv; + struct mtx nandbus_mtx; + uint8_t busy; +}; + +static device_method_t nandbus_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, nandbus_probe), + DEVMETHOD(device_attach, nandbus_attach), + DEVMETHOD(device_detach, nandbus_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_child_pnpinfo_str, nandbus_child_pnpinfo_str), + DEVMETHOD(bus_child_location_str, nandbus_child_location_str), + + /* nandbus interface */ + DEVMETHOD(nandbus_get_status, nandbus_get_status), + DEVMETHOD(nandbus_read_buffer, nandbus_read_buffer), + DEVMETHOD(nandbus_select_cs, nandbus_select_cs), + DEVMETHOD(nandbus_send_command, nandbus_send_command), + DEVMETHOD(nandbus_send_address, nandbus_send_address), + DEVMETHOD(nandbus_start_command,nandbus_start_command), + DEVMETHOD(nandbus_wait_ready, nandbus_wait_ready), + DEVMETHOD(nandbus_write_buffer, nandbus_write_buffer), + DEVMETHOD(nandbus_get_ecc, nandbus_get_ecc), + DEVMETHOD(nandbus_correct_ecc, nandbus_correct_ecc), + DEVMETHOD(nandbus_lock, nandbus_lock), + DEVMETHOD(nandbus_unlock, nandbus_unlock), + { 0, 0 } +}; + +devclass_t nandbus_devclass; + +driver_t nandbus_driver = { + "nandbus", + nandbus_methods, + sizeof(struct nandbus_softc) +}; + +DRIVER_MODULE(nandbus, nand, nandbus_driver, nandbus_devclass, 0, 0); + +int +nandbus_create(device_t nfc) +{ + device_t child; + + child = device_add_child(nfc, "nandbus", -1); + if (!child) + return (ENODEV); + + bus_generic_attach(nfc); + + return(0); +} + +void +nandbus_destroy(device_t nfc) +{ + device_t *children; + int nchildren, i; + + mtx_lock(&Giant); + /* Detach & delete all children */ + if (!device_get_children(nfc, &children, &nchildren)) { + for (i = 0; i < nchildren; i++) + device_delete_child(nfc, children[i]); + + free(children, M_TEMP); + } + mtx_unlock(&Giant); +} + +static int +nandbus_probe(device_t dev) +{ + + device_set_desc(dev, "NAND bus"); + + return (0); +} + +static int +nandbus_attach(device_t dev) +{ + device_t child, nfc; + struct nand_id chip_id; + struct nandbus_softc *sc; + struct nandbus_ivar *ivar; + struct nand_softc *nfc_sc; + struct nand_params *chip_params; + uint8_t cs, onfi; + + sc = device_get_softc(dev); + sc->dev = dev; + + nfc = device_get_parent(dev); + nfc_sc = device_get_softc(nfc); + + mtx_init(&sc->nandbus_mtx, "nandbus lock", MTX_DEF, 0); + cv_init(&sc->nandbus_cv, "nandbus cv"); + + /* Check each possible CS for existing nand devices */ + for (cs = 0; cs < NAND_NCS; cs++) { + nand_debug(NDBG_BUS,"probe chip select %x", cs); + + /* Select & reset chip */ + if (nandbus_select_cs(dev, cs)) + break; + + if (nand_reset(dev)) + continue; + + /* Read manufacturer and device id */ + if (nand_readid(dev, &chip_id.man_id, &chip_id.dev_id)) + continue; + + if (chip_id.man_id == 0xff) + continue; + + /* Check if chip is ONFI compliant */ + if (nand_probe_onfi(dev, &onfi) != 0) { + continue; + } + + ivar = malloc(sizeof(struct nandbus_ivar), + M_NAND, M_WAITOK); + + if (onfi == 1) { + ivar->cs = cs; + ivar->cols = 0; + ivar->rows = 0; + ivar->params = NULL; + ivar->man_id = chip_id.man_id; + ivar->dev_id = chip_id.dev_id; + ivar->is_onfi = onfi; + ivar->chip_cdev_name = nfc_sc->chip_cdev_name; + + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, ivar); + continue; + } + + chip_params = nand_get_params(&chip_id); + if (chip_params == NULL) { + nand_debug(NDBG_BUS,"Chip description not found! " + "(manuf: 0x%0x, chipid: 0x%0x)\n", + chip_id.man_id, chip_id.dev_id); + free(ivar, M_NAND); + continue; + } + + ivar->cs = cs; + ivar->cols = 1; + ivar->rows = 2; + ivar->params = chip_params; + ivar->man_id = chip_id.man_id; + ivar->dev_id = chip_id.dev_id; + ivar->is_onfi = onfi; + ivar->chip_cdev_name = nfc_sc->chip_cdev_name; + + /* + * Check what type of device we have. + * devices bigger than 32MiB have on more row (3) + */ + if (chip_params->chip_size > 32) + ivar->rows++; + /* Large page devices have one more col (2) */ + if (chip_params->chip_size >= 128 && + chip_params->page_size > 512) + ivar->cols++; + + child = device_add_child(dev, NULL, -1); + device_set_ivars(child, ivar); + } + + bus_generic_attach(dev); + return (0); +} + +static int +nandbus_detach(device_t dev) +{ + struct nandbus_softc *sc; + + sc = device_get_softc(dev); + + bus_generic_detach(dev); + + mtx_destroy(&sc->nandbus_mtx); + cv_destroy(&sc->nandbus_cv); + + return (0); +} + +static int +nandbus_child_location_str(device_t bus, device_t child, char *buf, + size_t buflen) +{ + struct nandbus_ivar *ivar = device_get_ivars(child); + + snprintf(buf, buflen, "at cs#%d", ivar->cs); + return (0); +} + +static int +nandbus_child_pnpinfo_str(device_t bus, device_t child, char *buf, + size_t buflen) +{ + // XXX man id, model id ???? + *buf = '\0'; + return (0); +} + +static int +nand_readid(device_t bus, uint8_t *man_id, uint8_t *dev_id) +{ + device_t nfc; + + if (!bus || !man_id || !dev_id) + return (EINVAL); + + nand_debug(NDBG_BUS,"read id"); + + nfc = device_get_parent(bus); + + if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) { + nand_debug(NDBG_BUS,"Error : could not send READ ID command"); + return (ENXIO); + } + + if (NFC_SEND_ADDRESS(nfc, 0)) { + nand_debug(NDBG_BUS,"Error : could not sent address to chip"); + return (ENXIO); + } + + if (NFC_START_COMMAND(nfc) != 0) { + nand_debug(NDBG_BUS,"Error : could not start command"); + return (ENXIO); + } + + DELAY(25); + + *man_id = NFC_READ_BYTE(nfc); + *dev_id = NFC_READ_BYTE(nfc); + + nand_debug(NDBG_BUS,"manufacturer id: %x chip id: %x", *man_id, + *dev_id); + + return (0); +} + +static int +nand_probe_onfi(device_t bus, uint8_t *onfi_compliant) +{ + device_t nfc; + char onfi_id[] = {'o', 'n', 'f', 'i', '\0'}; + int i; + + nand_debug(NDBG_BUS,"probing ONFI"); + + nfc = device_get_parent(bus); + + if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) { + nand_debug(NDBG_BUS,"Error : could not sent READ ID command"); + return (ENXIO); + } + + if (NFC_SEND_ADDRESS(nfc, ONFI_SIG_ADDR)) { + nand_debug(NDBG_BUS,"Error : could not sent address to chip"); + return (ENXIO); + } + + if (NFC_START_COMMAND(nfc) != 0) { + nand_debug(NDBG_BUS,"Error : could not start command"); + return (ENXIO); + } + for (i = 0; onfi_id[i] != '\0'; i++) + if (NFC_READ_BYTE(nfc) != onfi_id[i]) { + nand_debug(NDBG_BUS,"ONFI non-compliant"); + *onfi_compliant = 0; + return (0); + } + + nand_debug(NDBG_BUS,"ONFI compliant"); + *onfi_compliant = 1; + + return (0); +} + +static int +nand_reset(device_t bus) +{ + device_t nfc; + nand_debug(NDBG_BUS,"resetting..."); + + nfc = device_get_parent(bus); + + if (NFC_SEND_COMMAND(nfc, NAND_CMD_RESET) != 0) { + nand_debug(NDBG_BUS,"Error : could not sent RESET command"); + return (ENXIO); + } + + if (NFC_START_COMMAND(nfc) != 0) { + nand_debug(NDBG_BUS,"Error : could not start RESET command"); + return (ENXIO); + } + + DELAY(1000); + + return (0); +} + +void +nandbus_lock(device_t dev) +{ + struct nandbus_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock(&sc->nandbus_mtx); + if (sc->busy) + cv_wait(&sc->nandbus_cv, &sc->nandbus_mtx); + sc->busy = 1; + mtx_unlock(&sc->nandbus_mtx); +} + +void +nandbus_unlock(device_t dev) +{ + struct nandbus_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock(&sc->nandbus_mtx); + sc->busy = 0; + cv_signal(&sc->nandbus_cv); + mtx_unlock(&sc->nandbus_mtx); +} + +int +nandbus_select_cs(device_t dev, uint8_t cs) +{ + + return (NFC_SELECT_CS(device_get_parent(dev), cs)); +} + +int +nandbus_send_command(device_t dev, uint8_t command) +{ + int err; + + if ((err = NFC_SEND_COMMAND(device_get_parent(dev), command))) + nand_debug(NDBG_BUS,"Err: Could not send command %x, err %x", + command, err); + + return (err); +} + +int +nandbus_send_address(device_t dev, uint8_t address) +{ + int err; + + if ((err = NFC_SEND_ADDRESS(device_get_parent(dev), address))) + nand_debug(NDBG_BUS,"Err: Could not send address %x, err %x", + address, err); + + return (err); +} + +int +nandbus_start_command(device_t dev) +{ + int err; + + if ((err = NFC_START_COMMAND(device_get_parent(dev)))) + nand_debug(NDBG_BUS,"Err: Could not start command, err %x", + err); + + return (err); +} + +void +nandbus_read_buffer(device_t dev, void *buf, uint32_t len) +{ + + NFC_READ_BUF(device_get_parent(dev), buf, len); +} + +void +nandbus_write_buffer(device_t dev, void *buf, uint32_t len) +{ + + NFC_WRITE_BUF(device_get_parent(dev), buf, len); +} + +int +nandbus_get_status(device_t dev, uint8_t *status) +{ + int err; + + if ((err = NANDBUS_SEND_COMMAND(dev, NAND_CMD_STATUS))) + return (err); + if ((err = NANDBUS_START_COMMAND(dev))) + return (err); + + *status = NFC_READ_BYTE(device_get_parent(dev)); + + return (0); +} + +int +nandbus_wait_ready(device_t dev, uint8_t *status) +{ + struct timeval tv, tv2; + + tv2.tv_sec = 0; + tv2.tv_usec = 50 * 5000; /* 10ms */ + + getmicrotime(&tv); + timevaladd(&tv, &tv2); + + do { + if (NANDBUS_GET_STATUS(dev, status)) + return (ENXIO); + + if (*status & NAND_STATUS_RDY) + return (0); + + getmicrotime(&tv2); + } while (timevalcmp(&tv2, &tv, <=)); + + return (EBUSY); +} + +int +nandbus_get_ecc(device_t dev, void *buf, uint32_t pagesize, void *ecc, + int *needwrite) +{ + + return (NFC_GET_ECC(device_get_parent(dev), buf, pagesize, ecc, needwrite)); +} + +int +nandbus_correct_ecc(device_t dev, void *buf, int pagesize, void *readecc, + void *calcecc) +{ + + return (NFC_CORRECT_ECC(device_get_parent(dev), buf, pagesize, + readecc, calcecc)); +} + diff --git a/sys/dev/nand/nandbus.h b/sys/dev/nand/nandbus.h new file mode 100644 index 0000000..417fbf5 --- /dev/null +++ b/sys/dev/nand/nandbus.h @@ -0,0 +1,49 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NANDBUS_H_ +#define _NANDBUS_H_ + +struct nandbus_ivar { + uint8_t cs; + uint8_t cols; + uint8_t rows; + uint8_t man_id; + uint8_t dev_id; + uint8_t is_onfi; + char *chip_cdev_name; + struct nand_params *params; +}; + +extern devclass_t nandbus_devclass; +extern driver_t nandbus_driver; + +int nandbus_create(device_t nfc); +void nandbus_destroy(device_t nfc); + +#endif /* _NANDBUS_H_ */ diff --git a/sys/dev/nand/nandbus_if.m b/sys/dev/nand/nandbus_if.m new file mode 100644 index 0000000..e914e18 --- /dev/null +++ b/sys/dev/nand/nandbus_if.m @@ -0,0 +1,100 @@ +#- +# Copyright (C) 2009-2012 Semihalf +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# NAND bus interface description +# + +#include +#include + +INTERFACE nandbus; + +METHOD int get_status { + device_t dev; + uint8_t * status; +}; + +METHOD void read_buffer { + device_t dev; + void * buf; + uint32_t len; +}; + +METHOD int select_cs { + device_t dev; + uint8_t cs; +}; + +METHOD int send_command { + device_t dev; + uint8_t command; +}; + +METHOD int send_address { + device_t dev; + uint8_t address; +}; + +METHOD int start_command { + device_t dev; +}; + +METHOD int wait_ready { + device_t dev; + uint8_t * status; +} + +METHOD void write_buffer { + device_t dev; + void * buf; + uint32_t len; +}; + +METHOD int get_ecc { + device_t dev; + void * buf; + uint32_t pagesize; + void * ecc; + int * needwrite; +}; + +METHOD int correct_ecc { + device_t dev; + void * buf; + int pagesize; + void * readecc; + void * calcecc; +}; + +METHOD void lock { + device_t dev; +}; + +METHOD void unlock { + device_t dev; +}; + diff --git a/sys/dev/nand/nandsim.c b/sys/dev/nand/nandsim.c new file mode 100644 index 0000000..5390c01 --- /dev/null +++ b/sys/dev/nand/nandsim.c @@ -0,0 +1,665 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Simulated NAND controller driver */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct sim_param sim; +struct sim_ctrl_conf ctrls[MAX_SIM_DEV]; + +static struct cdev *nandsim_dev; +static d_ioctl_t nandsim_ioctl; + +static void nandsim_init_sim_param(struct sim_param *); +static int nandsim_create_ctrl(struct sim_ctrl *); +static int nandsim_destroy_ctrl(int); +static int nandsim_ctrl_status(struct sim_ctrl *); +static int nandsim_create_chip(struct sim_chip *); +static int nandsim_destroy_chip(struct sim_ctrl_chip *); +static int nandsim_chip_status(struct sim_chip *); +static int nandsim_start_ctrl(int); +static int nandsim_stop_ctrl(int); +static int nandsim_inject_error(struct sim_error *); +static int nandsim_get_block_state(struct sim_block_state *); +static int nandsim_set_block_state(struct sim_block_state *); +static int nandsim_modify(struct sim_mod *); +static int nandsim_dump(struct sim_dump *); +static int nandsim_restore(struct sim_dump *); +static int nandsim_freeze(struct sim_ctrl_chip *); +static void nandsim_print_log(struct sim_log *); +static struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t); + +static struct cdevsw nandsim_cdevsw = { + .d_version = D_VERSION, + .d_ioctl = nandsim_ioctl, + .d_name = "nandsim", +}; + +int +nandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int flags, struct thread *td) +{ + int ret = 0; + + switch (cmd) { + case NANDSIM_SIM_PARAM: + nandsim_init_sim_param((struct sim_param *)data); + break; + case NANDSIM_CREATE_CTRL: + ret = nandsim_create_ctrl((struct sim_ctrl *)data); + break; + case NANDSIM_DESTROY_CTRL: + ret = nandsim_destroy_ctrl(*(int *)data); + break; + case NANDSIM_STATUS_CTRL: + ret = nandsim_ctrl_status((struct sim_ctrl *)data); + break; + case NANDSIM_CREATE_CHIP: + ret = nandsim_create_chip((struct sim_chip *)data); + break; + case NANDSIM_DESTROY_CHIP: + ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data); + break; + case NANDSIM_STATUS_CHIP: + ret = nandsim_chip_status((struct sim_chip *)data); + break; + case NANDSIM_MODIFY: + ret = nandsim_modify((struct sim_mod *)data); + break; + case NANDSIM_START_CTRL: + ret = nandsim_start_ctrl(*(int *)data); + break; + case NANDSIM_STOP_CTRL: + ret = nandsim_stop_ctrl(*(int *)data); + break; + case NANDSIM_INJECT_ERROR: + ret = nandsim_inject_error((struct sim_error *)data); + break; + case NANDSIM_SET_BLOCK_STATE: + ret = nandsim_set_block_state((struct sim_block_state *)data); + break; + case NANDSIM_GET_BLOCK_STATE: + ret = nandsim_get_block_state((struct sim_block_state *)data); + break; + case NANDSIM_PRINT_LOG: + nandsim_print_log((struct sim_log *)data); + break; + case NANDSIM_DUMP: + ret = nandsim_dump((struct sim_dump *)data); + break; + case NANDSIM_RESTORE: + ret = nandsim_restore((struct sim_dump *)data); + break; + case NANDSIM_FREEZE: + ret = nandsim_freeze((struct sim_ctrl_chip *)data); + break; + default: + ret = EINVAL; + break; + } + + return (ret); +} + +static void +nandsim_init_sim_param(struct sim_param *param) +{ + + if (!param) + return; + + nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level, + param->log_output); + nandsim_log_level = param->log_level; + nandsim_log_output = param->log_output; +} + +static int +nandsim_create_ctrl(struct sim_ctrl *ctrl) +{ + struct sim_ctrl_conf *sim_ctrl; + + nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num, + ctrl->num_cs); + + if (ctrl->num >= MAX_SIM_DEV) { + return (EINVAL); + } + + sim_ctrl = &ctrls[ctrl->num]; + if(sim_ctrl->created) + return (EEXIST); + + sim_ctrl->num = ctrl->num; + sim_ctrl->num_cs = ctrl->num_cs; + sim_ctrl->ecc = ctrl->ecc; + memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout, + MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0])); + strlcpy(sim_ctrl->filename, ctrl->filename, + FILENAME_SIZE); + sim_ctrl->created = 1; + + return (0); +} + +static int +nandsim_destroy_ctrl(int ctrl_num) +{ + + nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num); + + if (ctrl_num >= MAX_SIM_DEV) { + return (EINVAL); + } + + if (!ctrls[ctrl_num].created) { + return (ENODEV); + } + + if (ctrls[ctrl_num].running) { + return (EBUSY); + } + + memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num])); + + return (0); +} + +static int +nandsim_ctrl_status(struct sim_ctrl *ctrl) +{ + + nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num, + ctrl->num_cs); + + if (ctrl->num >= MAX_SIM_DEV) { + return (EINVAL); + } + + ctrl->num_cs = ctrls[ctrl->num].num_cs; + ctrl->ecc = ctrls[ctrl->num].ecc; + memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout, + MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0])); + strlcpy(ctrl->filename, ctrls[ctrl->num].filename, + FILENAME_SIZE); + ctrl->running = ctrls[ctrl->num].running; + ctrl->created = ctrls[ctrl->num].created; + + return (0); +} + +static int +nandsim_create_chip(struct sim_chip *chip) +{ + struct sim_chip *sim_chip; + + nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num, + chip->ctrl_num); + + if (chip->ctrl_num >= MAX_SIM_DEV || + chip->num >= MAX_CTRL_CS) { + return (EINVAL); + } + + if (ctrls[chip->ctrl_num].chips[chip->num]) { + return (EEXIST); + } + + sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM, + M_WAITOK); + if (sim_chip == NULL) { + return (ENOMEM); + } + + memcpy(sim_chip, chip, sizeof(*sim_chip)); + ctrls[chip->ctrl_num].chips[chip->num] = sim_chip; + sim_chip->created = 1; + + return (0); +} + +static int +nandsim_destroy_chip(struct sim_ctrl_chip *chip) +{ + struct sim_ctrl_conf *ctrl_conf; + + nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num, + chip->ctrl_num); + + if (chip->ctrl_num >= MAX_SIM_DEV || + chip->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + ctrl_conf = &ctrls[chip->ctrl_num]; + + if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num]) + return (ENODEV); + + if (ctrl_conf->running) + return (EBUSY); + + free(ctrl_conf->chips[chip->chip_num], M_NANDSIM); + ctrl_conf->chips[chip->chip_num] = NULL; + + return (0); +} + +static int +nandsim_chip_status(struct sim_chip *chip) +{ + struct sim_ctrl_conf *ctrl_conf; + + nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num, + chip->ctrl_num); + + if (chip->ctrl_num >= MAX_SIM_DEV && + chip->num >= MAX_CTRL_CS) + return (EINVAL); + + ctrl_conf = &ctrls[chip->ctrl_num]; + if (!ctrl_conf->chips[chip->num]) + chip->created = 0; + else + memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip)); + + return (0); +} + +static int +nandsim_start_ctrl(int num) +{ + device_t nexus, ndev; + devclass_t nexus_devclass; + int ret = 0; + + nand_debug(NDBG_SIM,"start ctlr num:%d", num); + + if (num >= MAX_SIM_DEV) + return (EINVAL); + + if (!ctrls[num].created) + return (ENODEV); + + if (ctrls[num].running) + return (EBUSY); + + /* We will add our device as a child of the nexus0 device */ + if (!(nexus_devclass = devclass_find("nexus")) || + !(nexus = devclass_get_device(nexus_devclass, 0))) + return (EFAULT); + + /* + * Create a newbus device representing this frontend instance + * + * XXX powerpc nexus doesn't implement bus_add_child, so child + * must be added by device_add_child(). + */ +#if defined(__powerpc__) + ndev = device_add_child(nexus, "nandsim", num); +#else + ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num); +#endif + if (!ndev) + return (EFAULT); + + mtx_lock(&Giant); + ret = device_probe_and_attach(ndev); + mtx_unlock(&Giant); + + if (ret == 0) { + ctrls[num].sim_ctrl_dev = ndev; + ctrls[num].running = 1; + } + + return (ret); +} + +static int +nandsim_stop_ctrl(int num) +{ + device_t nexus; + devclass_t nexus_devclass; + int ret = 0; + + nand_debug(NDBG_SIM,"stop controller num:%d", num); + + if (num >= MAX_SIM_DEV) { + return (EINVAL); + } + + if (!ctrls[num].created || !ctrls[num].running) { + return (ENODEV); + } + + /* We will add our device as a child of the nexus0 device */ + if (!(nexus_devclass = devclass_find("nexus")) || + !(nexus = devclass_get_device(nexus_devclass, 0))) { + return (ENODEV); + } + + mtx_lock(&Giant); + if (ctrls[num].sim_ctrl_dev) { + ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev); + ctrls[num].sim_ctrl_dev = NULL; + } + mtx_unlock(&Giant); + + ctrls[num].running = 0; + + return (ret); +} + +static struct nandsim_chip * +get_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num) +{ + struct nandsim_softc *sc; + + if (!ctrls[ctrl_num].sim_ctrl_dev) + return (NULL); + + sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev); + return (sc->chips[chip_num]); +} + +static void +nandsim_print_log(struct sim_log *sim_log) +{ + struct nandsim_softc *sc; + int len1, len2; + + if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev) + return; + + sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev); + if (sc->log_buff) { + len1 = strlen(&sc->log_buff[sc->log_idx + 1]); + if (len1 >= sim_log->len) + len1 = sim_log->len; + copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1); + len2 = strlen(sc->log_buff); + if (len2 >= (sim_log->len - len1)) + len2 = (sim_log->len - len1); + copyout(sc->log_buff, &sim_log->log[len1], len2); + sim_log->len = len1 + len2; + } +} + +static int +nandsim_inject_error(struct sim_error *error) +{ + struct nandsim_chip *chip; + struct block_space *bs; + struct onfi_params *param; + int page, page_size, block, offset; + + nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n", + error->chip_num, error->ctrl_num); + + if (error->ctrl_num >= MAX_SIM_DEV || + error->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running) + return (ENODEV); + + chip = get_nandsim_chip(error->ctrl_num, error->chip_num); + param = &chip->params; + page_size = param->bytes_per_page + param->spare_bytes_per_page; + block = error->page_num / param->pages_per_block; + page = error->page_num % param->pages_per_block; + + bs = get_bs(chip->swap, block, 1); + if (!bs) + return (EINVAL); + + offset = (page * page_size) + error->column; + memset(&bs->blk_ptr[offset], error->pattern, error->len); + + return (0); +} + +static int +nandsim_set_block_state(struct sim_block_state *bs) +{ + struct onfi_params *params; + struct nandsim_chip *chip; + int blocks; + + nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n", + bs->chip_num, bs->ctrl_num, bs->block_num); + + if (bs->ctrl_num >= MAX_SIM_DEV || + bs->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num); + params = &chip->params; + blocks = params->luns * params->blocks_per_lun; + + if (bs->block_num > blocks) + return (EINVAL); + + chip->blk_state[bs->block_num].is_bad = bs->state; + + if (bs->wearout >= 0) + chip->blk_state[bs->block_num].wear_lev = bs->wearout; + + return (0); +} + +static int +nandsim_get_block_state(struct sim_block_state *bs) +{ + struct onfi_params *params; + struct nandsim_chip *chip; + int blocks; + + if (bs->ctrl_num >= MAX_SIM_DEV || + bs->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n", + bs->chip_num, bs->ctrl_num, bs->block_num); + + chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num); + params = &chip->params; + blocks = params->luns * params->blocks_per_lun; + + if (bs->block_num > blocks) + return (EINVAL); + + bs->state = chip->blk_state[bs->block_num].is_bad; + bs->wearout = chip->blk_state[bs->block_num].wear_lev; + + return (0); +} + +static int +nandsim_dump(struct sim_dump *dump) +{ + struct nandsim_chip *chip; + struct block_space *bs; + int blk_size; + + nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num); + + if (dump->ctrl_num >= MAX_SIM_DEV || + dump->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num); + blk_size = chip->cg.block_size + + (chip->cg.oob_size * chip->cg.pgs_per_blk); + + bs = get_bs(chip->swap, dump->block_num, 0); + if (!bs) + return (EINVAL); + + if (dump->len > blk_size) + dump->len = blk_size; + + copyout(bs->blk_ptr, dump->data, dump->len); + + return (0); +} + +static int +nandsim_restore(struct sim_dump *dump) +{ + struct nandsim_chip *chip; + struct block_space *bs; + int blk_size; + + nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num, + dump->chip_num); + + if (dump->ctrl_num >= MAX_SIM_DEV || + dump->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num); + blk_size = chip->cg.block_size + + (chip->cg.oob_size * chip->cg.pgs_per_blk); + + bs = get_bs(chip->swap, dump->block_num, 1); + if (!bs) + return (EINVAL); + + if (dump->len > blk_size) + dump->len = blk_size; + + + copyin(dump->data, bs->blk_ptr, dump->len); + + return (0); +} + +static int +nandsim_freeze(struct sim_ctrl_chip *ctrl_chip) +{ + struct nandsim_chip *chip; + + if (ctrl_chip->ctrl_num >= MAX_SIM_DEV || + ctrl_chip->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num); + nandsim_chip_freeze(chip); + + return (0); +} + +static int +nandsim_modify(struct sim_mod *mod) +{ + struct sim_chip *sim_conf = NULL; + struct nandsim_chip *sim_chip = NULL; + + nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num, + mod->chip_num); + + if (mod->field != SIM_MOD_LOG_LEVEL) { + if (mod->ctrl_num >= MAX_SIM_DEV || + mod->chip_num >= MAX_CTRL_CS) + return (EINVAL); + + sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num]; + sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num); + } + + switch (mod->field) { + case SIM_MOD_LOG_LEVEL: + nandsim_log_level = mod->new_value; + break; + case SIM_MOD_ERASE_TIME: + sim_conf->erase_time = sim_chip->erase_delay = mod->new_value; + break; + case SIM_MOD_PROG_TIME: + sim_conf->prog_time = sim_chip->prog_delay = mod->new_value; + break; + case SIM_MOD_READ_TIME: + sim_conf->read_time = sim_chip->read_delay = mod->new_value; + break; + case SIM_MOD_ERROR_RATIO: + sim_conf->error_ratio = mod->new_value; + sim_chip->error_ratio = mod->new_value; + break; + default: + break; + } + + return (0); +} +static int +nandsim_modevent(module_t mod __unused, int type, void *data __unused) +{ + struct sim_ctrl_chip chip_ctrl; + int i, j; + + switch (type) { + case MOD_LOAD: + nandsim_dev = make_dev(&nandsim_cdevsw, 0, + UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl"); + break; + case MOD_UNLOAD: + for (i = 0; i < MAX_SIM_DEV; i++) { + nandsim_stop_ctrl(i); + chip_ctrl.ctrl_num = i; + for (j = 0; j < MAX_CTRL_CS; j++) { + chip_ctrl.chip_num = j; + nandsim_destroy_chip(&chip_ctrl); + } + nandsim_destroy_ctrl(i); + } + destroy_dev(nandsim_dev); + break; + case MOD_SHUTDOWN: + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + +DEV_MODULE(nandsim, nandsim_modevent, NULL); +MODULE_VERSION(nandsim, 1); diff --git a/sys/dev/nand/nandsim.h b/sys/dev/nand/nandsim.h new file mode 100644 index 0000000..fbbb6d4 --- /dev/null +++ b/sys/dev/nand/nandsim.h @@ -0,0 +1,175 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NANDSIM_H_ +#define _NANDSIM_H_ + +#include +#include + +#define MAX_SIM_DEV 4 +#define MAX_CTRL_CS 4 +#define MAX_ECC_BYTES 512 +#define MAX_BAD_BLOCKS 512 +#define DEV_MODEL_STR_SIZE 21 +#define MAN_STR_SIZE 13 +#define FILENAME_SIZE 20 + +#define MAX_CHIPS (MAX_SIM_DEV*MAX_CTRL_CS) + +#define NANDSIM_OUTPUT_NONE 0x0 +#define NANDSIM_OUTPUT_CONSOLE 0x1 +#define NANDSIM_OUTPUT_RAM 0x2 +#define NANDSIM_OUTPUT_FILE 0x3 + +struct sim_ctrl_chip { + uint8_t ctrl_num; + uint8_t chip_num; +}; + +#define NANDSIM_BASE 'A' + +struct sim_param { + uint8_t log_level; + uint8_t log_output; +}; + +#define NANDSIM_SIM_PARAM _IOW(NANDSIM_BASE, 1, struct sim_param) + +struct sim_ctrl { + uint8_t running; + uint8_t created; + uint8_t num; + uint8_t num_cs; + uint8_t ecc; + char filename[FILENAME_SIZE]; + uint16_t ecc_layout[MAX_ECC_BYTES]; +}; +#define NANDSIM_CREATE_CTRL _IOW(NANDSIM_BASE, 2, struct sim_ctrl) +#define NANDSIM_DESTROY_CTRL _IOW(NANDSIM_BASE, 3, int) + +struct sim_chip { + uint8_t num; + uint8_t ctrl_num; + uint8_t created; + uint8_t device_id; + uint8_t manufact_id; + char device_model[DEV_MODEL_STR_SIZE]; + char manufacturer[MAN_STR_SIZE]; + uint8_t col_addr_cycles; + uint8_t row_addr_cycles; + uint8_t features; + uint8_t width; + uint32_t page_size; + uint32_t oob_size; + uint32_t pgs_per_blk; + uint32_t blks_per_lun; + uint32_t luns; + + uint32_t prog_time; + uint32_t erase_time; + uint32_t read_time; + uint32_t ccs_time; + + uint32_t error_ratio; + uint32_t wear_level; + uint32_t bad_block_map[MAX_BAD_BLOCKS]; + uint8_t is_wp; +}; + +#define NANDSIM_CREATE_CHIP _IOW(NANDSIM_BASE, 3, struct sim_chip) + +struct sim_chip_destroy { + uint8_t ctrl_num; + uint8_t chip_num; +}; +#define NANDSIM_DESTROY_CHIP _IOW(NANDSIM_BASE, 4, struct sim_chip_destroy) + +#define NANDSIM_START_CTRL _IOW(NANDSIM_BASE, 5, int) +#define NANDSIM_STOP_CTRL _IOW(NANDSIM_BASE, 6, int) +#define NANDSIM_RESTART_CTRL _IOW(NANDSIM_BASE, 7, int) + +#define NANDSIM_STATUS_CTRL _IOWR(NANDSIM_BASE, 8, struct sim_ctrl) +#define NANDSIM_STATUS_CHIP _IOWR(NANDSIM_BASE, 9, struct sim_chip) + +struct sim_mod { + uint8_t chip_num; + uint8_t ctrl_num; + uint32_t field; + uint32_t new_value; +}; +#define SIM_MOD_LOG_LEVEL 0 +#define SIM_MOD_ERASE_TIME 1 +#define SIM_MOD_PROG_TIME 2 +#define SIM_MOD_READ_TIME 3 +#define SIM_MOD_CCS_TIME 4 +#define SIM_MOD_ERROR_RATIO 5 + +#define NANDSIM_MODIFY _IOW(NANDSIM_BASE, 10, struct sim_mod) +#define NANDSIM_FREEZE _IOW(NANDSIM_BASE, 11, struct sim_ctrl_chip) + +struct sim_error { + uint8_t ctrl_num; + uint8_t chip_num; + uint32_t page_num; + uint32_t column; + uint32_t len; + uint32_t pattern; +}; +#define NANDSIM_INJECT_ERROR _IOW(NANDSIM_BASE, 20, struct sim_error) + +#define NANDSIM_GOOD_BLOCK 0 +#define NANDSIM_BAD_BLOCK 1 +struct sim_block_state { + uint8_t ctrl_num; + uint8_t chip_num; + uint32_t block_num; + int wearout; + uint8_t state; +}; +#define NANDSIM_SET_BLOCK_STATE _IOW(NANDSIM_BASE, 21, struct sim_block_state) +#define NANDSIM_GET_BLOCK_STATE _IOWR(NANDSIM_BASE, 22, struct sim_block_state) + +struct sim_log { + uint8_t ctrl_num; + char* log; + size_t len; +}; +#define NANDSIM_PRINT_LOG _IOWR(NANDSIM_BASE, 23, struct sim_log) + +struct sim_dump { + uint8_t ctrl_num; + uint8_t chip_num; + uint32_t block_num; + uint32_t len; + void* data; +}; +#define NANDSIM_DUMP _IOWR(NANDSIM_BASE, 24, struct sim_dump) +#define NANDSIM_RESTORE _IOWR(NANDSIM_BASE, 25, struct sim_dump) + +#endif /* _NANDSIM_H_ */ diff --git a/sys/dev/nand/nandsim_chip.c b/sys/dev/nand/nandsim_chip.c new file mode 100644 index 0000000..f04ad80 --- /dev/null +++ b/sys/dev/nand/nandsim_chip.c @@ -0,0 +1,901 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MALLOC_DEFINE(M_NANDSIM, "NANDsim", "NANDsim dynamic data"); + +#define NANDSIM_CHIP_LOCK(chip) mtx_lock(&(chip)->ns_lock) +#define NANDSIM_CHIP_UNLOCK(chip) mtx_unlock(&(chip)->ns_lock) + +static nandsim_evh_t erase_evh; +static nandsim_evh_t idle_evh; +static nandsim_evh_t poweron_evh; +static nandsim_evh_t reset_evh; +static nandsim_evh_t read_evh; +static nandsim_evh_t readid_evh; +static nandsim_evh_t readparam_evh; +static nandsim_evh_t write_evh; + +static void nandsim_loop(void *); +static void nandsim_undefined(struct nandsim_chip *, uint8_t); +static void nandsim_bad_address(struct nandsim_chip *, uint8_t *); +static void nandsim_ignore_address(struct nandsim_chip *, uint8_t); +static void nandsim_sm_error(struct nandsim_chip *); +static void nandsim_start_handler(struct nandsim_chip *, nandsim_evh_t); + +static void nandsim_callout_eh(void *); +static int nandsim_delay(struct nandsim_chip *, int); + +static int nandsim_bbm_init(struct nandsim_chip *, uint32_t, uint32_t *); +static int nandsim_blk_state_init(struct nandsim_chip *, uint32_t, uint32_t); +static void nandsim_blk_state_destroy(struct nandsim_chip *); +static int nandchip_is_block_valid(struct nandsim_chip *, int); + +static void nandchip_set_status(struct nandsim_chip *, uint8_t); +static void nandchip_clear_status(struct nandsim_chip *, uint8_t); + +struct proc *nandsim_proc; + +struct nandsim_chip * +nandsim_chip_init(struct nandsim_softc* sc, uint8_t chip_num, + struct sim_chip *sim_chip) +{ + struct nandsim_chip *chip; + struct onfi_params *chip_param; + char swapfile[20]; + uint32_t size; + int error; + + chip = malloc(sizeof(*chip), M_NANDSIM, M_WAITOK | M_ZERO); + if (!chip) + return (NULL); + + mtx_init(&chip->ns_lock, "nandsim lock", NULL, MTX_DEF); + callout_init(&chip->ns_callout, CALLOUT_MPSAFE); + STAILQ_INIT(&chip->nandsim_events); + + chip->chip_num = chip_num; + chip->ctrl_num = sim_chip->ctrl_num; + chip->sc = sc; + + if (!sim_chip->is_wp) + nandchip_set_status(chip, NAND_STATUS_WP); + + chip_param = &chip->params; + + chip->id.dev_id = sim_chip->device_id; + chip->id.man_id = sim_chip->manufact_id; + + chip->error_ratio = sim_chip->error_ratio; + chip->wear_level = sim_chip->wear_level; + chip->prog_delay = sim_chip->prog_time; + chip->erase_delay = sim_chip->erase_time; + chip->read_delay = sim_chip->read_time; + + chip_param->t_prog = sim_chip->prog_time; + chip_param->t_bers = sim_chip->erase_time; + chip_param->t_r = sim_chip->read_time; + bcopy("onfi", &chip_param->signature, 4); + + chip_param->manufacturer_id = sim_chip->manufact_id; + strncpy(chip_param->manufacturer_name, sim_chip->manufacturer, 12); + chip_param->manufacturer_name[11] = 0; + strncpy(chip_param->device_model, sim_chip->device_model, 20); + chip_param->device_model[19] = 0; + + chip_param->bytes_per_page = sim_chip->page_size; + chip_param->spare_bytes_per_page = sim_chip->oob_size; + chip_param->pages_per_block = sim_chip->pgs_per_blk; + chip_param->blocks_per_lun = sim_chip->blks_per_lun; + chip_param->luns = sim_chip->luns; + + init_chip_geom(&chip->cg, chip_param->luns, chip_param->blocks_per_lun, + chip_param->pages_per_block, chip_param->bytes_per_page, + chip_param->spare_bytes_per_page); + + chip_param->address_cycles = sim_chip->row_addr_cycles | + (sim_chip->col_addr_cycles << 4); + chip_param->features = sim_chip->features; + if (sim_chip->width == 16) + chip_param->features |= ONFI_FEAT_16BIT; + + size = chip_param->blocks_per_lun * chip_param->luns; + + error = nandsim_blk_state_init(chip, size, sim_chip->wear_level); + if (error) { + mtx_destroy(&chip->ns_lock); + free(chip, M_NANDSIM); + return (NULL); + } + + error = nandsim_bbm_init(chip, size, sim_chip->bad_block_map); + if (error) { + mtx_destroy(&chip->ns_lock); + nandsim_blk_state_destroy(chip); + free(chip, M_NANDSIM); + return (NULL); + } + + nandsim_start_handler(chip, poweron_evh); + + nand_debug(NDBG_SIM,"Create thread for chip%d [%8p]", chip->chip_num, + chip); + /* Create chip thread */ + error = kproc_kthread_add(nandsim_loop, chip, &nandsim_proc, + &chip->nandsim_td, RFSTOPPED | RFHIGHPID, + 0, "nandsim", "chip"); + if (error) { + mtx_destroy(&chip->ns_lock); + nandsim_blk_state_destroy(chip); + free(chip, M_NANDSIM); + return (NULL); + } + + thread_lock(chip->nandsim_td); + sched_class(chip->nandsim_td, PRI_REALTIME); + sched_add(chip->nandsim_td, SRQ_BORING); + thread_unlock(chip->nandsim_td); + + size = (chip_param->bytes_per_page + + chip_param->spare_bytes_per_page) * + chip_param->pages_per_block; + + sprintf(swapfile, "chip%d%d.swp", chip->ctrl_num, chip->chip_num); + chip->swap = nandsim_swap_init(swapfile, chip_param->blocks_per_lun * + chip_param->luns, size); + if (!chip->swap) + nandsim_chip_destroy(chip); + + /* Wait for new thread to enter main loop */ + tsleep(chip->nandsim_td, PWAIT, "ns_chip", 1 * hz); + + return (chip); +} + +static int +nandsim_blk_state_init(struct nandsim_chip *chip, uint32_t size, + uint32_t wear_lev) +{ + int i; + + if (!chip || size == 0) + return (-1); + + chip->blk_state = malloc(size * sizeof(struct nandsim_block_state), + M_NANDSIM, M_WAITOK | M_ZERO); + if (!chip->blk_state) { + return (-1); + } + + for (i = 0; i < size; i++) { + if (wear_lev) + chip->blk_state[i].wear_lev = wear_lev; + else + chip->blk_state[i].wear_lev = -1; + } + + return (0); +} + +static void +nandsim_blk_state_destroy(struct nandsim_chip *chip) +{ + + if (chip && chip->blk_state) + free(chip->blk_state, M_NANDSIM); +} + +static int +nandsim_bbm_init(struct nandsim_chip *chip, uint32_t size, + uint32_t *sim_bbm) +{ + uint32_t index; + int i; + + if ((chip == NULL) || (size == 0)) + return (-1); + + if (chip->blk_state == NULL) + return (-1); + + if (sim_bbm == NULL) + return (0); + + for (i = 0; i < MAX_BAD_BLOCKS; i++) { + index = sim_bbm[i]; + + if (index == 0xffffffff) + break; + else if (index > size) + return (-1); + else + chip->blk_state[index].is_bad = 1; + } + + return (0); +} + +void +nandsim_chip_destroy(struct nandsim_chip *chip) +{ + struct nandsim_ev *ev; + + ev = create_event(chip, NANDSIM_EV_EXIT, 0); + if (ev) + send_event(ev); +} + +void +nandsim_chip_freeze(struct nandsim_chip *chip) +{ + + chip->flags |= NANDSIM_CHIP_FROZEN; +} + +static void +nandsim_loop(void *arg) +{ + struct nandsim_chip *chip = (struct nandsim_chip *)arg; + struct nandsim_ev *ev; + + nand_debug(NDBG_SIM,"Start main loop for chip%d [%8p]", chip->chip_num, + chip); + for(;;) { + NANDSIM_CHIP_LOCK(chip); + if (!(chip->flags & NANDSIM_CHIP_ACTIVE)) { + chip->flags |= NANDSIM_CHIP_ACTIVE; + wakeup(chip->nandsim_td); + } + + if (STAILQ_EMPTY(&chip->nandsim_events)) { + nand_debug(NDBG_SIM,"Chip%d [%8p] going sleep", + chip->chip_num, chip); + msleep(chip, &chip->ns_lock, PRIBIO, "nandev", 0); + } + + ev = STAILQ_FIRST(&chip->nandsim_events); + STAILQ_REMOVE_HEAD(&chip->nandsim_events, links); + NANDSIM_CHIP_UNLOCK(chip); + if (ev->type == NANDSIM_EV_EXIT) { + NANDSIM_CHIP_LOCK(chip); + destroy_event(ev); + wakeup(ev); + while (!STAILQ_EMPTY(&chip->nandsim_events)) { + ev = STAILQ_FIRST(&chip->nandsim_events); + STAILQ_REMOVE_HEAD(&chip->nandsim_events, + links); + destroy_event(ev); + wakeup(ev); + }; + NANDSIM_CHIP_UNLOCK(chip); + nandsim_log(chip, NANDSIM_LOG_SM, "destroyed\n"); + mtx_destroy(&chip->ns_lock); + nandsim_blk_state_destroy(chip); + nandsim_swap_destroy(chip->swap); + free(chip, M_NANDSIM); + nandsim_proc = NULL; + + kthread_exit(); + } + + if (!(chip->flags & NANDSIM_CHIP_FROZEN)) { + nand_debug(NDBG_SIM,"Chip [%x] get event [%x]", + chip->chip_num, ev->type); + chip->ev_handler(chip, ev->type, ev->data); + } + + wakeup(ev); + destroy_event(ev); + } + +} + +struct nandsim_ev * +create_event(struct nandsim_chip *chip, uint8_t type, uint8_t data_size) +{ + struct nandsim_ev *ev; + + ev = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO); + if (!ev) { + nand_debug(NDBG_SIM,"Cannot create event"); + return (NULL); + } + + if (data_size > 0) + ev->data = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO); + ev->type = type; + ev->chip = chip; + + return (ev); +} + +void +destroy_event(struct nandsim_ev *ev) +{ + + if (ev->data) + free(ev->data, M_NANDSIM); + free(ev, M_NANDSIM); +} + +int +send_event(struct nandsim_ev *ev) +{ + struct nandsim_chip *chip = ev->chip; + + if (!(chip->flags & NANDSIM_CHIP_FROZEN)) { + nand_debug(NDBG_SIM,"Chip%d [%p] send event %x", + chip->chip_num, chip, ev->type); + + NANDSIM_CHIP_LOCK(chip); + STAILQ_INSERT_TAIL(&chip->nandsim_events, ev, links); + NANDSIM_CHIP_UNLOCK(chip); + + wakeup(chip); + if ((ev->type != NANDSIM_EV_TIMEOUT) && chip->nandsim_td && + (curthread != chip->nandsim_td)) + tsleep(ev, PWAIT, "ns_ev", 5 * hz); + } + + return (0); +} + +static void +nandsim_callout_eh(void *arg) +{ + struct nandsim_ev *ev = (struct nandsim_ev *)arg; + + send_event(ev); +} + +static int +nandsim_delay(struct nandsim_chip *chip, int timeout) +{ + struct nandsim_ev *ev; + struct timeval delay; + int tm; + + nand_debug(NDBG_SIM,"Chip[%d] Set delay: %d", chip->chip_num, timeout); + + ev = create_event(chip, NANDSIM_EV_TIMEOUT, 0); + if (!ev) + return (-1); + + chip->sm_state = NANDSIM_STATE_TIMEOUT; + tm = (timeout/10000) * (hz / 100); + if (callout_reset(&chip->ns_callout, tm, nandsim_callout_eh, ev)) + return (-1); + + delay.tv_sec = chip->read_delay / 1000000; + delay.tv_usec = chip->read_delay % 1000000; + timevaladd(&chip->delay_tv, &delay); + + return (0); +} + +static void +nandsim_start_handler(struct nandsim_chip *chip, nandsim_evh_t evh) +{ + struct nandsim_ev *ev; + + chip->ev_handler = evh; + + nand_debug(NDBG_SIM,"Start handler %p for chip%d [%p]", evh, + chip->chip_num, chip); + ev = create_event(chip, NANDSIM_EV_START, 0); + if (!ev) + nandsim_sm_error(chip); + + send_event(ev); +} + +static void +nandchip_set_data(struct nandsim_chip *chip, uint8_t *data, uint32_t len, + uint32_t idx) +{ + + nand_debug(NDBG_SIM,"Chip [%x] data %p [%x] at %x", chip->chip_num, + data, len, idx); + chip->data.data_ptr = data; + chip->data.size = len; + chip->data.index = idx; +} + +static int +nandchip_chip_space(struct nandsim_chip *chip, int32_t row, int32_t column, + size_t size, uint8_t writing) +{ + struct block_space *blk_space; + uint32_t lun, block, page, offset, block_size; + int err; + + block_size = chip->cg.block_size + + (chip->cg.oob_size * chip->cg.pgs_per_blk); + + err = nand_row_to_blkpg(&chip->cg, row, &lun, &block, &page); + if (err) { + nand_debug(NDBG_SIM,"cannot get address\n"); + return (-1); + } + + if (!nandchip_is_block_valid(chip, block)) { + nandchip_set_data(chip, NULL, 0, 0); + return (-1); + } + + blk_space = get_bs(chip->swap, block, writing); + if (!blk_space) { + nandchip_set_data(chip, NULL, 0, 0); + return (-1); + } + + if (size > block_size) + size = block_size; + + if (size == block_size) { + offset = 0; + column = 0; + } else + offset = page * (chip->cg.page_size + chip->cg.oob_size); + + nandchip_set_data(chip, &blk_space->blk_ptr[offset], size, column); + + return (0); +} + +static int +nandchip_get_addr_byte(struct nandsim_chip *chip, void *data, uint32_t *value) +{ + int ncycles = 0; + uint8_t byte; + uint8_t *buffer; + + buffer = (uint8_t *)value; + byte = *((uint8_t *)data); + + KASSERT((chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW || + chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL), + ("unexpected state")); + + if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) { + ncycles = chip->params.address_cycles & 0xf; + buffer[chip->sm_addr_cycle++] = byte; + } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) { + ncycles = (chip->params.address_cycles >> 4) & 0xf; + buffer[chip->sm_addr_cycle++] = byte; + } + + nand_debug(NDBG_SIM, "Chip [%x] read addr byte: %02x (%d of %d)\n", + chip->chip_num, byte, chip->sm_addr_cycle, ncycles); + + if (chip->sm_addr_cycle == ncycles) { + chip->sm_addr_cycle = 0; + return (0); + } + + return (1); +} + +static int +nandchip_is_block_valid(struct nandsim_chip *chip, int block_num) +{ + + if (!chip || !chip->blk_state) + return (0); + + if (chip->blk_state[block_num].wear_lev == 0 || + chip->blk_state[block_num].is_bad) + return (0); + + return (1); +} + +static void +nandchip_set_status(struct nandsim_chip *chip, uint8_t flags) +{ + + chip->chip_status |= flags; +} + +static void +nandchip_clear_status(struct nandsim_chip *chip, uint8_t flags) +{ + + chip->chip_status &= ~flags; +} + +uint8_t +nandchip_get_status(struct nandsim_chip *chip) +{ + return (chip->chip_status); +} + +void +nandsim_chip_timeout(struct nandsim_chip *chip) +{ + struct timeval tv; + + getmicrotime(&tv); + + if (chip->sm_state == NANDSIM_STATE_TIMEOUT && + timevalcmp(&tv, &chip->delay_tv, >=)) { + nandchip_set_status(chip, NAND_STATUS_RDY); + } +} +void +poweron_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + uint8_t cmd; + + if (type == NANDSIM_EV_START) + chip->sm_state = NANDSIM_STATE_IDLE; + else if (type == NANDSIM_EV_CMD) { + cmd = *(uint8_t *)data; + switch(cmd) { + case NAND_CMD_RESET: + nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n"); + nandsim_start_handler(chip, reset_evh); + break; + default: + nandsim_undefined(chip, type); + break; + } + } else + nandsim_undefined(chip, type); +} + +void +idle_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + uint8_t cmd; + + if (type == NANDSIM_EV_START) { + nandsim_log(chip, NANDSIM_LOG_SM, "in IDLE state\n"); + chip->sm_state = NANDSIM_STATE_WAIT_CMD; + } else if (type == NANDSIM_EV_CMD) { + nandchip_clear_status(chip, NAND_STATUS_FAIL); + getmicrotime(&chip->delay_tv); + cmd = *(uint8_t *)data; + switch(cmd) { + case NAND_CMD_READ_ID: + nandsim_start_handler(chip, readid_evh); + break; + case NAND_CMD_READ_PARAMETER: + nandsim_start_handler(chip, readparam_evh); + break; + case NAND_CMD_READ: + nandsim_start_handler(chip, read_evh); + break; + case NAND_CMD_PROG: + nandsim_start_handler(chip, write_evh); + break; + case NAND_CMD_ERASE: + nandsim_start_handler(chip, erase_evh); + break; + default: + nandsim_undefined(chip, type); + break; + } + } else + nandsim_undefined(chip, type); +} + +void +readid_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + struct onfi_params *params; + uint8_t addr; + + params = &chip->params; + + if (type == NANDSIM_EV_START) { + nandsim_log(chip, NANDSIM_LOG_SM, "in READID state\n"); + chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE; + } else if (type == NANDSIM_EV_ADDR) { + + addr = *((uint8_t *)data); + + if (addr == 0x0) + nandchip_set_data(chip, (uint8_t *)&chip->id, 2, 0); + else if (addr == ONFI_SIG_ADDR) + nandchip_set_data(chip, (uint8_t *)¶ms->signature, + 4, 0); + else + nandsim_bad_address(chip, &addr); + + nandsim_start_handler(chip, idle_evh); + } else + nandsim_undefined(chip, type); +} + +void +readparam_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + struct onfi_params *params; + uint8_t addr; + + params = &chip->params; + + if (type == NANDSIM_EV_START) { + nandsim_log(chip, NANDSIM_LOG_SM, "in READPARAM state\n"); + chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE; + } else if (type == NANDSIM_EV_ADDR) { + addr = *((uint8_t *)data); + + if (addr == 0) { + nandchip_set_data(chip, (uint8_t *)params, + sizeof(*params), 0); + } else + nandsim_bad_address(chip, &addr); + + nandsim_start_handler(chip, idle_evh); + } else + nandsim_undefined(chip, type); +} + +void +read_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + static uint32_t column = 0, row = 0; + uint32_t size; + uint8_t cmd; + + size = chip->cg.page_size + chip->cg.oob_size; + + switch (type) { + case NANDSIM_EV_START: + nandsim_log(chip, NANDSIM_LOG_SM, "in READ state\n"); + chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL; + break; + case NANDSIM_EV_ADDR: + if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) { + if (nandchip_get_addr_byte(chip, data, &column)) + break; + + chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW; + } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) { + if (nandchip_get_addr_byte(chip, data, &row)) + break; + + chip->sm_state = NANDSIM_STATE_WAIT_CMD; + } else + nandsim_ignore_address(chip, *((uint8_t *)data)); + break; + case NANDSIM_EV_CMD: + cmd = *(uint8_t *)data; + if (chip->sm_state == NANDSIM_STATE_WAIT_CMD && + cmd == NAND_CMD_READ_END) { + if (chip->read_delay != 0 && + nandsim_delay(chip, chip->read_delay) == 0) + nandchip_clear_status(chip, NAND_STATUS_RDY); + else { + nandchip_chip_space(chip, row, column, size, 0); + nandchip_set_status(chip, NAND_STATUS_RDY); + nandsim_start_handler(chip, idle_evh); + } + } else + nandsim_undefined(chip, type); + break; + case NANDSIM_EV_TIMEOUT: + if (chip->sm_state == NANDSIM_STATE_TIMEOUT) { + nandchip_chip_space(chip, row, column, size, 0); + nandchip_set_status(chip, NAND_STATUS_RDY); + nandsim_start_handler(chip, idle_evh); + } else + nandsim_undefined(chip, type); + break; + } +} +void +write_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + static uint32_t column, row; + uint32_t size; + uint8_t cmd; + int err; + + size = chip->cg.page_size + chip->cg.oob_size; + + switch(type) { + case NANDSIM_EV_START: + nandsim_log(chip, NANDSIM_LOG_SM, "in WRITE state\n"); + chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL; + break; + case NANDSIM_EV_ADDR: + if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) { + if (nandchip_get_addr_byte(chip, data, &column)) + break; + + chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW; + } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) { + if (nandchip_get_addr_byte(chip, data, &row)) + break; + + err = nandchip_chip_space(chip, row, column, size, 1); + if (err == -1) + nandchip_set_status(chip, NAND_STATUS_FAIL); + + chip->sm_state = NANDSIM_STATE_WAIT_CMD; + } else + nandsim_ignore_address(chip, *((uint8_t *)data)); + break; + case NANDSIM_EV_CMD: + cmd = *(uint8_t *)data; + if (chip->sm_state == NANDSIM_STATE_WAIT_CMD && + cmd == NAND_CMD_PROG_END) { + if (chip->prog_delay != 0 && + nandsim_delay(chip, chip->prog_delay) == 0) + nandchip_clear_status(chip, NAND_STATUS_RDY); + else { + nandchip_set_status(chip, NAND_STATUS_RDY); + nandsim_start_handler(chip, idle_evh); + } + } else + nandsim_undefined(chip, type); + break; + case NANDSIM_EV_TIMEOUT: + if (chip->sm_state == NANDSIM_STATE_TIMEOUT) { + nandsim_start_handler(chip, idle_evh); + nandchip_set_status(chip, NAND_STATUS_RDY); + } else + nandsim_undefined(chip, type); + break; + } +} + +void +erase_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + static uint32_t row, block_size; + uint32_t lun, block, page; + int err; + uint8_t cmd; + + block_size = chip->cg.block_size + + (chip->cg.oob_size * chip->cg.pgs_per_blk); + + switch (type) { + case NANDSIM_EV_START: + nandsim_log(chip, NANDSIM_LOG_SM, "in ERASE state\n"); + chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW; + break; + case NANDSIM_EV_CMD: + cmd = *(uint8_t *)data; + if (chip->sm_state == NANDSIM_STATE_WAIT_CMD && + cmd == NAND_CMD_ERASE_END) { + if (chip->data.data_ptr != NULL && + chip->data.size == block_size) + memset(chip->data.data_ptr, 0xff, block_size); + else + nand_debug(NDBG_SIM,"Bad block erase data\n"); + + err = nand_row_to_blkpg(&chip->cg, row, &lun, + &block, &page); + if (!err) { + if (chip->blk_state[block].wear_lev > 0) + chip->blk_state[block].wear_lev--; + } + + if (chip->erase_delay != 0 && + nandsim_delay(chip, chip->erase_delay) == 0) + nandchip_clear_status(chip, NAND_STATUS_RDY); + else { + nandchip_set_status(chip, NAND_STATUS_RDY); + nandsim_start_handler(chip, idle_evh); + } + } else + nandsim_undefined(chip, type); + break; + case NANDSIM_EV_ADDR: + if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) { + if (nandchip_get_addr_byte(chip, data, &row)) + break; + + err = nandchip_chip_space(chip, row, 0, block_size, 1); + if (err == -1) { + nandchip_set_status(chip, NAND_STATUS_FAIL); + } + chip->sm_state = NANDSIM_STATE_WAIT_CMD; + } else + nandsim_ignore_address(chip, *((uint8_t *)data)); + break; + case NANDSIM_EV_TIMEOUT: + if (chip->sm_state == NANDSIM_STATE_TIMEOUT) { + nandchip_set_status(chip, NAND_STATUS_RDY); + nandsim_start_handler(chip, idle_evh); + } else + nandsim_undefined(chip, type); + break; + } +} + +void +reset_evh(struct nandsim_chip *chip, uint32_t type, void *data) +{ + + if (type == NANDSIM_EV_START) { + nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n"); + chip->sm_state = NANDSIM_STATE_TIMEOUT; + nandchip_set_data(chip, NULL, 0, 0); + DELAY(500); + nandsim_start_handler(chip, idle_evh); + } else + nandsim_undefined(chip, type); +} + +static void +nandsim_undefined(struct nandsim_chip *chip, uint8_t type) +{ + + nandsim_log(chip, NANDSIM_LOG_ERR, + "ERR: Chip received ev %x in state %x\n", + type, chip->sm_state); + nandsim_start_handler(chip, idle_evh); +} + +static void +nandsim_bad_address(struct nandsim_chip *chip, uint8_t *addr) +{ + + nandsim_log(chip, NANDSIM_LOG_ERR, + "ERR: Chip received out of range address" + "%02x%02x - %02x%02x%02x\n", addr[0], addr[1], addr[2], + addr[3], addr[4]); +} + +static void +nandsim_ignore_address(struct nandsim_chip *chip, uint8_t byte) +{ + nandsim_log(chip, NANDSIM_LOG_SM, "ignored address byte: %d\n", byte); +} + +static void +nandsim_sm_error(struct nandsim_chip *chip) +{ + + nandsim_log(chip, NANDSIM_LOG_ERR, "ERR: State machine error." + "Restart required.\n"); +} diff --git a/sys/dev/nand/nandsim_chip.h b/sys/dev/nand/nandsim_chip.h new file mode 100644 index 0000000..a6fb234 --- /dev/null +++ b/sys/dev/nand/nandsim_chip.h @@ -0,0 +1,159 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NANDSIM_CHIP_H +#define _NANDSIM_CHIP_H + +#include +#include +#include +#include +#include + +MALLOC_DECLARE(M_NANDSIM); + +#define MAX_CS_NUM 4 +struct nandsim_chip; + +typedef void nandsim_evh_t(struct nandsim_chip *chip, uint32_t ev, void *data); + +enum addr_type { + ADDR_NONE, + ADDR_ID, + ADDR_ROW, + ADDR_ROWCOL +}; + +struct nandsim_softc { + struct nand_softc nand_dev; + device_t dev; + + struct nandsim_chip *chips[MAX_CS_NUM]; + struct nandsim_chip *active_chip; + + uint8_t address_cycle; + enum addr_type address_type; + int log_idx; + char *log_buff; + struct alq *alq; +}; + +struct nandsim_ev { + STAILQ_ENTRY(nandsim_ev) links; + struct nandsim_chip *chip; + uint8_t type; + void *data; +}; + +struct nandsim_data { + uint8_t *data_ptr; + uint32_t index; + uint32_t size; +}; + +struct nandsim_block_state { + int32_t wear_lev; + uint8_t is_bad; +}; + +#define NANDSIM_CHIP_ACTIVE 0x1 +#define NANDSIM_CHIP_FROZEN 0x2 +#define NANDSIM_CHIP_GET_STATUS 0x4 + +struct nandsim_chip { + struct nandsim_softc *sc; + struct thread *nandsim_td; + + STAILQ_HEAD(, nandsim_ev) nandsim_events; + nandsim_evh_t *ev_handler; + struct mtx ns_lock; + struct callout ns_callout; + + struct chip_geom cg; + struct nand_id id; + struct onfi_params params; + struct nandsim_data data; + struct nandsim_block_state *blk_state; + + struct chip_swap *swap; + + uint32_t error_ratio; + uint32_t wear_level; + uint32_t sm_state; + uint32_t sm_addr_cycle; + + uint32_t erase_delay; + uint32_t prog_delay; + uint32_t read_delay; + struct timeval delay_tv; + + uint8_t flags; + uint8_t chip_status; + uint8_t ctrl_num; + uint8_t chip_num; +}; + +struct sim_ctrl_conf { + uint8_t num; + uint8_t num_cs; + uint8_t ecc; + uint8_t running; + uint8_t created; + device_t sim_ctrl_dev; + struct sim_chip *chips[MAX_CTRL_CS]; + uint16_t ecc_layout[MAX_ECC_BYTES]; + char filename[FILENAME_SIZE]; +}; + +#define NANDSIM_STATE_IDLE 0x0 +#define NANDSIM_STATE_WAIT_ADDR_BYTE 0x1 +#define NANDSIM_STATE_WAIT_CMD 0x2 +#define NANDSIM_STATE_TIMEOUT 0x3 +#define NANDSIM_STATE_WAIT_ADDR_ROW 0x4 +#define NANDSIM_STATE_WAIT_ADDR_COL 0x5 + +#define NANDSIM_EV_START 0x1 +#define NANDSIM_EV_CMD 0x2 +#define NANDSIM_EV_ADDR 0x3 +#define NANDSIM_EV_TIMEOUT 0x4 +#define NANDSIM_EV_EXIT 0xff + +struct nandsim_chip *nandsim_chip_init(struct nandsim_softc *, + uint8_t, struct sim_chip *); +void nandsim_chip_destroy(struct nandsim_chip *); +void nandsim_chip_freeze(struct nandsim_chip *); +void nandsim_chip_timeout(struct nandsim_chip *); +int nandsim_chip_check_bad_block(struct nandsim_chip *, int); + +uint8_t nandchip_get_status(struct nandsim_chip *); + +void destroy_event(struct nandsim_ev *); +int send_event(struct nandsim_ev *); +struct nandsim_ev *create_event(struct nandsim_chip *, uint8_t, uint8_t); + +#endif /* _NANDSIM_CHIP_H */ diff --git a/sys/dev/nand/nandsim_ctrl.c b/sys/dev/nand/nandsim_ctrl.c new file mode 100644 index 0000000..5f7b019 --- /dev/null +++ b/sys/dev/nand/nandsim_ctrl.c @@ -0,0 +1,396 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Simulated NAND controller driver */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "nfc_if.h" + +#define ADDRESS_SIZE 5 + +extern struct sim_ctrl_conf ctrls[MAX_SIM_DEV]; + +static void byte_corrupt(struct nandsim_chip *, uint8_t *); + +static int nandsim_attach(device_t); +static int nandsim_detach(device_t); +static int nandsim_probe(device_t); + +static uint8_t nandsim_read_byte(device_t); +static uint16_t nandsim_read_word(device_t); +static int nandsim_select_cs(device_t, uint8_t); +static void nandsim_write_byte(device_t, uint8_t); +static void nandsim_write_word(device_t, uint16_t); +static void nandsim_read_buf(device_t, void *, uint32_t); +static void nandsim_write_buf(device_t, void *, uint32_t); +static int nandsim_send_command(device_t, uint8_t); +static int nandsim_send_address(device_t, uint8_t); + +static device_method_t nandsim_methods[] = { + DEVMETHOD(device_probe, nandsim_probe), + DEVMETHOD(device_attach, nandsim_attach), + DEVMETHOD(device_detach, nandsim_detach), + + DEVMETHOD(nfc_select_cs, nandsim_select_cs), + DEVMETHOD(nfc_send_command, nandsim_send_command), + DEVMETHOD(nfc_send_address, nandsim_send_address), + DEVMETHOD(nfc_read_byte, nandsim_read_byte), + DEVMETHOD(nfc_read_word, nandsim_read_word), + DEVMETHOD(nfc_write_byte, nandsim_write_byte), + DEVMETHOD(nfc_read_buf, nandsim_read_buf), + DEVMETHOD(nfc_write_buf, nandsim_write_buf), + + { 0, 0 }, +}; + +static driver_t nandsim_driver = { + "nandsim", + nandsim_methods, + sizeof(struct nandsim_softc), +}; + +static devclass_t nandsim_devclass; +DRIVER_MODULE(nandsim, nexus, nandsim_driver, nandsim_devclass, 0, 0); +DRIVER_MODULE(nandbus, nandsim, nandbus_driver, nandbus_devclass, 0, 0); + +static int +nandsim_probe(device_t dev) +{ + + device_set_desc(dev, "NAND controller simulator"); + return (BUS_PROBE_DEFAULT); +} + +static int +nandsim_attach(device_t dev) +{ + struct nandsim_softc *sc; + struct sim_ctrl_conf *params; + struct sim_chip *chip; + uint16_t *eccpos; + int i, err; + + sc = device_get_softc(dev); + params = &ctrls[device_get_unit(dev)]; + + if (strlen(params->filename) == 0) + snprintf(params->filename, FILENAME_SIZE, "ctrl%d.log", + params->num); + + nandsim_log_init(sc, params->filename); + for (i = 0; i < params->num_cs; i++) { + chip = params->chips[i]; + if (chip && chip->device_id != 0) { + sc->chips[i] = nandsim_chip_init(sc, i, chip); + if (chip->features & ONFI_FEAT_16BIT) + sc->nand_dev.flags |= NAND_16_BIT; + } + } + + if (params->ecc_layout[0] != 0xffff) + eccpos = params->ecc_layout; + else + eccpos = NULL; + + nand_init(&sc->nand_dev, dev, params->ecc, 0, 0, eccpos, "nandsim"); + + err = nandbus_create(dev); + + return (err); +} + +static int +nandsim_detach(device_t dev) +{ + struct nandsim_softc *sc; + struct sim_ctrl_conf *params; + int i; + + sc = device_get_softc(dev); + params = &ctrls[device_get_unit(dev)]; + + for (i = 0; i < params->num_cs; i++) + if (sc->chips[i] != NULL) + nandsim_chip_destroy(sc->chips[i]); + + nandsim_log_close(sc); + + return (0); +} + +static int +nandsim_select_cs(device_t dev, uint8_t cs) +{ + struct nandsim_softc *sc; + + sc = device_get_softc(dev); + + if (cs >= MAX_CS_NUM) + return (EINVAL); + + sc->active_chip = sc->chips[cs]; + + if (sc->active_chip) + nandsim_log(sc->active_chip, NANDSIM_LOG_EV, + "Select cs %d\n", cs); + + return (0); +} + +static int +nandsim_send_command(device_t dev, uint8_t command) +{ + struct nandsim_softc *sc; + struct nandsim_chip *chip; + struct nandsim_ev *ev; + + sc = device_get_softc(dev); + chip = sc->active_chip; + + if (chip == NULL) + return (0); + + nandsim_log(chip, NANDSIM_LOG_EV, "Send command %x\n", command); + + switch (command) { + case NAND_CMD_READ_ID: + case NAND_CMD_READ_PARAMETER: + sc->address_type = ADDR_ID; + break; + case NAND_CMD_ERASE: + sc->address_type = ADDR_ROW; + break; + case NAND_CMD_READ: + case NAND_CMD_PROG: + sc->address_type = ADDR_ROWCOL; + break; + default: + sc->address_type = ADDR_NONE; + break; + } + + if (command == NAND_CMD_STATUS) + chip->flags |= NANDSIM_CHIP_GET_STATUS; + else { + ev = create_event(chip, NANDSIM_EV_CMD, 1); + *(uint8_t *)ev->data = command; + send_event(ev); + } + + return (0); +} + +static int +nandsim_send_address(device_t dev, uint8_t addr) +{ + struct nandsim_ev *ev; + struct nandsim_softc *sc; + struct nandsim_chip *chip; + + sc = device_get_softc(dev); + chip = sc->active_chip; + + if (chip == NULL) + return (0); + + KASSERT((sc->address_type != ADDR_NONE), ("unexpected address")); + nandsim_log(chip, NANDSIM_LOG_EV, "Send addr %x\n", addr); + + ev = create_event(chip, NANDSIM_EV_ADDR, 1); + + *((uint8_t *)(ev->data)) = addr; + + send_event(ev); + return (0); +} + +static uint8_t +nandsim_read_byte(device_t dev) +{ + struct nandsim_softc *sc; + struct nandsim_chip *chip; + uint8_t ret = 0xff; + + sc = device_get_softc(dev); + chip = sc->active_chip; + + if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) { + if (chip->flags & NANDSIM_CHIP_GET_STATUS) { + nandsim_chip_timeout(chip); + ret = nandchip_get_status(chip); + chip->flags &= ~NANDSIM_CHIP_GET_STATUS; + } else if (chip->data.index < chip->data.size) { + ret = chip->data.data_ptr[chip->data.index++]; + byte_corrupt(chip, &ret); + } + nandsim_log(chip, NANDSIM_LOG_DATA, "read %02x\n", ret); + } + + return (ret); +} + +static uint16_t +nandsim_read_word(device_t dev) +{ + struct nandsim_softc *sc; + struct nandsim_chip *chip; + uint16_t *data_ptr; + uint16_t ret = 0xffff; + uint8_t *byte_ret = (uint8_t *)&ret; + + sc = device_get_softc(dev); + chip = sc->active_chip; + + if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) { + if (chip->data.index < chip->data.size - 1) { + data_ptr = + (uint16_t *)&(chip->data.data_ptr[chip->data.index]); + ret = *data_ptr; + chip->data.index += 2; + byte_corrupt(chip, byte_ret); + byte_corrupt(chip, byte_ret + 1); + } + nandsim_log(chip, NANDSIM_LOG_DATA, "read %04x\n", ret); + } + + return (ret); +} + +static void +nandsim_write_byte(device_t dev, uint8_t byte) +{ + struct nandsim_softc *sc; + struct nandsim_chip *chip; + + sc = device_get_softc(dev); + chip = sc->active_chip; + + if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN) && + (chip->data.index < chip->data.size)) { + byte_corrupt(chip, &byte); + chip->data.data_ptr[chip->data.index] &= byte; + chip->data.index++; + nandsim_log(chip, NANDSIM_LOG_DATA, "write %02x\n", byte); + } +} + +static void +nandsim_write_word(device_t dev, uint16_t word) +{ + struct nandsim_softc *sc; + struct nandsim_chip *chip; + uint16_t *data_ptr; + uint8_t *byte_ptr = (uint8_t *)&word; + + sc = device_get_softc(dev); + chip = sc->active_chip; + + if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) { + if ((chip->data.index + 1) < chip->data.size) { + byte_corrupt(chip, byte_ptr); + byte_corrupt(chip, byte_ptr + 1); + data_ptr = + (uint16_t *)&(chip->data.data_ptr[chip->data.index]); + *data_ptr &= word; + chip->data.index += 2; + } + + nandsim_log(chip, NANDSIM_LOG_DATA, "write %04x\n", word); + } +} + +static void +nandsim_read_buf(device_t dev, void *buf, uint32_t len) +{ + struct nandsim_softc *sc; + uint16_t *buf16 = (uint16_t *)buf; + uint8_t *buf8 = (uint8_t *)buf; + int i; + + sc = device_get_softc(dev); + + if (sc->nand_dev.flags & NAND_16_BIT) { + for (i = 0; i < len / 2; i++) + buf16[i] = nandsim_read_word(dev); + } else { + for (i = 0; i < len; i++) + buf8[i] = nandsim_read_byte(dev); + } +} + +static void +nandsim_write_buf(device_t dev, void *buf, uint32_t len) +{ + struct nandsim_softc *sc; + uint16_t *buf16 = (uint16_t *)buf; + uint8_t *buf8 = (uint8_t *)buf; + int i; + + sc = device_get_softc(dev); + + if (sc->nand_dev.flags & NAND_16_BIT) { + for (i = 0; i < len / 2; i++) + nandsim_write_word(dev, buf16[i]); + } else { + for (i = 0; i < len; i++) + nandsim_write_byte(dev, buf8[i]); + } +} + +static void +byte_corrupt(struct nandsim_chip *chip, uint8_t *byte) +{ + uint32_t rand; + uint8_t bit; + + rand = random(); + if ((rand % 1000000) < chip->error_ratio) { + bit = rand % 8; + if (*byte & (1 << bit)) + *byte &= ~(1 << bit); + else + *byte |= (1 << bit); + } +} diff --git a/sys/dev/nand/nandsim_log.c b/sys/dev/nand/nandsim_log.c new file mode 100644 index 0000000..c51118f --- /dev/null +++ b/sys/dev/nand/nandsim_log.c @@ -0,0 +1,186 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +int nandsim_log_level; +int nandsim_log_output; +int log_size = NANDSIM_RAM_LOG_SIZE; + +static int nandsim_entry_size = NANDSIM_ENTRY_SIZE; +static int nandsim_entry_count = NANDSIM_ENTRY_COUNT; +static int str_index = 0; +static char string[NANDSIM_ENTRY_SIZE + 1] = {0}; + +int +nandsim_log_init(struct nandsim_softc *sc, char *filename) +{ + int error = 0; + + if (nandsim_log_output == NANDSIM_OUTPUT_FILE) { + error = alq_open(&sc->alq, filename, + curthread->td_ucred, 0644, + nandsim_entry_size, nandsim_entry_count); + } else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) { + sc->log_buff = malloc(log_size, M_NANDSIM, M_WAITOK | M_ZERO); + if (!sc->log_buff) + error = ENOMEM; + } + + return (error); +} + +void +nandsim_log_close(struct nandsim_softc *sc) +{ + + if (nandsim_log_output == NANDSIM_OUTPUT_FILE) { + memset(&string[str_index], 0, NANDSIM_ENTRY_SIZE - str_index); + alq_write(sc->alq, (void *) string, ALQ_NOWAIT); + str_index = 0; + string[0] = '\0'; + alq_close(sc->alq); + } else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) { + free(sc->log_buff, M_NANDSIM); + sc->log_buff = NULL; + } +} + +void +nandsim_log(struct nandsim_chip *chip, int level, const char *fmt, ...) +{ + char hdr[TIME_STR_SIZE]; + char tmp[NANDSIM_ENTRY_SIZE]; + struct nandsim_softc *sc; + struct timeval currtime; + va_list ap; + int hdr_len, len, rest; + + if (nandsim_log_output == NANDSIM_OUTPUT_NONE) + return; + + if (chip == NULL) + return; + + sc = chip->sc; + if (!sc->alq && nandsim_log_output == NANDSIM_OUTPUT_FILE) + return; + + if (level <= nandsim_log_level) { + microtime(&currtime); + hdr_len = sprintf(hdr, "%08jd.%08li [chip:%d, ctrl:%d]: ", + (intmax_t)currtime.tv_sec, currtime.tv_usec, + chip->chip_num, chip->ctrl_num); + + switch(nandsim_log_output) { + case NANDSIM_OUTPUT_CONSOLE: + printf("%s", hdr); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + break; + case NANDSIM_OUTPUT_RAM: + va_start(ap, fmt); + len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap); + tmp[NANDSIM_ENTRY_SIZE - 1] = 0; + va_end(ap); + + rest = log_size - sc->log_idx - 1; + if (rest >= hdr_len) { + bcopy(hdr, &sc->log_buff[sc->log_idx], + hdr_len); + sc->log_idx += hdr_len; + sc->log_buff[sc->log_idx] = 0; + } else { + bcopy(hdr, &sc->log_buff[sc->log_idx], rest); + bcopy(&hdr[rest], sc->log_buff, + hdr_len - rest); + sc->log_idx = hdr_len - rest; + sc->log_buff[sc->log_idx] = 0; + } + + rest = log_size - sc->log_idx - 1; + if (rest >= len) { + bcopy(tmp, &sc->log_buff[sc->log_idx], len); + sc->log_idx += len; + sc->log_buff[sc->log_idx] = 0; + } else { + bcopy(tmp, &sc->log_buff[sc->log_idx], rest); + bcopy(&tmp[rest], sc->log_buff, len - rest); + sc->log_idx = len - rest; + sc->log_buff[sc->log_idx] = 0; + } + + break; + + case NANDSIM_OUTPUT_FILE: + va_start(ap, fmt); + len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap); + tmp[NANDSIM_ENTRY_SIZE - 1] = 0; + va_end(ap); + + rest = NANDSIM_ENTRY_SIZE - str_index; + if (rest >= hdr_len) { + strcat(string, hdr); + str_index += hdr_len; + } else { + strlcat(string, hdr, NANDSIM_ENTRY_SIZE + 1); + alq_write(sc->alq, (void *) string, + ALQ_NOWAIT); + strcpy(string, &hdr[rest]); + str_index = hdr_len - rest; + } + rest = NANDSIM_ENTRY_SIZE - str_index; + if (rest >= len) { + strcat(string, tmp); + str_index += len; + } else { + strlcat(string, tmp, NANDSIM_ENTRY_SIZE + 1); + alq_write(sc->alq, (void *) string, + ALQ_NOWAIT); + strcpy(string, &tmp[rest]); + str_index = len - rest; + } + break; + default: + break; + } + } +} diff --git a/sys/dev/nand/nandsim_log.h b/sys/dev/nand/nandsim_log.h new file mode 100644 index 0000000..8cf1082 --- /dev/null +++ b/sys/dev/nand/nandsim_log.h @@ -0,0 +1,52 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NANDSIM_LOG_H +#define _NANDSIM_LOG_H + +#include + +#define NANDSIM_ENTRY_SIZE 128 +#define NANDSIM_ENTRY_COUNT 1024 +#define NANDSIM_RAM_LOG_SIZE 16384 +#define TIME_STR_SIZE 40 + +#define NANDSIM_LOG_ERR 1 +#define NANDSIM_LOG_SM 5 +#define NANDSIM_LOG_EV 10 +#define NANDSIM_LOG_DATA 15 + +extern int nandsim_log_level; +extern int nandsim_log_output; + +int nandsim_log_init(struct nandsim_softc *, char *); +void nandsim_log_close(struct nandsim_softc *); +void nandsim_log(struct nandsim_chip *, int, const char *, ...); + +#endif /* _NANDSIM_LOG_H */ + diff --git a/sys/dev/nand/nandsim_swap.c b/sys/dev/nand/nandsim_swap.c new file mode 100644 index 0000000..cc4201d --- /dev/null +++ b/sys/dev/nand/nandsim_swap.c @@ -0,0 +1,389 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int init_block_state(struct chip_swap *); +static void destroy_block_state(struct chip_swap *); + +static int create_buffers(struct chip_swap *); +static void destroy_buffers(struct chip_swap *); + +static int swap_file_open(struct chip_swap *, const char *); +static void swap_file_close(struct chip_swap *); +static int swap_file_write(struct chip_swap *, struct block_state *); +static int swap_file_read(struct chip_swap *, struct block_state *); + +#define CHIP_SWAP_CMODE 0600 +#define CHIP_SWAP_BLOCKSPACES 2 + +static int +init_block_state(struct chip_swap *swap) +{ + struct block_state *blk_state; + int i; + + if (swap == NULL) + return (-1); + + blk_state = malloc(swap->nof_blks * sizeof(struct block_state), + M_NANDSIM, M_WAITOK | M_ZERO); + + for (i = 0; i < swap->nof_blks; i++) + blk_state[i].offset = 0xffffffff; + + swap->blk_state = blk_state; + + return (0); +} + +static void +destroy_block_state(struct chip_swap *swap) +{ + + if (swap == NULL) + return; + + if (swap->blk_state != NULL) + free(swap->blk_state, M_NANDSIM); +} + +static int +create_buffers(struct chip_swap *swap) +{ + struct block_space *block_space; + void *block; + int i; + + for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) { + block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK); + block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK); + block_space->blk_ptr = block; + SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link); + nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space, + block); + } + + if (i == 0) + return (-1); + + return (0); +} + +static void +destroy_buffers(struct chip_swap *swap) +{ + struct block_space *blk_space; + + if (swap == NULL) + return; + + blk_space = SLIST_FIRST(&swap->free_bs); + while (blk_space) { + SLIST_REMOVE_HEAD(&swap->free_bs, free_link); + nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n", + blk_space, blk_space->blk_ptr); + free(blk_space->blk_ptr, M_NANDSIM); + free(blk_space, M_NANDSIM); + blk_space = SLIST_FIRST(&swap->free_bs); + } + + blk_space = STAILQ_FIRST(&swap->used_bs); + while (blk_space) { + STAILQ_REMOVE_HEAD(&swap->used_bs, used_link); + nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n", + blk_space, blk_space->blk_ptr); + free(blk_space->blk_ptr, M_NANDSIM); + free(blk_space, M_NANDSIM); + blk_space = STAILQ_FIRST(&swap->used_bs); + } +} + +static int +swap_file_open(struct chip_swap *swap, const char *swap_file) +{ + struct nameidata nd; + int vfslocked, flags, error; + + NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, swap_file, + curthread); + + flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC; + + error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL); + if (error) { + nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file); + NDFREE(&nd, NDF_ONLY_PNBUF); + return (error); + } + + swap->swap_cred = crhold(curthread->td_ucred); + vfslocked = NDHASGIANT(&nd); + NDFREE(&nd, NDF_ONLY_PNBUF); + + /* We just unlock so we hold a reference */ + VOP_UNLOCK(nd.ni_vp, 0); + VFS_UNLOCK_GIANT(vfslocked); + + swap->swap_vp = nd.ni_vp; + + return (0); +} + +static void +swap_file_close(struct chip_swap *swap) +{ + + if (swap == NULL) + return; + + if (swap->swap_vp == NULL) + return; + + vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread); + crfree(swap->swap_cred); +} + +static int +swap_file_write(struct chip_swap *swap, struct block_state *blk_state) +{ + struct block_space *blk_space; + struct thread *td; + struct mount *mp; + struct vnode *vp; + struct uio auio; + struct iovec aiov; + int vfslocked; + + if (swap == NULL || blk_state == NULL) + return (-1); + + blk_space = blk_state->blk_sp; + if (blk_state->offset == -1) { + blk_state->offset = swap->swap_offset; + swap->swap_offset += swap->blk_size; + } + + nand_debug(NDBG_SIM,"saving %p[%p] at %x\n", + blk_space, blk_space->blk_ptr, blk_state->offset); + + bzero(&aiov, sizeof(aiov)); + bzero(&auio, sizeof(auio)); + + aiov.iov_base = blk_space->blk_ptr; + aiov.iov_len = swap->blk_size; + td = curthread; + vp = swap->swap_vp; + + auio.uio_iov = &aiov; + auio.uio_offset = blk_state->offset; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_WRITE; + auio.uio_iovcnt = 1; + auio.uio_resid = swap->blk_size; + auio.uio_td = td; + + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_start_write(vp, &mp, V_WAIT); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred); + VOP_UNLOCK(vp, 0); + vn_finished_write(mp); + VFS_UNLOCK_GIANT(vfslocked); + + return (0); +} + +static int +swap_file_read(struct chip_swap *swap, struct block_state *blk_state) +{ + struct block_space *blk_space; + struct thread *td; + struct vnode *vp; + struct uio auio; + struct iovec aiov; + int vfslocked; + + if (swap == NULL || blk_state == NULL) + return (-1); + + blk_space = blk_state->blk_sp; + + nand_debug(NDBG_SIM,"restore %p[%p] at %x\n", + blk_space, blk_space->blk_ptr, blk_state->offset); + + bzero(&aiov, sizeof(aiov)); + bzero(&auio, sizeof(auio)); + + aiov.iov_base = blk_space->blk_ptr; + aiov.iov_len = swap->blk_size; + td = curthread; + vp = swap->swap_vp; + + auio.uio_iov = &aiov; + auio.uio_offset = blk_state->offset; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_READ; + auio.uio_iovcnt = 1; + auio.uio_resid = swap->blk_size; + auio.uio_td = td; + + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + VOP_READ(vp, &auio, 0, swap->swap_cred); + VOP_UNLOCK(vp, 0); + VFS_UNLOCK_GIANT(vfslocked); + + return (0); +} + +struct chip_swap * +nandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size) +{ + struct chip_swap *swap; + int err = 0; + + if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0)) + return (NULL); + + swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO); + + SLIST_INIT(&swap->free_bs); + STAILQ_INIT(&swap->used_bs); + swap->blk_size = blk_size; + swap->nof_blks = nof_blks; + + err = init_block_state(swap); + if (err) { + nandsim_swap_destroy(swap); + return (NULL); + } + + err = create_buffers(swap); + if (err) { + nandsim_swap_destroy(swap); + return (NULL); + } + + err = swap_file_open(swap, swap_file); + if (err) { + nandsim_swap_destroy(swap); + return (NULL); + } + + return (swap); +} + +void +nandsim_swap_destroy(struct chip_swap *swap) +{ + + if (swap == NULL) + return; + + destroy_block_state(swap); + destroy_buffers(swap); + swap_file_close(swap); + free(swap, M_NANDSIM); +} + +struct block_space * +get_bs(struct chip_swap *swap, uint32_t block, uint8_t writing) +{ + struct block_state *blk_state, *old_blk_state = NULL; + struct block_space *blk_space; + + if (swap == NULL || (block >= swap->nof_blks)) + return (NULL); + + blk_state = &swap->blk_state[block]; + nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status); + + if (blk_state->status & BLOCK_ALLOCATED) { + blk_space = blk_state->blk_sp; + } else { + blk_space = SLIST_FIRST(&swap->free_bs); + if (blk_space) { + SLIST_REMOVE_HEAD(&swap->free_bs, free_link); + STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, + used_link); + } else { + blk_space = STAILQ_FIRST(&swap->used_bs); + old_blk_state = blk_space->blk_state; + STAILQ_REMOVE_HEAD(&swap->used_bs, used_link); + STAILQ_INSERT_TAIL(&swap->used_bs, blk_space, + used_link); + if (old_blk_state->status & BLOCK_DIRTY) { + swap_file_write(swap, old_blk_state); + old_blk_state->status &= ~BLOCK_DIRTY; + old_blk_state->status |= BLOCK_SWAPPED; + } + } + } + + if (blk_space == NULL) + return (NULL); + + if (old_blk_state != NULL) { + old_blk_state->status &= ~BLOCK_ALLOCATED; + old_blk_state->blk_sp = NULL; + } + + blk_state->blk_sp = blk_space; + blk_space->blk_state = blk_state; + + if (!(blk_state->status & BLOCK_ALLOCATED)) { + if (blk_state->status & BLOCK_SWAPPED) + swap_file_read(swap, blk_state); + else + memset(blk_space->blk_ptr, 0xff, swap->blk_size); + blk_state->status |= BLOCK_ALLOCATED; + } + + if (writing) + blk_state->status |= BLOCK_DIRTY; + + nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space, + blk_space->blk_ptr, blk_state->status); + + return (blk_space); +} diff --git a/sys/dev/nand/nandsim_swap.h b/sys/dev/nand/nandsim_swap.h new file mode 100644 index 0000000..4fbae2a --- /dev/null +++ b/sys/dev/nand/nandsim_swap.h @@ -0,0 +1,64 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NANDSIM_SWAP_CHIP_H_ +#define _NANDSIM_SWAP_CHIP_H_ + +struct block_space { + SLIST_ENTRY(block_space) free_link; + STAILQ_ENTRY(block_space) used_link; + struct block_state *blk_state; + uint8_t *blk_ptr; +}; + +#define BLOCK_ALLOCATED 0x1 +#define BLOCK_SWAPPED 0x2 +#define BLOCK_DIRTY 0x4 + +struct block_state { + struct block_space *blk_sp; + uint32_t offset; + uint8_t status; +}; + +struct chip_swap { + struct block_state *blk_state; + SLIST_HEAD(,block_space) free_bs; + STAILQ_HEAD(,block_space) used_bs; + struct ucred *swap_cred; + struct vnode *swap_vp; + uint32_t swap_offset; + uint32_t blk_size; + uint32_t nof_blks; +}; + +struct chip_swap *nandsim_swap_init(const char *, uint32_t, uint32_t); +void nandsim_swap_destroy(struct chip_swap *); +struct block_space *get_bs(struct chip_swap *, uint32_t, uint8_t); + +#endif /* _NANDSIM_SWAP_CHIP_H_ */ diff --git a/sys/dev/nand/nfc_if.m b/sys/dev/nand/nfc_if.m new file mode 100644 index 0000000..a4e1099 --- /dev/null +++ b/sys/dev/nand/nfc_if.m @@ -0,0 +1,165 @@ +#- +# Copyright (C) 2009-2012 Semihalf +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# NAND controller interface description +# + +#include +#include + +INTERFACE nfc; + +CODE { + static int nfc_default_method(device_t dev) + { + return (0); + } + + static int nfc_softecc_get(device_t dev, void *buf, int pagesize, + void *ecc, int *needwrite) + { + *needwrite = 1; + return (nand_softecc_get(dev, buf, pagesize, ecc)); + } + + static int nfc_softecc_correct(device_t dev, void *buf, int pagesize, + void *readecc, void *calcecc) + { + return (nand_softecc_correct(dev, buf, pagesize, readecc, + calcecc)); + } +}; + +# Send command to a NAND chip +# +# Return values: +# 0: Success +# +METHOD int send_command { + device_t dev; + uint8_t command; +}; + +# Send address to a NAND chip +# +# Return values: +# 0: Success +# +METHOD int send_address { + device_t dev; + uint8_t address; +}; + +# Read byte +# +# Return values: +# byte read +# +METHOD uint8_t read_byte { + device_t dev; +}; + +# Write byte +# +METHOD void write_byte { + device_t dev; + uint8_t byte; +}; + +# Read word +# +# Return values: +# word read +# +METHOD uint16_t read_word { + device_t dev; +}; + +# Write word +# +METHOD void write_word { + device_t dev; + uint16_t word; +}; + +# Read buf +# +METHOD void read_buf { + device_t dev; + void *buf; + uint32_t len; +}; + +# Write buf +# +METHOD void write_buf { + device_t dev; + void *buf; + uint32_t len; +}; + +# Select CS +# +METHOD int select_cs { + device_t dev; + uint8_t cs; +}; + +# Read ready/busy signal +# +METHOD int read_rnb { + device_t dev; +}; + +# Start command +# +# Return values: +# 0: Success +# +METHOD int start_command { + device_t dev; +} DEFAULT nfc_default_method; + +# Generate ECC or get it from H/W +# +METHOD int get_ecc { + device_t dev; + void *buf; + int pagesize; + void *ecc; + int *needwrite; +} DEFAULT nfc_softecc_get; + +# Correct ECC +# +METHOD int correct_ecc { + device_t dev; + void *buf; + int pagesize; + void *readecc; + void *calcecc; +} DEFAULT nfc_softecc_correct; diff --git a/sys/dev/nand/nfc_mv.c b/sys/dev/nand/nfc_mv.c new file mode 100644 index 0000000..7f68e82 --- /dev/null +++ b/sys/dev/nand/nfc_mv.c @@ -0,0 +1,236 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Integrated NAND controller driver */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "nfc_if.h" + +#define MV_NAND_DATA (0x00) +#define MV_NAND_COMMAND (0x01) +#define MV_NAND_ADDRESS (0x02) + +struct mv_nand_softc { + struct nand_softc nand_dev; + bus_space_handle_t sc_handle; + bus_space_tag_t sc_tag; + struct resource *res; + int rid; +}; + +static int mv_nand_attach(device_t); +static int mv_nand_probe(device_t); +static int mv_nand_send_command(device_t, uint8_t); +static int mv_nand_send_address(device_t, uint8_t); +static uint8_t mv_nand_read_byte(device_t); +static void mv_nand_read_buf(device_t, void *, uint32_t); +static void mv_nand_write_buf(device_t, void *, uint32_t); +static int mv_nand_select_cs(device_t, uint8_t); +static int mv_nand_read_rnb(device_t); + +static device_method_t mv_nand_methods[] = { + DEVMETHOD(device_probe, mv_nand_probe), + DEVMETHOD(device_attach, mv_nand_attach), + + DEVMETHOD(nfc_send_command, mv_nand_send_command), + DEVMETHOD(nfc_send_address, mv_nand_send_address), + DEVMETHOD(nfc_read_byte, mv_nand_read_byte), + DEVMETHOD(nfc_read_buf, mv_nand_read_buf), + DEVMETHOD(nfc_write_buf, mv_nand_write_buf), + DEVMETHOD(nfc_select_cs, mv_nand_select_cs), + DEVMETHOD(nfc_read_rnb, mv_nand_read_rnb), + + { 0, 0 }, +}; + +static driver_t mv_nand_driver = { + "nand", + mv_nand_methods, + sizeof(struct mv_nand_softc), +}; + +static devclass_t mv_nand_devclass; +DRIVER_MODULE(mv_nand, localbus, mv_nand_driver, mv_nand_devclass, 0, 0); + +static int +mv_nand_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "mrvl,nfc")) + return (ENXIO); + + device_set_desc(dev, "Marvell NAND controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_nand_attach(device_t dev) +{ + struct mv_nand_softc *sc; + int err; + + sc = device_get_softc(dev); + sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, + RF_ACTIVE); + if (sc->res == NULL) { + device_printf(dev, "could not allocate resources!\n"); + return (ENXIO); + } + + sc->sc_tag = rman_get_bustag(sc->res); + sc->sc_handle = rman_get_bushandle(sc->res); + + nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL); + + err = nandbus_create(dev); + + return (err); +} + +static int +mv_nand_send_command(device_t dev, uint8_t command) +{ + struct mv_nand_softc *sc; + + nand_debug(NDBG_DRV,"mv_nand: send command %x", command); + + sc = device_get_softc(dev); + bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_COMMAND, command); + return (0); +} + +static int +mv_nand_send_address(device_t dev, uint8_t addr) +{ + struct mv_nand_softc *sc; + + nand_debug(NDBG_DRV,"mv_nand: send address %x", addr); + + sc = device_get_softc(dev); + bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_ADDRESS, addr); + return (0); +} + +static uint8_t +mv_nand_read_byte(device_t dev) +{ + struct mv_nand_softc *sc; + uint8_t data; + + sc = device_get_softc(dev); + data = bus_space_read_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA); + + nand_debug(NDBG_DRV,"mv_nand: read %x", data); + + return (data); +} + +static void +mv_nand_read_buf(device_t dev, void* buf, uint32_t len) +{ + struct mv_nand_softc *sc; + int i; + uint8_t *b = (uint8_t*)buf; + + sc = device_get_softc(dev); + + for (i = 0; i < len; i++) { + b[i] = bus_space_read_1(sc->sc_tag, sc->sc_handle, + MV_NAND_DATA); +#ifdef NAND_DEBUG + if (!(i % 16)) + printf("%s", i == 0 ? "mv_nand:\n" : "\n"); + printf(" %x", b[i]); + if (i == len - 1) + printf("\n"); +#endif + } +} + +static void +mv_nand_write_buf(device_t dev, void* buf, uint32_t len) +{ + struct mv_nand_softc *sc; + int i; + uint8_t *b = (uint8_t*)buf; + + sc = device_get_softc(dev); + + for (i = 0; i < len; i++) { +#ifdef NAND_DEBUG + if (!(i % 16)) + printf("%s", i == 0 ? "mv_nand:\n" : "\n"); + printf(" %x", b[i]); + if (i == len - 1) + printf("\n"); +#endif + bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA, + b[i]); + } +} + +static int +mv_nand_select_cs(device_t dev, uint8_t cs) +{ + + if (cs > 0) + return (ENODEV); + + return (0); +} + +static int +mv_nand_read_rnb(device_t dev) +{ + + /* no-op */ + return (0); /* ready */ +} diff --git a/sys/fs/nandfs/bmap.c b/sys/fs/nandfs/bmap.c new file mode 100644 index 0000000..84e4a9e --- /dev/null +++ b/sys/fs/nandfs/bmap.c @@ -0,0 +1,621 @@ +/*- + * Copyright (c) 2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "nandfs_mount.h" +#include "nandfs.h" +#include "nandfs_subr.h" +#include "bmap.h" + +static int bmap_getlbns(struct nandfs_node *, nandfs_lbn_t, + struct nandfs_indir *, int *); + +int +bmap_lookup(struct nandfs_node *node, nandfs_lbn_t lblk, nandfs_daddr_t *vblk) +{ + struct nandfs_inode *ip; + struct nandfs_indir a[NIADDR + 1], *ap; + nandfs_daddr_t daddr; + struct buf *bp; + int error; + int num, *nump; + + DPRINTF(BMAP, ("%s: node %p lblk %jx enter\n", __func__, node, lblk)); + ip = &node->nn_inode; + + ap = a; + nump = # + + error = bmap_getlbns(node, lblk, ap, nump); + if (error) + return (error); + + if (num == 0) { + *vblk = ip->i_db[lblk]; + return (0); + } + + DPRINTF(BMAP, ("%s: node %p lblk=%jx trying ip->i_ib[%x]\n", __func__, + node, lblk, ap->in_off)); + daddr = ip->i_ib[ap->in_off]; + for (bp = NULL, ++ap; --num; ap++) { + if (daddr == 0) { + DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with " + "vblk 0\n", __func__, node, lblk)); + *vblk = 0; + return (0); + } + if (ap->in_lbn == lblk) { + DPRINTF(BMAP, ("%s: node %p lblk=%jx ap->in_lbn=%jx " + "returning address of indirect block (%jx)\n", + __func__, node, lblk, ap->in_lbn, daddr)); + *vblk = daddr; + return (0); + } + + DPRINTF(BMAP, ("%s: node %p lblk=%jx reading block " + "ap->in_lbn=%jx\n", __func__, node, lblk, ap->in_lbn)); + + error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off]; + brelse(bp); + } + + DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with %jx\n", __func__, + node, lblk, daddr)); + *vblk = daddr; + + return (0); +} + +int +bmap_dirty_meta(struct nandfs_node *node, nandfs_lbn_t lblk, int force) +{ + struct nandfs_indir a[NIADDR+1], *ap; +#ifdef DEBUG + nandfs_daddr_t daddr; +#endif + struct buf *bp; + int error; + int num, *nump; + + DPRINTF(BMAP, ("%s: node %p lblk=%jx\n", __func__, node, lblk)); + + ap = a; + nump = # + + error = bmap_getlbns(node, lblk, ap, nump); + if (error) + return (error); + + /* + * Direct block, nothing to do + */ + if (num == 0) + return (0); + + DPRINTF(BMAP, ("%s: node %p reading blocks\n", __func__, node)); + + for (bp = NULL, ++ap; --num; ap++) { + error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + +#ifdef DEBUG + daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off]; + MPASS(daddr != 0 || node->nn_ino == 3); +#endif + + error = nandfs_dirty_buf_meta(bp, force); + if (error) + return (error); + } + + return (0); +} + +int +bmap_insert_block(struct nandfs_node *node, nandfs_lbn_t lblk, + nandfs_daddr_t vblk) +{ + struct nandfs_inode *ip; + struct nandfs_indir a[NIADDR+1], *ap; + struct buf *bp; + nandfs_daddr_t daddr; + int error; + int num, *nump, i; + + DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx\n", __func__, node, lblk, + vblk)); + + ip = &node->nn_inode; + + ap = a; + nump = # + + error = bmap_getlbns(node, lblk, ap, nump); + if (error) + return (error); + + DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx got num=%d\n", __func__, + node, lblk, vblk, num)); + + if (num == 0) { + DPRINTF(BMAP, ("%s: node %p lblk=%jx direct block\n", __func__, + node, lblk)); + ip->i_db[lblk] = vblk; + return (0); + } + + DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block level %d\n", + __func__, node, lblk, ap->in_off)); + + if (num == 1) { + DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block: inserting " + "%jx as vblk for indirect block %d\n", __func__, node, + lblk, vblk, ap->in_off)); + ip->i_ib[ap->in_off] = vblk; + return (0); + } + + bp = NULL; + daddr = ip->i_ib[a[0].in_off]; + for (i = 1; i < num; i++) { + if (bp) + brelse(bp); + if (daddr == 0) { + DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx create " + "block %jx %d\n", __func__, node, lblk, vblk, + a[i].in_lbn, a[i].in_off)); + error = nandfs_bcreate_meta(node, a[i].in_lbn, NOCRED, + 0, &bp); + if (error) + return (error); + } else { + DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx read " + "block %jx %d\n", __func__, node, daddr, vblk, + a[i].in_lbn, a[i].in_off)); + error = nandfs_bread_meta(node, a[i].in_lbn, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + } + daddr = ((nandfs_daddr_t *)bp->b_data)[a[i].in_off]; + } + i--; + + DPRINTF(BMAP, + ("%s: bmap node %p lblk=%jx vblk=%jx inserting vblk level %d at " + "offset %d at %jx\n", __func__, node, lblk, vblk, i, a[i].in_off, + daddr)); + + if (!bp) { + nandfs_error("%s: cannot find indirect block\n", __func__); + return (-1); + } + ((nandfs_daddr_t *)bp->b_data)[a[i].in_off] = vblk; + + error = nandfs_dirty_buf_meta(bp, 0); + if (error) { + nandfs_warning("%s: dirty failed buf: %p\n", __func__, bp); + return (error); + } + DPRINTF(BMAP, ("%s: exiting node %p lblk=%jx vblk=%jx\n", __func__, + node, lblk, vblk)); + + return (error); +} + +CTASSERT(NIADDR <= 3); +#define SINGLE 0 /* index of single indirect block */ +#define DOUBLE 1 /* index of double indirect block */ +#define TRIPLE 2 /* index of triple indirect block */ + +static __inline nandfs_lbn_t +lbn_offset(struct nandfs_device *fsdev, int level) +{ + nandfs_lbn_t res; + + for (res = 1; level > 0; level--) + res *= MNINDIR(fsdev); + return (res); +} + +static nandfs_lbn_t +blocks_inside(struct nandfs_device *fsdev, int level, struct nandfs_indir *nip) +{ + nandfs_lbn_t blocks; + + for (blocks = 1; level >= SINGLE; level--, nip++) { + MPASS(nip->in_off >= 0 && nip->in_off < MNINDIR(fsdev)); + blocks += nip->in_off * lbn_offset(fsdev, level); + } + + return (blocks); +} + +static int +bmap_truncate_indirect(struct nandfs_node *node, int level, nandfs_lbn_t *left, + int *cleaned, struct nandfs_indir *ap, struct nandfs_indir *fp, + nandfs_daddr_t *copy) +{ + struct buf *bp; + nandfs_lbn_t i, lbn, nlbn, factor, tosub; + struct nandfs_device *fsdev; + int error, lcleaned, modified; + + DPRINTF(BMAP, ("%s: node %p level %d left %jx\n", __func__, + node, level, *left)); + + fsdev = node->nn_nandfsdev; + + MPASS(ap->in_off >= 0 && ap->in_off < MNINDIR(fsdev)); + + factor = lbn_offset(fsdev, level); + lbn = ap->in_lbn; + + error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + bcopy(bp->b_data, copy, fsdev->nd_blocksize); + bqrelse(bp); + + modified = 0; + + i = ap->in_off; + + if (ap != fp) + ap++; + for (nlbn = lbn + 1 - i * factor; i >= 0 && *left > 0; i--, + nlbn += factor) { + lcleaned = 0; + + DPRINTF(BMAP, + ("%s: node %p i=%jx nlbn=%jx left=%jx ap=%p vblk %jx\n", + __func__, node, i, nlbn, *left, ap, copy[i])); + + if (copy[i] == 0) { + tosub = blocks_inside(fsdev, level - 1, ap); + if (tosub > *left) + tosub = 0; + + *left -= tosub; + } else { + if (level > SINGLE) { + if (ap == fp) + ap->in_lbn = nlbn; + + error = bmap_truncate_indirect(node, level - 1, + left, &lcleaned, ap, fp, + copy + MNINDIR(fsdev)); + if (error) + return (error); + } else { + error = nandfs_bdestroy(node, copy[i]); + if (error) + return (error); + lcleaned = 1; + *left -= 1; + } + } + + if (lcleaned) { + if (level > SINGLE) { + error = nandfs_vblock_end(fsdev, copy[i]); + if (error) + return (error); + } + copy[i] = 0; + modified++; + } + + ap = fp; + } + + if (i == -1) + *cleaned = 1; + + error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + if (modified) + bcopy(copy, bp->b_data, fsdev->nd_blocksize); + + error = nandfs_dirty_buf_meta(bp, 0); + if (error) + return (error); + + return (error); +} + +int +bmap_truncate_mapping(struct nandfs_node *node, nandfs_lbn_t lastblk, + nandfs_lbn_t todo) +{ + struct nandfs_inode *ip; + struct nandfs_indir a[NIADDR + 1], f[NIADDR], *ap; + nandfs_daddr_t indir_lbn[NIADDR]; + nandfs_daddr_t *copy; + int error, level; + nandfs_lbn_t left, tosub; + struct nandfs_device *fsdev; + int cleaned, i; + int num, *nump; + + DPRINTF(BMAP, ("%s: node %p lastblk %jx truncating by %jx\n", __func__, + node, lastblk, todo)); + + ip = &node->nn_inode; + fsdev = node->nn_nandfsdev; + + ap = a; + nump = # + + error = bmap_getlbns(node, lastblk, ap, nump); + if (error) + return (error); + + indir_lbn[SINGLE] = -NDADDR; + indir_lbn[DOUBLE] = indir_lbn[SINGLE] - MNINDIR(fsdev) - 1; + indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - MNINDIR(fsdev) + * MNINDIR(fsdev) - 1; + + for (i = 0; i < NIADDR; i++) { + f[i].in_off = MNINDIR(fsdev) - 1; + f[i].in_lbn = 0xdeadbeef; + } + + left = todo; + +#ifdef DEBUG + a[num].in_off = -1; +#endif + + ap++; + num -= 2; + + if (num < 0) + goto direct; + + copy = malloc(MNINDIR(fsdev) * sizeof(nandfs_daddr_t) * (num + 1), + M_NANDFSTEMP, M_WAITOK); + + for (level = num; level >= SINGLE && left > 0; level--) { + cleaned = 0; + + if (ip->i_ib[level] == 0) { + tosub = blocks_inside(fsdev, level, ap); + if (tosub > left) + left = 0; + else + left -= tosub; + } else { + if (ap == f) + ap->in_lbn = indir_lbn[level]; + error = bmap_truncate_indirect(node, level, &left, + &cleaned, ap, f, copy); + if (error) { + nandfs_error("%s: error %d when truncate " + "at level %d\n", __func__, error, level); + return (error); + } + } + + if (cleaned) { + nandfs_vblock_end(fsdev, ip->i_ib[level]); + ip->i_ib[level] = 0; + } + + ap = f; + } + + free(copy, M_NANDFSTEMP); + +direct: + if (num < 0) + i = lastblk; + else + i = NDADDR - 1; + + for (; i >= 0 && left > 0; i--) { + if (ip->i_db[i] != 0) { + error = nandfs_bdestroy(node, ip->i_db[i]); + if (error) { + nandfs_error("%s: cannot destroy " + "block %jx, error %d\n", __func__, + (uintmax_t)ip->i_db[i], error); + return (error); + } + ip->i_db[i] = 0; + } + + left--; + } + + KASSERT(left == 0, + ("truncated wrong number of blocks (%jd should be 0)", left)); + + return (error); +} + +nandfs_lbn_t +get_maxfilesize(struct nandfs_device *fsdev) +{ + struct nandfs_indir f[NIADDR]; + nandfs_lbn_t max; + int i; + + max = NDADDR; + + for (i = 0; i < NIADDR; i++) { + f[i].in_off = MNINDIR(fsdev) - 1; + max += blocks_inside(fsdev, i, f); + } + + max *= fsdev->nd_blocksize; + + return (max); +} + +/* + * This is ufs_getlbns with minor modifications. + */ +/* + * Create an array of logical block number/offset pairs which represent the + * path of indirect blocks required to access a data block. The first "pair" + * contains the logical block number of the appropriate single, double or + * triple indirect block and the offset into the inode indirect block array. + * Note, the logical block number of the inode single/double/triple indirect + * block appears twice in the array, once with the offset into the i_ib and + * once with the offset into the page itself. + */ +static int +bmap_getlbns(struct nandfs_node *node, nandfs_lbn_t bn, struct nandfs_indir *ap, int *nump) +{ + nandfs_daddr_t blockcnt; + nandfs_lbn_t metalbn, realbn; + struct nandfs_device *fsdev; + int i, numlevels, off; + + fsdev = node->nn_nandfsdev; + + DPRINTF(BMAP, ("%s: node %p bn=%jx mnindir=%zd enter\n", __func__, + node, bn, MNINDIR(fsdev))); + + *nump = 0; + numlevels = 0; + realbn = bn; + + if (bn < 0) + bn = -bn; + + /* The first NDADDR blocks are direct blocks. */ + if (bn < NDADDR) + return (0); + + /* + * Determine the number of levels of indirection. After this loop + * is done, blockcnt indicates the number of data blocks possible + * at the previous level of indirection, and NIADDR - i is the number + * of levels of indirection needed to locate the requested block. + */ + for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) { + DPRINTF(BMAP, ("%s: blockcnt=%jd i=%d bn=%jd\n", __func__, + blockcnt, i, bn)); + if (i == 0) + return (EFBIG); + blockcnt *= MNINDIR(fsdev); + if (bn < blockcnt) + break; + } + + /* Calculate the address of the first meta-block. */ + if (realbn >= 0) + metalbn = -(realbn - bn + NIADDR - i); + else + metalbn = -(-realbn - bn + NIADDR - i); + + /* + * At each iteration, off is the offset into the bap array which is + * an array of disk addresses at the current level of indirection. + * The logical block number and the offset in that block are stored + * into the argument array. + */ + ap->in_lbn = metalbn; + ap->in_off = off = NIADDR - i; + + DPRINTF(BMAP, ("%s: initial: ap->in_lbn=%jx ap->in_off=%d\n", __func__, + metalbn, off)); + + ap++; + for (++numlevels; i <= NIADDR; i++) { + /* If searching for a meta-data block, quit when found. */ + if (metalbn == realbn) + break; + + blockcnt /= MNINDIR(fsdev); + off = (bn / blockcnt) % MNINDIR(fsdev); + + ++numlevels; + ap->in_lbn = metalbn; + ap->in_off = off; + + DPRINTF(BMAP, ("%s: in_lbn=%jx in_off=%d\n", __func__, + ap->in_lbn, ap->in_off)); + ++ap; + + metalbn -= -1 + off * blockcnt; + } + if (nump) + *nump = numlevels; + + DPRINTF(BMAP, ("%s: numlevels=%d\n", __func__, numlevels)); + + return (0); +} diff --git a/sys/fs/nandfs/bmap.h b/sys/fs/nandfs/bmap.h new file mode 100644 index 0000000..c27c61c --- /dev/null +++ b/sys/fs/nandfs/bmap.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _BMAP_H +#define _BMAP_H + +#include "nandfs_fs.h" + +int bmap_lookup(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t *); +int bmap_insert_block(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t); +int bmap_truncate_mapping(struct nandfs_node *, nandfs_lbn_t, nandfs_lbn_t); +int bmap_dirty_meta(struct nandfs_node *, nandfs_lbn_t, int); + +nandfs_lbn_t get_maxfilesize(struct nandfs_device *); + +#endif /* _BMAP_H */ diff --git a/sys/fs/nandfs/nandfs.h b/sys/fs/nandfs/nandfs.h new file mode 100644 index 0000000..beb4e16 --- /dev/null +++ b/sys/fs/nandfs/nandfs.h @@ -0,0 +1,310 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs.h,v 1.1 2009/07/18 16:31:42 reinoud + * + * $FreeBSD$ + */ + +#ifndef _FS_NANDFS_NANDFS_H_ +#define _FS_NANDFS_NANDFS_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "nandfs_fs.h" + +MALLOC_DECLARE(M_NANDFSTEMP); + +/* Debug categories */ +#define NANDFS_DEBUG_VOLUMES 0x000001 +#define NANDFS_DEBUG_BLOCK 0x000004 +#define NANDFS_DEBUG_LOCKING 0x000008 +#define NANDFS_DEBUG_NODE 0x000010 +#define NANDFS_DEBUG_LOOKUP 0x000020 +#define NANDFS_DEBUG_READDIR 0x000040 +#define NANDFS_DEBUG_TRANSLATE 0x000080 +#define NANDFS_DEBUG_STRATEGY 0x000100 +#define NANDFS_DEBUG_READ 0x000200 +#define NANDFS_DEBUG_WRITE 0x000400 +#define NANDFS_DEBUG_IFILE 0x000800 +#define NANDFS_DEBUG_ATTR 0x001000 +#define NANDFS_DEBUG_EXTATTR 0x002000 +#define NANDFS_DEBUG_ALLOC 0x004000 +#define NANDFS_DEBUG_CPFILE 0x008000 +#define NANDFS_DEBUG_DIRHASH 0x010000 +#define NANDFS_DEBUG_NOTIMPL 0x020000 +#define NANDFS_DEBUG_SHEDULE 0x040000 +#define NANDFS_DEBUG_SEG 0x080000 +#define NANDFS_DEBUG_SYNC 0x100000 +#define NANDFS_DEBUG_PARANOIA 0x200000 +#define NANDFS_DEBUG_VNCALL 0x400000 +#define NANDFS_DEBUG_BUF 0x1000000 +#define NANDFS_DEBUG_BMAP 0x2000000 +#define NANDFS_DEBUG_DAT 0x4000000 +#define NANDFS_DEBUG_GENERIC 0x8000000 +#define NANDFS_DEBUG_CLEAN 0x10000000 + +extern int nandfs_verbose; + +#define DPRINTF(name, arg) { \ + if (nandfs_verbose & NANDFS_DEBUG_##name) {\ + printf arg;\ + };\ + } +#define DPRINTFIF(name, cond, arg) { \ + if (nandfs_verbose & NANDFS_DEBUG_##name) { \ + if (cond) printf arg;\ + };\ + } + +#define VFSTONANDFS(mp) ((struct nandfsmount *)((mp)->mnt_data)) +#define VTON(vp) ((struct nandfs_node *)(vp)->v_data) +#define NTOV(xp) ((xp)->nn_vnode) + +int nandfs_init(struct vfsconf *); +int nandfs_uninit(struct vfsconf *); + +extern struct vop_vector nandfs_vnodeops; +extern struct vop_vector nandfs_system_vnodeops; + +struct nandfs_node; + +/* Structure and derivatives */ +struct nandfs_mdt { + uint32_t entries_per_block; + uint32_t entries_per_group; + uint32_t blocks_per_group; + uint32_t groups_per_desc_block; /* desc is super group */ + uint32_t blocks_per_desc_block; /* desc is super group */ +}; + +struct nandfs_segment { + LIST_ENTRY(nandfs_segment) seg_link; + + struct nandfs_device *fsdev; + + TAILQ_HEAD(, buf) segsum; + TAILQ_HEAD(, buf) data; + + uint64_t seg_num; + uint64_t seg_next; + uint64_t start_block; + uint32_t num_blocks; + + uint32_t nblocks; + uint32_t nbinfos; + uint32_t segsum_blocks; + uint32_t segsum_bytes; + uint32_t bytes_left; + char *current_off; +}; + +struct nandfs_seginfo { + LIST_HEAD( ,nandfs_segment) seg_list; + struct nandfs_segment *curseg; + struct nandfs_device *fsdev; + uint32_t blocks; + uint8_t reiterate; +}; + +#define NANDFS_FSSTOR_FAILED 1 +struct nandfs_fsarea { + int offset; + int flags; + int last_used; +}; + +extern int nandfs_cleaner_enable; +extern int nandfs_cleaner_interval; +extern int nandfs_cleaner_segments; + +struct nandfs_device { + struct vnode *nd_devvp; + struct g_consumer *nd_gconsumer; + + struct thread *nd_syncer; + struct thread *nd_cleaner; + int nd_syncer_exit; + int nd_cleaner_exit; + + int nd_is_nand; + + struct nandfs_fsarea nd_fsarea[NANDFS_NFSAREAS]; + int nd_last_fsarea; + + STAILQ_HEAD(nandfs_mnts, nandfsmount) nd_mounts; + SLIST_ENTRY(nandfs_device) nd_next_device; + + /* FS structures */ + struct nandfs_fsdata nd_fsdata; + struct nandfs_super_block nd_super; + struct nandfs_segment_summary nd_last_segsum; + struct nandfs_super_root nd_super_root; + struct nandfs_node *nd_dat_node; + struct nandfs_node *nd_cp_node; + struct nandfs_node *nd_su_node; + struct nandfs_node *nd_gc_node; + + struct nandfs_mdt nd_dat_mdt; + struct nandfs_mdt nd_ifile_mdt; + + struct timespec nd_ts; + + /* Synchronization */ + struct mtx nd_mutex; + struct mtx nd_sync_mtx; + struct cv nd_sync_cv; + struct mtx nd_clean_mtx; + struct cv nd_clean_cv; + struct lock nd_seg_const; + + struct nandfs_seginfo *nd_seginfo; + + /* FS geometry */ + uint64_t nd_devsize; + uint64_t nd_maxfilesize; + uint32_t nd_blocksize; + uint32_t nd_erasesize; + + uint32_t nd_devblocksize; + + /* Segment usage */ + uint64_t nd_clean_segs; + uint64_t *nd_free_base; + uint64_t nd_free_count; + uint64_t nd_dirty_bufs; + + /* Running values */ + uint64_t nd_seg_sequence; + uint64_t nd_seg_num; + uint64_t nd_next_seg_num; + uint64_t nd_last_pseg; + uint64_t nd_last_cno; + uint64_t nd_last_ino; + uint64_t nd_fakevblk; + + int nd_mount_state; + int nd_refcnt; + int nd_syncing; + int nd_cleaning; +}; + +extern SLIST_HEAD(_nandfs_devices, nandfs_device) nandfs_devices; + +#define NANDFS_FORCE_SYNCER 0x1 +#define NANDFS_UMOUNT 0x2 + +#define SYNCER_UMOUNT 0x0 +#define SYNCER_VFS_SYNC 0x1 +#define SYNCER_BDFLUSH 0x2 +#define SYNCER_FFORCE 0x3 +#define SYNCER_FSYNC 0x4 +#define SYNCER_ROUPD 0x5 + +static __inline int +nandfs_writelockflags(struct nandfs_device *fsdev, int flags) +{ + int error = 0; + + if (lockstatus(&fsdev->nd_seg_const) != LK_EXCLUSIVE) + error = lockmgr(&fsdev->nd_seg_const, flags | LK_SHARED, NULL); + + return (error); +} + +static __inline void +nandfs_writeunlock(struct nandfs_device *fsdev) +{ + + if (lockstatus(&fsdev->nd_seg_const) != LK_EXCLUSIVE) + lockmgr(&(fsdev)->nd_seg_const, LK_RELEASE, NULL); +} + +#define NANDFS_WRITELOCKFLAGS(fsdev, flags) nandfs_writelockflags(fsdev, flags) + +#define NANDFS_WRITELOCK(fsdev) NANDFS_WRITELOCKFLAGS(fsdev, 0) + +#define NANDFS_WRITEUNLOCK(fsdev) nandfs_writeunlock(fsdev) + +#define NANDFS_WRITEASSERT(fsdev) lockmgr_assert(&(fsdev)->nd_seg_const, KA_LOCKED) + +/* Specific mountpoint; head or a checkpoint/snapshot */ +struct nandfsmount { + STAILQ_ENTRY(nandfsmount) nm_next_mount; + + struct mount *nm_vfs_mountp; + struct nandfs_device *nm_nandfsdev; + struct nandfs_args nm_mount_args; + struct nandfs_node *nm_ifile_node; + + uint8_t nm_flags; + int8_t nm_ronly; +}; + +struct nandfs_node { + struct vnode *nn_vnode; + struct nandfsmount *nn_nmp; + struct nandfs_device *nn_nandfsdev; + struct lockf *nn_lockf; + + uint64_t nn_ino; + struct nandfs_inode nn_inode; + + uint64_t nn_diroff; + uint32_t nn_flags; +}; + +#define IN_ACCESS 0x0001 /* Inode access time update request */ +#define IN_CHANGE 0x0002 /* Inode change time update request */ +#define IN_UPDATE 0x0004 /* Inode was written to; update mtime*/ +#define IN_MODIFIED 0x0008 /* node has been modified */ +#define IN_RENAME 0x0010 /* node is being renamed. */ + +/* File permissions. */ +#define IEXEC 0000100 /* Executable. */ +#define IWRITE 0000200 /* Writeable. */ +#define IREAD 0000400 /* Readable. */ +#define ISVTX 0001000 /* Sticky bit. */ +#define ISGID 0002000 /* Set-gid. */ +#define ISUID 0004000 /* Set-uid. */ + +#define PRINT_NODE_FLAGS \ + "\10\1IN_ACCESS\2IN_CHANGE\3IN_UPDATE\4IN_MODIFIED\5IN_RENAME" + +#define NANDFS_GATHER(x) ((x)->b_flags |= B_00800000) +#define NANDFS_UNGATHER(x) ((x)->b_flags &= ~B_00800000) +#define NANDFS_ISGATHERED(x) ((x)->b_flags & B_00800000) + +#endif /* !_FS_NANDFS_NANDFS_H_ */ diff --git a/sys/fs/nandfs/nandfs_alloc.c b/sys/fs/nandfs/nandfs_alloc.c new file mode 100644 index 0000000..3417266 --- /dev/null +++ b/sys/fs/nandfs/nandfs_alloc.c @@ -0,0 +1,364 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +static void +nandfs_get_desc_block_nr(struct nandfs_mdt *mdt, uint64_t desc, + uint64_t *desc_block) +{ + + *desc_block = desc * mdt->blocks_per_desc_block; +} + +static void +nandfs_get_group_block_nr(struct nandfs_mdt *mdt, uint64_t group, + uint64_t *group_block) +{ + uint64_t desc, group_off; + + desc = group / mdt->groups_per_desc_block; + group_off = group % mdt->groups_per_desc_block; + *group_block = desc * mdt->blocks_per_desc_block + + 1 + group_off * mdt->blocks_per_group; +} + +static void +init_desc_block(struct nandfs_mdt *mdt, uint8_t *block_data) +{ + struct nandfs_block_group_desc *desc; + uint32_t i; + + desc = (struct nandfs_block_group_desc *) block_data; + for (i = 0; i < mdt->groups_per_desc_block; i++) + desc[i].bg_nfrees = mdt->entries_per_group; +} + +int +nandfs_find_free_entry(struct nandfs_mdt *mdt, struct nandfs_node *node, + struct nandfs_alloc_request *req) +{ + nandfs_daddr_t desc, group, maxgroup, maxdesc, pos = 0; + nandfs_daddr_t start_group, start_desc; + nandfs_daddr_t desc_block, group_block; + nandfs_daddr_t file_blocks; + struct nandfs_block_group_desc *descriptors; + struct buf *bp, *bp2; + uint32_t *mask, i, mcount, msize; + int error; + + file_blocks = node->nn_inode.i_blocks; + maxgroup = 0x100000000ull / mdt->entries_per_group; + maxdesc = maxgroup / mdt->groups_per_desc_block; + start_group = req->entrynum / mdt->entries_per_group; + start_desc = start_group / mdt->groups_per_desc_block; + + bp = bp2 = NULL; +restart: + for (desc = start_desc; desc < maxdesc; desc++) { + nandfs_get_desc_block_nr(mdt, desc, &desc_block); + + if (bp) + brelse(bp); + if (desc_block < file_blocks) { + error = nandfs_bread(node, desc_block, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + } else { + error = nandfs_bcreate(node, desc_block, NOCRED, 0, + &bp); + if (error) + return (error); + file_blocks++; + init_desc_block(mdt, bp->b_data); + } + + descriptors = (struct nandfs_block_group_desc *) bp->b_data; + for (group = start_group; group < mdt->groups_per_desc_block; + group++) { + if (descriptors[group].bg_nfrees > 0) { + nandfs_get_group_block_nr(mdt, group, + &group_block); + + if (bp2) + brelse(bp2); + if (group_block < file_blocks) { + error = nandfs_bread(node, group_block, + NOCRED, 0, &bp2); + if (error) { + brelse(bp); + return (error); + } + } else { + error = nandfs_bcreate(node, + group_block, NOCRED, 0, &bp2); + if (error) + return (error); + file_blocks++; + } + mask = (uint32_t *)bp2->b_data; + msize = (sizeof(uint32_t) * __CHAR_BIT); + mcount = mdt->entries_per_group / msize; + for (i = 0; i < mcount; i++) { + if (mask[i] == UINT32_MAX) + continue; + + pos = ffs(~mask[i]) - 1; + pos += (msize * i); + pos += (group * mdt->entries_per_group); + pos += desc * group * + mdt->groups_per_desc_block * + mdt->entries_per_group; + goto found; + } + } + } + start_group = 0; + } + + if (start_desc != 0) { + maxdesc = start_desc; + start_desc = 0; + req->entrynum = 0; + goto restart; + } + + return (ENOENT); + +found: + req->entrynum = pos; + req->bp_desc = bp; + req->bp_bitmap = bp2; + DPRINTF(ALLOC, ("%s: desc: %p bitmap: %p entry: %#jx\n", + __func__, req->bp_desc, req->bp_bitmap, (uintmax_t)pos)); + + return (0); +} + +int +nandfs_find_entry(struct nandfs_mdt* mdt, struct nandfs_node *nnode, + struct nandfs_alloc_request *req) +{ + uint64_t dblock, bblock, eblock; + uint32_t offset; + int error; + + nandfs_mdt_trans_blk(mdt, req->entrynum, &dblock, &bblock, &eblock, + &offset); + + error = nandfs_bread(nnode, dblock, NOCRED, 0, &req->bp_desc); + if (error) { + brelse(req->bp_desc); + return (error); + } + + error = nandfs_bread(nnode, bblock, NOCRED, 0, &req->bp_bitmap); + if (error) { + brelse(req->bp_desc); + brelse(req->bp_bitmap); + return (error); + } + + error = nandfs_bread(nnode, eblock, NOCRED, 0, &req->bp_entry); + if (error) { + brelse(req->bp_desc); + brelse(req->bp_bitmap); + brelse(req->bp_entry); + return (error); + } + + DPRINTF(ALLOC, + ("%s: desc_buf: %p bitmap_buf %p entry_buf %p offset %x\n", + __func__, req->bp_desc, req->bp_bitmap, req->bp_entry, offset)); + + return (0); +} + +static __inline void +nandfs_calc_idx_entry(struct nandfs_mdt* mdt, uint32_t entrynum, + uint64_t *group, uint64_t *bitmap_idx, uint64_t *bitmap_off) +{ + + /* Find group_desc index */ + entrynum = entrynum % + (mdt->entries_per_group * mdt->groups_per_desc_block); + *group = entrynum / mdt->entries_per_group; + /* Find bitmap index and bit offset */ + entrynum = entrynum % mdt->entries_per_group; + *bitmap_idx = entrynum / (sizeof(uint32_t) * __CHAR_BIT); + *bitmap_off = entrynum % (sizeof(uint32_t) * __CHAR_BIT); +} + +int +nandfs_free_entry(struct nandfs_mdt* mdt, struct nandfs_alloc_request *req) +{ + struct nandfs_block_group_desc *descriptors; + uint64_t bitmap_idx, bitmap_off; + uint64_t group; + uint32_t *mask, maskrw; + + nandfs_calc_idx_entry(mdt, req->entrynum, &group, &bitmap_idx, + &bitmap_off); + + DPRINTF(ALLOC, ("nandfs_free_entry: req->entrynum=%jx bitmap_idx=%jx" + " bitmap_off=%jx group=%jx\n", (uintmax_t)req->entrynum, + (uintmax_t)bitmap_idx, (uintmax_t)bitmap_off, (uintmax_t)group)); + + /* Update counter of free entries for group */ + descriptors = (struct nandfs_block_group_desc *) req->bp_desc->b_data; + descriptors[group].bg_nfrees++; + + /* Set bit to indicate that entry is taken */ + mask = (uint32_t *)req->bp_bitmap->b_data; + maskrw = mask[bitmap_idx]; + KASSERT(maskrw & (1 << bitmap_off), ("freeing unallocated vblock")); + maskrw &= ~(1 << bitmap_off); + mask[bitmap_idx] = maskrw; + + /* Make descriptor, bitmap and entry buffer dirty */ + if (nandfs_dirty_buf(req->bp_desc, 0) == 0) { + nandfs_dirty_buf(req->bp_bitmap, 1); + nandfs_dirty_buf(req->bp_entry, 1); + } else { + brelse(req->bp_bitmap); + brelse(req->bp_entry); + return (-1); + } + + return (0); +} + +int +nandfs_alloc_entry(struct nandfs_mdt* mdt, struct nandfs_alloc_request *req) +{ + struct nandfs_block_group_desc *descriptors; + uint64_t bitmap_idx, bitmap_off; + uint64_t group; + uint32_t *mask, maskrw; + + nandfs_calc_idx_entry(mdt, req->entrynum, &group, &bitmap_idx, + &bitmap_off); + + DPRINTF(ALLOC, ("nandfs_alloc_entry: req->entrynum=%jx bitmap_idx=%jx" + " bitmap_off=%jx group=%jx\n", (uintmax_t)req->entrynum, + (uintmax_t)bitmap_idx, (uintmax_t)bitmap_off, (uintmax_t)group)); + + /* Update counter of free entries for group */ + descriptors = (struct nandfs_block_group_desc *) req->bp_desc->b_data; + descriptors[group].bg_nfrees--; + + /* Clear bit to indicate that entry is free */ + mask = (uint32_t *)req->bp_bitmap->b_data; + maskrw = mask[bitmap_idx]; + maskrw |= 1 << bitmap_off; + mask[bitmap_idx] = maskrw; + + /* Make descriptor, bitmap and entry buffer dirty */ + if (nandfs_dirty_buf(req->bp_desc, 0) == 0) { + nandfs_dirty_buf(req->bp_bitmap, 1); + nandfs_dirty_buf(req->bp_entry, 1); + } else { + brelse(req->bp_bitmap); + brelse(req->bp_entry); + return (-1); + } + + return (0); +} + +void +nandfs_abort_entry(struct nandfs_alloc_request *req) +{ + + brelse(req->bp_desc); + brelse(req->bp_bitmap); + brelse(req->bp_entry); +} + +int +nandfs_get_entry_block(struct nandfs_mdt *mdt, struct nandfs_node *node, + struct nandfs_alloc_request *req, uint32_t *entry, int create) +{ + struct buf *bp; + nandfs_lbn_t blocknr; + int error; + + /* Find buffer number for given entry */ + nandfs_mdt_trans(mdt, req->entrynum, &blocknr, entry); + DPRINTF(ALLOC, ("%s: ino %#jx entrynum:%#jx block:%#jx entry:%x\n", + __func__, (uintmax_t)node->nn_ino, (uintmax_t)req->entrynum, + (uintmax_t)blocknr, *entry)); + + /* Read entry block or create if 'create' parameter is not zero */ + bp = NULL; + + if (blocknr < node->nn_inode.i_blocks) + error = nandfs_bread(node, blocknr, NOCRED, 0, &bp); + else if (create) + error = nandfs_bcreate(node, blocknr, NOCRED, 0, &bp); + else + error = E2BIG; + + if (error) { + DPRINTF(ALLOC, ("%s: ino %#jx block %#jx entry %x error %d\n", + __func__, (uintmax_t)node->nn_ino, (uintmax_t)blocknr, + *entry, error)); + if (bp) + brelse(bp); + return (error); + } + + MPASS(nandfs_vblk_get(bp) != 0 || node->nn_ino == NANDFS_DAT_INO); + + req->bp_entry = bp; + return (0); +} diff --git a/sys/fs/nandfs/nandfs_bmap.c b/sys/fs/nandfs/nandfs_bmap.c new file mode 100644 index 0000000..9f800b8 --- /dev/null +++ b/sys/fs/nandfs/nandfs_bmap.c @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "nandfs_mount.h" +#include "nandfs.h" +#include "nandfs_subr.h" +#include "bmap.h" + +nandfs_lbn_t +nandfs_get_maxfilesize(struct nandfs_device *fsdev) +{ + + return (get_maxfilesize(fsdev)); +} + +int +nandfs_bmap_lookup(struct nandfs_node *node, nandfs_lbn_t lblk, + nandfs_daddr_t *vblk) +{ + int error = 0; + + if (node->nn_ino == NANDFS_GC_INO && lblk >= 0) + *vblk = lblk; + else + error = bmap_lookup(node, lblk, vblk); + + DPRINTF(TRANSLATE, ("%s: error %d ino %#jx lblocknr %#jx -> %#jx\n", + __func__, error, (uintmax_t)node->nn_ino, (uintmax_t)lblk, + (uintmax_t)*vblk)); + + if (error) + nandfs_error("%s: returned %d", __func__, error); + + return (error); +} + +int +nandfs_bmap_insert_block(struct nandfs_node *node, nandfs_lbn_t lblk, + struct buf *bp) +{ + struct nandfs_device *fsdev; + nandfs_daddr_t vblk; + int error; + + fsdev = node->nn_nandfsdev; + + vblk = 0; + if (node->nn_ino != NANDFS_DAT_INO) { + error = nandfs_vblock_alloc(fsdev, &vblk); + if (error) + return (error); + } + + nandfs_buf_set(bp, NANDFS_VBLK_ASSIGNED); + nandfs_vblk_set(bp, vblk); + + error = bmap_insert_block(node, lblk, vblk); + if (error) { + nandfs_vblock_free(fsdev, vblk); + return (error); + } + + return (0); +} + +int +nandfs_bmap_dirty_blocks(struct nandfs_node *node, struct buf *bp, int force) +{ + int error; + + error = bmap_dirty_meta(node, bp->b_lblkno, force); + if (error) + nandfs_error("%s: cannot dirty buffer %p\n", + __func__, bp); + + return (error); +} + +static int +nandfs_bmap_update_mapping(struct nandfs_node *node, nandfs_lbn_t lblk, + nandfs_daddr_t blknr) +{ + int error; + + DPRINTF(BMAP, + ("%s: node: %p ino: %#jx lblk: %#jx vblk: %#jx\n", + __func__, node, (uintmax_t)node->nn_ino, (uintmax_t)lblk, + (uintmax_t)blknr)); + + error = bmap_insert_block(node, lblk, blknr); + + return (error); +} + +int +nandfs_bmap_update_block(struct nandfs_node *node, struct buf *bp, + nandfs_lbn_t blknr) +{ + nandfs_lbn_t lblk; + int error; + + lblk = bp->b_lblkno; + nandfs_vblk_set(bp, blknr); + + DPRINTF(BMAP, ("%s: node: %p ino: %#jx bp: %p lblk: %#jx blk: %#jx\n", + __func__, node, (uintmax_t)node->nn_ino, bp, + (uintmax_t)lblk, (uintmax_t)blknr)); + + error = nandfs_bmap_update_mapping(node, lblk, blknr); + if (error) { + nandfs_error("%s: cannot update lblk:%jx to blk:%jx for " + "node:%p, error:%d\n", __func__, (uintmax_t)lblk, + (uintmax_t)blknr, node, error); + return (error); + } + + return (error); +} + +int +nandfs_bmap_update_dat(struct nandfs_node *node, nandfs_daddr_t oldblk, + struct buf *bp) +{ + struct nandfs_device *fsdev; + nandfs_daddr_t vblk = 0; + int error; + + if (node->nn_ino == NANDFS_DAT_INO) + return (0); + + if (nandfs_buf_check(bp, NANDFS_VBLK_ASSIGNED)) { + nandfs_buf_clear(bp, NANDFS_VBLK_ASSIGNED); + return (0); + } + + fsdev = node->nn_nandfsdev; + + /* First alloc new virtual block.... */ + error = nandfs_vblock_alloc(fsdev, &vblk); + if (error) + return (error); + + error = nandfs_bmap_update_block(node, bp, vblk); + if (error) + return (error); + + /* Then we can end up with old one */ + nandfs_vblock_end(fsdev, oldblk); + + DPRINTF(BMAP, + ("%s: ino %#jx block %#jx: update vblk %#jx to %#jx\n", + __func__, (uintmax_t)node->nn_ino, (uintmax_t)bp->b_lblkno, + (uintmax_t)oldblk, (uintmax_t)vblk)); + return (error); +} + +int +nandfs_bmap_truncate_mapping(struct nandfs_node *node, nandfs_lbn_t oblk, + nandfs_lbn_t nblk) +{ + nandfs_lbn_t todo; + int error; + + todo = oblk - nblk; + + DPRINTF(BMAP, ("%s: node %p oblk %jx nblk %jx truncate by %jx\n", + __func__, node, oblk, nblk, todo)); + + error = bmap_truncate_mapping(node, oblk, todo); + if (error) + return (error); + + return (error); +} diff --git a/sys/fs/nandfs/nandfs_buffer.c b/sys/fs/nandfs/nandfs_buffer.c new file mode 100644 index 0000000..b0d72668 --- /dev/null +++ b/sys/fs/nandfs/nandfs_buffer.c @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct buf * +nandfs_geteblk(int size, int flags) +{ + struct buf *bp; + + /* + * XXX + * Right now we can call geteblk with GB_NOWAIT_BD flag, which means + * it can return NULL. But we cannot afford to get NULL, hence this panic. + */ + bp = geteblk(size, flags); + if (bp == NULL) + panic("geteblk returned NULL"); + + return (bp); +} + +void +nandfs_dirty_bufs_increment(struct nandfs_device *fsdev) +{ + + mtx_lock(&fsdev->nd_mutex); + KASSERT(fsdev->nd_dirty_bufs >= 0, ("negative nd_dirty_bufs")); + fsdev->nd_dirty_bufs++; + mtx_unlock(&fsdev->nd_mutex); +} + +void +nandfs_dirty_bufs_decrement(struct nandfs_device *fsdev) +{ + + mtx_lock(&fsdev->nd_mutex); + KASSERT(fsdev->nd_dirty_bufs > 0, + ("decrementing not-positive nd_dirty_bufs")); + fsdev->nd_dirty_bufs--; + mtx_unlock(&fsdev->nd_mutex); +} diff --git a/sys/fs/nandfs/nandfs_cleaner.c b/sys/fs/nandfs/nandfs_cleaner.c new file mode 100644 index 0000000..9257c10 --- /dev/null +++ b/sys/fs/nandfs/nandfs_cleaner.c @@ -0,0 +1,621 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define NANDFS_CLEANER_KILL 1 + +static void nandfs_cleaner(struct nandfs_device *); +static int nandfs_cleaner_clean_segments(struct nandfs_device *, + struct nandfs_vinfo *, uint32_t, struct nandfs_period *, uint32_t, + struct nandfs_bdesc *, uint32_t, uint64_t *, uint32_t); + +static int +nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd, + uint64_t nmembs); + +static void +nandfs_wakeup_wait_cleaner(struct nandfs_device *fsdev, int reason) +{ + + mtx_lock(&fsdev->nd_clean_mtx); + if (reason == NANDFS_CLEANER_KILL) + fsdev->nd_cleaner_exit = 1; + if (fsdev->nd_cleaning == 0) { + fsdev->nd_cleaning = 1; + wakeup(&fsdev->nd_cleaning); + } + cv_wait(&fsdev->nd_clean_cv, &fsdev->nd_clean_mtx); + mtx_unlock(&fsdev->nd_clean_mtx); +} + +int +nandfs_start_cleaner(struct nandfs_device *fsdev) +{ + int error; + + MPASS(fsdev->nd_cleaner == NULL); + + fsdev->nd_cleaner_exit = 0; + + error = kthread_add((void(*)(void *))nandfs_cleaner, fsdev, NULL, + &fsdev->nd_cleaner, 0, 0, "nandfs_cleaner"); + if (error) + printf("nandfs: could not start cleaner: %d\n", error); + + return (error); +} + +int +nandfs_stop_cleaner(struct nandfs_device *fsdev) +{ + + MPASS(fsdev->nd_cleaner != NULL); + nandfs_wakeup_wait_cleaner(fsdev, NANDFS_CLEANER_KILL); + fsdev->nd_cleaner = NULL; + + DPRINTF(CLEAN, ("cleaner stopped\n")); + return (0); +} + +static int +nandfs_cleaner_finished(struct nandfs_device *fsdev) +{ + int exit; + + mtx_lock(&fsdev->nd_clean_mtx); + fsdev->nd_cleaning = 0; + if (!fsdev->nd_cleaner_exit) { + DPRINTF(CLEAN, ("%s: sleep\n", __func__)); + msleep(&fsdev->nd_cleaning, &fsdev->nd_clean_mtx, PRIBIO, "-", + hz * nandfs_cleaner_interval); + } + exit = fsdev->nd_cleaner_exit; + cv_broadcast(&fsdev->nd_clean_cv); + mtx_unlock(&fsdev->nd_clean_mtx); + if (exit) { + DPRINTF(CLEAN, ("%s: no longer active\n", __func__)); + return (1); + } + + return (0); +} + +static void +print_suinfo(struct nandfs_suinfo *suinfo, int nsegs) +{ + int i; + + for (i = 0; i < nsegs; i++) { + DPRINTF(CLEAN, ("%jx %jd %c%c%c %10u\n", + suinfo[i].nsi_num, suinfo[i].nsi_lastmod, + (suinfo[i].nsi_flags & + (NANDFS_SEGMENT_USAGE_ACTIVE) ? 'a' : '-'), + (suinfo[i].nsi_flags & + (NANDFS_SEGMENT_USAGE_DIRTY) ? 'd' : '-'), + (suinfo[i].nsi_flags & + (NANDFS_SEGMENT_USAGE_ERROR) ? 'e' : '-'), + suinfo[i].nsi_blocks)); + } +} + +static int +nandfs_cleaner_vblock_is_alive(struct nandfs_device *fsdev, + struct nandfs_vinfo *vinfo, struct nandfs_cpinfo *cp, uint32_t ncps) +{ + int64_t idx, min, max; + + if (vinfo->nvi_end >= fsdev->nd_last_cno) + return (1); + + if (ncps == 0) + return (0); + + if (vinfo->nvi_end < cp[0].nci_cno || + vinfo->nvi_start > cp[ncps - 1].nci_cno) + return (0); + + idx = min = 0; + max = ncps - 1; + while (min <= max) { + idx = (min + max) / 2; + if (vinfo->nvi_start == cp[idx].nci_cno) + return (1); + if (vinfo->nvi_start < cp[idx].nci_cno) + max = idx - 1; + else + min = idx + 1; + } + + return (vinfo->nvi_end >= cp[idx].nci_cno); +} + +static void +nandfs_cleaner_vinfo_mark_alive(struct nandfs_device *fsdev, + struct nandfs_vinfo *vinfo, uint32_t nmembs, struct nandfs_cpinfo *cp, + uint32_t ncps) +{ + uint32_t i; + + for (i = 0; i < nmembs; i++) + vinfo[i].nvi_alive = + nandfs_cleaner_vblock_is_alive(fsdev, &vinfo[i], cp, ncps); +} + +static int +nandfs_cleaner_bdesc_is_alive(struct nandfs_device *fsdev, + struct nandfs_bdesc *bdesc) +{ + int alive; + + alive = bdesc->bd_oblocknr == bdesc->bd_blocknr; + if (!alive) + MPASS(abs(bdesc->bd_oblocknr - bdesc->bd_blocknr) > 2); + + return (alive); +} + +static void +nandfs_cleaner_bdesc_mark_alive(struct nandfs_device *fsdev, + struct nandfs_bdesc *bdesc, uint32_t nmembs) +{ + uint32_t i; + + for (i = 0; i < nmembs; i++) + bdesc[i].bd_alive = nandfs_cleaner_bdesc_is_alive(fsdev, + &bdesc[i]); +} + +static void +nandfs_cleaner_iterate_psegment(struct nandfs_device *fsdev, + struct nandfs_segment_summary *segsum, union nandfs_binfo *binfo, + nandfs_daddr_t blk, struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp) +{ + int i; + + DPRINTF(CLEAN, ("%s nbinfos %x\n", __func__, segsum->ss_nbinfos)); + for (i = 0; i < segsum->ss_nbinfos; i++) { + if (binfo[i].bi_v.bi_ino == NANDFS_DAT_INO) { + (*bdpp)->bd_oblocknr = blk + segsum->ss_nblocks - + segsum->ss_nbinfos + i; + /* + * XXX Hack + */ + if (segsum->ss_flags & NANDFS_SS_SR) + (*bdpp)->bd_oblocknr--; + (*bdpp)->bd_level = binfo[i].bi_dat.bi_level; + (*bdpp)->bd_offset = binfo[i].bi_dat.bi_blkoff; + (*bdpp)++; + } else { + (*vipp)->nvi_ino = binfo[i].bi_v.bi_ino; + (*vipp)->nvi_vblocknr = binfo[i].bi_v.bi_vblocknr; + (*vipp)++; + } + } +} + +static int +nandfs_cleaner_iterate_segment(struct nandfs_device *fsdev, uint64_t segno, + struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp, int *select) +{ + struct nandfs_segment_summary *segsum; + union nandfs_binfo *binfo; + struct buf *bp; + uint32_t nblocks; + nandfs_daddr_t curr, start, end; + int error = 0; + + nandfs_get_segment_range(fsdev, segno, &start, &end); + + DPRINTF(CLEAN, ("%s: segno %jx start %jx end %jx\n", __func__, segno, + start, end)); + + *select = 0; + + for (curr = start; curr < end; curr += nblocks) { + error = nandfs_dev_bread(fsdev, curr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + nandfs_error("%s: couldn't load segment summary of %jx: %d\n", + __func__, segno, error); + return (error); + } + + segsum = (struct nandfs_segment_summary *)bp->b_data; + binfo = (union nandfs_binfo *)(bp->b_data + segsum->ss_bytes); + + if (!nandfs_segsum_valid(segsum)) { + brelse(bp); + nandfs_error("nandfs: invalid summary of segment %jx\n", segno); + return (error); + } + + DPRINTF(CLEAN, ("%s: %jx magic %x bytes %x nblocks %x nbinfos " + "%x\n", __func__, segno, segsum->ss_magic, segsum->ss_bytes, + segsum->ss_nblocks, segsum->ss_nbinfos)); + + nandfs_cleaner_iterate_psegment(fsdev, segsum, binfo, curr, + vipp, bdpp); + nblocks = segsum->ss_nblocks; + brelse(bp); + } + + if (error == 0) + *select = 1; + + return (error); +} + +static int +nandfs_cleaner_choose_segment(struct nandfs_device *fsdev, uint64_t **segpp, + uint64_t nsegs, uint64_t *rseg) +{ + struct nandfs_suinfo *suinfo; + uint64_t i, ssegs; + int error; + + suinfo = malloc(sizeof(*suinfo) * nsegs, M_NANDFSTEMP, + M_ZERO | M_WAITOK); + + if (*rseg >= fsdev->nd_fsdata.f_nsegments) + *rseg = 0; + +retry: + error = nandfs_get_segment_info_filter(fsdev, suinfo, nsegs, *rseg, + &ssegs, NANDFS_SEGMENT_USAGE_DIRTY, + NANDFS_SEGMENT_USAGE_ACTIVE | NANDFS_SEGMENT_USAGE_ERROR | + NANDFS_SEGMENT_USAGE_GC); + if (error) { + nandfs_error("%s:%d", __FILE__, __LINE__); + goto out; + } + + if (ssegs == 0 && *rseg != 0) { + *rseg = 0; + goto retry; + } + + print_suinfo(suinfo, ssegs); + + for (i = 0; i < ssegs; i++) { + (**segpp) = suinfo[i].nsi_num; + (*segpp)++; + } + + *rseg = suinfo[i - 1].nsi_num + 1; +out: + free(suinfo, M_NANDFSTEMP); + + return (error); +} + +static int +nandfs_cleaner_body(struct nandfs_device *fsdev, uint64_t *rseg) +{ + struct nandfs_vinfo *vinfo, *vip, *vipi; + struct nandfs_bdesc *bdesc, *bdp, *bdpi; + struct nandfs_cpstat cpstat; + struct nandfs_cpinfo *cpinfo = NULL; + uint64_t *segnums, *segp; + int select, selected; + int error = 0; + int nsegs; + int i; + + nsegs = nandfs_cleaner_segments; + + vip = vinfo = malloc(sizeof(*vinfo) * + fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP, + M_ZERO | M_WAITOK); + bdp = bdesc = malloc(sizeof(*bdesc) * + fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP, + M_ZERO | M_WAITOK); + segp = segnums = malloc(sizeof(*segnums) * nsegs, M_NANDFSTEMP, + M_WAITOK); + + error = nandfs_cleaner_choose_segment(fsdev, &segp, nsegs, rseg); + if (error) { + nandfs_error("%s:%d", __FILE__, __LINE__); + goto out; + } + + if (segnums == segp) + goto out; + + selected = 0; + for (i = 0; i < segp - segnums; i++) { + error = nandfs_cleaner_iterate_segment(fsdev, segnums[i], &vip, + &bdp, &select); + if (error) { + /* + * XXX deselect (see below)? + */ + goto out; + } + if (!select) + segnums[i] = NANDFS_NOSEGMENT; + else { + error = nandfs_markgc_segment(fsdev, segnums[i]); + if (error) { + nandfs_error("%s:%d\n", __FILE__, __LINE__); + goto out; + } + selected++; + } + } + + if (selected == 0) { + MPASS(vinfo == vip); + MPASS(bdesc == bdp); + goto out; + } + + error = nandfs_get_cpstat(fsdev->nd_cp_node, &cpstat); + if (error) { + nandfs_error("%s:%d\n", __FILE__, __LINE__); + goto out; + } + + if (cpstat.ncp_nss != 0) { + cpinfo = malloc(sizeof(struct nandfs_cpinfo) * cpstat.ncp_nss, + M_NANDFSTEMP, M_WAITOK); + error = nandfs_get_cpinfo(fsdev->nd_cp_node, 1, NANDFS_SNAPSHOT, + cpinfo, cpstat.ncp_nss, NULL); + if (error) { + nandfs_error("%s:%d\n", __FILE__, __LINE__); + goto out_locked; + } + } + + NANDFS_WRITELOCK(fsdev); + DPRINTF(CLEAN, ("%s: got lock\n", __func__)); + + error = nandfs_get_dat_vinfo(fsdev, vinfo, vip - vinfo); + if (error) { + nandfs_error("%s:%d\n", __FILE__, __LINE__); + goto out_locked; + } + + nandfs_cleaner_vinfo_mark_alive(fsdev, vinfo, vip - vinfo, cpinfo, + cpstat.ncp_nss); + + error = nandfs_get_dat_bdescs(fsdev, bdesc, bdp - bdesc); + if (error) { + nandfs_error("%s:%d\n", __FILE__, __LINE__); + goto out_locked; + } + + nandfs_cleaner_bdesc_mark_alive(fsdev, bdesc, bdp - bdesc); + + DPRINTF(CLEAN, ("got:\n")); + for (vipi = vinfo; vipi < vip; vipi++) { + DPRINTF(CLEAN, ("v ino %jx vblocknr %jx start %jx end %jx " + "alive %d\n", vipi->nvi_ino, vipi->nvi_vblocknr, + vipi->nvi_start, vipi->nvi_end, vipi->nvi_alive)); + } + for (bdpi = bdesc; bdpi < bdp; bdpi++) { + DPRINTF(CLEAN, ("b oblocknr %jx blocknr %jx offset %jx " + "alive %d\n", bdpi->bd_oblocknr, bdpi->bd_blocknr, + bdpi->bd_offset, bdpi->bd_alive)); + } + DPRINTF(CLEAN, ("end list\n")); + + error = nandfs_cleaner_clean_segments(fsdev, vinfo, vip - vinfo, NULL, + 0, bdesc, bdp - bdesc, segnums, segp - segnums); + if (error) + nandfs_error("%s:%d\n", __FILE__, __LINE__); + +out_locked: + NANDFS_WRITEUNLOCK(fsdev); +out: + free(cpinfo, M_NANDFSTEMP); + free(segnums, M_NANDFSTEMP); + free(bdesc, M_NANDFSTEMP); + free(vinfo, M_NANDFSTEMP); + + return (error); +} + +static void +nandfs_cleaner(struct nandfs_device *fsdev) +{ + uint64_t checked_seg = 0; + int error; + + while (!nandfs_cleaner_finished(fsdev)) { + if (!nandfs_cleaner_enable || rebooting) + continue; + + DPRINTF(CLEAN, ("%s: run started\n", __func__)); + + fsdev->nd_cleaning = 1; + + error = nandfs_cleaner_body(fsdev, &checked_seg); + + DPRINTF(CLEAN, ("%s: run finished error %d\n", __func__, + error)); + } + + DPRINTF(CLEAN, ("%s: exiting\n", __func__)); + kthread_exit(); +} + +static int +nandfs_cleaner_clean_segments(struct nandfs_device *nffsdev, + struct nandfs_vinfo *vinfo, uint32_t nvinfo, + struct nandfs_period *pd, uint32_t npd, + struct nandfs_bdesc *bdesc, uint32_t nbdesc, + uint64_t *segments, uint32_t nsegs) +{ + struct nandfs_node *gc; + struct buf *bp; + uint32_t i; + int error = 0; + + gc = nffsdev->nd_gc_node; + + DPRINTF(CLEAN, ("%s: enter\n", __func__)); + + VOP_LOCK(NTOV(gc), LK_EXCLUSIVE); + for (i = 0; i < nvinfo; i++) { + if (!vinfo[i].nvi_alive) + continue; + DPRINTF(CLEAN, ("%s: read vblknr:%#jx blk:%#jx\n", + __func__, (uintmax_t)vinfo[i].nvi_vblocknr, + (uintmax_t)vinfo[i].nvi_blocknr)); + error = nandfs_bread(nffsdev->nd_gc_node, vinfo[i].nvi_blocknr, + NULL, 0, &bp); + if (error) { + nandfs_error("%s:%d", __FILE__, __LINE__); + VOP_UNLOCK(NTOV(gc), 0); + goto out; + } + nandfs_vblk_set(bp, vinfo[i].nvi_vblocknr); + nandfs_buf_set(bp, NANDFS_VBLK_ASSIGNED); + nandfs_dirty_buf(bp, 1); + } + VOP_UNLOCK(NTOV(gc), 0); + + /* Delete checkpoints */ + for (i = 0; i < npd; i++) { + DPRINTF(CLEAN, ("delete checkpoint: %jx\n", + (uintmax_t)pd[i].p_start)); + error = nandfs_delete_cp(nffsdev->nd_cp_node, pd[i].p_start, + pd[i].p_end); + if (error) { + nandfs_error("%s:%d", __FILE__, __LINE__); + goto out; + } + } + + /* Update vblocks */ + for (i = 0; i < nvinfo; i++) { + if (vinfo[i].nvi_alive) + continue; + DPRINTF(CLEAN, ("freeing vblknr: %jx\n", vinfo[i].nvi_vblocknr)); + error = nandfs_vblock_free(nffsdev, vinfo[i].nvi_vblocknr); + if (error) { + nandfs_error("%s:%d", __FILE__, __LINE__); + goto out; + } + } + + error = nandfs_process_bdesc(nffsdev, bdesc, nbdesc); + if (error) { + nandfs_error("%s:%d", __FILE__, __LINE__); + goto out; + } + + /* Add segments to clean */ + if (nffsdev->nd_free_count) { + nffsdev->nd_free_base = realloc(nffsdev->nd_free_base, + (nffsdev->nd_free_count + nsegs) * sizeof(uint64_t), + M_NANDFSTEMP, M_WAITOK | M_ZERO); + memcpy(&nffsdev->nd_free_base[nffsdev->nd_free_count], segments, + nsegs * sizeof(uint64_t)); + nffsdev->nd_free_count += nsegs; + } else { + nffsdev->nd_free_base = malloc(nsegs * sizeof(uint64_t), + M_NANDFSTEMP, M_WAITOK|M_ZERO); + memcpy(nffsdev->nd_free_base, segments, + nsegs * sizeof(uint64_t)); + nffsdev->nd_free_count = nsegs; + } + +out: + + DPRINTF(CLEAN, ("%s: exit error %d\n", __func__, error)); + + return (error); +} + +static int +nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd, + uint64_t nmembs) +{ + struct nandfs_node *dat_node; + struct buf *bp; + uint64_t i; + int error; + + dat_node = nffsdev->nd_dat_node; + + VOP_LOCK(NTOV(dat_node), LK_EXCLUSIVE); + + for (i = 0; i < nmembs; i++) { + if (!bd[i].bd_alive) + continue; + DPRINTF(CLEAN, ("%s: idx %jx offset %jx\n", + __func__, i, bd[i].bd_offset)); + if (bd[i].bd_level) { + error = nandfs_bread_meta(dat_node, bd[i].bd_offset, + NULL, 0, &bp); + if (error) { + nandfs_error("%s: cannot read dat node " + "level:%d\n", __func__, bd[i].bd_level); + brelse(bp); + VOP_UNLOCK(NTOV(dat_node), 0); + return (error); + } + nandfs_dirty_buf_meta(bp, 1); + nandfs_bmap_dirty_blocks(VTON(bp->b_vp), bp, 1); + } else { + error = nandfs_bread(dat_node, bd[i].bd_offset, NULL, + 0, &bp); + if (error) { + nandfs_error("%s: cannot read dat node\n", + __func__); + brelse(bp); + VOP_UNLOCK(NTOV(dat_node), 0); + return (error); + } + nandfs_dirty_buf(bp, 1); + } + DPRINTF(CLEAN, ("%s: bp: %p\n", __func__, bp)); + } + + VOP_UNLOCK(NTOV(dat_node), 0); + + return (0); +} diff --git a/sys/fs/nandfs/nandfs_cpfile.c b/sys/fs/nandfs/nandfs_cpfile.c new file mode 100644 index 0000000..8814fc0 --- /dev/null +++ b/sys/fs/nandfs/nandfs_cpfile.c @@ -0,0 +1,776 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "nandfs_mount.h" +#include "nandfs.h" +#include "nandfs_subr.h" + + +static int +nandfs_checkpoint_size(struct nandfs_device *fsdev) +{ + + return (fsdev->nd_fsdata.f_checkpoint_size); +} + +static int +nandfs_checkpoint_blk_offset(struct nandfs_device *fsdev, uint64_t cn, + uint64_t *blk, uint64_t *offset) +{ + uint64_t off; + uint16_t cp_size, cp_per_blk; + + KASSERT((cn), ("checkpoing cannot be zero")); + + cp_size = fsdev->nd_fsdata.f_checkpoint_size; + cp_per_blk = fsdev->nd_blocksize / cp_size; + off = roundup(sizeof(struct nandfs_cpfile_header), cp_size) / cp_size; + off += (cn - 1); + + *blk = off / cp_per_blk; + *offset = (off % cp_per_blk) * cp_size; + + return (0); +} + +static int +nandfs_checkpoint_blk_remaining(struct nandfs_device *fsdev, uint64_t cn, + uint64_t blk, uint64_t offset) +{ + uint16_t cp_size, cp_remaining; + + cp_size = fsdev->nd_fsdata.f_checkpoint_size; + cp_remaining = (fsdev->nd_blocksize - offset) / cp_size; + + return (cp_remaining); +} + +int +nandfs_get_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node, + uint64_t cn) +{ + struct buf *bp; + uint64_t blk, offset; + int error; + + if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) { + return (-1); + } + + error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (-1); + } + + error = nandfs_dirty_buf(bp, 0); + if (error) + return (-1); + + + nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset); + + if (blk != 0) { + if (blk < cp_node->nn_inode.i_blocks) + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + else + error = nandfs_bcreate(cp_node, blk, NOCRED, 0, &bp); + if (error) { + if (bp) + brelse(bp); + return (-1); + } + + nandfs_dirty_buf(bp, 1); + } + + DPRINTF(CPFILE, ("%s: cn:%#jx entry block:%#jx offset:%#jx\n", + __func__, (uintmax_t)cn, (uintmax_t)blk, (uintmax_t)offset)); + + return (0); +} + +int +nandfs_set_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node, + uint64_t cn, struct nandfs_inode *ifile_inode, uint64_t nblocks) +{ + struct nandfs_cpfile_header *cnh; + struct nandfs_checkpoint *cnp; + struct buf *bp; + uint64_t blk, offset; + int error; + + if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) { + nandfs_error("%s: trying to set invalid chekpoint %jx - %jx\n", + __func__, cn, fsdev->nd_last_cno); + return (-1); + } + + error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return error; + } + + cnh = (struct nandfs_cpfile_header *) bp->b_data; + cnh->ch_ncheckpoints++; + + nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset); + + if(blk != 0) { + brelse(bp); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return error; + } + } + + cnp = (struct nandfs_checkpoint *)((uint8_t *)bp->b_data + offset); + cnp->cp_flags = 0; + cnp->cp_checkpoints_count = 1; + memset(&cnp->cp_snapshot_list, 0, sizeof(struct nandfs_snapshot_list)); + cnp->cp_cno = cn; + cnp->cp_create = fsdev->nd_ts.tv_sec; + cnp->cp_nblk_inc = nblocks; + cnp->cp_blocks_count = 0; + memcpy (&cnp->cp_ifile_inode, ifile_inode, sizeof(cnp->cp_ifile_inode)); + + DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx nblk:%#jx\n", + __func__, (uintmax_t)cn, (uintmax_t)cnp->cp_create, + (uintmax_t)nblocks)); + + brelse(bp); + return (0); +} + +static int +nandfs_cp_mounted(struct nandfs_device *nandfsdev, uint64_t cno) +{ + struct nandfsmount *nmp; + int mounted = 0; + + mtx_lock(&nandfsdev->nd_mutex); + /* No double-mounting of the same checkpoint */ + STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) { + if (nmp->nm_mount_args.cpno == cno) { + mounted = 1; + break; + } + } + mtx_unlock(&nandfsdev->nd_mutex); + + return (mounted); +} + +static int +nandfs_cp_set_snapshot(struct nandfs_node *cp_node, uint64_t cno) +{ + struct nandfs_device *fsdev; + struct nandfs_cpfile_header *cnh; + struct nandfs_checkpoint *cnp; + struct nandfs_snapshot_list *list; + struct buf *bp; + uint64_t blk, prev_blk, offset; + uint64_t curr, prev; + int error; + + fsdev = cp_node->nn_nandfsdev; + + /* Get snapshot data */ + nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) { + brelse(bp); + return (ENOENT); + } + if ((cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) { + brelse(bp); + return (EINVAL); + } + + brelse(bp); + /* Get list from header */ + error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + cnh = (struct nandfs_cpfile_header *) bp->b_data; + list = &cnh->ch_snapshot_list; + prev = list->ssl_prev; + brelse(bp); + prev_blk = ~(0); + curr = 0; + while (prev > cno) { + curr = prev; + nandfs_checkpoint_blk_offset(fsdev, prev, &prev_blk, &offset); + error = nandfs_bread(cp_node, prev_blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + list = &cnp->cp_snapshot_list; + prev = list->ssl_prev; + brelse(bp); + } + + if (curr == 0) { + nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + cnh = (struct nandfs_cpfile_header *) bp->b_data; + list = &cnh->ch_snapshot_list; + } else { + nandfs_checkpoint_blk_offset(fsdev, curr, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + list = &cnp->cp_snapshot_list; + } + + list->ssl_prev = cno; + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + + + /* Update snapshot for cno */ + nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + list = &cnp->cp_snapshot_list; + list->ssl_prev = prev; + list->ssl_next = curr; + cnp->cp_flags |= NANDFS_CHECKPOINT_SNAPSHOT; + nandfs_dirty_buf(bp, 1); + + if (prev == 0) { + nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + cnh = (struct nandfs_cpfile_header *) bp->b_data; + list = &cnh->ch_snapshot_list; + } else { + /* Update snapshot list for prev */ + nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + list = &cnp->cp_snapshot_list; + } + list->ssl_next = cno; + nandfs_dirty_buf(bp, 1); + + /* Update header */ + error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnh = (struct nandfs_cpfile_header *) bp->b_data; + cnh->ch_nsnapshots++; + nandfs_dirty_buf(bp, 1); + + return (0); +} + +static int +nandfs_cp_clr_snapshot(struct nandfs_node *cp_node, uint64_t cno) +{ + struct nandfs_device *fsdev; + struct nandfs_cpfile_header *cnh; + struct nandfs_checkpoint *cnp; + struct nandfs_snapshot_list *list; + struct buf *bp; + uint64_t blk, offset, snapshot_cnt; + uint64_t next, prev; + int error; + + fsdev = cp_node->nn_nandfsdev; + + /* Get snapshot data */ + nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) { + brelse(bp); + return (ENOENT); + } + if (!(cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) { + brelse(bp); + return (EINVAL); + } + + list = &cnp->cp_snapshot_list; + next = list->ssl_next; + prev = list->ssl_prev; + brelse(bp); + + /* Get previous snapshot */ + if (prev != 0) { + nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + list = &cnp->cp_snapshot_list; + } else { + nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + cnh = (struct nandfs_cpfile_header *) bp->b_data; + list = &cnh->ch_snapshot_list; + } + + list->ssl_next = next; + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + + /* Get next snapshot */ + if (next != 0) { + nandfs_checkpoint_blk_offset(fsdev, next, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + list = &cnp->cp_snapshot_list; + } else { + nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + cnh = (struct nandfs_cpfile_header *) bp->b_data; + list = &cnh->ch_snapshot_list; + } + list->ssl_prev = prev; + nandfs_dirty_buf(bp, 1); + + /* Update snapshot list for cno */ + nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); + error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + list = &cnp->cp_snapshot_list; + list->ssl_prev = 0; + list->ssl_next = 0; + cnp->cp_flags &= !NANDFS_CHECKPOINT_SNAPSHOT; + nandfs_dirty_buf(bp, 1); + + /* Update header */ + error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnh = (struct nandfs_cpfile_header *) bp->b_data; + snapshot_cnt = cnh->ch_nsnapshots; + snapshot_cnt--; + cnh->ch_nsnapshots = snapshot_cnt; + nandfs_dirty_buf(bp, 1); + + return (0); +} + +int +nandfs_chng_cpmode(struct nandfs_node *node, struct nandfs_cpmode *ncpm) +{ + struct nandfs_device *fsdev; + uint64_t cno = ncpm->ncpm_cno; + int mode = ncpm->ncpm_mode; + int ret; + + fsdev = node->nn_nandfsdev; + VOP_LOCK(NTOV(node), LK_EXCLUSIVE); + switch (mode) { + case NANDFS_CHECKPOINT: + if (nandfs_cp_mounted(fsdev, cno)) { + ret = EBUSY; + } else + ret = nandfs_cp_clr_snapshot(node, cno); + break; + case NANDFS_SNAPSHOT: + ret = nandfs_cp_set_snapshot(node, cno); + break; + default: + ret = EINVAL; + break; + } + VOP_UNLOCK(NTOV(node), 0); + + return (ret); +} + +static void +nandfs_cpinfo_fill(struct nandfs_checkpoint *cnp, struct nandfs_cpinfo *nci) +{ + + nci->nci_flags = cnp->cp_flags; + nci->nci_pad = 0; + nci->nci_cno = cnp->cp_cno; + nci->nci_create = cnp->cp_create; + nci->nci_nblk_inc = cnp->cp_nblk_inc; + nci->nci_blocks_count = cnp->cp_blocks_count; + nci->nci_next = cnp->cp_snapshot_list.ssl_next; + DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx\n", + __func__, (uintmax_t)cnp->cp_cno, + (uintmax_t)cnp->cp_create)); +} + +static int +nandfs_get_cpinfo_cp(struct nandfs_node *node, uint64_t cno, + struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs) +{ + struct nandfs_device *fsdev; + struct buf *bp; + uint64_t blk, offset, last_cno, i; + uint16_t remaining; + int error; +#ifdef INVARIANTS + uint64_t testblk, testoffset; +#endif + + if (cno == 0) { + return (ENOENT); + } + + if (mnmembs < 1) { + return (EINVAL); + } + + fsdev = node->nn_nandfsdev; + last_cno = fsdev->nd_last_cno; + DPRINTF(CPFILE, ("%s: cno:%#jx mnmembs: %#jx last:%#jx\n", __func__, + (uintmax_t)cno, (uintmax_t)mnmembs, + (uintmax_t)fsdev->nd_last_cno)); + + /* + * do { + * get block + * read checkpoints until we hit last checkpoint, end of block or + * requested number + * } while (last read checkpoint <= last checkpoint on fs && + * read checkpoints < request number); + */ + *nmembs = i = 0; + do { + nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); + remaining = nandfs_checkpoint_blk_remaining(fsdev, cno, + blk, offset); + error = nandfs_bread(node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + while (cno <= last_cno && i < mnmembs && remaining) { +#ifdef INVARIANTS + nandfs_checkpoint_blk_offset(fsdev, cno, &testblk, + &testoffset); + KASSERT(testblk == blk, ("testblk != blk")); + KASSERT(testoffset == offset, ("testoffset != offset")); +#endif + DPRINTF(CPFILE, ("%s: cno %#jx\n", __func__, + (uintmax_t)cno)); + + nandfs_cpinfo_fill((struct nandfs_checkpoint *) + (bp->b_data + offset), nci); + offset += nandfs_checkpoint_size(fsdev); + i++; + nci++; + cno++; + (*nmembs)++; + remaining--; + } + brelse(bp); + } while (cno <= last_cno && i < mnmembs); + + return (0); +} + +static int +nandfs_get_cpinfo_sp(struct nandfs_node *node, uint64_t cno, + struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs) +{ + struct nandfs_checkpoint *cnp; + struct nandfs_cpfile_header *cnh; + struct nandfs_device *fsdev; + struct buf *bp = NULL; + uint64_t curr = 0; + uint64_t blk, offset, curr_cno; + uint32_t flag; + int i, error; + + if (cno == 0 || cno == ~(0)) + return (ENOENT); + + fsdev = node->nn_nandfsdev; + curr_cno = cno; + + if (nmembs) + *nmembs = 0; + if (curr_cno == 1) { + /* Get list from header */ + error = nandfs_bread(node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + cnh = (struct nandfs_cpfile_header *) bp->b_data; + curr_cno = cnh->ch_snapshot_list.ssl_next; + brelse(bp); + bp = NULL; + + /* No snapshots */ + if (curr_cno == 0) + return (0); + } + + for (i = 0; i < mnmembs; i++, nci++) { + nandfs_checkpoint_blk_offset(fsdev, curr_cno, &blk, &offset); + if (i == 0 || curr != blk) { + if (bp) + brelse(bp); + error = nandfs_bread(node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (ENOENT); + } + curr = blk; + } + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + flag = cnp->cp_flags; + if (!(flag & NANDFS_CHECKPOINT_SNAPSHOT) || + (flag & NANDFS_CHECKPOINT_INVALID)) + break; + + nci->nci_flags = flag; + nci->nci_pad = 0; + nci->nci_cno = cnp->cp_cno; + nci->nci_create = cnp->cp_create; + nci->nci_nblk_inc = cnp->cp_nblk_inc; + nci->nci_blocks_count = cnp->cp_blocks_count; + nci->nci_next = cnp->cp_snapshot_list.ssl_next; + if (nmembs) + (*nmembs)++; + + curr_cno = nci->nci_next; + if (!curr_cno) + break; + } + + brelse(bp); + + return (0); +} + +int +nandfs_get_cpinfo(struct nandfs_node *node, uint64_t cno, uint16_t flags, + struct nandfs_cpinfo *nci, uint32_t nmembs, uint32_t *nnmembs) +{ + int error; + + VOP_LOCK(NTOV(node), LK_EXCLUSIVE); + switch (flags) { + case NANDFS_CHECKPOINT: + error = nandfs_get_cpinfo_cp(node, cno, nci, nmembs, nnmembs); + break; + case NANDFS_SNAPSHOT: + error = nandfs_get_cpinfo_sp(node, cno, nci, nmembs, nnmembs); + break; + default: + error = EINVAL; + break; + } + VOP_UNLOCK(NTOV(node), 0); + + return (error); +} + +int +nandfs_get_cpinfo_ioctl(struct nandfs_node *node, struct nandfs_argv *nargv) +{ + struct nandfs_cpinfo *nci; + uint64_t cno = nargv->nv_index; + void *buf = (void *)((uintptr_t)nargv->nv_base); + uint16_t flags = nargv->nv_flags; + uint32_t nmembs = 0; + int error; + + if (nargv->nv_nmembs > NANDFS_CPINFO_MAX) + return (EINVAL); + + nci = malloc(sizeof(struct nandfs_cpinfo) * nargv->nv_nmembs, + M_NANDFSTEMP, M_WAITOK | M_ZERO); + + error = nandfs_get_cpinfo(node, cno, flags, nci, nargv->nv_nmembs, &nmembs); + + if (error == 0) { + nargv->nv_nmembs = nmembs; + error = copyout(nci, buf, + sizeof(struct nandfs_cpinfo) * nmembs); + } + + free(nci, M_NANDFSTEMP); + return (error); +} + +int +nandfs_delete_cp(struct nandfs_node *node, uint64_t start, uint64_t end) +{ + struct nandfs_checkpoint *cnp; + struct nandfs_device *fsdev; + struct buf *bp; + uint64_t cno = start, blk, offset; + int error; + + DPRINTF(CPFILE, ("%s: delete cno %jx-%jx\n", __func__, start, end)); + VOP_LOCK(NTOV(node), LK_EXCLUSIVE); + fsdev = node->nn_nandfsdev; + for (cno = start; cno <= end; cno++) { + if (!cno) + continue; + + nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset); + error = nandfs_bread(node, blk, NOCRED, 0, &bp); + if (error) { + VOP_UNLOCK(NTOV(node), 0); + brelse(bp); + return (error); + } + + cnp = (struct nandfs_checkpoint *)(bp->b_data + offset); + if (cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) { + brelse(bp); + VOP_UNLOCK(NTOV(node), 0); + return (0); + } + + cnp->cp_flags |= NANDFS_CHECKPOINT_INVALID; + + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + } + VOP_UNLOCK(NTOV(node), 0); + + return (0); +} + +int +nandfs_make_snap(struct nandfs_device *fsdev, uint64_t *cno) +{ + struct nandfs_cpmode cpm; + int error; + + *cno = cpm.ncpm_cno = fsdev->nd_last_cno; + cpm.ncpm_mode = NANDFS_SNAPSHOT; + error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm); + return (error); +} + +int +nandfs_delete_snap(struct nandfs_device *fsdev, uint64_t cno) +{ + struct nandfs_cpmode cpm; + int error; + + cpm.ncpm_cno = cno; + cpm.ncpm_mode = NANDFS_CHECKPOINT; + error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm); + return (error); +} + +int nandfs_get_cpstat(struct nandfs_node *cp_node, struct nandfs_cpstat *ncp) +{ + struct nandfs_device *fsdev; + struct nandfs_cpfile_header *cnh; + struct buf *bp; + int error; + + VOP_LOCK(NTOV(cp_node), LK_EXCLUSIVE); + fsdev = cp_node->nn_nandfsdev; + + /* Get header */ + error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + VOP_UNLOCK(NTOV(cp_node), 0); + return (error); + } + cnh = (struct nandfs_cpfile_header *) bp->b_data; + ncp->ncp_cno = fsdev->nd_last_cno; + ncp->ncp_ncps = cnh->ch_ncheckpoints; + ncp->ncp_nss = cnh->ch_nsnapshots; + DPRINTF(CPFILE, ("%s: cno:%#jx ncps:%#jx nss:%#jx\n", + __func__, ncp->ncp_cno, ncp->ncp_ncps, ncp->ncp_nss)); + brelse(bp); + VOP_UNLOCK(NTOV(cp_node), 0); + + return (0); +} diff --git a/sys/fs/nandfs/nandfs_dat.c b/sys/fs/nandfs/nandfs_dat.c new file mode 100644 index 0000000..799113d --- /dev/null +++ b/sys/fs/nandfs/nandfs_dat.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +int +nandfs_vblock_alloc(struct nandfs_device *nandfsdev, nandfs_daddr_t *vblock) +{ + struct nandfs_node *dat; + struct nandfs_mdt *mdt; + struct nandfs_alloc_request req; + struct nandfs_dat_entry *dat_entry; + uint64_t start; + uint32_t entry; + int locked, error; + + dat = nandfsdev->nd_dat_node; + mdt = &nandfsdev->nd_dat_mdt; + start = nandfsdev->nd_last_cno + 1; + + locked = NANDFS_VOP_ISLOCKED(NTOV(dat)); + if (!locked) + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + req.entrynum = 0; + + /* Alloc vblock number */ + error = nandfs_find_free_entry(mdt, dat, &req); + if (error) { + nandfs_error("%s: cannot find free vblk entry\n", + __func__); + if (!locked) + VOP_UNLOCK(NTOV(dat), 0); + return (error); + } + + /* Read/create buffer */ + error = nandfs_get_entry_block(mdt, dat, &req, &entry, 1); + if (error) { + nandfs_error("%s: cannot get free vblk entry\n", + __func__); + nandfs_abort_entry(&req); + if (!locked) + VOP_UNLOCK(NTOV(dat), 0); + return (error); + } + + /* Fill out vblock data */ + dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data; + dat_entry[entry].de_start = start; + dat_entry[entry].de_end = UINTMAX_MAX; + dat_entry[entry].de_blocknr = 0; + + /* Commit allocation */ + error = nandfs_alloc_entry(mdt, &req); + if (error) { + nandfs_error("%s: cannot get free vblk entry\n", + __func__); + if (!locked) + VOP_UNLOCK(NTOV(dat), 0); + return (error); + } + + /* Return allocated vblock */ + *vblock = req.entrynum; + DPRINTF(DAT, ("%s: allocated vblock %#jx\n", + __func__, (uintmax_t)*vblock)); + + if (!locked) + VOP_UNLOCK(NTOV(dat), 0); + return (error); +} + +int +nandfs_vblock_assign(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock, + nandfs_lbn_t block) +{ + struct nandfs_node *dat; + struct nandfs_mdt *mdt; + struct nandfs_alloc_request req; + struct nandfs_dat_entry *dat_entry; + uint32_t entry; + int locked, error; + + dat = nandfsdev->nd_dat_node; + mdt = &nandfsdev->nd_dat_mdt; + + locked = NANDFS_VOP_ISLOCKED(NTOV(dat)); + if (!locked) + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + req.entrynum = vblock; + + error = nandfs_get_entry_block(mdt, dat, &req, &entry, 0); + if (!error) { + dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data; + dat_entry[entry].de_blocknr = block; + + DPRINTF(DAT, ("%s: assing vblock %jx->%jx\n", + __func__, (uintmax_t)vblock, (uintmax_t)block)); + + /* + * It is mostly called from syncer() so + * we want to force making buf dirty + */ + error = nandfs_dirty_buf(req.bp_entry, 1); + } + + if (!locked) + VOP_UNLOCK(NTOV(dat), 0); + + return (error); +} + +int +nandfs_vblock_end(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock) +{ + struct nandfs_node *dat; + struct nandfs_mdt *mdt; + struct nandfs_alloc_request req; + struct nandfs_dat_entry *dat_entry; + uint64_t end; + uint32_t entry; + int locked, error; + + dat = nandfsdev->nd_dat_node; + mdt = &nandfsdev->nd_dat_mdt; + end = nandfsdev->nd_last_cno; + + locked = NANDFS_VOP_ISLOCKED(NTOV(dat)); + if (!locked) + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + req.entrynum = vblock; + + error = nandfs_get_entry_block(mdt, dat, &req, &entry, 0); + if (!error) { + dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data; + dat_entry[entry].de_end = end; + DPRINTF(DAT, ("%s: end vblock %#jx at checkpoint %#jx\n", + __func__, (uintmax_t)vblock, (uintmax_t)end)); + + /* + * It is mostly called from syncer() so + * we want to force making buf dirty + */ + error = nandfs_dirty_buf(req.bp_entry, 1); + } + + if (!locked) + VOP_UNLOCK(NTOV(dat), 0); + + return (error); +} + +int +nandfs_vblock_free(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock) +{ + struct nandfs_node *dat; + struct nandfs_mdt *mdt; + struct nandfs_alloc_request req; + int error; + + dat = nandfsdev->nd_dat_node; + mdt = &nandfsdev->nd_dat_mdt; + + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + req.entrynum = vblock; + + error = nandfs_find_entry(mdt, dat, &req); + if (!error) { + DPRINTF(DAT, ("%s: vblk %#jx\n", __func__, (uintmax_t)vblock)); + nandfs_free_entry(mdt, &req); + } + + VOP_UNLOCK(NTOV(dat), 0); + return (error); +} + +int +nandfs_get_dat_vinfo_ioctl(struct nandfs_device *nandfsdev, struct nandfs_argv *nargv) +{ + struct nandfs_vinfo *vinfo; + size_t size; + int error; + + if (nargv->nv_nmembs > NANDFS_VINFO_MAX) + return (EINVAL); + + size = sizeof(struct nandfs_vinfo) * nargv->nv_nmembs; + vinfo = malloc(size, M_NANDFSTEMP, M_WAITOK|M_ZERO); + + error = copyin((void *)(uintptr_t)nargv->nv_base, vinfo, size); + if (error) { + free(vinfo, M_NANDFSTEMP); + return (error); + } + + error = nandfs_get_dat_vinfo(nandfsdev, vinfo, nargv->nv_nmembs); + if (error == 0) + error = copyout(vinfo, (void *)(uintptr_t)nargv->nv_base, size); + free(vinfo, M_NANDFSTEMP); + return (error); +} + +int +nandfs_get_dat_vinfo(struct nandfs_device *nandfsdev, struct nandfs_vinfo *vinfo, + uint32_t nmembs) +{ + struct nandfs_node *dat; + struct nandfs_mdt *mdt; + struct nandfs_alloc_request req; + struct nandfs_dat_entry *dat_entry; + uint32_t i, idx; + int error = 0; + + dat = nandfsdev->nd_dat_node; + mdt = &nandfsdev->nd_dat_mdt; + + DPRINTF(DAT, ("%s: nmembs %#x\n", __func__, nmembs)); + + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + + for (i = 0; i < nmembs; i++) { + req.entrynum = vinfo[i].nvi_vblocknr; + + error = nandfs_get_entry_block(mdt, dat,&req, &idx, 0); + if (error) + break; + + dat_entry = ((struct nandfs_dat_entry *) req.bp_entry->b_data); + vinfo[i].nvi_start = dat_entry[idx].de_start; + vinfo[i].nvi_end = dat_entry[idx].de_end; + vinfo[i].nvi_blocknr = dat_entry[idx].de_blocknr; + + DPRINTF(DAT, ("%s: vinfo: %jx[%jx-%jx]->%jx\n", + __func__, vinfo[i].nvi_vblocknr, vinfo[i].nvi_start, + vinfo[i].nvi_end, vinfo[i].nvi_blocknr)); + + brelse(req.bp_entry); + } + + VOP_UNLOCK(NTOV(dat), 0); + return (error); +} + +int +nandfs_get_dat_bdescs_ioctl(struct nandfs_device *nffsdev, + struct nandfs_argv *nargv) +{ + struct nandfs_bdesc *bd; + size_t size; + int error; + + size = nargv->nv_nmembs * sizeof(struct nandfs_bdesc); + bd = malloc(size, M_NANDFSTEMP, M_WAITOK); + error = copyin((void *)(uintptr_t)nargv->nv_base, bd, size); + if (error) { + free(bd, M_NANDFSTEMP); + return (error); + } + + error = nandfs_get_dat_bdescs(nffsdev, bd, nargv->nv_nmembs); + + if (error == 0) + error = copyout(bd, (void *)(uintptr_t)nargv->nv_base, size); + + free(bd, M_NANDFSTEMP); + return (error); +} + +int +nandfs_get_dat_bdescs(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd, + uint32_t nmembs) +{ + struct nandfs_node *dat_node; + uint64_t map; + uint32_t i; + int error = 0; + + dat_node = nffsdev->nd_dat_node; + + VOP_LOCK(NTOV(dat_node), LK_EXCLUSIVE); + + for (i = 0; i < nmembs; i++) { + DPRINTF(CLEAN, + ("%s: bd ino:%#jx oblk:%#jx blocknr:%#jx off:%#jx\n", + __func__, (uintmax_t)bd[i].bd_ino, + (uintmax_t)bd[i].bd_oblocknr, (uintmax_t)bd[i].bd_blocknr, + (uintmax_t)bd[i].bd_offset)); + + error = nandfs_bmap_lookup(dat_node, bd[i].bd_offset, &map); + if (error) + break; + bd[i].bd_blocknr = map; + } + + VOP_UNLOCK(NTOV(dat_node), 0); + return (error); +} diff --git a/sys/fs/nandfs/nandfs_dir.c b/sys/fs/nandfs/nandfs_dir.c new file mode 100644 index 0000000..e279510 --- /dev/null +++ b/sys/fs/nandfs/nandfs_dir.c @@ -0,0 +1,314 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "nandfs_mount.h" +#include "nandfs.h" +#include "nandfs_subr.h" + +int +nandfs_add_dirent(struct vnode *dvp, uint64_t ino, char *nameptr, long namelen, + uint8_t type) +{ + struct nandfs_node *dir_node = VTON(dvp); + struct nandfs_dir_entry *dirent, *pdirent; + uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize; + uint64_t filesize = dir_node->nn_inode.i_size; + uint64_t inode_blks = dir_node->nn_inode.i_blocks; + uint32_t off, rest; + uint8_t *pos; + struct buf *bp; + int error; + + pdirent = NULL; + bp = NULL; + if (inode_blks) { + error = nandfs_bread(dir_node, inode_blks - 1, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + pos = bp->b_data; + off = 0; + while (off < blocksize) { + pdirent = (struct nandfs_dir_entry *) (pos + off); + if (!pdirent->rec_len) { + pdirent = NULL; + break; + } + off += pdirent->rec_len; + } + + if (pdirent) + rest = pdirent->rec_len - + NANDFS_DIR_REC_LEN(pdirent->name_len); + else + rest = blocksize; + + if (rest < NANDFS_DIR_REC_LEN(namelen)) { + /* Do not update pdirent as new block is created */ + pdirent = NULL; + brelse(bp); + /* Set to NULL to create new */ + bp = NULL; + filesize += rest; + } + } + + /* If no bp found create new */ + if (!bp) { + error = nandfs_bcreate(dir_node, inode_blks, NOCRED, 0, &bp); + if (error) + return (error); + off = 0; + pos = bp->b_data; + } + + /* Modify pdirent if exists */ + if (pdirent) { + DPRINTF(LOOKUP, ("modify pdirent %p\n", pdirent)); + /* modify last de */ + off -= pdirent->rec_len; + pdirent->rec_len = + NANDFS_DIR_REC_LEN(pdirent->name_len); + off += pdirent->rec_len; + } + + /* Create new dirent */ + dirent = (struct nandfs_dir_entry *) (pos + off); + dirent->rec_len = blocksize - off; + dirent->inode = ino; + dirent->name_len = namelen; + memset(dirent->name, 0, NANDFS_DIR_NAME_LEN(namelen)); + memcpy(dirent->name, nameptr, namelen); + dirent->file_type = type; + + filesize += NANDFS_DIR_REC_LEN(dirent->name_len); + + DPRINTF(LOOKUP, ("create dir_entry '%.*s' at %p with size %x " + "new filesize: %jx\n", + (int)namelen, dirent->name, dirent, dirent->rec_len, + (uintmax_t)filesize)); + + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + + dir_node->nn_inode.i_size = filesize; + dir_node->nn_flags |= IN_CHANGE | IN_UPDATE; + vnode_pager_setsize(dvp, filesize); + + return (0); +} + +int +nandfs_remove_dirent(struct vnode *dvp, struct nandfs_node *node, + struct componentname *cnp) +{ + struct nandfs_node *dir_node; + struct nandfs_dir_entry *dirent, *pdirent; + struct buf *bp; + uint64_t filesize, blocknr, ino, offset; + uint32_t blocksize, limit, off; + uint16_t newsize; + uint8_t *pos; + int error, found; + + dir_node = VTON(dvp); + filesize = dir_node->nn_inode.i_size; + if (!filesize) + return (0); + + if (node) { + offset = node->nn_diroff; + ino = node->nn_ino; + } else { + offset = dir_node->nn_diroff; + ino = NANDFS_WHT_INO; + } + + dirent = pdirent = NULL; + blocksize = dir_node->nn_nandfsdev->nd_blocksize; + blocknr = offset / blocksize; + + DPRINTF(LOOKUP, ("rm direntry dvp %p node %p ino %#jx at off %#jx\n", + dvp, node, (uintmax_t)ino, (uintmax_t)offset)); + + error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + pos = bp->b_data; + off = 0; + found = 0; + limit = offset % blocksize; + pdirent = (struct nandfs_dir_entry *) bp->b_data; + while (off <= limit) { + dirent = (struct nandfs_dir_entry *) (pos + off); + + if ((off == limit) && + (dirent->inode == ino)) { + found = 1; + break; + } + if (dirent->inode != 0) + pdirent = dirent; + off += dirent->rec_len; + } + + if (!found) { + nandfs_error("cannot find entry to remove"); + brelse(bp); + return (error); + } + DPRINTF(LOOKUP, + ("rm dirent ino %#jx at %#x with size %#x\n", + (uintmax_t)dirent->inode, off, dirent->rec_len)); + + newsize = (uintptr_t)dirent - (uintptr_t)pdirent; + newsize += dirent->rec_len; + pdirent->rec_len = newsize; + dirent->inode = 0; + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + + dir_node->nn_flags |= IN_CHANGE | IN_UPDATE; + /* If last one modify filesize */ + if ((offset + NANDFS_DIR_REC_LEN(dirent->name_len)) == filesize) { + filesize = blocknr * blocksize + + ((uintptr_t)pdirent - (uintptr_t)pos) + + NANDFS_DIR_REC_LEN(pdirent->name_len); + dir_node->nn_inode.i_size = filesize; + } + + return (0); +} + +int +nandfs_update_parent_dir(struct vnode *dvp, uint64_t newparent) +{ + struct nandfs_dir_entry *dirent; + struct nandfs_node *dir_node; + struct buf *bp; + int error; + + dir_node = VTON(dvp); + error = nandfs_bread(dir_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + dirent = (struct nandfs_dir_entry *)bp->b_data; + dirent->inode = newparent; + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + + return (0); +} + +int +nandfs_update_dirent(struct vnode *dvp, struct nandfs_node *fnode, + struct nandfs_node *tnode) +{ + struct nandfs_node *dir_node; + struct nandfs_dir_entry *dirent; + struct buf *bp; + uint64_t file_size, blocknr; + uint32_t blocksize, off; + uint8_t *pos; + int error; + + dir_node = VTON(dvp); + file_size = dir_node->nn_inode.i_size; + if (!file_size) + return (0); + + DPRINTF(LOOKUP, + ("chg direntry dvp %p ino %#jx to in %#jx at off %#jx\n", + dvp, (uintmax_t)tnode->nn_ino, (uintmax_t)fnode->nn_ino, + (uintmax_t)tnode->nn_diroff)); + + blocksize = dir_node->nn_nandfsdev->nd_blocksize; + blocknr = tnode->nn_diroff / blocksize; + off = tnode->nn_diroff % blocksize; + error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + pos = bp->b_data; + dirent = (struct nandfs_dir_entry *) (pos + off); + KASSERT((dirent->inode == tnode->nn_ino), + ("direntry mismatch")); + + dirent->inode = fnode->nn_ino; + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + + return (0); +} + +int +nandfs_init_dir(struct vnode *dvp, uint64_t ino, uint64_t parent_ino) +{ + + if (nandfs_add_dirent(dvp, parent_ino, "..", 2, DT_DIR) || + nandfs_add_dirent(dvp, ino, ".", 1, DT_DIR)) { + nandfs_error("%s: cannot initialize dir ino:%jd(pino:%jd)\n", + __func__, ino, parent_ino); + return (-1); + } + return (0); +} diff --git a/sys/fs/nandfs/nandfs_fs.h b/sys/fs/nandfs/nandfs_fs.h new file mode 100644 index 0000000..b72be40 --- /dev/null +++ b/sys/fs/nandfs/nandfs_fs.h @@ -0,0 +1,565 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Original definitions written by Koji Sato + * and Ryusuke Konishi + * From: NetBSD: nandfs_fs.h,v 1.1 2009/07/18 16:31:42 reinoud + * + * $FreeBSD$ + */ + +#ifndef _NANDFS_FS_H +#define _NANDFS_FS_H + +#include + +#define MNINDIR(fsdev) ((fsdev)->nd_blocksize / sizeof(nandfs_daddr_t)) + +/* + * Inode structure. There are a few dedicated inode numbers that are + * defined here first. + */ +#define NANDFS_WHT_INO 1 /* Whiteout ino */ +#define NANDFS_ROOT_INO 2 /* Root file inode */ +#define NANDFS_DAT_INO 3 /* DAT file */ +#define NANDFS_CPFILE_INO 4 /* checkpoint file */ +#define NANDFS_SUFILE_INO 5 /* segment usage file */ +#define NANDFS_IFILE_INO 6 /* ifile */ +#define NANDFS_GC_INO 7 /* Cleanerd node */ +#define NANDFS_ATIME_INO 8 /* Atime file (reserved) */ +#define NANDFS_XATTR_INO 9 /* Xattribute file (reserved) */ +#define NANDFS_SKETCH_INO 10 /* Sketch file (obsolete) */ +#define NANDFS_USER_INO 11 /* First user's file inode number */ + +#define NANDFS_SYS_NODE(ino) \ + (((ino) >= NANDFS_DAT_INO) && ((ino) <= NANDFS_GC_INO)) + +#define NDADDR 12 /* Direct addresses in inode. */ +#define NIADDR 3 /* Indirect addresses in inode. */ + +typedef int64_t nandfs_daddr_t; +typedef int64_t nandfs_lbn_t; + +struct nandfs_inode { + uint64_t i_blocks; /* 0: size in device blocks */ + uint64_t i_size; /* 8: size in bytes */ + uint64_t i_ctime; /* 16: creation time in seconds */ + uint64_t i_mtime; /* 24: modification time in seconds part*/ + uint32_t i_ctime_nsec; /* 32: creation time nanoseconds part */ + uint32_t i_mtime_nsec; /* 36: modification time in nanoseconds */ + uint32_t i_uid; /* 40: user id */ + uint32_t i_gid; /* 44: group id */ + uint16_t i_mode; /* 48: file mode */ + uint16_t i_links_count; /* 50: number of references to the inode*/ + uint32_t i_flags; /* 52: NANDFS_*_FL flags */ + nandfs_daddr_t i_special; /* 56: special */ + nandfs_daddr_t i_db[NDADDR]; /* 64: Direct disk blocks. */ + nandfs_daddr_t i_ib[NIADDR]; /* 160: Indirect disk blocks. */ + uint64_t i_xattr; /* 184: reserved for extended attributes*/ + uint32_t i_generation; /* 192: file generation for NFS */ + uint32_t i_pad[15]; /* 196: make it 64 bits aligned */ +}; + +#ifdef _KERNEL +CTASSERT(sizeof(struct nandfs_inode) == 256); +#endif + +/* + * Each checkpoint/snapshot has a super root. + * + * The super root holds the inodes of the three system files: `dat', `cp' and + * 'su' files. All other FS state is defined by those. + * + * It is CRC checksum'ed and time stamped. + */ + +struct nandfs_super_root { + uint32_t sr_sum; /* check-sum */ + uint16_t sr_bytes; /* byte count of this structure */ + uint16_t sr_flags; /* reserved for flags */ + uint64_t sr_nongc_ctime; /* timestamp, not for cleaner(?) */ + struct nandfs_inode sr_dat; /* DAT, virt->phys translation inode */ + struct nandfs_inode sr_cpfile; /* CP, checkpoints inode */ + struct nandfs_inode sr_sufile; /* SU, segment usage inode */ +}; + +#define NANDFS_SR_MDT_OFFSET(inode_size, i) \ + ((uint32_t)&((struct nandfs_super_root *)0)->sr_dat + \ + (inode_size) * (i)) + +#define NANDFS_SR_DAT_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 0) +#define NANDFS_SR_CPFILE_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 1) +#define NANDFS_SR_SUFILE_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 2) +#define NANDFS_SR_BYTES (sizeof(struct nandfs_super_root)) + +/* + * The superblock describes the basic structure and mount history. It also + * records some sizes of structures found on the disc for sanity checks. + * + * The superblock is stored at two places: NANDFS_SB_OFFSET_BYTES and + * NANDFS_SB2_OFFSET_BYTES. + */ + +/* File system states stored on media in superblock's sbp->s_state */ +#define NANDFS_VALID_FS 0x0001 /* cleanly unmounted and all is ok */ +#define NANDFS_ERROR_FS 0x0002 /* there were errors detected, fsck */ +#define NANDFS_RESIZE_FS 0x0004 /* resize required, XXX unknown flag*/ +#define NANDFS_MOUNT_STATE_BITS "\20\1VALID_FS\2ERROR_FS\3RESIZE_FS" + +/* + * Brief description of control structures: + * + * NANDFS_NFSAREAS first blocks contain fsdata and some amount of super blocks. + * Simple round-robin policy is used in order to choose which block will + * contain new super block. + * + * Simple case with 2 blocks: + * 1: fsdata sblock1 [sblock3 [sblock5 ..]] + * 2: fsdata sblock2 [sblock4 [sblock6 ..]] + */ +struct nandfs_fsdata { + uint16_t f_magic; + uint16_t f_bytes; + + uint32_t f_sum; /* checksum of fsdata */ + uint32_t f_rev_level; /* major disk format revision */ + + uint64_t f_ctime; /* creation time (execution time + of newfs) */ + /* Block size represented as: blocksize = 1 << (f_log_block_size + 10) */ + uint32_t f_log_block_size; + + uint16_t f_inode_size; /* size of an inode */ + uint16_t f_dat_entry_size; /* size of a dat entry */ + uint16_t f_checkpoint_size; /* size of a checkpoint */ + uint16_t f_segment_usage_size; /* size of a segment usage */ + + uint16_t f_sbbytes; /* byte count of CRC calculation + for super blocks. s_reserved + is excluded! */ + + uint16_t f_errors; /* behaviour on detecting errors */ + + uint32_t f_erasesize; + uint64_t f_nsegments; /* number of segm. in filesystem */ + nandfs_daddr_t f_first_data_block; /* 1st seg disk block number */ + uint32_t f_blocks_per_segment; /* number of blocks per segment */ + uint32_t f_r_segments_percentage; /* reserved segments percentage */ + + struct uuid f_uuid; /* 128-bit uuid for volume */ + char f_volume_name[16]; /* volume name */ + uint32_t f_pad[104]; +} __packed; + +#ifdef _KERNEL +CTASSERT(sizeof(struct nandfs_fsdata) == 512); +#endif + +struct nandfs_super_block { + uint16_t s_magic; /* magic value for identification */ + + uint32_t s_sum; /* check sum of super block */ + + uint64_t s_last_cno; /* last checkpoint number */ + uint64_t s_last_pseg; /* addr part. segm. written last */ + uint64_t s_last_seq; /* seq.number of seg written last */ + uint64_t s_free_blocks_count; /* free blocks count */ + + uint64_t s_mtime; /* mount time */ + uint64_t s_wtime; /* write time */ + uint16_t s_state; /* file system state */ + + char s_last_mounted[64]; /* directory where last mounted */ + + uint32_t s_c_interval; /* commit interval of segment */ + uint32_t s_c_block_max; /* threshold of data amount for + the segment construction */ + uint32_t s_reserved[32]; /* padding to end of the block */ +} __packed; + +#ifdef _KERNEL +CTASSERT(sizeof(struct nandfs_super_block) == 256); +#endif + +#define NANDFS_FSDATA_MAGIC 0xf8da +#define NANDFS_SUPER_MAGIC 0x8008 + +#define NANDFS_NFSAREAS 4 +#define NANDFS_DATA_OFFSET_BYTES(esize) (NANDFS_NFSAREAS * (esize)) + +#define NANDFS_SBLOCK_OFFSET_BYTES (sizeof(struct nandfs_fsdata)) + +#define NANDFS_DEF_BLOCKSIZE 4096 +#define NANDFS_MIN_BLOCKSIZE 512 + +#define NANDFS_DEF_ERASESIZE (2 << 16) + +#define NANDFS_MIN_SEGSIZE NANDFS_DEF_ERASESIZE + +#define NANDFS_CURRENT_REV 9 /* current major revision */ + +#define NANDFS_FSDATA_CRC_BYTES offsetof(struct nandfs_fsdata, f_pad) +/* Bytes count of super_block for CRC-calculation */ +#define NANDFS_SB_BYTES offsetof(struct nandfs_super_block, s_reserved) + +/* Maximal count of links to a file */ +#define NANDFS_LINK_MAX 32000 + +/* + * Structure of a directory entry. + * + * Note that they can't span blocks; the rec_len fills out. + */ + +#define NANDFS_NAME_LEN 255 +struct nandfs_dir_entry { + uint64_t inode; /* inode number */ + uint16_t rec_len; /* directory entry length */ + uint8_t name_len; /* name length */ + uint8_t file_type; + char name[NANDFS_NAME_LEN]; /* file name */ + char pad; +}; + +/* + * NANDFS_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 8 + */ +#define NANDFS_DIR_PAD 8 +#define NANDFS_DIR_ROUND (NANDFS_DIR_PAD - 1) +#define NANDFS_DIR_NAME_OFFSET (offsetof(struct nandfs_dir_entry, name)) +#define NANDFS_DIR_REC_LEN(name_len) \ + (((name_len) + NANDFS_DIR_NAME_OFFSET + NANDFS_DIR_ROUND) \ + & ~NANDFS_DIR_ROUND) +#define NANDFS_DIR_NAME_LEN(name_len) \ + (NANDFS_DIR_REC_LEN(name_len) - NANDFS_DIR_NAME_OFFSET) + +/* + * NiLFS/NANDFS devides the disc into fixed length segments. Each segment is + * filled with one or more partial segments of variable lengths. + * + * Each partial segment has a segment summary header followed by updates of + * files and optionally a super root. + */ + +/* + * Virtual to physical block translation information. For data blocks it maps + * logical block number bi_blkoff to virtual block nr bi_vblocknr. For non + * datablocks it is the virtual block number assigned to an indirect block + * and has no bi_blkoff. The physical block number is the next + * available data block in the partial segment after all the binfo's. + */ +struct nandfs_binfo_v { + uint64_t bi_ino; /* file's inode */ + uint64_t bi_vblocknr; /* assigned virtual block number */ + uint64_t bi_blkoff; /* for file's logical block number */ +}; + +/* + * DAT allocation. For data blocks just the logical block number that maps on + * the next available data block in the partial segment after the binfo's. + */ +struct nandfs_binfo_dat { + uint64_t bi_ino; + uint64_t bi_blkoff; /* DAT file's logical block number */ + uint8_t bi_level; /* whether this is meta block */ + uint8_t bi_pad[7]; +}; + +#ifdef _KERNEL +CTASSERT(sizeof(struct nandfs_binfo_v) == sizeof(struct nandfs_binfo_dat)); +#endif + +/* Convenience union for both types of binfo's */ +union nandfs_binfo { + struct nandfs_binfo_v bi_v; + struct nandfs_binfo_dat bi_dat; +}; + +/* Indirect buffers path */ +struct nandfs_indir { + nandfs_daddr_t in_lbn; + int in_off; +}; + +/* The (partial) segment summary */ +struct nandfs_segment_summary { + uint32_t ss_datasum; /* CRC of complete data block */ + uint32_t ss_sumsum; /* CRC of segment summary only */ + uint32_t ss_magic; /* magic to identify segment summary */ + uint16_t ss_bytes; /* size of segment summary structure */ + uint16_t ss_flags; /* NANDFS_SS_* flags */ + uint64_t ss_seq; /* sequence number of this segm. sum */ + uint64_t ss_create; /* creation timestamp in seconds */ + uint64_t ss_next; /* blocknumber of next segment */ + uint32_t ss_nblocks; /* number of blocks used by summary */ + uint32_t ss_nbinfos; /* number of binfo structures */ + uint32_t ss_sumbytes; /* total size of segment summary */ + uint32_t ss_pad; + /* stream of binfo structures */ +}; + +#define NANDFS_SEGSUM_MAGIC 0x8e680011 /* segment summary magic number */ + +/* Segment summary flags */ +#define NANDFS_SS_LOGBGN 0x0001 /* begins a logical segment */ +#define NANDFS_SS_LOGEND 0x0002 /* ends a logical segment */ +#define NANDFS_SS_SR 0x0004 /* has super root */ +#define NANDFS_SS_SYNDT 0x0008 /* includes data only updates */ +#define NANDFS_SS_GC 0x0010 /* segment written for cleaner operation */ +#define NANDFS_SS_FLAG_BITS "\20\1LOGBGN\2LOGEND\3SR\4SYNDT\5GC" + +/* Segment summary constrains */ +#define NANDFS_SEG_MIN_BLOCKS 16 /* minimum number of blocks in a + full segment */ +#define NANDFS_PSEG_MIN_BLOCKS 2 /* minimum number of blocks in a + partial segment */ +#define NANDFS_MIN_NRSVSEGS 8 /* minimum number of reserved + segments */ + +/* + * Structure of DAT/inode file. + * + * A DAT file is devided into groups. The maximum number of groups is the + * number of block group descriptors that fit into one block; this descriptor + * only gives the number of free entries in the associated group. + * + * Each group has a block sized bitmap indicating if an entry is taken or + * empty. Each bit stands for a DAT entry. + * + * The inode file has exactly the same format only the entries are inode + * entries. + */ + +struct nandfs_block_group_desc { + uint32_t bg_nfrees; /* num. free entries in block group */ +}; + +/* DAT entry in a super root's DAT file */ +struct nandfs_dat_entry { + uint64_t de_blocknr; /* block number */ + uint64_t de_start; /* valid from checkpoint */ + uint64_t de_end; /* valid till checkpoint */ + uint64_t de_rsv; /* reserved for future use */ +}; + +/* + * Structure of CP file. + * + * A snapshot is just a checkpoint only it's protected against removal by the + * cleaner. The snapshots are kept on a double linked list of checkpoints. + */ +struct nandfs_snapshot_list { + uint64_t ssl_next; /* checkpoint nr. forward */ + uint64_t ssl_prev; /* checkpoint nr. back */ +}; + +/* Checkpoint entry structure */ +struct nandfs_checkpoint { + uint32_t cp_flags; /* NANDFS_CHECKPOINT_* flags */ + uint32_t cp_checkpoints_count; /* ZERO, not used anymore? */ + struct nandfs_snapshot_list cp_snapshot_list; /* list of snapshots */ + uint64_t cp_cno; /* checkpoint number */ + uint64_t cp_create; /* creation timestamp */ + uint64_t cp_nblk_inc; /* number of blocks incremented */ + uint64_t cp_blocks_count; /* reserved (might be deleted) */ + struct nandfs_inode cp_ifile_inode; /* inode file inode */ +}; + +/* Checkpoint flags */ +#define NANDFS_CHECKPOINT_SNAPSHOT 1 +#define NANDFS_CHECKPOINT_INVALID 2 +#define NANDFS_CHECKPOINT_SKETCH 4 +#define NANDFS_CHECKPOINT_MINOR 8 +#define NANDFS_CHECKPOINT_BITS "\20\1SNAPSHOT\2INVALID\3SKETCH\4MINOR" + +/* Header of the checkpoint file */ +struct nandfs_cpfile_header { + uint64_t ch_ncheckpoints; /* number of checkpoints */ + uint64_t ch_nsnapshots; /* number of snapshots */ + struct nandfs_snapshot_list ch_snapshot_list; /* snapshot list */ +}; + +#define NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET \ + ((sizeof(struct nandfs_cpfile_header) + \ + sizeof(struct nandfs_checkpoint) - 1) / \ + sizeof(struct nandfs_checkpoint)) + + +#define NANDFS_NOSEGMENT 0xffffffff + +/* + * Structure of SU file. + * + * The segment usage file sums up how each of the segments are used. They are + * indexed by their segment number. + */ + +/* Segment usage entry */ +struct nandfs_segment_usage { + uint64_t su_lastmod; /* last modified timestamp */ + uint32_t su_nblocks; /* number of blocks in segment */ + uint32_t su_flags; /* NANDFS_SEGMENT_USAGE_* flags */ +}; + +/* Segment usage flag */ +#define NANDFS_SEGMENT_USAGE_ACTIVE 1 +#define NANDFS_SEGMENT_USAGE_DIRTY 2 +#define NANDFS_SEGMENT_USAGE_ERROR 4 +#define NANDFS_SEGMENT_USAGE_GC 8 +#define NANDFS_SEGMENT_USAGE_BITS "\20\1ACTIVE\2DIRTY\3ERROR" + +/* Header of the segment usage file */ +struct nandfs_sufile_header { + uint64_t sh_ncleansegs; /* number of segments marked clean */ + uint64_t sh_ndirtysegs; /* number of segments marked dirty */ + uint64_t sh_last_alloc; /* last allocated segment number */ +}; + +#define NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET \ + ((sizeof(struct nandfs_sufile_header) + \ + sizeof(struct nandfs_segment_usage) - 1) / \ + sizeof(struct nandfs_segment_usage)) + +struct nandfs_seg_stat { + uint64_t nss_nsegs; + uint64_t nss_ncleansegs; + uint64_t nss_ndirtysegs; + uint64_t nss_ctime; + uint64_t nss_nongc_ctime; + uint64_t nss_prot_seq; +}; + +enum { + NANDFS_CHECKPOINT, + NANDFS_SNAPSHOT +}; + +#define NANDFS_CPINFO_MAX 512 + +struct nandfs_cpinfo { + uint32_t nci_flags; + uint32_t nci_pad; + uint64_t nci_cno; + uint64_t nci_create; + uint64_t nci_nblk_inc; + uint64_t nci_blocks_count; + uint64_t nci_next; +}; + +#define NANDFS_SEGMENTS_MAX 512 + +struct nandfs_suinfo { + uint64_t nsi_num; + uint64_t nsi_lastmod; + uint32_t nsi_blocks; + uint32_t nsi_flags; +}; + +#define NANDFS_VINFO_MAX 512 + +struct nandfs_vinfo { + uint64_t nvi_ino; + uint64_t nvi_vblocknr; + uint64_t nvi_start; + uint64_t nvi_end; + uint64_t nvi_blocknr; + int nvi_alive; +}; + +struct nandfs_cpmode { + uint64_t ncpm_cno; + uint32_t ncpm_mode; + uint32_t ncpm_pad; +}; + +struct nandfs_argv { + uint64_t nv_base; + uint32_t nv_nmembs; + uint16_t nv_size; + uint16_t nv_flags; + uint64_t nv_index; +}; + +struct nandfs_cpstat { + uint64_t ncp_cno; + uint64_t ncp_ncps; + uint64_t ncp_nss; +}; + +struct nandfs_period { + uint64_t p_start; + uint64_t p_end; +}; + +struct nandfs_vdesc { + uint64_t vd_ino; + uint64_t vd_cno; + uint64_t vd_vblocknr; + struct nandfs_period vd_period; + uint64_t vd_blocknr; + uint64_t vd_offset; + uint32_t vd_flags; + uint32_t vd_pad; +}; + +struct nandfs_bdesc { + uint64_t bd_ino; + uint64_t bd_oblocknr; + uint64_t bd_blocknr; + uint64_t bd_offset; + uint32_t bd_level; + uint32_t bd_alive; +}; + +#ifndef _KERNEL +#ifndef MNAMELEN +#define MNAMELEN 88 +#endif +#endif + +struct nandfs_fsinfo { + struct nandfs_fsdata fs_fsdata; + struct nandfs_super_block fs_super; + char fs_dev[MNAMELEN]; +}; + +#define NANDFS_MAX_MOUNTS 65535 + +#define NANDFS_IOCTL_GET_SUSTAT _IOR('N', 100, struct nandfs_seg_stat) +#define NANDFS_IOCTL_CHANGE_CPMODE _IOWR('N', 101, struct nandfs_cpmode) +#define NANDFS_IOCTL_GET_CPINFO _IOWR('N', 102, struct nandfs_argv) +#define NANDFS_IOCTL_DELETE_CP _IOWR('N', 103, uint64_t[2]) +#define NANDFS_IOCTL_GET_CPSTAT _IOR('N', 104, struct nandfs_cpstat) +#define NANDFS_IOCTL_GET_SUINFO _IOWR('N', 105, struct nandfs_argv) +#define NANDFS_IOCTL_GET_VINFO _IOWR('N', 106, struct nandfs_argv) +#define NANDFS_IOCTL_GET_BDESCS _IOWR('N', 107, struct nandfs_argv) +#define NANDFS_IOCTL_GET_FSINFO _IOR('N', 108, struct nandfs_fsinfo) +#define NANDFS_IOCTL_MAKE_SNAP _IOWR('N', 109, uint64_t) +#define NANDFS_IOCTL_DELETE_SNAP _IOWR('N', 110, uint64_t) +#define NANDFS_IOCTL_SYNC _IOWR('N', 111, uint64_t) + +#endif /* _NANDFS_FS_H */ diff --git a/sys/fs/nandfs/nandfs_ifile.c b/sys/fs/nandfs/nandfs_ifile.c new file mode 100644 index 0000000..7e4db87 --- /dev/null +++ b/sys/fs/nandfs/nandfs_ifile.c @@ -0,0 +1,213 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +int +nandfs_node_create(struct nandfsmount *nmp, struct nandfs_node **node, + uint16_t mode) +{ + struct nandfs_alloc_request req; + struct nandfs_device *nandfsdev; + struct nandfs_mdt *mdt; + struct nandfs_node *ifile; + struct nandfs_inode *inode; + struct vnode *vp; + uint32_t entry; + int error = 0; + + nandfsdev = nmp->nm_nandfsdev; + mdt = &nandfsdev->nd_ifile_mdt; + ifile = nmp->nm_ifile_node; + vp = NTOV(ifile); + + VOP_LOCK(vp, LK_EXCLUSIVE); + /* Allocate new inode in ifile */ + req.entrynum = nandfsdev->nd_last_ino + 1; + error = nandfs_find_free_entry(mdt, ifile, &req); + if (error) { + VOP_UNLOCK(vp, 0); + return (error); + } + + error = nandfs_get_entry_block(mdt, ifile, &req, &entry, 1); + if (error) { + VOP_UNLOCK(vp, 0); + return (error); + } + + /* Inode initialization */ + inode = ((struct nandfs_inode *) req.bp_entry->b_data) + entry; + nandfs_inode_init(inode, mode); + + error = nandfs_alloc_entry(mdt, &req); + if (error) { + VOP_UNLOCK(vp, 0); + return (error); + } + + VOP_UNLOCK(vp, 0); + + nandfsdev->nd_last_ino = req.entrynum; + error = nandfs_get_node(nmp, req.entrynum, node); + DPRINTF(IFILE, ("%s: node: %p ino: %#jx\n", + __func__, node, (uintmax_t)((*node)->nn_ino))); + + return (error); +} + +int +nandfs_node_destroy(struct nandfs_node *node) +{ + struct nandfs_alloc_request req; + struct nandfsmount *nmp; + struct nandfs_mdt *mdt; + struct nandfs_node *ifile; + struct vnode *vp; + int error = 0; + + nmp = node->nn_nmp; + req.entrynum = node->nn_ino; + mdt = &nmp->nm_nandfsdev->nd_ifile_mdt; + ifile = nmp->nm_ifile_node; + vp = NTOV(ifile); + + DPRINTF(IFILE, ("%s: destroy node: %p ino: %#jx\n", + __func__, node, (uintmax_t)node->nn_ino)); + VOP_LOCK(vp, LK_EXCLUSIVE); + + error = nandfs_find_entry(mdt, ifile, &req); + if (error) { + nandfs_error("%s: finding entry error:%d node %p(%jx)", + __func__, error, node, node->nn_ino); + VOP_UNLOCK(vp, 0); + return (error); + } + + nandfs_inode_destroy(&node->nn_inode); + + error = nandfs_free_entry(mdt, &req); + if (error) { + nandfs_error("%s: freing entry error:%d node %p(%jx)", + __func__, error, node, node->nn_ino); + VOP_UNLOCK(vp, 0); + return (error); + } + + VOP_UNLOCK(vp, 0); + DPRINTF(IFILE, ("%s: freed node %p ino %#jx\n", + __func__, node, (uintmax_t)node->nn_ino)); + return (error); +} + +int +nandfs_node_update(struct nandfs_node *node) +{ + struct nandfs_alloc_request req; + struct nandfsmount *nmp; + struct nandfs_mdt *mdt; + struct nandfs_node *ifile; + struct nandfs_inode *inode; + uint32_t index; + int error = 0; + + nmp = node->nn_nmp; + ifile = nmp->nm_ifile_node; + ASSERT_VOP_LOCKED(NTOV(ifile), __func__); + + req.entrynum = node->nn_ino; + mdt = &nmp->nm_nandfsdev->nd_ifile_mdt; + + DPRINTF(IFILE, ("%s: node:%p ino:%#jx\n", + __func__, &node->nn_inode, (uintmax_t)node->nn_ino)); + + error = nandfs_get_entry_block(mdt, ifile, &req, &index, 0); + if (error) { + printf("nandfs_get_entry_block returned with ERROR=%d\n", + error); + return (error); + } + + inode = ((struct nandfs_inode *) req.bp_entry->b_data) + index; + memcpy(inode, &node->nn_inode, sizeof(*inode)); + error = nandfs_dirty_buf(req.bp_entry, 0); + + return (error); +} + +int +nandfs_get_node_entry(struct nandfsmount *nmp, struct nandfs_inode **inode, + uint64_t ino, struct buf **bp) +{ + struct nandfs_alloc_request req; + struct nandfs_mdt *mdt; + struct nandfs_node *ifile; + struct vnode *vp; + uint32_t index; + int error = 0; + + req.entrynum = ino; + mdt = &nmp->nm_nandfsdev->nd_ifile_mdt; + ifile = nmp->nm_ifile_node; + vp = NTOV(ifile); + + VOP_LOCK(vp, LK_EXCLUSIVE); + error = nandfs_get_entry_block(mdt, ifile, &req, &index, 0); + if (error) { + VOP_UNLOCK(vp, 0); + return (error); + } + + *inode = ((struct nandfs_inode *) req.bp_entry->b_data) + index; + *bp = req.bp_entry; + VOP_UNLOCK(vp, 0); + return (0); +} + diff --git a/sys/fs/nandfs/nandfs_mount.h b/sys/fs/nandfs/nandfs_mount.h new file mode 100644 index 0000000..f733e22 --- /dev/null +++ b/sys/fs/nandfs/nandfs_mount.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs_mount.h,v 1.1 2009/07/18 16:31:42 reinoud + * + * $FreeBSD$ + */ + +#ifndef _FS_NANDFS_NANDFS_MOUNT_H_ +#define _FS_NANDFS_NANDFS_MOUNT_H_ + +/* + * Arguments to mount NANDFS filingsystem. + */ + +struct nandfs_args { + char *fspec; /* mount specifier */ + int64_t cpno; /* checkpoint number */ +}; + +#endif /* !_FS_NANDFS_NANDFS_MOUNT_H_ */ + diff --git a/sys/fs/nandfs/nandfs_segment.c b/sys/fs/nandfs/nandfs_segment.c new file mode 100644 index 0000000..836bead --- /dev/null +++ b/sys/fs/nandfs/nandfs_segment.c @@ -0,0 +1,1329 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_ddb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +static int +nandfs_new_segment(struct nandfs_device *fsdev) +{ + int error = 0; + uint64_t new; + + error = nandfs_alloc_segment(fsdev, &new); + if (!error) { + fsdev->nd_seg_num = fsdev->nd_next_seg_num; + fsdev->nd_next_seg_num = new; + } + DPRINTF(SYNC, ("%s: new segment %jx next %jx error %d\n", + __func__, (uintmax_t)fsdev->nd_seg_num, (uintmax_t)new, error)); + if (error) + nandfs_error("%s: cannot create segment error %d\n", + __func__, error); + + return (error); +} + +static int +create_segment(struct nandfs_seginfo *seginfo) +{ + struct nandfs_segment *seg; + struct nandfs_device *fsdev; + struct nandfs_segment *prev; + struct buf *bp; + uint64_t start_block, curr; + uint32_t blks_per_seg, nblocks; + int error; + + fsdev = seginfo->fsdev; + prev = seginfo->curseg; + blks_per_seg = fsdev->nd_fsdata.f_blocks_per_segment; + nblocks = fsdev->nd_last_segsum.ss_nblocks; + + if (!prev) { + vfs_timestamp(&fsdev->nd_ts); + /* Touch current segment */ + error = nandfs_touch_segment(fsdev, fsdev->nd_seg_num); + if (error) { + nandfs_error("%s: cannot preallocate segment %jx\n", + __func__, fsdev->nd_seg_num); + return (error); + } + error = nandfs_touch_segment(fsdev, 0); + if (error) { + nandfs_error("%s: cannot dirty block with segment 0\n", + __func__); + return (error); + } + start_block = fsdev->nd_last_pseg + (uint64_t)nblocks; + /* + * XXX Hack + */ + if (blks_per_seg - (start_block % blks_per_seg) - 1 == 0) + start_block++; + curr = nandfs_get_segnum_of_block(fsdev, start_block); + /* Allocate new segment if last one is full */ + if (fsdev->nd_seg_num != curr) { + error = nandfs_new_segment(fsdev); + if (error) { + nandfs_error("%s: cannot create new segment\n", + __func__); + return (error); + } + /* + * XXX Hack + */ + nandfs_get_segment_range(fsdev, fsdev->nd_seg_num, &start_block, NULL); + } + } else { + nandfs_get_segment_range(fsdev, fsdev->nd_next_seg_num, + &start_block, NULL); + + /* Touch current segment and allocate and touch new one */ + error = nandfs_new_segment(fsdev); + if (error) { + nandfs_error("%s: cannot create next segment\n", + __func__); + return (error); + } + + /* Reiterate in case new buf is dirty */ + seginfo->reiterate = 1; + } + + /* Allocate and initialize nandfs_segment structure */ + seg = malloc(sizeof(*seg), M_DEVBUF, M_WAITOK|M_ZERO); + TAILQ_INIT(&seg->segsum); + TAILQ_INIT(&seg->data); + seg->fsdev = fsdev; + seg->start_block = start_block; + seg->num_blocks = blks_per_seg - (start_block % blks_per_seg) - 1; + seg->seg_num = fsdev->nd_seg_num; + seg->seg_next = fsdev->nd_next_seg_num; + seg->segsum_blocks = 1; + seg->bytes_left = fsdev->nd_blocksize - + sizeof(struct nandfs_segment_summary); + seg->segsum_bytes = sizeof(struct nandfs_segment_summary); + + /* Allocate buffer for segment summary */ + bp = getblk(fsdev->nd_devvp, nandfs_block_to_dblock(fsdev, + seg->start_block), fsdev->nd_blocksize, 0, 0, 0); + bzero(bp->b_data, seginfo->fsdev->nd_blocksize); + bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj; + bp->b_flags |= B_MANAGED; + + /* Add buffer to segment */ + TAILQ_INSERT_TAIL(&seg->segsum, bp, b_cluster.cluster_entry); + seg->current_off = bp->b_data + sizeof(struct nandfs_segment_summary); + + DPRINTF(SYNC, ("%s: seg %p : initial settings: start %#jx size :%#x\n", + __func__, seg, (uintmax_t)seg->start_block, seg->num_blocks)); + DPRINTF(SYNC, ("%s: seg->seg_num %#jx cno %#jx next %#jx\n", __func__, + (uintmax_t)seg->seg_num, (uintmax_t)(fsdev->nd_last_cno + 1), + (uintmax_t)seg->seg_next)); + + if (!prev) + LIST_INSERT_HEAD(&seginfo->seg_list, seg, seg_link); + else + LIST_INSERT_AFTER(prev, seg, seg_link); + + seginfo->curseg = seg; + + return (0); +} + +static int +delete_segment(struct nandfs_seginfo *seginfo) +{ + struct nandfs_segment *seg, *tseg; + struct buf *bp, *tbp; + + LIST_FOREACH_SAFE(seg, &seginfo->seg_list, seg_link, tseg) { + TAILQ_FOREACH_SAFE(bp, &seg->segsum, b_cluster.cluster_entry, + tbp) { + TAILQ_REMOVE(&seg->segsum, bp, b_cluster.cluster_entry); + bp->b_flags &= ~B_MANAGED; + brelse(bp); + }; + + LIST_REMOVE(seg, seg_link); + free(seg, M_DEVBUF); + } + + return (0); +} + +static int +create_seginfo(struct nandfs_device *fsdev, struct nandfs_seginfo **seginfo) +{ + struct nandfs_seginfo *info; + + info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK); + + LIST_INIT(&info->seg_list); + info->fsdev = fsdev; + info->curseg = NULL; + info->blocks = 0; + *seginfo = info; + fsdev->nd_seginfo = info; + return (0); +} + +static int +delete_seginfo(struct nandfs_seginfo *seginfo) +{ + struct nandfs_device *nffsdev; + + nffsdev = seginfo->fsdev; + delete_segment(seginfo); + nffsdev->nd_seginfo = NULL; + free(seginfo, M_DEVBUF); + + return (0); +} + +static int +nandfs_create_superroot_block(struct nandfs_seginfo *seginfo, + struct buf **newbp) +{ + struct buf *bp; + int error; + + bp = nandfs_geteblk(seginfo->fsdev->nd_blocksize, GB_NOWAIT_BD); + + bzero(bp->b_data, seginfo->fsdev->nd_blocksize); + bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj; + bp->b_flags |= B_MANAGED; + + if (!(seginfo->curseg) || !seginfo->curseg->num_blocks) { + error = create_segment(seginfo); + if (error) { + brelse(bp); + nandfs_error("%s: no segment for superroot\n", + __func__); + return (error); + } + } + + TAILQ_INSERT_TAIL(&seginfo->curseg->data, bp, b_cluster.cluster_entry); + + seginfo->curseg->nblocks++; + seginfo->curseg->num_blocks--; + seginfo->blocks++; + + *newbp = bp; + return (0); +} + +static int +nandfs_add_superroot(struct nandfs_seginfo *seginfo) +{ + struct nandfs_device *fsdev; + struct nandfs_super_root *sr; + struct buf *bp = NULL; + uint64_t crc_skip; + uint32_t crc_calc; + int error; + + fsdev = seginfo->fsdev; + + error = nandfs_create_superroot_block(seginfo, &bp); + if (error) { + nandfs_error("%s: cannot add superroot\n", __func__); + return (error); + } + + sr = (struct nandfs_super_root *)bp->b_data; + /* Save superroot CRC */ + sr->sr_bytes = NANDFS_SR_BYTES; + sr->sr_flags = 0; + sr->sr_nongc_ctime = 0; + + memcpy(&sr->sr_dat, &fsdev->nd_dat_node->nn_inode, + sizeof(struct nandfs_inode)); + memcpy(&sr->sr_cpfile, &fsdev->nd_cp_node->nn_inode, + sizeof(struct nandfs_inode)); + memcpy(&sr->sr_sufile, &fsdev->nd_su_node->nn_inode, + sizeof(struct nandfs_inode)); + + crc_skip = sizeof(sr->sr_sum); + crc_calc = crc32((uint8_t *)sr + crc_skip, NANDFS_SR_BYTES - crc_skip); + + sr->sr_sum = crc_calc; + + bp->b_flags |= B_MANAGED; + bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj; + + bp->b_flags &= ~B_INVAL; + nandfs_dirty_bufs_increment(fsdev); + DPRINTF(SYNC, ("%s: bp:%p\n", __func__, bp)); + + return (0); +} + +static int +nandfs_add_segsum_block(struct nandfs_seginfo *seginfo, struct buf **newbp) +{ + struct nandfs_device *fsdev; + nandfs_daddr_t blk; + struct buf *bp; + int error; + + if (!(seginfo->curseg) || seginfo->curseg->num_blocks <= 1) { + error = create_segment(seginfo); + if (error) { + nandfs_error("%s: error:%d when creating segment\n", + __func__, error); + return (error); + } + *newbp = TAILQ_FIRST(&seginfo->curseg->segsum); + return (0); + } + + fsdev = seginfo->fsdev; + blk = nandfs_block_to_dblock(fsdev, seginfo->curseg->start_block + + seginfo->curseg->segsum_blocks); + + bp = getblk(fsdev->nd_devvp, blk, fsdev->nd_blocksize, 0, 0, 0); + + bzero(bp->b_data, seginfo->fsdev->nd_blocksize); + bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj; + bp->b_flags |= B_MANAGED; + + TAILQ_INSERT_TAIL(&seginfo->curseg->segsum, bp, + b_cluster.cluster_entry); + seginfo->curseg->num_blocks--; + + seginfo->curseg->segsum_blocks++; + seginfo->curseg->bytes_left = seginfo->fsdev->nd_blocksize; + seginfo->curseg->current_off = bp->b_data; + seginfo->blocks++; + + *newbp = bp; + + DPRINTF(SYNC, ("%s: bp %p\n", __func__, bp)); + + return (0); +} + +static int +nandfs_add_blocks(struct nandfs_seginfo *seginfo, struct nandfs_node *node, + struct buf *bp) +{ + union nandfs_binfo *binfo; + struct buf *seg_bp; + int error; + + if (!(seginfo->curseg) || !seginfo->curseg->num_blocks) { + error = create_segment(seginfo); + if (error) { + nandfs_error("%s: error:%d when creating segment\n", + __func__, error); + return (error); + } + } + + if (seginfo->curseg->bytes_left < sizeof(union nandfs_binfo)) { + error = nandfs_add_segsum_block(seginfo, &seg_bp); + if (error) { + nandfs_error("%s: error:%d when adding segsum\n", + __func__, error); + return (error); + } + } + binfo = (union nandfs_binfo *)seginfo->curseg->current_off; + + if (node->nn_ino != NANDFS_DAT_INO) { + binfo->bi_v.bi_blkoff = bp->b_lblkno; + binfo->bi_v.bi_ino = node->nn_ino; + } else { + binfo->bi_dat.bi_blkoff = bp->b_lblkno; + binfo->bi_dat.bi_ino = node->nn_ino; + if (NANDFS_IS_INDIRECT(bp)) + binfo->bi_dat.bi_level = 1; + else + binfo->bi_dat.bi_level = 0; + } + binfo++; + + seginfo->curseg->bytes_left -= sizeof(union nandfs_binfo); + seginfo->curseg->segsum_bytes += sizeof(union nandfs_binfo); + seginfo->curseg->current_off = (char *)binfo; + + TAILQ_INSERT_TAIL(&seginfo->curseg->data, bp, b_cluster.cluster_entry); + + seginfo->curseg->nbinfos++; + seginfo->curseg->nblocks++; + seginfo->curseg->num_blocks--; + seginfo->blocks++; + + DPRINTF(SYNC, ("%s: bp (%p) number %x (left %x)\n", + __func__, bp, seginfo->curseg->nblocks, + seginfo->curseg->num_blocks)); + return (0); +} + +static int +nandfs_iterate_dirty_buf(struct vnode *vp, struct nandfs_seginfo *seginfo, + uint8_t hold) +{ + struct buf *bp, *tbd; + struct bufobj *bo; + struct nandfs_node *node; + int error; + + node = VTON(vp); + bo = &vp->v_bufobj; + + ASSERT_VOP_ELOCKED(vp, __func__); + + /* Iterate dirty data bufs */ + TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, tbd) { + DPRINTF(SYNC, ("%s: vp (%p): bp (%p) with lblkno %jx ino %jx " + "add buf\n", __func__, vp, bp, bp->b_lblkno, node->nn_ino)); + + if (!(NANDFS_ISGATHERED(bp))) { + error = nandfs_bmap_update_dat(node, + nandfs_vblk_get(bp), bp); + if (error) + return (error); + NANDFS_GATHER(bp); + nandfs_add_blocks(seginfo, node, bp); + } + } + + return (0); +} + +static int +nandfs_iterate_system_vnode(struct nandfs_node *node, + struct nandfs_seginfo *seginfo) +{ + struct vnode *vp; + int nblocks; + uint8_t hold = 0; + + if (node->nn_ino != NANDFS_IFILE_INO) + hold = 1; + + vp = NTOV(node); + + nblocks = vp->v_bufobj.bo_dirty.bv_cnt; + DPRINTF(SYNC, ("%s: vp (%p): nblocks %x ino %jx\n", + __func__, vp, nblocks, node->nn_ino)); + + if (nblocks) + nandfs_iterate_dirty_buf(vp, seginfo, hold); + + return (0); +} + +static int +nandfs_iterate_dirty_vnodes(struct mount *mp, struct nandfs_seginfo *seginfo) +{ + struct nandfs_node *nandfs_node; + struct vnode *vp, *mvp; + struct thread *td; + int error, lockreq, update; + + td = curthread; + lockreq = LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY; + + MNT_ILOCK(mp); + + MNT_VNODE_FOREACH(vp, mp, mvp) { + update = 0; + + if (mp->mnt_syncer == vp) + continue; + if (VOP_ISLOCKED(vp)) + continue; + + VI_LOCK(vp); + MNT_IUNLOCK(mp); + if (vp->v_iflag & VI_DOOMED) { + VI_UNLOCK(vp); + MNT_ILOCK(mp); + continue; + } + + if ((error = vget(vp, lockreq, td)) != 0) { + MNT_ILOCK(mp); + continue; + } + + if (vp->v_iflag & VI_DOOMED) { + vput(vp); + MNT_ILOCK(mp); + continue; + } + + nandfs_node = VTON(vp); + if (nandfs_node->nn_flags & IN_MODIFIED) { + nandfs_node->nn_flags &= ~(IN_MODIFIED); + update = 1; + } + + if (vp->v_bufobj.bo_dirty.bv_cnt) { + error = nandfs_iterate_dirty_buf(vp, seginfo, 0); + if (error) { + nandfs_error("%s: cannot iterate vnode:%p " + "err:%d\n", __func__, vp, error); + vput(vp); + return (error); + } + update = 1; + } else + vput(vp); + + if (update) + nandfs_node_update(nandfs_node); + + MNT_ILOCK(mp); + } + + MNT_IUNLOCK(mp); + + return (0); +} + +static int +nandfs_update_phys_block(struct nandfs_device *fsdev, struct buf *bp, + uint64_t phys_blknr, union nandfs_binfo *binfo) +{ + struct nandfs_node *node, *dat; + struct vnode *vp; + uint64_t new_blknr; + int error; + + vp = bp->b_vp; + node = VTON(vp); + new_blknr = nandfs_vblk_get(bp); + dat = fsdev->nd_dat_node; + + DPRINTF(BMAP, ("%s: ino %#jx lblk %#jx: vblk %#jx -> %#jx\n", + __func__, (uintmax_t)node->nn_ino, (uintmax_t)bp->b_lblkno, + (uintmax_t)new_blknr, (uintmax_t)phys_blknr)); + + if (node->nn_ino != NANDFS_DAT_INO) { + KASSERT((new_blknr != 0), ("vblk for bp %p is 0", bp)); + + nandfs_vblock_assign(fsdev, new_blknr, phys_blknr); + binfo->bi_v.bi_vblocknr = new_blknr; + binfo->bi_v.bi_blkoff = bp->b_lblkno; + binfo->bi_v.bi_ino = node->nn_ino; + } else { + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + error = nandfs_bmap_update_block(node, bp, phys_blknr); + if (error) { + nandfs_error("%s: error updating block:%jx for bp:%p\n", + __func__, (uintmax_t)phys_blknr, bp); + VOP_UNLOCK(NTOV(dat), 0); + return (error); + } + VOP_UNLOCK(NTOV(dat), 0); + binfo->bi_dat.bi_blkoff = bp->b_lblkno; + binfo->bi_dat.bi_ino = node->nn_ino; + if (NANDFS_IS_INDIRECT(bp)) + binfo->bi_dat.bi_level = 1; + else + binfo->bi_dat.bi_level = 0; + } + + return (0); +} + +#define NBINFO(off) ((off) + sizeof(union nandfs_binfo)) +static int +nandfs_segment_assign_pblk(struct nandfs_segment *nfsseg) +{ + struct nandfs_device *fsdev; + union nandfs_binfo *binfo; + struct buf *bp, *seg_bp; + uint64_t blocknr; + uint32_t curr_off, blocksize; + int error; + + fsdev = nfsseg->fsdev; + blocksize = fsdev->nd_blocksize; + + blocknr = nfsseg->start_block + nfsseg->segsum_blocks; + seg_bp = TAILQ_FIRST(&nfsseg->segsum); + DPRINTF(SYNC, ("%s: seg:%p segsum bp:%p data:%p\n", + __func__, nfsseg, seg_bp, seg_bp->b_data)); + + binfo = (union nandfs_binfo *)(seg_bp->b_data + + sizeof(struct nandfs_segment_summary)); + curr_off = sizeof(struct nandfs_segment_summary); + + TAILQ_FOREACH(bp, &nfsseg->data, b_cluster.cluster_entry) { + KASSERT((bp->b_vp), ("bp %p has not vp", bp)); + + DPRINTF(BMAP, ("\n\n%s: assign buf %p for ino %#jx next %p\n", + __func__, bp, (uintmax_t)VTON(bp->b_vp)->nn_ino, + TAILQ_NEXT(bp, b_cluster.cluster_entry))); + + if (NBINFO(curr_off) > blocksize) { + seg_bp = TAILQ_NEXT(seg_bp, b_cluster.cluster_entry); + binfo = (union nandfs_binfo *)seg_bp->b_data; + curr_off = 0; + DPRINTF(SYNC, ("%s: next segsum %p data %p\n", + __func__, seg_bp, seg_bp->b_data)); + } + + error = nandfs_update_phys_block(fsdev, bp, blocknr, binfo); + if (error) { + nandfs_error("%s: err:%d when updatinng phys block:%jx" + " for bp:%p and binfo:%p\n", __func__, error, + (uintmax_t)blocknr, bp, binfo); + return (error); + } + binfo++; + curr_off = NBINFO(curr_off); + + blocknr++; + } + + return (0); +} + +static int +nandfs_seginfo_assign_pblk(struct nandfs_seginfo *seginfo) +{ + struct nandfs_segment *nfsseg; + int error = 0; + + LIST_FOREACH(nfsseg, &seginfo->seg_list, seg_link) { + error = nandfs_segment_assign_pblk(nfsseg); + if (error) + break; + } + + return (error); +} + +static struct nandfs_segment_summary * +nandfs_fill_segsum(struct nandfs_segment *seg, int has_sr) +{ + struct nandfs_segment_summary *ss; + struct nandfs_device *fsdev; + struct buf *bp; + uint32_t rest, segsum_size, blocksize, crc_calc; + uint16_t flags; + uint8_t *crc_area, crc_skip; + + DPRINTF(SYNC, ("%s: seg %#jx nblocks %#x sumbytes %#x\n", + __func__, (uintmax_t) seg->seg_num, + seg->nblocks + seg->segsum_blocks, + seg->segsum_bytes)); + + fsdev = seg->fsdev; + + flags = NANDFS_SS_LOGBGN | NANDFS_SS_LOGEND; + if (has_sr) + flags |= NANDFS_SS_SR; + + bp = TAILQ_FIRST(&seg->segsum); + ss = (struct nandfs_segment_summary *) bp->b_data; + ss->ss_magic = NANDFS_SEGSUM_MAGIC; + ss->ss_bytes = sizeof(struct nandfs_segment_summary); + ss->ss_flags = flags; + ss->ss_seq = ++(fsdev->nd_seg_sequence); + ss->ss_create = fsdev->nd_ts.tv_sec; + nandfs_get_segment_range(fsdev, seg->seg_next, &ss->ss_next, NULL); + ss->ss_nblocks = seg->nblocks + seg->segsum_blocks; + ss->ss_nbinfos = seg->nbinfos; + ss->ss_sumbytes = seg->segsum_bytes; + + crc_skip = sizeof(ss->ss_datasum) + sizeof(ss->ss_sumsum); + blocksize = seg->fsdev->nd_blocksize; + + segsum_size = seg->segsum_bytes - crc_skip; + rest = min(seg->segsum_bytes, blocksize) - crc_skip; + crc_area = (uint8_t *)ss + crc_skip; + crc_calc = ~0U; + while (segsum_size > 0) { + crc_calc = crc32_raw(crc_area, rest, crc_calc); + segsum_size -= rest; + if (!segsum_size) + break; + bp = TAILQ_NEXT(bp, b_cluster.cluster_entry); + crc_area = (uint8_t *)bp->b_data; + rest = segsum_size <= blocksize ? segsum_size : blocksize; + } + ss->ss_sumsum = crc_calc ^ ~0U; + + return (ss); + +} + +static int +nandfs_save_buf(struct buf *bp, uint64_t blocknr, struct nandfs_device *fsdev) +{ + struct bufobj *bo; + int error; + + bo = &fsdev->nd_devvp->v_bufobj; + + bp->b_blkno = nandfs_block_to_dblock(fsdev, blocknr); + bp->b_iooffset = dbtob(bp->b_blkno); + + KASSERT(bp->b_bufobj != NULL, ("no bufobj for %p", bp)); + if (bp->b_bufobj != bo) { + BO_LOCK(bp->b_bufobj); + BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, + BO_MTX(bp->b_bufobj)); + KASSERT(BUF_ISLOCKED(bp), ("Problem with locking buffer")); + } + + DPRINTF(SYNC, ("%s: buf: %p offset %#jx blk %#jx size %#x\n", + __func__, bp, (uintmax_t)bp->b_offset, (uintmax_t)blocknr, + fsdev->nd_blocksize)); + + NANDFS_UNGATHER(bp); + nandfs_buf_clear(bp, 0xffffffff); + bp->b_flags &= ~(B_ASYNC|B_INVAL|B_MANAGED); + error = bwrite(bp); + if (error) { + nandfs_error("%s: error:%d when writing buffer:%p\n", + __func__, error, bp); + return (error); + } + return (error); +} + +static void +nandfs_clean_buf(struct nandfs_device *fsdev, struct buf *bp) +{ + + DPRINTF(SYNC, ("%s: buf: %p\n", __func__, bp)); + + NANDFS_UNGATHER(bp); + nandfs_buf_clear(bp, 0xffffffff); + bp->b_flags &= ~(B_ASYNC|B_INVAL|B_MANAGED); + nandfs_undirty_buf_fsdev(fsdev, bp); +} + +static void +nandfs_clean_segblocks(struct nandfs_segment *seg, uint8_t unlock) +{ + struct nandfs_device *fsdev = seg->fsdev; + struct nandfs_segment *next_seg; + struct buf *bp, *tbp, *next_bp; + struct vnode *vp, *next_vp; + + VOP_LOCK(fsdev->nd_devvp, LK_EXCLUSIVE); + TAILQ_FOREACH_SAFE(bp, &seg->segsum, b_cluster.cluster_entry, tbp) { + TAILQ_REMOVE(&seg->segsum, bp, b_cluster.cluster_entry); + nandfs_clean_buf(fsdev, bp); + }; + + TAILQ_FOREACH_SAFE(bp, &seg->data, b_cluster.cluster_entry, tbp) { + TAILQ_REMOVE(&seg->data, bp, b_cluster.cluster_entry); + + /* + * If bp is not super-root and vnode is not currently + * locked lock it. + */ + vp = bp->b_vp; + next_vp = NULL; + next_bp = TAILQ_NEXT(bp, b_cluster.cluster_entry); + if (!next_bp) { + next_seg = LIST_NEXT(seg, seg_link); + if (next_seg) + next_bp = TAILQ_FIRST(&next_seg->data); + } + + if (next_bp) + next_vp = next_bp->b_vp; + + nandfs_clean_buf(fsdev, bp); + + if (unlock && vp != NULL && next_vp != vp && + !NANDFS_SYS_NODE(VTON(vp)->nn_ino)) + vput(vp); + + nandfs_dirty_bufs_decrement(fsdev); + } + + VOP_UNLOCK(fsdev->nd_devvp, 0); +} + +static int +nandfs_save_segblocks(struct nandfs_segment *seg, uint8_t unlock) +{ + struct nandfs_device *fsdev = seg->fsdev; + struct nandfs_segment *next_seg; + struct buf *bp, *tbp, *next_bp; + struct vnode *vp, *next_vp; + uint64_t blocknr; + uint32_t i = 0; + int error = 0; + + VOP_LOCK(fsdev->nd_devvp, LK_EXCLUSIVE); + TAILQ_FOREACH_SAFE(bp, &seg->segsum, b_cluster.cluster_entry, tbp) { + TAILQ_REMOVE(&seg->segsum, bp, b_cluster.cluster_entry); + blocknr = seg->start_block + i; + error = nandfs_save_buf(bp, blocknr, fsdev); + if (error) { + nandfs_error("%s: error saving buf: %p blocknr:%jx\n", + __func__, bp, (uintmax_t)blocknr); + goto out; + } + i++; + }; + + i = 0; + TAILQ_FOREACH_SAFE(bp, &seg->data, b_cluster.cluster_entry, tbp) { + TAILQ_REMOVE(&seg->data, bp, b_cluster.cluster_entry); + + blocknr = seg->start_block + seg->segsum_blocks + i; + /* + * If bp is not super-root and vnode is not currently + * locked lock it. + */ + vp = bp->b_vp; + next_vp = NULL; + next_bp = TAILQ_NEXT(bp, b_cluster.cluster_entry); + if (!next_bp) { + next_seg = LIST_NEXT(seg, seg_link); + if (next_seg) + next_bp = TAILQ_FIRST(&next_seg->data); + } + + if (next_bp) + next_vp = next_bp->b_vp; + + error = nandfs_save_buf(bp, blocknr, fsdev); + if (error) { + nandfs_error("%s: error saving buf: %p blknr: %jx\n", + __func__, bp, (uintmax_t)blocknr); + if (unlock && vp != NULL && next_vp != vp && + !NANDFS_SYS_NODE(VTON(vp)->nn_ino)) + vput(vp); + goto out; + } + + if (unlock && vp != NULL && next_vp != vp && + !NANDFS_SYS_NODE(VTON(vp)->nn_ino)) + vput(vp); + + i++; + nandfs_dirty_bufs_decrement(fsdev); + } +out: + if (error) { + nandfs_clean_segblocks(seg, unlock); + VOP_UNLOCK(fsdev->nd_devvp, 0); + return (error); + } + + VOP_UNLOCK(fsdev->nd_devvp, 0); + return (error); +} + + +static void +clean_seginfo(struct nandfs_seginfo *seginfo, uint8_t unlock) +{ + struct nandfs_segment *seg; + + DPRINTF(SYNC, ("%s: seginfo %p\n", __func__, seginfo)); + + LIST_FOREACH(seg, &seginfo->seg_list, seg_link) { + nandfs_clean_segblocks(seg, unlock); + } +} + +static int +save_seginfo(struct nandfs_seginfo *seginfo, uint8_t unlock) +{ + struct nandfs_segment *seg; + struct nandfs_device *fsdev; + struct nandfs_segment_summary *ss; + int error = 0; + + fsdev = seginfo->fsdev; + + DPRINTF(SYNC, ("%s: seginfo %p\n", __func__, seginfo)); + + LIST_FOREACH(seg, &seginfo->seg_list, seg_link) { + if (LIST_NEXT(seg, seg_link)) { + nandfs_fill_segsum(seg, 0); + error = nandfs_save_segblocks(seg, unlock); + if (error) { + nandfs_error("%s: error:%d saving seg:%p\n", + __func__, error, seg); + goto out; + } + } else { + ss = nandfs_fill_segsum(seg, 1); + fsdev->nd_last_segsum = *ss; + error = nandfs_save_segblocks(seg, unlock); + if (error) { + nandfs_error("%s: error:%d saving seg:%p\n", + __func__, error, seg); + goto out; + } + fsdev->nd_last_cno++; + fsdev->nd_last_pseg = seg->start_block; + } + } +out: + if (error) + clean_seginfo(seginfo, unlock); + return (error); +} + +static void +nandfs_invalidate_bufs(struct nandfs_device *fsdev, uint64_t segno) +{ + uint64_t start, end; + struct buf *bp, *tbd; + struct bufobj *bo; + + nandfs_get_segment_range(fsdev, segno, &start, &end); + + bo = &NTOV(fsdev->nd_gc_node)->v_bufobj; + + BO_LOCK(bo); +restart_locked_gc: + TAILQ_FOREACH_SAFE(bp, &bo->bo_clean.bv_hd, b_bobufs, tbd) { + if (!(bp->b_lblkno >= start && bp->b_lblkno <= end)) + continue; + + if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) + goto restart_locked_gc; + + bremfree(bp); + bp->b_flags |= (B_INVAL | B_RELBUF); + bp->b_flags &= ~(B_ASYNC | B_MANAGED); + BO_UNLOCK(bo); + brelse(bp); + BO_LOCK(bo); + } + BO_UNLOCK(bo); +} + +/* Process segments marks to free by cleaner */ +static void +nandfs_process_segments(struct nandfs_device *fsdev) +{ + uint64_t saved_segment; + int i; + + if (fsdev->nd_free_base) { + saved_segment = nandfs_get_segnum_of_block(fsdev, + fsdev->nd_super.s_last_pseg); + for (i = 0; i < fsdev->nd_free_count; i++) { + if (fsdev->nd_free_base[i] == NANDFS_NOSEGMENT) + continue; + /* Update superblock if clearing segment point by it */ + if (fsdev->nd_free_base[i] == saved_segment) { + nandfs_write_superblock(fsdev); + saved_segment = nandfs_get_segnum_of_block( + fsdev, fsdev->nd_super.s_last_pseg); + } + nandfs_invalidate_bufs(fsdev, fsdev->nd_free_base[i]); + nandfs_clear_segment(fsdev, fsdev->nd_free_base[i]); + } + + free(fsdev->nd_free_base, M_NANDFSTEMP); + fsdev->nd_free_base = NULL; + fsdev->nd_free_count = 0; + } +} + +/* Collect and write dirty buffers */ +int +nandfs_sync_file(struct vnode *vp) +{ + struct nandfs_device *fsdev; + struct nandfs_node *nandfs_node; + struct nandfsmount *nmp; + struct nandfs_node *dat, *su, *ifile, *cp; + struct nandfs_seginfo *seginfo = NULL; + struct nandfs_segment *seg; + int update, error; + int cno_changed; + + ASSERT_VOP_LOCKED(vp, __func__); + DPRINTF(SYNC, ("%s: START\n", __func__)); + + error = 0; + nmp = VFSTONANDFS(vp->v_mount); + fsdev = nmp->nm_nandfsdev; + + dat = fsdev->nd_dat_node; + su = fsdev->nd_su_node; + cp = fsdev->nd_cp_node; + ifile = nmp->nm_ifile_node; + + NANDFS_WRITEASSERT(fsdev); + if (lockmgr(&fsdev->nd_seg_const, LK_UPGRADE, NULL) != 0) { + DPRINTF(SYNC, ("%s: lost shared lock\n", __func__)); + if (lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL) != 0) + panic("couldn't lock exclusive"); + } + DPRINTF(SYNC, ("%s: got lock\n", __func__)); + + VOP_LOCK(NTOV(su), LK_EXCLUSIVE); + create_seginfo(fsdev, &seginfo); + + update = 0; + + nandfs_node = VTON(vp); + if (nandfs_node->nn_flags & IN_MODIFIED) { + nandfs_node->nn_flags &= ~(IN_MODIFIED); + update = 1; + } + + if (vp->v_bufobj.bo_dirty.bv_cnt) { + error = nandfs_iterate_dirty_buf(vp, seginfo, 0); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + VOP_UNLOCK(NTOV(su), 0); + lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL); + nandfs_error("%s: err:%d iterating dirty bufs vp:%p", + __func__, error, vp); + return (error); + } + update = 1; + } + + if (update) { + VOP_LOCK(NTOV(ifile), LK_EXCLUSIVE); + error = nandfs_node_update(nandfs_node); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + VOP_UNLOCK(NTOV(ifile), 0); + VOP_UNLOCK(NTOV(su), 0); + lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL); + nandfs_error("%s: err:%d updating vp:%p", + __func__, error, vp); + return (error); + } + VOP_UNLOCK(NTOV(ifile), 0); + } + + cno_changed = 0; + if (seginfo->blocks) { + VOP_LOCK(NTOV(cp), LK_EXCLUSIVE); + cno_changed = 1; + /* Create new checkpoint */ + error = nandfs_get_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + VOP_UNLOCK(NTOV(cp), 0); + VOP_UNLOCK(NTOV(su), 0); + lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL); + nandfs_error("%s: err:%d getting cp:%jx", + __func__, error, fsdev->nd_last_cno + 1); + return (error); + } + + /* Reiterate all blocks and assign physical block number */ + nandfs_seginfo_assign_pblk(seginfo); + + /* Fill checkpoint data */ + error = nandfs_set_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1, + &ifile->nn_inode, seginfo->blocks); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + VOP_UNLOCK(NTOV(cp), 0); + VOP_UNLOCK(NTOV(su), 0); + lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL); + nandfs_error("%s: err:%d setting cp:%jx", + __func__, error, fsdev->nd_last_cno + 1); + return (error); + } + + VOP_UNLOCK(NTOV(cp), 0); + LIST_FOREACH(seg, &seginfo->seg_list, seg_link) + nandfs_update_segment(fsdev, seg->seg_num, + seg->nblocks + seg->segsum_blocks); + + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + error = save_seginfo(seginfo, 0); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + VOP_UNLOCK(NTOV(dat), 0); + VOP_UNLOCK(NTOV(su), 0); + lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL); + nandfs_error("%s: err:%d updating seg", + __func__, error); + return (error); + } + VOP_UNLOCK(NTOV(dat), 0); + } + + VOP_UNLOCK(NTOV(su), 0); + + delete_seginfo(seginfo); + lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL); + + if (cno_changed && !error) { + if (nandfs_cps_between_sblocks != 0 && + fsdev->nd_last_cno % nandfs_cps_between_sblocks == 0) + nandfs_write_superblock(fsdev); + } + + ASSERT_VOP_LOCKED(vp, __func__); + DPRINTF(SYNC, ("%s: END error %d\n", __func__, error)); + return (error); +} + +int +nandfs_segment_constructor(struct nandfsmount *nmp, int flags) +{ + struct nandfs_device *fsdev; + struct nandfs_seginfo *seginfo = NULL; + struct nandfs_segment *seg; + struct nandfs_node *dat, *su, *ifile, *cp, *gc; + int cno_changed, error; + + DPRINTF(SYNC, ("%s: START\n", __func__)); + fsdev = nmp->nm_nandfsdev; + + lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL); + DPRINTF(SYNC, ("%s: git lock\n", __func__)); +again: + create_seginfo(fsdev, &seginfo); + + dat = fsdev->nd_dat_node; + su = fsdev->nd_su_node; + cp = fsdev->nd_cp_node; + gc = fsdev->nd_gc_node; + ifile = nmp->nm_ifile_node; + + VOP_LOCK(NTOV(su), LK_EXCLUSIVE); + VOP_LOCK(NTOV(ifile), LK_EXCLUSIVE); + VOP_LOCK(NTOV(gc), LK_EXCLUSIVE); + VOP_LOCK(NTOV(cp), LK_EXCLUSIVE); + + nandfs_iterate_system_vnode(gc, seginfo); + nandfs_iterate_dirty_vnodes(nmp->nm_vfs_mountp, seginfo); + nandfs_iterate_system_vnode(ifile, seginfo); + nandfs_iterate_system_vnode(su, seginfo); + + cno_changed = 0; + if (seginfo->blocks || flags) { + cno_changed = 1; + /* Create new checkpoint */ + error = nandfs_get_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + goto error_locks; + } + + /* Collect blocks from system files */ + nandfs_iterate_system_vnode(cp, seginfo); + nandfs_iterate_system_vnode(su, seginfo); + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + nandfs_iterate_system_vnode(dat, seginfo); + VOP_UNLOCK(NTOV(dat), 0); +reiterate: + seginfo->reiterate = 0; + nandfs_iterate_system_vnode(su, seginfo); + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + nandfs_iterate_system_vnode(dat, seginfo); + VOP_UNLOCK(NTOV(dat), 0); + if (seginfo->reiterate) + goto reiterate; + if (!(seginfo->curseg) || !seginfo->curseg->num_blocks) { + error = create_segment(seginfo); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + goto error_locks; + } + goto reiterate; + } + + /* Reiterate all blocks and assign physical block number */ + nandfs_seginfo_assign_pblk(seginfo); + + /* Fill superroot */ + error = nandfs_add_superroot(seginfo); + if (error) { + clean_seginfo(seginfo, 0); + delete_seginfo(seginfo); + goto error_locks; + } + KASSERT(!(seginfo->reiterate), ("reiteration after superroot")); + + /* Fill checkpoint data */ + nandfs_set_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1, + &ifile->nn_inode, seginfo->blocks); + + LIST_FOREACH(seg, &seginfo->seg_list, seg_link) + nandfs_update_segment(fsdev, seg->seg_num, + seg->nblocks + seg->segsum_blocks); + + VOP_LOCK(NTOV(dat), LK_EXCLUSIVE); + error = save_seginfo(seginfo, 1); + if (error) { + clean_seginfo(seginfo, 1); + delete_seginfo(seginfo); + goto error_dat; + } + VOP_UNLOCK(NTOV(dat), 0); + } + + VOP_UNLOCK(NTOV(cp), 0); + VOP_UNLOCK(NTOV(gc), 0); + VOP_UNLOCK(NTOV(ifile), 0); + + nandfs_process_segments(fsdev); + + VOP_UNLOCK(NTOV(su), 0); + + delete_seginfo(seginfo); + + /* + * XXX: a hack, will go away soon + */ + if ((NTOV(dat)->v_bufobj.bo_dirty.bv_cnt != 0 || + NTOV(cp)->v_bufobj.bo_dirty.bv_cnt != 0 || + NTOV(gc)->v_bufobj.bo_dirty.bv_cnt != 0 || + NTOV(ifile)->v_bufobj.bo_dirty.bv_cnt != 0 || + NTOV(su)->v_bufobj.bo_dirty.bv_cnt != 0) && + (flags & NANDFS_UMOUNT)) { + DPRINTF(SYNC, ("%s: RERUN\n", __func__)); + goto again; + } + + MPASS(fsdev->nd_free_base == NULL); + + lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL); + + if (cno_changed) { + if ((nandfs_cps_between_sblocks != 0 && + fsdev->nd_last_cno % nandfs_cps_between_sblocks == 0) || + flags & NANDFS_UMOUNT) + nandfs_write_superblock(fsdev); + } + + DPRINTF(SYNC, ("%s: END\n", __func__)); + return (0); +error_dat: + VOP_UNLOCK(NTOV(dat), 0); +error_locks: + VOP_UNLOCK(NTOV(cp), 0); + VOP_UNLOCK(NTOV(gc), 0); + VOP_UNLOCK(NTOV(ifile), 0); + VOP_UNLOCK(NTOV(su), 0); + lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL); + + return (error); +} + +#ifdef DDB +/* + * Show details about the given NANDFS mount point. + */ +DB_SHOW_COMMAND(nandfs, db_show_nandfs) +{ + struct mount *mp; + struct nandfs_device *nffsdev; + struct nandfs_segment *seg; + struct nandfsmount *nmp; + struct buf *bp; + struct vnode *vp; + + if (!have_addr) { + db_printf("\nUsage: show nandfs \n"); + return; + } + + mp = (struct mount *)addr; + db_printf("%p %s on %s (%s)\n", mp, mp->mnt_stat.f_mntfromname, + mp->mnt_stat.f_mntonname, mp->mnt_stat.f_fstypename); + + + nmp = (struct nandfsmount *)(mp->mnt_data); + nffsdev = nmp->nm_nandfsdev; + db_printf("dev vnode:%p\n", nffsdev->nd_devvp); + db_printf("blocksize:%jx last cno:%jx last pseg:%jx seg num:%jx\n", + (uintmax_t)nffsdev->nd_blocksize, (uintmax_t)nffsdev->nd_last_cno, + (uintmax_t)nffsdev->nd_last_pseg, (uintmax_t)nffsdev->nd_seg_num); + db_printf("system nodes: dat:%p cp:%p su:%p ifile:%p gc:%p\n", + nffsdev->nd_dat_node, nffsdev->nd_cp_node, nffsdev->nd_su_node, + nmp->nm_ifile_node, nffsdev->nd_gc_node); + + if (nffsdev->nd_seginfo != NULL) { + LIST_FOREACH(seg, &nffsdev->nd_seginfo->seg_list, seg_link) { + db_printf("seg: %p\n", seg); + TAILQ_FOREACH(bp, &seg->segsum, + b_cluster.cluster_entry) + db_printf("segbp %p\n", bp); + TAILQ_FOREACH(bp, &seg->data, + b_cluster.cluster_entry) { + vp = bp->b_vp; + db_printf("bp:%p bp->b_vp:%p ino:%jx\n", bp, vp, + (uintmax_t)(vp ? VTON(vp)->nn_ino : 0)); + } + } + } +} +#endif diff --git a/sys/fs/nandfs/nandfs_subr.c b/sys/fs/nandfs/nandfs_subr.c new file mode 100644 index 0000000..b485422 --- /dev/null +++ b/sys/fs/nandfs/nandfs_subr.c @@ -0,0 +1,1120 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include "nandfs_mount.h" +#include "nandfs.h" +#include "nandfs_subr.h" + +MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount");; +MALLOC_DEFINE(M_NANDFSTEMP, "nandfs_tmt", "NANDFS tmp"); + +uma_zone_t nandfs_node_zone; + +void nandfs_bdflush(struct bufobj *bo, struct buf *bp); +int nandfs_bufsync(struct bufobj *bo, int waitfor); + +struct buf_ops buf_ops_nandfs = { + .bop_name = "buf_ops_nandfs", + .bop_write = bufwrite, + .bop_strategy = bufstrategy, + .bop_sync = nandfs_bufsync, + .bop_bdflush = nandfs_bdflush, +}; + +int +nandfs_bufsync(struct bufobj *bo, int waitfor) +{ + struct vnode *vp; + int error = 0; + + vp = bo->__bo_vnode; + + ASSERT_VOP_LOCKED(vp, __func__); + error = nandfs_sync_file(vp); + if (error) + nandfs_warning("%s: cannot flush buffers err:%d\n", + __func__, error); + + return (error); +} + +void +nandfs_bdflush(bo, bp) + struct bufobj *bo; + struct buf *bp; +{ + struct vnode *vp; + int error; + + if (bo->bo_dirty.bv_cnt <= ((dirtybufthresh * 8) / 10)) + return; + + vp = bp->b_vp; + if (NANDFS_SYS_NODE(VTON(vp)->nn_ino)) + return; + + if (NANDFS_IS_INDIRECT(bp)) + return; + + error = nandfs_sync_file(vp); + if (error) + nandfs_warning("%s: cannot flush buffers err:%d\n", + __func__, error); +} + +int +nandfs_init(struct vfsconf *vfsp) +{ + + nandfs_node_zone = uma_zcreate("nandfs node zone", + sizeof(struct nandfs_node), NULL, NULL, NULL, NULL, 0, 0); + + return (0); +} + +int +nandfs_uninit(struct vfsconf *vfsp) +{ + + uma_zdestroy(nandfs_node_zone); + return (0); +} + +/* Basic calculators */ +uint64_t +nandfs_get_segnum_of_block(struct nandfs_device *nandfsdev, + nandfs_daddr_t blocknr) +{ + uint64_t segnum, blks_per_seg; + + MPASS(blocknr >= nandfsdev->nd_fsdata.f_first_data_block); + + blks_per_seg = nandfsdev->nd_fsdata.f_blocks_per_segment; + + segnum = blocknr / blks_per_seg; + segnum -= nandfsdev->nd_fsdata.f_first_data_block / blks_per_seg; + + DPRINTF(SYNC, ("%s: returning blocknr %jx -> segnum %jx\n", __func__, + blocknr, segnum)); + + return (segnum); +} + +void +nandfs_get_segment_range(struct nandfs_device *nandfsdev, uint64_t segnum, + uint64_t *seg_start, uint64_t *seg_end) +{ + uint64_t blks_per_seg; + + blks_per_seg = nandfsdev->nd_fsdata.f_blocks_per_segment; + *seg_start = nandfsdev->nd_fsdata.f_first_data_block + + blks_per_seg * segnum; + if (seg_end != NULL) + *seg_end = *seg_start + blks_per_seg -1; +} + +void nandfs_calc_mdt_consts(struct nandfs_device *nandfsdev, + struct nandfs_mdt *mdt, int entry_size) +{ + uint32_t blocksize = nandfsdev->nd_blocksize; + + mdt->entries_per_group = blocksize * 8; + mdt->entries_per_block = blocksize / entry_size; + + mdt->blocks_per_group = + (mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1; + mdt->groups_per_desc_block = + blocksize / sizeof(struct nandfs_block_group_desc); + mdt->blocks_per_desc_block = + mdt->groups_per_desc_block * mdt->blocks_per_group + 1; +} + +int +nandfs_dev_bread(struct nandfs_device *nandfsdev, nandfs_lbn_t blocknr, + struct ucred *cred, int flags, struct buf **bpp) +{ + int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE; + int error; + + DPRINTF(BLOCK, ("%s: read from block %jx vp %p\n", __func__, + blocknr * blk2dev, nandfsdev->nd_devvp)); + error = bread(nandfsdev->nd_devvp, blocknr * blk2dev, + nandfsdev->nd_blocksize, NOCRED, bpp); + if (error) + nandfs_error("%s: cannot read from device - blk:%jx\n", + __func__, blocknr); + return (error); +} + +/* Read on a node */ +int +nandfs_bread(struct nandfs_node *node, nandfs_lbn_t blocknr, + struct ucred *cred, int flags, struct buf **bpp) +{ + nandfs_daddr_t vblk; + int error; + + DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node), + blocknr)); + + error = bread(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize, + cred, bpp); + + KASSERT(error == 0, ("%s: vp:%p lbn:%#jx err:%d\n", __func__, + NTOV(node), blocknr, error)); + + if (!nandfs_vblk_get(*bpp) && + ((*bpp)->b_flags & B_CACHE) && node->nn_ino != NANDFS_DAT_INO) { + nandfs_bmap_lookup(node, blocknr, &vblk); + nandfs_vblk_set(*bpp, vblk); + } + return (error); +} + +int +nandfs_bread_meta(struct nandfs_node *node, nandfs_lbn_t blocknr, + struct ucred *cred, int flags, struct buf **bpp) +{ + nandfs_daddr_t vblk; + int error; + + DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node), + blocknr)); + + error = bread(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize, + cred, bpp); + + KASSERT(error == 0, ("%s: vp:%p lbn:%#jx err:%d\n", __func__, + NTOV(node), blocknr, error)); + + if (!nandfs_vblk_get(*bpp) && + ((*bpp)->b_flags & B_CACHE) && node->nn_ino != NANDFS_DAT_INO) { + nandfs_bmap_lookup(node, blocknr, &vblk); + nandfs_vblk_set(*bpp, vblk); + } + + return (error); +} + +int +nandfs_bdestroy(struct nandfs_node *node, nandfs_daddr_t vblk) +{ + int error; + + if (!NANDFS_SYS_NODE(node->nn_ino)) + NANDFS_WRITEASSERT(node->nn_nandfsdev); + + error = nandfs_vblock_end(node->nn_nandfsdev, vblk); + if (error) { + nandfs_error("%s: ending vblk: %jx failed\n", + __func__, (uintmax_t)vblk); + return (error); + } + node->nn_inode.i_blocks--; + + return (0); +} + +int +nandfs_bcreate(struct nandfs_node *node, nandfs_lbn_t blocknr, + struct ucred *cred, int flags, struct buf **bpp) +{ + int error; + + ASSERT_VOP_LOCKED(NTOV(node), __func__); + if (!NANDFS_SYS_NODE(node->nn_ino)) + NANDFS_WRITEASSERT(node->nn_nandfsdev); + + DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node), + blocknr)); + + *bpp = getblk(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize, + 0, 0, 0); + + KASSERT((*bpp), ("%s: vp:%p lbn:%#jx\n", __func__, + NTOV(node), blocknr)); + + if (*bpp) { + vfs_bio_clrbuf(*bpp); + (*bpp)->b_blkno = ~(0); /* To avoid VOP_BMAP in bdwrite */ + error = nandfs_bmap_insert_block(node, blocknr, *bpp); + if (error) { + nandfs_warning("%s: failed bmap insert node:%p" + " blk:%jx\n", __func__, node, blocknr); + brelse(*bpp); + return (error); + } + node->nn_inode.i_blocks++; + + return (0); + } + + return (-1); +} + +int +nandfs_bcreate_meta(struct nandfs_node *node, nandfs_lbn_t blocknr, + struct ucred *cred, int flags, struct buf **bpp) +{ + struct nandfs_device *fsdev; + nandfs_daddr_t vblk; + int error; + + ASSERT_VOP_LOCKED(NTOV(node), __func__); + NANDFS_WRITEASSERT(node->nn_nandfsdev); + + DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node), + blocknr)); + + fsdev = node->nn_nandfsdev; + + *bpp = getblk(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize, + 0, 0, 0); + + KASSERT((*bpp), ("%s: vp:%p lbn:%#jx\n", __func__, + NTOV(node), blocknr)); + + memset((*bpp)->b_data, 0, fsdev->nd_blocksize); + + vfs_bio_clrbuf(*bpp); + (*bpp)->b_blkno = ~(0); /* To avoid VOP_BMAP in bdwrite */ + + nandfs_buf_set(*bpp, NANDFS_VBLK_ASSIGNED); + + if (node->nn_ino != NANDFS_DAT_INO) { + error = nandfs_vblock_alloc(fsdev, &vblk); + if (error) { + nandfs_buf_clear(*bpp, NANDFS_VBLK_ASSIGNED); + brelse(*bpp); + return (error); + } + } else + vblk = fsdev->nd_fakevblk++; + + nandfs_vblk_set(*bpp, vblk); + + nandfs_bmap_insert_block(node, blocknr, *bpp); + return (0); +} + +/* Translate index to a file block number and an entry */ +void +nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index, + nandfs_lbn_t *blocknr, uint32_t *entry_in_block) +{ + uint64_t blknr; + uint64_t group, group_offset, blocknr_in_group; + uint64_t desc_block, desc_offset; + + /* Calculate our offset in the file */ + group = index / mdt->entries_per_group; + group_offset = index % mdt->entries_per_group; + desc_block = group / mdt->groups_per_desc_block; + desc_offset = group % mdt->groups_per_desc_block; + blocknr_in_group = group_offset / mdt->entries_per_block; + + /* To descgroup offset */ + blknr = 1 + desc_block * mdt->blocks_per_desc_block; + + /* To group offset */ + blknr += desc_offset * mdt->blocks_per_group; + + /* To actual file block */ + blknr += 1 + blocknr_in_group; + + *blocknr = blknr; + *entry_in_block = group_offset % mdt->entries_per_block; +} + +void +nandfs_mdt_trans_blk(struct nandfs_mdt *mdt, uint64_t index, + uint64_t *desc, uint64_t *bitmap, nandfs_lbn_t *blocknr, + uint32_t *entry_in_block) +{ + uint64_t blknr; + uint64_t group, group_offset, blocknr_in_group; + uint64_t desc_block, desc_offset; + + /* Calculate our offset in the file */ + group = index / mdt->entries_per_group; + group_offset = index % mdt->entries_per_group; + desc_block = group / mdt->groups_per_desc_block; + desc_offset = group % mdt->groups_per_desc_block; + blocknr_in_group = group_offset / mdt->entries_per_block; + + /* To descgroup offset */ + *desc = desc_block * mdt->blocks_per_desc_block; + blknr = 1 + desc_block * mdt->blocks_per_desc_block; + + /* To group offset */ + blknr += desc_offset * mdt->blocks_per_group; + *bitmap = blknr; + + /* To actual file block */ + blknr += 1 + blocknr_in_group; + + *blocknr = blknr; + *entry_in_block = group_offset % mdt->entries_per_block; + + DPRINTF(ALLOC, + ("%s: desc_buf: %jx bitmap_buf: %jx entry_buf: %jx entry: %x\n", + __func__, (uintmax_t)*desc, (uintmax_t)*bitmap, + (uintmax_t)*blocknr, *entry_in_block)); +} + +int +nandfs_vtop(struct nandfs_node *node, nandfs_daddr_t vblocknr, + nandfs_daddr_t *pblocknr) +{ + struct nandfs_node *dat_node; + struct nandfs_dat_entry *entry; + struct buf *bp; + nandfs_lbn_t ldatblknr; + uint32_t entry_in_block; + int locked, error; + + if (node->nn_ino == NANDFS_DAT_INO || node->nn_ino == NANDFS_GC_INO) { + *pblocknr = vblocknr; + return (0); + } + + /* only translate valid vblocknrs */ + if (vblocknr == 0) + return (0); + + dat_node = node->nn_nandfsdev->nd_dat_node; + nandfs_mdt_trans(&node->nn_nandfsdev->nd_dat_mdt, vblocknr, &ldatblknr, + &entry_in_block); + + locked = NANDFS_VOP_ISLOCKED(NTOV(dat_node)); + if (!locked) + VOP_LOCK(NTOV(dat_node), LK_SHARED); + error = nandfs_bread(dat_node, ldatblknr, NOCRED, 0, &bp); + if (error) { + DPRINTF(TRANSLATE, ("vtop: can't read in DAT block %#jx!\n", + (uintmax_t)ldatblknr)); + brelse(bp); + VOP_UNLOCK(NTOV(dat_node), 0); + return (error); + } + + /* Get our translation */ + entry = ((struct nandfs_dat_entry *) bp->b_data) + entry_in_block; + DPRINTF(TRANSLATE, ("\tentry %p data %p entry_in_block %x\n", + entry, bp->b_data, entry_in_block)) + DPRINTF(TRANSLATE, ("\tvblk %#jx -> %#jx for cp [%#jx-%#jx]\n", + (uintmax_t)vblocknr, (uintmax_t)entry->de_blocknr, + (uintmax_t)entry->de_start, (uintmax_t)entry->de_end)); + + *pblocknr = entry->de_blocknr; + brelse(bp); + if (!locked) + VOP_UNLOCK(NTOV(dat_node), 0); + + MPASS(*pblocknr >= node->nn_nandfsdev->nd_fsdata.f_first_data_block || + *pblocknr == 0); + + return (0); +} + +int +nandfs_segsum_valid(struct nandfs_segment_summary *segsum) +{ + + return (segsum->ss_magic == NANDFS_SEGSUM_MAGIC); +} + +int +nandfs_load_segsum(struct nandfs_device *fsdev, nandfs_daddr_t blocknr, + struct nandfs_segment_summary *segsum) +{ + struct buf *bp; + int error; + + DPRINTF(VOLUMES, ("nandfs: try segsum at block %jx\n", + (uintmax_t)blocknr)); + + error = nandfs_dev_bread(fsdev, blocknr, NOCRED, 0, &bp); + if (error) + return (error); + + memcpy(segsum, bp->b_data, sizeof(struct nandfs_segment_summary)); + brelse(bp); + + if (!nandfs_segsum_valid(segsum)) { + DPRINTF(VOLUMES, ("%s: bad magic pseg:%jx\n", __func__, + blocknr)); + return (EINVAL); + } + + return (error); +} + +static int +nandfs_load_super_root(struct nandfs_device *nandfsdev, + struct nandfs_segment_summary *segsum, uint64_t pseg) +{ + struct nandfs_super_root super_root; + struct buf *bp; + uint64_t blocknr; + uint32_t super_root_crc, comp_crc; + int off, error; + + /* Check if there is a superroot */ + if ((segsum->ss_flags & NANDFS_SS_SR) == 0) { + DPRINTF(VOLUMES, ("%s: no super root in pseg:%jx\n", __func__, + pseg)); + return (ENOENT); + } + + /* Get our super root, located at the end of the pseg */ + blocknr = pseg + segsum->ss_nblocks - 1; + DPRINTF(VOLUMES, ("%s: try at %#jx\n", __func__, (uintmax_t)blocknr)); + + error = nandfs_dev_bread(nandfsdev, blocknr, NOCRED, 0, &bp); + if (error) + return (error); + + memcpy(&super_root, bp->b_data, sizeof(struct nandfs_super_root)); + brelse(bp); + + /* Check super root CRC */ + super_root_crc = super_root.sr_sum; + off = sizeof(super_root.sr_sum); + comp_crc = crc32((uint8_t *)&super_root + off, + NANDFS_SR_BYTES - off); + + if (super_root_crc != comp_crc) { + DPRINTF(VOLUMES, ("%s: invalid crc:%#x [expect:%#x]\n", + __func__, super_root_crc, comp_crc)); + return (EINVAL); + } + + nandfsdev->nd_super_root = super_root; + DPRINTF(VOLUMES, ("%s: got valid superroot\n", __func__)); + + return (0); +} + +/* + * Search for the last super root recorded. + */ +int +nandfs_search_super_root(struct nandfs_device *nandfsdev) +{ + struct nandfs_super_block *super; + struct nandfs_segment_summary segsum; + uint64_t seg_start, seg_end, cno, seq, create, pseg; + uint64_t segnum; + int error, found; + + error = found = 0; + + /* Search for last super root */ + pseg = nandfsdev->nd_super.s_last_pseg; + segnum = nandfs_get_segnum_of_block(nandfsdev, pseg); + + cno = nandfsdev->nd_super.s_last_cno; + create = seq = 0; + DPRINTF(VOLUMES, ("%s: start in pseg %#jx\n", __func__, + (uintmax_t)pseg)); + + for (;;) { + error = nandfs_load_segsum(nandfsdev, pseg, &segsum); + if (error) + break; + + if (segsum.ss_seq < seq || segsum.ss_create < create) + break; + + /* Try to load super root */ + if (segsum.ss_flags & NANDFS_SS_SR) { + error = nandfs_load_super_root(nandfsdev, &segsum, pseg); + if (error) + break; /* confused */ + found = 1; + + super = &nandfsdev->nd_super; + nandfsdev->nd_last_segsum = segsum; + super->s_last_pseg = pseg; + super->s_last_cno = cno++; + super->s_last_seq = segsum.ss_seq; + super->s_state = NANDFS_VALID_FS; + seq = segsum.ss_seq; + create = segsum.ss_create; + } else { + seq = segsum.ss_seq; + create = segsum.ss_create; + } + + /* Calculate next partial segment location */ + pseg += segsum.ss_nblocks; + DPRINTF(VOLUMES, ("%s: next partial seg is %jx\n", __func__, + (uintmax_t)pseg)); + + /* Did we reach the end of the segment? if so, go to the next */ + nandfs_get_segment_range(nandfsdev, segnum, &seg_start, + &seg_end); + if (pseg >= seg_end) { + pseg = segsum.ss_next; + DPRINTF(VOLUMES, + (" partial seg oor next is %jx[%jx - %jx]\n", + (uintmax_t)pseg, (uintmax_t)seg_start, + (uintmax_t)seg_end)); + } + segnum = nandfs_get_segnum_of_block(nandfsdev, pseg); + } + + if (error && !found) + return (error); + + return (0); +} + +int +nandfs_get_node_raw(struct nandfs_device *nandfsdev, struct nandfsmount *nmp, + uint64_t ino, struct nandfs_inode *inode, struct nandfs_node **nodep) +{ + struct nandfs_node *node; + struct vnode *nvp; + struct mount *mp; + int error; + + *nodep = NULL; + + /* Associate with mountpoint if present */ + if (nmp) { + mp = nmp->nm_vfs_mountp; + error = getnewvnode("nandfs", mp, &nandfs_vnodeops, &nvp); + if (error) { + return (error); + } + } else { + mp = NULL; + error = getnewvnode("snandfs", mp, &nandfs_system_vnodeops, + &nvp); + if (error) { + return (error); + } + } + + if (mp) + NANDFS_WRITELOCK(nandfsdev); + + DPRINTF(IFILE, ("%s: ino: %#jx -> vp: %p\n", + __func__, (uintmax_t)ino, nvp)); + /* Lock node */ + lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL); + + if (mp) { + error = insmntque(nvp, mp); + if (error != 0) { + *nodep = NULL; + return (error); + } + } + + node = uma_zalloc(nandfs_node_zone, M_WAITOK | M_ZERO); + + /* Crosslink */ + node->nn_vnode = nvp; + nvp->v_bufobj.bo_ops = &buf_ops_nandfs; + node->nn_nmp = nmp; + node->nn_nandfsdev = nandfsdev; + nvp->v_data = node; + + /* Initiase NANDFS node */ + node->nn_ino = ino; + if (inode != NULL) + node->nn_inode = *inode; + + nandfs_vinit(nvp, ino); + + /* Return node */ + *nodep = node; + DPRINTF(IFILE, ("%s: ino:%#jx vp:%p node:%p\n", + __func__, (uintmax_t)ino, nvp, *nodep)); + + return (0); +} + +int +nandfs_get_node(struct nandfsmount *nmp, uint64_t ino, + struct nandfs_node **nodep) +{ + struct nandfs_device *nandfsdev; + struct nandfs_inode inode, *entry; + struct vnode *nvp, *vpp; + struct thread *td; + struct buf *bp; + uint64_t ivblocknr; + uint32_t entry_in_block; + int error; + + /* Look up node in hash table */ + td = curthread; + *nodep = NULL; + + if ((ino < NANDFS_ATIME_INO) && (ino != NANDFS_ROOT_INO)) { + printf("nandfs_get_node: system ino %"PRIu64" not in mount " + "point!\n", ino); + return (ENOENT); + } + + error = vfs_hash_get(nmp->nm_vfs_mountp, ino, LK_EXCLUSIVE, td, &nvp, + NULL, NULL); + if (error) + return (error); + + if (nvp != NULL) { + *nodep = (struct nandfs_node *)nvp->v_data; + return (0); + } + + /* Look up inode structure in mountpoints ifile */ + nandfsdev = nmp->nm_nandfsdev; + nandfs_mdt_trans(&nandfsdev->nd_ifile_mdt, ino, &ivblocknr, + &entry_in_block); + + VOP_LOCK(NTOV(nmp->nm_ifile_node), LK_SHARED); + error = nandfs_bread(nmp->nm_ifile_node, ivblocknr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + VOP_UNLOCK(NTOV(nmp->nm_ifile_node), 0); + return (ENOENT); + } + + /* Get inode entry */ + entry = (struct nandfs_inode *) bp->b_data + entry_in_block; + memcpy(&inode, entry, sizeof(struct nandfs_inode)); + brelse(bp); + VOP_UNLOCK(NTOV(nmp->nm_ifile_node), 0); + + /* Get node */ + error = nandfs_get_node_raw(nmp->nm_nandfsdev, nmp, ino, &inode, nodep); + if (error) { + *nodep = NULL; + return (error); + } + + nvp = (*nodep)->nn_vnode; + error = vfs_hash_insert(nvp, ino, 0, td, &vpp, NULL, NULL); + if (error) { + *nodep = NULL; + return (error); + } + + return (error); +} + +void +nandfs_dispose_node(struct nandfs_node **nodep) +{ + struct nandfs_node *node; + struct vnode *vp; + + /* Protect against rogue values */ + node = *nodep; + if (!node) { + return; + } + DPRINTF(NODE, ("nandfs_dispose_node: %p\n", *nodep)); + + vp = NTOV(node); + vp->v_data = NULL; + + /* Free our associated memory */ + uma_zfree(nandfs_node_zone, node); + + *nodep = NULL; +} + +int +nandfs_lookup_name_in_dir(struct vnode *dvp, const char *name, int namelen, + uint64_t *ino, int *found, uint64_t *off) +{ + struct nandfs_node *dir_node = VTON(dvp); + struct nandfs_dir_entry *ndirent; + struct buf *bp; + uint64_t file_size, diroffset, blkoff; + uint64_t blocknr; + uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize; + uint8_t *pos, name_len; + int error; + + *found = 0; + + DPRINTF(VNCALL, ("%s: %s file\n", __func__, name)); + if (dvp->v_type != VDIR) { + return (ENOTDIR); + } + + /* Get directory filesize */ + file_size = dir_node->nn_inode.i_size; + + /* Walk the directory */ + diroffset = 0; + blocknr = 0; + blkoff = 0; + error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (EIO); + } + + while (diroffset < file_size) { + if (blkoff >= blocksize) { + blkoff = 0; blocknr++; + brelse(bp); + error = nandfs_bread(dir_node, blocknr, NOCRED, 0, + &bp); + if (error) { + brelse(bp); + return (EIO); + } + } + + /* Read in one dirent */ + pos = (uint8_t *) bp->b_data + blkoff; + ndirent = (struct nandfs_dir_entry *) pos; + name_len = ndirent->name_len; + + if ((name_len == namelen) && + (strncmp(name, ndirent->name, name_len) == 0) && + (ndirent->inode != 0)) { + *ino = ndirent->inode; + *off = diroffset; + DPRINTF(LOOKUP, ("found `%.*s` with ino %"PRIx64"\n", + name_len, ndirent->name, *ino)); + *found = 1; + break; + } + + /* Advance */ + diroffset += ndirent->rec_len; + blkoff += ndirent->rec_len; + } + brelse(bp); + + return (error); +} + +int +nandfs_get_fsinfo(struct nandfsmount *nmp, struct nandfs_fsinfo *fsinfo) +{ + struct nandfs_device *fsdev; + + fsdev = nmp->nm_nandfsdev; + + memcpy(&fsinfo->fs_fsdata, &fsdev->nd_fsdata, sizeof(fsdev->nd_fsdata)); + memcpy(&fsinfo->fs_super, &fsdev->nd_super, sizeof(fsdev->nd_super)); + snprintf(fsinfo->fs_dev, sizeof(fsinfo->fs_dev), + "%s", nmp->nm_vfs_mountp->mnt_stat.f_mntfromname); + + return (0); +} + +void +nandfs_inode_init(struct nandfs_inode *inode, uint16_t mode) +{ + struct timespec ts; + + vfs_timestamp(&ts); + + inode->i_blocks = 0; + inode->i_size = 0; + inode->i_ctime = ts.tv_sec; + inode->i_ctime_nsec = ts.tv_nsec; + inode->i_mtime = ts.tv_sec; + inode->i_mtime_nsec = ts.tv_nsec; + inode->i_mode = mode; + inode->i_links_count = 1; + if (S_ISDIR(mode)) + inode->i_links_count = 2; + inode->i_flags = 0; + + inode->i_special = 0; + memset(inode->i_db, 0, sizeof(inode->i_db)); + memset(inode->i_ib, 0, sizeof(inode->i_ib)); +} + +void +nandfs_inode_destroy(struct nandfs_inode *inode) +{ + + MPASS(inode->i_blocks == 0); + bzero(inode, sizeof(*inode)); +} + +int +nandfs_fs_full(struct nandfs_device *nffsdev) +{ + uint64_t space, bps; + + bps = nffsdev->nd_fsdata.f_blocks_per_segment; + space = (nffsdev->nd_clean_segs - 1) * bps; + + DPRINTF(BUF, ("%s: bufs:%jx space:%jx\n", __func__, + (uintmax_t)nffsdev->nd_dirty_bufs, (uintmax_t)space)); + + if (nffsdev->nd_dirty_bufs + (10 * bps) >= space) + return (1); + + return (0); +} + +static int +_nandfs_dirty_buf(struct buf *bp, int dirty_meta, int force) +{ + struct nandfs_device *nffsdev; + struct nandfs_node *node; + uint64_t ino, bps; + + if (NANDFS_ISGATHERED(bp)) { + bqrelse(bp); + return (0); + } + if ((bp->b_flags & (B_MANAGED | B_DELWRI)) == (B_MANAGED | B_DELWRI)) { + bqrelse(bp); + return (0); + } + + node = VTON(bp->b_vp); + nffsdev = node->nn_nandfsdev; + DPRINTF(BUF, ("%s: buf:%p\n", __func__, bp)); + ino = node->nn_ino; + + if (nandfs_fs_full(nffsdev) && !NANDFS_SYS_NODE(ino) && !force) { + brelse(bp); + return (ENOSPC); + } + + bp->b_flags |= B_MANAGED; + bdwrite(bp); + + nandfs_dirty_bufs_increment(nffsdev); + + KASSERT((bp->b_vp), ("vp missing for bp")); + KASSERT((nandfs_vblk_get(bp) || ino == NANDFS_DAT_INO), + ("bp vblk is 0")); + + /* + * To maintain consistency of FS we need to force making + * meta buffers dirty, even if free space is low. + */ + if (dirty_meta && ino != NANDFS_GC_INO) + nandfs_bmap_dirty_blocks(VTON(bp->b_vp), bp, 1); + + bps = nffsdev->nd_fsdata.f_blocks_per_segment; + + if (nffsdev->nd_dirty_bufs >= (bps * nandfs_max_dirty_segs)) { + mtx_lock(&nffsdev->nd_sync_mtx); + if (nffsdev->nd_syncing == 0) { + DPRINTF(SYNC, ("%s: wakeup gc\n", __func__)); + nffsdev->nd_syncing = 1; + wakeup(&nffsdev->nd_syncing); + } + mtx_unlock(&nffsdev->nd_sync_mtx); + } + + return (0); +} + +int +nandfs_dirty_buf(struct buf *bp, int force) +{ + + return (_nandfs_dirty_buf(bp, 1, force)); +} + +int +nandfs_dirty_buf_meta(struct buf *bp, int force) +{ + + return (_nandfs_dirty_buf(bp, 0, force)); +} + +void +nandfs_undirty_buf_fsdev(struct nandfs_device *nffsdev, struct buf *bp) +{ + + BUF_ASSERT_HELD(bp); + + if (bp->b_flags & B_DELWRI) { + bp->b_flags &= ~(B_DELWRI|B_MANAGED); + nandfs_dirty_bufs_decrement(nffsdev); + } + /* + * Since it is now being written, we can clear its deferred write flag. + */ + bp->b_flags &= ~B_DEFERRED; + + brelse(bp); +} + +void +nandfs_undirty_buf(struct buf *bp) +{ + struct nandfs_node *node; + + node = VTON(bp->b_vp); + + nandfs_undirty_buf_fsdev(node->nn_nandfsdev, bp); +} + +void +nandfs_vblk_set(struct buf *bp, nandfs_daddr_t blocknr) +{ + + nandfs_daddr_t *vblk = (nandfs_daddr_t *)(&bp->b_fsprivate1); + *vblk = blocknr; +} + +nandfs_daddr_t +nandfs_vblk_get(struct buf *bp) +{ + + nandfs_daddr_t *vblk = (nandfs_daddr_t *)(&bp->b_fsprivate1); + return (*vblk); +} + +void +nandfs_buf_set(struct buf *bp, uint32_t bits) +{ + uintptr_t flags; + + flags = (uintptr_t)bp->b_fsprivate3; + flags |= (uintptr_t)bits; + bp->b_fsprivate3 = (void *)flags; +} + +void +nandfs_buf_clear(struct buf *bp, uint32_t bits) +{ + uintptr_t flags; + + flags = (uintptr_t)bp->b_fsprivate3; + flags &= ~(uintptr_t)bits; + bp->b_fsprivate3 = (void *)flags; +} + +int +nandfs_buf_check(struct buf *bp, uint32_t bits) +{ + uintptr_t flags; + + flags = (uintptr_t)bp->b_fsprivate3; + if (flags & bits) + return (1); + return (0); +} + +int +nandfs_erase(struct nandfs_device *fsdev, off_t offset, size_t size) +{ + struct buf *bp; + int read_size, error, i; + + DPRINTF(BLOCK, ("%s: performing erase at offset %jx size %zx\n", + __func__, offset, size)); + + MPASS(size % fsdev->nd_erasesize == 0); + + if (fsdev->nd_is_nand) { + error = g_delete_data(fsdev->nd_gconsumer, offset, size); + return (error); + } + + if (size > MAXBSIZE) + read_size = MAXBSIZE; + else + read_size = size; + + error = 0; + for (i = 0; i < size / MAXBSIZE; i++) { + error = bread(fsdev->nd_devvp, btodb(offset + i * read_size), + read_size, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + memset(bp->b_data, 0xff, read_size); + error = bwrite(bp); + if (error) { + nandfs_error("%s: err:%d from bwrite\n", + __func__, error); + return (error); + } + } + + return (error); +} + +int +nandfs_vop_islocked(struct vnode *vp) +{ + int islocked; + + islocked = VOP_ISLOCKED(vp); + return (islocked == LK_EXCLUSIVE || islocked == LK_SHARED); +} + +nandfs_daddr_t +nandfs_block_to_dblock(struct nandfs_device *fsdev, nandfs_lbn_t block) +{ + + return (btodb(block * fsdev->nd_blocksize)); +} diff --git a/sys/fs/nandfs/nandfs_subr.h b/sys/fs/nandfs/nandfs_subr.h new file mode 100644 index 0000000..0bcda18 --- /dev/null +++ b/sys/fs/nandfs/nandfs_subr.h @@ -0,0 +1,238 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs_subr.h,v 1.1 2009/07/18 16:31:42 reinoud + * + * $FreeBSD$ + */ + +#ifndef _FS_NANDFS_NANDFS_SUBR_H_ +#define _FS_NANDFS_NANDFS_SUBR_H_ + +struct nandfs_mdt; + +struct nandfs_alloc_request +{ + uint64_t entrynum; + struct buf *bp_desc; + struct buf *bp_bitmap; + struct buf *bp_entry; +}; + +/* Segment creation */ +void nandfs_wakeup_wait_sync(struct nandfs_device *, int); +int nandfs_segment_constructor(struct nandfsmount *, int); +int nandfs_sync_file(struct vnode *); + +/* Basic calculators */ +uint64_t nandfs_get_segnum_of_block(struct nandfs_device *, nandfs_daddr_t); +void nandfs_get_segment_range(struct nandfs_device *, uint64_t, uint64_t *, + uint64_t *); +void nandfs_calc_mdt_consts(struct nandfs_device *, struct nandfs_mdt *, int); + +/* Log reading / volume helpers */ +int nandfs_search_super_root(struct nandfs_device *); + +/* Reading */ +int nandfs_dev_bread(struct nandfs_device *, nandfs_daddr_t, struct ucred *, + int, struct buf **); +int nandfs_bread(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int, + struct buf **); +int nandfs_bread_meta(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int, + struct buf **); +int nandfs_bdestroy(struct nandfs_node *, nandfs_daddr_t); +int nandfs_bcreate(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int, + struct buf **); +int nandfs_bcreate_meta(struct nandfs_node *, nandfs_lbn_t, struct ucred *, + int, struct buf **); +int nandfs_bread_create(struct nandfs_node *, nandfs_lbn_t, struct ucred *, + int, struct buf **); + +/* vtop operations */ +int nandfs_vtop(struct nandfs_node *, nandfs_daddr_t, nandfs_daddr_t *); + +/* Node action implementators */ +int nandfs_vinit(struct vnode *, uint64_t); +int nandfs_get_node(struct nandfsmount *, uint64_t, struct nandfs_node **); +int nandfs_get_node_raw(struct nandfs_device *, struct nandfsmount *, uint64_t, + struct nandfs_inode *, struct nandfs_node **); +void nandfs_dispose_node(struct nandfs_node **); + +void nandfs_itimes(struct vnode *); +int nandfs_lookup_name_in_dir(struct vnode *, const char *, int, uint64_t *, + int *, uint64_t *); +int nandfs_create_node(struct vnode *, struct vnode **, struct vattr *, + struct componentname *); +void nandfs_delete_node(struct nandfs_node *); + +int nandfs_chsize(struct vnode *, u_quad_t, struct ucred *); +int nandfs_dir_detach(struct nandfsmount *, struct nandfs_node *, + struct nandfs_node *, struct componentname *); +int nandfs_dir_attach(struct nandfsmount *, struct nandfs_node *, + struct nandfs_node *, struct vattr *, struct componentname *); + +int nandfs_dirty_buf(struct buf *, int); +int nandfs_dirty_buf_meta(struct buf *, int); +int nandfs_fs_full(struct nandfs_device *); +void nandfs_undirty_buf_fsdev(struct nandfs_device *, struct buf *); +void nandfs_undirty_buf(struct buf *); + +void nandfs_clear_buf(struct buf *); +void nandfs_buf_set(struct buf *, uint32_t); +void nandfs_buf_clear(struct buf *, uint32_t); +int nandfs_buf_check(struct buf *, uint32_t); + +int nandfs_find_free_entry(struct nandfs_mdt *, struct nandfs_node *, + struct nandfs_alloc_request *); +int nandfs_find_entry(struct nandfs_mdt *, struct nandfs_node *, + struct nandfs_alloc_request *); +int nandfs_alloc_entry(struct nandfs_mdt *, struct nandfs_alloc_request *); +void nandfs_abort_entry(struct nandfs_alloc_request *); +int nandfs_free_entry(struct nandfs_mdt *, struct nandfs_alloc_request *); +int nandfs_get_entry_block(struct nandfs_mdt *, struct nandfs_node *, + struct nandfs_alloc_request *, uint32_t *, int); + +/* inode managment */ +int nandfs_node_create(struct nandfsmount *, struct nandfs_node **, uint16_t); +int nandfs_node_destroy(struct nandfs_node *); +int nandfs_node_update(struct nandfs_node *); +int nandfs_get_node_entry(struct nandfsmount *, struct nandfs_inode **, + uint64_t, struct buf **); +void nandfs_mdt_trans_blk(struct nandfs_mdt *, uint64_t, uint64_t *, + uint64_t *, nandfs_lbn_t *, uint32_t *); + +/* vblock management */ +void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t, nandfs_lbn_t *, uint32_t *); +int nandfs_vblock_alloc(struct nandfs_device *, nandfs_daddr_t *); +int nandfs_vblock_end(struct nandfs_device *, nandfs_daddr_t); +int nandfs_vblock_assign(struct nandfs_device *, nandfs_daddr_t, + nandfs_lbn_t); +int nandfs_vblock_free(struct nandfs_device *, nandfs_daddr_t); + +/* Checkpoint management */ +int nandfs_get_checkpoint(struct nandfs_device *, struct nandfs_node *, + uint64_t); +int nandfs_set_checkpoint(struct nandfs_device *, struct nandfs_node *, + uint64_t, struct nandfs_inode *, uint64_t); + +/* Segment management */ +int nandfs_alloc_segment(struct nandfs_device *, uint64_t *); +int nandfs_update_segment(struct nandfs_device *, uint64_t, uint32_t); +int nandfs_free_segment(struct nandfs_device *, uint64_t); +int nandfs_clear_segment(struct nandfs_device *, uint64_t); +int nandfs_touch_segment(struct nandfs_device *, uint64_t); +int nandfs_markgc_segment(struct nandfs_device *, uint64_t); + +int nandfs_bmap_insert_block(struct nandfs_node *, nandfs_lbn_t, struct buf *); +int nandfs_bmap_update_block(struct nandfs_node *, struct buf *, nandfs_lbn_t); +int nandfs_bmap_update_dat(struct nandfs_node *, nandfs_daddr_t, struct buf *); +int nandfs_bmap_dirty_blocks(struct nandfs_node *, struct buf *, int); +int nandfs_bmap_truncate_mapping(struct nandfs_node *, nandfs_lbn_t, + nandfs_lbn_t); +int nandfs_bmap_lookup(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t *); + +/* dirent */ +int nandfs_add_dirent(struct vnode *, uint64_t, char *, long, uint8_t); +int nandfs_remove_dirent(struct vnode *, struct nandfs_node *, + struct componentname *); +int nandfs_update_dirent(struct vnode *, struct nandfs_node *, + struct nandfs_node *); +int nandfs_init_dir(struct vnode *, uint64_t, uint64_t); +int nandfs_update_parent_dir(struct vnode *, uint64_t); + +void nandfs_vblk_set(struct buf *, nandfs_daddr_t); +nandfs_daddr_t nandfs_vblk_get(struct buf *); + +void nandfs_inode_init(struct nandfs_inode *, uint16_t); +void nandfs_inode_destroy(struct nandfs_inode *); + +/* ioctl */ +int nandfs_get_seg_stat(struct nandfs_device *, struct nandfs_seg_stat *); +int nandfs_chng_cpmode(struct nandfs_node *, struct nandfs_cpmode *); +int nandfs_get_cpinfo_ioctl(struct nandfs_node *, struct nandfs_argv *); +int nandfs_delete_cp(struct nandfs_node *, uint64_t start, uint64_t); +int nandfs_make_snap(struct nandfs_device *, uint64_t *); +int nandfs_delete_snap(struct nandfs_device *, uint64_t); +int nandfs_get_cpstat(struct nandfs_node *, struct nandfs_cpstat *); +int nandfs_get_segment_info_ioctl(struct nandfs_device *, struct nandfs_argv *); +int nandfs_get_dat_vinfo_ioctl(struct nandfs_device *, struct nandfs_argv *); +int nandfs_get_dat_bdescs_ioctl(struct nandfs_device *, struct nandfs_argv *); +int nandfs_get_fsinfo(struct nandfsmount *, struct nandfs_fsinfo *); + +int nandfs_get_cpinfo(struct nandfs_node *, uint64_t, uint16_t, + struct nandfs_cpinfo *, uint32_t, uint32_t *); + +nandfs_lbn_t nandfs_get_maxfilesize(struct nandfs_device *); + +int nandfs_write_superblock(struct nandfs_device *); + +extern int nandfs_sync_interval; +extern int nandfs_max_dirty_segs; +extern int nandfs_cps_between_sblocks; + +struct buf *nandfs_geteblk(int, int); + +void nandfs_dirty_bufs_increment(struct nandfs_device *); +void nandfs_dirty_bufs_decrement(struct nandfs_device *); + +int nandfs_start_cleaner(struct nandfs_device *); +int nandfs_stop_cleaner(struct nandfs_device *); + +int nandfs_segsum_valid(struct nandfs_segment_summary *); +int nandfs_load_segsum(struct nandfs_device *, nandfs_daddr_t, + struct nandfs_segment_summary *); +int nandfs_get_segment_info(struct nandfs_device *, struct nandfs_suinfo *, + uint32_t, uint64_t); +int nandfs_get_segment_info_filter(struct nandfs_device *, + struct nandfs_suinfo *, uint32_t, uint64_t, uint64_t *, uint32_t, uint32_t); +int nandfs_get_dat_vinfo(struct nandfs_device *, struct nandfs_vinfo *, + uint32_t); +int nandfs_get_dat_bdescs(struct nandfs_device *, struct nandfs_bdesc *, + uint32_t); + +#define NANDFS_VBLK_ASSIGNED 1 + +#define NANDFS_IS_INDIRECT(bp) ((bp)->b_lblkno < 0) + +int nandfs_erase(struct nandfs_device *, off_t, size_t); + +#define NANDFS_VOP_ISLOCKED(vp) nandfs_vop_islocked((vp)) +int nandfs_vop_islocked(struct vnode *vp); + +nandfs_daddr_t nandfs_block_to_dblock(struct nandfs_device *, nandfs_lbn_t); + +#define DEBUG_MODE +#if defined(DEBUG_MODE) +#define nandfs_error panic +#define nandfs_warning printf +#elif defined(TEST_MODE) +#define nandfs_error printf +#define nandfs_warning printf +#else +#define nandfs_error(...) +#define nandfs_warning(...) +#endif + +#endif /* !_FS_NANDFS_NANDFS_SUBR_H_ */ diff --git a/sys/fs/nandfs/nandfs_sufile.c b/sys/fs/nandfs/nandfs_sufile.c new file mode 100644 index 0000000..d4f4326 --- /dev/null +++ b/sys/fs/nandfs/nandfs_sufile.c @@ -0,0 +1,569 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define SU_USAGE_OFF(bp, offset) \ + ((struct nandfs_segment_usage *)((bp)->b_data + offset)) + +static int +nandfs_seg_usage_blk_offset(struct nandfs_device *fsdev, uint64_t seg, + uint64_t *blk, uint64_t *offset) +{ + uint64_t off; + uint16_t seg_size; + + seg_size = fsdev->nd_fsdata.f_segment_usage_size; + + off = roundup(sizeof(struct nandfs_sufile_header), seg_size); + off += (seg * seg_size); + + *blk = off / fsdev->nd_blocksize; + *offset = off % fsdev->nd_blocksize; + return (0); +} + +/* Alloc new segment */ +int +nandfs_alloc_segment(struct nandfs_device *fsdev, uint64_t *seg) +{ + struct nandfs_node *su_node; + struct nandfs_sufile_header *su_header; + struct nandfs_segment_usage *su_usage; + struct buf *bp_header, *bp; + uint64_t blk, vblk, offset, i, rest, nsegments; + uint16_t seg_size; + int error, found; + + seg_size = fsdev->nd_fsdata.f_segment_usage_size; + nsegments = fsdev->nd_fsdata.f_nsegments; + + su_node = fsdev->nd_su_node; + ASSERT_VOP_LOCKED(NTOV(su_node), __func__); + + /* Read header buffer */ + error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header); + if (error) { + brelse(bp_header); + return (error); + } + + su_header = (struct nandfs_sufile_header *)bp_header->b_data; + + /* Get last allocated segment */ + i = su_header->sh_last_alloc + 1; + + found = 0; + bp = NULL; + while (!found) { + nandfs_seg_usage_blk_offset(fsdev, i, &blk, &offset); + if(blk != 0) { + error = nandfs_bmap_lookup(su_node, blk, &vblk); + if (error) { + nandfs_error("%s: cannot find vblk for blk " + "blk:%jx\n", __func__, blk); + return (error); + } + if (vblk) + error = nandfs_bread(su_node, blk, NOCRED, 0, + &bp); + else + error = nandfs_bcreate(su_node, blk, NOCRED, 0, + &bp); + if (error) { + nandfs_error("%s: cannot create/read " + "vblk:%jx\n", __func__, vblk); + if (bp) + brelse(bp); + return (error); + } + + su_usage = SU_USAGE_OFF(bp, offset); + } else { + su_usage = SU_USAGE_OFF(bp_header, offset); + bp = bp_header; + } + + rest = (fsdev->nd_blocksize - offset) / seg_size; + /* Go through all su usage in block */ + while (rest) { + /* When last check start from beggining */ + if (i == nsegments) + break; + + if (!su_usage->su_flags) { + su_usage->su_flags = 1; + found = 1; + break; + } + su_usage++; + i++; + + /* If all checked return error */ + if (i == su_header->sh_last_alloc) { + DPRINTF(SEG, ("%s: cannot allocate segment \n", + __func__)); + brelse(bp_header); + if (blk != 0) + brelse(bp); + return (1); + } + rest--; + } + if (!found) { + /* Otherwise read another block */ + if (blk != 0) + brelse(bp); + if (i == nsegments) { + blk = 0; + i = 0; + } else + blk++; + offset = 0; + } + } + + if (found) { + *seg = i; + su_header->sh_last_alloc = i; + su_header->sh_ncleansegs--; + su_header->sh_ndirtysegs++; + + fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs * + fsdev->nd_fsdata.f_blocks_per_segment; + fsdev->nd_clean_segs--; + + /* + * It is mostly called from syncer() so we want to force + * making buf dirty. + */ + error = nandfs_dirty_buf(bp_header, 1); + if (error) { + if (bp && bp != bp_header) + brelse(bp); + return (error); + } + if (bp && bp != bp_header) + nandfs_dirty_buf(bp, 1); + + DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)i)); + + return (0); + } + + DPRINTF(SEG, ("%s: failed\n", __func__)); + + return (1); +} + +/* + * Make buffer dirty, it will be updated soon but first it need to be + * gathered by syncer. + */ +int +nandfs_touch_segment(struct nandfs_device *fsdev, uint64_t seg) +{ + struct nandfs_node *su_node; + struct buf *bp; + uint64_t blk, offset; + int error; + + su_node = fsdev->nd_su_node; + ASSERT_VOP_LOCKED(NTOV(su_node), __func__); + + nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset); + + error = nandfs_bread(su_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + nandfs_error("%s: cannot preallocate new segment\n", __func__); + return (error); + } else + nandfs_dirty_buf(bp, 1); + + DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg)); + return (error); +} + +/* Update block count of segment */ +int +nandfs_update_segment(struct nandfs_device *fsdev, uint64_t seg, uint32_t nblks) +{ + struct nandfs_node *su_node; + struct nandfs_segment_usage *su_usage; + struct buf *bp; + uint64_t blk, offset; + int error; + + su_node = fsdev->nd_su_node; + ASSERT_VOP_LOCKED(NTOV(su_node), __func__); + + nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset); + + error = nandfs_bread(su_node, blk, NOCRED, 0, &bp); + if (error) { + nandfs_error("%s: read block:%jx to update\n", + __func__, blk); + brelse(bp); + return (error); + } + + su_usage = SU_USAGE_OFF(bp, offset); + su_usage->su_lastmod = fsdev->nd_ts.tv_sec; + su_usage->su_flags = NANDFS_SEGMENT_USAGE_DIRTY; + su_usage->su_nblocks += nblks; + + DPRINTF(SEG, ("%s: seg:%#jx inc:%#x cur:%#x\n", __func__, + (uintmax_t)seg, nblks, su_usage->su_nblocks)); + + nandfs_dirty_buf(bp, 1); + + return (0); +} + +/* Make segment free */ +int +nandfs_free_segment(struct nandfs_device *fsdev, uint64_t seg) +{ + struct nandfs_node *su_node; + struct nandfs_sufile_header *su_header; + struct nandfs_segment_usage *su_usage; + struct buf *bp_header, *bp; + uint64_t blk, offset; + int error; + + su_node = fsdev->nd_su_node; + ASSERT_VOP_LOCKED(NTOV(su_node), __func__); + + /* Read su header */ + error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header); + if (error) { + brelse(bp_header); + return (error); + } + + su_header = (struct nandfs_sufile_header *)bp_header->b_data; + nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset); + + /* Read su usage block if other than su header block */ + if (blk != 0) { + error = nandfs_bread(su_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + brelse(bp_header); + return (error); + } + } else + bp = bp_header; + + /* Reset su usage data */ + su_usage = SU_USAGE_OFF(bp, offset); + su_usage->su_lastmod = fsdev->nd_ts.tv_sec; + su_usage->su_nblocks = 0; + su_usage->su_flags = 0; + + /* Update clean/dirty counter in header */ + su_header->sh_ncleansegs++; + su_header->sh_ndirtysegs--; + + /* + * Make buffers dirty, called by cleaner + * so force dirty even if no much space left + * on device + */ + nandfs_dirty_buf(bp_header, 1); + if (bp != bp_header) + nandfs_dirty_buf(bp, 1); + + /* Update free block count */ + fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs * + fsdev->nd_fsdata.f_blocks_per_segment; + fsdev->nd_clean_segs++; + + DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg)); + + return (0); +} + +static int +nandfs_bad_segment(struct nandfs_device *fsdev, uint64_t seg) +{ + struct nandfs_node *su_node; + struct nandfs_segment_usage *su_usage; + struct buf *bp; + uint64_t blk, offset; + int error; + + su_node = fsdev->nd_su_node; + ASSERT_VOP_LOCKED(NTOV(su_node), __func__); + + nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset); + + error = nandfs_bread(su_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (error); + } + + su_usage = SU_USAGE_OFF(bp, offset); + su_usage->su_lastmod = fsdev->nd_ts.tv_sec; + su_usage->su_flags = NANDFS_SEGMENT_USAGE_ERROR; + + DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg)); + + nandfs_dirty_buf(bp, 1); + + return (0); +} + +int +nandfs_markgc_segment(struct nandfs_device *fsdev, uint64_t seg) +{ + struct nandfs_node *su_node; + struct nandfs_segment_usage *su_usage; + struct buf *bp; + uint64_t blk, offset; + int error; + + su_node = fsdev->nd_su_node; + + VOP_LOCK(NTOV(su_node), LK_EXCLUSIVE); + + nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset); + + error = nandfs_bread(su_node, blk, NOCRED, 0, &bp); + if (error) { + brelse(bp); + VOP_UNLOCK(NTOV(su_node), 0); + return (error); + } + + su_usage = SU_USAGE_OFF(bp, offset); + MPASS((su_usage->su_flags & NANDFS_SEGMENT_USAGE_GC) == 0); + su_usage->su_flags |= NANDFS_SEGMENT_USAGE_GC; + + brelse(bp); + VOP_UNLOCK(NTOV(su_node), 0); + + DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg)); + + return (0); +} + +int +nandfs_clear_segment(struct nandfs_device *fsdev, uint64_t seg) +{ + uint64_t offset, segsize; + uint32_t bps, bsize; + int error = 0; + + bps = fsdev->nd_fsdata.f_blocks_per_segment; + bsize = fsdev->nd_blocksize; + segsize = bsize * bps; + nandfs_get_segment_range(fsdev, seg, &offset, NULL); + offset *= bsize; + + DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg)); + + /* Erase it and mark it bad when fail */ + if (nandfs_erase(fsdev, offset, segsize)) + error = nandfs_bad_segment(fsdev, seg); + + if (error) + return (error); + + /* Mark it free */ + error = nandfs_free_segment(fsdev, seg); + + return (error); +} + +int +nandfs_get_seg_stat(struct nandfs_device *nandfsdev, + struct nandfs_seg_stat *nss) +{ + struct nandfs_sufile_header *suhdr; + struct nandfs_node *su_node; + struct buf *bp; + int err; + + su_node = nandfsdev->nd_su_node; + + NANDFS_WRITELOCK(nandfsdev); + VOP_LOCK(NTOV(su_node), LK_SHARED); + err = nandfs_bread(nandfsdev->nd_su_node, 0, NOCRED, 0, &bp); + if (err) { + brelse(bp); + VOP_UNLOCK(NTOV(su_node), 0); + NANDFS_WRITEUNLOCK(nandfsdev); + return (-1); + } + + suhdr = (struct nandfs_sufile_header *)bp->b_data; + nss->nss_nsegs = nandfsdev->nd_fsdata.f_nsegments; + nss->nss_ncleansegs = suhdr->sh_ncleansegs; + nss->nss_ndirtysegs = suhdr->sh_ndirtysegs; + nss->nss_ctime = 0; + nss->nss_nongc_ctime = nandfsdev->nd_ts.tv_sec; + nss->nss_prot_seq = nandfsdev->nd_seg_sequence; + + brelse(bp); + VOP_UNLOCK(NTOV(su_node), 0); + + NANDFS_WRITEUNLOCK(nandfsdev); + + return (0); +} + +int +nandfs_get_segment_info_ioctl(struct nandfs_device *fsdev, + struct nandfs_argv *nargv) +{ + struct nandfs_suinfo *nsi; + int error; + + if (nargv->nv_nmembs > NANDFS_SEGMENTS_MAX) + return (EINVAL); + + nsi = malloc(sizeof(struct nandfs_suinfo) * nargv->nv_nmembs, + M_NANDFSTEMP, M_WAITOK | M_ZERO); + + error = nandfs_get_segment_info(fsdev, nsi, nargv->nv_nmembs, + nargv->nv_index); + + if (error == 0) + error = copyout(nsi, (void *)(uintptr_t)nargv->nv_base, + sizeof(struct nandfs_suinfo) * nargv->nv_nmembs); + + free(nsi, M_NANDFSTEMP); + return (error); +} + +int +nandfs_get_segment_info(struct nandfs_device *fsdev, struct nandfs_suinfo *nsi, + uint32_t nmembs, uint64_t segment) +{ + + return (nandfs_get_segment_info_filter(fsdev, nsi, nmembs, segment, + NULL, 0, 0)); +} + +int +nandfs_get_segment_info_filter(struct nandfs_device *fsdev, + struct nandfs_suinfo *nsi, uint32_t nmembs, uint64_t segment, + uint64_t *nsegs, uint32_t filter, uint32_t nfilter) +{ + struct nandfs_segment_usage *su; + struct nandfs_node *su_node; + struct buf *bp; + uint64_t curr, blocknr, blockoff, i; + uint32_t flags; + int err = 0; + + curr = ~(0); + + lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL); + su_node = fsdev->nd_su_node; + + VOP_LOCK(NTOV(su_node), LK_SHARED); + + bp = NULL; + if (nsegs != NULL) + *nsegs = 0; + for (i = 0; i < nmembs; segment++) { + if (segment == fsdev->nd_fsdata.f_nsegments) + break; + + nandfs_seg_usage_blk_offset(fsdev, segment, &blocknr, + &blockoff); + + if (i == 0 || curr != blocknr) { + if (bp != NULL) + brelse(bp); + err = nandfs_bread(su_node, blocknr, NOCRED, + 0, &bp); + if (err) { + goto out; + } + curr = blocknr; + } + + su = SU_USAGE_OFF(bp, blockoff); + flags = su->su_flags; + if (segment == fsdev->nd_seg_num || + segment == fsdev->nd_next_seg_num) + flags |= NANDFS_SEGMENT_USAGE_ACTIVE; + + if (nfilter != 0 && (flags & nfilter) != 0) + continue; + if (filter != 0 && (flags & filter) == 0) + continue; + + nsi->nsi_num = segment; + nsi->nsi_lastmod = su->su_lastmod; + nsi->nsi_blocks = su->su_nblocks; + nsi->nsi_flags = flags; + nsi++; + i++; + if (nsegs != NULL) + (*nsegs)++; + } + +out: + if (bp != NULL) + brelse(bp); + VOP_UNLOCK(NTOV(su_node), 0); + lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL); + + return (err); +} diff --git a/sys/fs/nandfs/nandfs_vfsops.c b/sys/fs/nandfs/nandfs_vfsops.c new file mode 100644 index 0000000..ba53546 --- /dev/null +++ b/sys/fs/nandfs/nandfs_vfsops.c @@ -0,0 +1,1590 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs_vfsops.c,v 1.1 2009/07/18 16:31:42 reinoud Exp + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +static MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount structure"); + +#define NANDFS_SET_SYSTEMFILE(vp) { \ + (vp)->v_vflag |= VV_SYSTEM; \ + vref(vp); \ + vput(vp); } + +#define NANDFS_UNSET_SYSTEMFILE(vp) { \ + VOP_LOCK(vp, LK_EXCLUSIVE); \ + MPASS(vp->v_bufobj.bo_dirty.bv_cnt == 0); \ + (vp)->v_vflag &= ~VV_SYSTEM; \ + vgone(vp); \ + vput(vp); } + +/* Globals */ +struct _nandfs_devices nandfs_devices; + +/* Parameters */ +int nandfs_verbose = 0; + +static void +nandfs_tunable_init(void *arg) +{ + + TUNABLE_INT_FETCH("vfs.nandfs.verbose", &nandfs_verbose); +} +SYSINIT(nandfs_tunables, SI_SUB_VFS, SI_ORDER_ANY, nandfs_tunable_init, NULL); + +static SYSCTL_NODE(_vfs, OID_AUTO, nandfs, CTLFLAG_RD, 0, "NAND filesystem"); +static SYSCTL_NODE(_vfs_nandfs, OID_AUTO, mount, CTLFLAG_RD, 0, + "NANDFS mountpoints"); +SYSCTL_INT(_vfs_nandfs, OID_AUTO, verbose, CTLFLAG_RW, &nandfs_verbose, 0, ""); + +#define NANDFS_CONSTR_INTERVAL 5 +int nandfs_sync_interval = NANDFS_CONSTR_INTERVAL; /* sync every 5 seconds */ +SYSCTL_UINT(_vfs_nandfs, OID_AUTO, sync_interval, CTLFLAG_RW, + &nandfs_sync_interval, 0, ""); + +#define NANDFS_MAX_DIRTY_SEGS 5 +int nandfs_max_dirty_segs = NANDFS_MAX_DIRTY_SEGS; /* sync when 5 dirty seg */ +SYSCTL_UINT(_vfs_nandfs, OID_AUTO, max_dirty_segs, CTLFLAG_RW, + &nandfs_max_dirty_segs, 0, ""); + +#define NANDFS_CPS_BETWEEN_SBLOCKS 5 +int nandfs_cps_between_sblocks = NANDFS_CPS_BETWEEN_SBLOCKS; /* write superblock every 5 checkpoints */ +SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cps_between_sblocks, CTLFLAG_RW, + &nandfs_cps_between_sblocks, 0, ""); + +#define NANDFS_CLEANER_ENABLE 1 +int nandfs_cleaner_enable = NANDFS_CLEANER_ENABLE; +SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_enable, CTLFLAG_RW, + &nandfs_cleaner_enable, 0, ""); + +#define NANDFS_CLEANER_INTERVAL 5 +int nandfs_cleaner_interval = NANDFS_CLEANER_INTERVAL; +SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_interval, CTLFLAG_RW, + &nandfs_cleaner_interval, 0, ""); + +#define NANDFS_CLEANER_SEGMENTS 5 +int nandfs_cleaner_segments = NANDFS_CLEANER_SEGMENTS; +SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_segments, CTLFLAG_RW, + &nandfs_cleaner_segments, 0, ""); + +static int nandfs_mountfs(struct vnode *devvp, struct mount *mp); +static vfs_mount_t nandfs_mount; +static vfs_root_t nandfs_root; +static vfs_statfs_t nandfs_statfs; +static vfs_unmount_t nandfs_unmount; +static vfs_vget_t nandfs_vget; +static vfs_sync_t nandfs_sync; +static const char *nandfs_opts[] = { + "snap", "from", "noatime", NULL +}; + +/* System nodes */ +static int +nandfs_create_system_nodes(struct nandfs_device *nandfsdev) +{ + int error; + + error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_DAT_INO, + &nandfsdev->nd_super_root.sr_dat, &nandfsdev->nd_dat_node); + if (error) + goto errorout; + + error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_CPFILE_INO, + &nandfsdev->nd_super_root.sr_cpfile, &nandfsdev->nd_cp_node); + if (error) + goto errorout; + + error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_SUFILE_INO, + &nandfsdev->nd_super_root.sr_sufile, &nandfsdev->nd_su_node); + if (error) + goto errorout; + + error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_GC_INO, + NULL, &nandfsdev->nd_gc_node); + if (error) + goto errorout; + + NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node)); + NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node)); + NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node)); + NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node)); + + DPRINTF(VOLUMES, ("System vnodes: dat: %p cp: %p su: %p\n", + NTOV(nandfsdev->nd_dat_node), NTOV(nandfsdev->nd_cp_node), + NTOV(nandfsdev->nd_su_node))); + return (0); + +errorout: + nandfs_dispose_node(&nandfsdev->nd_gc_node); + nandfs_dispose_node(&nandfsdev->nd_dat_node); + nandfs_dispose_node(&nandfsdev->nd_cp_node); + nandfs_dispose_node(&nandfsdev->nd_su_node); + + return (error); +} + +static void +nandfs_release_system_nodes(struct nandfs_device *nandfsdev) +{ + + if (!nandfsdev) + return; + if (nandfsdev->nd_refcnt > 0) + return; + + if (nandfsdev->nd_gc_node) + NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node)); + if (nandfsdev->nd_dat_node) + NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node)); + if (nandfsdev->nd_cp_node) + NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node)); + if (nandfsdev->nd_su_node) + NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node)); +} + +static int +nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata) +{ + uint32_t fsdata_crc, comp_crc; + + if (fsdata->f_magic != NANDFS_FSDATA_MAGIC) + return (0); + + /* Preserve CRC */ + fsdata_crc = fsdata->f_sum; + + /* Calculate */ + fsdata->f_sum = (0); + comp_crc = crc32((uint8_t *)fsdata, fsdata->f_bytes); + + /* Restore */ + fsdata->f_sum = fsdata_crc; + + /* Check CRC */ + return (fsdata_crc == comp_crc); +} + +static int +nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata, + struct nandfs_super_block *super) +{ + uint32_t super_crc, comp_crc; + + /* Check super block magic */ + if (super->s_magic != NANDFS_SUPER_MAGIC) + return (0); + + /* Preserve CRC */ + super_crc = super->s_sum; + + /* Calculate */ + super->s_sum = (0); + comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes); + + /* Restore */ + super->s_sum = super_crc; + + /* Check CRC */ + return (super_crc == comp_crc); +} + +static void +nandfs_calc_superblock_crc(struct nandfs_fsdata *fsdata, + struct nandfs_super_block *super) +{ + uint32_t comp_crc; + + /* Calculate */ + super->s_sum = 0; + comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes); + + /* Restore */ + super->s_sum = comp_crc; +} + +static int +nandfs_is_empty(u_char *area, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (area[i] != 0xff) + return (0); + + return (1); +} + +static __inline int +nandfs_sblocks_in_esize(struct nandfs_device *fsdev) +{ + + return ((fsdev->nd_erasesize - NANDFS_SBLOCK_OFFSET_BYTES) / + sizeof(struct nandfs_super_block)); +} + +static __inline int +nandfs_max_sblocks(struct nandfs_device *fsdev) +{ + + return (NANDFS_NFSAREAS * nandfs_sblocks_in_esize(fsdev)); +} + +static __inline int +nandfs_sblocks_in_block(struct nandfs_device *fsdev) +{ + + return (fsdev->nd_devblocksize / sizeof(struct nandfs_super_block)); +} + +static __inline int +nandfs_sblocks_in_first_block(struct nandfs_device *fsdev) +{ + int n; + + n = nandfs_sblocks_in_block(fsdev) - + NANDFS_SBLOCK_OFFSET_BYTES / sizeof(struct nandfs_super_block); + if (n < 0) + n = 0; + + return (n); +} + +static int +nandfs_write_superblock_at(struct nandfs_device *fsdev, + struct nandfs_fsarea *fstp) +{ + struct nandfs_super_block *super, *supert; + struct buf *bp; + int sb_per_sector, sbs_in_fsd, read_block; + int index, pos, error; + off_t offset; + + DPRINTF(SYNC, ("%s: last_used %d nandfs_sblocks_in_esize %d\n", + __func__, fstp->last_used, nandfs_sblocks_in_esize(fsdev))); + if (fstp->last_used == nandfs_sblocks_in_esize(fsdev) - 1) + index = 0; + else + index = fstp->last_used + 1; + + super = &fsdev->nd_super; + supert = NULL; + + sb_per_sector = nandfs_sblocks_in_block(fsdev); + sbs_in_fsd = sizeof(struct nandfs_fsdata) / + sizeof(struct nandfs_super_block); + index += sbs_in_fsd; + offset = fstp->offset; + + DPRINTF(SYNC, ("%s: offset %#jx s_last_pseg %#jx s_last_cno %#jx " + "s_last_seq %#jx wtime %jd index %d\n", __func__, offset, + super->s_last_pseg, super->s_last_cno, super->s_last_seq, + super->s_wtime, index)); + + read_block = btodb(offset + ((index / sb_per_sector) * sb_per_sector) + * sizeof(struct nandfs_super_block)); + + DPRINTF(SYNC, ("%s: read_block %#x\n", __func__, read_block)); + + if (index == sbs_in_fsd) { + error = nandfs_erase(fsdev, offset, fsdev->nd_erasesize); + if (error) + return (error); + + error = bread(fsdev->nd_devvp, btodb(offset), + fsdev->nd_devblocksize, NOCRED, &bp); + if (error) { + printf("NANDFS: couldn't read initial data: %d\n", + error); + brelse(bp); + return (error); + } + memcpy(bp->b_data, &fsdev->nd_fsdata, sizeof(fsdev->nd_fsdata)); + /* + * 0xff-out the rest. This bp could be cached, so potentially + * b_data contains stale super blocks. + * + * We don't mind cached bp since most of the time we just add + * super blocks to already 0xff-out b_data and don't need to + * perform actual read. + */ + if (fsdev->nd_devblocksize > sizeof(fsdev->nd_fsdata)) + memset(bp->b_data + sizeof(fsdev->nd_fsdata), 0xff, + fsdev->nd_devblocksize - sizeof(fsdev->nd_fsdata)); + error = bwrite(bp); + if (error) { + printf("NANDFS: cannot rewrite initial data at %jx\n", + offset); + return (error); + } + } + + error = bread(fsdev->nd_devvp, read_block, fsdev->nd_devblocksize, + NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + + supert = (struct nandfs_super_block *)(bp->b_data); + pos = index % sb_per_sector; + + DPRINTF(SYNC, ("%s: storing at %d\n", __func__, pos)); + memcpy(&supert[pos], super, sizeof(struct nandfs_super_block)); + + /* + * See comment above in code that performs erase. + */ + if (pos == 0) + memset(&supert[1], 0xff, + (sb_per_sector - 1) * sizeof(struct nandfs_super_block)); + + error = bwrite(bp); + if (error) { + printf("NANDFS: cannot update superblock at %jx\n", offset); + return (error); + } + + DPRINTF(SYNC, ("%s: fstp->last_used %d -> %d\n", __func__, + fstp->last_used, index - sbs_in_fsd)); + fstp->last_used = index - sbs_in_fsd; + + return (0); +} + +int +nandfs_write_superblock(struct nandfs_device *fsdev) +{ + struct nandfs_super_block *super; + struct timespec ts; + int error; + int i, j; + + vfs_timestamp(&ts); + + super = &fsdev->nd_super; + + super->s_last_pseg = fsdev->nd_last_pseg; + super->s_last_cno = fsdev->nd_last_cno; + super->s_last_seq = fsdev->nd_seg_sequence; + super->s_wtime = ts.tv_sec; + + nandfs_calc_superblock_crc(&fsdev->nd_fsdata, super); + + error = 0; + for (i = 0, j = fsdev->nd_last_fsarea; i < NANDFS_NFSAREAS; + i++, j = (j + 1 % NANDFS_NFSAREAS)) { + if (fsdev->nd_fsarea[j].flags & NANDFS_FSSTOR_FAILED) { + DPRINTF(SYNC, ("%s: skipping %d\n", __func__, j)); + continue; + } + error = nandfs_write_superblock_at(fsdev, &fsdev->nd_fsarea[j]); + if (error) { + printf("NANDFS: writing superblock at offset %d failed:" + "%d\n", j * fsdev->nd_erasesize, error); + fsdev->nd_fsarea[j].flags |= NANDFS_FSSTOR_FAILED; + } else + break; + } + + if (i == NANDFS_NFSAREAS) { + printf("NANDFS: superblock was not written\n"); + /* + * TODO: switch to read-only? + */ + return (error); + } else + fsdev->nd_last_fsarea = (j + 1) % NANDFS_NFSAREAS; + + return (0); +} + +static int +nandfs_select_fsdata(struct nandfs_device *fsdev, + struct nandfs_fsdata *fsdatat, struct nandfs_fsdata **fsdata, int nfsds) +{ + int i; + + *fsdata = NULL; + for (i = 0; i < nfsds; i++) { + DPRINTF(VOLUMES, ("%s: i %d f_magic %x f_crc %x\n", __func__, + i, fsdatat[i].f_magic, fsdatat[i].f_sum)); + if (!nandfs_check_fsdata_crc(&fsdatat[i])) + continue; + *fsdata = &fsdatat[i]; + break; + } + + return (*fsdata != NULL ? 0 : EINVAL); +} + +static int +nandfs_select_sb(struct nandfs_device *fsdev, + struct nandfs_super_block *supert, struct nandfs_super_block **super, + int nsbs) +{ + int i; + + *super = NULL; + for (i = 0; i < nsbs; i++) { + if (!nandfs_check_superblock_crc(&fsdev->nd_fsdata, &supert[i])) + continue; + DPRINTF(SYNC, ("%s: i %d s_last_cno %jx s_magic %x " + "s_wtime %jd\n", __func__, i, supert[i].s_last_cno, + supert[i].s_magic, supert[i].s_wtime)); + if (*super == NULL || supert[i].s_last_cno > + (*super)->s_last_cno) + *super = &supert[i]; + } + + return (*super != NULL ? 0 : EINVAL); +} + +static int +nandfs_read_structures_at(struct nandfs_device *fsdev, + struct nandfs_fsarea *fstp, struct nandfs_fsdata *fsdata, + struct nandfs_super_block *super) +{ + struct nandfs_super_block *tsuper, *tsuperd; + struct buf *bp; + int error, read_size; + int i; + int offset; + + offset = fstp->offset; + + if (fsdev->nd_erasesize > MAXBSIZE) + read_size = MAXBSIZE; + else + read_size = fsdev->nd_erasesize; + + error = bread(fsdev->nd_devvp, btodb(offset), read_size, NOCRED, &bp); + if (error) { + printf("couldn't read: %d\n", error); + brelse(bp); + fstp->flags |= NANDFS_FSSTOR_FAILED; + return (error); + } + + tsuper = super; + + memcpy(fsdata, bp->b_data, sizeof(struct nandfs_fsdata)); + memcpy(tsuper, (bp->b_data + sizeof(struct nandfs_fsdata)), + read_size - sizeof(struct nandfs_fsdata)); + brelse(bp); + + tsuper += (read_size - sizeof(struct nandfs_fsdata)) / + sizeof(struct nandfs_super_block); + + for (i = 1; i < fsdev->nd_erasesize / read_size; i++) { + error = bread(fsdev->nd_devvp, btodb(offset + i * read_size), + read_size, NOCRED, &bp); + if (error) { + printf("couldn't read: %d\n", error); + brelse(bp); + fstp->flags |= NANDFS_FSSTOR_FAILED; + return (error); + } + memcpy(tsuper, bp->b_data, read_size); + tsuper += read_size / sizeof(struct nandfs_super_block); + brelse(bp); + } + + tsuper -= 1; + fstp->last_used = nandfs_sblocks_in_esize(fsdev) - 1; + for (tsuperd = super - 1; (tsuper != tsuperd); tsuper -= 1) { + if (nandfs_is_empty((u_char *)tsuper, sizeof(*tsuper))) + fstp->last_used--; + else + break; + } + + DPRINTF(VOLUMES, ("%s: last_used %d\n", __func__, fstp->last_used)); + + return (0); +} + +static int +nandfs_read_structures(struct nandfs_device *fsdev) +{ + struct nandfs_fsdata *fsdata, *fsdatat; + struct nandfs_super_block *sblocks, *ssblock; + int nsbs, nfsds, i; + int error = 0; + int nrsbs; + + nfsds = NANDFS_NFSAREAS; + nsbs = nandfs_max_sblocks(fsdev); + + fsdatat = malloc(sizeof(struct nandfs_fsdata) * nfsds, M_NANDFSTEMP, + M_WAITOK | M_ZERO); + sblocks = malloc(sizeof(struct nandfs_super_block) * nsbs, M_NANDFSTEMP, + M_WAITOK | M_ZERO); + + nrsbs = 0; + for (i = 0; i < NANDFS_NFSAREAS; i++) { + fsdev->nd_fsarea[i].offset = i * fsdev->nd_erasesize; + error = nandfs_read_structures_at(fsdev, &fsdev->nd_fsarea[i], + &fsdatat[i], sblocks + nrsbs); + if (error) + continue; + nrsbs += (fsdev->nd_fsarea[i].last_used + 1); + if (fsdev->nd_fsarea[fsdev->nd_last_fsarea].last_used > + fsdev->nd_fsarea[i].last_used) + fsdev->nd_last_fsarea = i; + } + + if (nrsbs == 0) { + printf("nandfs: no valid superblocks found\n"); + error = EINVAL; + goto out; + } + + error = nandfs_select_fsdata(fsdev, fsdatat, &fsdata, nfsds); + if (error) + goto out; + memcpy(&fsdev->nd_fsdata, fsdata, sizeof(struct nandfs_fsdata)); + + error = nandfs_select_sb(fsdev, sblocks, &ssblock, nsbs); + if (error) + goto out; + + memcpy(&fsdev->nd_super, ssblock, sizeof(struct nandfs_super_block)); +out: + free(fsdatat, M_NANDFSTEMP); + free(sblocks, M_NANDFSTEMP); + + if (error == 0) + DPRINTF(VOLUMES, ("%s: selected sb with w_time %jd " + "last_pseg %#jx\n", __func__, fsdev->nd_super.s_wtime, + fsdev->nd_super.s_last_pseg)); + + return (error); +} + +static void +nandfs_unmount_base(struct nandfs_device *nandfsdev) +{ + int error; + + if (!nandfsdev) + return; + + /* Remove all our information */ + error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0); + if (error) { + /* + * Flushing buffers failed when fs was umounting, can't do + * much now, just printf error and continue with umount. + */ + nandfs_error("%s(): error:%d when umounting FS\n", + __func__, error); + } + + /* Release the device's system nodes */ + nandfs_release_system_nodes(nandfsdev); +} + +static void +nandfs_get_ncleanseg(struct nandfs_device *nandfsdev) +{ + struct nandfs_seg_stat nss; + + nandfs_get_seg_stat(nandfsdev, &nss); + nandfsdev->nd_clean_segs = nss.nss_ncleansegs; + DPRINTF(VOLUMES, ("nandfs_mount: clean segs: %jx\n", + (uintmax_t)nandfsdev->nd_clean_segs)); +} + + +static int +nandfs_mount_base(struct nandfs_device *nandfsdev, struct mount *mp, + struct nandfs_args *args) +{ + uint32_t log_blocksize; + int error; + + /* Flush out any old buffers remaining from a previous use. */ + if ((error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0))) + return (error); + + error = nandfs_read_structures(nandfsdev); + if (error) { + printf("nandfs: could not get valid filesystem structures\n"); + return (error); + } + + if (nandfsdev->nd_fsdata.f_rev_level != NANDFS_CURRENT_REV) { + printf("nandfs: unsupported file system revision: %d " + "(supported is %d).\n", nandfsdev->nd_fsdata.f_rev_level, + NANDFS_CURRENT_REV); + return (EINVAL); + } + + if (nandfsdev->nd_fsdata.f_erasesize != nandfsdev->nd_erasesize) { + printf("nandfs: erasesize mismatch (device %#x, fs %#x)\n", + nandfsdev->nd_erasesize, nandfsdev->nd_fsdata.f_erasesize); + return (EINVAL); + } + + /* Get our blocksize */ + log_blocksize = nandfsdev->nd_fsdata.f_log_block_size; + nandfsdev->nd_blocksize = (uint64_t) 1 << (log_blocksize + 10); + DPRINTF(VOLUMES, ("%s: blocksize:%x\n", __func__, + nandfsdev->nd_blocksize)); + + DPRINTF(VOLUMES, ("%s: accepted super block with cp %#jx\n", __func__, + (uintmax_t)nandfsdev->nd_super.s_last_cno)); + + /* Calculate dat structure parameters */ + nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_dat_mdt, + nandfsdev->nd_fsdata.f_dat_entry_size); + nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_ifile_mdt, + nandfsdev->nd_fsdata.f_inode_size); + + /* Search for the super root and roll forward when needed */ + if (nandfs_search_super_root(nandfsdev)) { + printf("Cannot find valid SuperRoot\n"); + return (EINVAL); + } + + nandfsdev->nd_mount_state = nandfsdev->nd_super.s_state; + if (nandfsdev->nd_mount_state != NANDFS_VALID_FS) { + printf("FS is seriously damaged, needs repairing\n"); + printf("aborting mount\n"); + return (EINVAL); + } + + /* + * FS should be ok now. The superblock and the last segsum could be + * updated from the repair so extract running values again. + */ + nandfsdev->nd_last_pseg = nandfsdev->nd_super.s_last_pseg; + nandfsdev->nd_seg_sequence = nandfsdev->nd_super.s_last_seq; + nandfsdev->nd_seg_num = nandfs_get_segnum_of_block(nandfsdev, + nandfsdev->nd_last_pseg); + nandfsdev->nd_next_seg_num = nandfs_get_segnum_of_block(nandfsdev, + nandfsdev->nd_last_segsum.ss_next); + nandfsdev->nd_ts.tv_sec = nandfsdev->nd_last_segsum.ss_create; + nandfsdev->nd_last_cno = nandfsdev->nd_super.s_last_cno; + nandfsdev->nd_fakevblk = 1; + nandfsdev->nd_last_ino = NANDFS_USER_INO; + DPRINTF(VOLUMES, ("%s: last_pseg %#jx last_cno %#jx last_seq %#jx\n" + "fsdev: last_seg: seq %#jx num %#jx, next_seg_num %#jx\n", + __func__, (uintmax_t)nandfsdev->nd_last_pseg, + (uintmax_t)nandfsdev->nd_last_cno, + (uintmax_t)nandfsdev->nd_seg_sequence, + (uintmax_t)nandfsdev->nd_seg_sequence, + (uintmax_t)nandfsdev->nd_seg_num, + (uintmax_t)nandfsdev->nd_next_seg_num)); + + DPRINTF(VOLUMES, ("nandfs_mount: accepted super root\n")); + + /* Create system vnodes for DAT, CP and SEGSUM */ + error = nandfs_create_system_nodes(nandfsdev); + if (error) + nandfs_unmount_base(nandfsdev); + + nandfs_get_ncleanseg(nandfsdev); + + return (error); +} + +static void +nandfs_unmount_device(struct nandfs_device *nandfsdev) +{ + + /* Is there anything? */ + if (nandfsdev == NULL) + return; + + /* Remove the device only if we're the last reference */ + nandfsdev->nd_refcnt--; + if (nandfsdev->nd_refcnt >= 1) + return; + + MPASS(nandfsdev->nd_syncer == NULL); + MPASS(nandfsdev->nd_cleaner == NULL); + MPASS(nandfsdev->nd_free_base == NULL); + + /* Unmount our base */ + nandfs_unmount_base(nandfsdev); + + /* Remove from our device list */ + SLIST_REMOVE(&nandfs_devices, nandfsdev, nandfs_device, nd_next_device); + + DROP_GIANT(); + g_topology_lock(); + g_vfs_close(nandfsdev->nd_gconsumer); + g_topology_unlock(); + PICKUP_GIANT(); + + DPRINTF(VOLUMES, ("closing device\n")); + + /* Clear our mount reference and release device node */ + vrele(nandfsdev->nd_devvp); + + dev_rel(nandfsdev->nd_devvp->v_rdev); + + /* Free our device info */ + cv_destroy(&nandfsdev->nd_sync_cv); + mtx_destroy(&nandfsdev->nd_sync_mtx); + cv_destroy(&nandfsdev->nd_clean_cv); + mtx_destroy(&nandfsdev->nd_clean_mtx); + mtx_destroy(&nandfsdev->nd_mutex); + lockdestroy(&nandfsdev->nd_seg_const); + free(nandfsdev, M_NANDFSMNT); +} + +static int +nandfs_check_mounts(struct nandfs_device *nandfsdev, struct mount *mp, + struct nandfs_args *args) +{ + struct nandfsmount *nmp; + uint64_t last_cno; + + /* no double-mounting of the same checkpoint */ + STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) { + if (nmp->nm_mount_args.cpno == args->cpno) + return (EBUSY); + } + + /* Allow readonly mounts without questioning here */ + if (mp->mnt_flag & MNT_RDONLY) + return (0); + + /* Read/write mount */ + STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) { + /* Only one RW mount on this device! */ + if ((nmp->nm_vfs_mountp->mnt_flag & MNT_RDONLY)==0) + return (EROFS); + /* RDONLY on last mountpoint is device busy */ + last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno; + if (nmp->nm_mount_args.cpno == last_cno) + return (EBUSY); + } + + /* OK for now */ + return (0); +} + +static int +nandfs_mount_device(struct vnode *devvp, struct mount *mp, + struct nandfs_args *args, struct nandfs_device **nandfsdev_p) +{ + struct nandfs_device *nandfsdev; + struct g_provider *pp; + struct g_consumer *cp; + struct cdev *dev; + uint32_t erasesize; + int error, size; + int ronly; + + DPRINTF(VOLUMES, ("Mounting NANDFS device\n")); + + ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + + /* Look up device in our nandfs_mountpoints */ + *nandfsdev_p = NULL; + SLIST_FOREACH(nandfsdev, &nandfs_devices, nd_next_device) + if (nandfsdev->nd_devvp == devvp) + break; + + if (nandfsdev) { + DPRINTF(VOLUMES, ("device already mounted\n")); + error = nandfs_check_mounts(nandfsdev, mp, args); + if (error) + return error; + nandfsdev->nd_refcnt++; + *nandfsdev_p = nandfsdev; + + if (!ronly) { + DROP_GIANT(); + g_topology_lock(); + error = g_access(nandfsdev->nd_gconsumer, 0, 1, 0); + g_topology_unlock(); + PICKUP_GIANT(); + } + return (error); + } + + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + dev = devvp->v_rdev; + dev_ref(dev); + DROP_GIANT(); + g_topology_lock(); + error = g_vfs_open(devvp, &cp, "nandfs", ronly ? 0 : 1); + pp = g_dev_getprovider(dev); + g_topology_unlock(); + PICKUP_GIANT(); + VOP_UNLOCK(devvp, 0); + if (error) { + dev_rel(dev); + return (error); + } + + nandfsdev = malloc(sizeof(struct nandfs_device), M_NANDFSMNT, M_WAITOK | M_ZERO); + + /* Initialise */ + nandfsdev->nd_refcnt = 1; + nandfsdev->nd_devvp = devvp; + nandfsdev->nd_syncing = 0; + nandfsdev->nd_cleaning = 0; + nandfsdev->nd_gconsumer = cp; + cv_init(&nandfsdev->nd_sync_cv, "nandfssync"); + mtx_init(&nandfsdev->nd_sync_mtx, "nffssyncmtx", NULL, MTX_DEF); + cv_init(&nandfsdev->nd_clean_cv, "nandfsclean"); + mtx_init(&nandfsdev->nd_clean_mtx, "nffscleanmtx", NULL, MTX_DEF); + mtx_init(&nandfsdev->nd_mutex, "nandfsdev lock", NULL, MTX_DEF); + lockinit(&nandfsdev->nd_seg_const, PVFS, "nffssegcon", VLKTIMEOUT, + LK_CANRECURSE); + STAILQ_INIT(&nandfsdev->nd_mounts); + + nandfsdev->nd_devsize = pp->mediasize; + nandfsdev->nd_devblocksize = pp->sectorsize; + + size = sizeof(erasesize); + error = g_io_getattr("NAND::blocksize", nandfsdev->nd_gconsumer, &size, + &erasesize); + if (error) { + DPRINTF(VOLUMES, ("couldn't get erasesize: %d\n", error)); + + if (error == ENOIOCTL || error == EOPNOTSUPP) { + /* + * We conclude that this is not NAND storage + */ + nandfsdev->nd_erasesize = NANDFS_DEF_ERASESIZE; + nandfsdev->nd_is_nand = 0; + } else { + DROP_GIANT(); + g_topology_lock(); + g_vfs_close(nandfsdev->nd_gconsumer); + g_topology_unlock(); + PICKUP_GIANT(); + dev_rel(dev); + free(nandfsdev, M_NANDFSMNT); + return (error); + } + } else { + nandfsdev->nd_erasesize = erasesize; + nandfsdev->nd_is_nand = 1; + } + + DPRINTF(VOLUMES, ("%s: erasesize %x\n", __func__, + nandfsdev->nd_erasesize)); + + /* Register nandfs_device in list */ + SLIST_INSERT_HEAD(&nandfs_devices, nandfsdev, nd_next_device); + + error = nandfs_mount_base(nandfsdev, mp, args); + if (error) { + /* Remove all our information */ + nandfs_unmount_device(nandfsdev); + return (EINVAL); + } + + nandfsdev->nd_maxfilesize = nandfs_get_maxfilesize(nandfsdev); + + *nandfsdev_p = nandfsdev; + DPRINTF(VOLUMES, ("NANDFS device mounted ok\n")); + + return (0); +} + +static int +nandfs_mount_checkpoint(struct nandfsmount *nmp) +{ + struct nandfs_cpfile_header *cphdr; + struct nandfs_checkpoint *cp; + struct nandfs_inode ifile_inode; + struct nandfs_node *cp_node; + struct buf *bp; + uint64_t ncp, nsn, cpno, fcpno, blocknr, last_cno; + uint32_t off, dlen; + int cp_per_block, error; + + cpno = nmp->nm_mount_args.cpno; + if (cpno == 0) + cpno = nmp->nm_nandfsdev->nd_super.s_last_cno; + + DPRINTF(VOLUMES, ("%s: trying to mount checkpoint number %"PRIu64"\n", + __func__, cpno)); + + cp_node = nmp->nm_nandfsdev->nd_cp_node; + + VOP_LOCK(NTOV(cp_node), LK_SHARED); + /* Get cpfile header from 1st block of cp file */ + error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + VOP_UNLOCK(NTOV(cp_node), 0); + return (error); + } + + cphdr = (struct nandfs_cpfile_header *) bp->b_data; + ncp = cphdr->ch_ncheckpoints; + nsn = cphdr->ch_nsnapshots; + + brelse(bp); + + DPRINTF(VOLUMES, ("mount_nandfs: checkpoint header read in\n")); + DPRINTF(VOLUMES, ("\tNumber of checkpoints %"PRIu64"\n", ncp)); + DPRINTF(VOLUMES, ("\tNumber of snapshots %"PRIu64"\n", nsn)); + + /* Read in our specified checkpoint */ + dlen = nmp->nm_nandfsdev->nd_fsdata.f_checkpoint_size; + cp_per_block = nmp->nm_nandfsdev->nd_blocksize / dlen; + + fcpno = cpno + NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1; + blocknr = fcpno / cp_per_block; + off = (fcpno % cp_per_block) * dlen; + error = nandfs_bread(cp_node, blocknr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + VOP_UNLOCK(NTOV(cp_node), 0); + printf("mount_nandfs: couldn't read cp block %"PRIu64"\n", + fcpno); + return (EINVAL); + } + + /* Needs to be a valid checkpoint */ + cp = (struct nandfs_checkpoint *) ((uint8_t *) bp->b_data + off); + if (cp->cp_flags & NANDFS_CHECKPOINT_INVALID) { + printf("mount_nandfs: checkpoint marked invalid\n"); + brelse(bp); + VOP_UNLOCK(NTOV(cp_node), 0); + return (EINVAL); + } + + /* Is this really the checkpoint we want? */ + if (cp->cp_cno != cpno) { + printf("mount_nandfs: checkpoint file corrupt? " + "expected cpno %"PRIu64", found cpno %"PRIu64"\n", + cpno, cp->cp_cno); + brelse(bp); + VOP_UNLOCK(NTOV(cp_node), 0); + return (EINVAL); + } + + /* Check if it's a snapshot ! */ + last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno; + if (cpno != last_cno) { + /* Only allow snapshots if not mounting on the last cp */ + if ((cp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) == 0) { + printf( "mount_nandfs: checkpoint %"PRIu64" is not a " + "snapshot\n", cpno); + brelse(bp); + VOP_UNLOCK(NTOV(cp_node), 0); + return (EINVAL); + } + } + + ifile_inode = cp->cp_ifile_inode; + brelse(bp); + + /* Get ifile inode */ + error = nandfs_get_node_raw(nmp->nm_nandfsdev, NULL, NANDFS_IFILE_INO, + &ifile_inode, &nmp->nm_ifile_node); + if (error) { + printf("mount_nandfs: can't read ifile node\n"); + VOP_UNLOCK(NTOV(cp_node), 0); + return (EINVAL); + } + + NANDFS_SET_SYSTEMFILE(NTOV(nmp->nm_ifile_node)); + VOP_UNLOCK(NTOV(cp_node), 0); + /* Get root node? */ + + return (0); +} + +static void +free_nandfs_mountinfo(struct mount *mp) +{ + struct nandfsmount *nmp = VFSTONANDFS(mp); + + if (nmp == NULL) + return; + + free(nmp, M_NANDFSMNT); +} + +void +nandfs_wakeup_wait_sync(struct nandfs_device *nffsdev, int reason) +{ + char *reasons[] = { + "umount", + "vfssync", + "bdflush", + "fforce", + "fsync", + "ro_upd" + }; + + DPRINTF(SYNC, ("%s: %s\n", __func__, reasons[reason])); + mtx_lock(&nffsdev->nd_sync_mtx); + if (nffsdev->nd_syncing) + cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx); + if (reason == SYNCER_UMOUNT) + nffsdev->nd_syncer_exit = 1; + nffsdev->nd_syncing = 1; + wakeup(&nffsdev->nd_syncing); + cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx); + + mtx_unlock(&nffsdev->nd_sync_mtx); +} + +static void +nandfs_gc_finished(struct nandfs_device *nffsdev, int exit) +{ + int error; + + mtx_lock(&nffsdev->nd_sync_mtx); + nffsdev->nd_syncing = 0; + DPRINTF(SYNC, ("%s: cleaner finish\n", __func__)); + cv_broadcast(&nffsdev->nd_sync_cv); + mtx_unlock(&nffsdev->nd_sync_mtx); + if (!exit) { + error = tsleep(&nffsdev->nd_syncing, PRIBIO, "-", + hz * nandfs_sync_interval); + DPRINTF(SYNC, ("%s: cleaner waked up: %d\n", + __func__, error)); + } +} + +static void +nandfs_syncer(struct nandfsmount *nmp) +{ + struct nandfs_device *nffsdev; + struct mount *mp; + int flags, error; + + mp = nmp->nm_vfs_mountp; + nffsdev = nmp->nm_nandfsdev; + tsleep(&nffsdev->nd_syncing, PRIBIO, "-", hz * nandfs_sync_interval); + + while (!nffsdev->nd_syncer_exit) { + DPRINTF(SYNC, ("%s: syncer run\n", __func__)); + nffsdev->nd_syncing = 1; + + flags = (nmp->nm_flags & (NANDFS_FORCE_SYNCER | NANDFS_UMOUNT)); + + error = nandfs_segment_constructor(nmp, flags); + if (error) + nandfs_error("%s: error:%d when creating segments\n", + __func__, error); + + nmp->nm_flags &= ~flags; + + nandfs_gc_finished(nffsdev, 0); + } + + MPASS(nffsdev->nd_cleaner == NULL); + error = nandfs_segment_constructor(nmp, + NANDFS_FORCE_SYNCER | NANDFS_UMOUNT); + if (error) + nandfs_error("%s: error:%d when creating segments\n", + __func__, error); + nandfs_gc_finished(nffsdev, 1); + nffsdev->nd_syncer = NULL; + MPASS(nffsdev->nd_free_base == NULL); + + DPRINTF(SYNC, ("%s: exiting\n", __func__)); + kthread_exit(); +} + +static int +start_syncer(struct nandfsmount *nmp) +{ + int error; + + MPASS(nmp->nm_nandfsdev->nd_syncer == NULL); + + DPRINTF(SYNC, ("%s: start syncer\n", __func__)); + + nmp->nm_nandfsdev->nd_syncer_exit = 0; + + error = kthread_add((void(*)(void *))nandfs_syncer, nmp, NULL, + &nmp->nm_nandfsdev->nd_syncer, 0, 0, "nandfs_syncer"); + + if (error) + printf("nandfs: could not start syncer: %d\n", error); + + return (error); +} + +static int +stop_syncer(struct nandfsmount *nmp) +{ + + MPASS(nmp->nm_nandfsdev->nd_syncer != NULL); + + nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_UMOUNT); + + DPRINTF(SYNC, ("%s: stop syncer\n", __func__)); + return (0); +} + +/* + * Mount null layer + */ +static int +nandfs_mount(struct mount *mp) +{ + struct nandfsmount *nmp; + struct vnode *devvp; + struct nameidata nd; + struct vfsoptlist *opts; + struct thread *td; + char *from; + int error = 0, flags; + + DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp)); + + td = curthread; + opts = mp->mnt_optnew; + + if (vfs_filteropt(opts, nandfs_opts)) + return (EINVAL); + + /* + * Update is a no-op + */ + if (mp->mnt_flag & MNT_UPDATE) { + nmp = VFSTONANDFS(mp); + if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) { + return (error); + } + if (!(nmp->nm_ronly) && vfs_flagopt(opts, "ro", NULL, 0)) { + vn_start_write(NULL, &mp, V_WAIT); + error = VFS_SYNC(mp, MNT_WAIT); + if (error) + return (error); + vn_finished_write(mp); + + flags = WRITECLOSE; + if (mp->mnt_flag & MNT_FORCE) + flags |= FORCECLOSE; + + nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, + SYNCER_ROUPD); + error = vflush(mp, 0, flags, td); + if (error) + return (error); + + nandfs_stop_cleaner(nmp->nm_nandfsdev); + stop_syncer(nmp); + DROP_GIANT(); + g_topology_lock(); + g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1, 0); + g_topology_unlock(); + PICKUP_GIANT(); + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_RDONLY; + MNT_IUNLOCK(mp); + nmp->nm_ronly = 1; + + } else if ((nmp->nm_ronly) && + !vfs_flagopt(opts, "ro", NULL, 0)) { + /* + * Don't allow read-write snapshots. + */ + if (nmp->nm_mount_args.cpno != 0) + return (EROFS); + /* + * If upgrade to read-write by non-root, then verify + * that user has necessary permissions on the device. + */ + devvp = nmp->nm_nandfsdev->nd_devvp; + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_ACCESS(devvp, VREAD | VWRITE, + td->td_ucred, td); + if (error) { + error = priv_check(td, PRIV_VFS_MOUNT_PERM); + if (error) { + VOP_UNLOCK(devvp, 0); + return (error); + } + } + + VOP_UNLOCK(devvp, 0); + DROP_GIANT(); + g_topology_lock(); + error = g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, 1, + 0); + g_topology_unlock(); + PICKUP_GIANT(); + if (error) + return (error); + + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_RDONLY; + MNT_IUNLOCK(mp); + error = start_syncer(nmp); + if (error == 0) + error = nandfs_start_cleaner(nmp->nm_nandfsdev); + if (error) { + DROP_GIANT(); + g_topology_lock(); + g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1, + 0); + g_topology_unlock(); + PICKUP_GIANT(); + return (error); + } + + nmp->nm_ronly = 0; + } + return (0); + } + + from = vfs_getopts(opts, "from", &error); + if (error) + return (error); + + /* + * Find device node + */ + NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, from, curthread); + error = namei(&nd); + if (error) + return (error); + NDFREE(&nd, NDF_ONLY_PNBUF); + + devvp = nd.ni_vp; + + if (!vn_isdisk(devvp, &error)) { + vput(devvp); + return (error); + } + + /* Check the access rights on the mount device */ + error = VOP_ACCESS(devvp, VREAD, curthread->td_ucred, curthread); + if (error) + error = priv_check(curthread, PRIV_VFS_MOUNT_PERM); + if (error) { + vput(devvp); + return (error); + } + + vfs_getnewfsid(mp); + + error = nandfs_mountfs(devvp, mp); + if (error) + return (error); + vfs_mountedfrom(mp, from); + + return (0); +} + +static int +nandfs_mountfs(struct vnode *devvp, struct mount *mp) +{ + struct nandfsmount *nmp = NULL; + struct nandfs_args *args = NULL; + struct nandfs_device *nandfsdev; + char *from; + int error, ronly; + char *cpno; + + ronly = (mp->mnt_flag & MNT_RDONLY) != 0; + + if (devvp->v_rdev->si_iosize_max != 0) + mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; + VOP_UNLOCK(devvp, 0); + + if (mp->mnt_iosize_max > MAXPHYS) + mp->mnt_iosize_max = MAXPHYS; + + from = vfs_getopts(mp->mnt_optnew, "from", &error); + if (error) + goto error; + + error = vfs_getopt(mp->mnt_optnew, "snap", (void **)&cpno, NULL); + if (error == ENOENT) + cpno = NULL; + else if (error) + goto error; + + args = (struct nandfs_args *)malloc(sizeof(struct nandfs_args), + M_NANDFSMNT, M_WAITOK | M_ZERO); + + if (cpno != NULL) + args->cpno = strtoul(cpno, (char **)NULL, 10); + else + args->cpno = 0; + args->fspec = from; + + if (args->cpno != 0 && !ronly) { + error = EROFS; + goto error; + } + + printf("WARNING: NANDFS is considered to be a highly experimental " + "feature in FreeBSD.\n"); + + error = nandfs_mount_device(devvp, mp, args, &nandfsdev); + if (error) + goto error; + + nmp = (struct nandfsmount *) malloc(sizeof(struct nandfsmount), + M_NANDFSMNT, M_WAITOK | M_ZERO); + + mp->mnt_data = nmp; + nmp->nm_vfs_mountp = mp; + nmp->nm_ronly = ronly; + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_kern_flag |= MNTK_MPSAFE; + MNT_IUNLOCK(mp); + nmp->nm_nandfsdev = nandfsdev; + /* Add our mountpoint */ + STAILQ_INSERT_TAIL(&nandfsdev->nd_mounts, nmp, nm_next_mount); + + if (args->cpno > nandfsdev->nd_last_cno) { + printf("WARNING: supplied checkpoint number (%jd) is greater " + "than last known checkpoint on filesystem (%jd). Mounting" + " checkpoint %jd\n", (uintmax_t)args->cpno, + (uintmax_t)nandfsdev->nd_last_cno, + (uintmax_t)nandfsdev->nd_last_cno); + args->cpno = nandfsdev->nd_last_cno; + } + + /* Setting up other parameters */ + nmp->nm_mount_args = *args; + free(args, M_NANDFSMNT); + error = nandfs_mount_checkpoint(nmp); + if (error) { + nandfs_unmount(mp, MNT_FORCE); + goto unmounted; + } + + if (!ronly) { + error = start_syncer(nmp); + if (error == 0) + error = nandfs_start_cleaner(nmp->nm_nandfsdev); + if (error) + nandfs_unmount(mp, MNT_FORCE); + } + + return (0); + +error: + if (args != NULL) + free(args, M_NANDFSMNT); + + if (nmp != NULL) { + free(nmp, M_NANDFSMNT); + mp->mnt_data = NULL; + } +unmounted: + return (error); +} + +static int +nandfs_unmount(struct mount *mp, int mntflags) +{ + struct nandfs_device *nandfsdev; + struct nandfsmount *nmp; + int error; + int flags = 0; + + DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp)); + + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + nmp = mp->mnt_data; + nandfsdev = nmp->nm_nandfsdev; + + error = vflush(mp, 0, flags | SKIPSYSTEM, curthread); + if (error) + return (error); + + if (!(nmp->nm_ronly)) { + nandfs_stop_cleaner(nandfsdev); + stop_syncer(nmp); + } + + if (nmp->nm_ifile_node) + NANDFS_UNSET_SYSTEMFILE(NTOV(nmp->nm_ifile_node)); + + /* Remove our mount point */ + STAILQ_REMOVE(&nandfsdev->nd_mounts, nmp, nandfsmount, nm_next_mount); + + /* Unmount the device itself when we're the last one */ + nandfs_unmount_device(nandfsdev); + + free_nandfs_mountinfo(mp); + + /* + * Finally, throw away the null_mount structure + */ + mp->mnt_data = 0; + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_LOCAL; + MNT_IUNLOCK(mp); + + return (0); +} + +static int +nandfs_statfs(struct mount *mp, struct statfs *sbp) +{ + struct nandfsmount *nmp; + struct nandfs_device *nandfsdev; + struct nandfs_fsdata *fsdata; + struct nandfs_super_block *sb; + struct nandfs_block_group_desc *groups; + struct nandfs_node *ifile; + struct nandfs_mdt *mdt; + struct buf *bp; + int i, error; + uint32_t entries_per_group; + uint64_t files = 0; + + nmp = mp->mnt_data; + nandfsdev = nmp->nm_nandfsdev; + fsdata = &nandfsdev->nd_fsdata; + sb = &nandfsdev->nd_super; + ifile = nmp->nm_ifile_node; + mdt = &nandfsdev->nd_ifile_mdt; + entries_per_group = mdt->entries_per_group; + + VOP_LOCK(NTOV(ifile), LK_SHARED); + error = nandfs_bread(ifile, 0, NOCRED, 0, &bp); + if (error) { + brelse(bp); + VOP_UNLOCK(NTOV(ifile), 0); + return (error); + } + + groups = (struct nandfs_block_group_desc *)bp->b_data; + + for (i = 0; i < mdt->groups_per_desc_block; i++) + files += (entries_per_group - groups[i].bg_nfrees); + + brelse(bp); + VOP_UNLOCK(NTOV(ifile), 0); + + sbp->f_bsize = nandfsdev->nd_blocksize; + sbp->f_iosize = sbp->f_bsize; + sbp->f_blocks = fsdata->f_blocks_per_segment * fsdata->f_nsegments; + sbp->f_bfree = sb->s_free_blocks_count; + sbp->f_bavail = sbp->f_bfree; + sbp->f_files = files; + sbp->f_ffree = 0; + return (0); +} + +static int +nandfs_root(struct mount *mp, int flags, struct vnode **vpp) +{ + struct nandfsmount *nmp = VFSTONANDFS(mp); + struct nandfs_node *node; + int error; + + error = nandfs_get_node(nmp, NANDFS_ROOT_INO, &node); + if (error) + return (error); + + KASSERT(NTOV(node)->v_vflag & VV_ROOT, + ("root_vp->v_vflag & VV_ROOT")); + + *vpp = NTOV(node); + + return (error); +} + +static int +nandfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) +{ + struct nandfsmount *nmp = VFSTONANDFS(mp); + struct nandfs_node *node; + int error; + + error = nandfs_get_node(nmp, ino, &node); + if (node) + *vpp = NTOV(node); + + return (error); +} + +static int +nandfs_sync(struct mount *mp, int waitfor) +{ + struct nandfsmount *nmp = VFSTONANDFS(mp); + + DPRINTF(SYNC, ("%s: mp %p waitfor %d\n", __func__, mp, waitfor)); + + /* + * XXX: A hack to be removed soon + */ + if (waitfor == MNT_LAZY) + return (0); + if (waitfor == MNT_SUSPEND) + return (0); + nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_VFS_SYNC); + return (0); +} + +static struct vfsops nandfs_vfsops = { + .vfs_init = nandfs_init, + .vfs_mount = nandfs_mount, + .vfs_root = nandfs_root, + .vfs_statfs = nandfs_statfs, + .vfs_uninit = nandfs_uninit, + .vfs_unmount = nandfs_unmount, + .vfs_vget = nandfs_vget, + .vfs_sync = nandfs_sync, +}; + +VFS_SET(nandfs_vfsops, nandfs, VFCF_LOOPBACK); diff --git a/sys/fs/nandfs/nandfs_vnops.c b/sys/fs/nandfs/nandfs_vnops.c new file mode 100644 index 0000000..b226d30 --- /dev/null +++ b/sys/fs/nandfs/nandfs_vnops.c @@ -0,0 +1,2455 @@ +/*- + * Copyright (c) 2010-2012 Semihalf + * Copyright (c) 2008, 2009 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: NetBSD: nilfs_vnops.c,v 1.2 2009/08/26 03:40:48 elad + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +extern uma_zone_t nandfs_node_zone; +static void nandfs_read_filebuf(struct nandfs_node *, struct buf *); +static void nandfs_itimes_locked(struct vnode *); +static int nandfs_truncate(struct vnode *, uint64_t); + +static vop_pathconf_t nandfs_pathconf; + +#define UPDATE_CLOSE 0 +#define UPDATE_WAIT 0 + +static int +nandfs_inactive(struct vop_inactive_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + int error = 0; + + DPRINTF(VNCALL, ("%s: vp:%p node:%p\n", __func__, vp, node)); + + if (node == NULL) { + DPRINTF(NODE, ("%s: inactive NULL node\n", __func__)); + return (0); + } + + if (node->nn_inode.i_mode != 0 && !(node->nn_inode.i_links_count)) { + nandfs_truncate(vp, 0); + error = nandfs_node_destroy(node); + if (error) + nandfs_error("%s: destroy node: %p\n", __func__, node); + node->nn_flags = 0; + vrecycle(vp); + } + + return (error); +} + +static int +nandfs_reclaim(struct vop_reclaim_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *nandfs_node = VTON(vp); + struct nandfs_device *fsdev = nandfs_node->nn_nandfsdev; + uint64_t ino = nandfs_node->nn_ino; + + DPRINTF(VNCALL, ("%s: vp:%p node:%p\n", __func__, vp, nandfs_node)); + + /* Invalidate all entries to a particular vnode. */ + cache_purge(vp); + + /* Destroy the vm object and flush associated pages. */ + vnode_destroy_vobject(vp); + + /* Remove from vfs hash if not system vnode */ + if (!NANDFS_SYS_NODE(nandfs_node->nn_ino)) + vfs_hash_remove(vp); + + /* Dispose all node knowledge */ + nandfs_dispose_node(&nandfs_node); + + if (!NANDFS_SYS_NODE(ino)) + NANDFS_WRITEUNLOCK(fsdev); + + return (0); +} + +static int +nandfs_read(struct vop_read_args *ap) +{ + register struct vnode *vp = ap->a_vp; + register struct nandfs_node *node = VTON(vp); + struct nandfs_device *nandfsdev = node->nn_nandfsdev; + struct uio *uio = ap->a_uio; + struct buf *bp; + uint64_t size; + uint32_t blocksize; + off_t bytesinfile; + ssize_t toread, off; + daddr_t lbn; + ssize_t resid; + int error = 0; + + if (uio->uio_resid == 0) + return (0); + + size = node->nn_inode.i_size; + if (uio->uio_offset >= size) + return (0); + + blocksize = nandfsdev->nd_blocksize; + bytesinfile = size - uio->uio_offset; + + resid = omin(uio->uio_resid, bytesinfile); + + while (resid) { + lbn = uio->uio_offset / blocksize; + off = uio->uio_offset & (blocksize - 1); + + toread = omin(resid, blocksize - off); + + DPRINTF(READ, ("nandfs_read bn: 0x%jx toread: 0x%zx (0x%x)\n", + (uintmax_t)lbn, toread, blocksize)); + + error = nandfs_bread(node, lbn, NOCRED, 0, &bp); + if (error) { + brelse(bp); + break; + } + + error = uiomove(bp->b_data + off, toread, uio); + if (error) { + brelse(bp); + break; + } + + brelse(bp); + resid -= toread; + } + + return (error); +} + +static int +nandfs_write(struct vop_write_args *ap) +{ + struct nandfs_device *fsdev; + struct nandfs_node *node; + struct vnode *vp; + struct uio *uio; + struct buf *bp; + uint64_t file_size, vblk; + uint32_t blocksize; + ssize_t towrite, off; + daddr_t lbn; + ssize_t resid; + int error, ioflag, modified; + + vp = ap->a_vp; + uio = ap->a_uio; + ioflag = ap->a_ioflag; + node = VTON(vp); + fsdev = node->nn_nandfsdev; + + if (nandfs_fs_full(fsdev)) + return (ENOSPC); + + DPRINTF(WRITE, ("nandfs_write called %#zx at %#jx\n", + uio->uio_resid, (uintmax_t)uio->uio_offset)); + + if (uio->uio_offset < 0) + return (EINVAL); + if (uio->uio_resid == 0) + return (0); + + blocksize = fsdev->nd_blocksize; + file_size = node->nn_inode.i_size; + + switch (vp->v_type) { + case VREG: + if (ioflag & IO_APPEND) + uio->uio_offset = file_size; + break; + case VDIR: + return (EISDIR); + case VLNK: + break; + default: + panic("%s: bad file type vp: %p", __func__, vp); + } + + /* If explicitly asked to append, uio_offset can be wrong? */ + if (ioflag & IO_APPEND) + uio->uio_offset = file_size; + + resid = uio->uio_resid; + modified = error = 0; + + while (uio->uio_resid) { + lbn = uio->uio_offset / blocksize; + off = uio->uio_offset & (blocksize - 1); + + towrite = omin(uio->uio_resid, blocksize - off); + + DPRINTF(WRITE, ("%s: lbn: 0x%jd toread: 0x%zx (0x%x)\n", + __func__, (uintmax_t)lbn, towrite, blocksize)); + + error = nandfs_bmap_lookup(node, lbn, &vblk); + if (error) + break; + + DPRINTF(WRITE, ("%s: lbn: 0x%jd toread: 0x%zx (0x%x) " + "vblk=%jx\n", __func__, (uintmax_t)lbn, towrite, blocksize, + vblk)); + + if (vblk != 0) + error = nandfs_bread(node, lbn, NOCRED, 0, &bp); + else + error = nandfs_bcreate(node, lbn, NOCRED, 0, &bp); + + DPRINTF(WRITE, ("%s: vp %p bread bp %p lbn %#jx\n", __func__, + vp, bp, (uintmax_t)lbn)); + if (error) { + if (bp) + brelse(bp); + break; + } + + error = uiomove((char *)bp->b_data + off, (int)towrite, uio); + if (error) + break; + + error = nandfs_dirty_buf(bp, 0); + if (error) + break; + + modified++; + } + + /* XXX proper handling when only part of file was properly written */ + if (modified) { + if (resid > uio->uio_resid && ap->a_cred && + ap->a_cred->cr_uid != 0) + node->nn_inode.i_mode &= ~(ISUID | ISGID); + + if (file_size < uio->uio_offset + uio->uio_resid) { + node->nn_inode.i_size = uio->uio_offset + + uio->uio_resid; + node->nn_flags |= IN_CHANGE | IN_UPDATE; + vnode_pager_setsize(vp, uio->uio_offset + + uio->uio_resid); + nandfs_itimes(vp); + } + } + + DPRINTF(WRITE, ("%s: return:%d\n", __func__, error)); + + return (error); +} + +static int +nandfs_lookup(struct vop_cachedlookup_args *ap) +{ + struct vnode *dvp, **vpp; + struct componentname *cnp; + struct ucred *cred; + struct thread *td; + struct nandfs_node *dir_node, *node; + struct nandfsmount *nmp; + uint64_t ino, off; + const char *name; + int namelen, nameiop, islastcn, mounted_ro; + int error, found; + + DPRINTF(VNCALL, ("%s\n", __func__)); + + dvp = ap->a_dvp; + vpp = ap->a_vpp; + *vpp = NULL; + + cnp = ap->a_cnp; + cred = cnp->cn_cred; + td = cnp->cn_thread; + + dir_node = VTON(dvp); + nmp = dir_node->nn_nmp; + + /* Simplify/clarification flags */ + nameiop = cnp->cn_nameiop; + islastcn = cnp->cn_flags & ISLASTCN; + mounted_ro = dvp->v_mount->mnt_flag & MNT_RDONLY; + + /* + * If requesting a modify on the last path element on a read-only + * filingsystem, reject lookup; + */ + if (islastcn && mounted_ro && (nameiop == DELETE || nameiop == RENAME)) + return (EROFS); + + if (dir_node->nn_inode.i_links_count == 0) + return (ENOENT); + + /* + * Obviously, the file is not (anymore) in the namecache, we have to + * search for it. There are three basic cases: '.', '..' and others. + * + * Following the guidelines of VOP_LOOKUP manpage and tmpfs. + */ + error = 0; + if ((cnp->cn_namelen == 1) && (cnp->cn_nameptr[0] == '.')) { + DPRINTF(LOOKUP, ("\tlookup '.'\n")); + /* Special case 1 '.' */ + VREF(dvp); + *vpp = dvp; + /* Done */ + } else if (cnp->cn_flags & ISDOTDOT) { + /* Special case 2 '..' */ + DPRINTF(LOOKUP, ("\tlookup '..'\n")); + + /* Get our node */ + name = ".."; + namelen = 2; + error = nandfs_lookup_name_in_dir(dvp, name, namelen, &ino, + &found, &off); + if (error) + goto out; + if (!found) + error = ENOENT; + + /* First unlock parent */ + VOP_UNLOCK(dvp, 0); + + if (error == 0) { + DPRINTF(LOOKUP, ("\tfound '..'\n")); + /* Try to create/reuse the node */ + error = nandfs_get_node(nmp, ino, &node); + + if (!error) { + DPRINTF(LOOKUP, + ("\tnode retrieved/created OK\n")); + *vpp = NTOV(node); + } + } + + /* Try to relock parent */ + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); + } else { + DPRINTF(LOOKUP, ("\tlookup file\n")); + /* All other files */ + /* Look up filename in the directory returning its inode */ + name = cnp->cn_nameptr; + namelen = cnp->cn_namelen; + error = nandfs_lookup_name_in_dir(dvp, name, namelen, + &ino, &found, &off); + if (error) + goto out; + if (!found) { + DPRINTF(LOOKUP, ("\tNOT found\n")); + /* + * UGH, didn't find name. If we're creating or + * renaming on the last name this is OK and we ought + * to return EJUSTRETURN if its allowed to be created. + */ + error = ENOENT; + if ((nameiop == CREATE || nameiop == RENAME) && + islastcn) { + error = VOP_ACCESS(dvp, VWRITE, cred, + td); + if (!error) { + /* keep the component name */ + cnp->cn_flags |= SAVENAME; + error = EJUSTRETURN; + } + } + /* Done */ + } else { + if (ino == NANDFS_WHT_INO) + cnp->cn_flags |= ISWHITEOUT; + + if ((cnp->cn_flags & ISWHITEOUT) && + (nameiop == LOOKUP)) + return (ENOENT); + + if ((nameiop == DELETE) && islastcn) { + if ((cnp->cn_flags & ISWHITEOUT) && + (cnp->cn_flags & DOWHITEOUT)) { + cnp->cn_flags |= SAVENAME; + dir_node->nn_diroff = off; + return (EJUSTRETURN); + } + + error = VOP_ACCESS(dvp, VWRITE, cred, + cnp->cn_thread); + if (error) + return (error); + + /* Try to create/reuse the node */ + error = nandfs_get_node(nmp, ino, &node); + if (!error) { + *vpp = NTOV(node); + node->nn_diroff = off; + } + + if ((dir_node->nn_inode.i_mode & ISVTX) && + cred->cr_uid != 0 && + cred->cr_uid != dir_node->nn_inode.i_uid && + node->nn_inode.i_uid != cred->cr_uid) { + vput(*vpp); + *vpp = NULL; + return (EPERM); + } + } else if ((nameiop == RENAME) && islastcn) { + error = VOP_ACCESS(dvp, VWRITE, cred, + cnp->cn_thread); + if (error) + return (error); + + /* Try to create/reuse the node */ + error = nandfs_get_node(nmp, ino, &node); + if (!error) { + *vpp = NTOV(node); + node->nn_diroff = off; + } + } else { + /* Try to create/reuse the node */ + error = nandfs_get_node(nmp, ino, &node); + if (!error) { + *vpp = NTOV(node); + node->nn_diroff = off; + } + } + } + } + +out: + /* + * Store result in the cache if requested. If we are creating a file, + * the file might not be found and thus putting it into the namecache + * might be seen as negative caching. + */ + if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) + cache_enter(dvp, *vpp, cnp); + + return (error); + +} + +static int +nandfs_getattr(struct vop_getattr_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + struct nandfs_node *node = VTON(vp); + struct nandfs_inode *inode = &node->nn_inode; + + DPRINTF(VNCALL, ("%s: vp: %p\n", __func__, vp)); + nandfs_itimes(vp); + + /* Basic info */ + VATTR_NULL(vap); + vap->va_atime.tv_sec = inode->i_mtime; + vap->va_atime.tv_nsec = inode->i_mtime_nsec; + vap->va_mtime.tv_sec = inode->i_mtime; + vap->va_mtime.tv_nsec = inode->i_mtime_nsec; + vap->va_ctime.tv_sec = inode->i_ctime; + vap->va_ctime.tv_nsec = inode->i_ctime_nsec; + vap->va_type = IFTOVT(inode->i_mode); + vap->va_mode = inode->i_mode & ~S_IFMT; + vap->va_nlink = inode->i_links_count; + vap->va_uid = inode->i_uid; + vap->va_gid = inode->i_gid; + vap->va_rdev = inode->i_special; + vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; + vap->va_fileid = node->nn_ino; + vap->va_size = inode->i_size; + vap->va_blocksize = node->nn_nandfsdev->nd_blocksize; + vap->va_gen = 0; + vap->va_flags = inode->i_flags; + vap->va_bytes = inode->i_blocks * vap->va_blocksize; + vap->va_filerev = 0; + vap->va_vaflags = 0; + + return (0); +} + +static int +nandfs_vtruncbuf(struct vnode *vp, uint64_t nblks) +{ + struct nandfs_device *nffsdev; + struct bufobj *bo; + struct buf *bp, *nbp; + + bo = &vp->v_bufobj; + nffsdev = VTON(vp)->nn_nandfsdev; + + ASSERT_VOP_LOCKED(vp, "nandfs_truncate"); +restart: + BO_LOCK(bo); +restart_locked: + TAILQ_FOREACH_SAFE(bp, &bo->bo_clean.bv_hd, b_bobufs, nbp) { + if (bp->b_lblkno < nblks) + continue; + if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL)) + goto restart_locked; + + bremfree(bp); + bp->b_flags |= (B_INVAL | B_RELBUF); + bp->b_flags &= ~(B_ASYNC | B_MANAGED); + BO_UNLOCK(bo); + brelse(bp); + BO_LOCK(bo); + } + + TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { + if (bp->b_lblkno < nblks) + continue; + if (BUF_LOCK(bp, + LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, + BO_MTX(bo)) == ENOLCK) + goto restart; + bp->b_flags |= (B_INVAL | B_RELBUF); + bp->b_flags &= ~(B_ASYNC | B_MANAGED); + brelse(bp); + nandfs_dirty_bufs_decrement(nffsdev); + BO_LOCK(bo); + } + + BO_UNLOCK(bo); + + return (0); +} + +static int +nandfs_truncate(struct vnode *vp, uint64_t newsize) +{ + struct nandfs_device *nffsdev; + struct nandfs_node *node; + struct nandfs_inode *inode; + struct buf *bp = NULL; + uint64_t oblks, nblks, vblk, size, rest; + int error; + + node = VTON(vp); + nffsdev = node->nn_nandfsdev; + inode = &node->nn_inode; + + /* Calculate end of file */ + size = inode->i_size; + + if (newsize == size) { + node->nn_flags |= IN_CHANGE | IN_UPDATE; + nandfs_itimes(vp); + return (0); + } + + if (newsize > size) { + inode->i_size = newsize; + vnode_pager_setsize(vp, newsize); + node->nn_flags |= IN_CHANGE | IN_UPDATE; + nandfs_itimes(vp); + return (0); + } + + nblks = howmany(newsize, nffsdev->nd_blocksize); + oblks = howmany(size, nffsdev->nd_blocksize); + rest = newsize % nffsdev->nd_blocksize; + + if (rest) { + error = nandfs_bmap_lookup(node, nblks - 1, &vblk); + if (error) + return (error); + + if (vblk != 0) + error = nandfs_bread(node, nblks - 1, NOCRED, 0, &bp); + else + error = nandfs_bcreate(node, nblks - 1, NOCRED, 0, &bp); + + if (error) { + if (bp) + brelse(bp); + return (error); + } + + bzero((char *)bp->b_data + rest, + (u_int)(nffsdev->nd_blocksize - rest)); + error = nandfs_dirty_buf(bp, 0); + if (error) + return (error); + } + + DPRINTF(VNCALL, ("%s: vp %p oblks %jx nblks %jx\n", __func__, vp, oblks, + nblks)); + + error = nandfs_bmap_truncate_mapping(node, oblks - 1, nblks - 1); + if (error) { + if (bp) + nandfs_undirty_buf(bp); + return (error); + } + + error = nandfs_vtruncbuf(vp, nblks); + if (error) { + if (bp) + nandfs_undirty_buf(bp); + return (error); + } + + inode->i_size = newsize; + vnode_pager_setsize(vp, newsize); + node->nn_flags |= IN_CHANGE | IN_UPDATE; + nandfs_itimes(vp); + + return (error); +} + +static void +nandfs_itimes_locked(struct vnode *vp) +{ + struct nandfs_node *node; + struct nandfs_inode *inode; + struct timespec ts; + + ASSERT_VI_LOCKED(vp, __func__); + + node = VTON(vp); + inode = &node->nn_inode; + + if ((node->nn_flags & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0) + return; + + if (((vp->v_mount->mnt_kern_flag & + (MNTK_SUSPENDED | MNTK_SUSPEND)) == 0) || + (node->nn_flags & (IN_CHANGE | IN_UPDATE))) + node->nn_flags |= IN_MODIFIED; + + vfs_timestamp(&ts); + if (node->nn_flags & IN_UPDATE) { + inode->i_mtime = ts.tv_sec; + inode->i_mtime_nsec = ts.tv_nsec; + } + if (node->nn_flags & IN_CHANGE) { + inode->i_ctime = ts.tv_sec; + inode->i_ctime_nsec = ts.tv_nsec; + } + + node->nn_flags &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE); +} + +void +nandfs_itimes(struct vnode *vp) +{ + + VI_LOCK(vp); + nandfs_itimes_locked(vp); + VI_UNLOCK(vp); +} + +static int +nandfs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct thread *td) +{ + struct nandfs_node *node = VTON(vp); + struct nandfs_inode *inode = &node->nn_inode; + uint16_t nmode; + int error = 0; + + DPRINTF(VNCALL, ("%s: vp %p, mode %x, cred %p, td %p\n", __func__, vp, + mode, cred, td)); + /* + * To modify the permissions on a file, must possess VADMIN + * for that file. + */ + if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + return (error); + + /* + * Privileged processes may set the sticky bit on non-directories, + * as well as set the setgid bit on a file with a group that the + * process is not a member of. Both of these are allowed in + * jail(8). + */ + if (vp->v_type != VDIR && (mode & S_ISTXT)) { + if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0)) + return (EFTYPE); + } + if (!groupmember(inode->i_gid, cred) && (mode & ISGID)) { + error = priv_check_cred(cred, PRIV_VFS_SETGID, 0); + if (error) + return (error); + } + + /* + * Deny setting setuid if we are not the file owner. + */ + if ((mode & ISUID) && inode->i_uid != cred->cr_uid) { + error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0); + if (error) + return (error); + } + + nmode = inode->i_mode; + nmode &= ~ALLPERMS; + nmode |= (mode & ALLPERMS); + inode->i_mode = nmode; + node->nn_flags |= IN_CHANGE; + + DPRINTF(VNCALL, ("%s: to mode %x\n", __func__, nmode)); + + return (error); +} + +static int +nandfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, + struct thread *td) +{ + struct nandfs_node *node = VTON(vp); + struct nandfs_inode *inode = &node->nn_inode; + uid_t ouid; + gid_t ogid; + int error = 0; + + if (uid == (uid_t)VNOVAL) + uid = inode->i_uid; + if (gid == (gid_t)VNOVAL) + gid = inode->i_gid; + /* + * To modify the ownership of a file, must possess VADMIN for that + * file. + */ + if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td))) + return (error); + /* + * To change the owner of a file, or change the group of a file to a + * group of which we are not a member, the caller must have + * privilege. + */ + if (((uid != inode->i_uid && uid != cred->cr_uid) || + (gid != inode->i_gid && !groupmember(gid, cred))) && + (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0))) + return (error); + ogid = inode->i_gid; + ouid = inode->i_uid; + + inode->i_gid = gid; + inode->i_uid = uid; + + node->nn_flags |= IN_CHANGE; + if ((inode->i_mode & (ISUID | ISGID)) && + (ouid != uid || ogid != gid)) { + if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) { + inode->i_mode &= ~(ISUID | ISGID); + } + } + DPRINTF(VNCALL, ("%s: vp %p, cred %p, td %p - ret OK\n", __func__, vp, + cred, td)); + return (0); +} + +static int +nandfs_setattr(struct vop_setattr_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + struct nandfs_inode *inode = &node->nn_inode; + struct vattr *vap = ap->a_vap; + struct ucred *cred = ap->a_cred; + struct thread *td = curthread; + uint32_t flags; + int error = 0; + + if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || + (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || + (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || + (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { + DPRINTF(VNCALL, ("%s: unsettable attribute\n", __func__)); + return (EINVAL); + } + + if (vap->va_flags != VNOVAL) { + DPRINTF(VNCALL, ("%s: vp:%p td:%p flags:%lx\n", __func__, vp, + td, vap->va_flags)); + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + /* + * Callers may only modify the file flags on objects they + * have VADMIN rights for. + */ + if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + return (error); + /* + * Unprivileged processes are not permitted to unset system + * flags, or modify flags if any system flags are set. + * Privileged non-jail processes may not modify system flags + * if securelevel > 0 and any existing system flags are set. + * Privileged jail processes behave like privileged non-jail + * processes if the security.jail.chflags_allowed sysctl is + * is non-zero; otherwise, they behave like unprivileged + * processes. + */ + + flags = inode->i_flags; + if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) { + if (flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) { + error = securelevel_gt(cred, 0); + if (error) + return (error); + } + /* Snapshot flag cannot be set or cleared */ + if (((vap->va_flags & SF_SNAPSHOT) != 0 && + (flags & SF_SNAPSHOT) == 0) || + ((vap->va_flags & SF_SNAPSHOT) == 0 && + (flags & SF_SNAPSHOT) != 0)) + return (EPERM); + + inode->i_flags = vap->va_flags; + } else { + if (flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) || + (vap->va_flags & UF_SETTABLE) != vap->va_flags) + return (EPERM); + + flags &= SF_SETTABLE; + flags |= (vap->va_flags & UF_SETTABLE); + inode->i_flags = flags; + } + node->nn_flags |= IN_CHANGE; + if (vap->va_flags & (IMMUTABLE | APPEND)) + return (0); + } + if (inode->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); + + if (vap->va_size != (u_quad_t)VNOVAL) { + DPRINTF(VNCALL, ("%s: vp:%p td:%p size:%jx\n", __func__, vp, td, + (uintmax_t)vap->va_size)); + + switch (vp->v_type) { + case VDIR: + return (EISDIR); + case VLNK: + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + if ((inode->i_flags & SF_SNAPSHOT) != 0) + return (EPERM); + break; + default: + return (0); + } + + if (vap->va_size > node->nn_nandfsdev->nd_maxfilesize) + return (EFBIG); + + KASSERT((vp->v_type == VREG), ("Set size %d", vp->v_type)); + nandfs_truncate(vp, vap->va_size); + node->nn_flags |= IN_CHANGE; + + return (0); + } + + if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + DPRINTF(VNCALL, ("%s: vp:%p td:%p uid/gid %x/%x\n", __func__, + vp, td, vap->va_uid, vap->va_gid)); + error = nandfs_chown(vp, vap->va_uid, vap->va_gid, cred, td); + if (error) + return (error); + } + + if (vap->va_mode != (mode_t)VNOVAL) { + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + DPRINTF(VNCALL, ("%s: vp:%p td:%p mode %x\n", __func__, vp, td, + vap->va_mode)); + + error = nandfs_chmod(vp, (int)vap->va_mode, cred, td); + if (error) + return (error); + } + if (vap->va_atime.tv_sec != VNOVAL || + vap->va_mtime.tv_sec != VNOVAL || + vap->va_birthtime.tv_sec != VNOVAL) { + DPRINTF(VNCALL, ("%s: vp:%p td:%p time a/m/b %jx/%jx/%jx\n", + __func__, vp, td, (uintmax_t)vap->va_atime.tv_sec, + (uintmax_t)vap->va_mtime.tv_sec, + (uintmax_t)vap->va_birthtime.tv_sec)); + + if (vap->va_atime.tv_sec != VNOVAL) + node->nn_flags |= IN_ACCESS; + if (vap->va_mtime.tv_sec != VNOVAL) + node->nn_flags |= IN_CHANGE | IN_UPDATE; + if (vap->va_birthtime.tv_sec != VNOVAL) + node->nn_flags |= IN_MODIFIED; + nandfs_itimes(vp); + return (0); + } + + return (0); +} + +static int +nandfs_open(struct vop_open_args *ap) +{ + struct nandfs_node *node = VTON(ap->a_vp); + uint64_t filesize; + + DPRINTF(VNCALL, ("nandfs_open called ap->a_mode %x\n", ap->a_mode)); + + if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) + return (EOPNOTSUPP); + + if ((node->nn_inode.i_flags & APPEND) && + (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) + return (EPERM); + + filesize = node->nn_inode.i_size; + vnode_create_vobject(ap->a_vp, filesize, ap->a_td); + + return (0); +} + +static int +nandfs_close(struct vop_close_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + + DPRINTF(VNCALL, ("%s: vp %p node %p\n", __func__, vp, node)); + + mtx_lock(&vp->v_interlock); + if (vp->v_usecount > 1) + nandfs_itimes_locked(vp); + mtx_unlock(&vp->v_interlock); + + return (0); +} + +static int +nandfs_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode) +{ + + /* Check if we are allowed to write */ + switch (vap->va_type) { + case VDIR: + case VLNK: + case VREG: + /* + * Normal nodes: check if we're on a read-only mounted + * filingsystem and bomb out if we're trying to write. + */ + if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) + return (EROFS); + break; + case VBLK: + case VCHR: + case VSOCK: + case VFIFO: + /* + * Special nodes: even on read-only mounted filingsystems + * these are allowed to be written to if permissions allow. + */ + break; + default: + /* No idea what this is */ + return (EINVAL); + } + + /* Noone may write immutable files */ + if ((mode & VWRITE) && (VTON(vp)->nn_inode.i_flags & IMMUTABLE)) + return (EPERM); + + return (0); +} + +static int +nandfs_check_permitted(struct vnode *vp, struct vattr *vap, mode_t mode, + struct ucred *cred) +{ + + return (vaccess(vp->v_type, vap->va_mode, vap->va_uid, vap->va_gid, mode, + cred, NULL)); +} + +static int +nandfs_advlock(struct vop_advlock_args *ap) +{ + struct nandfs_node *nvp; + quad_t size; + + nvp = VTON(ap->a_vp); + size = nvp->nn_inode.i_size; + return (lf_advlock(ap, &(nvp->nn_lockf), size)); +} + +static int +nandfs_access(struct vop_access_args *ap) +{ + struct vnode *vp = ap->a_vp; + accmode_t accmode = ap->a_accmode; + struct ucred *cred = ap->a_cred; + struct vattr vap; + int error; + + DPRINTF(VNCALL, ("%s: vp:%p mode: %x\n", __func__, vp, accmode)); + + error = VOP_GETATTR(vp, &vap, NULL); + if (error) + return (error); + + error = nandfs_check_possible(vp, &vap, accmode); + if (error) { + return (error); + } + + error = nandfs_check_permitted(vp, &vap, accmode, cred); + + return (error); +} + +static int +nandfs_print(struct vop_print_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *nvp = VTON(vp); + + printf("\tvp=%p, nandfs_node=%p\n", vp, nvp); + printf("nandfs inode %#jx\n", (uintmax_t)nvp->nn_ino); + printf("flags = 0x%b\n", (u_int)nvp->nn_flags, PRINT_NODE_FLAGS); + + return (0); +} + +static void +nandfs_read_filebuf(struct nandfs_node *node, struct buf *bp) +{ + struct nandfs_device *nandfsdev = node->nn_nandfsdev; + struct buf *nbp; + nandfs_daddr_t vblk, pblk; + nandfs_lbn_t from; + uint32_t blocksize; + int error = 0; + int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE; + + /* + * Translate all the block sectors into a series of buffers to read + * asynchronously from the nandfs device. Note that this lookup may + * induce readin's too. + */ + + blocksize = nandfsdev->nd_blocksize; + if (bp->b_bcount / blocksize != 1) + panic("invalid b_count in bp %p\n", bp); + + from = bp->b_blkno; + + DPRINTF(READ, ("\tread in from inode %#jx blkno %#jx" + " count %#lx\n", (uintmax_t)node->nn_ino, from, + bp->b_bcount)); + + /* Get virtual block numbers for the vnode's buffer span */ + error = nandfs_bmap_lookup(node, from, &vblk); + if (error) { + bp->b_error = EINVAL; + bp->b_ioflags |= BIO_ERROR; + bufdone(bp); + return; + } + + /* Translate virtual block numbers to physical block numbers */ + error = nandfs_vtop(node, vblk, &pblk); + if (error) { + bp->b_error = EINVAL; + bp->b_ioflags |= BIO_ERROR; + bufdone(bp); + return; + } + + /* Issue translated blocks */ + bp->b_resid = bp->b_bcount; + + /* Note virtual block 0 marks not mapped */ + if (vblk == 0) { + vfs_bio_clrbuf(bp); + bufdone(bp); + return; + } + + nbp = bp; + nbp->b_blkno = pblk * blk2dev; + bp->b_iooffset = dbtob(nbp->b_blkno); + MPASS(bp->b_iooffset >= 0); + BO_STRATEGY(&nandfsdev->nd_devvp->v_bufobj, nbp); + nandfs_vblk_set(bp, vblk); + DPRINTF(READ, ("read_filebuf : ino %#jx blk %#jx -> " + "%#jx -> %#jx [bp %p]\n", (uintmax_t)node->nn_ino, + (uintmax_t)(from), (uintmax_t)vblk, + (uintmax_t)pblk, nbp)); +} + +static void +nandfs_write_filebuf(struct nandfs_node *node, struct buf *bp) +{ + struct nandfs_device *nandfsdev = node->nn_nandfsdev; + + bp->b_iooffset = dbtob(bp->b_blkno); + MPASS(bp->b_iooffset >= 0); + BO_STRATEGY(&nandfsdev->nd_devvp->v_bufobj, bp); +} + +static int +nandfs_strategy(struct vop_strategy_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct buf *bp = ap->a_bp; + struct nandfs_node *node = VTON(vp); + + + /* check if we ought to be here */ + KASSERT((vp->v_type != VBLK && vp->v_type != VCHR), + ("nandfs_strategy on type %d", vp->v_type)); + + /* Translate if needed and pass on */ + if (bp->b_iocmd == BIO_READ) { + nandfs_read_filebuf(node, bp); + return (0); + } + + /* Send to segment collector */ + nandfs_write_filebuf(node, bp); + return (0); +} + +static int +nandfs_readdir(struct vop_readdir_args *ap) +{ + struct uio *uio = ap->a_uio; + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + struct nandfs_dir_entry *ndirent; + struct dirent dirent; + struct buf *bp; + uint64_t file_size, diroffset, transoffset, blkoff; + uint64_t blocknr; + uint32_t blocksize = node->nn_nandfsdev->nd_blocksize; + uint8_t *pos, name_len; + int error; + + DPRINTF(READDIR, ("nandfs_readdir called\n")); + + if (vp->v_type != VDIR) + return (ENOTDIR); + + file_size = node->nn_inode.i_size; + DPRINTF(READDIR, ("nandfs_readdir filesize %jd resid %zd\n", + (uintmax_t)file_size, uio->uio_resid )); + + /* We are called just as long as we keep on pushing data in */ + error = 0; + if ((uio->uio_offset < file_size) && + (uio->uio_resid >= sizeof(struct dirent))) { + diroffset = uio->uio_offset; + transoffset = diroffset; + + blocknr = diroffset / blocksize; + blkoff = diroffset % blocksize; + error = nandfs_bread(node, blocknr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (EIO); + } + while (diroffset < file_size) { + DPRINTF(READDIR, ("readdir : offset = %"PRIu64"\n", + diroffset)); + if (blkoff >= blocksize) { + blkoff = 0; blocknr++; + brelse(bp); + error = nandfs_bread(node, blocknr, NOCRED, 0, + &bp); + if (error) { + brelse(bp); + return (EIO); + } + } + + /* Read in one dirent */ + pos = (uint8_t *)bp->b_data + blkoff; + ndirent = (struct nandfs_dir_entry *)pos; + + name_len = ndirent->name_len; + memset(&dirent, 0, sizeof(struct dirent)); + dirent.d_fileno = ndirent->inode; + if (dirent.d_fileno) { + dirent.d_type = ndirent->file_type; + dirent.d_namlen = name_len; + strncpy(dirent.d_name, ndirent->name, name_len); + dirent.d_reclen = GENERIC_DIRSIZ(&dirent); + DPRINTF(READDIR, ("copying `%*.*s`\n", name_len, + name_len, dirent.d_name)); + } + + /* + * If there isn't enough space in the uio to return a + * whole dirent, break off read + */ + if (uio->uio_resid < GENERIC_DIRSIZ(&dirent)) + break; + + /* Transfer */ + if (dirent.d_fileno) + uiomove(&dirent, GENERIC_DIRSIZ(&dirent), uio); + + /* Advance */ + diroffset += ndirent->rec_len; + blkoff += ndirent->rec_len; + + /* Remember the last entry we transfered */ + transoffset = diroffset; + } + brelse(bp); + + /* Pass on last transfered offset */ + uio->uio_offset = transoffset; + } + + if (ap->a_eofflag) + *ap->a_eofflag = (uio->uio_offset >= file_size); + + return (error); +} + +static int +nandfs_dirempty(struct vnode *dvp, uint64_t parentino, struct ucred *cred) +{ + struct nandfs_node *dnode = VTON(dvp); + struct nandfs_dir_entry *dirent; + uint64_t file_size = dnode->nn_inode.i_size; + uint64_t blockcount = dnode->nn_inode.i_blocks; + uint64_t blocknr; + uint32_t blocksize = dnode->nn_nandfsdev->nd_blocksize; + uint32_t limit; + uint32_t off; + uint8_t *pos; + struct buf *bp; + int error; + + DPRINTF(LOOKUP, ("%s: dvp %p parentino %#jx cred %p\n", __func__, dvp, + (uintmax_t)parentino, cred)); + + KASSERT((file_size != 0), ("nandfs_dirempty for NULL dir %p", dvp)); + + blocknr = 0; + while (blocknr < blockcount) { + error = nandfs_bread(dnode, blocknr, NOCRED, 0, &bp); + if (error) { + brelse(bp); + return (0); + } + + pos = (uint8_t *)bp->b_data; + off = 0; + + if (blocknr == (blockcount - 1)) + limit = file_size % blocksize; + else + limit = blocksize; + + while (off < limit) { + dirent = (struct nandfs_dir_entry *)(pos + off); + off += dirent->rec_len; + + if (dirent->inode == 0) + continue; + + switch (dirent->name_len) { + case 0: + break; + case 1: + if (dirent->name[0] != '.') + goto notempty; + + KASSERT(dirent->inode == dnode->nn_ino, + (".'s inode does not match dir")); + break; + case 2: + if (dirent->name[0] != '.' && + dirent->name[1] != '.') + goto notempty; + + KASSERT(dirent->inode == parentino, + ("..'s inode does not match parent")); + break; + default: + goto notempty; + } + } + + brelse(bp); + blocknr++; + } + + return (1); +notempty: + brelse(bp); + return (0); +} + +static int +nandfs_link(struct vop_link_args *ap) +{ + struct vnode *tdvp = ap->a_tdvp; + struct vnode *vp = ap->a_vp; + struct componentname *cnp = ap->a_cnp; + struct nandfs_node *node = VTON(vp); + struct nandfs_inode *inode = &node->nn_inode; + int error; + + if (tdvp->v_mount != vp->v_mount) + return (EXDEV); + + if (inode->i_links_count >= LINK_MAX) + return (EMLINK); + + if (inode->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); + + /* Update link count */ + inode->i_links_count++; + + /* Add dir entry */ + error = nandfs_add_dirent(tdvp, node->nn_ino, cnp->cn_nameptr, + cnp->cn_namelen, IFTODT(inode->i_mode)); + if (error) { + inode->i_links_count--; + } + + node->nn_flags |= IN_CHANGE; + nandfs_itimes(vp); + DPRINTF(VNCALL, ("%s: tdvp %p vp %p cnp %p\n", + __func__, tdvp, vp, cnp)); + + return (0); +} + +static int +nandfs_create(struct vop_create_args *ap) +{ + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode); + struct nandfs_node *dir_node = VTON(dvp); + struct nandfsmount *nmp = dir_node->nn_nmp; + struct nandfs_node *node; + int error; + + DPRINTF(VNCALL, ("%s: dvp %p\n", __func__, dvp)); + + if (nandfs_fs_full(dir_node->nn_nandfsdev)) + return (ENOSPC); + + /* Create new vnode/inode */ + error = nandfs_node_create(nmp, &node, mode); + if (error) + return (error); + node->nn_inode.i_gid = dir_node->nn_inode.i_gid; + node->nn_inode.i_uid = cnp->cn_cred->cr_uid; + + /* Add new dir entry */ + error = nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr, + cnp->cn_namelen, IFTODT(mode)); + if (error) { + if (nandfs_node_destroy(node)) { + nandfs_error("%s: error destroying node %p\n", + __func__, node); + } + return (error); + } + *vpp = NTOV(node); + + DPRINTF(VNCALL, ("created file vp %p nandnode %p ino %jx\n", *vpp, node, + (uintmax_t)node->nn_ino)); + return (0); +} + +static int +nandfs_remove(struct vop_remove_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct vnode *dvp = ap->a_dvp; + struct nandfs_node *node = VTON(vp); + struct nandfs_node *dnode = VTON(dvp); + struct componentname *cnp = ap->a_cnp; + + DPRINTF(VNCALL, ("%s: dvp %p vp %p nandnode %p ino %#jx link %d\n", + __func__, dvp, vp, node, (uintmax_t)node->nn_ino, + node->nn_inode.i_links_count)); + + if (vp->v_type == VDIR) + return (EISDIR); + + /* Files marked as immutable or append-only cannot be deleted. */ + if ((node->nn_inode.i_flags & (IMMUTABLE | APPEND | NOUNLINK)) || + (dnode->nn_inode.i_flags & APPEND)) + return (EPERM); + + nandfs_remove_dirent(dvp, node, cnp); + node->nn_inode.i_links_count--; + node->nn_flags |= IN_CHANGE; + + return (0); +} + +/* + * Check if source directory is in the path of the target directory. + * Target is supplied locked, source is unlocked. + * The target is always vput before returning. + */ +static int +nandfs_checkpath(struct nandfs_node *src, struct nandfs_node *dest, + struct ucred *cred) +{ + struct vnode *vp; + int error, rootino; + struct nandfs_dir_entry dirent; + + vp = NTOV(dest); + if (src->nn_ino == dest->nn_ino) { + error = EEXIST; + goto out; + } + rootino = NANDFS_ROOT_INO; + error = 0; + if (dest->nn_ino == rootino) + goto out; + + for (;;) { + if (vp->v_type != VDIR) { + error = ENOTDIR; + break; + } + + error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirent, + NANDFS_DIR_REC_LEN(2), (off_t)0, UIO_SYSSPACE, + IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, + NULL, NULL); + if (error != 0) + break; + if (dirent.name_len != 2 || + dirent.name[0] != '.' || + dirent.name[1] != '.') { + error = ENOTDIR; + break; + } + if (dirent.inode == src->nn_ino) { + error = EINVAL; + break; + } + if (dirent.inode == rootino) + break; + vput(vp); + if ((error = VFS_VGET(vp->v_mount, dirent.inode, + LK_EXCLUSIVE, &vp)) != 0) { + vp = NULL; + break; + } + } + +out: + if (error == ENOTDIR) + printf("checkpath: .. not a directory\n"); + if (vp != NULL) + vput(vp); + return (error); +} + +static int +nandfs_rename(struct vop_rename_args *ap) +{ + struct vnode *tvp = ap->a_tvp; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *fvp = ap->a_fvp; + struct vnode *fdvp = ap->a_fdvp; + struct componentname *tcnp = ap->a_tcnp; + struct componentname *fcnp = ap->a_fcnp; + int doingdirectory = 0, oldparent = 0, newparent = 0; + int error = 0; + + struct nandfs_node *fdnode, *fnode, *fnode1; + struct nandfs_node *tdnode = VTON(tdvp); + struct nandfs_node *tnode; + + uint32_t tdflags, fflags, fdflags; + uint16_t mode; + + DPRINTF(VNCALL, ("%s: fdvp:%p fvp:%p tdvp:%p tdp:%p\n", __func__, fdvp, + fvp, tdvp, tvp)); + + /* + * Check for cross-device rename. + */ + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; +abortit: + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + vrele(fdvp); + vrele(fvp); + return (error); + } + + tdflags = tdnode->nn_inode.i_flags; + if (tvp && + ((VTON(tvp)->nn_inode.i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || + (tdflags & APPEND))) { + error = EPERM; + goto abortit; + } + + /* + * Renaming a file to itself has no effect. The upper layers should + * not call us in that case. Temporarily just warn if they do. + */ + if (fvp == tvp) { + printf("nandfs_rename: fvp == tvp (can't happen)\n"); + error = 0; + goto abortit; + } + + if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0) + goto abortit; + + fdnode = VTON(fdvp); + fnode = VTON(fvp); + + if (fnode->nn_inode.i_links_count >= LINK_MAX) { + VOP_UNLOCK(fvp, 0); + error = EMLINK; + goto abortit; + } + + fflags = fnode->nn_inode.i_flags; + fdflags = fdnode->nn_inode.i_flags; + + if ((fflags & (NOUNLINK | IMMUTABLE | APPEND)) || + (fdflags & APPEND)) { + VOP_UNLOCK(fvp, 0); + error = EPERM; + goto abortit; + } + + mode = fnode->nn_inode.i_mode; + if ((mode & S_IFMT) == S_IFDIR) { + /* + * Avoid ".", "..", and aliases of "." for obvious reasons. + */ + + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || + (fdvp == fvp) || + ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT) || + (fnode->nn_flags & IN_RENAME)) { + VOP_UNLOCK(fvp, 0); + error = EINVAL; + goto abortit; + } + fnode->nn_flags |= IN_RENAME; + doingdirectory = 1; + DPRINTF(VNCALL, ("%s: doingdirectory dvp %p\n", __func__, + tdvp)); + oldparent = fdnode->nn_ino; + } + + vrele(fdvp); + + tnode = NULL; + if (tvp) + tnode = VTON(tvp); + + /* + * Bump link count on fvp while we are moving stuff around. If we + * crash before completing the work, the link count may be wrong + * but correctable. + */ + fnode->nn_inode.i_links_count++; + + /* Check for in path moving XXX */ + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread); + VOP_UNLOCK(fvp, 0); + if (oldparent != tdnode->nn_ino) + newparent = tdnode->nn_ino; + if (doingdirectory && newparent) { + if (error) /* write access check above */ + goto bad; + if (tnode != NULL) + vput(tvp); + + error = nandfs_checkpath(fnode, tdnode, tcnp->cn_cred); + if (error) + goto out; + + VREF(tdvp); + error = relookup(tdvp, &tvp, tcnp); + if (error) + goto out; + vrele(tdvp); + tdnode = VTON(tdvp); + tnode = NULL; + if (tvp) + tnode = VTON(tvp); + } + + /* + * If the target doesn't exist, link the target to the source and + * unlink the source. Otherwise, rewrite the target directory to + * reference the source and remove the original entry. + */ + + if (tvp == NULL) { + /* + * Account for ".." in new directory. + */ + if (doingdirectory && fdvp != tdvp) + tdnode->nn_inode.i_links_count++; + + DPRINTF(VNCALL, ("%s: new entry in dvp:%p\n", __func__, tdvp)); + /* + * Add name in new directory. + */ + error = nandfs_add_dirent(tdvp, fnode->nn_ino, tcnp->cn_nameptr, + tcnp->cn_namelen, IFTODT(fnode->nn_inode.i_mode)); + if (error) { + if (doingdirectory && fdvp != tdvp) + tdnode->nn_inode.i_links_count--; + goto bad; + } + + vput(tdvp); + } else { + /* + * If the parent directory is "sticky", then the user must + * own the parent directory, or the destination of the rename, + * otherwise the destination may not be changed (except by + * root). This implements append-only directories. + */ + if ((tdnode->nn_inode.i_mode & S_ISTXT) && + tcnp->cn_cred->cr_uid != 0 && + tcnp->cn_cred->cr_uid != tdnode->nn_inode.i_uid && + tnode->nn_inode.i_uid != tcnp->cn_cred->cr_uid) { + error = EPERM; + goto bad; + } + /* + * Target must be empty if a directory and have no links + * to it. Also, ensure source and target are compatible + * (both directories, or both not directories). + */ + mode = tnode->nn_inode.i_mode; + if ((mode & S_IFMT) == S_IFDIR) { + if (!nandfs_dirempty(tvp, tdnode->nn_ino, + tcnp->cn_cred)) { + error = ENOTEMPTY; + goto bad; + } + if (!doingdirectory) { + error = ENOTDIR; + goto bad; + } + /* + * Update name cache since directory is going away. + */ + cache_purge(tdvp); + } else if (doingdirectory) { + error = EISDIR; + goto bad; + } + + DPRINTF(VNCALL, ("%s: update entry dvp:%p\n", __func__, tdvp)); + /* + * Change name tcnp in tdvp to point at fvp. + */ + error = nandfs_update_dirent(tdvp, fnode, tnode); + if (error) + goto bad; + + if (doingdirectory && !newparent) + tdnode->nn_inode.i_links_count--; + + vput(tdvp); + + tnode->nn_inode.i_links_count--; + vput(tvp); + tnode = NULL; + } + + /* + * Unlink the source. + */ + fcnp->cn_flags &= ~MODMASK; + fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + VREF(fdvp); + error = relookup(fdvp, &fvp, fcnp); + if (error == 0) + vrele(fdvp); + if (fvp != NULL) { + fnode1 = VTON(fvp); + fdnode = VTON(fdvp); + } else { + /* + * From name has disappeared. + */ + if (doingdirectory) + panic("nandfs_rename: lost dir entry"); + vrele(ap->a_fvp); + return (0); + } + + DPRINTF(VNCALL, ("%s: unlink source fnode:%p\n", __func__, fnode)); + + /* + * Ensure that the directory entry still exists and has not + * changed while the new name has been entered. If the source is + * a file then the entry may have been unlinked or renamed. In + * either case there is no further work to be done. If the source + * is a directory then it cannot have been rmdir'ed; its link + * count of three would cause a rmdir to fail with ENOTEMPTY. + * The IN_RENAME flag ensures that it cannot be moved by another + * rename. + */ + if (fnode != fnode1) { + if (doingdirectory) + panic("nandfs: lost dir entry"); + } else { + /* + * If the source is a directory with a + * new parent, the link count of the old + * parent directory must be decremented + * and ".." set to point to the new parent. + */ + if (doingdirectory && newparent) { + DPRINTF(VNCALL, ("%s: new parent %#jx -> %#jx\n", + __func__, (uintmax_t) oldparent, + (uintmax_t) newparent)); + error = nandfs_update_parent_dir(fvp, newparent); + if (!error) { + fdnode->nn_inode.i_links_count--; + fdnode->nn_flags |= IN_CHANGE; + } + } + error = nandfs_remove_dirent(fdvp, fnode, fcnp); + if (!error) { + fnode->nn_inode.i_links_count--; + fnode->nn_flags |= IN_CHANGE; + } + fnode->nn_flags &= ~IN_RENAME; + } + if (fdnode) + vput(fdvp); + if (fnode) + vput(fvp); + vrele(ap->a_fvp); + return (error); + +bad: + DPRINTF(VNCALL, ("%s: error:%d\n", __func__, error)); + if (tnode) + vput(NTOV(tnode)); + vput(NTOV(tdnode)); +out: + if (doingdirectory) + fnode->nn_flags &= ~IN_RENAME; + if (vn_lock(fvp, LK_EXCLUSIVE) == 0) { + fnode->nn_inode.i_links_count--; + fnode->nn_flags |= IN_CHANGE; + fnode->nn_flags &= ~IN_RENAME; + vput(fvp); + } else + vrele(fvp); + return (error); +} + +static int +nandfs_mkdir(struct vop_mkdir_args *ap) +{ + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct nandfs_node *dir_node = VTON(dvp); + struct nandfs_inode *dir_inode = &dir_node->nn_inode; + struct nandfs_node *node; + struct nandfsmount *nmp = dir_node->nn_nmp; + uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode); + int error; + + DPRINTF(VNCALL, ("%s: dvp %p\n", __func__, dvp)); + + if (nandfs_fs_full(dir_node->nn_nandfsdev)) + return (ENOSPC); + + if (dir_inode->i_links_count >= LINK_MAX) + return (EMLINK); + + error = nandfs_node_create(nmp, &node, mode); + if (error) + return (error); + + node->nn_inode.i_gid = dir_node->nn_inode.i_gid; + node->nn_inode.i_uid = cnp->cn_cred->cr_uid; + + *vpp = NTOV(node); + + error = nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr, + cnp->cn_namelen, IFTODT(mode)); + if (error) { + vput(*vpp); + return (error); + } + + dir_node->nn_inode.i_links_count++; + dir_node->nn_flags |= IN_CHANGE; + + error = nandfs_init_dir(NTOV(node), node->nn_ino, dir_node->nn_ino); + if (error) { + vput(NTOV(node)); + return (error); + } + + DPRINTF(VNCALL, ("created dir vp %p nandnode %p ino %jx\n", *vpp, node, + (uintmax_t)node->nn_ino)); + return (0); +} + +static int +nandfs_mknod(struct vop_mknod_args *ap) +{ + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + uint16_t mode = MAKEIMODE(vap->va_type, vap->va_mode); + struct componentname *cnp = ap->a_cnp; + struct nandfs_node *dir_node = VTON(dvp); + struct nandfsmount *nmp = dir_node->nn_nmp; + struct nandfs_node *node; + int error; + + if (nandfs_fs_full(dir_node->nn_nandfsdev)) + return (ENOSPC); + + error = nandfs_node_create(nmp, &node, mode); + if (error) + return (error); + node->nn_inode.i_gid = dir_node->nn_inode.i_gid; + node->nn_inode.i_uid = cnp->cn_cred->cr_uid; + if (vap->va_rdev != VNOVAL) + node->nn_inode.i_special = vap->va_rdev; + + *vpp = NTOV(node); + + if (nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr, + cnp->cn_namelen, IFTODT(mode))) { + vput(*vpp); + return (ENOTDIR); + } + + node->nn_flags |= IN_ACCESS | IN_CHANGE | IN_UPDATE; + + return (0); +} + +static int +nandfs_symlink(struct vop_symlink_args *ap) +{ + struct vnode **vpp = ap->a_vpp; + struct vnode *dvp = ap->a_dvp; + uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode); + struct componentname *cnp = ap->a_cnp; + struct nandfs_node *dir_node = VTON(dvp); + struct nandfsmount *nmp = dir_node->nn_nmp; + struct nandfs_node *node; + int len, error; + + if (nandfs_fs_full(dir_node->nn_nandfsdev)) + return (ENOSPC); + + error = nandfs_node_create(nmp, &node, S_IFLNK | mode); + if (error) + return (error); + node->nn_inode.i_gid = dir_node->nn_inode.i_gid; + node->nn_inode.i_uid = cnp->cn_cred->cr_uid; + + *vpp = NTOV(node); + + if (nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr, + cnp->cn_namelen, IFTODT(mode))) { + vput(*vpp); + return (ENOTDIR); + } + + + len = strlen(ap->a_target); + error = vn_rdwr(UIO_WRITE, *vpp, ap->a_target, len, (off_t)0, + UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, + cnp->cn_cred, NOCRED, NULL, NULL); + if (error) + vput(*vpp); + + return (error); +} + +static int +nandfs_readlink(struct vop_readlink_args *ap) +{ + struct vnode *vp = ap->a_vp; + + return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); +} + +static int +nandfs_rmdir(struct vop_rmdir_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + struct nandfs_node *node, *dnode; + uint32_t dflag, flag; + int error = 0; + + node = VTON(vp); + dnode = VTON(dvp); + + /* Files marked as immutable or append-only cannot be deleted. */ + if ((node->nn_inode.i_flags & (IMMUTABLE | APPEND | NOUNLINK)) || + (dnode->nn_inode.i_flags & APPEND)) + return (EPERM); + + DPRINTF(VNCALL, ("%s: dvp %p vp %p nandnode %p ino %#jx\n", __func__, + dvp, vp, node, (uintmax_t)node->nn_ino)); + + if (node->nn_inode.i_links_count < 2) + return (EINVAL); + + if (!nandfs_dirempty(vp, dnode->nn_ino, cnp->cn_cred)) + return (ENOTEMPTY); + + /* Files marked as immutable or append-only cannot be deleted. */ + dflag = dnode->nn_inode.i_flags; + flag = node->nn_inode.i_flags; + if ((dflag & APPEND) || + (flag & (NOUNLINK | IMMUTABLE | APPEND))) { + return (EPERM); + } + + if (vp->v_mountedhere != 0) + return (EINVAL); + + nandfs_remove_dirent(dvp, node, cnp); + dnode->nn_inode.i_links_count -= 1; + dnode->nn_flags |= IN_CHANGE; + + cache_purge(dvp); + + error = nandfs_truncate(vp, (uint64_t)0); + if (error) + return (error); + + node->nn_inode.i_links_count -= 2; + node->nn_flags |= IN_CHANGE; + + cache_purge(vp); + + return (error); +} + +static int +nandfs_fsync(struct vop_fsync_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + int locked; + + DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx\n", __func__, vp, + node, (uintmax_t)node->nn_ino)); + + /* + * Start syncing vnode only if inode was modified or + * there are some dirty buffers + */ + if (VTON(vp)->nn_flags & IN_MODIFIED || + vp->v_bufobj.bo_dirty.bv_cnt) { + locked = VOP_ISLOCKED(vp); + VOP_UNLOCK(vp, 0); + nandfs_wakeup_wait_sync(node->nn_nandfsdev, SYNCER_FSYNC); + VOP_LOCK(vp, locked | LK_RETRY); + } + + return (0); +} + +static int +nandfs_bmap(struct vop_bmap_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *nnode = VTON(vp); + struct nandfs_device *nandfsdev = nnode->nn_nandfsdev; + nandfs_daddr_t l2vmap, v2pmap; + int error; + int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE; + + DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx\n", __func__, vp, + nnode, (uintmax_t)nnode->nn_ino)); + + if (ap->a_bop != NULL) + *ap->a_bop = &nandfsdev->nd_devvp->v_bufobj; + if (ap->a_bnp == NULL) + return (0); + if (ap->a_runp != NULL) + *ap->a_runp = 0; + if (ap->a_runb != NULL) + *ap->a_runb = 0; + + /* + * Translate all the block sectors into a series of buffers to read + * asynchronously from the nandfs device. Note that this lookup may + * induce readin's too. + */ + + /* Get virtual block numbers for the vnode's buffer span */ + error = nandfs_bmap_lookup(nnode, ap->a_bn, &l2vmap); + if (error) + return (-1); + + /* Translate virtual block numbers to physical block numbers */ + error = nandfs_vtop(nnode, l2vmap, &v2pmap); + if (error) + return (-1); + + /* Note virtual block 0 marks not mapped */ + if (l2vmap == 0) + *ap->a_bnp = -1; + else + *ap->a_bnp = v2pmap * blk2dev; /* in DEV_BSIZE */ + + DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx lblk %jx -> blk %jx\n", + __func__, vp, nnode, (uintmax_t)nnode->nn_ino, (uintmax_t)ap->a_bn, + (uintmax_t)*ap->a_bnp )); + + return (0); +} + +static void +nandfs_force_syncer(struct nandfsmount *nmp) +{ + + nmp->nm_flags |= NANDFS_FORCE_SYNCER; + nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_FFORCE); +} + +static int +nandfs_ioctl(struct vop_ioctl_args *ap) +{ + struct vnode *vp = ap->a_vp; + u_long command = ap->a_command; + caddr_t data = ap->a_data; + struct nandfs_node *node = VTON(vp); + struct nandfs_device *nandfsdev = node->nn_nandfsdev; + struct nandfsmount *nmp = node->nn_nmp; + uint64_t *tab, *cno; + struct nandfs_seg_stat *nss; + struct nandfs_cpmode *ncpm; + struct nandfs_argv *nargv; + struct nandfs_cpstat *ncp; + int error; + + DPRINTF(VNCALL, ("%s: %x\n", __func__, (uint32_t)command)); + + error = priv_check(ap->a_td, PRIV_VFS_MOUNT); + if (error) + return (error); + + if (nmp->nm_ronly) { + switch (command) { + case NANDFS_IOCTL_GET_FSINFO: + case NANDFS_IOCTL_GET_SUSTAT: + case NANDFS_IOCTL_GET_CPINFO: + case NANDFS_IOCTL_GET_CPSTAT: + case NANDFS_IOCTL_GET_SUINFO: + case NANDFS_IOCTL_GET_VINFO: + case NANDFS_IOCTL_GET_BDESCS: + break; + default: + return (EROFS); + } + } + + switch (command) { + case NANDFS_IOCTL_GET_FSINFO: + error = nandfs_get_fsinfo(nmp, (struct nandfs_fsinfo *)data); + break; + case NANDFS_IOCTL_GET_SUSTAT: + nss = (struct nandfs_seg_stat *)data; + error = nandfs_get_seg_stat(nandfsdev, nss); + break; + case NANDFS_IOCTL_CHANGE_CPMODE: + ncpm = (struct nandfs_cpmode *)data; + error = nandfs_chng_cpmode(nandfsdev->nd_cp_node, ncpm); + nandfs_force_syncer(nmp); + break; + case NANDFS_IOCTL_GET_CPINFO: + nargv = (struct nandfs_argv *)data; + error = nandfs_get_cpinfo_ioctl(nandfsdev->nd_cp_node, nargv); + break; + case NANDFS_IOCTL_DELETE_CP: + tab = (uint64_t *)data; + error = nandfs_delete_cp(nandfsdev->nd_cp_node, tab[0], tab[1]); + nandfs_force_syncer(nmp); + break; + case NANDFS_IOCTL_GET_CPSTAT: + ncp = (struct nandfs_cpstat *)data; + error = nandfs_get_cpstat(nandfsdev->nd_cp_node, ncp); + break; + case NANDFS_IOCTL_GET_SUINFO: + nargv = (struct nandfs_argv *)data; + error = nandfs_get_segment_info_ioctl(nandfsdev, nargv); + break; + case NANDFS_IOCTL_GET_VINFO: + nargv = (struct nandfs_argv *)data; + error = nandfs_get_dat_vinfo_ioctl(nandfsdev, nargv); + break; + case NANDFS_IOCTL_GET_BDESCS: + nargv = (struct nandfs_argv *)data; + error = nandfs_get_dat_bdescs_ioctl(nandfsdev, nargv); + break; + case NANDFS_IOCTL_SYNC: + cno = (uint64_t *)data; + nandfs_force_syncer(nmp); + *cno = nandfsdev->nd_last_cno; + error = 0; + break; + case NANDFS_IOCTL_MAKE_SNAP: + cno = (uint64_t *)data; + error = nandfs_make_snap(nandfsdev, cno); + nandfs_force_syncer(nmp); + break; + case NANDFS_IOCTL_DELETE_SNAP: + cno = (uint64_t *)data; + error = nandfs_delete_snap(nandfsdev, *cno); + nandfs_force_syncer(nmp); + break; + default: + error = ENOTTY; + break; + } + + return (error); +} + +/* + * Whiteout vnode call + */ +static int +nandfs_whiteout(struct vop_whiteout_args *ap) +{ + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + int error = 0; + + switch (ap->a_flags) { + case LOOKUP: + return (0); + case CREATE: + /* Create a new directory whiteout */ +#ifdef INVARIANTS + if ((cnp->cn_flags & SAVENAME) == 0) + panic("ufs_whiteout: missing name"); +#endif + error = nandfs_add_dirent(dvp, NANDFS_WHT_INO, cnp->cn_nameptr, + cnp->cn_namelen, DT_WHT); + break; + + case DELETE: + /* Remove an existing directory whiteout */ + cnp->cn_flags &= ~DOWHITEOUT; + error = nandfs_remove_dirent(dvp, NULL, cnp); + break; + default: + panic("nandf_whiteout: unknown op: %d", ap->a_flags); + } + + return (error); +} + +static int +nandfs_pathconf(struct vop_pathconf_args *ap) +{ + int error; + + error = 0; + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = LINK_MAX; + break; + case _PC_NAME_MAX: + *ap->a_retval = NAME_MAX; + break; + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; + break; + case _PC_PIPE_BUF: + *ap->a_retval = PIPE_BUF; + break; + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + break; + case _PC_NO_TRUNC: + *ap->a_retval = 1; + break; + case _PC_ACL_EXTENDED: + *ap->a_retval = 0; + break; + case _PC_ALLOC_SIZE_MIN: + *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_bsize; + break; + case _PC_FILESIZEBITS: + *ap->a_retval = 64; + break; + case _PC_REC_INCR_XFER_SIZE: + *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize; + break; + case _PC_REC_MAX_XFER_SIZE: + *ap->a_retval = -1; /* means ``unlimited'' */ + break; + case _PC_REC_MIN_XFER_SIZE: + *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize; + break; + default: + error = EINVAL; + break; + } + return (error); +} + +static int +nandfs_vnlock1(struct vop_lock1_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + int error, vi_locked; + + /* + * XXX can vnode go away while we are sleeping? + */ + vi_locked = mtx_owned(&vp->v_interlock); + if (vi_locked) + VI_UNLOCK(vp); + error = NANDFS_WRITELOCKFLAGS(node->nn_nandfsdev, + ap->a_flags & LK_NOWAIT); + if (vi_locked && !error) + VI_LOCK(vp); + if (error) + return (error); + + error = vop_stdlock(ap); + if (error) { + NANDFS_WRITEUNLOCK(node->nn_nandfsdev); + return (error); + } + + return (0); +} + +static int +nandfs_vnunlock(struct vop_unlock_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + int error; + + error = vop_stdunlock(ap); + if (error) + return (error); + + NANDFS_WRITEUNLOCK(node->nn_nandfsdev); + + return (0); +} + +/* + * Global vfs data structures + */ +struct vop_vector nandfs_vnodeops = { + .vop_default = &default_vnodeops, + .vop_access = nandfs_access, + .vop_advlock = nandfs_advlock, + .vop_bmap = nandfs_bmap, + .vop_close = nandfs_close, + .vop_create = nandfs_create, + .vop_fsync = nandfs_fsync, + .vop_getattr = nandfs_getattr, + .vop_inactive = nandfs_inactive, + .vop_cachedlookup = nandfs_lookup, + .vop_ioctl = nandfs_ioctl, + .vop_link = nandfs_link, + .vop_lookup = vfs_cache_lookup, + .vop_mkdir = nandfs_mkdir, + .vop_mknod = nandfs_mknod, + .vop_open = nandfs_open, + .vop_pathconf = nandfs_pathconf, + .vop_print = nandfs_print, + .vop_read = nandfs_read, + .vop_readdir = nandfs_readdir, + .vop_readlink = nandfs_readlink, + .vop_reclaim = nandfs_reclaim, + .vop_remove = nandfs_remove, + .vop_rename = nandfs_rename, + .vop_rmdir = nandfs_rmdir, + .vop_whiteout = nandfs_whiteout, + .vop_write = nandfs_write, + .vop_setattr = nandfs_setattr, + .vop_strategy = nandfs_strategy, + .vop_symlink = nandfs_symlink, + .vop_lock1 = nandfs_vnlock1, + .vop_unlock = nandfs_vnunlock, +}; + +struct vop_vector nandfs_system_vnodeops = { + .vop_default = &default_vnodeops, + .vop_close = nandfs_close, + .vop_inactive = nandfs_inactive, + .vop_reclaim = nandfs_reclaim, + .vop_strategy = nandfs_strategy, + .vop_fsync = nandfs_fsync, + .vop_bmap = nandfs_bmap, + .vop_access = VOP_PANIC, + .vop_advlock = VOP_PANIC, + .vop_create = VOP_PANIC, + .vop_getattr = VOP_PANIC, + .vop_cachedlookup = VOP_PANIC, + .vop_ioctl = VOP_PANIC, + .vop_link = VOP_PANIC, + .vop_lookup = VOP_PANIC, + .vop_mkdir = VOP_PANIC, + .vop_mknod = VOP_PANIC, + .vop_open = VOP_PANIC, + .vop_pathconf = VOP_PANIC, + .vop_print = VOP_PANIC, + .vop_read = VOP_PANIC, + .vop_readdir = VOP_PANIC, + .vop_readlink = VOP_PANIC, + .vop_remove = VOP_PANIC, + .vop_rename = VOP_PANIC, + .vop_rmdir = VOP_PANIC, + .vop_whiteout = VOP_PANIC, + .vop_write = VOP_PANIC, + .vop_setattr = VOP_PANIC, + .vop_symlink = VOP_PANIC, +}; + +static int +nandfsfifo_close(struct vop_close_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct nandfs_node *node = VTON(vp); + + DPRINTF(VNCALL, ("%s: vp %p node %p\n", __func__, vp, node)); + + mtx_lock(&vp->v_interlock); + if (vp->v_usecount > 1) + nandfs_itimes_locked(vp); + mtx_unlock(&vp->v_interlock); + + return (fifo_specops.vop_close(ap)); +} + +struct vop_vector nandfs_fifoops = { + .vop_default = &fifo_specops, + .vop_fsync = VOP_PANIC, + .vop_access = nandfs_access, + .vop_close = nandfsfifo_close, + .vop_getattr = nandfs_getattr, + .vop_inactive = nandfs_inactive, + .vop_print = nandfs_print, + .vop_read = VOP_PANIC, + .vop_reclaim = nandfs_reclaim, + .vop_setattr = nandfs_setattr, + .vop_write = VOP_PANIC, + .vop_lock1 = nandfs_vnlock1, + .vop_unlock = nandfs_vnunlock, +}; + +int +nandfs_vinit(struct vnode *vp, uint64_t ino) +{ + struct nandfs_node *node; + + ASSERT_VOP_LOCKED(vp, __func__); + + node = VTON(vp); + + /* Check if we're fetching the root */ + if (ino == NANDFS_ROOT_INO) + vp->v_vflag |= VV_ROOT; + + if (ino != NANDFS_GC_INO) + vp->v_type = IFTOVT(node->nn_inode.i_mode); + else + vp->v_type = VREG; + + if (vp->v_type == VFIFO) + vp->v_op = &nandfs_fifoops; + + return (0); +} diff --git a/sys/modules/Makefile b/sys/modules/Makefile index ed32b1c..05d3a45 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -217,6 +217,8 @@ SUBDIR= ${_3dfx} \ ${_mwlfw} \ mxge \ my \ + ${_nandfs} \ + ${_nandsim} \ ${_ncp} \ ${_ncv} \ ${_ndis} \ @@ -335,7 +337,6 @@ SUBDIR= ${_3dfx} \ vx \ ${_vxge} \ wb \ - ${_wbwd} \ ${_wi} \ wlan \ wlan_acl \ @@ -398,6 +399,11 @@ _ipdivert= ipdivert _ipfw= ipfw .endif +.if ${MK_NAND} != "no" || defined(ALL_MODULES) +_nandfs= nandfs +_nandsim= nandsim +.endif + .if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES) _netgraph= netgraph .endif @@ -513,7 +519,6 @@ _stg= stg _streams= streams _svr4= svr4 _vxge= vxge -_wbwd= wbwd _wi= wi _xe= xe .if ${MK_ZFS} != "no" || defined(ALL_MODULES) @@ -708,7 +713,6 @@ _viawd= viawd _virtio= virtio _vxge= vxge _x86bios= x86bios -_wbwd= wbwd _wi= wi _wpi= wpi .if ${MK_SOURCELESS_UCODE} != "no" diff --git a/sys/modules/nandfs/Makefile b/sys/modules/nandfs/Makefile new file mode 100644 index 0000000..3c67539 --- /dev/null +++ b/sys/modules/nandfs/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../fs/nandfs + +KMOD= nandfs +SRCS= vnode_if.h \ + bmap.c nandfs_bmap.c nandfs_dir.c nandfs_subr.c nandfs_vfsops.c \ + nandfs_vnops.c nandfs_alloc.c nandfs_cpfile.c nandfs_dat.c \ + nandfs_ifile.c nandfs_segment.c nandfs_sufile.c nandfs_buffer.c \ + nandfs_cleaner.c + +.include diff --git a/sys/modules/nandsim/Makefile b/sys/modules/nandsim/Makefile new file mode 100644 index 0000000..1dc9501 --- /dev/null +++ b/sys/modules/nandsim/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/nand + +KMOD= nandsim +SRCS= nandsim.c nandsim_chip.c nandsim_swap.c nandsim_ctrl.c nandsim_log.c\ + bus_if.h device_if.h vnode_if.h nfc_if.h nand_if.h +MFILES= kern/bus_if.m kern/device_if.m\ + dev/nand/nfc_if.m dev/nand/nand_if.m + +.include diff --git a/tools/build/options/WITHOUT_NAND b/tools/build/options/WITHOUT_NAND new file mode 100644 index 0000000..67cadaf --- /dev/null +++ b/tools/build/options/WITHOUT_NAND @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set to not build the NAND Flash components. diff --git a/tools/build/options/WITH_NAND b/tools/build/options/WITH_NAND new file mode 100644 index 0000000..527d43d --- /dev/null +++ b/tools/build/options/WITH_NAND @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set to build the NAND Flash components. diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 68b82e7..ded1725 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -223,6 +223,11 @@ SUBDIR+= lpr SUBDIR+= manctl .endif +.if ${MK_NAND} != "no" +SUBDIR+= nandsim +SUBDIR+= nandtool +.endif + .if ${MK_NETGRAPH} != "no" SUBDIR+= flowctl SUBDIR+= lmcconfig diff --git a/usr.sbin/nandsim/Makefile b/usr.sbin/nandsim/Makefile new file mode 100644 index 0000000..9269ab5 --- /dev/null +++ b/usr.sbin/nandsim/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= nandsim +SRCS= nandsim.c nandsim_rcfile.c nandsim_cfgparse.c +BINDIR= /usr/sbin +MAN= nandsim.8 + +.include diff --git a/usr.sbin/nandsim/nandsim.8 b/usr.sbin/nandsim/nandsim.8 new file mode 100644 index 0000000..5e86299 --- /dev/null +++ b/usr.sbin/nandsim/nandsim.8 @@ -0,0 +1,230 @@ +.\" Copyright (c) 2010 Semihalf +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 10, 2010 +.Dt NANDSIM 8 +.Os +.Sh NAME +.Nm nandsim +.Nd NAND simulator control program +.Sh SYNOPSIS +.Nm +.Ic status +.Aq ctrl_no | Fl -all | Fl a +.Op Fl v +.Nm +.Ic conf +.Aq filename +.Nm +.Ic start +.Aq ctrl_no +.Nm +.Ic mod +.Aq ctrl_no:cs_no | Fl l Aq loglevel +.Op Fl p Aq prog_time +.Op Fl e Aq erase_time +.Op Fl r Aq read_time +.Op Fl E Aq error_ratio +.Op Fl h +.Nm +.Ic stop +.Aq ctrl_no +.Nm +.Ic error +.Aq ctrl_no:cs_no +.Aq page_num +.Aq column +.Aq length +.Aq pattern +.Nm +.Ic bb +.Aq ctrl_no:cs_no +.Op blk_num,blk_num2,... +.Op Fl U +.Op Fl L +.Nm +.Ic freeze +.Op ctrl_no +.Nm +.Ic log +.Aq ctrl_no | Fl -all | Fl a +.Nm +.Ic stats +.Aq ctrl_no:cs_no +.Aq page_num +.Nm +.Ic dump +.Aq ctrl_no:cs_no +.Aq filename +.Nm +.Ic restore +.Aq ctrl_no:chip_no +.Aq filename +.Nm +.Ic destroy +.Aq ctrl_no[:cs_no] | Fl -all | Fl a +.Nm +.Ic help +.Op Fl v +.Sh COMMAND DESCRIPTION +Controllers and chips are arranged into a simple hierarchy. +There can be up to 4 controllers configured, each with 4 chip select (CS) lines. +A given chip is connected to one of the chip selects. +.Pp +Controllers are specified as +.Aq ctrl_no ; +chip selects are specified as +.Aq cs_no . +.Bl -tag -width periphlist +.It Ic status +Gets controller(s) status. If +.Fl a +or +.Fl -all +flag is specified - command will print status of every controller +currently available. +Optional flag +.Fl v +causes printing complete information about the controller, and all +chips attached to it. +.It Ic conf +Reads simulator configuration from a specified file (this includes +the simulation "layout" i.e. controllers-chips assignments). +Configuration changes for an already started simulation require a +full stop-start cycle in order to take effect i.e.: +.Pp +.Bl -column +.It nandsim stop ... +.It nandsim destroy ... +.Pp +.It << edit config file >> +.Pp +.It nandsim conf ... +.It nandsim start ... +.El +.It Ic mod +Alters simulator parameters on-the-fly. +If controller number and CS pair is not specified, the general +simulator parameters (not specific to a controller or a chip) will be modified. +Changing chip's parameters requires specifying both controller number and CS +to which the given chip is connected. +Parameters which can be altered: +.Pp +General simulator related: +.Bl -tag -width flag +.It Fl l Aq log_level +change logging level to +.Aq log_level +.El +.Pp +Chip related: +.Bl -tag -width flag +.It Fl p Aq prog_time +change prog time for specified chip to +.Aq prog_time +.It Fl e Aq erase_time +change erase time for specified chip to +.Aq erase_time +.It Fl r Aq read_time +change read time for specified chip to +.Aq read_time +.It Fl E Aq error_ratio +change error ratio for specified chip to +.Aq error_ratio . +Error ratio is a number of errors per million read/write bytes. +.El +.Pp +Additionally, flag +.Fl h +will list parameters which can be altered. +.El +.Bl -tag -width periphlist +.It Ic bb +Marks/unmarks a specified block as bad. +To mark/unmark the bad condition an a block, the following parameters +have to be supplied: controller number, CS number, and at least one +block number. +It is possible to specify multiple blocks, by separating blocks numbers +with a comma. +The following options can be used for the 'bb' command: +.Bl -tag -width flag +.It Fl U +unmark the bad previously marked block as bad. +.It Fl L +list all blocks marked as bad on a given chip. +.El +.It Ic log +Prints activity log of the specified controller to stdout; if +controller number is not specifed, logs for all available +controllers are printed. +.It Ic stats +Print statistics of the selected controller, chip and page. +Statistics includes read count, write count, raw read count, raw +write count, ECC stats (succeeded corrections, failed correction). +.It Ic dump +Dumps a snaphot of a single chip (including data and bad blocks +information, wearout level) into the file. +.It Ic restore +Restores chip state from a dump-file snapshot (produced previously +with the 'dump' command). +.It Ic start +Starts a controller i.e. the simulation. +.It Ic stop +Stops an already started controller; if the controller number is not +supplied, attempts to stop all currently working controllers. +.It Ic destroy +Removes existing active chip/controller and its configuration from +memory and releases the resources. +Specifying flag +.Fl a +or +.Fl -all +causes removal of every chip and controller. +Controller must be stopped in order to be destroyed. +.It Ic error +Directly overwrites a certain number of bytes in the specified page +at a given offset with a supplied pattern (which mimics the +corruption of flash contents). +.It Ic help +Prints synopsis, +.Fl v +gives more verbose output. +.It Ic freeze +Stops simulation of given controller (simulates power-loss). +All commands issues to any chip on this controller are ignored. +.El +.Sh SEE ALSO +.Xr nand 4 , +.Xr nandsim 4 +.Xr nandsim.conf 5 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 10.0 . +.Sh AUTHOR +This utility was written by +.An Lukasz Wojcik . diff --git a/usr.sbin/nandsim/nandsim.c b/usr.sbin/nandsim/nandsim.c new file mode 100644 index 0000000..082085f --- /dev/null +++ b/usr.sbin/nandsim/nandsim.c @@ -0,0 +1,1397 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Control application for the NAND simulator. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nandsim_cfgparse.h" + +#define SIMDEVICE "/dev/nandsim.ioctl" + +#define error(fmt, args...) do { \ + printf("ERROR: " fmt "\n", ##args); } while (0) + +#define warn(fmt, args...) do { \ + printf("WARNING: " fmt "\n", ##args); } while (0) + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define debug(fmt, args...) do { \ + printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0) +#else +#define debug(fmt, args...) do {} while(0) +#endif + +#define NANDSIM_RAM_LOG_SIZE 16384 + +#define MSG_NOTRUNNING "Controller#%d is not running.Please start" \ + " it first." +#define MSG_RUNNING "Controller#%d is already running!" +#define MSG_CTRLCHIPNEEDED "You have to specify ctrl_no:cs_no pair!" +#define MSG_STATUSACQCTRLCHIP "Could not acquire status for ctrl#%d chip#%d" +#define MSG_STATUSACQCTRL "Could not acquire status for ctrl#%d" +#define MSG_NOCHIP "There is no such chip configured (chip#%d "\ + "at ctrl#%d)!" + +#define MSG_NOCTRL "Controller#%d is not configured!" +#define MSG_NOTCONFIGDCTRLCHIP "Chip connected to ctrl#%d at cs#%d " \ + "is not configured." + +typedef int (commandfunc_t)(int , char **); + +static struct nandsim_command *getcommand(char *); +static int parse_devstring(char *, int *, int *); +static void printchip(struct sim_chip *, uint8_t); +static void printctrl(struct sim_ctrl *); +static int opendev(int *); +static commandfunc_t cmdstatus; +static commandfunc_t cmdconf; +static commandfunc_t cmdstart; +static commandfunc_t cmdstop; +static commandfunc_t cmdmod; +static commandfunc_t cmderror; +static commandfunc_t cmdbb; +static commandfunc_t cmdfreeze; +static commandfunc_t cmdlog; +static commandfunc_t cmdstats; +static commandfunc_t cmddump; +static commandfunc_t cmdrestore; +static commandfunc_t cmddestroy; +static commandfunc_t cmdhelp; +static int checkusage(int, int, char **); +static int is_chip_created(int, int, int *); +static int is_ctrl_created(int, int *); +static int is_ctrl_running(int, int *); +static int assert_chip_connected(int , int); +static int printstats(int, int, uint32_t, int); + +struct nandsim_command { + const char *cmd_name; /* Command name */ + commandfunc_t *commandfunc; /* Ptr to command function */ + uint8_t req_argc; /* Mandatory arguments count */ + const char *usagestring; /* Usage string */ +}; + +static struct nandsim_command commands[] = { + {"status", cmdstatus, 1, + "status [-v]\n" }, + {"conf", cmdconf, 1, + "conf \n" }, + {"start", cmdstart, 1, + "start \n" }, + {"mod", cmdmod, 2, + "mod [-l ] | [-p ]\n" + "\t[-e ] [-r ]\n" + "\t[-E ] | [-h]\n" }, + {"stop", cmdstop, 1, + "stop \n" }, + {"error", cmderror, 5, + "error \n" }, + {"bb", cmdbb, 2, + "bb [blk_num1,blk_num2,..] [-U] [-L]\n" }, + {"freeze", cmdfreeze, 1, + "freeze [ctrl_no]\n" }, + {"log", cmdlog, 1, + "log \n" }, + {"stats", cmdstats, 2, + "stats \n" }, + {"dump", cmddump, 2, + "dump \n" }, + {"restore", cmdrestore, 2, + "restore \n" }, + {"destroy", cmddestroy, 1, + "destroy \n" }, + {"help", cmdhelp, 0, + "help [-v]" }, + {NULL, NULL, 0, NULL}, +}; + + +/* Parse command name, and start appropriate function */ +static struct nandsim_command* +getcommand(char *arg) +{ + struct nandsim_command *opts; + + for (opts = commands; (opts != NULL) && + (opts->cmd_name != NULL); opts++) { + if (strcmp(opts->cmd_name, arg) == 0) + return (opts); + } + return (NULL); +} + +/* + * Parse given string in format :, if possible -- set + * ctrl and/or cs, and return 0 (success) or 1 (in case of error). + * + * ctrl == 0xff && chip == 0xff : '--all' flag specified + * ctrl != 0xff && chip != 0xff : both ctrl & chip were specified + * ctrl != 0xff && chip == 0xff : only ctrl was specified + */ +static int +parse_devstring(char *str, int *ctrl, int *cs) +{ + char *tmpstr; + unsigned int num = 0; + + /* Ignore white spaces at the beginning */ + while (isspace(*str) && (*str != '\0')) + str++; + + *ctrl = 0xff; + *cs = 0xff; + if (strcmp(str, "--all") == 0 || + strcmp(str, "-a") == 0) { + /* If --all or -a is specified, ctl==chip==0xff */ + debug("CTRL=%d CHIP=%d\n", *ctrl, *cs); + return (0); + } + /* Separate token and try to convert it to int */ + tmpstr = (char *)strtok(str, ":"); + if ((tmpstr != NULL) && (*tmpstr != '\0')) { + if (convert_arguint(tmpstr, &num) != 0) + return (1); + + if (num > MAX_SIM_DEV - 1) { + error("Invalid ctrl_no supplied: %s. Valid ctrl_no " + "value must lie between 0 and 3!", tmpstr); + return (1); + } + + *ctrl = num; + tmpstr = (char *)strtok(NULL, ":"); + + if ((tmpstr != NULL) && (*tmpstr != '\0')) { + if (convert_arguint(tmpstr, &num) != 0) + return (1); + + /* Check if chip_no is valid */ + if (num > MAX_CTRL_CS - 1) { + error("Invalid chip_no supplied: %s. Valid " + "chip_no value must lie between 0 and 3!", + tmpstr); + return (1); + } + *cs = num; + } + } else + /* Empty devstring supplied */ + return (1); + + debug("CTRL=%d CHIP=%d\n", *ctrl, *cs); + return (0); +} + +static int +opendev(int *fd) +{ + + *fd = open(SIMDEVICE, O_RDWR); + if (*fd == -1) { + error("Could not open simulator device file (%s)!", + SIMDEVICE); + return (EX_OSFILE); + } + return (EX_OK); +} + +static int +opencdev(int *cdevd, int ctrl, int chip) +{ + char fname[255]; + + sprintf(fname, "/dev/nandsim%d.%d", ctrl, chip); + *cdevd = open(fname, O_RDWR); + if (*cdevd == -1) + return (EX_NOINPUT); + + return (EX_OK); +} + +/* + * Check if given arguments count match requirements. If no, or + * --help (-h) flag is specified -- return 1 (print usage) + */ +static int +checkusage(int gargc, int argsreqd, char **gargv) +{ + + if (gargc < argsreqd + 2 || (gargc >= (argsreqd + 2) && + (strcmp(gargv[1], "--help") == 0 || + strcmp(gargv[1], "-h") == 0))) + return (1); + + return (0); +} + +static int +cmdstatus(int gargc, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, idx, idx2, start, stop; + uint8_t verbose = 0; + struct sim_ctrl ctrlconf; + struct sim_chip chipconf; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) { + return (EX_USAGE); + } else if (ctl == 0xff) { + /* Every controller */ + start = 0; + stop = MAX_SIM_DEV-1; + } else { + /* Specified controller only */ + start = ctl; + stop = ctl; + } + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + for (idx = 0; idx < gargc; idx ++) + if (strcmp(gargv[idx], "-v") == 0 || + strcmp(gargv[idx], "--verbose") == 0) + verbose = 1; + + for (idx = start; idx <= stop; idx++) { + ctrlconf.num = idx; + err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrlconf); + if (err) { + err = EX_SOFTWARE; + error(MSG_STATUSACQCTRL, idx); + continue; + } + + printctrl(&ctrlconf); + + for (idx2 = 0; idx2 < MAX_CTRL_CS; idx2++) { + chipconf.num = idx2; + chipconf.ctrl_num = idx; + + err = ioctl(fd, NANDSIM_STATUS_CHIP, &chipconf); + if (err) { + err = EX_SOFTWARE; + error(MSG_STATUSACQCTRL, idx); + continue; + } + + printchip(&chipconf, verbose); + } + } + close(fd); + return (err); +} + +static int +cmdconf(int gargc __unused, char **gargv) +{ + int err; + + err = parse_config(gargv[2], SIMDEVICE); + if (err) + return (EX_DATAERR); + + return (EX_OK); +} + +static int +cmdstart(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, running, state; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + err = is_ctrl_created(ctl, &state); + if (err) { + return (EX_SOFTWARE); + } else if (state == 0) { + error(MSG_NOCTRL, ctl); + return (EX_SOFTWARE); + } + + err = is_ctrl_running(ctl, &running); + if (err) + return (EX_SOFTWARE); + + if (running) { + warn(MSG_RUNNING, ctl); + } else { + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_START_CTRL, &ctl); + close(fd); + if (err) { + error("Cannot start controller#%d", ctl); + err = EX_SOFTWARE; + } + } + return (err); +} + +static int +cmdstop(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, running; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + err = is_ctrl_running(ctl, &running); + if (err) + return (EX_SOFTWARE); + + if (!running) { + error(MSG_NOTRUNNING, ctl); + } else { + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STOP_CTRL, &ctl); + close(fd); + if (err) { + error("Cannot stop controller#%d", ctl); + err = EX_SOFTWARE; + } + } + + return (err); +} + +static int +cmdmod(int gargc __unused, char **gargv) +{ + int chip, ctl, err = 0, fd = -1, i; + struct sim_mod mods; + + if (gargc >= 4) { + if (strcmp(gargv[2], "--loglevel") == 0 || strcmp(gargv[2], + "-l") == 0) { + /* Set loglevel (ctrl:chip pair independant) */ + mods.field = SIM_MOD_LOG_LEVEL; + + if (convert_arguint(gargv[3], &mods.new_value) != 0) + return (EX_SOFTWARE); + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_MODIFY, &mods); + if (err) { + error("simulator parameter %s could not be " + "modified !", gargv[3]); + close(fd); + return (EX_SOFTWARE); + } + + debug("request : loglevel = %d\n", mods.new_value); + close(fd); + return (EX_OK); + } + } + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + else if (chip == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + /* Find out which flags were passed */ + for (i = 3; i < gargc; i++) { + + if (convert_arguint(gargv[i + 1], &mods.new_value) != 0) + continue; + + if (strcmp(gargv[i], "--prog-time") == 0 || + strcmp(gargv[i], "-p") == 0) { + + mods.field = SIM_MOD_PROG_TIME; + debug("request : progtime = %d\n", mods.new_value); + + } else if (strcmp(gargv[i], "--erase-time") == 0 || + strcmp(gargv[i], "-e") == 0) { + + mods.field = SIM_MOD_ERASE_TIME; + debug("request : eraseime = %d\n", mods.new_value); + + } else if (strcmp(gargv[i], "--read-time") == 0 || + strcmp(gargv[i], "-r") == 0) { + + mods.field = SIM_MOD_READ_TIME; + debug("request : read_time = %d\n", mods.new_value); + + } else if (strcmp(gargv[i], "--error-ratio") == 0 || + strcmp(gargv[i], "-E") == 0) { + + mods.field = SIM_MOD_ERROR_RATIO; + debug("request : error_ratio = %d\n", mods.new_value); + + } else { + /* Flag not recognized, or nothing specified. */ + error("Unrecognized flag:%s\n", gargv[i]); + if (fd >= 0) + close(fd); + return (EX_USAGE); + } + + mods.chip_num = chip; + mods.ctrl_num = ctl; + + /* Call appropriate ioctl */ + err = ioctl(fd, NANDSIM_MODIFY, &mods); + if (err) { + error("simulator parameter %s could not be modified! ", + gargv[i]); + continue; + } + i++; + } + close(fd); + return (EX_OK); +} + +static int +cmderror(int gargc __unused, char **gargv) +{ + uint32_t page, column, len, pattern; + int chip = 0, ctl = 0, err = 0, fd; + struct sim_error sim_err; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + if (chip == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (convert_arguint(gargv[3], &page) || + convert_arguint(gargv[4], &column) || + convert_arguint(gargv[5], &len) || + convert_arguint(gargv[6], &pattern)) + return (EX_SOFTWARE); + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + sim_err.page_num = page; + sim_err.column = column; + sim_err.len = len; + sim_err.pattern = pattern; + sim_err.ctrl_num = ctl; + sim_err.chip_num = chip; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_INJECT_ERROR, &sim_err); + + close(fd); + if (err) { + error("Could not inject error !"); + return (EX_SOFTWARE); + } + return (EX_OK); +} + +static int +cmdbb(int gargc, char **gargv) +{ + struct sim_block_state bs; + struct chip_param_io cparams; + uint32_t blkidx; + int c, cdevd, chip = 0, ctl = 0, err = 0, fd, idx; + uint8_t flagL = 0, flagU = 0; + int *badblocks = NULL; + + /* Check for --list/-L or --unmark/-U flags */ + for (idx = 3; idx < gargc; idx++) { + if (strcmp(gargv[idx], "--list") == 0 || + strcmp(gargv[idx], "-L") == 0) + flagL = idx; + if (strcmp(gargv[idx], "--unmark") == 0 || + strcmp(gargv[idx], "-U") == 0) + flagU = idx; + } + + if (flagL == 2 || flagU == 2 || flagU == 3) + return (EX_USAGE); + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) { + return (EX_USAGE); + } + if (chip == 0xff || ctl == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + bs.ctrl_num = ctl; + bs.chip_num = chip; + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opencdev(&cdevd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams); + if (err) + return (EX_SOFTWARE); + + close(cdevd); + + bs.ctrl_num = ctl; + bs.chip_num = chip; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + if (flagL != 3) { + /* + * Flag -L was specified either after blocklist or was not + * specified at all. + */ + c = parse_intarray(gargv[3], &badblocks); + + for (idx = 0; idx < c; idx++) { + bs.block_num = badblocks[idx]; + /* Do not change wearout */ + bs.wearout = -1; + bs.state = (flagU == 0) ? NANDSIM_BAD_BLOCK : + NANDSIM_GOOD_BLOCK; + + err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs); + if (err) { + error("Could not set bad block(%d) for " + "controller (%d)!", + badblocks[idx], ctl); + err = EX_SOFTWARE; + break; + } + } + } + if (flagL != 0) { + /* If flag -L was specified (anywhere) */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + bs.block_num = blkidx; + /* Do not change the wearout */ + bs.wearout = -1; + err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs); + if (err) { + error("Could not acquire block state"); + err = EX_SOFTWARE; + continue; + } + printf("Block#%d: wear count: %d %s\n", blkidx, + bs.wearout, + (bs.state == NANDSIM_BAD_BLOCK) ? "BAD":"GOOD"); + } + } + close(fd); + return (err); +} + +static int +cmdfreeze(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, i, start = 0, state, stop = 0; + struct sim_ctrl_chip ctrlchip; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + if (ctl == 0xff) { + error("You have to specify at least controller number"); + return (EX_USAGE); + } + + if (ctl != 0xff && chip == 0xff) { + start = 0; + stop = MAX_CTRL_CS - 1; + } else { + start = chip; + stop = chip; + } + + ctrlchip.ctrl_num = ctl; + + err = is_ctrl_running(ctl, &state); + if (err) + return (EX_SOFTWARE); + if (state == 0) { + error(MSG_NOTRUNNING, ctl); + return (EX_SOFTWARE); + } + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + for (i = start; i <= stop; i++) { + err = is_chip_created(ctl, i, &state); + if (err) + return (EX_SOFTWARE); + else if (state == 0) { + continue; + } + + ctrlchip.chip_num = i; + err = ioctl(fd, NANDSIM_FREEZE, &ctrlchip); + if (err) { + error("Could not freeze ctrl#%d chip#%d", ctl, i); + close(fd); + return (EX_SOFTWARE); + } + } + close(fd); + return (EX_OK); +} + +static int +cmdlog(int gargc __unused, char **gargv) +{ + struct sim_log log; + int chip = 0, ctl = 0, err = 0, fd, idx, start = 0, stop = 0; + char *logbuf; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + logbuf = (char *)malloc(sizeof(char) * NANDSIM_RAM_LOG_SIZE); + if (logbuf == NULL) { + error("Not enough memory to create log buffer"); + return (EX_SOFTWARE); + } + + memset(logbuf, 0, NANDSIM_RAM_LOG_SIZE); + log.log = logbuf; + log.len = NANDSIM_RAM_LOG_SIZE; + + if (ctl == 0xff) { + start = 0; + stop = MAX_SIM_DEV-1; + } else { + start = ctl; + stop = ctl; + } + + if (opendev(&fd) != EX_OK) { + free(logbuf); + return (EX_OSFILE); + } + + /* Print logs for selected controller(s) */ + for (idx = start; idx <= stop; idx++) { + log.ctrl_num = idx; + + err = ioctl(fd, NANDSIM_PRINT_LOG, &log); + if (err) { + error("Could not get log for controller %d!", idx); + continue; + } + + printf("Logs for controller#%d:\n%s\n", idx, logbuf); + } + + free(logbuf); + close(fd); + return (EX_OK); +} + +static int +cmdstats(int gargc __unused, char **gargv) +{ + int cdevd, chip = 0, ctl = 0, err = 0; + uint32_t pageno = 0; + + err = parse_devstring(gargv[2], &ctl, &chip); + + if (err) + return (EX_USAGE); + + if (chip == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (convert_arguint(gargv[3], &pageno) != 0) + return (EX_USAGE); + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opencdev(&cdevd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = printstats(ctl, chip, pageno, cdevd); + if (err) { + close(cdevd); + return (EX_SOFTWARE); + } + close(cdevd); + return (EX_OK); +} + +static int +cmddump(int gargc __unused, char **gargv) +{ + struct sim_dump dump; + struct sim_block_state bs; + struct chip_param_io cparams; + int chip = 0, ctl = 0, err = EX_OK, fd, dumpfd; + uint32_t blkidx, bwritten = 0, totalwritten = 0; + void *buf; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + if (chip == 0xff || ctl == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opencdev(&fd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams); + if (err) { + error("Cannot get parameters for chip %d:%d", ctl, chip); + close(fd); + return (EX_SOFTWARE); + } + close(fd); + + dump.ctrl_num = ctl; + dump.chip_num = chip; + + dump.len = cparams.pages_per_block * (cparams.page_size + + cparams.oob_size); + + buf = malloc(dump.len); + if (buf == NULL) { + error("Could not allocate memory!"); + return (EX_SOFTWARE); + } + dump.data = buf; + + errno = 0; + dumpfd = open(gargv[3], O_WRONLY | O_CREAT, 0666); + if (dumpfd == -1) { + error("Cannot create dump file."); + free(buf); + return (EX_SOFTWARE); + } + + if (opendev(&fd)) { + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + + bs.ctrl_num = ctl; + bs.chip_num = chip; + + /* First uint32_t in file shall contain block count */ + if (write(dumpfd, &cparams, sizeof(cparams)) < (int)sizeof(cparams)) { + error("Error writing to dumpfile!"); + close(fd); + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + + /* + * First loop acquires blocks states and writes them to + * the dump file. + */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + bs.block_num = blkidx; + err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs); + if (err) { + error("Could not get bad block(%d) for " + "controller (%d)!", blkidx, ctl); + close(fd); + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + + bwritten = write(dumpfd, &bs, sizeof(bs)); + if (bwritten != sizeof(bs)) { + error("Error writing to dumpfile"); + close(fd); + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + } + + /* Second loop dumps the data */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + debug("Block#%d...", blkidx); + dump.block_num = blkidx; + + err = ioctl(fd, NANDSIM_DUMP, &dump); + if (err) { + error("Could not dump ctrl#%d chip#%d " + "block#%d", ctl, chip, blkidx); + err = EX_SOFTWARE; + break; + } + + bwritten = write(dumpfd, dump.data, dump.len); + if (bwritten != dump.len) { + error("Error writing to dumpfile"); + err = EX_SOFTWARE; + break; + } + debug("OK!\n"); + totalwritten += bwritten; + } + printf("%d out of %d B written.\n", totalwritten, dump.len * blkidx); + + close(fd); + close(dumpfd); + free(buf); + return (err); +} + +static int +cmdrestore(int gargc __unused, char **gargv) +{ + struct sim_dump dump; + struct sim_block_state bs; + struct stat filestat; + int chip = 0, ctl = 0, err = 0, fd, dumpfd = -1; + uint32_t blkidx, blksz, fsize = 0, expfilesz; + void *buf; + struct chip_param_io cparams, dumpcparams; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + else if (ctl == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + /* Get chip geometry */ + if (opencdev(&fd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams); + if (err) { + error("Cannot get parameters for chip %d:%d", ctl, chip); + close(fd); + return (err); + } + close(fd); + + /* Obtain dump file size */ + errno = 0; + if (stat(gargv[3], &filestat) != 0) { + error("Could not acquire file size! : %s", + strerror(errno)); + return (EX_IOERR); + } + + fsize = filestat.st_size; + blksz = cparams.pages_per_block * (cparams.page_size + + cparams.oob_size); + + /* Expected dump file size for chip */ + expfilesz = cparams.blocks * (blksz + sizeof(bs)) + sizeof(cparams); + + if (fsize != expfilesz) { + error("File size does not match chip geometry (file size: %d" + ", dump size: %d)", fsize, expfilesz); + return (EX_SOFTWARE); + } + + dumpfd = open(gargv[3], O_RDONLY); + if (dumpfd == -1) { + error("Could not open dump file!"); + return (EX_IOERR); + } + + /* Read chip params saved in dumpfile */ + read(dumpfd, &dumpcparams, sizeof(dumpcparams)); + + /* XXX */ + if (bcmp(&dumpcparams, &cparams, sizeof(cparams)) != 0) { + error("Supplied dump is created for a chip with different " + "chip configuration!"); + close(dumpfd); + return (EX_SOFTWARE); + } + + if (opendev(&fd) != EX_OK) { + close(dumpfd); + return (EX_OSFILE); + } + + buf = malloc(blksz); + if (buf == NULL) { + error("Could not allocate memory for block buffer"); + close(dumpfd); + close(fd); + return (EX_SOFTWARE); + } + + dump.ctrl_num = ctl; + dump.chip_num = chip; + dump.data = buf; + /* Restore block states and wearouts */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + dump.block_num = blkidx; + if (read(dumpfd, &bs, sizeof(bs)) != sizeof(bs)) { + error("Error reading dumpfile"); + close(dumpfd); + close(fd); + free(buf); + return (EX_SOFTWARE); + } + bs.ctrl_num = ctl; + bs.chip_num = chip; + debug("BLKIDX=%d BLOCKS=%d CTRL=%d CHIP=%d STATE=%d\n" + "WEAROUT=%d BS.CTRL_NUM=%d BS.CHIP_NUM=%d\n", + blkidx, cparams.blocks, dump.ctrl_num, dump.chip_num, + bs.state, bs.wearout, bs.ctrl_num, bs.chip_num); + + err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs); + if (err) { + error("Could not set bad block(%d) for " + "controller: %d, chip: %d!", blkidx, ctl, chip); + close(dumpfd); + close(fd); + free(buf); + return (EX_SOFTWARE); + } + } + /* Restore data */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + errno = 0; + dump.len = read(dumpfd, buf, blksz); + if (errno) { + error("Failed to read block#%d from dumpfile.", blkidx); + err = EX_SOFTWARE; + break; + } + dump.block_num = blkidx; + err = ioctl(fd, NANDSIM_RESTORE, &dump); + if (err) { + error("Could not restore block#%d of ctrl#%d chip#%d" + ": %s", blkidx, ctl, chip, strerror(errno)); + err = EX_SOFTWARE; + break; + } + } + + free(buf); + close(dumpfd); + close(fd); + return (err); + +} + +static int +cmddestroy(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, idx, idx2, state; + int chipstart, chipstop, ctrlstart, ctrlstop; + struct sim_chip_destroy chip_destroy; + + err = parse_devstring(gargv[2], &ctl, &chip); + + if (err) + return (EX_USAGE); + + if (ctl == 0xff) { + /* Every chip at every controller */ + ctrlstart = chipstart = 0; + ctrlstop = MAX_SIM_DEV - 1; + chipstop = MAX_CTRL_CS - 1; + } else { + ctrlstart = ctrlstop = ctl; + if (chip == 0xff) { + /* Every chip at selected controller */ + chipstart = 0; + chipstop = MAX_CTRL_CS - 1; + } else + /* Selected chip at selected controller */ + chipstart = chipstop = chip; + } + debug("CTRLSTART=%d CTRLSTOP=%d CHIPSTART=%d CHIPSTOP=%d\n", + ctrlstart, ctrlstop, chipstart, chipstop); + for (idx = ctrlstart; idx <= ctrlstop; idx++) { + err = is_ctrl_created(idx, &state); + if (err) { + error("Could not acquire ctrl#%d state. Cannot " + "destroy controller.", idx); + return (EX_SOFTWARE); + } + if (state == 0) { + continue; + } + err = is_ctrl_running(idx, &state); + if (err) { + error(MSG_STATUSACQCTRL, idx); + return (EX_SOFTWARE); + } + if (state != 0) { + error(MSG_RUNNING, ctl); + return (EX_SOFTWARE); + } + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + for (idx2 = chipstart; idx2 <= chipstop; idx2++) { + err = is_chip_created(idx, idx2, &state); + if (err) { + error(MSG_STATUSACQCTRLCHIP, idx2, idx); + continue; + } + if (state == 0) + /* There is no such chip running */ + continue; + chip_destroy.ctrl_num = idx; + chip_destroy.chip_num = idx2; + ioctl(fd, NANDSIM_DESTROY_CHIP, + &chip_destroy); + } + /* If chip isn't explicitly specified -- destroy ctrl */ + if (chip == 0xff) { + err = ioctl(fd, NANDSIM_DESTROY_CTRL, &idx); + if (err) { + error("Could not destroy ctrl#%d", idx); + continue; + } + } + close(fd); + } + return (err); +} + +int +main(int argc, char **argv) +{ + struct nandsim_command *cmdopts; + int retcode = 0; + + if (argc < 2) { + cmdhelp(argc, argv); + retcode = EX_USAGE; + } else { + cmdopts = getcommand(argv[1]); + if (cmdopts != NULL && cmdopts->commandfunc != NULL) { + if (checkusage(argc, cmdopts->req_argc, argv) == 1) { + /* Print command specific usage */ + printf("nandsim %s", cmdopts->usagestring); + return (EX_USAGE); + } + retcode = cmdopts->commandfunc(argc, argv); + + if (retcode == EX_USAGE) { + /* Print command-specific usage */ + printf("nandsim %s", cmdopts->usagestring); + } else if (retcode == EX_OSFILE) { + error("Could not open device file"); + } + + } else { + error("Unknown command!"); + retcode = EX_USAGE; + } + } + return (retcode); +} + +static int +cmdhelp(int gargc __unused, char **gargv __unused) +{ + struct nandsim_command *opts; + + printf("usage: nandsim [command params] [params]\n\n"); + + for (opts = commands; (opts != NULL) && + (opts->cmd_name != NULL); opts++) + printf("nandsim %s", opts->usagestring); + + printf("\n"); + return (EX_OK); +} + +static void +printchip(struct sim_chip *chip, uint8_t verbose) +{ + + if (chip->created == 0) + return; + if (verbose > 0) { + printf("\n[Chip info]\n"); + printf("num= %d\nctrl_num=%d\ndevice_id=%02x" + "\tmanufacturer_id=%02x\ndevice_model=%s\nmanufacturer=" + "%s\ncol_addr_cycles=%d\nrow_addr_cycles=%d" + "\npage_size=%d\noob_size=%d\npages_per_block=%d\n" + "blocks_per_lun=%d\nluns=%d\n\nprog_time=%d\n" + "erase_time=%d\nread_time=%d\n" + "error_ratio=%d\nwear_level=%d\nwrite_protect=%c\n" + "chip_width=%db\n", chip->num, chip->ctrl_num, + chip->device_id, chip->manufact_id,chip->device_model, + chip->manufacturer, chip->col_addr_cycles, + chip->row_addr_cycles, chip->page_size, + chip->oob_size, chip->pgs_per_blk, chip->blks_per_lun, + chip->luns,chip->prog_time, chip->erase_time, + chip->read_time, chip->error_ratio, chip->wear_level, + (chip->is_wp == 0) ? 'N':'Y', chip->width); + } else { + printf("[Chip info]\n"); + printf("\tnum=%d\n\tdevice_model=%s\n\tmanufacturer=%s\n" + "\tpage_size=%d\n\twrite_protect=%s\n", + chip->num, chip->device_model, chip->manufacturer, + chip->page_size, (chip->is_wp == 0) ? "NO":"YES"); + } +} + +static void +printctrl(struct sim_ctrl *ctrl) +{ + int i; + + if (ctrl->created == 0) { + printf(MSG_NOCTRL "\n", ctrl->num); + return; + } + printf("\n[Controller info]\n"); + printf("\trunning: %s\n", ctrl->running ? "yes" : "no"); + printf("\tnum cs: %d\n", ctrl->num_cs); + printf("\tecc: %d\n", ctrl->ecc); + printf("\tlog_filename: %s\n", ctrl->filename); + printf("\tecc_layout:"); + for (i = 0; i < MAX_ECC_BYTES; i++) { + if (ctrl->ecc_layout[i] == 0xffff) + break; + else + printf("%c%d", i%16 ? ' ' : '\n', + ctrl->ecc_layout[i]); + } + printf("\n"); +} + +static int +is_ctrl_running(int ctrl_no, int *running) +{ + struct sim_ctrl ctrl; + int err, fd; + + ctrl.num = ctrl_no; + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl); + if (err) { + error(MSG_STATUSACQCTRL, ctrl_no); + close(fd); + return (err); + } + *running = ctrl.running; + close(fd); + return (0); +} + +static int +is_ctrl_created(int ctrl_no, int *created) +{ + struct sim_ctrl ctrl; + int err, fd; + + ctrl.num = ctrl_no; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl); + if (err) { + error("Could not acquire conf for ctrl#%d", ctrl_no); + close(fd); + return (err); + } + *created = ctrl.created; + close(fd); + return (0); +} + +static int +is_chip_created(int ctrl_no, int chip_no, int *created) +{ + struct sim_chip chip; + int err, fd; + + chip.ctrl_num = ctrl_no; + chip.num = chip_no; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STATUS_CHIP, &chip); + if (err) { + error("Could not acquire conf for chip#%d", chip_no); + close(fd); + return (err); + } + *created = chip.created; + close(fd); + return (0); +} + +static int +assert_chip_connected(int ctrl_no, int chip_no) +{ + int created, running; + + if (is_ctrl_created(ctrl_no, &created)) + return (0); + + if (!created) { + error(MSG_NOCTRL, ctrl_no); + return (0); + } + + if (is_chip_created(ctrl_no, chip_no, &created)) + return (0); + + if (!created) { + error(MSG_NOTCONFIGDCTRLCHIP, ctrl_no, chip_no); + return (0); + } + + if (is_ctrl_running(ctrl_no, &running)) + return (0); + + if (!running) { + error(MSG_NOTRUNNING, ctrl_no); + return (0); + } + + return (1); +} + +static int +printstats(int ctrlno, int chipno, uint32_t pageno, int cdevd) +{ + struct page_stat_io pstats; + struct block_stat_io bstats; + struct chip_param_io cparams; + uint32_t blkidx; + int err; + + /* Gather information about chip */ + err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams); + + if (err) { + error("Could not acquire chip info for chip attached to cs#" + "%d, ctrl#%d", chipno, ctrlno); + return (EX_SOFTWARE); + } + + blkidx = (pageno / cparams.pages_per_block); + bstats.block_num = blkidx; + + err = ioctl(cdevd, NAND_IO_BLOCK_STAT, &bstats); + if (err) { + error("Could not acquire block#%d statistics!", blkidx); + return (ENXIO); + } + + printf("Block #%d erased: %d\n", blkidx, bstats.block_erased); + pstats.page_num = pageno; + + err = ioctl(cdevd, NAND_IO_PAGE_STAT, &pstats); + if (err) { + error("Could not acquire page statistics!"); + return (ENXIO); + } + + debug("BLOCKIDX = %d PAGENO (REL. TO BLK) = %d\n", blkidx, + pstats.page_num); + + printf("Page#%d : reads:%d writes:%d \n\traw reads:%d raw writes:%d " + "\n\tecc_succeeded:%d ecc_corrected:%d ecc_failed:%d\n", + pstats.page_num, pstats.page_read, pstats.page_written, + pstats.page_raw_read, pstats.page_raw_written, + pstats.ecc_succeded, pstats.ecc_corrected, pstats.ecc_failed); + return (0); +} diff --git a/usr.sbin/nandsim/nandsim_cfgparse.c b/usr.sbin/nandsim/nandsim_cfgparse.c new file mode 100644 index 0000000..40e8d4c --- /dev/null +++ b/usr.sbin/nandsim/nandsim_cfgparse.c @@ -0,0 +1,957 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "nandsim_cfgparse.h" + +#define warn(fmt, args...) do { \ + printf("WARNING: " fmt "\n", ##args); } while (0) + +#define error(fmt, args...) do { \ + printf("ERROR: " fmt "\n", ##args); } while (0) + +#define MSG_MANDATORYKEYMISSING "mandatory key \"%s\" value belonging to " \ + "section \"%s\" is missing!\n" + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define debug(fmt, args...) do { \ + printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0) +#else +#define debug(fmt, args...) do {} while(0) +#endif + +#define STRBUFSIZ 2000 + +/* Macros extracts type and type size */ +#define TYPE(x) ((x) & 0xf8) +#define SIZE(x) (((x) & 0x07)) + +/* Erase/Prog/Read time max and min values */ +#define DELAYTIME_MIN 10000 +#define DELAYTIME_MAX 10000000 + +/* Structure holding configuration for controller. */ +static struct sim_ctrl ctrl_conf; +/* Structure holding configuration for chip. */ +static struct sim_chip chip_conf; + +static struct nandsim_key nandsim_ctrl_keys[] = { + {"num_cs", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num_cs, 0}, + {"ctrl_num", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num, 0}, + + {"ecc_layout", 1, VALUE_UINTARRAY | SIZE_16, + (void *)&ctrl_conf.ecc_layout, MAX_ECC_BYTES}, + + {"filename", 0, VALUE_STRING, + (void *)&ctrl_conf.filename, FILENAME_SIZE}, + + {"ecc", 0, VALUE_BOOL, (void *)&ctrl_conf.ecc, 0}, + {NULL, 0, 0, NULL, 0}, +}; + +static struct nandsim_key nandsim_chip_keys[] = { + {"chip_cs", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.num, 0}, + {"chip_ctrl", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.ctrl_num, + 0}, + {"device_id", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.device_id, + 0}, + {"manufacturer_id", 1, VALUE_UINT | SIZE_8, + (void *)&chip_conf.manufact_id, 0}, + {"model", 0, VALUE_STRING, (void *)&chip_conf.device_model, + DEV_MODEL_STR_SIZE}, + {"manufacturer", 0, VALUE_STRING, (void *)&chip_conf.manufacturer, + MAN_STR_SIZE}, + {"page_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.page_size, + 0}, + {"oob_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.oob_size, + 0}, + {"pages_per_block", 1, VALUE_UINT | SIZE_32, + (void *)&chip_conf.pgs_per_blk, 0}, + {"blocks_per_lun", 1, VALUE_UINT | SIZE_32, + (void *)&chip_conf.blks_per_lun, 0}, + {"luns", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.luns, 0}, + {"column_addr_cycle", 1,VALUE_UINT | SIZE_8, + (void *)&chip_conf.col_addr_cycles, 0}, + {"row_addr_cycle", 1, VALUE_UINT | SIZE_8, + (void *)&chip_conf.row_addr_cycles, 0}, + {"program_time", 0, VALUE_UINT | SIZE_32, + (void *)&chip_conf.prog_time, 0}, + {"erase_time", 0, VALUE_UINT | SIZE_32, + (void *)&chip_conf.erase_time, 0}, + {"read_time", 0, VALUE_UINT | SIZE_32, + (void *)&chip_conf.read_time, 0}, + {"width", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.width, 0}, + {"wear_out", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.wear_level, + 0}, + {"bad_block_map", 0, VALUE_UINTARRAY | SIZE_32, + (void *)&chip_conf.bad_block_map, MAX_BAD_BLOCKS}, + {NULL, 0, 0, NULL, 0}, +}; + +struct nandsim_section sections[] = { + {"ctrl", (struct nandsim_key *)&nandsim_ctrl_keys}, + {"chip", (struct nandsim_key *)&nandsim_chip_keys}, + {NULL, NULL}, +}; + +static uint8_t logoutputtoint(char *, int *); +static uint8_t validate_chips(struct sim_chip *, int, struct sim_ctrl *, int); +static uint8_t validate_ctrls(struct sim_ctrl *, int); +static int configure_sim(const char *, struct rcfile *); +static int create_ctrls(struct rcfile *, struct sim_ctrl **, int *); +static int create_chips(struct rcfile *, struct sim_chip **, int *); +static void destroy_ctrls(struct sim_ctrl *); +static void destroy_chips(struct sim_chip *); +static int validate_section_config(struct rcfile *, const char *, int); + +int +convert_argint(char *arg, int *value) +{ + + if (arg == NULL || value == NULL) + return (EINVAL); + + errno = 0; + *value = (int)strtol(arg, NULL, 0); + if (*value == 0 && errno != 0) { + error("Cannot convert to number argument \'%s\'", arg); + return (EINVAL); + } + return (0); +} + +int +convert_arguint(char *arg, unsigned int *value) +{ + + if (arg == NULL || value == NULL) + return (EINVAL); + + errno = 0; + *value = (unsigned int)strtol(arg, NULL, 0); + if (*value == 0 && errno != 0) { + error("Cannot convert to number argument \'%s\'", arg); + return (EINVAL); + } + return (0); +} + +/* Parse given ',' separated list of bytes into buffer. */ +int +parse_intarray(char *array, int **buffer) +{ + char *tmp, *tmpstr, *origstr; + unsigned int currbufp = 0, i; + unsigned int count = 0, from = 0, to = 0; + + /* Remove square braces */ + if (array[0] == '[') + array ++; + if (array[strlen(array)-1] == ']') + array[strlen(array)-1] = ','; + + from = strlen(array); + origstr = (char *)malloc(sizeof(char) * from); + strcpy(origstr, array); + + tmpstr = (char *)strtok(array, ","); + /* First loop checks for how big int array we need to allocate */ + while (tmpstr != NULL) { + errno = 0; + if ((tmp = strchr(tmpstr, '-')) != NULL) { + *tmp = ' '; + if (convert_arguint(tmpstr, &from) || + convert_arguint(tmp, &to)) { + free(origstr); + return (EINVAL); + } + + count += to - from + 1; + } else { + if (convert_arguint(tmpstr, &from)) { + free(origstr); + return (EINVAL); + } + count++; + } + tmpstr = (char *)strtok(NULL, ","); + } + + if (count == 0) + goto out; + + /* Allocate buffer of ints */ + tmpstr = (char *)strtok(origstr, ","); + *buffer = malloc(count * sizeof(int)); + + /* Second loop is just inserting converted values into int array */ + while (tmpstr != NULL) { + errno = 0; + if ((tmp = strchr(tmpstr, '-')) != NULL) { + *tmp = ' '; + from = strtol(tmpstr, NULL, 0); + to = strtol(tmp, NULL, 0); + tmpstr = strtok(NULL, ","); + for (i = from; i <= to; i ++) + (*buffer)[currbufp++] = i; + continue; + } + errno = 0; + from = (int)strtol(tmpstr, NULL, 0); + (*buffer)[currbufp++] = from; + tmpstr = (char *)strtok(NULL, ","); + } +out: + free(origstr); + return (count); +} + +/* Convert logoutput strings literals into appropriate ints. */ +static uint8_t +logoutputtoint(char *logoutput, int *output) +{ + int out; + + if (strcmp(logoutput, "file") == 0) + out = NANDSIM_OUTPUT_FILE; + + else if (strcmp(logoutput, "console") == 0) + out = NANDSIM_OUTPUT_CONSOLE; + + else if (strcmp(logoutput, "ram") == 0) + out = NANDSIM_OUTPUT_RAM; + + else if (strcmp(logoutput, "none") == 0) + out = NANDSIM_OUTPUT_NONE; + else + out = -1; + + *output = out; + + if (out == -1) + return (EINVAL); + else + return (0); +} + +static int +configure_sim(const char *devfname, struct rcfile *f) +{ + struct sim_param sim_conf; + char buf[255]; + int err, tmpv, fd; + + err = rc_getint(f, "sim", 0, "log_level", &tmpv); + + if (tmpv < 0 || tmpv > 255 || err) { + error("Bad log level specified (%d)\n", tmpv); + return (ENOTSUP); + } else + sim_conf.log_level = tmpv; + + rc_getstring(f, "sim", 0, "log_output", 255, (char *)&buf); + + tmpv = -1; + err = logoutputtoint((char *)&buf, &tmpv); + if (err) { + error("Log output specified in config file does not seem to " + "be valid (%s)!", (char *)&buf); + return (ENOTSUP); + } + + sim_conf.log_output = tmpv; + + fd = open(devfname, O_RDWR); + if (fd == -1) { + error("could not open simulator device file (%s)!", + devfname); + return (EX_OSFILE); + } + + err = ioctl(fd, NANDSIM_SIM_PARAM, &sim_conf); + if (err) { + error("simulator parameters could not be modified: %s", + strerror(errno)); + close(fd); + return (ENXIO); + } + + close(fd); + return (EX_OK); +} + +static int +create_ctrls(struct rcfile *f, struct sim_ctrl **ctrls, int *cnt) +{ + int count, i; + struct sim_ctrl *ctrlsptr; + + count = rc_getsectionscount(f, "ctrl"); + if (count > MAX_SIM_DEV) { + error("Too many CTRL sections specified(%d)", count); + return (ENOTSUP); + } else if (count == 0) { + error("No ctrl sections specified"); + return (ENOENT); + } + + ctrlsptr = (struct sim_ctrl *)malloc(sizeof(struct sim_ctrl) * count); + if (ctrlsptr == NULL) { + error("Could not allocate memory for ctrl configuration"); + return (ENOMEM); + } + + for (i = 0; i < count; i++) { + bzero((void *)&ctrl_conf, sizeof(ctrl_conf)); + + /* + * ECC layout have to end up with 0xffff, so + * we're filling buffer with 0xff. If ecc_layout is + * defined in config file, values will be overriden. + */ + memset((void *)&ctrl_conf.ecc_layout, 0xff, + sizeof(ctrl_conf.ecc_layout)); + + if (validate_section_config(f, "ctrl", i) != 0) { + free(ctrlsptr); + return (EINVAL); + } + + if (parse_section(f, "ctrl", i) != 0) { + free(ctrlsptr); + return (EINVAL); + } + + memcpy(&ctrlsptr[i], &ctrl_conf, sizeof(ctrl_conf)); + /* Try to create ctrl with config parsed */ + debug("NUM=%d\nNUM_CS=%d\nECC=%d\nFILENAME=%s\nECC_LAYOUT[0]" + "=%d\nECC_LAYOUT[1]=%d\n\n", + ctrlsptr[i].num, ctrlsptr[i].num_cs, ctrlsptr[i].ecc, + ctrlsptr[i].filename, ctrlsptr[i].ecc_layout[0], + ctrlsptr[i].ecc_layout[1]); + } + *cnt = count; + *ctrls = ctrlsptr; + return (0); +} + +static void +destroy_ctrls(struct sim_ctrl *ctrls) +{ + + free(ctrls); +} + +static int +create_chips(struct rcfile *f, struct sim_chip **chips, int *cnt) +{ + struct sim_chip *chipsptr; + int count, i; + + count = rc_getsectionscount(f, "chip"); + if (count > (MAX_CTRL_CS * MAX_SIM_DEV)) { + error("Too many chip sections specified(%d)", count); + return (ENOTSUP); + } else if (count == 0) { + error("No chip sections specified"); + return (ENOENT); + } + + chipsptr = (struct sim_chip *)malloc(sizeof(struct sim_chip) * count); + if (chipsptr == NULL) { + error("Could not allocate memory for chip configuration"); + return (ENOMEM); + } + + for (i = 0; i < count; i++) { + bzero((void *)&chip_conf, sizeof(chip_conf)); + + /* + * Bad block map have to end up with 0xffff, so + * we're filling array with 0xff. If bad block map is + * defined in config file, values will be overriden. + */ + memset((void *)&chip_conf.bad_block_map, 0xff, + sizeof(chip_conf.bad_block_map)); + + if (validate_section_config(f, "chip", i) != 0) { + free(chipsptr); + return (EINVAL); + } + + if (parse_section(f, "chip", i) != 0) { + free(chipsptr); + return (EINVAL); + } + + memcpy(&chipsptr[i], &chip_conf, sizeof(chip_conf)); + + /* Try to create chip with config parsed */ + debug("CHIP:\nNUM=%d\nCTRL_NUM=%d\nDEVID=%d\nMANID=%d\n" + "PAGE_SZ=%d\nOOBSZ=%d\nREAD_T=%d\nDEVMODEL=%s\n" + "MAN=%s\nCOLADDRCYCLES=%d\nROWADDRCYCLES=%d\nCHWIDTH=%d\n" + "PGS/BLK=%d\nBLK/LUN=%d\nLUNS=%d\nERR_RATIO=%d\n" + "WEARLEVEL=%d\nISWP=%d\n\n\n\n", + chipsptr[i].num, chipsptr[i].ctrl_num, + chipsptr[i].device_id, chipsptr[i].manufact_id, + chipsptr[i].page_size, chipsptr[i].oob_size, + chipsptr[i].read_time, chipsptr[i].device_model, + chipsptr[i].manufacturer, chipsptr[i].col_addr_cycles, + chipsptr[i].row_addr_cycles, chipsptr[i].width, + chipsptr[i].pgs_per_blk, chipsptr[i].blks_per_lun, + chipsptr[i].luns, chipsptr[i].error_ratio, + chipsptr[i].wear_level, chipsptr[i].is_wp); + } + *cnt = count; + *chips = chipsptr; + return (0); +} + +static void +destroy_chips(struct sim_chip *chips) +{ + + free(chips); +} + +int +parse_config(char *cfgfname, const char *devfname) +{ + int err = 0, fd; + unsigned int chipsectionscnt, ctrlsectionscnt, i; + struct rcfile *f; + struct sim_chip *chips; + struct sim_ctrl *ctrls; + + err = rc_open(cfgfname, "r", &f); + if (err) { + error("could not open configuration file (%s)", cfgfname); + return (EX_NOINPUT); + } + + /* First, try to configure simulator itself. */ + if (configure_sim(devfname, f) != EX_OK) { + rc_close(f); + return (EINVAL); + } + + debug("SIM CONFIGURED!\n"); + /* Then create controllers' configs */ + if (create_ctrls(f, &ctrls, &ctrlsectionscnt) != 0) { + rc_close(f); + return (ENXIO); + } + debug("CTRLS CONFIG READ!\n"); + + /* Then create chips' configs */ + if (create_chips(f, &chips, &chipsectionscnt) != 0) { + destroy_ctrls(ctrls); + rc_close(f); + return (ENXIO); + } + debug("CHIPS CONFIG READ!\n"); + + if (validate_ctrls(ctrls, ctrlsectionscnt) != 0) { + destroy_ctrls(ctrls); + destroy_chips(chips); + rc_close(f); + return (EX_SOFTWARE); + } + if (validate_chips(chips, chipsectionscnt, ctrls, + ctrlsectionscnt) != 0) { + destroy_ctrls(ctrls); + destroy_chips(chips); + rc_close(f); + return (EX_SOFTWARE); + } + + /* Open device */ + fd = open(devfname, O_RDWR); + if (fd == -1) { + error("could not open simulator device file (%s)!", + devfname); + rc_close(f); + destroy_chips(chips); + destroy_ctrls(ctrls); + return (EX_OSFILE); + } + + debug("SIM CONFIG STARTED!\n"); + + /* At this stage, both ctrls' and chips' configs should be valid */ + for (i = 0; i < ctrlsectionscnt; i++) { + err = ioctl(fd, NANDSIM_CREATE_CTRL, &ctrls[i]); + if (err) { + if (err == EEXIST) + error("Controller#%d already created\n", + ctrls[i].num); + else if (err == EINVAL) + error("Incorrect controler number (%d)\n", + ctrls[i].num); + else + error("Could not created controller#%d\n", + ctrls[i].num); + /* Errors during controller creation stops parsing */ + close(fd); + rc_close(f); + destroy_ctrls(ctrls); + destroy_chips(chips); + return (ENXIO); + } + debug("CTRL#%d CONFIG STARTED!\n", i); + } + + for (i = 0; i < chipsectionscnt; i++) { + err = ioctl(fd, NANDSIM_CREATE_CHIP, &chips[i]); + if (err) { + if (err == EEXIST) + error("Chip#%d for controller#%d already " + "created\n", chips[i].num, + chips[i].ctrl_num); + else if (err == EINVAL) + error("Incorrect chip number (%d:%d)\n", + chips[i].num, chips[i].ctrl_num); + else + error("Could not create chip (%d:%d)\n", + chips[i].num, chips[i].ctrl_num); + error("Could not start chip#%d\n", i); + destroy_chips(chips); + destroy_ctrls(ctrls); + close(fd); + rc_close(f); + return (ENXIO); + } + } + debug("CHIPS CONFIG STARTED!\n"); + + close(fd); + rc_close(f); + destroy_chips(chips); + destroy_ctrls(ctrls); + return (0); +} + +/* + * Function tries to get appropriate value for given key, convert it to + * array of ints (of given size), and perform all the neccesary checks and + * conversions. + */ +static int +get_argument_intarray(const char *sect_name, int sectno, + struct nandsim_key *key, struct rcfile *f) +{ + char strbuf[STRBUFSIZ]; + int *intbuf; + int getres; + uint32_t cnt, i = 0; + + getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ, + (char *)&strbuf); + + if (getres != 0) { + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + return (EINVAL); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + cnt = parse_intarray((char *)&strbuf, &intbuf); + cnt = (cnt <= key->maxlength) ? cnt : key->maxlength; + + for (i = 0; i < cnt; i++) { + if (SIZE(key->valuetype) == SIZE_8) + *((uint8_t *)(key->field) + i) = + (uint8_t)intbuf[i]; + else if (SIZE(key->valuetype) == SIZE_16) + *((uint16_t *)(key->field) + i) = + (uint16_t)intbuf[i]; + else + *((uint32_t *)(key->field) + i) = + (uint32_t)intbuf[i]; + } + free(intbuf); + return (0); +} + +/* + * Function tries to get appropriate value for given key, convert it to + * int of certain length. + */ +static int +get_argument_int(const char *sect_name, int sectno, struct nandsim_key *key, + struct rcfile *f) +{ + int getres; + uint32_t val; + + getres = rc_getint(f, sect_name, sectno, key->keyname, &val); + if (getres != 0) { + + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + + return (EINVAL); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + if (SIZE(key->valuetype) == SIZE_8) + *(uint8_t *)(key->field) = (uint8_t)val; + else if (SIZE(key->valuetype) == SIZE_16) + *(uint16_t *)(key->field) = (uint16_t)val; + else + *(uint32_t *)(key->field) = (uint32_t)val; + return (0); +} + +/* Function tries to get string value for given key */ +static int +get_argument_string(const char *sect_name, int sectno, + struct nandsim_key *key, struct rcfile *f) +{ + char strbuf[STRBUFSIZ]; + int getres; + + getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ, + strbuf); + + if (getres != 0) { + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + return (1); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + strncpy(key->field, (char *)&strbuf, (size_t)(key->maxlength - 1)); + return (0); +} + +/* Function tries to get on/off value for given key */ +static int +get_argument_bool(const char *sect_name, int sectno, struct nandsim_key *key, + struct rcfile *f) +{ + int getres, val; + + getres = rc_getbool(f, sect_name, sectno, key->keyname, &val); + if (getres != 0) { + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + return (1); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + *(uint8_t *)key->field = (uint8_t)val; + return (0); +} + +int +parse_section(struct rcfile *f, const char *sect_name, int sectno) +{ + struct nandsim_key *key; + struct nandsim_section *sect = (struct nandsim_section *)§ions; + int getres = 0; + + while (1) { + if (sect == NULL) + return (EINVAL); + + if (strcmp(sect->name, sect_name) == 0) + break; + else + sect++; + } + key = sect->keys; + do { + debug("->Section: %s, Key: %s, type: %d, size: %d", + sect_name, key->keyname, TYPE(key->valuetype), + SIZE(key->valuetype)/2); + + switch (TYPE(key->valuetype)) { + case VALUE_UINT: + /* Single int value */ + getres = get_argument_int(sect_name, sectno, key, f); + + if (getres != 0) + return (getres); + + break; + case VALUE_UINTARRAY: + /* Array of ints */ + getres = get_argument_intarray(sect_name, + sectno, key, f); + + if (getres != 0) + return (getres); + + break; + case VALUE_STRING: + /* Array of chars */ + getres = get_argument_string(sect_name, sectno, key, + f); + + if (getres != 0) + return (getres); + + break; + case VALUE_BOOL: + /* Boolean value (true/false/on/off/yes/no) */ + getres = get_argument_bool(sect_name, sectno, key, + f); + + if (getres != 0) + return (getres); + + break; + } + } while ((++key)->keyname != NULL); + + return (0); +} + +static uint8_t +validate_chips(struct sim_chip *chips, int chipcnt, + struct sim_ctrl *ctrls, int ctrlcnt) +{ + int cchipcnt, i, width, j, id, max; + + cchipcnt = chipcnt; + for (chipcnt -= 1; chipcnt >= 0; chipcnt--) { + if (chips[chipcnt].num >= MAX_CTRL_CS) { + error("chip no. too high (%d)!!\n", + chips[chipcnt].num); + return (EINVAL); + } + + if (chips[chipcnt].ctrl_num >= MAX_SIM_DEV) { + error("controller no. too high (%d)!!\n", + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + if (chips[chipcnt].width != 8 && + chips[chipcnt].width != 16) { + error("invalid width:%d for chip#%d", + chips[chipcnt].width, chips[chipcnt].num); + return (EINVAL); + } + + /* Check if page size is > 512 and if its power of 2 */ + if (chips[chipcnt].page_size < 512 || + (chips[chipcnt].page_size & + (chips[chipcnt].page_size - 1)) != 0) { + error("invalid page size:%d for chip#%d at ctrl#%d!!" + "\n", chips[chipcnt].page_size, + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + /* Check if controller no. ctrl_num is configured */ + for (i = 0, id = -1; i < ctrlcnt && id == -1; i++) + if (ctrls[i].num == chips[chipcnt].ctrl_num) + id = i; + + if (i == ctrlcnt && id == -1) { + error("Missing configuration for controller %d" + " (at least one chip is connected to it)", + chips[chipcnt].ctrl_num); + return (EINVAL); + } else { + /* + * Controller is configured -> check oob_size + * validity + */ + i = 0; + max = ctrls[id].ecc_layout[0]; + while (i < MAX_ECC_BYTES && + ctrls[id].ecc_layout[i] != 0xffff) { + + if (ctrls[id].ecc_layout[i] > max) + max = ctrls[id].ecc_layout[i]; + i++; + } + + if (chips[chipcnt].oob_size < (unsigned)i) { + error("OOB size for chip#%d at ctrl#%d is " + "smaller than ecc layout length!", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + exit(EINVAL); + } + + if (chips[chipcnt].oob_size < (unsigned)max) { + error("OOB size for chip#%d at ctrl#%d is " + "smaller than maximal ecc position in " + "defined layout!", chips[chipcnt].num, + chips[chipcnt].ctrl_num); + exit(EINVAL); + } + + + } + + if ((chips[chipcnt].erase_time < DELAYTIME_MIN || + chips[chipcnt].erase_time > DELAYTIME_MAX) && + chips[chipcnt].erase_time != 0) { + error("Invalid erase time value for chip#%d at " + "ctrl#%d", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + if ((chips[chipcnt].prog_time < DELAYTIME_MIN || + chips[chipcnt].prog_time > DELAYTIME_MAX) && + chips[chipcnt].prog_time != 0) { + error("Invalid prog time value for chip#%d at " + "ctr#%d!", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + if ((chips[chipcnt].read_time < DELAYTIME_MIN || + chips[chipcnt].read_time > DELAYTIME_MAX) && + chips[chipcnt].read_time != 0) { + error("Invalid read time value for chip#%d at " + "ctrl#%d!", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + } + /* Check if chips attached to the same controller, have same width */ + for (i = 0; i < ctrlcnt; i++) { + width = -1; + for (j = 0; j < cchipcnt; j++) { + if (chips[j].ctrl_num == i) { + if (width == -1) { + width = chips[j].width; + } else { + if (width != chips[j].width) { + error("Chips attached to " + "ctrl#%d have different " + "widths!\n", i); + return (EINVAL); + } + } + } + } + } + + return (0); +} + +static uint8_t +validate_ctrls(struct sim_ctrl *ctrl, int ctrlcnt) +{ + for (ctrlcnt -= 1; ctrlcnt >= 0; ctrlcnt--) { + if (ctrl[ctrlcnt].num > MAX_SIM_DEV) { + error("Controller no. too high (%d)!!\n", + ctrl[ctrlcnt].num); + return (EINVAL); + } + if (ctrl[ctrlcnt].num_cs > MAX_CTRL_CS) { + error("Too many CS (%d)!!\n", ctrl[ctrlcnt].num_cs); + return (EINVAL); + } + if (ctrl[ctrlcnt].ecc != 0 && ctrl[ctrlcnt].ecc != 1) { + error("ECC is set to neither 0 nor 1 !\n"); + return (EINVAL); + } + } + + return (0); +} + +static int validate_section_config(struct rcfile *f, const char *sect_name, + int sectno) +{ + struct nandsim_key *key; + struct nandsim_section *sect; + char **keys_tbl; + int i, match; + + for (match = 0, sect = (struct nandsim_section *)§ions; + sect != NULL; sect++) { + if (strcmp(sect->name, sect_name) == 0) { + match = 1; + break; + } + } + + if (match == 0) + return (EINVAL); + + keys_tbl = rc_getkeys(f, sect_name, sectno); + if (keys_tbl == NULL) + return (ENOMEM); + + for (i = 0; keys_tbl[i] != NULL; i++) { + key = sect->keys; + match = 0; + do { + if (strcmp(keys_tbl[i], key->keyname) == 0) { + match = 1; + break; + } + } while ((++key)->keyname != NULL); + + if (match == 0) { + error("Invalid key in config file: %s\n", keys_tbl[i]); + free(keys_tbl); + return (EINVAL); + } + } + + free(keys_tbl); + return (0); +} diff --git a/usr.sbin/nandsim/nandsim_cfgparse.h b/usr.sbin/nandsim/nandsim_cfgparse.h new file mode 100644 index 0000000..b9c642a --- /dev/null +++ b/usr.sbin/nandsim/nandsim_cfgparse.h @@ -0,0 +1,86 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NANDSIM_CONFPARSER_H_ +#define _NANDSIM_CONFPARSER_H_ + +#define VALUE_UINT 0x08 +#define VALUE_INT 0x10 +#define VALUE_UINTARRAY 0x18 +#define VALUE_INTARRAY 0x20 +#define VALUE_STRING 0x28 +#define VALUE_CHAR 0x40 +#define VALUE_BOOL 0x48 + +#define SIZE_8 0x01 +#define SIZE_16 0x02 +#define SIZE_32 0x04 + +#include "nandsim_rcfile.h" + +/* + * keyname = name of a key, + * mandatory = is key mandatory in section belonging to, 0=false 1=true + * valuetype = what kind of value is assigned to that key, e.g. + * VALUE_UINT | SIZE_8 -- unsigned uint size 8 bits; + * VALUE_UINTARRAY | SIZE_8 -- array of uints 8-bit long; + * VALUE_BOOL -- 'on', 'off','true','false','yes' or 'no' + * literals; + * VALUE_STRING -- strings + * field = ptr to the field that should hold value for parsed value + * maxlength = contains maximum length of an array (used only with either + * VALUE_STRING or VALUE_(U)INTARRAY value types. + */ +struct nandsim_key { + const char *keyname; + uint8_t mandatory; + uint8_t valuetype; + void *field; + uint32_t maxlength; +}; +struct nandsim_section { + const char *name; + struct nandsim_key *keys; +}; + +struct nandsim_config { + struct sim_param **simparams; + struct sim_chip **simchips; + struct sim_ctrl **simctrls; + int chipcnt; + int ctrlcnt; +}; + +int parse_intarray(char *, int **); +int parse_config(char *, const char *); +int parse_section(struct rcfile *, const char *, int); +int compare_configs(struct nandsim_config *, struct nandsim_config *); +int convert_argint(char *, int *); +int convert_arguint(char *, unsigned int *); + +#endif /* _NANDSIM_CONFPARSER_H_ */ diff --git a/usr.sbin/nandsim/nandsim_rcfile.c b/usr.sbin/nandsim/nandsim_rcfile.c new file mode 100644 index 0000000..0f99e7b --- /dev/null +++ b/usr.sbin/nandsim/nandsim_rcfile.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 1999, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nandsim_rcfile.h" + +SLIST_HEAD(rcfile_head, rcfile); +static struct rcfile_head pf_head = {NULL}; +static struct rcsection *rc_findsect(struct rcfile *rcp, + const char *sectname, int sect_id); +static struct rcsection *rc_addsect(struct rcfile *rcp, + const char *sectname); +static int rc_sect_free(struct rcsection *rsp); +static struct rckey *rc_sect_findkey(struct rcsection *rsp, + const char *keyname); +static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, + char *value); +static void rc_key_free(struct rckey *p); +static void rc_parse(struct rcfile *rcp); + +static struct rcfile* rc_find(const char *filename); + +/* + * open rcfile and load its content, if already open - return previous handle + */ +int +rc_open(const char *filename, const char *mode,struct rcfile **rcfile) +{ + struct rcfile *rcp; + FILE *f; + rcp = rc_find(filename); + if (rcp) { + *rcfile = rcp; + return (0); + } + f = fopen (filename, mode); + if (f == NULL) + return errno; + rcp = malloc(sizeof(struct rcfile)); + if (rcp == NULL) { + fclose(f); + return ENOMEM; + } + bzero(rcp, sizeof(struct rcfile)); + rcp->rf_name = strdup(filename); + rcp->rf_f = f; + SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); + rc_parse(rcp); + *rcfile = rcp; + return (0); +} + +int +rc_close(struct rcfile *rcp) +{ + struct rcsection *p,*n; + + fclose(rcp->rf_f); + for (p = SLIST_FIRST(&rcp->rf_sect); p; ) { + n = p; + p = SLIST_NEXT(p,rs_next); + rc_sect_free(n); + } + free(rcp->rf_name); + SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next); + free(rcp); + return (0); +} + +static struct rcfile* +rc_find(const char *filename) +{ + struct rcfile *p; + + SLIST_FOREACH(p, &pf_head, rf_next) + if (strcmp (filename, p->rf_name) == 0) + return (p); + return (0); +} + +/* Find section with given name and id */ +static struct rcsection * +rc_findsect(struct rcfile *rcp, const char *sectname, int sect_id) +{ + struct rcsection *p; + + SLIST_FOREACH(p, &rcp->rf_sect, rs_next) + if (strcmp(p->rs_name, sectname) == 0 && p->rs_id == sect_id) + return (p); + return (NULL); +} + +static struct rcsection * +rc_addsect(struct rcfile *rcp, const char *sectname) +{ + struct rcsection *p; + int id = 0; + p = rc_findsect(rcp, sectname, 0); + if (p) { + /* + * If section with that name already exists -- add one more, + * same named, but with different id (higher by one) + */ + while (p != NULL) { + id = p->rs_id + 1; + p = rc_findsect(rcp, sectname, id); + } + } + p = malloc(sizeof(*p)); + if (!p) + return (NULL); + p->rs_name = strdup(sectname); + p->rs_id = id; + SLIST_INIT(&p->rs_keys); + SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next); + return (p); +} + +static int +rc_sect_free(struct rcsection *rsp) +{ + struct rckey *p,*n; + + for (p = SLIST_FIRST(&rsp->rs_keys); p; ) { + n = p; + p = SLIST_NEXT(p,rk_next); + rc_key_free(n); + } + free(rsp->rs_name); + free(rsp); + return (0); +} + +static struct rckey * +rc_sect_findkey(struct rcsection *rsp, const char *keyname) +{ + struct rckey *p; + + SLIST_FOREACH(p, &rsp->rs_keys, rk_next) + if (strcmp(p->rk_name, keyname)==0) + return (p); + return (NULL); +} + +static struct rckey * +rc_sect_addkey(struct rcsection *rsp, const char *name, char *value) +{ + struct rckey *p; + p = rc_sect_findkey(rsp, name); + if (p) { + free(p->rk_value); + } else { + p = malloc(sizeof(*p)); + if (!p) + return (NULL); + SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next); + p->rk_name = strdup(name); + } + p->rk_value = value ? strdup(value) : strdup(""); + return (p); +} + +static void +rc_key_free(struct rckey *p) +{ + free(p->rk_value); + free(p->rk_name); + free(p); +} + +enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue}; + +static void +rc_parse(struct rcfile *rcp) +{ + FILE *f = rcp->rf_f; + int state = stNewLine, c; + struct rcsection *rsp = NULL; + struct rckey *rkp = NULL; + char buf[2048]; + char *next = buf, *last = &buf[sizeof(buf)-1]; + + while ((c = getc (f)) != EOF) { + if (c == '\r') + continue; + if (state == stNewLine) { + next = buf; + if (isspace(c)) + continue; /* skip leading junk */ + if (c == '[') { + state = stHeader; + rsp = NULL; + continue; + } + if (c == '#' || c == ';') { + state = stSkipToEOL; + } else { /* something meaningful */ + state = stGetKey; + } + } + if (state == stSkipToEOL || next == last) {/* ignore long lines */ + if (c == '\n') { + state = stNewLine; + next = buf; + } + continue; + } + if (state == stHeader) { + if (c == ']') { + *next = 0; + next = buf; + rsp = rc_addsect(rcp, buf); + state = stSkipToEOL; + } else + *next++ = c; + continue; + } + if (state == stGetKey) { + if (c == ' ' || c == '\t')/* side effect: 'key name='*/ + continue; /* become 'keyname=' */ + if (c == '\n') { /* silently ignore ... */ + state = stNewLine; + continue; + } + if (c != '=') { + *next++ = c; + continue; + } + *next = 0; + if (rsp == NULL) { + fprintf(stderr, "Key '%s' defined before " + "section\n", buf); + state = stSkipToEOL; + continue; + } + rkp = rc_sect_addkey(rsp, buf, NULL); + next = buf; + state = stGetValue; + continue; + } + /* only stGetValue left */ + if (state != stGetValue) { + fprintf(stderr, "Well, I can't parse file " + "'%s'\n",rcp->rf_name); + state = stSkipToEOL; + } + if (c != '\n') { + *next++ = c; + continue; + } + *next = 0; + rkp->rk_value = strdup(buf); + state = stNewLine; + rkp = NULL; + } /* while */ + if (c == EOF && state == stGetValue) { + *next = 0; + rkp->rk_value = strdup(buf); + } +} + +int +rc_getstringptr(struct rcfile *rcp, const char *section, int sect_id, + const char *key, char **dest) +{ + struct rcsection *rsp; + struct rckey *rkp; + + *dest = NULL; + rsp = rc_findsect(rcp, section, sect_id); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp,key); + if (!rkp) + return (ENOENT); + *dest = rkp->rk_value; + return (0); +} + +int +rc_getstring(struct rcfile *rcp, const char *section, int sect_id, + const char *key, unsigned int maxlen, char *dest) +{ + char *value; + int error; + + error = rc_getstringptr(rcp, section, sect_id, key, &value); + if (error) + return (error); + if (strlen(value) >= maxlen) { + fprintf(stderr, "line too long for key '%s' in section '%s'," + "max = %d\n",key, section, maxlen); + return (EINVAL); + } + strcpy(dest,value); + return (0); +} + +int +rc_getint(struct rcfile *rcp, const char *section, int sect_id, + const char *key, int *value) +{ + struct rcsection *rsp; + struct rckey *rkp; + + rsp = rc_findsect(rcp, section, sect_id); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp,key); + if (!rkp) + return (ENOENT); + errno = 0; + *value = strtol(rkp->rk_value,NULL,0); + if (errno) { + fprintf(stderr, "invalid int value '%s' for key '%s' in " + "section '%s'\n",rkp->rk_value,key,section); + return (errno); + } + return (0); +} + +/* + * 1,yes,true + * 0,no,false + */ +int +rc_getbool(struct rcfile *rcp, const char *section, int sect_id, + const char *key, int *value) +{ + struct rcsection *rsp; + struct rckey *rkp; + char *p; + + rsp = rc_findsect(rcp, section, sect_id); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp,key); + if (!rkp) + return (ENOENT); + p = rkp->rk_value; + while (*p && isspace(*p)) p++; + if (*p == '0' || strcasecmp(p,"no") == 0 || + strcasecmp(p, "false") == 0 || + strcasecmp(p, "off") == 0) { + *value = 0; + return (0); + } + if (*p == '1' || strcasecmp(p,"yes") == 0 || + strcasecmp(p, "true") == 0 || + strcasecmp(p, "on") == 0) { + *value = 1; + return (0); + } + fprintf(stderr, "invalid boolean value '%s' for key '%s' in section " + "'%s' \n",p, key, section); + return (EINVAL); +} + +/* Count how many sections with given name exists in configuration. */ +int rc_getsectionscount(struct rcfile *f, const char *sectname) +{ + struct rcsection *p; + int count = 0; + + p = rc_findsect(f, sectname, 0); + if (p) { + while (p != NULL) { + count = p->rs_id + 1; + p = rc_findsect(f, sectname, count); + } + return (count); + } else + return (0); +} + +char ** +rc_getkeys(struct rcfile *rcp, const char *sectname, int sect_id) +{ + struct rcsection *rsp; + struct rckey *p; + char **names_tbl; + int i = 0, count = 0; + + rsp = rc_findsect(rcp, sectname, sect_id); + if (rsp == NULL) + return (NULL); + + SLIST_FOREACH(p, &rsp->rs_keys, rk_next) + count++; + + names_tbl = malloc(sizeof(char *) * (count + 1)); + if (names_tbl == NULL) + return (NULL); + + SLIST_FOREACH(p, &rsp->rs_keys, rk_next) + names_tbl[i++] = p->rk_name; + + names_tbl[i] = NULL; + return (names_tbl); +} + diff --git a/usr.sbin/nandsim/nandsim_rcfile.h b/usr.sbin/nandsim/nandsim_rcfile.h new file mode 100644 index 0000000..f5c3ce9 --- /dev/null +++ b/usr.sbin/nandsim/nandsim_rcfile.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1999, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp + */ + +#ifndef _SIMRC_H_ +#define _SIMRC_H_ + +#include + +struct rckey { + SLIST_ENTRY(rckey) rk_next; + char *rk_name; /* key name */ + char *rk_value; /* key value */ +}; + +struct rcsection { + SLIST_ENTRY(rcsection) rs_next; + SLIST_HEAD(rckey_head,rckey) rs_keys; /* key list */ + char *rs_name; /* section name */ + int rs_id; /* allow few same named */ +}; + +struct rcfile { + SLIST_ENTRY(rcfile) rf_next; + SLIST_HEAD(rcsec_head, rcsection) rf_sect; /* sections list */ + char *rf_name; /* file name */ + FILE *rf_f; /* file desc */ +}; + +int rc_open(const char *, const char *,struct rcfile **); +int rc_close(struct rcfile *); +int rc_getstringptr(struct rcfile *, const char *, int, const char *, + char **); +int rc_getstring(struct rcfile *, const char *, int, const char *, + unsigned int, char *); +int rc_getint(struct rcfile *, const char *, int, const char *, int *); +int rc_getbool(struct rcfile *, const char *, int, const char *, int *); +int rc_getsectionscount(struct rcfile *, const char *); +char **rc_getkeys(struct rcfile *, const char *, int); + +#endif /* _SIMRC_H_ */ diff --git a/usr.sbin/nandsim/sample.conf b/usr.sbin/nandsim/sample.conf new file mode 100644 index 0000000..bc534e1 --- /dev/null +++ b/usr.sbin/nandsim/sample.conf @@ -0,0 +1,174 @@ +#- +# Copyright (C) 2009-2012 Semihalf +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# +# Sample NANDsim configuration file. +# + +############################################################################# +# +# [sim] General (common) simulator configuration section. +# +[sim] +# log_level=0..255 +log_level=11 + +# log_output=[none, console, ram, file] +# +# When log_output=file is specified, each [ctrl] section must have a +# corresponding 'log_filename' field provided, which specifies log file name +# to be used. +log_output=none + +############################################################################# +# +# [ctrl] Controller configuration section. +# +# There can be a number of controllers defined for simulation, each has a +# dedicated [ctrl] section. With a given controller there are associated +# subordinate NAND chips, which are tied to chip select lines. +# +[ctrl] +# The number of this controller. +# ctrl_num=0..3 +ctrl_num=0 + +# The number of chip selects available at this controller. +# num_cs=1..4 +num_cs=1 + +# ECC enable flag. +# ecc=[on|off] +ecc=on + +# ECC layout. This is the list of byte offsets within OOB area, which comprise +# the ECC contents set. +# +# ecc_layout=[byte1, byte2-byte3, ..byten] +ecc_layout=[0-53] + +# Absolute path to the log file for this controller. +#log_filename=/var/log/nandsim-ctl0.log + + +############################################################################# +# +# [chip] Chip configuration section. +# +# There can be a number of individual NAND chip devices defined for +# simulation, and each has a dedicated [chip] section. +# +# A particular chip needs to be associated with its parent NAND controller by +# specifying the following fields: controller number (chip_ctrl) and the chip +# select line it is connected to (chip_cs). The chip can be connected to only +# a single (and unique) controller:cs pair. +# +[chip] +# The number of parent controller. This has to fit one of the controller +# instance number (ctrl_num from [ctrl] section). +# chip_ctrl=0..3 +chip_ctrl=0 + +# Chip select line. +# chip_cs=0..3 +chip_cs=0 + +# ONFI device identifier. +# device_id=0x00..0xff +device_id=0xd3 + +# ONFI manufacturer identifier. +# manufacturer_id=0x00..0xff +manufacturer_id=0xec + +# Textual description of the chip. +# model="model_name" +model="k9xxg08uxM:1GiB 3,3V 8-bit" + +# Textual name of the chip manufacturer. +# manufacturer="manufacturer name" +manufacturer="SAMSUNG" + +# page_size=[must be power of 2 and >= 512] (in bytes) +page_size=2048 +# oob_size=[>0] +oob_size=64 +# pages_per_block=n*32 +pages_per_block=64 +# blocks_per_lun=[>0] +blocks_per_lun=4096 +# luns=1..N +luns=1 +# column_addr_cycle=[1,2] +column_addr_cycle=2 +# row_addr_cycle=[1,2,3] +row_addr_cycle=3 + +# program_time= (in us) +program_time=0 +# erase_time= (in us) +erase_time=0 +# read_time= (in us) +read_time=0 +# ccs_time= (in us) +#ccs_time=200 + +# Simulate write-protect on the chip. +# write_protect=[yes|no] +#write_protect=no + +# Blocks wear-out threshold. Each block has a counter of program-erase cycles; +# when this counter reaches 'wear_out' value a given block is treated as a bad +# block (access will report error). +# +# Setting wear_out to 0 means that blocks will never wear out. +# +# wear_out=0..100000 +wear_out=50000 + +# Errors per million read/write bytes. This simulates an accidental read/write +# block error, which can happen in real devices with certain probability. Note +# this isn't a bad block condition i.e. the block at which the read/write +# operation is simulated to fail here remains usable, only the operation has +# not succeeded (this is where ECC comes into play and is supposed to correct +# such problems). +# +# error_ratio=0..1000000 +#error_ratio=50 + +# Chip data bus width. All chips connected to the same controller must have +# the same bus width. +# +# width=[8|16] +width=8 + +# Bad block map. NANDsim emulates bad block behavior upon accessing a block +# with number from the specified list. +# +# bad_block_map=[bad_block1, bad_block2-bad_block3, ..bad_blockn] +bad_block_map=[100-200] + diff --git a/usr.sbin/nandtool/Makefile b/usr.sbin/nandtool/Makefile new file mode 100644 index 0000000..ae9de2d --- /dev/null +++ b/usr.sbin/nandtool/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +PROG= nandtool +SRCS= nandtool.c nand_read.c nand_write.c nand_erase.c nand_info.c +SRCS+= nand_readoob.c nand_writeoob.c +BINDIR= /usr/sbin +DPADD= ${LIBGEOM} +LDADD= -lgeom +MAN= nandtool.8 + +.include diff --git a/usr.sbin/nandtool/nand_erase.c b/usr.sbin/nandtool/nand_erase.c new file mode 100644 index 0000000..50bfaa6 --- /dev/null +++ b/usr.sbin/nandtool/nand_erase.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "nandtool.h" + +int nand_erase(struct cmd_param *params) +{ + struct chip_param_io chip_params; + char *dev; + int fd = -1, ret = 0; + off_t pos, count; + off_t start, nblocks, i; + int block_size, mult; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "Please supply valid 'dev' parameter.\n"); + return (1); + } + + if (param_has_value(params, "count")) + count = param_get_intx(params, "count"); + else + count = 1; + + if ((fd = g_open(dev, 1)) < 0) { + perrorf("Cannot open %s", dev); + return (1); + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + + if (param_has_value(params, "page")) { + pos = chip_params.page_size * param_get_intx(params, "page"); + mult = chip_params.page_size; + } else if (param_has_value(params, "block")) { + pos = block_size * param_get_intx(params, "block"); + mult = block_size; + } else if (param_has_value(params, "pos")) { + pos = param_get_intx(params, "pos"); + mult = 1; + } else { + /* Erase whole chip */ + if (ioctl(fd, DIOCGMEDIASIZE, &count) == -1) { + ret = 1; + goto out; + } + + pos = 0; + mult = 1; + } + + if (pos % block_size) { + fprintf(stderr, "Position must be block-size aligned!\n"); + ret = 1; + goto out; + } + + count *= mult; + start = pos / block_size; + nblocks = count / block_size; + + for (i = 0; i < nblocks; i++) { + if (g_delete(fd, (start + i) * block_size, block_size) == -1) { + perrorf("Cannot erase block %d - probably a bad block", + start + i); + ret = 1; + } + } + +out: + g_close(fd); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_info.c b/usr.sbin/nandtool/nand_info.c new file mode 100644 index 0000000..38fe010 --- /dev/null +++ b/usr.sbin/nandtool/nand_info.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "nandtool.h" + +int nand_info(struct cmd_param *params) +{ + struct chip_param_io chip_params; + int fd = -1, ret = 0; + int block_size; + off_t chip_size, media_size; + const char *dev; + + if ((dev = param_get_string(params, "dev")) == NULL) { + fprintf(stderr, "Please supply 'dev' parameter, eg. " + "'dev=/dev/gnand0'\n"); + return (1); + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + return (1); + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + if (ioctl(fd, DIOCGMEDIASIZE, &media_size) == -1) { + perrorf("Cannot ioctl(DIOCGMEDIASIZE)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + chip_size = block_size * chip_params.blocks; + + printf("Device:\t\t\t%s\n", dev); + printf("Page size:\t\t%d bytes\n", chip_params.page_size); + printf("Block size:\t\t%d bytes (%d KB)\n", block_size, + block_size / 1024); + printf("OOB size per page:\t%d bytes\n", chip_params.oob_size); + printf("Chip size:\t\t%jd MB\n", (uintmax_t)(chip_size / 1024 / 1024)); + printf("Slice size:\t\t%jd MB\n", + (uintmax_t)(media_size / 1024 / 1024)); + +out: + g_close(fd); + + return (ret); +} diff --git a/usr.sbin/nandtool/nand_read.c b/usr.sbin/nandtool/nand_read.c new file mode 100644 index 0000000..5267b7d --- /dev/null +++ b/usr.sbin/nandtool/nand_read.c @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "nandtool.h" + +int nand_read(struct cmd_param *params) +{ + struct chip_param_io chip_params; + int fd = -1, out_fd = -1, done = 0, ret = 0; + char *dev, *out; + int pos, count, mult, block_size; + uint8_t *buf = NULL; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "You must specify 'dev' parameter\n"); + return (1); + } + + if ((out = param_get_string(params, "out"))) { + out_fd = open(out, O_WRONLY|O_CREAT); + if (out_fd == -1) { + perrorf("Cannot open %s for writing", out); + return (1); + } + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + + if (param_has_value(params, "page")) { + pos = chip_params.page_size * param_get_int(params, "page"); + mult = chip_params.page_size; + } else if (param_has_value(params, "block")) { + pos = block_size * param_get_int(params, "block"); + mult = block_size; + } else if (param_has_value(params, "pos")) { + pos = param_get_int(params, "pos"); + mult = 1; + if (pos % chip_params.page_size) { + fprintf(stderr, "Position must be page-size aligned!\n"); + ret = 1; + goto out; + } + } else { + fprintf(stderr, "You must specify one of: 'block', 'page'," + "'pos' arguments\n"); + ret = 1; + goto out; + } + + if (!(param_has_value(params, "count"))) + count = mult; + else + count = param_get_int(params, "count") * mult; + + if (!(buf = malloc(chip_params.page_size))) { + perrorf("Cannot allocate buffer [size %x]", + chip_params.page_size); + ret = 1; + goto out; + } + + lseek(fd, pos, SEEK_SET); + + while (done < count) { + if ((ret = read(fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + perrorf("read error (read %d bytes)", ret); + goto out; + } + + if (out_fd != -1) { + done += ret; + if ((ret = write(out_fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + perrorf("write error (written %d bytes)", ret); + ret = 1; + goto out; + } + } else { + hexdumpoffset(buf, chip_params.page_size, done); + done += ret; + } + } + +out: + g_close(fd); + if (out_fd != -1) + close(out_fd); + if (buf) + free(buf); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_readoob.c b/usr.sbin/nandtool/nand_readoob.c new file mode 100644 index 0000000..37fd14b --- /dev/null +++ b/usr.sbin/nandtool/nand_readoob.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include "nandtool.h" + +int nand_read_oob(struct cmd_param *params) +{ + struct chip_param_io chip_params; + struct nand_oob_rw req; + char *dev, *out; + int fd = -1, fd_out = -1, ret = 0; + int page; + uint8_t *buf = NULL; + + if ((page = param_get_int(params, "page")) < 0) { + fprintf(stderr, "You must supply valid 'page' argument.\n"); + return (1); + } + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "You must supply 'dev' argument.\n"); + return (1); + } + + if ((out = param_get_string(params, "out"))) { + if ((fd_out = open(out, O_WRONLY | O_CREAT)) == -1) { + perrorf("Cannot open %s", out); + ret = 1; + goto out; + } + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + buf = malloc(chip_params.oob_size); + if (buf == NULL) { + perrorf("Cannot allocate %d bytes\n", chip_params.oob_size); + ret = 1; + goto out; + } + + req.page = page; + req.len = chip_params.oob_size; + req.data = buf; + + if (ioctl(fd, NAND_IO_OOB_READ, &req) == -1) { + perrorf("Cannot read OOB from %s", dev); + ret = 1; + goto out; + } + + if (fd_out != -1) + write(fd_out, buf, chip_params.oob_size); + else + hexdump(buf, chip_params.oob_size); + +out: + close(fd_out); + + if (fd != -1) + g_close(fd); + if (buf) + free(buf); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_write.c b/usr.sbin/nandtool/nand_write.c new file mode 100644 index 0000000..157c6aa --- /dev/null +++ b/usr.sbin/nandtool/nand_write.c @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "nandtool.h" + +int nand_write(struct cmd_param *params) +{ + struct chip_param_io chip_params; + char *dev, *file; + int in_fd = -1, ret = 0, done = 0; + int fd, block_size, mult, pos, count; + uint8_t *buf = NULL; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "Please supply 'dev' argument.\n"); + return (1); + } + + if (!(file = param_get_string(params, "in"))) { + fprintf(stderr, "Please supply 'in' argument.\n"); + return (1); + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + return (1); + } + + if ((in_fd = open(file, O_RDONLY)) == -1) { + perrorf("Cannot open file %s", file); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + + if (param_has_value(params, "page")) { + pos = chip_params.page_size * param_get_int(params, "page"); + mult = chip_params.page_size; + } else if (param_has_value(params, "block")) { + pos = block_size * param_get_int(params, "block"); + mult = block_size; + } else if (param_has_value(params, "pos")) { + pos = param_get_int(params, "pos"); + mult = 1; + if (pos % chip_params.page_size) { + fprintf(stderr, "Position must be page-size " + "aligned!\n"); + ret = 1; + goto out; + } + } else { + fprintf(stderr, "You must specify one of: 'block', 'page'," + "'pos' arguments\n"); + ret = 1; + goto out; + } + + if (!(param_has_value(params, "count"))) + count = mult; + else + count = param_get_int(params, "count") * mult; + + if (!(buf = malloc(chip_params.page_size))) { + perrorf("Cannot allocate buffer [size %x]", + chip_params.page_size); + ret = 1; + goto out; + } + + lseek(fd, pos, SEEK_SET); + + while (done < count) { + if ((ret = read(in_fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + if (ret > 0) { + /* End of file ahead, truncate here */ + break; + } else { + perrorf("Cannot read from %s", file); + ret = 1; + goto out; + } + } + + if ((ret = write(fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + ret = 1; + goto out; + } + + done += ret; + } + +out: + g_close(fd); + if (in_fd != -1) + close(in_fd); + if (buf) + free(buf); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_writeoob.c b/usr.sbin/nandtool/nand_writeoob.c new file mode 100644 index 0000000..53cb32a --- /dev/null +++ b/usr.sbin/nandtool/nand_writeoob.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include "nandtool.h" + +int nand_write_oob(struct cmd_param *params) +{ + struct chip_param_io chip_params; + struct nand_oob_rw req; + char *dev, *in; + int fd = -1, fd_in = -1, ret = 0; + uint8_t *buf = NULL; + int page; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "Please supply valid 'dev' parameter.\n"); + return (1); + } + + if (!(in = param_get_string(params, "in"))) { + fprintf(stderr, "Please supply valid 'in' parameter.\n"); + return (1); + } + + if ((page = param_get_int(params, "page")) < 0) { + fprintf(stderr, "Please supply valid 'page' parameter.\n"); + return (1); + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + return (1); + } + + if ((fd_in = open(in, O_RDONLY)) == -1) { + perrorf("Cannot open %s", in); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + buf = malloc(chip_params.oob_size); + if (buf == NULL) { + perrorf("Cannot allocate %d bytes\n", chip_params.oob_size); + ret = 1; + goto out; + } + + if (read(fd_in, buf, chip_params.oob_size) == -1) { + perrorf("Cannot read from %s", in); + ret = 1; + goto out; + } + + req.page = page; + req.len = chip_params.oob_size; + req.data = buf; + + if (ioctl(fd, NAND_IO_OOB_PROG, &req) == -1) { + perrorf("Cannot write OOB to %s", dev); + ret = 1; + goto out; + } + +out: + g_close(fd); + if (fd_in != -1) + close(fd_in); + if (buf) + free(buf); + + return (ret); +} + + diff --git a/usr.sbin/nandtool/nandtool.8 b/usr.sbin/nandtool/nandtool.8 new file mode 100644 index 0000000..2cc4588 --- /dev/null +++ b/usr.sbin/nandtool/nandtool.8 @@ -0,0 +1,188 @@ +.\" Copyright (c) 2010 Semihalf +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd April 10, 2012 +.Dt NANDTOOL 8 +.Os +.Sh NAME +.Nm nandtool +.Nd NAND devices swiss army knife +.Sh SYNOPSIS +.Nm +.Ar command +.Op Ar operands ... +.Sh DESCRIPTION +The +.Nm +utility can be used to perform various operations on +.Xr gnand 4 +devices (read, write, erase, +read and write OOB area and to get info about NAND flash chip). +.Pp +The following commands are available: +.Bl -tag -width ".Cm of Ns = Ns Ar file" +.It Cm read Ns +Read pages from NAND device. +.It Cm write Ns +Write pages to NAND device. +.It Cm erase Ns +Erase blocks. +Requires offset aligned to block granularity. +.It Cm info Ns +Get information about NAND chip (page size, block size, OOB area size, chip size +and media size) +.It Cm readoob Ns +Read OOB area from specified page. +.It Cm writeoob Ns +Write OOB area bound to specified page. +.It Cm help Ns +Get usage info. +.El +.Sh COMMAND read +.Pp +The following operands are available for +.Nm +.Cm read +command: +.Bl -tag -width ".Cm of Ns = Ns Ar file" +.It Cm dev Ns = Ns Ar +Path to a +.Xr gnand 4 +device node, required for all operations. +.It Cm out Ns = Ns Ar +Output file path. If not specified, page contents +will be dumped to stdout in format similar to +.Xr hexdump 1 +.It Cm page Ns = Ns Ar +Offset on device, expressed as page number. +.It Cm block Ns = Ns Ar +Offset on device, expressed as block number. +.It Cm pos Ns = Ns Ar +Offset on device, expressed in bytes (however, must be aligned +to page granularity). +.It Cm count Ns = Ns Ar +Count of objects (pages, blocks, bytes). +.El +.Sh COMMAND readoob +.Bl -tag -width ".Cm of Ns = Ns Ar file" +.Pp +The following operands are available for +.Nm +.Cm readoob +command: +.Pp +.It Cm dev Ns = Ns Ar +Path to NAND device node. +.It Cm page Ns = Ns Ar +Offset on device, expressed as page number. +.It Cm out Ns = Ns Ar +Output file path, optional. +.El +.Sh COMMAND write +.Bl -tag -width ".Cm of Ns = Ns Ar file" +The following operands are available for +.Nm +.Cm write +command: +.It Cm dev Ns = Ns Ar +Path to NAND device node. +.It Cm page Ns = Ns Ar +Offset on device, expressed as page number. +.It Cm block Ns = Ns Ar +Offset on device, expressed as block number. +.It Cm pos Ns = Ns Ar +Offset on device, expressed in bytes (however, must be aligned +to page granularity). +.It Cm in Ns = Ns Ar +Input file path. +.El +.Sh COMMAND writeoob +.Bl -tag -width ".Cm of Ns = Ns Ar file" +The following operands are available for +.Nm +.Cm writeoob +command: +.It Cm dev Ns = Ns Ar +Path to NAND device node. +.It Cm page Ns = Ns Ar +Offset on device, expressed as page number. +.It Cm in Ns = Ns Ar +Input file path. +.El +.Sh COMMAND erase +.Bl -tag -width ".Cm of Ns = Ns Ar file" +The following operands are available for +.Nm +.Cm erase +command: +.It Cm dev Ns = Ns Ar +Path to NAND device node. +.It Cm page Ns = Ns Ar +Offset on device, expressed as page number. +.It Cm block Ns = Ns Ar +Offset on device, expressed as block number. +.It Cm pos Ns = Ns Ar +Offset on device, epressed in bytes (however, must be aligned +to block granularity). +.It Cm count Ns = Ns Ar +Count of objects (pages, blocks, bytes). +.El +.Pp +WARNING: The only required parameter for the \fBerase\fP command is +.Ar dev . +When no other arguments are provided the whole device is erased! +.Sh COMMAND info +.Bl -tag -width ".Cm of Ns = Ns Ar file" +There is only one operand available for +.Nm +.Cm info +command: +.It Cm dev Ns = Ns Ar +Path to NAND device node. +.El +.Sh COMMAND help +.Bl -tag -width ".Cm of Ns = Ns Ar file" +There is only one operand available for +.Nm +.Cm help +command: +.Pp +.It Cm topic Ns = Ns Ar +Help topic. +.El +.Sh EXIT STATUS +.Ex -std +If the supplied argument +.Ar dev +points to a device node other than gnand or gnand.raw both +.Nm +.Cm readoob +and +.Nm +.Cm writeoob +return error. +.Sh SEE ALSO +.Xr gnand 4 diff --git a/usr.sbin/nandtool/nandtool.c b/usr.sbin/nandtool/nandtool.c new file mode 100644 index 0000000..bd7075b --- /dev/null +++ b/usr.sbin/nandtool/nandtool.c @@ -0,0 +1,283 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include "nandtool.h" +#include "usage.h" + +int usage(struct cmd_param *); + +static const struct { + const char *name; + const char *usage; + int (*handler)(struct cmd_param *); +} commands[] = { + { "help", nand_help_usage, usage }, + { "read", nand_read_usage, nand_read }, + { "write", nand_write_usage, nand_write }, + { "erase", nand_erase_usage, nand_erase }, + { "readoob", nand_read_oob_usage, nand_read_oob }, + { "writeoob", nand_write_oob_usage, nand_write_oob }, + { "info", nand_info_usage, nand_info }, + { NULL, NULL, NULL }, +}; + +static char * +_param_get_stringx(struct cmd_param *params, const char *name, int doexit) +{ + int i; + + for (i = 0; params[i].name[0] != '\0'; i++) { + if (!strcmp(params[i].name, name)) + return params[i].value; + } + + if (doexit) { + perrorf("Missing parameter %s", name); + exit(1); + } + return (NULL); +} + +char * +param_get_string(struct cmd_param *params, const char *name) +{ + + return (_param_get_stringx(params, name, 0)); +} + +static int +_param_get_intx(struct cmd_param *params, const char *name, int doexit) +{ + int ret; + char *str = _param_get_stringx(params, name, doexit); + + if (!str) + return (-1); + + errno = 0; + ret = (int)strtol(str, (char **)NULL, 10); + if (errno) { + if (doexit) { + perrorf("Invalid value for parameter %s", name); + exit(1); + } + return (-1); + } + + return (ret); +} + +int +param_get_intx(struct cmd_param *params, const char *name) +{ + + return (_param_get_intx(params, name, 1)); +} + +int +param_get_int(struct cmd_param *params, const char *name) +{ + + return (_param_get_intx(params, name, 0)); +} + +int +param_get_boolean(struct cmd_param *params, const char *name) +{ + char *str = param_get_string(params, name); + + if (!str) + return (0); + + if (!strcmp(str, "true") || !strcmp(str, "yes")) + return (1); + + return (0); +} + +int +param_has_value(struct cmd_param *params, const char *name) +{ + int i; + + for (i = 0; params[i].name[0] != '\0'; i++) { + if (!strcmp(params[i].name, name)) + return (1); + } + + return (0); +} + +int +param_get_count(struct cmd_param *params) +{ + int i; + + for (i = 0; params[i].name[0] != '\0'; i++); + + return (i); +} + +void +hexdumpoffset(uint8_t *buf, int length, int off) +{ + int i, j; + for (i = 0; i < length; i += 16) { + printf("%08x: ", off + i); + + for (j = 0; j < 16; j++) + printf("%02x ", buf[i+j]); + + printf("| "); + + for (j = 0; j < 16; j++) { + printf("%c", isalnum(buf[i+j]) + ? buf[i+j] + : '.'); + } + + printf("\n"); + } +} + +void +hexdump(uint8_t *buf, int length) +{ + + hexdumpoffset(buf, length, 0); +} + +void * +xmalloc(size_t len) +{ + void *ret = malloc(len); + + if (!ret) { + fprintf(stderr, "Cannot allocate buffer of %zd bytes. " + "Exiting.\n", len); + exit(EX_OSERR); + } + + return (ret); +} + +void +perrorf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, ": %s\n", strerror(errno)); +} + +int +usage(struct cmd_param *params) +{ + int i; + + if (!params || !param_get_count(params)) { + fprintf(stderr, "Usage: nandtool [arguments...]\n"); + fprintf(stderr, "Arguments are in form 'name=value'.\n\n"); + fprintf(stderr, "Available commands:\n"); + + for (i = 0; commands[i].name != NULL; i++) + fprintf(stderr, "\t%s\n", commands[i].name); + + fprintf(stderr, "\n"); + fprintf(stderr, "For information about particular command, " + "type:\n"); + fprintf(stderr, "'nandtool help topic='\n"); + } else if (param_has_value(params, "topic")) { + for (i = 0; commands[i].name != NULL; i++) { + if (!strcmp(param_get_string(params, "topic"), + commands[i].name)) { + fprintf(stderr, commands[i].usage, "nandtool"); + return (0); + } + } + + fprintf(stderr, "No such command\n"); + return (EX_SOFTWARE); + } else { + fprintf(stderr, "Wrong arguments given. Try: 'nandtool help'\n"); + } + + return (EX_USAGE); +} + +int +main(int argc, const char *argv[]) +{ + struct cmd_param *params; + int i, ret, idx; + + if (argc < 2) { + usage(NULL); + return (0); + } + + params = malloc(sizeof(struct cmd_param) * (argc - 1)); + + for (i = 2, idx = 0; i < argc; i++, idx++) { + if (sscanf(argv[i], "%63[^=]=%63s", params[idx].name, + params[idx].value) < 2) { + fprintf(stderr, "Syntax error in argument %d. " + "Argument should be in form 'name=value'.\n", i); + free(params); + return (-1); + } + } + + params[idx].name[0] = '\0'; + params[idx].value[0] = '\0'; + + for (i = 0; commands[i].name != NULL; i++) { + if (!strcmp(commands[i].name, argv[1])) { + ret = commands[i].handler(params); + free(params); + return (ret); + } + } + + free(params); + fprintf(stderr, "Unknown command. Try '%s help'\n", argv[0]); + + return (-1); +} + diff --git a/usr.sbin/nandtool/nandtool.h b/usr.sbin/nandtool/nandtool.h new file mode 100644 index 0000000..639f95e --- /dev/null +++ b/usr.sbin/nandtool/nandtool.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __UTILS_H +#define __UTILS_H + +struct cmd_param +{ + char name[64]; + char value[64]; +}; + +char *param_get_string(struct cmd_param *, const char *); +int param_get_int(struct cmd_param *, const char *); +int param_get_intx(struct cmd_param *, const char *); +int param_get_boolean(struct cmd_param *, const char *); +int param_has_value(struct cmd_param *, const char *); +int param_get_count(struct cmd_param *); +void perrorf(const char *, ...); +void hexdumpoffset(uint8_t *, int, int); +void hexdump(uint8_t *, int); +void *xmalloc(size_t); + +/* Command handlers */ +int nand_read(struct cmd_param *); +int nand_write(struct cmd_param *); +int nand_read_oob(struct cmd_param *); +int nand_write_oob(struct cmd_param *); +int nand_erase(struct cmd_param *); +int nand_info(struct cmd_param *); + +#endif /* __UTILS_H */ diff --git a/usr.sbin/nandtool/usage.h b/usr.sbin/nandtool/usage.h new file mode 100644 index 0000000..74e8543 --- /dev/null +++ b/usr.sbin/nandtool/usage.h @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2010-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __USAGE_H +#define __USAGE_H + +static const char nand_help_usage[] = + "Usage: %s help topic=\n" + "\n" + "Arguments:\n" + "\tcmd\t- [help|read|write|erase|readoob|writeoob|info]\n" + "\n"; + +static const char nand_read_usage[] = + "Usage: %s read dev= (block|page|pos)=n [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tblock\t- starting block or\n" + "\tpage\t- starting page or\n" + "\tpos\t- starting position (in bytes, must be page-aligned)\n" + "\tout\t- output file (hexdump to stdout if not supplied)\n" + "\n" + "Note that you can only specify only one of: 'block', 'page', 'pos'\n" + "parameters at once. 'count' parameter is meaningful in terms of used\n" + "unit (page, block or byte).\n"; + +static const char nand_write_usage[] = + "Usage: %s write dev= in= (block|page|pos)=n [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tin\t- path to input file which be writed to gnand\n" + "\tblock\t- starting block or\n" + "\tpage\t- starting page or\n" + "\tpos\t- starting position (in bytes, must be page-aligned)\n" + "\tcount\t- byte/page/block count\n" + "\n" + ""; + +static const char nand_erase_usage[] = + "Usage: %s erase dev= (block|page|pos)=n [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tblock\t- starting block or\n" + "\tpage\t- starting page or\n" + "\tpos\t- starting position (in bytes, muse be block-aligned)\n" + "\tcount\t- byte/page/block count\n" + "\n" + "NOTE: position and count for erase operation MUST be block-aligned\n"; + +static const char nand_read_oob_usage[] = + "Usage: %s readoob dev= page=n [out=file] [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tpage\t- page (page) number\n" + "\tout\t- outut file (hexdump to stdout if not supplied)\n" + "\tcount\t- page count (default is 1)\n" + "\n" + "If you supply count parameter with value other than 1, data will be\n" + "read from subsequent page's OOB areas\n"; + +static const char nand_write_oob_usage[] = + "Usage: %s writeoob dev= in= page=n [count=n]\n" + "\n" + "\tdev\t- path to gnand device node\n" + "\tin\t- path to file containing data which will be written\n" + "\tpage\t- page (page) number\n" + "\n" + "If you supply count parameter with value other than 1, data will be\n" + "written to subsequent page's OOB areas\n"; + +static const char nand_info_usage[] = + "Usage: %s info dev=\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n"; + +static const char nand_stats_usage[] = + "Usage: %s stats dev= (page|block)=\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n"; + +#endif /* __USAGE_H */ -- cgit v1.1