diff -r 43de9d7c3c63 drivers/xen/gntdev/gntdev.c --- a/drivers/xen/gntdev/gntdev.c Tue Feb 26 17:59:18 2008 +0000 +++ b/drivers/xen/gntdev/gntdev.c Tue Mar 18 14:03:45 2008 +0000 @@ -43,7 +43,8 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -#define MAX_GRANTS 128 +#define MAX_GRANTS_LIMIT 1024 +#define DEFAULT_MAX_GRANTS 128 /* A slot can be in one of three states: * @@ -90,7 +91,8 @@ typedef struct gntdev_file_private_data typedef struct gntdev_file_private_data { /* Array of grant information. */ - gntdev_grant_info_t grants[MAX_GRANTS]; + gntdev_grant_info_t *grants; + uint32_t grants_size; /* Read/write semaphore used to protect the grants array. */ struct rw_semaphore grants_sem; @@ -102,7 +104,7 @@ typedef struct gntdev_file_private_data * been compressed. However, this is not visible across invocations of * the device. */ - int32_t free_list[MAX_GRANTS]; + int32_t *free_list; /* The number of free slots in the grants array. */ uint32_t free_list_size; @@ -314,7 +316,7 @@ static int find_contiguous_free_range(st /* First search from the start_index to the end of the array. */ range_length = 0; - for (i = start_index; i < MAX_GRANTS; ++i) { + for (i = start_index; i < private_data->grants_size; ++i) { if (private_data->grants[i].state == GNTDEV_SLOT_INVALID) { if (range_length == 0) { range_start = i; @@ -343,6 +345,50 @@ static int find_contiguous_free_range(st return -ENOMEM; } +static int init_private_data(gntdev_file_private_data_t *priv, + uint32_t max_grants) +{ + int i; + + /* Allocate space for the kernel-mapping of granted pages. */ + priv->foreign_pages = + alloc_empty_pages_and_pagevec(max_grants); + if (!priv->foreign_pages) + goto nomem_out; + + /* Allocate the grant list and free-list. */ + priv->grants = kmalloc(max_grants * sizeof(gntdev_grant_info_t), + GFP_KERNEL); + if (!priv->grants) + goto nomem_out2; + priv->free_list = kmalloc(max_grants * sizeof(int32_t), GFP_KERNEL); + if (!priv->free_list) + goto nomem_out3; + + /* Initialise the free-list, which contains all slots at first. */ + for (i = 0; i < max_grants; ++i) { + priv->free_list[max_grants - i - 1] = i; + priv->grants[i].state = GNTDEV_SLOT_INVALID; + priv->grants[i].u.free_list_index = max_grants - i - 1; + } + priv->grants_size = max_grants; + priv->free_list_size = max_grants; + priv->next_fit_index = 0; + + up_write(&priv->grants_sem); + up_write(&priv->free_list_sem); + + return 0; + +nomem_out3: + kfree(priv->grants); +nomem_out2: + free_empty_pages_and_pagevec(priv->foreign_pages, max_grants); +nomem_out: + return -ENOMEM; + +} + /* Interface functions. */ /* Initialises the driver. Called when the module is loaded. */ @@ -400,7 +446,6 @@ static int gntdev_open(struct inode *ino static int gntdev_open(struct inode *inode, struct file *flip) { gntdev_file_private_data_t *private_data; - int i; try_module_get(THIS_MODULE); @@ -409,21 +454,10 @@ static int gntdev_open(struct inode *ino if (!private_data) goto nomem_out; - /* Allocate space for the kernel-mapping of granted pages. */ - private_data->foreign_pages = - alloc_empty_pages_and_pagevec(MAX_GRANTS); - if (!private_data->foreign_pages) - goto nomem_out2; - - /* Initialise the free-list, which contains all slots at first. - */ - for (i = 0; i < MAX_GRANTS; ++i) { - private_data->free_list[MAX_GRANTS - i - 1] = i; - private_data->grants[i].state = GNTDEV_SLOT_INVALID; - private_data->grants[i].u.free_list_index = MAX_GRANTS - i - 1; - } - private_data->free_list_size = MAX_GRANTS; - private_data->next_fit_index = 0; + /* These will be lazily initialised by init_private_data. */ + private_data->grants = NULL; + private_data->free_list = NULL; + private_data->foreign_pages = NULL; init_rwsem(&private_data->grants_sem); init_rwsem(&private_data->free_list_sem); @@ -432,8 +466,6 @@ static int gntdev_open(struct inode *ino return 0; -nomem_out2: - kfree(private_data); nomem_out: return -ENOMEM; } @@ -445,10 +477,14 @@ static int gntdev_release(struct inode * if (flip->private_data) { gntdev_file_private_data_t *private_data = (gntdev_file_private_data_t *) flip->private_data; - if (private_data->foreign_pages) { + if (private_data->foreign_pages) free_empty_pages_and_pagevec - (private_data->foreign_pages, MAX_GRANTS); - } + (private_data->foreign_pages, + private_data->grants_size); + if (private_data->grants) + kfree(private_data->grants); + if (private_data->free_list) + kfree(private_data->free_list); kfree(private_data); } module_put(THIS_MODULE); @@ -479,7 +515,17 @@ static int gntdev_mmap (struct file *fli return -EINVAL; } - if (unlikely((size <= 0) || (size + slot_index) > MAX_GRANTS)) { + /* Test to make sure that the grants array has been initialised. */ + down_read(&private_data->grants_sem); + if (unlikely(!private_data->grants)) { + up_read(&private_data->grants_sem); + printk(KERN_ERR "Attempted to mmap before ioctl.\n"); + return -EINVAL; + } + up_read(&private_data->grants_sem); + + if (unlikely((size <= 0) || + (size + slot_index) > private_data->grants_size)) { printk(KERN_ERR "Invalid number of pages or offset" "(num_pages = %d, first_slot = %ld).\n", size, slot_index); @@ -789,6 +835,33 @@ static long gntdev_ioctl(struct file *fl gntdev_file_private_data_t *private_data = (gntdev_file_private_data_t *) flip->private_data; + /* On the first invocation, we will lazily initialise the grant array + * and free-list. + */ + if (unlikely(!private_data->grants) + && likely(cmd != IOCTL_GNTDEV_SET_MAX_GRANTS)) { + down_write(&private_data->grants_sem); + + if (unlikely(private_data->grants)) { + up_write(&private_data->grants_sem); + goto private_data_initialised; + } + + /* Just use the default. Setting to a non-default is handled + * in the ioctl switch. + */ + rc = init_private_data(private_data, DEFAULT_MAX_GRANTS); + + up_write(&private_data->grants_sem); + + if (rc) { + printk (KERN_ERR "Initialising gntdev private data " + "failed.\n"); + return rc; + } + } + +private_data_initialised: switch (cmd) { case IOCTL_GNTDEV_MAP_GRANT_REF: { @@ -972,6 +1045,30 @@ static long gntdev_ioctl(struct file *fl get_offset_out: return rc; } + case IOCTL_GNTDEV_SET_MAX_GRANTS: + { + struct ioctl_gntdev_set_max_grants op; + if ((rc = copy_from_user(&op, + (void __user *) arg, + sizeof(op)))) { + rc = -EFAULT; + goto set_max_out; + } + down_write(&private_data->grants_sem); + if (private_data->grants) { + rc = -EBUSY; + goto set_max_unlock_out; + } + if (op.count > MAX_GRANTS_LIMIT) { + rc = -EINVAL; + goto set_max_unlock_out; + } + rc = init_private_data(private_data, op.count); + set_max_unlock_out: + up_write(&private_data->grants_sem); + set_max_out: + return rc; + } default: return -ENOIOCTLCMD; } diff -r 43de9d7c3c63 include/xen/public/gntdev.h --- a/include/xen/public/gntdev.h Tue Feb 26 17:59:18 2008 +0000 +++ b/include/xen/public/gntdev.h Tue Mar 18 14:03:45 2008 +0000 @@ -102,4 +102,18 @@ struct ioctl_gntdev_get_offset_for_vaddr uint32_t pad; }; +/* + * Sets the maximum number of grants that may mapped at once by this gntdev + * instance. + * + * N.B. This must be called before any other ioctl is performed on the device. + */ +#define IOCTL_GNTDEV_SET_MAX_GRANTS \ +_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_gntdev_set_max_grants)) +struct ioctl_gntdev_set_max_grants { + /* IN parameter */ + /* The maximum number of grants that may be mapped at once. */ + uint32_t count; +}; + #endif /* __LINUX_PUBLIC_GNTDEV_H__ */