Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / sunrpc / cache.c
index 3e823aa..7026b08 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/proc_fs.h>
 #include <linux/net.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 #include <asm/ioctls.h>
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/cache.h>
 
 #define         RPCDBG_FACILITY RPCDBG_CACHE
 
-void cache_init(struct cache_head *h)
+static void cache_defer_req(struct cache_req *req, struct cache_head *item);
+static void cache_revisit_request(struct cache_head *item);
+
+static void cache_init(struct cache_head *h)
 {
        time_t now = get_seconds();
        h->next = NULL;
        h->flags = 0;
-       atomic_set(&h->refcnt, 0);
+       kref_init(&h->ref);
        h->expiry_time = now + CACHE_NEW_EXPIRY;
        h->last_refresh = now;
 }
 
+struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
+                                      struct cache_head *key, int hash)
+{
+       struct cache_head **head,  **hp;
+       struct cache_head *new = NULL;
+
+       head = &detail->hash_table[hash];
+
+       read_lock(&detail->hash_lock);
+
+       for (hp=head; *hp != NULL ; hp = &(*hp)->next) {
+               struct cache_head *tmp = *hp;
+               if (detail->match(tmp, key)) {
+                       cache_get(tmp);
+                       read_unlock(&detail->hash_lock);
+                       return tmp;
+               }
+       }
+       read_unlock(&detail->hash_lock);
+       /* Didn't find anything, insert an empty entry */
+
+       new = detail->alloc();
+       if (!new)
+               return NULL;
+       cache_init(new);
+
+       write_lock(&detail->hash_lock);
+
+       /* check if entry appeared while we slept */
+       for (hp=head; *hp != NULL ; hp = &(*hp)->next) {
+               struct cache_head *tmp = *hp;
+               if (detail->match(tmp, key)) {
+                       cache_get(tmp);
+                       write_unlock(&detail->hash_lock);
+                       cache_put(new, detail);
+                       return tmp;
+               }
+       }
+       detail->init(new, key);
+       new->next = *head;
+       *head = new;
+       detail->entries++;
+       cache_get(new);
+       write_unlock(&detail->hash_lock);
+
+       return new;
+}
+EXPORT_SYMBOL(sunrpc_cache_lookup);
+
+
+static void queue_loose(struct cache_detail *detail, struct cache_head *ch);
+
+static int cache_fresh_locked(struct cache_head *head, time_t expiry)
+{
+       head->expiry_time = expiry;
+       head->last_refresh = get_seconds();
+       return !test_and_set_bit(CACHE_VALID, &head->flags);
+}
+
+static void cache_fresh_unlocked(struct cache_head *head,
+                       struct cache_detail *detail, int new)
+{
+       if (new)
+               cache_revisit_request(head);
+       if (test_and_clear_bit(CACHE_PENDING, &head->flags)) {
+               cache_revisit_request(head);
+               queue_loose(detail, head);
+       }
+}
+
+struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
+                                      struct cache_head *new, struct cache_head *old, int hash)
+{
+       /* The 'old' entry is to be replaced by 'new'.
+        * If 'old' is not VALID, we update it directly,
+        * otherwise we need to replace it
+        */
+       struct cache_head **head;
+       struct cache_head *tmp;
+       int is_new;
+
+       if (!test_bit(CACHE_VALID, &old->flags)) {
+               write_lock(&detail->hash_lock);
+               if (!test_bit(CACHE_VALID, &old->flags)) {
+                       if (test_bit(CACHE_NEGATIVE, &new->flags))
+                               set_bit(CACHE_NEGATIVE, &old->flags);
+                       else
+                               detail->update(old, new);
+                       is_new = cache_fresh_locked(old, new->expiry_time);
+                       write_unlock(&detail->hash_lock);
+                       cache_fresh_unlocked(old, detail, is_new);
+                       return old;
+               }
+               write_unlock(&detail->hash_lock);
+       }
+       /* We need to insert a new entry */
+       tmp = detail->alloc();
+       if (!tmp) {
+               cache_put(old, detail);
+               return NULL;
+       }
+       cache_init(tmp);
+       detail->init(tmp, old);
+       head = &detail->hash_table[hash];
+
+       write_lock(&detail->hash_lock);
+       if (test_bit(CACHE_NEGATIVE, &new->flags))
+               set_bit(CACHE_NEGATIVE, &tmp->flags);
+       else
+               detail->update(tmp, new);
+       tmp->next = *head;
+       *head = tmp;
+       detail->entries++;
+       cache_get(tmp);
+       is_new = cache_fresh_locked(tmp, new->expiry_time);
+       cache_fresh_locked(old, 0);
+       write_unlock(&detail->hash_lock);
+       cache_fresh_unlocked(tmp, detail, is_new);
+       cache_fresh_unlocked(old, detail, 0);
+       cache_put(old, detail);
+       return tmp;
+}
+EXPORT_SYMBOL(sunrpc_cache_update);
 
 static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
 /*
@@ -90,7 +217,8 @@ int cache_check(struct cache_detail *detail,
                                clear_bit(CACHE_PENDING, &h->flags);
                                if (rv == -EAGAIN) {
                                        set_bit(CACHE_NEGATIVE, &h->flags);
-                                       cache_fresh(detail, h, get_seconds()+CACHE_NEW_EXPIRY);
+                                       cache_fresh_unlocked(h, detail,
+                                            cache_fresh_locked(h, get_seconds()+CACHE_NEW_EXPIRY));
                                        rv = -ENOENT;
                                }
                                break;
@@ -106,25 +234,11 @@ int cache_check(struct cache_detail *detail,
        if (rv == -EAGAIN)
                cache_defer_req(rqstp, h);
 
-       if (rv && h)
-               detail->cache_put(h, detail);
+       if (rv)
+               cache_put(h, detail);
        return rv;
 }
 
-static void queue_loose(struct cache_detail *detail, struct cache_head *ch);
-
-void cache_fresh(struct cache_detail *detail,
-                struct cache_head *head, time_t expiry)
-{
-
-       head->expiry_time = expiry;
-       head->last_refresh = get_seconds();
-       if (!test_and_set_bit(CACHE_VALID, &head->flags))
-               cache_revisit_request(head);
-       if (test_and_clear_bit(CACHE_PENDING, &head->flags))
-               queue_loose(detail, head);
-}
-
 /*
  * caches need to be periodically cleaned.
  * For this we maintain a list of cache_detail and
@@ -158,7 +272,7 @@ void cache_fresh(struct cache_detail *detail,
  */
 
 static LIST_HEAD(cache_list);
-static spinlock_t cache_list_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(cache_list_lock);
 static struct cache_detail *current_detail;
 static int current_index;
 
@@ -174,7 +288,7 @@ void cache_register(struct cache_detail *cd)
        cd->proc_ent = proc_mkdir(cd->name, proc_net_rpc);
        if (cd->proc_ent) {
                struct proc_dir_entry *p;
-               cd->proc_ent->owner = THIS_MODULE;
+               cd->proc_ent->owner = cd->owner;
                cd->channel_ent = cd->content_ent = NULL;
                
                p = create_proc_entry("flush", S_IFREG|S_IRUSR|S_IWUSR,
@@ -182,7 +296,7 @@ void cache_register(struct cache_detail *cd)
                cd->flush_ent =  p;
                if (p) {
                        p->proc_fops = &cache_flush_operations;
-                       p->owner = THIS_MODULE;
+                       p->owner = cd->owner;
                        p->data = cd;
                }
  
@@ -192,7 +306,7 @@ void cache_register(struct cache_detail *cd)
                        cd->channel_ent = p;
                        if (p) {
                                p->proc_fops = &cache_file_operations;
-                               p->owner = THIS_MODULE;
+                               p->owner = cd->owner;
                                p->data = cd;
                        }
                }
@@ -202,7 +316,7 @@ void cache_register(struct cache_detail *cd)
                        cd->content_ent = p;
                        if (p) {
                                p->proc_fops = &content_file_operations;
-                               p->owner = THIS_MODULE;
+                               p->owner = cd->owner;
                                p->data = cd;
                        }
                }
@@ -214,6 +328,7 @@ void cache_register(struct cache_detail *cd)
        cd->entries = 0;
        atomic_set(&cd->readers, 0);
        cd->last_close = 0;
+       cd->last_warn = -1;
        list_add(&cd->others, &cache_list);
        spin_unlock(&cache_list_lock);
 
@@ -255,39 +370,13 @@ int cache_unregister(struct cache_detail *cd)
        return 0;
 }
 
-struct cache_detail *cache_find(char *name)
-{
-       struct list_head *l;
-
-       spin_lock(&cache_list_lock);
-       list_for_each(l, &cache_list) {
-               struct cache_detail *cd = list_entry(l, struct cache_detail, others);
-               
-               if (strcmp(cd->name, name)==0) {
-                       atomic_inc(&cd->inuse);
-                       spin_unlock(&cache_list_lock);
-                       return cd;
-               }
-       }
-       spin_unlock(&cache_list_lock);
-       return NULL;
-}
-
-/* cache_drop must be called on any cache returned by
- * cache_find, after it has been used
- */
-void cache_drop(struct cache_detail *detail)
-{
-       atomic_dec(&detail->inuse);
-}
-
 /* clean cache tries to find something to clean
  * and cleans it.
  * It returns 1 if it cleaned something,
  *            0 if it didn't find anything this time
  *           -1 if it fell off the end of the list.
  */
-int cache_clean(void)
+static int cache_clean(void)
 {
        int rv = 0;
        struct list_head *next;
@@ -343,12 +432,10 @@ int cache_clean(void)
                        if (test_and_clear_bit(CACHE_PENDING, &ch->flags))
                                queue_loose(current_detail, ch);
 
-                       if (!atomic_read(&ch->refcnt))
+                       if (atomic_read(&ch->ref.refcount) == 1)
                                break;
                }
                if (ch) {
-                       cache_get(ch);
-                       clear_bit(CACHE_HASHED, &ch->flags);
                        *cp = ch->next;
                        ch->next = NULL;
                        current_detail->entries--;
@@ -360,7 +447,7 @@ int cache_clean(void)
                        current_index ++;
                spin_unlock(&cache_list_lock);
                if (ch)
-                       d->cache_put(ch, d);
+                       cache_put(ch, d);
        } else
                spin_unlock(&cache_list_lock);
 
@@ -399,9 +486,10 @@ void cache_flush(void)
 
 void cache_purge(struct cache_detail *detail)
 {
-       detail->flush_time = get_seconds()+1;
+       detail->flush_time = LONG_MAX;
        detail->nextcheck = get_seconds();
        cache_flush();
+       detail->flush_time = 1;
 }
 
 
@@ -426,12 +514,12 @@ void cache_purge(struct cache_detail *detail)
 
 #define        DFR_MAX 300     /* ??? */
 
-spinlock_t cache_defer_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(cache_defer_lock);
 static LIST_HEAD(cache_defer_list);
 static struct list_head cache_defer_hash[DFR_HASHSIZE];
 static int cache_defer_cnt;
 
-void cache_defer_req(struct cache_req *req, struct cache_head *item)
+static void cache_defer_req(struct cache_req *req, struct cache_head *item)
 {
        struct cache_deferred_req *dreq;
        int hash = DFR_HASH(item);
@@ -475,13 +563,13 @@ void cache_defer_req(struct cache_req *req, struct cache_head *item)
                /* there was one too many */
                dreq->revisit(dreq, 1);
        }
-       if (test_bit(CACHE_VALID, &item->flags)) {
+       if (!test_bit(CACHE_PENDING, &item->flags)) {
                /* must have just been validated... */
                cache_revisit_request(item);
        }
 }
 
-void cache_revisit_request(struct cache_head *item)
+static void cache_revisit_request(struct cache_head *item)
 {
        struct cache_deferred_req *dreq;
        struct list_head pending;
@@ -554,8 +642,8 @@ void cache_clean_deferred(void *owner)
  *
  */
 
-static spinlock_t queue_lock = SPIN_LOCK_UNLOCKED;
-static DECLARE_MUTEX(queue_io_sem);
+static DEFINE_SPINLOCK(queue_lock);
+static DEFINE_MUTEX(queue_io_mutex);
 
 struct cache_queue {
        struct list_head        list;
@@ -574,20 +662,17 @@ struct cache_reader {
 };
 
 static ssize_t
-cache_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+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_dentry->d_inode)->data;
        int err;
 
-       if (ppos != &filp->f_pos)
-               return -ESPIPE;
-
        if (count == 0)
                return 0;
 
-       down(&queue_io_sem); /* protect against multiple concurrent
+       mutex_lock(&queue_io_mutex); /* protect against multiple concurrent
                              * readers on this file */
  again:
        spin_lock(&queue_lock);
@@ -600,13 +685,12 @@ cache_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
        }
        if (rp->q.list.next == &cd->queue) {
                spin_unlock(&queue_lock);
-               up(&queue_io_sem);
-               if (rp->offset)
-                       BUG();
+               mutex_unlock(&queue_io_mutex);
+               BUG_ON(rp->offset);
                return 0;
        }
        rq = container_of(rp->q.list.next, struct cache_request, q.list);
-       if (rq->q.reader) BUG();
+       BUG_ON(rq->q.reader);
        if (rp->offset == 0)
                rq->readers++;
        spin_unlock(&queue_lock);
@@ -640,7 +724,7 @@ cache_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
                    !test_bit(CACHE_PENDING, &rq->item->flags)) {
                        list_del(&rq->q.list);
                        spin_unlock(&queue_lock);
-                       cd->cache_put(rq->item, cd);
+                       cache_put(rq->item, cd);
                        kfree(rq->buf);
                        kfree(rq);
                } else
@@ -648,48 +732,37 @@ cache_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
        }
        if (err == -EAGAIN)
                goto again;
-       up(&queue_io_sem);
+       mutex_unlock(&queue_io_mutex);
        return err ? err :  count;
 }
 
+static char write_buf[8192]; /* protected by queue_io_mutex */
+
 static ssize_t
-cache_write(struct file *filp, const char *buf, size_t count,
+cache_write(struct file *filp, const char __user *buf, size_t count,
            loff_t *ppos)
 {
        int err;
-       char *page;
        struct cache_detail *cd = PDE(filp->f_dentry->d_inode)->data;
 
-       if (ppos != &filp->f_pos)
-               return -ESPIPE;
-
        if (count == 0)
                return 0;
-       if (count > PAGE_SIZE)
+       if (count >= sizeof(write_buf))
                return -EINVAL;
 
-       down(&queue_io_sem);
-
-       page = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (page == NULL) {
-               up(&queue_io_sem);
-               return -ENOMEM;
-       }
+       mutex_lock(&queue_io_mutex);
 
-       if (copy_from_user(page, buf, count)) {
-               up(&queue_io_sem);
-               kfree(page);
+       if (copy_from_user(write_buf, buf, count)) {
+               mutex_unlock(&queue_io_mutex);
                return -EFAULT;
        }
-       if (count < PAGE_SIZE)
-               page[count] = '\0';
+       write_buf[count] = '\0';
        if (cd->cache_parse)
-               err = cd->cache_parse(cd, page, count);
+               err = cd->cache_parse(cd, write_buf, count);
        else
                err = -EINVAL;
 
-       up(&queue_io_sem);
-       kfree(page);
+       mutex_unlock(&queue_io_mutex);
        return err ? err : count;
 }
 
@@ -750,7 +823,7 @@ cache_ioctl(struct inode *ino, struct file *filp,
                }
        spin_unlock(&queue_lock);
 
-       return put_user(len, (int *)arg);
+       return put_user(len, (int __user *)arg);
 }
 
 static int
@@ -758,6 +831,7 @@ cache_open(struct inode *inode, struct file *filp)
 {
        struct cache_reader *rp = NULL;
 
+       nonseekable_open(inode, filp);
        if (filp->f_mode & FMODE_READ) {
                struct cache_detail *cd = PDE(inode)->data;
 
@@ -830,10 +904,10 @@ static void queue_loose(struct cache_detail *detail, struct cache_head *ch)
                        if (cr->item != ch)
                                continue;
                        if (cr->readers != 0)
-                               break;
+                               continue;
                        list_del(&cr->q.list);
                        spin_unlock(&queue_lock);
-                       detail->cache_put(cr->item, detail);
+                       cache_put(cr->item, detail);
                        kfree(cr->buf);
                        kfree(cr);
                        return;
@@ -913,7 +987,14 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen)
        *lp = len;
 }
 
-                       
+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);
+       }
+}
 
 /*
  * register an upcall request to user-space.
@@ -931,9 +1012,10 @@ static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
                return -EINVAL;
 
        if (atomic_read(&detail->readers) == 0 &&
-           detail->last_close < get_seconds() - 60)
-               /* nobody is listening */
-               return -EINVAL;
+           detail->last_close < get_seconds() - 30) {
+                       warn_no_listener(detail);
+                       return -EINVAL;
+       }
 
        buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!buf)
@@ -1110,8 +1192,8 @@ static int c_show(struct seq_file *m, void *p)
                return cd->cache_show(m, cd, NULL);
 
        ifdebug(CACHE)
-               seq_printf(m, "# expiry=%ld refcnt=%d\n",
-                          cp->expiry_time, atomic_read(&cp->refcnt));
+               seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n",
+                          cp->expiry_time, atomic_read(&cp->ref.refcount), cp->flags);
        cache_get(cp);
        if (cache_check(cd, cp, NULL))
                /* cache_check does a cache_put on failure */
@@ -1122,7 +1204,7 @@ static int c_show(struct seq_file *m, void *p)
        return cd->cache_show(m, cd, cp);
 }
 
-struct seq_operations cache_content_op = {
+static struct seq_operations cache_content_op = {
        .start  = c_start,
        .next   = c_next,
        .stop   = c_stop,
@@ -1165,7 +1247,7 @@ static struct file_operations content_file_operations = {
        .release        = content_release,
 };
 
-static ssize_t read_flush(struct file *file, char *buf,
+static ssize_t read_flush(struct file *file, char __user *buf,
                            size_t count, loff_t *ppos)
 {
        struct cache_detail *cd = PDE(file->f_dentry->d_inode)->data;
@@ -1186,7 +1268,7 @@ static ssize_t read_flush(struct file *file, char *buf,
        return len;
 }
 
-static ssize_t write_flush(struct file * file, const char * buf,
+static ssize_t write_flush(struct file * file, const char __user * buf,
                             size_t count, loff_t *ppos)
 {
        struct cache_detail *cd = PDE(file->f_dentry->d_inode)->data;
@@ -1211,6 +1293,7 @@ static ssize_t write_flush(struct file * file, const char * buf,
 }
 
 static struct file_operations cache_flush_operations = {
+       .open           = nonseekable_open,
        .read           = read_flush,
        .write          = write_flush,
 };