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)
inode = fl->fl_file->f_dentry->d_inode;
if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
continue;
if (fl->fl_u.nfs_fl.host != host)
if (fl->fl_u.nfs_fl.owner->host != host)
continue;
if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED))
continue;
......@@ -215,7 +215,7 @@ reclaimer(void *ptr)
inode = fl->fl_file->f_dentry->d_inode;
if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
continue;
if (fl->fl_u.nfs_fl.host != host)
if (fl->fl_u.nfs_fl.owner->host != host)
continue;
if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM))
continue;
......
......@@ -42,6 +42,79 @@ static inline void nlmclnt_next_cookie(struct nlm_cookie *c)
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
*/
......@@ -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)
{
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)
{
nlm_release_host(fl->fl_u.nfs_fl.host);
nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
fl->fl_ops = NULL;
}
......@@ -437,7 +510,7 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho
BUG_ON(fl->fl_ops != NULL);
fl->fl_u.nfs_fl.state = 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;
}
......
......@@ -119,13 +119,15 @@ nlm_lookup_host(int server, struct sockaddr_in *sin,
init_MUTEX(&host->h_sema);
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
host->h_expires = jiffies + NLM_HOST_EXPIRE;
host->h_count = 1;
atomic_set(&host->h_count, 1);
init_waitqueue_head(&host->h_gracewait);
host->h_state = 0; /* pseudo NSM state */
host->h_nsmstate = 0; /* real NSM state */
host->h_server = server;
host->h_next = nlm_hosts[hash];
nlm_hosts[hash] = host;
INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock);
if (++nrhosts > NLM_HOST_MAX)
next_gc = 0;
......@@ -235,7 +237,7 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
{
if (host) {
dprintk("lockd: get host %s\n", host->h_name);
host->h_count ++;
atomic_inc(&host->h_count);
host->h_expires = jiffies + NLM_HOST_EXPIRE;
}
return host;
......@@ -246,9 +248,10 @@ struct nlm_host * nlm_get_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);
host->h_count --;
atomic_dec(&host->h_count);
BUG_ON(atomic_read(&host->h_count) < 0);
}
}
......@@ -283,7 +286,7 @@ nlm_shutdown_hosts(void)
for (i = 0; i < NLM_HOST_NRHASH; i++) {
for (host = nlm_hosts[i]; host; host = host->h_next) {
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);
}
}
......@@ -314,10 +317,10 @@ nlm_gc_hosts(void)
for (i = 0; i < NLM_HOST_NRHASH; i++) {
q = &nlm_hosts[i];
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)) {
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);
q = &host->h_next;
continue;
......@@ -336,6 +339,7 @@ nlm_gc_hosts(void)
rpc_destroy_client(host->h_rpcclnt);
}
}
BUG_ON(!list_empty(&host->h_lockowners));
kfree(host);
nrhosts--;
}
......
......@@ -52,10 +52,25 @@ struct nlm_host {
wait_queue_head_t h_gracewait; /* wait while reclaiming */
u32 h_state; /* pseudo-state counter */
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 */
unsigned long h_nextrebind; /* next portmap call */
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 @@
#include <linux/list.h>
#include <linux/nfs.h>
struct nlm_lockowner;
/*
* NFS lock info
*/
struct nfs_lock_info {
u32 state;
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