Merge tag 'nfs-for-4.2-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 28 Jul 2015 16:37:44 +0000 (09:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 28 Jul 2015 16:37:44 +0000 (09:37 -0700)
Pull NFS client bugfixes from Trond Myklebust:
 "Highlights include:

  Stable patches:
   - Fix a situation where the client uses the wrong (zero) stateid.
   - Fix a memory leak in nfs_do_recoalesce

  Bugfixes:
   - Plug a memory leak when ->prepare_layoutcommit fails
   - Fix an Oops in the NFSv4 open code
   - Fix a backchannel deadlock
   - Fix a livelock in sunrpc when sendmsg fails due to low memory
     availability
   - Don't revalidate the mapping if both size and change attr are up to
     date
   - Ensure we don't miss a file extension when doing pNFS
   - Several fixes to handle NFSv4.1 sequence operation status bits
     correctly
   - Several pNFS layout return bugfixes"

* tag 'nfs-for-4.2-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (28 commits)
  nfs: Fix an oops caused by using other thread's stack space in ASYNC mode
  nfs: plug memory leak when ->prepare_layoutcommit fails
  SUNRPC: Report TCP errors to the caller
  sunrpc: translate -EAGAIN to -ENOBUFS when socket is writable.
  NFSv4.2: handle NFS-specific llseek errors
  NFS: Don't clear desc->pg_moreio in nfs_do_recoalesce()
  NFS: Fix a memory leak in nfs_do_recoalesce
  NFS: nfs_mark_for_revalidate should always set NFS_INO_REVAL_PAGECACHE
  NFS: Remove the "NFS_CAP_CHANGE_ATTR" capability
  NFS: Set NFS_INO_REVAL_PAGECACHE if the change attribute is uninitialised
  NFS: Don't revalidate the mapping if both size and change attr are up to date
  NFSv4/pnfs: Ensure we don't miss a file extension
  NFSv4: We must set NFS_OPEN_STATE flag in nfs_resync_open_stateid_locked
  SUNRPC: xprt_complete_bc_request must also decrement the free slot count
  SUNRPC: Fix a backchannel deadlock
  pNFS: Don't throw out valid layout segments
  pNFS: pnfs_roc_drain() fix a race with open
  pNFS: Fix races between return-on-close and layoutreturn.
  pNFS: pnfs_roc_drain should return 'true' when sleeping
  pNFS: Layoutreturn must invalidate all existing layout segments.
  ...

1  2 
fs/nfs/internal.h
fs/nfs/nfs4proc.c
fs/nfs/write.c
net/sunrpc/xprtsock.c

diff --combined fs/nfs/internal.h
index 7e3c4604bea8a6e6e92906b6c2096adb6df5ca3f,797013822765fa8bdf8815105b01878383aa4197..9b372b845f6a6ff06a4a035e2f6799d7d29cd8f7
@@@ -296,6 -296,22 +296,22 @@@ extern struct rpc_procinfo nfs4_procedu
  
  #ifdef CONFIG_NFS_V4_SECURITY_LABEL
  extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags);
+ static inline struct nfs4_label *
+ nfs4_label_copy(struct nfs4_label *dst, struct nfs4_label *src)
+ {
+       if (!dst || !src)
+               return NULL;
+       if (src->len > NFS4_MAXLABELLEN)
+               return NULL;
+       dst->lfs = src->lfs;
+       dst->pi = src->pi;
+       dst->len = src->len;
+       memcpy(dst->label, src->label, src->len);
+       return dst;
+ }
  static inline void nfs4_label_free(struct nfs4_label *label)
  {
        if (label) {
@@@ -316,6 -332,11 +332,11 @@@ static inline void nfs4_label_free(voi
  static inline void nfs_zap_label_cache_locked(struct nfs_inode *nfsi)
  {
  }
+ static inline struct nfs4_label *
+ nfs4_label_copy(struct nfs4_label *dst, struct nfs4_label *src)
+ {
+       return NULL;
+ }
  #endif /* CONFIG_NFS_V4_SECURITY_LABEL */
  
  /* proc.c */
@@@ -607,7 -628,7 +628,7 @@@ void nfs_mark_page_unstable(struct pag
        struct inode *inode = page_file_mapping(page)->host;
  
        inc_zone_page_state(page, NR_UNSTABLE_NFS);
 -      inc_bdi_stat(inode_to_bdi(inode), BDI_RECLAIMABLE);
 +      inc_wb_stat(&inode_to_bdi(inode)->wb, WB_RECLAIMABLE);
         __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
  }
  
diff --combined fs/nfs/nfs4proc.c
index 8bee93469617eeddffa0e56038deed9051221420,e94a964e9b4ff2dc987d4bd4a49210cc46044d55..3acb1eb72930c40828bab90aeb27a3918f71138d
@@@ -467,7 -467,10 +467,10 @@@ static void do_renew_lease(struct nfs_c
  
  static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
  {
-       do_renew_lease(server->nfs_client, timestamp);
+       struct nfs_client *clp = server->nfs_client;
+       if (!nfs4_has_session(clp))
+               do_renew_lease(clp, timestamp);
  }
  
  struct nfs4_call_sync_data {
@@@ -616,8 -619,7 +619,7 @@@ int nfs41_sequence_done(struct rpc_tas
                clp = session->clp;
                do_renew_lease(clp, res->sr_timestamp);
                /* Check sequence flags */
-               if (res->sr_status_flags != 0)
-                       nfs4_schedule_lease_recovery(clp);
+               nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
                nfs41_update_target_slotid(slot->table, slot, res);
                break;
        case 1:
@@@ -910,6 -912,7 +912,7 @@@ struct nfs4_opendata 
        struct nfs_open_confirmres c_res;
        struct nfs4_string owner_name;
        struct nfs4_string group_name;
+       struct nfs4_label *a_label;
        struct nfs_fattr f_attr;
        struct nfs4_label *f_label;
        struct dentry *dir;
@@@ -1013,6 -1016,10 +1016,10 @@@ static struct nfs4_opendata *nfs4_opend
        if (IS_ERR(p->f_label))
                goto err_free_p;
  
+       p->a_label = nfs4_label_alloc(server, gfp_mask);
+       if (IS_ERR(p->a_label))
+               goto err_free_f;
        alloc_seqid = server->nfs_client->cl_mvops->alloc_seqid;
        p->o_arg.seqid = alloc_seqid(&sp->so_seqid, gfp_mask);
        if (IS_ERR(p->o_arg.seqid))
        p->o_arg.server = server;
        p->o_arg.bitmask = nfs4_bitmask(server, label);
        p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
-       p->o_arg.label = label;
+       p->o_arg.label = nfs4_label_copy(p->a_label, label);
        p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
        switch (p->o_arg.claim) {
        case NFS4_OPEN_CLAIM_NULL:
        return p;
  
  err_free_label:
+       nfs4_label_free(p->a_label);
+ err_free_f:
        nfs4_label_free(p->f_label);
  err_free_p:
        kfree(p);
@@@ -1093,6 -1102,7 +1102,7 @@@ static void nfs4_opendata_free(struct k
                nfs4_put_open_state(p->state);
        nfs4_put_state_owner(p->owner);
  
+       nfs4_label_free(p->a_label);
        nfs4_label_free(p->f_label);
  
        dput(p->dir);
@@@ -1198,12 -1208,15 +1208,15 @@@ static bool nfs_need_update_open_statei
  
  static void nfs_resync_open_stateid_locked(struct nfs4_state *state)
  {
+       if (!(state->n_wronly || state->n_rdonly || state->n_rdwr))
+               return;
        if (state->n_wronly)
                set_bit(NFS_O_WRONLY_STATE, &state->flags);
        if (state->n_rdonly)
                set_bit(NFS_O_RDONLY_STATE, &state->flags);
        if (state->n_rdwr)
                set_bit(NFS_O_RDWR_STATE, &state->flags);
+       set_bit(NFS_OPEN_STATE, &state->flags);
  }
  
  static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
@@@ -5439,15 -5452,15 +5452,15 @@@ static int nfs4_proc_getlk(struct nfs4_
        return err;
  }
  
 -static int do_vfs_lock(struct file *file, struct file_lock *fl)
 +static int do_vfs_lock(struct inode *inode, struct file_lock *fl)
  {
        int res = 0;
        switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
                case FL_POSIX:
 -                      res = posix_lock_file_wait(file, fl);
 +                      res = posix_lock_inode_wait(inode, fl);
                        break;
                case FL_FLOCK:
 -                      res = flock_lock_file_wait(file, fl);
 +                      res = flock_lock_inode_wait(inode, fl);
                        break;
                default:
                        BUG();
@@@ -5484,6 -5497,7 +5497,6 @@@ static struct nfs4_unlockdata *nfs4_all
        atomic_inc(&lsp->ls_count);
        /* Ensure we don't close file until we're done freeing locks! */
        p->ctx = get_nfs_open_context(ctx);
 -      get_file(fl->fl_file);
        memcpy(&p->fl, fl, sizeof(p->fl));
        p->server = NFS_SERVER(inode);
        return p;
@@@ -5495,6 -5509,7 +5508,6 @@@ static void nfs4_locku_release_calldata
        nfs_free_seqid(calldata->arg.seqid);
        nfs4_put_lock_state(calldata->lsp);
        put_nfs_open_context(calldata->ctx);
 -      fput(calldata->fl.fl_file);
        kfree(calldata);
  }
  
@@@ -5507,7 -5522,7 +5520,7 @@@ static void nfs4_locku_done(struct rpc_
        switch (task->tk_status) {
                case 0:
                        renew_lease(calldata->server, calldata->timestamp);
 -                      do_vfs_lock(calldata->fl.fl_file, &calldata->fl);
 +                      do_vfs_lock(calldata->lsp->ls_state->inode, &calldata->fl);
                        if (nfs4_update_lock_stateid(calldata->lsp,
                                        &calldata->res.stateid))
                                break;
@@@ -5615,7 -5630,7 +5628,7 @@@ static int nfs4_proc_unlck(struct nfs4_
        mutex_lock(&sp->so_delegreturn_mutex);
        /* Exclude nfs4_reclaim_open_stateid() - note nesting! */
        down_read(&nfsi->rwsem);
 -      if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
 +      if (do_vfs_lock(inode, request) == -ENOENT) {
                up_read(&nfsi->rwsem);
                mutex_unlock(&sp->so_delegreturn_mutex);
                goto out;
@@@ -5756,7 -5771,7 +5769,7 @@@ static void nfs4_lock_done(struct rpc_t
                                data->timestamp);
                if (data->arg.new_lock) {
                        data->fl.fl_flags &= ~(FL_SLEEP | FL_ACCESS);
 -                      if (do_vfs_lock(data->fl.fl_file, &data->fl) < 0) {
 +                      if (do_vfs_lock(lsp->ls_state->inode, &data->fl) < 0) {
                                rpc_restart_call_prepare(task);
                                break;
                        }
@@@ -5998,7 -6013,7 +6011,7 @@@ static int _nfs4_proc_setlk(struct nfs4
        if (status != 0)
                goto out;
        request->fl_flags |= FL_ACCESS;
 -      status = do_vfs_lock(request->fl_file, request);
 +      status = do_vfs_lock(state->inode, request);
        if (status < 0)
                goto out;
        down_read(&nfsi->rwsem);
                /* Yes: cache locks! */
                /* ...but avoid races with delegation recall... */
                request->fl_flags = fl_flags & ~FL_SLEEP;
 -              status = do_vfs_lock(request->fl_file, request);
 +              status = do_vfs_lock(state->inode, request);
                up_read(&nfsi->rwsem);
                goto out;
        }
@@@ -7571,13 -7586,8 +7584,8 @@@ static int nfs4_proc_sequence(struct nf
                goto out;
        }
        ret = rpc_wait_for_completion_task(task);
-       if (!ret) {
-               struct nfs4_sequence_res *res = task->tk_msg.rpc_resp;
-               if (task->tk_status == 0)
-                       nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
+       if (!ret)
                ret = task->tk_status;
-       }
        rpc_put_task(task);
  out:
        dprintk("<-- %s status=%d\n", __func__, ret);
@@@ -7965,16 -7975,17 +7973,17 @@@ static void nfs4_layoutreturn_release(v
  {
        struct nfs4_layoutreturn *lrp = calldata;
        struct pnfs_layout_hdr *lo = lrp->args.layout;
+       LIST_HEAD(freeme);
  
        dprintk("--> %s\n", __func__);
        spin_lock(&lo->plh_inode->i_lock);
        if (lrp->res.lrs_present)
                pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
+       pnfs_mark_matching_lsegs_invalid(lo, &freeme, &lrp->args.range);
        pnfs_clear_layoutreturn_waitbit(lo);
-       clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, &lo->plh_flags);
-       rpc_wake_up(&NFS_SERVER(lo->plh_inode)->roc_rpcwaitq);
        lo->plh_block_lgets--;
        spin_unlock(&lo->plh_inode->i_lock);
+       pnfs_free_lseg_list(&freeme);
        pnfs_put_layout_hdr(lrp->args.layout);
        nfs_iput_and_deactive(lrp->inode);
        kfree(calldata);
@@@ -8588,7 -8599,6 +8597,6 @@@ static const struct nfs4_minor_version_
        .minor_version = 0,
        .init_caps = NFS_CAP_READDIRPLUS
                | NFS_CAP_ATOMIC_OPEN
-               | NFS_CAP_CHANGE_ATTR
                | NFS_CAP_POSIX_LOCK,
        .init_client = nfs40_init_client,
        .shutdown_client = nfs40_shutdown_client,
@@@ -8614,7 -8624,6 +8622,6 @@@ static const struct nfs4_minor_version_
        .minor_version = 1,
        .init_caps = NFS_CAP_READDIRPLUS
                | NFS_CAP_ATOMIC_OPEN
-               | NFS_CAP_CHANGE_ATTR
                | NFS_CAP_POSIX_LOCK
                | NFS_CAP_STATEID_NFSV41
                | NFS_CAP_ATOMIC_OPEN_V1,
@@@ -8637,7 -8646,6 +8644,6 @@@ static const struct nfs4_minor_version_
        .minor_version = 2,
        .init_caps = NFS_CAP_READDIRPLUS
                | NFS_CAP_ATOMIC_OPEN
-               | NFS_CAP_CHANGE_ATTR
                | NFS_CAP_POSIX_LOCK
                | NFS_CAP_STATEID_NFSV41
                | NFS_CAP_ATOMIC_OPEN_V1
diff --combined fs/nfs/write.c
index 65869ca9c851dbf4f0b289ca84865a018c2b6e57,0e6a2b8786b473dbb10509395d1727c97653ad4d..75a35a1afa7944d4ac54bd94994cddf1fd05ab54
@@@ -853,8 -853,7 +853,8 @@@ static voi
  nfs_clear_page_commit(struct page *page)
  {
        dec_zone_page_state(page, NR_UNSTABLE_NFS);
 -      dec_bdi_stat(inode_to_bdi(page_file_mapping(page)->host), BDI_RECLAIMABLE);
 +      dec_wb_stat(&inode_to_bdi(page_file_mapping(page)->host)->wb,
 +                  WB_RECLAIMABLE);
  }
  
  /* Called holding inode (/cinfo) lock */
@@@ -1379,24 -1378,27 +1379,27 @@@ static void nfs_writeback_check_extend(
  {
        struct nfs_pgio_args *argp = &hdr->args;
        struct nfs_pgio_res *resp = &hdr->res;
+       u64 size = argp->offset + resp->count;
  
        if (!(fattr->valid & NFS_ATTR_FATTR_SIZE))
+               fattr->size = size;
+       if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode)) {
+               fattr->valid &= ~NFS_ATTR_FATTR_SIZE;
                return;
-       if (argp->offset + resp->count != fattr->size)
-               return;
-       if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode))
+       }
+       if (size != fattr->size)
                return;
        /* Set attribute barrier */
        nfs_fattr_set_barrier(fattr);
+       /* ...and update size */
+       fattr->valid |= NFS_ATTR_FATTR_SIZE;
  }
  
  void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
  {
-       struct nfs_fattr *fattr = hdr->res.fattr;
+       struct nfs_fattr *fattr = &hdr->fattr;
        struct inode *inode = hdr->inode;
  
-       if (fattr == NULL)
-               return;
        spin_lock(&inode->i_lock);
        nfs_writeback_check_extend(hdr, fattr);
        nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
diff --combined net/sunrpc/xprtsock.c
index e193c2b5476b3a83973e9799e2e826fdcd2b842c,6a21368bdd8eb72313430ff192e582781f35023c..0030376327b77f0a08d4af887286a0616a60069e
@@@ -527,6 -527,10 +527,10 @@@ static int xs_local_send_request(struc
                              true, &sent);
        dprintk("RPC:       %s(%u) = %d\n",
                        __func__, xdr->len - req->rq_bytes_sent, status);
+       if (status == -EAGAIN && sock_writeable(transport->inet))
+               status = -ENOBUFS;
        if (likely(sent > 0) || status == 0) {
                req->rq_bytes_sent += sent;
                req->rq_xmit_bytes_sent += sent;
  
        switch (status) {
        case -ENOBUFS:
+               break;
        case -EAGAIN:
                status = xs_nospace(task);
                break;
@@@ -589,6 -594,9 +594,9 @@@ static int xs_udp_send_request(struct r
        if (status == -EPERM)
                goto process_status;
  
+       if (status == -EAGAIN && sock_writeable(transport->inet))
+               status = -ENOBUFS;
        if (sent > 0 || status == 0) {
                req->rq_xmit_bytes_sent += sent;
                if (sent >= req->rq_slen)
@@@ -669,9 -677,6 +677,6 @@@ static int xs_tcp_send_request(struct r
                dprintk("RPC:       xs_tcp_send_request(%u) = %d\n",
                                xdr->len - req->rq_bytes_sent, status);
  
-               if (unlikely(sent == 0 && status < 0))
-                       break;
                /* If we've sent the entire packet, immediately
                 * reset the count of bytes sent. */
                req->rq_bytes_sent += sent;
                        return 0;
                }
  
-               if (sent != 0)
-                       continue;
-               status = -EAGAIN;
-               break;
+               if (status < 0)
+                       break;
+               if (sent == 0) {
+                       status = -EAGAIN;
+                       break;
+               }
        }
+       if (status == -EAGAIN && sk_stream_is_writeable(transport->inet))
+               status = -ENOBUFS;
  
        switch (status) {
        case -ENOTSOCK:
                status = -ENOTCONN;
                /* Should we call xs_close() here? */
                break;
-       case -ENOBUFS:
        case -EAGAIN:
                status = xs_nospace(task);
                break;
        case -ECONNREFUSED:
        case -ENOTCONN:
        case -EADDRINUSE:
+       case -ENOBUFS:
        case -EPIPE:
                clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
        }
@@@ -3047,7 -3056,7 +3056,7 @@@ static int param_set_portnr(const char 
                        RPC_MAX_RESVPORT);
  }
  
 -static struct kernel_param_ops param_ops_portnr = {
 +static const struct kernel_param_ops param_ops_portnr = {
        .set = param_set_portnr,
        .get = param_get_uint,
  };
@@@ -3066,7 -3075,7 +3075,7 @@@ static int param_set_slot_table_size(co
                        RPC_MAX_SLOT_TABLE);
  }
  
 -static struct kernel_param_ops param_ops_slot_table_size = {
 +static const struct kernel_param_ops param_ops_slot_table_size = {
        .set = param_set_slot_table_size,
        .get = param_get_uint,
  };
@@@ -3082,7 -3091,7 +3091,7 @@@ static int param_set_max_slot_table_siz
                        RPC_MAX_SLOT_TABLE_LIMIT);
  }
  
 -static struct kernel_param_ops param_ops_max_slot_table_size = {
 +static const struct kernel_param_ops param_ops_max_slot_table_size = {
        .set = param_set_max_slot_table_size,
        .get = param_get_uint,
  };
This page took 0.040736 seconds and 5 git commands to generate.