SUNRPC: Remove the global temporary write buffer in net/sunrpc/cache.c

While we do want to protect against multiple concurrent readers and writers
on each upcall/downcall pipe, we don't want to limit concurrent reading and
writing to separate caches.

This patch therefore replaces the static buffer 'write_buf', which can only
be used by one writer at a time, with use of the page cache as the
temporary buffer for downcalls. We still fall back to using the the old
global buffer if the downcall is larger than PAGE_CACHE_SIZE, since this is
apparently needed by the SPKM security context initialisation.

It then replaces the use of the global 'queue_io_mutex' with the
inode->i_mutex in cache_read() and cache_write().

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Trond Myklebust 2009-08-09 15:14:28 -04:00
parent 5b7a1b9f92
commit da77005f0d

View File

@ -27,6 +27,7 @@
#include <linux/net.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <asm/ioctls.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/cache.h>
@ -702,13 +703,14 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
struct cache_reader *rp = filp->private_data;
struct cache_request *rq;
struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
struct inode *inode = filp->f_path.dentry->d_inode;
struct cache_detail *cd = PDE(inode)->data;
int err;
if (count == 0)
return 0;
mutex_lock(&queue_io_mutex); /* protect against multiple concurrent
mutex_lock(&inode->i_mutex); /* protect against multiple concurrent
* readers on this file */
again:
spin_lock(&queue_lock);
@ -721,7 +723,7 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
}
if (rp->q.list.next == &cd->queue) {
spin_unlock(&queue_lock);
mutex_unlock(&queue_io_mutex);
mutex_unlock(&inode->i_mutex);
BUG_ON(rp->offset);
return 0;
}
@ -768,38 +770,81 @@ cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
}
if (err == -EAGAIN)
goto again;
mutex_unlock(&queue_io_mutex);
mutex_unlock(&inode->i_mutex);
return err ? err : count;
}
static char write_buf[8192]; /* protected by queue_io_mutex */
static ssize_t cache_do_downcall(char *kaddr, const char __user *buf,
size_t count, struct cache_detail *cd)
{
ssize_t ret;
if (copy_from_user(kaddr, buf, count))
return -EFAULT;
kaddr[count] = '\0';
ret = cd->cache_parse(cd, kaddr, count);
if (!ret)
ret = count;
return ret;
}
static ssize_t cache_slow_downcall(const char __user *buf,
size_t count, struct cache_detail *cd)
{
static char write_buf[8192]; /* protected by queue_io_mutex */
ssize_t ret = -EINVAL;
if (count >= sizeof(write_buf))
goto out;
mutex_lock(&queue_io_mutex);
ret = cache_do_downcall(write_buf, buf, count, cd);
mutex_unlock(&queue_io_mutex);
out:
return ret;
}
static ssize_t cache_downcall(struct address_space *mapping,
const char __user *buf,
size_t count, struct cache_detail *cd)
{
struct page *page;
char *kaddr;
ssize_t ret = -ENOMEM;
if (count >= PAGE_CACHE_SIZE)
goto out_slow;
page = find_or_create_page(mapping, 0, GFP_KERNEL);
if (!page)
goto out_slow;
kaddr = kmap(page);
ret = cache_do_downcall(kaddr, buf, count, cd);
kunmap(page);
unlock_page(page);
page_cache_release(page);
return ret;
out_slow:
return cache_slow_downcall(buf, count, cd);
}
static ssize_t
cache_write(struct file *filp, const char __user *buf, size_t count,
loff_t *ppos)
{
int err;
struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
struct address_space *mapping = filp->f_mapping;
struct inode *inode = filp->f_path.dentry->d_inode;
struct cache_detail *cd = PDE(inode)->data;
ssize_t ret = -EINVAL;
if (count == 0)
return 0;
if (count >= sizeof(write_buf))
return -EINVAL;
if (!cd->cache_parse)
goto out;
mutex_lock(&queue_io_mutex);
if (copy_from_user(write_buf, buf, count)) {
mutex_unlock(&queue_io_mutex);
return -EFAULT;
}
write_buf[count] = '\0';
if (cd->cache_parse)
err = cd->cache_parse(cd, write_buf, count);
else
err = -EINVAL;
mutex_unlock(&queue_io_mutex);
return err ? err : count;
mutex_lock(&inode->i_mutex);
ret = cache_downcall(mapping, buf, count, cd);
mutex_unlock(&inode->i_mutex);
out:
return ret;
}
static DECLARE_WAIT_QUEUE_HEAD(queue_wait);