- 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
This commit is contained in:
behlendo
2008-03-12 20:52:46 +00:00
parent 9490c14835
commit 4b17158506
13 changed files with 830 additions and 264 deletions
+15 -36
View File
@@ -1,21 +1,18 @@
#include <sys/kobj.h>
#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);
+321 -131
View File
@@ -1,168 +1,358 @@
#include <sys/sysmacros.h>
#include <sys/vnode.h>
#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.
*/
void *rootdir = NULL;
EXPORT_SYMBOL(rootdir);
/*ARGSUSED*/
int
vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
static vtype_t
vn_get_sol_type(umode_t mode)
{
int fd;
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(const char *path, int seg, int flags, int mode,
vnode_t **vpp, int x1, void *x2)
{
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);
}
BUG_ON(seg != UIO_SYSSPACE);
BUG_ON(!vpp);
*vpp = NULL;
#ifdef __linux__
if (!(flags & FCREAT) && S_ISBLK(st.st_mode)) {
flags |= O_DIRECT;
if (flags & FWRITE)
flags |= O_EXCL;
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(&current->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(&current->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);
vp = kmalloc(sizeof(vnode_t), GFP_ATOMIC);
if (!vp) {
filp_close(fp, 0);
return -ENOMEM;
}
*vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL);
vp->v_type = vn_get_sol_type(stat.mode);
vp->v_fp = fp;
*vpp = vp;
vp->v_fd = fd;
vp->v_size = st.st_size;
vp->v_mode = st.st_mode;
vp->v_path = spa_strdup(path);
return 0;
} /* vn_open() */
EXPORT_SYMBOL(vn_open);
return (0);
}
/*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;
if (uio == UIO_READ) {
iolen = pread64(vp->v_fd, addr, len, offset);
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 (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);
void
vn_close(vnode_t *vp)
int
vn_close(vnode_t *vp, int flags, int x1, int x2, int x3, int x4)
{
close(vp->v_fd);
spa_strfree(vp->v_path);
umem_free(vp, sizeof (vnode_t));
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;
}
#endif
EXPORT_SYMBOL(vn_rename);
int
vn_getattr(vnode_t *vp, vattr_t *vap, int flags, int x3, void *x4)
{
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;
}
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);