nfsd: remove redundant assignment to pointer 'this'
[linux-2.6-microblaze.git] / fs / nfsd / nfs4proc.c
index f4ce93d..486c5db 100644 (file)
@@ -55,6 +55,13 @@ module_param(inter_copy_offload_enable, bool, 0644);
 MODULE_PARM_DESC(inter_copy_offload_enable,
                 "Enable inter server to server copy offload. Default: false");
 
+#ifdef CONFIG_NFSD_V4_2_INTER_SSC
+static int nfsd4_ssc_umount_timeout = 900000;          /* default to 15 mins */
+module_param(nfsd4_ssc_umount_timeout, int, 0644);
+MODULE_PARM_DESC(nfsd4_ssc_umount_timeout,
+               "idle msecs before unmount export from source server");
+#endif
+
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 #include <linux/security.h>
 
@@ -1165,6 +1172,81 @@ extern void nfs_sb_deactive(struct super_block *sb);
 
 #define NFSD42_INTERSSC_MOUNTOPS "vers=4.2,addr=%s,sec=sys"
 
+/*
+ * setup a work entry in the ssc delayed unmount list.
+ */
+static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
+               struct nfsd4_ssc_umount_item **retwork, struct vfsmount **ss_mnt)
+{
+       struct nfsd4_ssc_umount_item *ni = 0;
+       struct nfsd4_ssc_umount_item *work = NULL;
+       struct nfsd4_ssc_umount_item *tmp;
+       DEFINE_WAIT(wait);
+
+       *ss_mnt = NULL;
+       *retwork = NULL;
+       work = kzalloc(sizeof(*work), GFP_KERNEL);
+try_again:
+       spin_lock(&nn->nfsd_ssc_lock);
+       list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
+               if (strncmp(ni->nsui_ipaddr, ipaddr, sizeof(ni->nsui_ipaddr)))
+                       continue;
+               /* found a match */
+               if (ni->nsui_busy) {
+                       /*  wait - and try again */
+                       prepare_to_wait(&nn->nfsd_ssc_waitq, &wait,
+                               TASK_INTERRUPTIBLE);
+                       spin_unlock(&nn->nfsd_ssc_lock);
+
+                       /* allow 20secs for mount/unmount for now - revisit */
+                       if (signal_pending(current) ||
+                                       (schedule_timeout(20*HZ) == 0)) {
+                               kfree(work);
+                               return nfserr_eagain;
+                       }
+                       finish_wait(&nn->nfsd_ssc_waitq, &wait);
+                       goto try_again;
+               }
+               *ss_mnt = ni->nsui_vfsmount;
+               refcount_inc(&ni->nsui_refcnt);
+               spin_unlock(&nn->nfsd_ssc_lock);
+               kfree(work);
+
+               /* return vfsmount in ss_mnt */
+               return 0;
+       }
+       if (work) {
+               strncpy(work->nsui_ipaddr, ipaddr, sizeof(work->nsui_ipaddr));
+               refcount_set(&work->nsui_refcnt, 2);
+               work->nsui_busy = true;
+               list_add_tail(&work->nsui_list, &nn->nfsd_ssc_mount_list);
+               *retwork = work;
+       }
+       spin_unlock(&nn->nfsd_ssc_lock);
+       return 0;
+}
+
+static void nfsd4_ssc_update_dul_work(struct nfsd_net *nn,
+               struct nfsd4_ssc_umount_item *work, struct vfsmount *ss_mnt)
+{
+       /* set nsui_vfsmount, clear busy flag and wakeup waiters */
+       spin_lock(&nn->nfsd_ssc_lock);
+       work->nsui_vfsmount = ss_mnt;
+       work->nsui_busy = false;
+       wake_up_all(&nn->nfsd_ssc_waitq);
+       spin_unlock(&nn->nfsd_ssc_lock);
+}
+
+static void nfsd4_ssc_cancel_dul_work(struct nfsd_net *nn,
+               struct nfsd4_ssc_umount_item *work)
+{
+       spin_lock(&nn->nfsd_ssc_lock);
+       list_del(&work->nsui_list);
+       wake_up_all(&nn->nfsd_ssc_waitq);
+       spin_unlock(&nn->nfsd_ssc_lock);
+       kfree(work);
+}
+
 /*
  * Support one copy source server for now.
  */
@@ -1181,6 +1263,8 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
        char *ipaddr, *dev_name, *raw_data;
        int len, raw_len;
        __be32 status = nfserr_inval;
+       struct nfsd4_ssc_umount_item *work = NULL;
+       struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 
        naddr = &nss->u.nl4_addr;
        tmp_addrlen = rpc_uaddr2sockaddr(SVC_NET(rqstp), naddr->addr,
@@ -1229,12 +1313,24 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
                goto out_free_rawdata;
        snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
 
+       status = nfsd4_ssc_setup_dul(nn, ipaddr, &work, &ss_mnt);
+       if (status)
+               goto out_free_devname;
+       if (ss_mnt)
+               goto out_done;
+
        /* Use an 'internal' mount: SB_KERNMOUNT -> MNT_INTERNAL */
        ss_mnt = vfs_kern_mount(type, SB_KERNMOUNT, dev_name, raw_data);
        module_put(type->owner);
-       if (IS_ERR(ss_mnt))
+       if (IS_ERR(ss_mnt)) {
+               status = nfserr_nodev;
+               if (work)
+                       nfsd4_ssc_cancel_dul_work(nn, work);
                goto out_free_devname;
-
+       }
+       if (work)
+               nfsd4_ssc_update_dul_work(nn, work, ss_mnt);
+out_done:
        status = 0;
        *mount = ss_mnt;
 
@@ -1301,10 +1397,42 @@ static void
 nfsd4_cleanup_inter_ssc(struct vfsmount *ss_mnt, struct nfsd_file *src,
                        struct nfsd_file *dst)
 {
+       bool found = false;
+       long timeout;
+       struct nfsd4_ssc_umount_item *tmp;
+       struct nfsd4_ssc_umount_item *ni = NULL;
+       struct nfsd_net *nn = net_generic(dst->nf_net, nfsd_net_id);
+
        nfs42_ssc_close(src->nf_file);
-       fput(src->nf_file);
        nfsd_file_put(dst);
-       mntput(ss_mnt);
+       fput(src->nf_file);
+
+       if (!nn) {
+               mntput(ss_mnt);
+               return;
+       }
+       spin_lock(&nn->nfsd_ssc_lock);
+       timeout = msecs_to_jiffies(nfsd4_ssc_umount_timeout);
+       list_for_each_entry_safe(ni, tmp, &nn->nfsd_ssc_mount_list, nsui_list) {
+               if (ni->nsui_vfsmount->mnt_sb == ss_mnt->mnt_sb) {
+                       list_del(&ni->nsui_list);
+                       /*
+                        * vfsmount can be shared by multiple exports,
+                        * decrement refcnt. If the count drops to 1 it
+                        * will be unmounted when nsui_expire expires.
+                        */
+                       refcount_dec(&ni->nsui_refcnt);
+                       ni->nsui_expire = jiffies + timeout;
+                       list_add_tail(&ni->nsui_list, &nn->nfsd_ssc_mount_list);
+                       found = true;
+                       break;
+               }
+       }
+       spin_unlock(&nn->nfsd_ssc_lock);
+       if (!found) {
+               mntput(ss_mnt);
+               return;
+       }
 }
 
 #else /* CONFIG_NFSD_V4_2_INTER_SSC */
@@ -1375,7 +1503,8 @@ static const struct nfsd4_callback_ops nfsd4_cb_offload_ops = {
 
 static void nfsd4_init_copy_res(struct nfsd4_copy *copy, bool sync)
 {
-       copy->cp_res.wr_stable_how = NFS_UNSTABLE;
+       copy->cp_res.wr_stable_how =
+               copy->committed ? NFS_FILE_SYNC : NFS_UNSTABLE;
        copy->cp_synchronous = sync;
        gen_boot_verifier(&copy->cp_res.wr_verifier, copy->cp_clp->net);
 }
@@ -1386,6 +1515,7 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy)
        u64 bytes_total = copy->cp_count;
        u64 src_pos = copy->cp_src_pos;
        u64 dst_pos = copy->cp_dst_pos;
+       __be32 status;
 
        /* See RFC 7862 p.67: */
        if (bytes_total == 0)
@@ -1403,6 +1533,16 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy)
                src_pos += bytes_copied;
                dst_pos += bytes_copied;
        } while (bytes_total > 0 && !copy->cp_synchronous);
+       /* for a non-zero asynchronous copy do a commit of data */
+       if (!copy->cp_synchronous && copy->cp_res.wr_bytes_written > 0) {
+               down_write(&copy->nf_dst->nf_rwsem);
+               status = vfs_fsync_range(copy->nf_dst->nf_file,
+                                        copy->cp_dst_pos,
+                                        copy->cp_res.wr_bytes_written, 0);
+               up_write(&copy->nf_dst->nf_rwsem);
+               if (!status)
+                       copy->committed = true;
+       }
        return bytes_copied;
 }
 
@@ -1497,6 +1637,8 @@ do_callback:
        memcpy(&cb_copy->fh, &copy->fh, sizeof(copy->fh));
        nfsd4_init_cb(&cb_copy->cp_cb, cb_copy->cp_clp,
                        &nfsd4_cb_offload_ops, NFSPROC4_CLNT_CB_OFFLOAD);
+       trace_nfsd_cb_offload(copy->cp_clp, &copy->cp_res.cb_stateid,
+                             &copy->fh, copy->cp_count, copy->nfserr);
        nfsd4_run_cb(&cb_copy->cp_cb);
 out:
        if (!copy->cp_intra)
@@ -3232,7 +3374,7 @@ bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
 {
        struct nfsd4_compoundres *resp = rqstp->rq_resp;
        struct nfsd4_compoundargs *argp = rqstp->rq_argp;
-       struct nfsd4_op *this = &argp->ops[resp->opcnt - 1];
+       struct nfsd4_op *this;
        struct nfsd4_compound_state *cstate = &resp->cstate;
        struct nfs4_op_map *allow = &cstate->clp->cl_spo_must_allow;
        u32 opiter;