gfs2: nfs lock support for gfs2
authorMarc Eshel <eshel@almaden.ibm.com>
Tue, 14 Nov 2006 21:37:25 +0000 (16:37 -0500)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Mon, 7 May 2007 00:38:50 +0000 (20:38 -0400)
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>
fs/gfs2/locking/dlm/plock.c
fs/gfs2/ops_file.c

index 1dd4215..f82495e 100644 (file)
@@ -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;
index 48b248d..329c4dc 100644 (file)
@@ -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)