Commit ab77ca16 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] Fix posix file locking (5/9)

NLM: file_lock->fl_owner may live for longer than the pid of the
   original process that created it. Fix NFSv2/v3 client locking code
   to map file_lock->fl_owner into a unique 32-bit number or
   "pseudo-pid".
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@fys.uio.no>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 7a25aba8
...@@ -146,7 +146,7 @@ void nlmclnt_mark_reclaim(struct nlm_host *host) ...@@ -146,7 +146,7 @@ void nlmclnt_mark_reclaim(struct nlm_host *host)
inode = fl->fl_file->f_dentry->d_inode; inode = fl->fl_file->f_dentry->d_inode;
if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
continue; continue;
if (fl->fl_u.nfs_fl.host != host) if (fl->fl_u.nfs_fl.owner->host != host)
continue; continue;
if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED))
continue; continue;
...@@ -215,7 +215,7 @@ reclaimer(void *ptr) ...@@ -215,7 +215,7 @@ reclaimer(void *ptr)
inode = fl->fl_file->f_dentry->d_inode; inode = fl->fl_file->f_dentry->d_inode;
if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
continue; continue;
if (fl->fl_u.nfs_fl.host != host) if (fl->fl_u.nfs_fl.owner->host != host)
continue; continue;
if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM)) if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM))
continue; continue;
......
...@@ -42,6 +42,79 @@ static inline void nlmclnt_next_cookie(struct nlm_cookie *c) ...@@ -42,6 +42,79 @@ static inline void nlmclnt_next_cookie(struct nlm_cookie *c)
nlm_cookie++; nlm_cookie++;
} }
static struct nlm_lockowner *nlm_get_lockowner(struct nlm_lockowner *lockowner)
{
atomic_inc(&lockowner->count);
return lockowner;
}
static void nlm_put_lockowner(struct nlm_lockowner *lockowner)
{
if (!atomic_dec_and_lock(&lockowner->count, &lockowner->host->h_lock))
return;
list_del(&lockowner->list);
spin_unlock(&lockowner->host->h_lock);
nlm_release_host(lockowner->host);
kfree(lockowner);
}
static inline int nlm_pidbusy(struct nlm_host *host, uint32_t pid)
{
struct nlm_lockowner *lockowner;
list_for_each_entry(lockowner, &host->h_lockowners, list) {
if (lockowner->pid == pid)
return -EBUSY;
}
return 0;
}
static inline uint32_t __nlm_alloc_pid(struct nlm_host *host)
{
uint32_t res;
do {
res = host->h_pidcount++;
} while (nlm_pidbusy(host, res) < 0);
return res;
}
static struct nlm_lockowner *__nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner)
{
struct nlm_lockowner *lockowner;
list_for_each_entry(lockowner, &host->h_lockowners, list) {
if (lockowner->owner != owner)
continue;
return nlm_get_lockowner(lockowner);
}
return NULL;
}
static struct nlm_lockowner *nlm_find_lockowner(struct nlm_host *host, fl_owner_t owner)
{
struct nlm_lockowner *res, *new = NULL;
spin_lock(&host->h_lock);
res = __nlm_find_lockowner(host, owner);
if (res == NULL) {
spin_unlock(&host->h_lock);
new = (struct nlm_lockowner *)kmalloc(sizeof(*new), GFP_KERNEL);
spin_lock(&host->h_lock);
res = __nlm_find_lockowner(host, owner);
if (res == NULL && new != NULL) {
res = new;
atomic_set(&new->count, 1);
new->owner = owner;
new->pid = __nlm_alloc_pid(host);
new->host = nlm_get_host(host);
list_add(&new->list, &host->h_lockowners);
new = NULL;
}
}
spin_unlock(&host->h_lock);
if (new != NULL)
kfree(new);
return res;
}
/* /*
* Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
*/ */
...@@ -418,12 +491,12 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) ...@@ -418,12 +491,12 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl) static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{ {
memcpy(&new->fl_u.nfs_fl, &fl->fl_u.nfs_fl, sizeof(new->fl_u.nfs_fl)); memcpy(&new->fl_u.nfs_fl, &fl->fl_u.nfs_fl, sizeof(new->fl_u.nfs_fl));
nlm_get_host(new->fl_u.nfs_fl.host); nlm_get_lockowner(new->fl_u.nfs_fl.owner);
} }
static void nlmclnt_locks_release_private(struct file_lock *fl) static void nlmclnt_locks_release_private(struct file_lock *fl)
{ {
nlm_release_host(fl->fl_u.nfs_fl.host); nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
fl->fl_ops = NULL; fl->fl_ops = NULL;
} }
...@@ -437,7 +510,7 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho ...@@ -437,7 +510,7 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho
BUG_ON(fl->fl_ops != NULL); BUG_ON(fl->fl_ops != NULL);
fl->fl_u.nfs_fl.state = 0; fl->fl_u.nfs_fl.state = 0;
fl->fl_u.nfs_fl.flags = 0; fl->fl_u.nfs_fl.flags = 0;
fl->fl_u.nfs_fl.host = nlm_get_host(host); fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner);
fl->fl_ops = &nlmclnt_lock_ops; fl->fl_ops = &nlmclnt_lock_ops;
} }
......
...@@ -119,13 +119,15 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, ...@@ -119,13 +119,15 @@ nlm_lookup_host(int server, struct sockaddr_in *sin,
init_MUTEX(&host->h_sema); init_MUTEX(&host->h_sema);
host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_nextrebind = jiffies + NLM_HOST_REBIND;
host->h_expires = jiffies + NLM_HOST_EXPIRE; host->h_expires = jiffies + NLM_HOST_EXPIRE;
host->h_count = 1; atomic_set(&host->h_count, 1);
init_waitqueue_head(&host->h_gracewait); init_waitqueue_head(&host->h_gracewait);
host->h_state = 0; /* pseudo NSM state */ host->h_state = 0; /* pseudo NSM state */
host->h_nsmstate = 0; /* real NSM state */ host->h_nsmstate = 0; /* real NSM state */
host->h_server = server; host->h_server = server;
host->h_next = nlm_hosts[hash]; host->h_next = nlm_hosts[hash];
nlm_hosts[hash] = host; nlm_hosts[hash] = host;
INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock);
if (++nrhosts > NLM_HOST_MAX) if (++nrhosts > NLM_HOST_MAX)
next_gc = 0; next_gc = 0;
...@@ -235,7 +237,7 @@ struct nlm_host * nlm_get_host(struct nlm_host *host) ...@@ -235,7 +237,7 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
{ {
if (host) { if (host) {
dprintk("lockd: get host %s\n", host->h_name); dprintk("lockd: get host %s\n", host->h_name);
host->h_count ++; atomic_inc(&host->h_count);
host->h_expires = jiffies + NLM_HOST_EXPIRE; host->h_expires = jiffies + NLM_HOST_EXPIRE;
} }
return host; return host;
...@@ -246,9 +248,10 @@ struct nlm_host * nlm_get_host(struct nlm_host *host) ...@@ -246,9 +248,10 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
*/ */
void nlm_release_host(struct nlm_host *host) void nlm_release_host(struct nlm_host *host)
{ {
if (host && host->h_count) { if (host != NULL) {
dprintk("lockd: release host %s\n", host->h_name); dprintk("lockd: release host %s\n", host->h_name);
host->h_count --; atomic_dec(&host->h_count);
BUG_ON(atomic_read(&host->h_count) < 0);
} }
} }
...@@ -283,7 +286,7 @@ nlm_shutdown_hosts(void) ...@@ -283,7 +286,7 @@ nlm_shutdown_hosts(void)
for (i = 0; i < NLM_HOST_NRHASH; i++) { for (i = 0; i < NLM_HOST_NRHASH; i++) {
for (host = nlm_hosts[i]; host; host = host->h_next) { for (host = nlm_hosts[i]; host; host = host->h_next) {
dprintk(" %s (cnt %d use %d exp %ld)\n", dprintk(" %s (cnt %d use %d exp %ld)\n",
host->h_name, host->h_count, host->h_name, atomic_read(&host->h_count),
host->h_inuse, host->h_expires); host->h_inuse, host->h_expires);
} }
} }
...@@ -314,10 +317,10 @@ nlm_gc_hosts(void) ...@@ -314,10 +317,10 @@ nlm_gc_hosts(void)
for (i = 0; i < NLM_HOST_NRHASH; i++) { for (i = 0; i < NLM_HOST_NRHASH; i++) {
q = &nlm_hosts[i]; q = &nlm_hosts[i];
while ((host = *q) != NULL) { while ((host = *q) != NULL) {
if (host->h_count || host->h_inuse if (atomic_read(&host->h_count) || host->h_inuse
|| time_before(jiffies, host->h_expires)) { || time_before(jiffies, host->h_expires)) {
dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n", dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
host->h_name, host->h_count, host->h_name, atomic_read(&host->h_count),
host->h_inuse, host->h_expires); host->h_inuse, host->h_expires);
q = &host->h_next; q = &host->h_next;
continue; continue;
...@@ -336,6 +339,7 @@ nlm_gc_hosts(void) ...@@ -336,6 +339,7 @@ nlm_gc_hosts(void)
rpc_destroy_client(host->h_rpcclnt); rpc_destroy_client(host->h_rpcclnt);
} }
} }
BUG_ON(!list_empty(&host->h_lockowners));
kfree(host); kfree(host);
nrhosts--; nrhosts--;
} }
......
...@@ -52,10 +52,25 @@ struct nlm_host { ...@@ -52,10 +52,25 @@ struct nlm_host {
wait_queue_head_t h_gracewait; /* wait while reclaiming */ wait_queue_head_t h_gracewait; /* wait while reclaiming */
u32 h_state; /* pseudo-state counter */ u32 h_state; /* pseudo-state counter */
u32 h_nsmstate; /* true remote NSM state */ u32 h_nsmstate; /* true remote NSM state */
unsigned int h_count; /* reference count */ u32 h_pidcount; /* Pseudopids */
atomic_t h_count; /* reference count */
struct semaphore h_sema; /* mutex for pmap binding */ struct semaphore h_sema; /* mutex for pmap binding */
unsigned long h_nextrebind; /* next portmap call */ unsigned long h_nextrebind; /* next portmap call */
unsigned long h_expires; /* eligible for GC */ unsigned long h_expires; /* eligible for GC */
struct list_head h_lockowners; /* Lockowners for the client */
spinlock_t h_lock;
};
/*
* Map an fl_owner_t into a unique 32-bit "pid"
*/
struct nlm_lockowner {
struct list_head list;
atomic_t count;
struct nlm_host *host;
fl_owner_t owner;
uint32_t pid;
}; };
/* /*
......
...@@ -5,13 +5,15 @@ ...@@ -5,13 +5,15 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/nfs.h> #include <linux/nfs.h>
struct nlm_lockowner;
/* /*
* NFS lock info * NFS lock info
*/ */
struct nfs_lock_info { struct nfs_lock_info {
u32 state; u32 state;
u32 flags; u32 flags;
struct nlm_host *host; struct nlm_lockowner *owner;
}; };
/* /*
......
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