|
@@ -27,10 +27,12 @@
|
|
|
#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>
|
|
|
#include <linux/sunrpc/stats.h>
|
|
|
+#include <linux/sunrpc/rpc_pipe_fs.h>
|
|
|
|
|
|
#define RPCDBG_FACILITY RPCDBG_CACHE
|
|
|
|
|
@@ -175,7 +177,13 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(sunrpc_cache_update);
|
|
|
|
|
|
-static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
|
|
|
+static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h)
|
|
|
+{
|
|
|
+ if (!cd->cache_upcall)
|
|
|
+ return -EINVAL;
|
|
|
+ return cd->cache_upcall(cd, h);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This is the generic cache management routine for all
|
|
|
* the authentication caches.
|
|
@@ -284,76 +292,11 @@ static DEFINE_SPINLOCK(cache_list_lock);
|
|
|
static struct cache_detail *current_detail;
|
|
|
static int current_index;
|
|
|
|
|
|
-static const struct file_operations cache_file_operations;
|
|
|
-static const struct file_operations content_file_operations;
|
|
|
-static const struct file_operations cache_flush_operations;
|
|
|
-
|
|
|
static void do_cache_clean(struct work_struct *work);
|
|
|
static DECLARE_DELAYED_WORK(cache_cleaner, do_cache_clean);
|
|
|
|
|
|
-static void remove_cache_proc_entries(struct cache_detail *cd)
|
|
|
-{
|
|
|
- if (cd->proc_ent == NULL)
|
|
|
- return;
|
|
|
- if (cd->flush_ent)
|
|
|
- remove_proc_entry("flush", cd->proc_ent);
|
|
|
- if (cd->channel_ent)
|
|
|
- remove_proc_entry("channel", cd->proc_ent);
|
|
|
- if (cd->content_ent)
|
|
|
- remove_proc_entry("content", cd->proc_ent);
|
|
|
- cd->proc_ent = NULL;
|
|
|
- remove_proc_entry(cd->name, proc_net_rpc);
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef CONFIG_PROC_FS
|
|
|
-static int create_cache_proc_entries(struct cache_detail *cd)
|
|
|
-{
|
|
|
- struct proc_dir_entry *p;
|
|
|
-
|
|
|
- cd->proc_ent = proc_mkdir(cd->name, proc_net_rpc);
|
|
|
- if (cd->proc_ent == NULL)
|
|
|
- goto out_nomem;
|
|
|
- cd->channel_ent = cd->content_ent = NULL;
|
|
|
-
|
|
|
- p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR,
|
|
|
- cd->proc_ent, &cache_flush_operations, cd);
|
|
|
- cd->flush_ent = p;
|
|
|
- if (p == NULL)
|
|
|
- goto out_nomem;
|
|
|
-
|
|
|
- if (cd->cache_request || cd->cache_parse) {
|
|
|
- p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR,
|
|
|
- cd->proc_ent, &cache_file_operations, cd);
|
|
|
- cd->channel_ent = p;
|
|
|
- if (p == NULL)
|
|
|
- goto out_nomem;
|
|
|
- }
|
|
|
- if (cd->cache_show) {
|
|
|
- p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR,
|
|
|
- cd->proc_ent, &content_file_operations, cd);
|
|
|
- cd->content_ent = p;
|
|
|
- if (p == NULL)
|
|
|
- goto out_nomem;
|
|
|
- }
|
|
|
- return 0;
|
|
|
-out_nomem:
|
|
|
- remove_cache_proc_entries(cd);
|
|
|
- return -ENOMEM;
|
|
|
-}
|
|
|
-#else /* CONFIG_PROC_FS */
|
|
|
-static int create_cache_proc_entries(struct cache_detail *cd)
|
|
|
-{
|
|
|
- return 0;
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-int cache_register(struct cache_detail *cd)
|
|
|
+static void sunrpc_init_cache_detail(struct cache_detail *cd)
|
|
|
{
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = create_cache_proc_entries(cd);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
rwlock_init(&cd->hash_lock);
|
|
|
INIT_LIST_HEAD(&cd->queue);
|
|
|
spin_lock(&cache_list_lock);
|
|
@@ -367,11 +310,9 @@ int cache_register(struct cache_detail *cd)
|
|
|
|
|
|
/* start the cleaning process */
|
|
|
schedule_delayed_work(&cache_cleaner, 0);
|
|
|
- return 0;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(cache_register);
|
|
|
|
|
|
-void cache_unregister(struct cache_detail *cd)
|
|
|
+static void sunrpc_destroy_cache_detail(struct cache_detail *cd)
|
|
|
{
|
|
|
cache_purge(cd);
|
|
|
spin_lock(&cache_list_lock);
|
|
@@ -386,7 +327,6 @@ void cache_unregister(struct cache_detail *cd)
|
|
|
list_del_init(&cd->others);
|
|
|
write_unlock(&cd->hash_lock);
|
|
|
spin_unlock(&cache_list_lock);
|
|
|
- remove_cache_proc_entries(cd);
|
|
|
if (list_empty(&cache_list)) {
|
|
|
/* module must be being unloaded so its safe to kill the worker */
|
|
|
cancel_delayed_work_sync(&cache_cleaner);
|
|
@@ -395,7 +335,6 @@ void cache_unregister(struct cache_detail *cd)
|
|
|
out:
|
|
|
printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name);
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(cache_unregister);
|
|
|
|
|
|
/* clean cache tries to find something to clean
|
|
|
* and cleans it.
|
|
@@ -687,18 +626,18 @@ struct cache_reader {
|
|
|
int offset; /* if non-0, we have a refcnt on next request */
|
|
|
};
|
|
|
|
|
|
-static ssize_t
|
|
|
-cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
|
|
|
+static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
|
|
|
+ loff_t *ppos, struct cache_detail *cd)
|
|
|
{
|
|
|
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;
|
|
|
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);
|
|
@@ -711,7 +650,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;
|
|
|
}
|
|
@@ -758,49 +697,90 @@ 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;
|
|
|
|
|
|
-static ssize_t
|
|
|
-cache_write(struct file *filp, const char __user *buf, size_t count,
|
|
|
- loff_t *ppos)
|
|
|
+ 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)
|
|
|
{
|
|
|
- int err;
|
|
|
- struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
|
|
|
+ static char write_buf[8192]; /* protected by queue_io_mutex */
|
|
|
+ ssize_t ret = -EINVAL;
|
|
|
|
|
|
- if (count == 0)
|
|
|
- return 0;
|
|
|
if (count >= sizeof(write_buf))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
+ goto out;
|
|
|
mutex_lock(&queue_io_mutex);
|
|
|
+ ret = cache_do_downcall(write_buf, buf, count, cd);
|
|
|
+ mutex_unlock(&queue_io_mutex);
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
|
|
|
- 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;
|
|
|
+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);
|
|
|
+}
|
|
|
|
|
|
- mutex_unlock(&queue_io_mutex);
|
|
|
- return err ? err : count;
|
|
|
+static ssize_t cache_write(struct file *filp, const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos,
|
|
|
+ struct cache_detail *cd)
|
|
|
+{
|
|
|
+ struct address_space *mapping = filp->f_mapping;
|
|
|
+ struct inode *inode = filp->f_path.dentry->d_inode;
|
|
|
+ ssize_t ret = -EINVAL;
|
|
|
+
|
|
|
+ if (!cd->cache_parse)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ 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);
|
|
|
|
|
|
-static unsigned int
|
|
|
-cache_poll(struct file *filp, poll_table *wait)
|
|
|
+static unsigned int cache_poll(struct file *filp, poll_table *wait,
|
|
|
+ struct cache_detail *cd)
|
|
|
{
|
|
|
unsigned int mask;
|
|
|
struct cache_reader *rp = filp->private_data;
|
|
|
struct cache_queue *cq;
|
|
|
- struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
|
|
|
|
|
|
poll_wait(filp, &queue_wait, wait);
|
|
|
|
|
@@ -822,14 +802,13 @@ cache_poll(struct file *filp, poll_table *wait)
|
|
|
return mask;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-cache_ioctl(struct inode *ino, struct file *filp,
|
|
|
- unsigned int cmd, unsigned long arg)
|
|
|
+static int cache_ioctl(struct inode *ino, struct file *filp,
|
|
|
+ unsigned int cmd, unsigned long arg,
|
|
|
+ struct cache_detail *cd)
|
|
|
{
|
|
|
int len = 0;
|
|
|
struct cache_reader *rp = filp->private_data;
|
|
|
struct cache_queue *cq;
|
|
|
- struct cache_detail *cd = PDE(ino)->data;
|
|
|
|
|
|
if (cmd != FIONREAD || !rp)
|
|
|
return -EINVAL;
|
|
@@ -852,15 +831,15 @@ cache_ioctl(struct inode *ino, struct file *filp,
|
|
|
return put_user(len, (int __user *)arg);
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-cache_open(struct inode *inode, struct file *filp)
|
|
|
+static int cache_open(struct inode *inode, struct file *filp,
|
|
|
+ struct cache_detail *cd)
|
|
|
{
|
|
|
struct cache_reader *rp = NULL;
|
|
|
|
|
|
+ if (!cd || !try_module_get(cd->owner))
|
|
|
+ return -EACCES;
|
|
|
nonseekable_open(inode, filp);
|
|
|
if (filp->f_mode & FMODE_READ) {
|
|
|
- struct cache_detail *cd = PDE(inode)->data;
|
|
|
-
|
|
|
rp = kmalloc(sizeof(*rp), GFP_KERNEL);
|
|
|
if (!rp)
|
|
|
return -ENOMEM;
|
|
@@ -875,11 +854,10 @@ cache_open(struct inode *inode, struct file *filp)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-cache_release(struct inode *inode, struct file *filp)
|
|
|
+static int cache_release(struct inode *inode, struct file *filp,
|
|
|
+ struct cache_detail *cd)
|
|
|
{
|
|
|
struct cache_reader *rp = filp->private_data;
|
|
|
- struct cache_detail *cd = PDE(inode)->data;
|
|
|
|
|
|
if (rp) {
|
|
|
spin_lock(&queue_lock);
|
|
@@ -903,23 +881,12 @@ cache_release(struct inode *inode, struct file *filp)
|
|
|
cd->last_close = get_seconds();
|
|
|
atomic_dec(&cd->readers);
|
|
|
}
|
|
|
+ module_put(cd->owner);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-static const struct file_operations cache_file_operations = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .llseek = no_llseek,
|
|
|
- .read = cache_read,
|
|
|
- .write = cache_write,
|
|
|
- .poll = cache_poll,
|
|
|
- .ioctl = cache_ioctl, /* for FIONREAD */
|
|
|
- .open = cache_open,
|
|
|
- .release = cache_release,
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
static void queue_loose(struct cache_detail *detail, struct cache_head *ch)
|
|
|
{
|
|
|
struct cache_queue *cq;
|
|
@@ -1020,15 +987,21 @@ static void warn_no_listener(struct cache_detail *detail)
|
|
|
if (detail->last_warn != detail->last_close) {
|
|
|
detail->last_warn = detail->last_close;
|
|
|
if (detail->warn_no_listener)
|
|
|
- detail->warn_no_listener(detail);
|
|
|
+ detail->warn_no_listener(detail, detail->last_close != 0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * register an upcall request to user-space.
|
|
|
+ * register an upcall request to user-space and queue it up for read() by the
|
|
|
+ * upcall daemon.
|
|
|
+ *
|
|
|
* Each request is at most one page long.
|
|
|
*/
|
|
|
-static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
|
|
|
+int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
|
|
|
+ void (*cache_request)(struct cache_detail *,
|
|
|
+ struct cache_head *,
|
|
|
+ char **,
|
|
|
+ int *))
|
|
|
{
|
|
|
|
|
|
char *buf;
|
|
@@ -1036,9 +1009,6 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
|
|
|
char *bp;
|
|
|
int len;
|
|
|
|
|
|
- if (detail->cache_request == NULL)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
if (atomic_read(&detail->readers) == 0 &&
|
|
|
detail->last_close < get_seconds() - 30) {
|
|
|
warn_no_listener(detail);
|
|
@@ -1057,7 +1027,7 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
|
|
|
|
|
|
bp = buf; len = PAGE_SIZE;
|
|
|
|
|
|
- detail->cache_request(detail, h, &bp, &len);
|
|
|
+ cache_request(detail, h, &bp, &len);
|
|
|
|
|
|
if (len < 0) {
|
|
|
kfree(buf);
|
|
@@ -1075,6 +1045,7 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
|
|
|
wake_up(&queue_wait);
|
|
|
return 0;
|
|
|
}
|
|
|
+EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
|
|
|
|
|
|
/*
|
|
|
* parse a message from user-space and pass it
|
|
@@ -1242,11 +1213,13 @@ static const struct seq_operations cache_content_op = {
|
|
|
.show = c_show,
|
|
|
};
|
|
|
|
|
|
-static int content_open(struct inode *inode, struct file *file)
|
|
|
+static int content_open(struct inode *inode, struct file *file,
|
|
|
+ struct cache_detail *cd)
|
|
|
{
|
|
|
struct handle *han;
|
|
|
- struct cache_detail *cd = PDE(inode)->data;
|
|
|
|
|
|
+ if (!cd || !try_module_get(cd->owner))
|
|
|
+ return -EACCES;
|
|
|
han = __seq_open_private(file, &cache_content_op, sizeof(*han));
|
|
|
if (han == NULL)
|
|
|
return -ENOMEM;
|
|
@@ -1255,17 +1228,33 @@ static int content_open(struct inode *inode, struct file *file)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static const struct file_operations content_file_operations = {
|
|
|
- .open = content_open,
|
|
|
- .read = seq_read,
|
|
|
- .llseek = seq_lseek,
|
|
|
- .release = seq_release_private,
|
|
|
-};
|
|
|
+static int content_release(struct inode *inode, struct file *file,
|
|
|
+ struct cache_detail *cd)
|
|
|
+{
|
|
|
+ int ret = seq_release_private(inode, file);
|
|
|
+ module_put(cd->owner);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int open_flush(struct inode *inode, struct file *file,
|
|
|
+ struct cache_detail *cd)
|
|
|
+{
|
|
|
+ if (!cd || !try_module_get(cd->owner))
|
|
|
+ return -EACCES;
|
|
|
+ return nonseekable_open(inode, file);
|
|
|
+}
|
|
|
+
|
|
|
+static int release_flush(struct inode *inode, struct file *file,
|
|
|
+ struct cache_detail *cd)
|
|
|
+{
|
|
|
+ module_put(cd->owner);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
static ssize_t read_flush(struct file *file, char __user *buf,
|
|
|
- size_t count, loff_t *ppos)
|
|
|
+ size_t count, loff_t *ppos,
|
|
|
+ struct cache_detail *cd)
|
|
|
{
|
|
|
- struct cache_detail *cd = PDE(file->f_path.dentry->d_inode)->data;
|
|
|
char tbuf[20];
|
|
|
unsigned long p = *ppos;
|
|
|
size_t len;
|
|
@@ -1283,10 +1272,10 @@ static ssize_t read_flush(struct file *file, char __user *buf,
|
|
|
return len;
|
|
|
}
|
|
|
|
|
|
-static ssize_t write_flush(struct file * file, const char __user * buf,
|
|
|
- size_t count, loff_t *ppos)
|
|
|
+static ssize_t write_flush(struct file *file, const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos,
|
|
|
+ struct cache_detail *cd)
|
|
|
{
|
|
|
- struct cache_detail *cd = PDE(file->f_path.dentry->d_inode)->data;
|
|
|
char tbuf[20];
|
|
|
char *ep;
|
|
|
long flushtime;
|
|
@@ -1307,8 +1296,343 @@ static ssize_t write_flush(struct file * file, const char __user * buf,
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
-static const struct file_operations cache_flush_operations = {
|
|
|
- .open = nonseekable_open,
|
|
|
- .read = read_flush,
|
|
|
- .write = write_flush,
|
|
|
+static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
|
|
|
+
|
|
|
+ return cache_read(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t cache_write_procfs(struct file *filp, const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
|
|
|
+
|
|
|
+ return cache_write(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned int cache_poll_procfs(struct file *filp, poll_table *wait)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
|
|
|
+
|
|
|
+ return cache_poll(filp, wait, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int cache_ioctl_procfs(struct inode *inode, struct file *filp,
|
|
|
+ unsigned int cmd, unsigned long arg)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(inode)->data;
|
|
|
+
|
|
|
+ return cache_ioctl(inode, filp, cmd, arg, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int cache_open_procfs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(inode)->data;
|
|
|
+
|
|
|
+ return cache_open(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int cache_release_procfs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(inode)->data;
|
|
|
+
|
|
|
+ return cache_release(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations cache_file_operations_procfs = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .llseek = no_llseek,
|
|
|
+ .read = cache_read_procfs,
|
|
|
+ .write = cache_write_procfs,
|
|
|
+ .poll = cache_poll_procfs,
|
|
|
+ .ioctl = cache_ioctl_procfs, /* for FIONREAD */
|
|
|
+ .open = cache_open_procfs,
|
|
|
+ .release = cache_release_procfs,
|
|
|
};
|
|
|
+
|
|
|
+static int content_open_procfs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(inode)->data;
|
|
|
+
|
|
|
+ return content_open(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int content_release_procfs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(inode)->data;
|
|
|
+
|
|
|
+ return content_release(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations content_file_operations_procfs = {
|
|
|
+ .open = content_open_procfs,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .release = content_release_procfs,
|
|
|
+};
|
|
|
+
|
|
|
+static int open_flush_procfs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(inode)->data;
|
|
|
+
|
|
|
+ return open_flush(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int release_flush_procfs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(inode)->data;
|
|
|
+
|
|
|
+ return release_flush(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t read_flush_procfs(struct file *filp, char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
|
|
|
+
|
|
|
+ return read_flush(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t write_flush_procfs(struct file *filp,
|
|
|
+ const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = PDE(filp->f_path.dentry->d_inode)->data;
|
|
|
+
|
|
|
+ return write_flush(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations cache_flush_operations_procfs = {
|
|
|
+ .open = open_flush_procfs,
|
|
|
+ .read = read_flush_procfs,
|
|
|
+ .write = write_flush_procfs,
|
|
|
+ .release = release_flush_procfs,
|
|
|
+};
|
|
|
+
|
|
|
+static void remove_cache_proc_entries(struct cache_detail *cd)
|
|
|
+{
|
|
|
+ if (cd->u.procfs.proc_ent == NULL)
|
|
|
+ return;
|
|
|
+ if (cd->u.procfs.flush_ent)
|
|
|
+ remove_proc_entry("flush", cd->u.procfs.proc_ent);
|
|
|
+ if (cd->u.procfs.channel_ent)
|
|
|
+ remove_proc_entry("channel", cd->u.procfs.proc_ent);
|
|
|
+ if (cd->u.procfs.content_ent)
|
|
|
+ remove_proc_entry("content", cd->u.procfs.proc_ent);
|
|
|
+ cd->u.procfs.proc_ent = NULL;
|
|
|
+ remove_proc_entry(cd->name, proc_net_rpc);
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_PROC_FS
|
|
|
+static int create_cache_proc_entries(struct cache_detail *cd)
|
|
|
+{
|
|
|
+ struct proc_dir_entry *p;
|
|
|
+
|
|
|
+ cd->u.procfs.proc_ent = proc_mkdir(cd->name, proc_net_rpc);
|
|
|
+ if (cd->u.procfs.proc_ent == NULL)
|
|
|
+ goto out_nomem;
|
|
|
+ cd->u.procfs.channel_ent = NULL;
|
|
|
+ cd->u.procfs.content_ent = NULL;
|
|
|
+
|
|
|
+ p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR,
|
|
|
+ cd->u.procfs.proc_ent,
|
|
|
+ &cache_flush_operations_procfs, cd);
|
|
|
+ cd->u.procfs.flush_ent = p;
|
|
|
+ if (p == NULL)
|
|
|
+ goto out_nomem;
|
|
|
+
|
|
|
+ if (cd->cache_upcall || cd->cache_parse) {
|
|
|
+ p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR,
|
|
|
+ cd->u.procfs.proc_ent,
|
|
|
+ &cache_file_operations_procfs, cd);
|
|
|
+ cd->u.procfs.channel_ent = p;
|
|
|
+ if (p == NULL)
|
|
|
+ goto out_nomem;
|
|
|
+ }
|
|
|
+ if (cd->cache_show) {
|
|
|
+ p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR,
|
|
|
+ cd->u.procfs.proc_ent,
|
|
|
+ &content_file_operations_procfs, cd);
|
|
|
+ cd->u.procfs.content_ent = p;
|
|
|
+ if (p == NULL)
|
|
|
+ goto out_nomem;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+out_nomem:
|
|
|
+ remove_cache_proc_entries(cd);
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+#else /* CONFIG_PROC_FS */
|
|
|
+static int create_cache_proc_entries(struct cache_detail *cd)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+int cache_register(struct cache_detail *cd)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ sunrpc_init_cache_detail(cd);
|
|
|
+ ret = create_cache_proc_entries(cd);
|
|
|
+ if (ret)
|
|
|
+ sunrpc_destroy_cache_detail(cd);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cache_register);
|
|
|
+
|
|
|
+void cache_unregister(struct cache_detail *cd)
|
|
|
+{
|
|
|
+ remove_cache_proc_entries(cd);
|
|
|
+ sunrpc_destroy_cache_detail(cd);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(cache_unregister);
|
|
|
+
|
|
|
+static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
|
|
|
+
|
|
|
+ return cache_read(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t cache_write_pipefs(struct file *filp, const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
|
|
|
+
|
|
|
+ return cache_write(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned int cache_poll_pipefs(struct file *filp, poll_table *wait)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
|
|
|
+
|
|
|
+ return cache_poll(filp, wait, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int cache_ioctl_pipefs(struct inode *inode, struct file *filp,
|
|
|
+ unsigned int cmd, unsigned long arg)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(inode)->private;
|
|
|
+
|
|
|
+ return cache_ioctl(inode, filp, cmd, arg, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int cache_open_pipefs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(inode)->private;
|
|
|
+
|
|
|
+ return cache_open(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int cache_release_pipefs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(inode)->private;
|
|
|
+
|
|
|
+ return cache_release(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+const struct file_operations cache_file_operations_pipefs = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .llseek = no_llseek,
|
|
|
+ .read = cache_read_pipefs,
|
|
|
+ .write = cache_write_pipefs,
|
|
|
+ .poll = cache_poll_pipefs,
|
|
|
+ .ioctl = cache_ioctl_pipefs, /* for FIONREAD */
|
|
|
+ .open = cache_open_pipefs,
|
|
|
+ .release = cache_release_pipefs,
|
|
|
+};
|
|
|
+
|
|
|
+static int content_open_pipefs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(inode)->private;
|
|
|
+
|
|
|
+ return content_open(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int content_release_pipefs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(inode)->private;
|
|
|
+
|
|
|
+ return content_release(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+const struct file_operations content_file_operations_pipefs = {
|
|
|
+ .open = content_open_pipefs,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .release = content_release_pipefs,
|
|
|
+};
|
|
|
+
|
|
|
+static int open_flush_pipefs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(inode)->private;
|
|
|
+
|
|
|
+ return open_flush(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static int release_flush_pipefs(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(inode)->private;
|
|
|
+
|
|
|
+ return release_flush(inode, filp, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t read_flush_pipefs(struct file *filp, char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
|
|
|
+
|
|
|
+ return read_flush(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t write_flush_pipefs(struct file *filp,
|
|
|
+ const char __user *buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct cache_detail *cd = RPC_I(filp->f_path.dentry->d_inode)->private;
|
|
|
+
|
|
|
+ return write_flush(filp, buf, count, ppos, cd);
|
|
|
+}
|
|
|
+
|
|
|
+const struct file_operations cache_flush_operations_pipefs = {
|
|
|
+ .open = open_flush_pipefs,
|
|
|
+ .read = read_flush_pipefs,
|
|
|
+ .write = write_flush_pipefs,
|
|
|
+ .release = release_flush_pipefs,
|
|
|
+};
|
|
|
+
|
|
|
+int sunrpc_cache_register_pipefs(struct dentry *parent,
|
|
|
+ const char *name, mode_t umode,
|
|
|
+ struct cache_detail *cd)
|
|
|
+{
|
|
|
+ struct qstr q;
|
|
|
+ struct dentry *dir;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ sunrpc_init_cache_detail(cd);
|
|
|
+ q.name = name;
|
|
|
+ q.len = strlen(name);
|
|
|
+ q.hash = full_name_hash(q.name, q.len);
|
|
|
+ dir = rpc_create_cache_dir(parent, &q, umode, cd);
|
|
|
+ if (!IS_ERR(dir))
|
|
|
+ cd->u.pipefs.dir = dir;
|
|
|
+ else {
|
|
|
+ sunrpc_destroy_cache_detail(cd);
|
|
|
+ ret = PTR_ERR(dir);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs);
|
|
|
+
|
|
|
+void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
|
|
|
+{
|
|
|
+ rpc_remove_cache_dir(cd->u.pipefs.dir);
|
|
|
+ cd->u.pipefs.dir = NULL;
|
|
|
+ sunrpc_destroy_cache_detail(cd);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
|
|
|
+
|