diff options
author | Marc Eshel <eshel@almaden.ibm.com> | 2006-11-14 16:37:25 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2007-05-06 20:38:50 -0400 |
commit | 586759f03e2e9031ac5589912a51a909ed53c30a (patch) | |
tree | 5c788be0c13fb6d0baf3824e29a6bb6b195bf61a | |
parent | 1a8322b2b02071b0c7ac37a28357b93e6362f13e (diff) |
gfs2: nfs lock support for gfs2
Add NFS lock support to GFS2.
Signed-off-by: Marc Eshel <eshel@almaden.ibm.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Acked-by: Steven Whitehouse <swhiteho@redhat.com>
-rw-r--r-- | fs/gfs2/locking/dlm/plock.c | 109 | ||||
-rw-r--r-- | fs/gfs2/ops_file.c | 5 |
2 files changed, 104 insertions, 10 deletions
diff --git a/fs/gfs2/locking/dlm/plock.c b/fs/gfs2/locking/dlm/plock.c index 1dd4215b83d0..f82495e18c2d 100644 --- a/fs/gfs2/locking/dlm/plock.c +++ b/fs/gfs2/locking/dlm/plock.c @@ -25,6 +25,15 @@ struct plock_op { struct gdlm_plock_info info; }; +struct plock_xop { + struct plock_op xop; + void *callback; + void *fl; + void *file; + struct file_lock flc; +}; + + static inline void set_version(struct gdlm_plock_info *info) { info->version[0] = GDLM_PLOCK_VERSION_MAJOR; @@ -64,12 +73,14 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, { struct gdlm_ls *ls = lockspace; struct plock_op *op; + struct plock_xop *xop; int rv; - op = kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) + xop = kzalloc(sizeof(*xop), GFP_KERNEL); + if (!xop) return -ENOMEM; + op = &xop->xop; op->info.optype = GDLM_PLOCK_OP_LOCK; op->info.pid = fl->fl_pid; op->info.ex = (fl->fl_type == F_WRLCK); @@ -79,9 +90,21 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, op->info.start = fl->fl_start; op->info.end = fl->fl_end; op->info.owner = (__u64)(long) fl->fl_owner; + if (fl->fl_lmops && fl->fl_lmops->fl_grant) { + xop->callback = fl->fl_lmops->fl_grant; + locks_init_lock(&xop->flc); + locks_copy_lock(&xop->flc, fl); + xop->fl = fl; + xop->file = file; + } else + xop->callback = NULL; send_op(op); - wait_event(recv_wq, (op->done != 0)); + + if (xop->callback == NULL) + wait_event(recv_wq, (op->done != 0)); + else + return -EINPROGRESS; spin_lock(&ops_lock); if (!list_empty(&op->list)) { @@ -99,7 +122,63 @@ int gdlm_plock(void *lockspace, struct lm_lockname *name, (unsigned long long)name->ln_number); } - kfree(op); + kfree(xop); + return rv; +} + +/* Returns failure iff a succesful lock operation should be canceled */ +static int gdlm_plock_callback(struct plock_op *op) +{ + struct file *file; + struct file_lock *fl; + struct file_lock *flc; + int (*notify)(void *, void *, int) = NULL; + struct plock_xop *xop = (struct plock_xop *)op; + int rv = 0; + + spin_lock(&ops_lock); + if (!list_empty(&op->list)) { + printk(KERN_INFO "plock op on list\n"); + list_del(&op->list); + } + spin_unlock(&ops_lock); + + /* check if the following 2 are still valid or make a copy */ + file = xop->file; + flc = &xop->flc; + fl = xop->fl; + notify = xop->callback; + + if (op->info.rv) { + notify(flc, NULL, op->info.rv); + goto out; + } + + /* got fs lock; bookkeep locally as well: */ + flc->fl_flags &= ~FL_SLEEP; + if (posix_lock_file(file, flc, NULL)) { + /* + * This can only happen in the case of kmalloc() failure. + * The filesystem's own lock is the authoritative lock, + * so a failure to get the lock locally is not a disaster. + * As long as GFS cannot reliably cancel locks (especially + * in a low-memory situation), we're better off ignoring + * this failure than trying to recover. + */ + log_error("gdlm_plock: vfs lock error file %p fl %p", + file, fl); + } + + rv = notify(flc, NULL, 0); + if (rv) { + /* XXX: We need to cancel the fs lock here: */ + printk("gfs2 lock granted after lock request failed;" + " dangling lock!\n"); + goto out; + } + +out: + kfree(xop); return rv; } @@ -138,6 +217,9 @@ int gdlm_punlock(void *lockspace, struct lm_lockname *name, rv = op->info.rv; + if (rv == -ENOENT) + rv = 0; + kfree(op); return rv; } @@ -161,6 +243,7 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, op->info.start = fl->fl_start; op->info.end = fl->fl_end; + send_op(op); wait_event(recv_wq, (op->done != 0)); @@ -173,9 +256,10 @@ int gdlm_plock_get(void *lockspace, struct lm_lockname *name, rv = op->info.rv; - if (rv == 0) - fl->fl_type = F_UNLCK; - else if (rv > 0) { + fl->fl_type = F_UNLCK; + if (rv == -ENOENT) + rv = 0; + else if (rv == 0 && op->info.pid != fl->fl_pid) { fl->fl_type = (op->info.ex) ? F_WRLCK : F_RDLCK; fl->fl_pid = op->info.pid; fl->fl_start = op->info.start; @@ -243,9 +327,14 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count, } spin_unlock(&ops_lock); - if (found) - wake_up(&recv_wq); - else + if (found) { + struct plock_xop *xop; + xop = (struct plock_xop *)op; + if (xop->callback) + count = gdlm_plock_callback(op); + else + wake_up(&recv_wq); + } else printk(KERN_INFO "gdlm dev_write no op %x %llx\n", info.fsid, (unsigned long long)info.number); return count; diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 48b248d7c823..329c4dcdecdb 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -520,6 +520,11 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl) } } + if (cmd == F_CANCELLK) { + /* Hack: */ + cmd = F_SETLK; + fl->fl_type = F_UNLCK; + } if (IS_GETLK(cmd)) return gfs2_lm_plock_get(sdp, &name, file, fl); else if (fl->fl_type == F_UNLCK) |