From 4b17158506d5f3bb7e71111f5b6287f19bf20190 Mon Sep 17 00:00:00 2001 From: behlendo Date: Wed, 12 Mar 2008 20:52:46 +0000 Subject: [PATCH] - Implemented vnode interfaces and 6 test cases to the test suite. - Re-implmented kobj support based on the vnode support. - Add TESTS option to check.sh, and removed delay after module load. git-svn-id: https://outreach.scidac.gov/svn/spl/trunk@39 7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c --- FIXME | 6 +- include/sys/kobj.h | 11 +- include/sys/sysmacros.h | 16 -- include/sys/uio.h | 5 +- include/sys/vnode.h | 161 +++++++----- modules/spl/spl-kobj.c | 51 ++-- modules/spl/spl-vnode.c | 452 +++++++++++++++++++++++---------- modules/splat/Makefile.in | 1 + modules/splat/splat-ctl.c | 2 + modules/splat/splat-internal.h | 3 + modules/splat/splat-kobj.c | 10 +- modules/splat/splat-vnode.c | 372 +++++++++++++++++++++++++++ scripts/check.sh | 10 +- 13 files changed, 833 insertions(+), 267 deletions(-) create mode 100644 modules/splat/splat-vnode.c diff --git a/FIXME b/FIXME index d853e96ac..887616efb 100644 --- a/FIXME +++ b/FIXME @@ -11,8 +11,4 @@ sys/u8_textprep_data - AlL borrowed from libsolcompat sys/acl.h _ All borrowed from libsolcompat sys/acl_impl.h _ All borrowed from libsolcompat -* Fix failure in rw lock test 6 - -* Non-atomic 64bit support for kstat.h - -* Write vnode interface +* Implement solaris style atomic interfaces diff --git a/include/sys/kobj.h b/include/sys/kobj.h index 306fcdcfc..82da951e1 100644 --- a/include/sys/kobj.h +++ b/include/sys/kobj.h @@ -5,21 +5,16 @@ extern "C" { #endif -#include -#include -#include -#include +#include typedef struct _buf { - struct file *fp; + vnode_t *vp; } _buf_t; -extern void *rootdir; - extern struct _buf *kobj_open_file(const char *name); extern void kobj_close_file(struct _buf *file); extern int kobj_read_file(struct _buf *file, char *buf, - unsigned size, unsigned off); + ssize_t size, offset_t off); extern int kobj_get_filesize(struct _buf *file, uint64_t *size); #ifdef __cplusplus diff --git a/include/sys/sysmacros.h b/include/sys/sysmacros.h index 218f59567..076f26784 100644 --- a/include/sys/sysmacros.h +++ b/include/sys/sysmacros.h @@ -56,21 +56,6 @@ extern "C" { #define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20) #define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) -#define kred NULL - -#define FREAD 1 -#define FWRITE 2 -#define FCREAT O_CREAT -#define FTRUNC O_TRUNC -#define FOFFMAX O_LARGEFILE -#define FSYNC O_SYNC -#define FDSYNC O_DSYNC -#define FRSYNC O_RSYNC -#define FEXCL O_EXCL - -#define FNODSYNC 0x10000 /* fsync pseudo flag */ -#define FNOFOLLOW 0x20000 /* don't follow symlinks */ - /* Missing macros */ #define PAGESIZE PAGE_SIZE @@ -136,7 +121,6 @@ extern int highbit(unsigned long i); #define zone_dataset_visible(x, y) (1) #define INGLOBALZONE(z) (1) -/* XXX - Borrowed from zfs project libsolcompat/include/sys/sysmacros.h */ /* common macros */ #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) diff --git a/include/sys/uio.h b/include/sys/uio.h index 845256dc9..6da4d34ed 100644 --- a/include/sys/uio.h +++ b/include/sys/uio.h @@ -1,7 +1,10 @@ #ifndef _SPL_UIO_H #define _SPL_UIO_H -typedef enum uio_rw { UIO_READ, UIO_WRITE } uio_rw_t; +typedef enum uio_rw { + UIO_READ = 0, + UIO_WRITE = 1, +} uio_rw_t; #define UIO_SYSSPACE 1 diff --git a/include/sys/vnode.h b/include/sys/vnode.h index 9d3484fcf..7583d04d4 100644 --- a/include/sys/vnode.h +++ b/include/sys/vnode.h @@ -1,35 +1,94 @@ #ifndef _SPL_VNODE_H #define _SPL_VNODE_H +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define XVA_MAPSIZE 3 #define XVA_MAGIC 0x78766174 +#define FREAD 1 +#define FWRITE 2 +#define FCREAT O_CREAT +#define FTRUNC O_TRUNC +#define FOFFMAX O_LARGEFILE +#define FSYNC O_SYNC +#define FDSYNC O_DSYNC +#define FRSYNC O_RSYNC +#define FEXCL O_EXCL +#define FDIRECT O_DIRECT + +#define FNODSYNC 0x10000 /* fsync pseudo flag */ +#define FNOFOLLOW 0x20000 /* don't follow symlinks */ + +#define AT_TYPE 0x00001 +#define AT_MODE 0x00002 +#undef AT_UID /* Conflicts with linux/auxvec.h */ +#define AT_UID 0x00004 +#undef AT_GID /* Conflicts with linux/auxvec.h */ +#define AT_GID 0x00008 +#define AT_FSID 0x00010 +#define AT_NODEID 0x00020 +#define AT_NLINK 0x00040 +#define AT_SIZE 0x00080 +#define AT_ATIME 0x00100 +#define AT_MTIME 0x00200 +#define AT_CTIME 0x00400 +#define AT_RDEV 0x00800 +#define AT_BLKSIZE 0x01000 +#define AT_NBLOCKS 0x02000 +#define AT_SEQ 0x08000 +#define AT_XVATTR 0x10000 + +#define CRCREAT 0 + typedef enum vtype { - VNON = 0, - VREG = 1, - VDIR = 2, - VBLK = 3, - VCHR = 4, - VLNK = 5, - VFIFO = 6, - VDOOR = 7, - VPROC = 8, - VSOCK = 9, - VPORT = 10, - VBAD = 11 + VNON = 0, + VREG = 1, + VDIR = 2, + VBLK = 3, + VCHR = 4, + VLNK = 5, + VFIFO = 6, + VDOOR = 7, + VPROC = 8, + VSOCK = 9, + VPORT = 10, + VBAD = 11 } vtype_t; typedef struct vnode { - uint64_t v_size; - int v_fd; - mode_t v_mode; - char *v_path; + struct file *v_fp; vtype_t v_type; } vnode_t; +typedef struct vattr { + enum vtype va_type; /* vnode type */ + u_short va_mode; /* acc mode */ + short va_uid; /* owner uid */ + short va_gid; /* owner gid */ + long va_fsid; /* fs id */ + long va_nodeid; /* node # */ + short va_nlink; /* # links */ + u_long va_size; /* file size */ + long va_blocksize; /* block size */ + struct timeval va_atime; /* last acc */ + struct timeval va_mtime; /* last mod */ + struct timeval va_ctime; /* last chg */ + dev_t va_rdev; /* dev */ + long va_blocks; /* space used */ +} vattr_t; +#if 0 typedef struct xoptattr { timestruc_t xoa_createtime; /* Create time of file */ uint8_t xoa_archive; @@ -46,12 +105,6 @@ typedef struct xoptattr { uint8_t xoa_av_modified; } xoptattr_t; -typedef struct vattr { - uint_t va_mask; /* bit-mask of attributes */ - u_offset_t va_size; /* file size in bytes */ -} vattr_t; - - typedef struct xvattr { vattr_t xva_vattr; /* Embedded vattr structure */ uint32_t xva_magic; /* Magic Number */ @@ -70,46 +123,28 @@ typedef struct vsecattr { void *vsa_dfaclentp; /* pointer to default ACL entries */ size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */ } vsecattr_t; - -#define AT_TYPE 0x00001 -#define AT_MODE 0x00002 -// #define AT_UID 0x00004 /* Conflicts with linux/auxvec.h */ -// #define AT_GID 0x00008 /* Conflicts with linux/auxvec.h */ -#define AT_FSID 0x00010 -#define AT_NODEID 0x00020 -#define AT_NLINK 0x00040 -#define AT_SIZE 0x00080 -#define AT_ATIME 0x00100 -#define AT_MTIME 0x00200 -#define AT_CTIME 0x00400 -#define AT_RDEV 0x00800 -#define AT_BLKSIZE 0x01000 -#define AT_NBLOCKS 0x02000 -#define AT_SEQ 0x08000 -#define AT_XVATTR 0x10000 - -#define CRCREAT 0 - -#define VOP_CLOSE(vp, f, c, o, cr, ct) 0 -#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) 0 -#define VOP_GETATTR(vp, vap, fl, cr, ct) ((vap)->va_size = (vp)->v_size, 0) - -#define VOP_FSYNC(vp, f, cr, ct) sys_fsync((vp)->v_fd) - -#define VN_RELE(vp) vn_close(vp) - -extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp, - int x2, int x3); -extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp, - int x2, int x3, vnode_t *vp, int fd); -extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, - offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp); -extern void vn_close(vnode_t *vp); - -#define vn_remove(path, x1, x2) remove(path) -#define vn_rename(from, to, seg) rename((from), (to)) -#define vn_is_readonly(vp) B_FALSE - -extern vnode_t *rootdir; +#endif + +extern int vn_open(const char *path, int seg, int flags, int mode, + vnode_t **vpp, int x1, void *x2); +extern int vn_openat(const char *path, int seg, int flags, int mode, + vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd); +extern int vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, + offset_t off, int seg, int x1, rlim64_t x2, + void *x3, ssize_t *residp); +extern int vn_close(vnode_t *vp, int flags, int x1, int x2, int x3, int x4); +extern int vn_remove(const char *path, int x1, int x2); +extern int vn_rename(const char *path1, const char *path2, int x1); +extern int vn_getattr(vnode_t *vp, vattr_t *vap, int flags, int x3, void *x4); +extern int vn_fsync(vnode_t *vp, int flags, int x3, int x4); + +#define VOP_CLOSE vn_close +#define VN_RELE(vp) +#define VOP_GETATTR vn_getattr +#define VOP_FSYNC vn_fsync +#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) +#define vn_is_readonly(vp) 0 + +extern void *rootdir; #endif /* SPL_VNODE_H */ diff --git a/modules/spl/spl-kobj.c b/modules/spl/spl-kobj.c index ce0625b9f..eb55ad3e1 100644 --- a/modules/spl/spl-kobj.c +++ b/modules/spl/spl-kobj.c @@ -1,21 +1,18 @@ #include #include "config.h" -void *rootdir = NULL; -EXPORT_SYMBOL(rootdir); - struct _buf * kobj_open_file(const char *name) { struct _buf *file; - struct file *fp; + vnode_t *vp; + int rc; - fp = filp_open(name, O_RDONLY, 0644); - if (IS_ERR(fp)) + if ((rc = vn_open(name, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0))) return ((_buf_t *)-1UL); - file = kmem_zalloc(sizeof(_buf_t), KM_SLEEP); - file->fp = fp; + file = kmalloc(sizeof(_buf_t), GFP_KERNEL); + file->vp = vp; return file; } /* kobj_open_file() */ @@ -24,52 +21,34 @@ EXPORT_SYMBOL(kobj_open_file); void kobj_close_file(struct _buf *file) { - filp_close(file->fp, 0); - kmem_free(file, sizeof(_buf_t)); + VOP_CLOSE(file->vp, 0, 0, 0, 0, 0); + VN_RELE(file->vp); + kfree(file); return; } /* kobj_close_file() */ EXPORT_SYMBOL(kobj_close_file); int -kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off) +kobj_read_file(struct _buf *file, char *buf, ssize_t size, offset_t off) { - loff_t offset = off; - mm_segment_t saved_fs; - int rc; - - if (!file || !file->fp) - return -EINVAL; - - if (!file->fp->f_op || !file->fp->f_op->read) - return -ENOSYS; - - /* Writable user data segment must be briefly increased for this - * process so we can use the user space read call paths to write - * in to memory allocated by the kernel. */ - saved_fs = get_fs(); - set_fs(get_ds()); - rc = file->fp->f_op->read(file->fp, buf, size, &offset); - set_fs(saved_fs); - - return rc; + return vn_rdwr(UIO_READ, file->vp, buf, size, off, + UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); } /* kobj_read_file() */ EXPORT_SYMBOL(kobj_read_file); int kobj_get_filesize(struct _buf *file, uint64_t *size) { - struct kstat stat; + vattr_t vap; int rc; - if (!file || !file->fp || !size) - return -EINVAL; - - rc = vfs_getattr(file->fp->f_vfsmnt, file->fp->f_dentry, &stat); + rc = VOP_GETATTR(file->vp, &vap, 0, 0, NULL); if (rc) return rc; - *size = stat.size; + *size = vap.va_size; + return rc; } /* kobj_get_filesize() */ EXPORT_SYMBOL(kobj_get_filesize); diff --git a/modules/spl/spl-vnode.c b/modules/spl/spl-vnode.c index 5089f8567..9024909b8 100644 --- a/modules/spl/spl-vnode.c +++ b/modules/spl/spl-vnode.c @@ -1,168 +1,358 @@ #include +#include #include "config.h" -/* - * XXX: currently borrrowed from libsolcompat until this - * can be adapted to the linux kernel interfaces. - */ -#if 0 -/* - * ========================================================================= - * vnode operations - * ========================================================================= - */ -/* - * Note: for the xxxat() versions of these functions, we assume that the - * starting vp is always rootdir (which is true for spa_directory.c, the only - * ZFS consumer of these interfaces). We assert this is true, and then emulate - * them by adding '/' in front of the path. - */ - -/*ARGSUSED*/ +void *rootdir = NULL; +EXPORT_SYMBOL(rootdir); + +static vtype_t +vn_get_sol_type(umode_t mode) +{ + if (S_ISREG(mode)) + return VREG; + + if (S_ISDIR(mode)) + return VDIR; + + if (S_ISCHR(mode)) + return VCHR; + + if (S_ISBLK(mode)) + return VBLK; + + if (S_ISFIFO(mode)) + return VFIFO; + + if (S_ISLNK(mode)) + return VLNK; + + if (S_ISSOCK(mode)) + return VSOCK; + + if (S_ISCHR(mode)) + return VCHR; + + return VNON; +} /* vn_get_sol_type() */ + int -vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3) +vn_open(const char *path, int seg, int flags, int mode, + vnode_t **vpp, int x1, void *x2) { - int fd; + struct file *fp; + struct kstat stat; + int rc, saved_umask, flags_rw; vnode_t *vp; - int old_umask; - char realpath[MAXPATHLEN]; - struct stat64 st; - - /* - * If we're accessing a real disk from userland, we need to use - * the character interface to avoid caching. This is particularly - * important if we're trying to look at a real in-kernel storage - * pool from userland, e.g. via zdb, because otherwise we won't - * see the changes occurring under the segmap cache. - * On the other hand, the stupid character device returns zero - * for its size. So -- gag -- we open the block device to get - * its size, and remember it for subsequent VOP_GETATTR(). - */ -#if defined(__sun__) || defined(__sun) - if (strncmp(path, "/dev/", 5) == 0) { -#else - if (0) { -#endif - char *dsk; - fd = open64(path, O_RDONLY); - if (fd == -1) - return (errno); - if (fstat64(fd, &st) == -1) { - close(fd); - return (errno); - } - close(fd); - (void) sprintf(realpath, "%s", path); - dsk = strstr(path, "/dsk/"); - if (dsk != NULL) - (void) sprintf(realpath + (dsk - path) + 1, "r%s", - dsk + 1); - } else { - (void) sprintf(realpath, "%s", path); - if (!(flags & FCREAT) && stat64(realpath, &st) == -1) - return (errno); - } -#ifdef __linux__ - if (!(flags & FCREAT) && S_ISBLK(st.st_mode)) { - flags |= O_DIRECT; - if (flags & FWRITE) - flags |= O_EXCL; + BUG_ON(seg != UIO_SYSSPACE); + BUG_ON(!vpp); + *vpp = NULL; + + if (!(flags & FCREAT) && (flags & FWRITE)) + flags |= FEXCL; + + flags_rw = flags & (FWRITE | FREAD); + flags &= ~(FWRITE | FREAD); + switch (flags_rw) { + case FWRITE: flags |= O_WRONLY; + case FREAD: flags |= O_RDONLY; + case (FWRITE | FREAD): flags |= O_RDWR; } -#endif if (flags & FCREAT) - old_umask = umask(0); + saved_umask = xchg(¤t->fs->umask, 0); - /* - * The construct 'flags - FREAD' conveniently maps combinations of - * FREAD and FWRITE to the corresponding O_RDONLY, O_WRONLY, and O_RDWR. - */ - fd = open64(realpath, flags - FREAD, mode); + fp = filp_open(path, flags, mode); if (flags & FCREAT) - (void) umask(old_umask); + (void)xchg(¤t->fs->umask, saved_umask); - if (fd == -1) - return (errno); + if (IS_ERR(fp)) + return PTR_ERR(fp); - if (fstat64(fd, &st) == -1) { - close(fd); - return (errno); + rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat); + if (rc) { + filp_close(fp, 0); + return rc; } - (void) fcntl(fd, F_SETFD, FD_CLOEXEC); - - *vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL); + vp = kmalloc(sizeof(vnode_t), GFP_ATOMIC); + if (!vp) { + filp_close(fp, 0); + return -ENOMEM; + } - vp->v_fd = fd; - vp->v_size = st.st_size; - vp->v_mode = st.st_mode; - vp->v_path = spa_strdup(path); + vp->v_type = vn_get_sol_type(stat.mode); + vp->v_fp = fp; + *vpp = vp; - return (0); -} + return 0; +} /* vn_open() */ +EXPORT_SYMBOL(vn_open); -/*ARGSUSED*/ int -vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, - int x3, vnode_t *startvp, int fd) +vn_openat(const char *path, int seg, int flags, int mode, + vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd) { - char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL); - int ret; + char *realpath; + int rc; - ASSERT(startvp == rootdir); - (void) sprintf(realpath, "/%s", path); + BUG_ON(vp != rootdir); - /* fd ignored for now, need if want to simulate nbmand support */ - ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3); + realpath = kmalloc(strlen(path) + 2, GFP_KERNEL); + if (!realpath) + return -ENOMEM; - umem_free(realpath, strlen(path) + 2); + sprintf(realpath, "/%s", path); + rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2); - return (ret); -} + kfree(realpath); + + return rc; +} /* vn_openat() */ +EXPORT_SYMBOL(vn_openat); -/*ARGSUSED*/ int -vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset, - int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp) +vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off, + int seg, int x1, rlim64_t x2, void *x3, ssize_t *residp) { - ssize_t iolen, split; + loff_t offset; + mm_segment_t saved_fs; + struct file *fp; + int rc; + + BUG_ON(!(uio == UIO_WRITE || uio == UIO_READ)); + BUG_ON(!vp); + BUG_ON(!vp->v_fp); + BUG_ON(seg != UIO_SYSSPACE); + BUG_ON(x1 != 0); + BUG_ON(x2 != RLIM64_INFINITY); + + offset = off; + fp = vp->v_fp; + + /* Writable user data segment must be briefly increased for this + * process so we can use the user space read call paths to write + * in to memory allocated by the kernel. */ + saved_fs = get_fs(); + set_fs(get_ds()); + + if (uio & UIO_WRITE) + rc = vfs_write(fp, addr, len, &offset); + else + rc = vfs_read(fp, addr, len, &offset); + + set_fs(saved_fs); + + if (rc < 0) + return rc; - if (uio == UIO_READ) { - iolen = pread64(vp->v_fd, addr, len, offset); + if (residp) { + *residp = len - rc; } else { - /* - * To simulate partial disk writes, we split writes into two - * system calls so that the process can be killed in between. - */ -#ifdef ZFS_DEBUG - if (!S_ISBLK(vp->v_mode) && !S_ISCHR(vp->v_mode)) { - split = (len > 0 ? rand() % len : 0); - iolen = pwrite64(vp->v_fd, addr, split, offset); - iolen += pwrite64(vp->v_fd, (char *)addr + split, - len - split, offset + split); - } else - iolen = pwrite64(vp->v_fd, addr, len, offset); -#else - iolen = pwrite64(vp->v_fd, addr, len, offset); -#endif + if (rc != len) + return -EIO; } - if (iolen < 0) - return (errno); - if (residp) - *residp = len - iolen; - else if (iolen != len) - return (EIO); - return (0); + return 0; +} /* vn_rdwr() */ +EXPORT_SYMBOL(vn_rdwr); + +int +vn_close(vnode_t *vp, int flags, int x1, int x2, int x3, int x4) +{ + int rc; + + BUG_ON(!vp); + BUG_ON(!vp->v_fp); + + rc = filp_close(vp->v_fp, 0); + kfree(vp); + + return rc; +} /* vn_close() */ +EXPORT_SYMBOL(vn_close); + +static struct dentry *lookup_hash(struct nameidata *nd) +{ + return __lookup_hash(&nd->last, nd->dentry, nd); +} /* lookup_hash() */ + +/* Modified do_unlinkat() from linux/fs/namei.c, only uses exported symbols */ +int +vn_remove(const char *path, int x1, int x2) +{ + struct dentry *dentry; + struct nameidata nd; + struct inode *inode = NULL; + int rc = 0; + + rc = path_lookup(path, LOOKUP_PARENT, &nd); + if (rc) + goto exit; + + rc = -EISDIR; + if (nd.last_type != LAST_NORM) + goto exit1; + + mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = lookup_hash(&nd); + rc = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + /* Why not before? Because we want correct rc value */ + if (nd.last.name[nd.last.len]) + goto slashes; + inode = dentry->d_inode; + if (inode) + atomic_inc(&inode->i_count); + rc = vfs_unlink(nd.dentry->d_inode, dentry); +exit2: + dput(dentry); + } + mutex_unlock(&nd.dentry->d_inode->i_mutex); + if (inode) + iput(inode); /* truncate the inode here */ +exit1: + path_release(&nd); +exit: + return rc; + +slashes: + rc = !dentry->d_inode ? -ENOENT : + S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; + goto exit2; +} /* vn_remove() */ +EXPORT_SYMBOL(vn_remove); + +/* Modified do_rename() from linux/fs/namei.c, only uses exported symbols */ +int +vn_rename(const char *oldname, const char *newname, int x1) +{ + struct dentry * old_dir, * new_dir; + struct dentry * old_dentry, *new_dentry; + struct dentry * trap; + struct nameidata oldnd, newnd; + int rc = 0; + + rc = path_lookup(oldname, LOOKUP_PARENT, &oldnd); + if (rc) + goto exit; + + rc = path_lookup(newname, LOOKUP_PARENT, &newnd); + if (rc) + goto exit1; + + rc = -EXDEV; + if (oldnd.mnt != newnd.mnt) + goto exit2; + + old_dir = oldnd.dentry; + rc = -EBUSY; + if (oldnd.last_type != LAST_NORM) + goto exit2; + + new_dir = newnd.dentry; + if (newnd.last_type != LAST_NORM) + goto exit2; + + trap = lock_rename(new_dir, old_dir); + + old_dentry = lookup_hash(&oldnd); + + rc = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto exit3; + + /* source must exist */ + rc = -ENOENT; + if (!old_dentry->d_inode) + goto exit4; + + /* unless the source is a directory trailing slashes give -ENOTDIR */ + if (!S_ISDIR(old_dentry->d_inode->i_mode)) { + rc = -ENOTDIR; + if (oldnd.last.name[oldnd.last.len]) + goto exit4; + if (newnd.last.name[newnd.last.len]) + goto exit4; + } + + /* source should not be ancestor of target */ + rc = -EINVAL; + if (old_dentry == trap) + goto exit4; + + new_dentry = lookup_hash(&newnd); + rc = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit4; + + /* target should not be an ancestor of source */ + rc = -ENOTEMPTY; + if (new_dentry == trap) + goto exit5; + + rc = vfs_rename(old_dir->d_inode, old_dentry, + new_dir->d_inode, new_dentry); +exit5: + dput(new_dentry); +exit4: + dput(old_dentry); +exit3: + unlock_rename(new_dir, old_dir); +exit2: + path_release(&newnd); +exit1: + path_release(&oldnd); +exit: + return rc; } +EXPORT_SYMBOL(vn_rename); -void -vn_close(vnode_t *vp) +int +vn_getattr(vnode_t *vp, vattr_t *vap, int flags, int x3, void *x4) { - close(vp->v_fd); - spa_strfree(vp->v_path); - umem_free(vp, sizeof (vnode_t)); + struct file *fp; + struct kstat stat; + int rc; + + BUG_ON(!vp); + BUG_ON(!vp->v_fp); + BUG_ON(!vap); + + fp = vp->v_fp; + + rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat); + if (rc) + return rc; + + vap->va_type = vn_get_sol_type(stat.mode); + vap->va_mode = stat.mode; + vap->va_uid = stat.uid; + vap->va_gid = stat.gid; + vap->va_fsid = 0; + vap->va_nodeid = stat.ino; + vap->va_nlink = stat.nlink; + vap->va_size = stat.size; + vap->va_blocksize = stat.blksize; + vap->va_atime.tv_sec = stat.atime.tv_sec; + vap->va_atime.tv_usec = stat.atime.tv_nsec / NSEC_PER_USEC; + vap->va_mtime.tv_sec = stat.mtime.tv_sec; + vap->va_mtime.tv_usec = stat.mtime.tv_nsec / NSEC_PER_USEC; + vap->va_ctime.tv_sec = stat.ctime.tv_sec; + vap->va_ctime.tv_usec = stat.ctime.tv_nsec / NSEC_PER_USEC; + vap->va_rdev = stat.rdev; + vap->va_blocks = stat.blocks; + + return rc; } -#endif +EXPORT_SYMBOL(vn_getattr); + +int vn_fsync(vnode_t *vp, int flags, int x3, int x4) +{ + BUG_ON(!vp); + BUG_ON(!vp->v_fp); + + return file_fsync(vp->v_fp, vp->v_fp->f_dentry, 0); +} /* vn_fsync() */ +EXPORT_SYMBOL(vn_fsync); diff --git a/modules/splat/Makefile.in b/modules/splat/Makefile.in index 0c7526f19..69f38b1f5 100644 --- a/modules/splat/Makefile.in +++ b/modules/splat/Makefile.in @@ -21,6 +21,7 @@ splat-objs += splat-condvar.o splat-objs += splat-thread.o splat-objs += splat-rwlock.o splat-objs += splat-time.o +splat-objs += splat-vnode.o splat-objs += splat-kobj.o splatmodule := splat.ko diff --git a/modules/splat/splat-ctl.c b/modules/splat/splat-ctl.c index 693277c5c..e9026cd8d 100644 --- a/modules/splat/splat-ctl.c +++ b/modules/splat/splat-ctl.c @@ -591,6 +591,7 @@ splat_init(void) SPLAT_SUBSYSTEM_INIT(thread); SPLAT_SUBSYSTEM_INIT(rwlock); SPLAT_SUBSYSTEM_INIT(time); + SPLAT_SUBSYSTEM_INIT(vnode); SPLAT_SUBSYSTEM_INIT(kobj); dev = MKDEV(SPLAT_MAJOR, 0); @@ -654,6 +655,7 @@ splat_fini(void) unregister_chrdev_region(dev, SPLAT_MINORS); SPLAT_SUBSYSTEM_FINI(kobj); + SPLAT_SUBSYSTEM_FINI(vnode); SPLAT_SUBSYSTEM_FINI(time); SPLAT_SUBSYSTEM_FINI(rwlock); SPLAT_SUBSYSTEM_FINI(thread); diff --git a/modules/splat/splat-internal.h b/modules/splat/splat-internal.h index b8e803290..aca4b3d38 100644 --- a/modules/splat/splat-internal.h +++ b/modules/splat/splat-internal.h @@ -170,6 +170,7 @@ splat_subsystem_t * splat_rwlock_init(void); splat_subsystem_t * splat_taskq_init(void); splat_subsystem_t * splat_thread_init(void); splat_subsystem_t * splat_time_init(void); +splat_subsystem_t * splat_vnode_init(void); splat_subsystem_t * splat_kobj_init(void); void splat_condvar_fini(splat_subsystem_t *); @@ -180,6 +181,7 @@ void splat_rwlock_fini(splat_subsystem_t *); void splat_taskq_fini(splat_subsystem_t *); void splat_thread_fini(splat_subsystem_t *); void splat_time_fini(splat_subsystem_t *); +void splat_vnode_fini(splat_subsystem_t *); void splat_kobj_fini(splat_subsystem_t *); int splat_condvar_id(void); @@ -190,6 +192,7 @@ int splat_rwlock_id(void); int splat_taskq_id(void); int splat_thread_id(void); int splat_time_id(void); +int splat_vnode_id(void); int splat_kobj_id(void); #endif /* _SPLAT_INTERNAL_H */ diff --git a/modules/splat/splat-kobj.c b/modules/splat/splat-kobj.c index 91f591f46..c24057b0e 100644 --- a/modules/splat/splat-kobj.c +++ b/modules/splat/splat-kobj.c @@ -1,15 +1,15 @@ #include "splat-internal.h" -#define SPLAT_SUBSYSTEM_KOBJ 0x0900 +#define SPLAT_SUBSYSTEM_KOBJ 0x0a00 #define SPLAT_KOBJ_NAME "kobj" #define SPLAT_KOBJ_DESC "Kernel File Tests" -#define SPLAT_KOBJ_TEST1_ID 0x0901 -#define SPLAT_KOBJ_TEST1_NAME "kobj1" +#define SPLAT_KOBJ_TEST1_ID 0x0a01 +#define SPLAT_KOBJ_TEST1_NAME "open" #define SPLAT_KOBJ_TEST1_DESC "File Open/Close Test" -#define SPLAT_KOBJ_TEST2_ID 0x0902 -#define SPLAT_KOBJ_TEST2_NAME "kobj2" +#define SPLAT_KOBJ_TEST2_ID 0x0a02 +#define SPLAT_KOBJ_TEST2_NAME "size/read" #define SPLAT_KOBJ_TEST2_DESC "File Size/Read Test" #define SPLAT_KOBJ_TEST_FILE "/etc/fstab" diff --git a/modules/splat/splat-vnode.c b/modules/splat/splat-vnode.c new file mode 100644 index 000000000..188448db8 --- /dev/null +++ b/modules/splat/splat-vnode.c @@ -0,0 +1,372 @@ +#include "splat-internal.h" + +#define SPLAT_SUBSYSTEM_VNODE 0x0900 +#define SPLAT_VNODE_NAME "vnode" +#define SPLAT_VNODE_DESC "Kernel Vnode Tests" + +#define SPLAT_VNODE_TEST1_ID 0x0901 +#define SPLAT_VNODE_TEST1_NAME "vn_open" +#define SPLAT_VNODE_TEST1_DESC "Vn_open Test" + +#define SPLAT_VNODE_TEST2_ID 0x0902 +#define SPLAT_VNODE_TEST2_NAME "vn_openat" +#define SPLAT_VNODE_TEST2_DESC "Vn_openat Test" + +#define SPLAT_VNODE_TEST3_ID 0x0903 +#define SPLAT_VNODE_TEST3_NAME "vn_rdwr" +#define SPLAT_VNODE_TEST3_DESC "Vn_rdwrt Test" + +#define SPLAT_VNODE_TEST4_ID 0x0904 +#define SPLAT_VNODE_TEST4_NAME "vn_rename" +#define SPLAT_VNODE_TEST4_DESC "Vn_rename Test" + +#define SPLAT_VNODE_TEST5_ID 0x0905 +#define SPLAT_VNODE_TEST5_NAME "vn_getattr" +#define SPLAT_VNODE_TEST5_DESC "Vn_getattr Test" + +#define SPLAT_VNODE_TEST6_ID 0x0906 +#define SPLAT_VNODE_TEST6_NAME "vn_sync" +#define SPLAT_VNODE_TEST6_DESC "Vn_sync Test" + +#define SPLAT_VNODE_TEST_FILE "/etc/fstab" +#define SPLAT_VNODE_TEST_FILE_AT "etc/fstab" +#define SPLAT_VNODE_TEST_FILE_RW "/tmp/spl.vnode.tmp" +#define SPLAT_VNODE_TEST_FILE_RW1 "/tmp/spl.vnode.tmp.1" +#define SPLAT_VNODE_TEST_FILE_RW2 "/tmp/spl.vnode.tmp.2" + +static int +splat_vnode_test1(struct file *file, void *arg) +{ + vnode_t *vp; + int rc; + + if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, + FREAD, 0644, &vp, 0, 0))) { + splat_vprint(file, SPLAT_VNODE_TEST1_NAME, + "Failed to vn_open test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE, rc); + return rc; + } + + rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); + VN_RELE(vp); + + if (rc) { + splat_vprint(file, SPLAT_VNODE_TEST1_NAME, + "Failed to vn_close test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE, rc); + return rc; + } + + splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully vn_open'ed " + "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); + + return rc; +} /* splat_vnode_test1() */ + +static int +splat_vnode_test2(struct file *file, void *arg) +{ + vnode_t *vp; + int rc; + + if ((rc = vn_openat(SPLAT_VNODE_TEST_FILE_AT, UIO_SYSSPACE, + FREAD, 0644, &vp, 0, 0, rootdir, 0))) { + splat_vprint(file, SPLAT_VNODE_TEST2_NAME, + "Failed to vn_openat test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE, rc); + return rc; + } + + rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); + VN_RELE(vp); + + if (rc) { + splat_vprint(file, SPLAT_VNODE_TEST2_NAME, + "Failed to vn_close test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE, rc); + return rc; + } + + splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Successfully vn_openat'ed " + "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); + + return rc; +} /* splat_vnode_test2() */ + +static int +splat_vnode_test3(struct file *file, void *arg) +{ + vnode_t *vp; + char buf1[32] = "SPL VNode Interface Test File\n"; + char buf2[32] = ""; + int rc; + + if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, + FWRITE | FREAD | FCREAT | FEXCL, + 0644, &vp, 0, 0))) { + splat_vprint(file, SPLAT_VNODE_TEST3_NAME, + "Failed to vn_open test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW, rc); + return rc; + } + + rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0, + UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); + if (rc < 0) { + splat_vprint(file, SPLAT_VNODE_TEST3_NAME, + "Failed vn_rdwr write of test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW, rc); + goto out; + } + + rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0, + UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); + if (rc < 0) { + splat_vprint(file, SPLAT_VNODE_TEST3_NAME, + "Failed vn_rdwr read of test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW, rc); + goto out; + } + + if (strncmp(buf1, buf2, strlen(buf1))) { + rc = EINVAL; + splat_vprint(file, SPLAT_VNODE_TEST3_NAME, + "Failed strncmp data written does not match " + "data read\nWrote: %sRead: %s\n", buf1, buf2); + goto out; + } + + rc = 0; + splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1); + splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2); + splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and " + "read expected data pattern to test file: %s\n", + SPLAT_VNODE_TEST_FILE_RW); + +out: + VOP_CLOSE(vp, 0, 0, 0, 0, 0); + VN_RELE(vp); + vn_remove(SPLAT_VNODE_TEST_FILE_RW, 0, 0); + + return rc; +} /* splat_vnode_test3() */ + +static int +splat_vnode_test4(struct file *file, void *arg) +{ + vnode_t *vp; + char buf1[32] = "SPL VNode Interface Test File\n"; + char buf2[32] = ""; + int rc; + + if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE, + FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) { + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, + "Failed to vn_open test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW1, rc); + goto out; + } + + rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0, + UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); + if (rc < 0) { + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, + "Failed vn_rdwr write of test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW1, rc); + goto out2; + } + + VOP_CLOSE(vp, 0, 0, 0, 0, 0); + VN_RELE(vp); + + rc = vn_rename(SPLAT_VNODE_TEST_FILE_RW1,SPLAT_VNODE_TEST_FILE_RW2,0); + if (rc) { + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rename " + "%s -> %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW1, + SPLAT_VNODE_TEST_FILE_RW2, rc); + goto out; + } + + if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE, + FREAD | FEXCL, 0644, &vp, 0, 0))) { + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, + "Failed to vn_open test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW2, rc); + goto out; + } + + rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0, + UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); + if (rc < 0) { + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, + "Failed vn_rdwr read of test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW2, rc); + goto out2; + } + + if (strncmp(buf1, buf2, strlen(buf1))) { + rc = EINVAL; + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, + "Failed strncmp data written does not match " + "data read\nWrote: %sRead: %s\n", buf1, buf2); + goto out2; + } + + rc = 0; + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Wrote to %s: %s", + SPLAT_VNODE_TEST_FILE_RW1, buf1); + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Read from %s: %s", + SPLAT_VNODE_TEST_FILE_RW2, buf2); + splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Successfully renamed " + "test file %s -> %s and verified data pattern\n", + SPLAT_VNODE_TEST_FILE_RW1, SPLAT_VNODE_TEST_FILE_RW2); +out2: + VOP_CLOSE(vp, 0, 0, 0, 0, 0); + VN_RELE(vp); +out: + vn_remove(SPLAT_VNODE_TEST_FILE_RW1, 0, 0); + vn_remove(SPLAT_VNODE_TEST_FILE_RW2, 0, 0); + + return rc; +} /* splat_vnode_test4() */ + +static int +splat_vnode_test5(struct file *file, void *arg) +{ + vnode_t *vp; + vattr_t vap; + int rc; + + if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, + FREAD, 0644, &vp, 0, 0))) { + splat_vprint(file, SPLAT_VNODE_TEST5_NAME, + "Failed to vn_open test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE, rc); + return rc; + } + + rc = VOP_GETATTR(vp, &vap, 0, 0, NULL); + if (rc) { + splat_vprint(file, SPLAT_VNODE_TEST5_NAME, + "Failed to vn_getattr test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE, rc); + goto out; + } + + if (vap.va_type != VREG) { + rc = -EINVAL; + splat_vprint(file, SPLAT_VNODE_TEST5_NAME, + "Failed expected regular file type " + "(%d != VREG): %s (%d)\n", vap.va_type, + SPLAT_VNODE_TEST_FILE, rc); + goto out; + } + + splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully " + "vn_getattr'ed test file: %s\n", SPLAT_VNODE_TEST_FILE); + +out: + VOP_CLOSE(vp, 0, 0, 0, 0, 0); + VN_RELE(vp); + + return rc; +} /* splat_vnode_test5() */ + +static int +splat_vnode_test6(struct file *file, void *arg) +{ + vnode_t *vp; + char buf[32] = "SPL VNode Interface Test File\n"; + int rc; + + if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, + FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) { + splat_vprint(file, SPLAT_VNODE_TEST6_NAME, + "Failed to vn_open test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW, rc); + return rc; + } + + rc = vn_rdwr(UIO_WRITE, vp, buf, strlen(buf), 0, + UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); + if (rc < 0) { + splat_vprint(file, SPLAT_VNODE_TEST6_NAME, + "Failed vn_rdwr write of test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW, rc); + goto out; + } + + rc = vn_fsync(vp, 0, 0, 0); + if (rc) { + splat_vprint(file, SPLAT_VNODE_TEST6_NAME, + "Failed vn_fsync of test file: %s (%d)\n", + SPLAT_VNODE_TEST_FILE_RW, rc); + goto out; + } + + rc = 0; + splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Successfully " + "fsync'ed test file %s\n", SPLAT_VNODE_TEST_FILE_RW); +out: + VOP_CLOSE(vp, 0, 0, 0, 0, 0); + VN_RELE(vp); + vn_remove(SPLAT_VNODE_TEST_FILE_RW, 0, 0); + + return rc; +} /* splat_vnode_test4() */ + +splat_subsystem_t * +splat_vnode_init(void) +{ + splat_subsystem_t *sub; + + sub = kmalloc(sizeof(*sub), GFP_KERNEL); + if (sub == NULL) + return NULL; + + memset(sub, 0, sizeof(*sub)); + strncpy(sub->desc.name, SPLAT_VNODE_NAME, SPLAT_NAME_SIZE); + strncpy(sub->desc.desc, SPLAT_VNODE_DESC, SPLAT_DESC_SIZE); + INIT_LIST_HEAD(&sub->subsystem_list); + INIT_LIST_HEAD(&sub->test_list); + spin_lock_init(&sub->test_lock); + sub->desc.id = SPLAT_SUBSYSTEM_VNODE; + + SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST1_NAME, SPLAT_VNODE_TEST1_DESC, + SPLAT_VNODE_TEST1_ID, splat_vnode_test1); + SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST2_NAME, SPLAT_VNODE_TEST2_DESC, + SPLAT_VNODE_TEST2_ID, splat_vnode_test2); + SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST3_NAME, SPLAT_VNODE_TEST3_DESC, + SPLAT_VNODE_TEST3_ID, splat_vnode_test3); + SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST4_NAME, SPLAT_VNODE_TEST4_DESC, + SPLAT_VNODE_TEST4_ID, splat_vnode_test4); + SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST5_NAME, SPLAT_VNODE_TEST5_DESC, + SPLAT_VNODE_TEST5_ID, splat_vnode_test5); + SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST6_NAME, SPLAT_VNODE_TEST6_DESC, + SPLAT_VNODE_TEST6_ID, splat_vnode_test6); + + return sub; +} /* splat_vnode_init() */ + +void +splat_vnode_fini(splat_subsystem_t *sub) +{ + ASSERT(sub); + + SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST6_ID); + SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST5_ID); + SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST4_ID); + SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST3_ID); + SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST2_ID); + SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST1_ID); + + kfree(sub); +} /* splat_vnode_fini() */ + +int +splat_vnode_id(void) +{ + return SPLAT_SUBSYSTEM_VNODE; +} /* splat_vnode_id() */ diff --git a/scripts/check.sh b/scripts/check.sh index 5d7123684..3b8b75801 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -19,6 +19,12 @@ if [ -n "$V" ]; then verbose="-v" fi +if [ -n "$TESTS" ]; then + tests="$TESTS" +else + tests="-a" +fi + if [ $(id -u) != 0 ]; then die "Must run as root" fi @@ -37,8 +43,8 @@ echo "Loading ${spl_module}" echo "Loading ${splat_module}" /sbin/insmod ${splat_module} || die "Unable to load ${splat_module}" -sleep 3 -$splat_cmd -a $verbose +while [ ! -c /dev/splatctl ]; do sleep 0.1; done +$splat_cmd $tests $verbose echo "Unloading ${splat_module}" /sbin/rmmod ${splat_module} || die "Failed to unload ${splat_module}" -- 2.40.0