diff --git a/.gitignore b/.gitignore index fe64a289..5a4bcb13 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ tests/getstats tests/getversion tests/lock tests/mmfs_basic +tests/mmfs_readwrite tests/openclose tests/setversion tests/updatedraw diff --git a/linux-core/mmfs_drv.c b/linux-core/mmfs_drv.c index f4b07117..d973592d 100644 --- a/linux-core/mmfs_drv.c +++ b/linux-core/mmfs_drv.c @@ -91,12 +91,12 @@ mmfs_handle_delete(struct mmfs_file *mmfs_filp, int handle) * we may want to use ida for number allocation and a hash table * for the pointers, anyway. */ - spin_lock(&mmfs_filp->delete_lock); + spin_lock(&mmfs_filp->table_lock); /* Check if we currently have a reference on the object */ obj = idr_find(&mmfs_filp->object_idr, handle); if (obj == NULL) { - spin_unlock(&mmfs_filp->delete_lock); + spin_unlock(&mmfs_filp->table_lock); return -EINVAL; } @@ -104,11 +104,34 @@ mmfs_handle_delete(struct mmfs_file *mmfs_filp, int handle) idr_remove(&mmfs_filp->object_idr, handle); mmfs_object_unreference(obj); - spin_unlock(&mmfs_filp->delete_lock); + spin_unlock(&mmfs_filp->table_lock); return 0; } +/** Returns a reference to the object named by the handle. */ +static struct mmfs_object * +mmfs_object_lookup(struct mmfs_file *mmfs_filp, int handle) +{ + struct mmfs_object *obj; + + spin_lock(&mmfs_filp->table_lock); + + /* Check if we currently have a reference on the object */ + obj = idr_find(&mmfs_filp->object_idr, handle); + if (obj == NULL) { + spin_unlock(&mmfs_filp->table_lock); + return NULL; + } + + mmfs_object_reference(obj); + + spin_unlock(&mmfs_filp->table_lock); + + return obj; +} + + /** * Allocates a new mmfs object and returns a handle to it. */ @@ -164,7 +187,7 @@ mmfs_alloc_ioctl(struct inode *inode, struct file *filp, } /** - * Allocates a new mmfs object and returns a handle to it. + * Releases the handle to an mmfs object. */ static int mmfs_unreference_ioctl(struct inode *inode, struct file *filp, @@ -182,6 +205,84 @@ mmfs_unreference_ioctl(struct inode *inode, struct file *filp, return ret; } +/** + * Reads data from the object referenced by handle. + * + * On error, the contents of *data are undefined. + */ +static int +mmfs_pread_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct mmfs_file *mmfs_filp = filp->private_data; + struct mmfs_pread_args args; + struct mmfs_object *obj; + ssize_t read; + loff_t offset; + + if (copy_from_user(&args, (void __user *)arg, sizeof(args))) + return -EFAULT; + + obj = mmfs_object_lookup(mmfs_filp, args.handle); + if (obj == NULL) + return -EINVAL; + + offset = args.offset; + + read = obj->filp->f_op->read(obj->filp, (char __user *)args.data, + args.size, &offset); + if (read != args.size) { + mmfs_object_unreference(obj); + if (read < 0) + return read; + else + return -EINVAL; + } + + mmfs_object_unreference(obj); + + return 0; +} + +/** + * Writes data to the object referenced by handle. + * + * On error, the contents of the buffer that were to be modified are undefined. + */ +static int +mmfs_pwrite_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct mmfs_file *mmfs_filp = filp->private_data; + struct mmfs_pwrite_args args; + struct mmfs_object *obj; + ssize_t written; + loff_t offset; + + if (copy_from_user(&args, (void __user *)arg, sizeof(args))) + return -EFAULT; + + obj = mmfs_object_lookup(mmfs_filp, args.handle); + if (obj == NULL) + return -EINVAL; + + offset = args.offset; + + written = obj->filp->f_op->write(obj->filp, (char __user *)args.data, + args.size, &offset); + if (written != args.size) { + mmfs_object_unreference(obj); + if (written < 0) + return written; + else + return -EINVAL; + } + + mmfs_object_unreference(obj); + + return 0; +} + static int mmfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) @@ -192,6 +293,10 @@ mmfs_ioctl(struct inode *inode, struct file *filp, return mmfs_alloc_ioctl(inode, filp, cmd, arg); case MMFS_IOCTL_UNREFERENCE: return mmfs_unreference_ioctl(inode, filp, cmd, arg); + case MMFS_IOCTL_PREAD: + return mmfs_pread_ioctl(inode, filp, cmd, arg); + case MMFS_IOCTL_PWRITE: + return mmfs_pwrite_ioctl(inode, filp, cmd, arg); default: return -EINVAL; } diff --git a/linux-core/mmfs_drv.h b/linux-core/mmfs_drv.h index 1944d2af..53d2f6cb 100644 --- a/linux-core/mmfs_drv.h +++ b/linux-core/mmfs_drv.h @@ -54,11 +54,8 @@ struct mmfs_object { struct mmfs_file { /** Mapping of object handles to object pointers. */ struct idr object_idr; - /** - * Lock for synchronization of access to object->refcount and - * object_idr. See note in mmfs_unreference_ioctl. - */ - spinlock_t delete_lock; + /** Lock for synchronization of access to object_idr. */ + spinlock_t table_lock; }; void mmfs_object_reference(struct mmfs_object *obj); diff --git a/shared-core/mmfs.h b/shared-core/mmfs.h index bc7e991f..e0a4f25b 100644 --- a/shared-core/mmfs.h +++ b/shared-core/mmfs.h @@ -25,7 +25,12 @@ * */ -#include +#ifdef __linux__ +#include +#else +#include +#include +#endif /** @file mmfs.h * This file provides ioctl and ioctl argument definitions for using the diff --git a/tests/Makefile.am b/tests/Makefile.am index 97752774..e2931013 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,7 +23,8 @@ TESTS = auth \ lock \ setversion \ updatedraw \ - mmfs_basic + mmfs_basic \ + mmfs_readwrite EXTRA_PROGRAMS = $(TESTS) CLEANFILES = $(EXTRA_PROGRAMS) $(EXTRA_LTLIBRARIES) diff --git a/tests/drmtest.c b/tests/drmtest.c index cae99a0c..58f71a6a 100644 --- a/tests/drmtest.c +++ b/tests/drmtest.c @@ -26,7 +26,9 @@ */ #include +#include #include "drmtest.h" +#include "mmfs.h" /** Open the first DRM device we can find, searching up to 16 device nodes */ int drm_open_any(void) @@ -81,3 +83,34 @@ int drm_open_any_master(void) abort(); } +static void +create_mmfs_device() +{ + struct stat sb; + int ret; + + ret = stat(MMFS_DEVICE_PATH, &sb); + + if (ret == 0) + return; + + ret = mknod(MMFS_DEVICE_PATH, S_IFCHR | S_IRUSR | S_IWUSR, + makedev(MMFS_DEVICE_MAJOR, 0)); + + if (ret != 0) + errx(1, "mknod()"); +} + +int +open_mmfs_device() +{ + int fd; + + create_mmfs_device(); + + fd = open(MMFS_DEVICE_PATH, O_RDWR); + if (fd == -1) + errx(1, "open()"); + + return fd; +} diff --git a/tests/drmtest.h b/tests/drmtest.h index afa0df4a..b84ada71 100644 --- a/tests/drmtest.h +++ b/tests/drmtest.h @@ -35,3 +35,4 @@ int drm_open_any(void); int drm_open_any_master(void); +int open_mmfs_device(); diff --git a/tests/mmfs_basic.c b/tests/mmfs_basic.c index b0ae8905..c6975b0b 100644 --- a/tests/mmfs_basic.c +++ b/tests/mmfs_basic.c @@ -35,24 +35,6 @@ #include #include "mmfs.h" -static void -create_mmfs_device() -{ - struct stat sb; - int ret; - - ret = stat(MMFS_DEVICE_PATH, &sb); - - if (ret == 0) - return; - - ret = mknod(MMFS_DEVICE_PATH, S_IFCHR | S_IRUSR | S_IWUSR, - makedev(MMFS_DEVICE_MAJOR, 0)); - - if (ret != 0) - errx(1, "mknod()"); -} - static void test_bad_unref(int fd) { @@ -117,11 +99,7 @@ int main(int argc, char **argv) { int fd; - create_mmfs_device(); - - fd = open(MMFS_DEVICE_PATH, O_RDWR); - if (fd == -1) - errx(1, "open()"); + fd = open_mmfs_device(); test_bad_ioctl(fd); test_bad_unref(fd); diff --git a/tests/mmfs_readwrite.c b/tests/mmfs_readwrite.c new file mode 100644 index 00000000..09d1967d --- /dev/null +++ b/tests/mmfs_readwrite.c @@ -0,0 +1,125 @@ +/* + * Copyright © 2008 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eric Anholt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmfs.h" + +#define MMFS_BUFFER_SIZE 16384 + +int do_read(int fd, int handle, void *buf, int offset, int size) +{ + struct mmfs_pread_args read; + + /* Ensure that we don't have any convenient data in buf in case + * we fail. + */ + memset(buf, 0xd0, size); + + memset(&read, 0, sizeof(read)); + read.handle = handle; + read.data = buf; + read.size = size; + read.offset = offset; + + return ioctl(fd, MMFS_IOCTL_PREAD, &read); +} + +int do_write(int fd, int handle, void *buf, int offset, int size) +{ + struct mmfs_pwrite_args write; + + memset(&write, 0, sizeof(write)); + write.handle = handle; + write.data = buf; + write.size = size; + write.offset = offset; + + return ioctl(fd, MMFS_IOCTL_PWRITE, &write); +} + +int main(int argc, char **argv) +{ + int fd; + struct mmfs_alloc_args alloc; + uint8_t expected[MMFS_BUFFER_SIZE]; + uint8_t buf[MMFS_BUFFER_SIZE]; + int ret; + int handle; + + fd = open_mmfs_device(); + + memset(&alloc, 0, sizeof(alloc)); + alloc.size = MMFS_BUFFER_SIZE; + ret = ioctl(fd, MMFS_IOCTL_ALLOC, &alloc); + assert(ret == 0); + handle = alloc.handle; + + printf("Testing contents of newly allocated object.\n"); + ret = do_read(fd, handle, buf, 0, MMFS_BUFFER_SIZE); + assert(ret == 0); + memset(&expected, 0, sizeof(expected)); + assert(memcmp(expected, buf, sizeof(expected)) == 0); + + printf("Testing read beyond end of buffer.\n"); + ret = do_read(fd, handle, buf, MMFS_BUFFER_SIZE / 2, MMFS_BUFFER_SIZE); + assert(ret == -1 && errno == EINVAL); + + printf("Testing full write of buffer\n"); + memset(buf, 0, sizeof(buf)); + memset(buf + 1024, 0x01, 1024); + memset(expected + 1024, 0x01, 1024); + ret = do_write(fd, handle, buf, 0, MMFS_BUFFER_SIZE); + assert(ret == 0); + ret = do_read(fd, handle, buf, 0, MMFS_BUFFER_SIZE); + assert(ret == 0); + assert(memcmp(buf, expected, sizeof(buf)) == 0); + + printf("Testing partial write of buffer\n"); + memset(buf + 4096, 0x02, 1024); + memset(expected + 4096, 0x02, 1024); + ret = do_write(fd, handle, buf + 4096, 4096, 1024); + assert(ret == 0); + ret = do_read(fd, handle, buf, 0, MMFS_BUFFER_SIZE); + assert(ret == 0); + assert(memcmp(buf, expected, sizeof(buf)) == 0); + + printf("Testing partial read of buffer\n"); + ret = do_read(fd, handle, buf, 512, 1024); + assert(ret == 0); + assert(memcmp(buf, expected + 512, 1024) == 0); + + close(fd); + + return 0; +}