Commit a9f2e47f authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] kNFSd: Provide generic code for making an upcall.

This code enhances 'cache_check' to try to initiate
an up-call if the cache entry is not up-to-date, and also
defines add_word and add_hex for formating up-call
requests.  See rpc-cache.txt for more detail.
parent 04ffa550
...@@ -138,4 +138,34 @@ Each cache should define a "cache_parse" method which takes a message ...@@ -138,4 +138,34 @@ Each cache should define a "cache_parse" method which takes a message
written from user-space and processes it. It should return an error written from user-space and processes it. It should return an error
(which propagates back to the write syscall) or 0. (which propagates back to the write syscall) or 0.
Each cache should also define a "cache_request" method which
takes a cache item and encodes a request into the buffer
provided.
Note: If a cache has no active readers on the channel, and has had not
active readers for more than 60 seconds, further requests will not be
added to the channel but instead all looks that do not find a valid
entry will fail. This is partly for backward compatability: The
previous nfs exports table was deemed to be authoritative and a
failed lookup meant a definate 'no'.
request/response format
-----------------------
While each cache is free to use it's own format for requests
and responses over channel, the following is recommended are
appropriate and support routines are available to help:
Each request or response record should be printable ASCII
with precisely one newline character which should be at the end.
Fields within the record should be separated by spaces, normally one.
If spaces, newlines, or nul characters are needed in a field they
much be quotes. two mechanisms are available:
1/ If a field begins '\x' then it must contain an even number of
hex digits, and pairs of these digits provide the bytes in the
field.
2/ otherwise a \ in the field must be followed by 3 octal digits
which give the code for a byte. Other characters are treated
as them selves. At the very least, space, newlines nul, and
'\' must be quoted in this way.
...@@ -72,9 +72,9 @@ struct cache_detail { ...@@ -72,9 +72,9 @@ struct cache_detail {
void (*cache_put)(struct cache_head *, void (*cache_put)(struct cache_head *,
struct cache_detail*); struct cache_detail*);
/* request and update functions for interaction with userspace void (*cache_request)(struct cache_detail *cd,
* will go here struct cache_head *h,
*/ char **bpp, int *blen);
int (*cache_parse)(struct cache_detail *, int (*cache_parse)(struct cache_detail *,
char *buf, int len); char *buf, int len);
...@@ -90,6 +90,8 @@ struct cache_detail { ...@@ -90,6 +90,8 @@ struct cache_detail {
/* fields for communication over channel */ /* fields for communication over channel */
struct list_head queue; struct list_head queue;
struct proc_dir_entry *proc_ent; struct proc_dir_entry *proc_ent;
atomic_t readers; /* how many time is /chennel open */
time_t last_close; /* it no readers, when did last close */
}; };
...@@ -269,4 +271,7 @@ extern int cache_unregister(struct cache_detail *cd); ...@@ -269,4 +271,7 @@ extern int cache_unregister(struct cache_detail *cd);
extern struct cache_detail *cache_find(char *name); extern struct cache_detail *cache_find(char *name);
extern void cache_drop(struct cache_detail *detail); extern void cache_drop(struct cache_detail *detail);
extern void add_word(char **bpp, int *lp, char *str);
extern void add_hex(char **bpp, int *lp, char *buf, int blen);
#endif /* _LINUX_SUNRPC_CACHE_H_ */ #endif /* _LINUX_SUNRPC_CACHE_H_ */
...@@ -40,6 +40,7 @@ void cache_init(struct cache_head *h) ...@@ -40,6 +40,7 @@ void cache_init(struct cache_head *h)
} }
static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h);
/* /*
* This is the generic cache management routine for all * This is the generic cache management routine for all
* the authentication caches. * the authentication caches.
...@@ -55,6 +56,7 @@ int cache_check(struct cache_detail *detail, ...@@ -55,6 +56,7 @@ int cache_check(struct cache_detail *detail,
struct cache_head *h, struct cache_req *rqstp) struct cache_head *h, struct cache_req *rqstp)
{ {
int rv; int rv;
long refresh_age, age;
/* First decide return status as best we can */ /* First decide return status as best we can */
if (!test_bit(CACHE_VALID, &h->flags) || if (!test_bit(CACHE_VALID, &h->flags) ||
...@@ -69,31 +71,54 @@ int cache_check(struct cache_detail *detail, ...@@ -69,31 +71,54 @@ int cache_check(struct cache_detail *detail,
else rv = 0; else rv = 0;
} }
/* up-call processing goes here later */ /* now see if we want to start an upcall */
/* if cache_pending, initiate upcall if none pending. refresh_age = (h->expiry_time - h->last_refresh);
* if upcall cannot be initiated, change to CACHE_NEGATIVE age = CURRENT_TIME - h->last_refresh;
*/
if (rv == CACHE_PENDING) rv = CACHE_NEGATIVE;
if (rv == CACHE_PENDING) if (rqstp == NULL) {
cache_defer_req(rqstp, h); if (rv == -EAGAIN)
rv = -ENOENT;
} else if (rv == -EAGAIN || age > refresh_age/2) {
dprintk("Want update, refage=%ld, age=%ld\n", refresh_age, age);
if (!test_and_set_bit(CACHE_PENDING, &h->flags)) {
switch (cache_make_upcall(detail, h)) {
case -EINVAL:
clear_bit(CACHE_PENDING, &h->flags);
if (rv == -EAGAIN) {
set_bit(CACHE_NEGATIVE, &h->flags);
cache_fresh(detail, h, CURRENT_TIME+CACHE_NEW_EXPIRY);
rv = -ENOENT;
}
break;
if (rv == -EAGAIN /* && cannot do upcall */) case -EAGAIN:
rv = -ENOENT; clear_bit(CACHE_PENDING, &h->flags);
cache_revisit_request(h);
break;
}
}
}
if (rv == -EAGAIN)
cache_defer_req(rqstp, h);
if (rv && h) if (rv && h)
detail->cache_put(h, detail); detail->cache_put(h, detail);
return rv; return rv;
} }
static void queue_loose(struct cache_detail *detail, struct cache_head *ch);
void cache_fresh(struct cache_detail *detail, void cache_fresh(struct cache_detail *detail,
struct cache_head *head, time_t expiry) struct cache_head *head, time_t expiry)
{ {
head->expiry_time = expiry; head->expiry_time = expiry;
head->last_refresh = CURRENT_TIME; head->last_refresh = CURRENT_TIME;
set_bit(CACHE_VALID, &head->flags); if (!test_and_set_bit(CACHE_VALID, &head->flags))
clear_bit(CACHE_PENDING, &head->flags); cache_revisit_request(head);
if (test_and_clear_bit(CACHE_PENDING, &head->flags))
queue_loose(detail, head);
} }
/* /*
...@@ -155,6 +180,8 @@ void cache_register(struct cache_detail *cd) ...@@ -155,6 +180,8 @@ void cache_register(struct cache_detail *cd)
spin_lock(&cache_list_lock); spin_lock(&cache_list_lock);
cd->nextcheck = 0; cd->nextcheck = 0;
cd->entries = 0; cd->entries = 0;
atomic_set(&cd->readers, 0);
cd->last_close = CURRENT_TIME;
list_add(&cd->others, &cache_list); list_add(&cd->others, &cache_list);
spin_unlock(&cache_list_lock); spin_unlock(&cache_list_lock);
} }
...@@ -639,6 +666,7 @@ cache_open(struct inode *inode, struct file *filp) ...@@ -639,6 +666,7 @@ cache_open(struct inode *inode, struct file *filp)
rp->page = NULL; rp->page = NULL;
rp->offset = 0; rp->offset = 0;
rp->q.reader = 1; rp->q.reader = 1;
atomic_inc(&cd->readers);
spin_lock(&queue_lock); spin_lock(&queue_lock);
list_add(&rp->q.list, &cd->queue); list_add(&rp->q.list, &cd->queue);
spin_unlock(&queue_lock); spin_unlock(&queue_lock);
...@@ -672,6 +700,9 @@ cache_release(struct inode *inode, struct file *filp) ...@@ -672,6 +700,9 @@ cache_release(struct inode *inode, struct file *filp)
filp->private_data = NULL; filp->private_data = NULL;
kfree(rp); kfree(rp);
cd->last_close = CURRENT_TIME;
atomic_dec(&cd->readers);
return 0; return 0;
} }
...@@ -686,3 +717,150 @@ struct file_operations cache_file_operations = { ...@@ -686,3 +717,150 @@ struct file_operations cache_file_operations = {
.open = cache_open, .open = cache_open,
.release = cache_release, .release = cache_release,
}; };
static void queue_loose(struct cache_detail *detail, struct cache_head *ch)
{
struct cache_queue *cq;
spin_lock(&queue_lock);
list_for_each_entry(cq, &detail->queue, list)
if (!cq->reader) {
struct cache_request *cr = container_of(cq, struct cache_request, q);
if (cr->item != ch)
continue;
if (cr->readers != 0)
break;
list_del(&cr->q.list);
spin_unlock(&queue_lock);
detail->cache_put(cr->item, detail);
kfree(cr->buf);
kfree(cr);
return;
}
spin_unlock(&queue_lock);
}
/*
* Support routines for text-based upcalls.
* Fields are separated by spaces.
* Fields are either mangled to quote space tab newline slosh with slosh
* or a hexified with a leading \x
* Record is terminated with newline.
*
*/
void add_word(char **bpp, int *lp, char *str)
{
char *bp = *bpp;
int len = *lp;
char c;
if (len < 0) return;
while ((c=*str++) && len)
switch(c) {
case ' ':
case '\t':
case '\n':
case '\\':
if (len >= 4) {
*bp++ = '\\';
*bp++ = '0' + ((c & 0300)>>6);
*bp++ = '0' + ((c & 0070)>>3);
*bp++ = '0' + ((c & 0007)>>0);
}
len -= 4;
break;
default:
*bp++ = c;
len--;
}
if (c || len <1) len = -1;
else {
*bp++ = ' ';
len--;
}
*bpp = bp;
*lp = len;
}
void add_hex(char **bpp, int *lp, char *buf, int blen)
{
char *bp = *bpp;
int len = *lp;
if (len < 0) return;
if (len > 2) {
*bp++ = '\\';
*bp++ = 'x';
len -= 2;
while (blen && len >= 2) {
unsigned char c = *buf++;
*bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'0');
*bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'0');
len -= 2;
blen--;
}
}
if (blen || len<1) len = -1;
else {
*bp++ = ' ';
len--;
}
*bpp = bp;
*lp = len;
}
/*
* register an upcall request to user-space.
* Each request is at most one page long.
*/
static int cache_make_upcall(struct cache_detail *detail, struct cache_head *h)
{
char *buf;
struct cache_request *crq;
char *bp;
int len;
if (detail->cache_request == NULL)
return -EINVAL;
if (atomic_read(&detail->readers) == 0 &&
detail->last_close < CURRENT_TIME - 60)
/* nobody is listening */
return -EINVAL;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -EAGAIN;
crq = kmalloc(sizeof (*crq), GFP_KERNEL);
if (!crq) {
kfree(buf);
return -EAGAIN;
}
bp = buf; len = PAGE_SIZE;
detail->cache_request(detail, h, &bp, &len);
if (len < 0) {
kfree(buf);
kfree(crq);
return -EAGAIN;
}
crq->q.reader = 0;
crq->item = cache_get(h);
crq->buf = buf;
crq->len = PAGE_SIZE - len;
crq->readers = 0;
spin_lock(&queue_lock);
list_add_tail(&crq->q.list, &detail->queue);
spin_unlock(&queue_lock);
wake_up(&queue_wait);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment