Add pread/pwrite ioctls to mmfs.

main
Eric Anholt 2008-04-23 13:06:58 -07:00
parent c1fec43b55
commit 8c741ed54e
9 changed files with 280 additions and 34 deletions

1
.gitignore vendored
View File

@ -59,6 +59,7 @@ tests/getstats
tests/getversion tests/getversion
tests/lock tests/lock
tests/mmfs_basic tests/mmfs_basic
tests/mmfs_readwrite
tests/openclose tests/openclose
tests/setversion tests/setversion
tests/updatedraw tests/updatedraw

View File

@ -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 * we may want to use ida for number allocation and a hash table
* for the pointers, anyway. * 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 */ /* Check if we currently have a reference on the object */
obj = idr_find(&mmfs_filp->object_idr, handle); obj = idr_find(&mmfs_filp->object_idr, handle);
if (obj == NULL) { if (obj == NULL) {
spin_unlock(&mmfs_filp->delete_lock); spin_unlock(&mmfs_filp->table_lock);
return -EINVAL; return -EINVAL;
} }
@ -104,11 +104,34 @@ mmfs_handle_delete(struct mmfs_file *mmfs_filp, int handle)
idr_remove(&mmfs_filp->object_idr, handle); idr_remove(&mmfs_filp->object_idr, handle);
mmfs_object_unreference(obj); mmfs_object_unreference(obj);
spin_unlock(&mmfs_filp->delete_lock); spin_unlock(&mmfs_filp->table_lock);
return 0; 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. * 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 static int
mmfs_unreference_ioctl(struct inode *inode, struct file *filp, mmfs_unreference_ioctl(struct inode *inode, struct file *filp,
@ -182,6 +205,84 @@ mmfs_unreference_ioctl(struct inode *inode, struct file *filp,
return ret; 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 static int
mmfs_ioctl(struct inode *inode, struct file *filp, mmfs_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg) 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); return mmfs_alloc_ioctl(inode, filp, cmd, arg);
case MMFS_IOCTL_UNREFERENCE: case MMFS_IOCTL_UNREFERENCE:
return mmfs_unreference_ioctl(inode, filp, cmd, arg); 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: default:
return -EINVAL; return -EINVAL;
} }

View File

@ -54,11 +54,8 @@ struct mmfs_object {
struct mmfs_file { struct mmfs_file {
/** Mapping of object handles to object pointers. */ /** Mapping of object handles to object pointers. */
struct idr object_idr; struct idr object_idr;
/** /** Lock for synchronization of access to object_idr. */
* Lock for synchronization of access to object->refcount and spinlock_t table_lock;
* object_idr. See note in mmfs_unreference_ioctl.
*/
spinlock_t delete_lock;
}; };
void mmfs_object_reference(struct mmfs_object *obj); void mmfs_object_reference(struct mmfs_object *obj);

View File

@ -25,7 +25,12 @@
* *
*/ */
#include <sys/ioctl.h> #ifdef __linux__
#include <asm/ioctl.h>
#else
#include <sys/types.h>
#include <sys/ioccom.h>
#endif
/** @file mmfs.h /** @file mmfs.h
* This file provides ioctl and ioctl argument definitions for using the * This file provides ioctl and ioctl argument definitions for using the

View File

@ -23,7 +23,8 @@ TESTS = auth \
lock \ lock \
setversion \ setversion \
updatedraw \ updatedraw \
mmfs_basic mmfs_basic \
mmfs_readwrite
EXTRA_PROGRAMS = $(TESTS) EXTRA_PROGRAMS = $(TESTS)
CLEANFILES = $(EXTRA_PROGRAMS) $(EXTRA_LTLIBRARIES) CLEANFILES = $(EXTRA_PROGRAMS) $(EXTRA_LTLIBRARIES)

View File

@ -26,7 +26,9 @@
*/ */
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h>
#include "drmtest.h" #include "drmtest.h"
#include "mmfs.h"
/** Open the first DRM device we can find, searching up to 16 device nodes */ /** Open the first DRM device we can find, searching up to 16 device nodes */
int drm_open_any(void) int drm_open_any(void)
@ -81,3 +83,34 @@ int drm_open_any_master(void)
abort(); 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;
}

View File

@ -35,3 +35,4 @@
int drm_open_any(void); int drm_open_any(void);
int drm_open_any_master(void); int drm_open_any_master(void);
int open_mmfs_device();

View File

@ -35,24 +35,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "mmfs.h" #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 static void
test_bad_unref(int fd) test_bad_unref(int fd)
{ {
@ -117,11 +99,7 @@ int main(int argc, char **argv)
{ {
int fd; int fd;
create_mmfs_device(); fd = open_mmfs_device();
fd = open(MMFS_DEVICE_PATH, O_RDWR);
if (fd == -1)
errx(1, "open()");
test_bad_ioctl(fd); test_bad_ioctl(fd);
test_bad_unref(fd); test_bad_unref(fd);

125
tests/mmfs_readwrite.c Normal file
View File

@ -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 <eric@anholt.net>
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/stat.h>
#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;
}