]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
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.
  ...

15 files changed:
fs/nfs/client.c
fs/nfs/flexfilelayout/flexfilelayout.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/nfs42proc.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/pagelist.c
fs/nfs/pnfs.c
fs/nfs/write.c
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
net/sunrpc/backchannel_rqst.c
net/sunrpc/clnt.c
net/sunrpc/xprtsock.c

index ecebb406cc1aec554ce780f2c3680eddc351e8da..4a90c9bb31357305ed6f38166bb0e9afabaae953 100644 (file)
@@ -775,7 +775,7 @@ static int nfs_init_server(struct nfs_server *server,
        server->options = data->options;
        server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
                NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
-               NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME|NFS_CAP_CHANGE_ATTR;
+               NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
 
        if (data->rsize)
                server->rsize = nfs_block_size(data->rsize, NULL);
index c12951b9551eab8b0394ed2aa218cf15ce6f2c39..b3289d701eea21623f4081fee1b2807e3e2f4b3a 100644 (file)
@@ -1852,7 +1852,7 @@ ff_layout_mirror_prepare_stats(struct nfs42_layoutstat_args *args,
        struct nfs42_layoutstat_devinfo *devinfo;
        int i;
 
-       for (i = 0; i <= FF_LAYOUT_MIRROR_COUNT(pls); i++) {
+       for (i = 0; i < FF_LAYOUT_MIRROR_COUNT(pls); i++) {
                if (*dev_count >= dev_limit)
                        break;
                mirror = FF_LAYOUT_COMP(pls, i);
index b77b328a06d74f0124d2a65b51fac0fc21fbd692..0adc7d245b3dd838e32371920e23d9dda5071ee0 100644 (file)
@@ -442,8 +442,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
                        nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
                if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
                        inode->i_version = fattr->change_attr;
-               else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
-                       nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);
+               else
+                       nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
+                               | NFS_INO_REVAL_PAGECACHE);
                if (fattr->valid & NFS_ATTR_FATTR_SIZE)
                        inode->i_size = nfs_size_to_loff_t(fattr->size);
                else
@@ -1244,9 +1245,11 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
        if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
                cur_size = i_size_read(inode);
                new_isize = nfs_size_to_loff_t(fattr->size);
-               if (cur_size != new_isize && nfsi->nrequests == 0)
+               if (cur_size != new_isize)
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
        }
+       if (nfsi->nrequests != 0)
+               invalid &= ~NFS_INO_REVAL_PAGECACHE;
 
        /* Have any file permissions changed? */
        if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO))
@@ -1684,13 +1687,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        invalid |= NFS_INO_INVALID_ATTR
                                | NFS_INO_INVALID_DATA
                                | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL
-                               | NFS_INO_REVAL_PAGECACHE;
+                               | NFS_INO_INVALID_ACL;
                        if (S_ISDIR(inode->i_mode))
                                nfs_force_lookup_revalidate(inode);
                        inode->i_version = fattr->change_attr;
                }
-       } else if (server->caps & NFS_CAP_CHANGE_ATTR)
+       } else
                nfsi->cache_validity |= save_cache_validity;
 
        if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
@@ -1717,7 +1719,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        if ((nfsi->nrequests == 0) || new_isize > cur_isize) {
                                i_size_write(inode, new_isize);
                                invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
-                               invalid &= ~NFS_INO_REVAL_PAGECACHE;
                        }
                        dprintk("NFS: isize change on server for file %s/%ld "
                                        "(%Ld to %Ld)\n",
index 7e3c4604bea8a6e6e92906b6c2096adb6df5ca3f..9b372b845f6a6ff06a4a035e2f6799d7d29cd8f7 100644 (file)
@@ -296,6 +296,22 @@ extern struct rpc_procinfo nfs4_procedures[];
 
 #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 @@ static inline void nfs4_label_free(void *label) {}
 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 */
index f486b80f927ab7204159852a9740900a6c73aec6..d731bbf974aaf1d4bf695c2cb57a99cc586e0258 100644 (file)
@@ -135,7 +135,7 @@ int nfs42_proc_deallocate(struct file *filep, loff_t offset, loff_t len)
        return err;
 }
 
-loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
+static loff_t _nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
 {
        struct inode *inode = file_inode(filep);
        struct nfs42_seek_args args = {
@@ -171,6 +171,23 @@ loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
        return vfs_setpos(filep, res.sr_offset, inode->i_sb->s_maxbytes);
 }
 
+loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence)
+{
+       struct nfs_server *server = NFS_SERVER(file_inode(filep));
+       struct nfs4_exception exception = { };
+       int err;
+
+       do {
+               err = _nfs42_proc_llseek(filep, offset, whence);
+               if (err == -ENOTSUPP)
+                       return -EOPNOTSUPP;
+               err = nfs4_handle_exception(server, err, &exception);
+       } while (exception.retry);
+
+       return err;
+}
+
+
 static void
 nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
 {
index 8bee93469617eeddffa0e56038deed9051221420..3acb1eb72930c40828bab90aeb27a3918f71138d 100644 (file)
@@ -467,7 +467,10 @@ static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp)
 
 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 @@ int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
                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 @@ 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 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        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))
@@ -1041,7 +1048,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        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:
@@ -1074,6 +1081,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        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 @@ static void nfs4_opendata_free(struct kref *kref)
                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 @@ static bool nfs_need_update_open_stateid(struct nfs4_state *state,
 
 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,
@@ -7571,13 +7584,8 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
                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 +7973,17 @@ static void nfs4_layoutreturn_release(void *calldata)
 {
        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 +8597,6 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
        .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 +8622,6 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .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 +8644,6 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
        .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
index 605840dc89cf9e28c173659af201aab109f9328d..f2e2ad8944617f679a4a85934f6a276d3665229d 100644 (file)
@@ -2191,25 +2191,35 @@ static void nfs41_handle_server_reboot(struct nfs_client *clp)
        }
 }
 
-static void nfs41_handle_state_revoked(struct nfs_client *clp)
+static void nfs41_handle_all_state_revoked(struct nfs_client *clp)
 {
        nfs4_reset_all_state(clp);
        dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
 }
 
+static void nfs41_handle_some_state_revoked(struct nfs_client *clp)
+{
+       nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
+       nfs4_schedule_state_manager(clp);
+
+       dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
+}
+
 static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp)
 {
-       /* This will need to handle layouts too */
-       nfs_expire_all_delegations(clp);
+       /* FIXME: For now, we destroy all layouts. */
+       pnfs_destroy_all_layouts(clp);
+       /* FIXME: For now, we test all delegations+open state+locks. */
+       nfs41_handle_some_state_revoked(clp);
        dprintk("%s: Recallable state revoked on server %s!\n", __func__,
                        clp->cl_hostname);
 }
 
 static void nfs41_handle_backchannel_fault(struct nfs_client *clp)
 {
-       nfs_expire_all_delegations(clp);
-       if (test_and_set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) == 0)
-               nfs4_schedule_state_manager(clp);
+       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+       nfs4_schedule_state_manager(clp);
+
        dprintk("%s: server %s declared a backchannel fault\n", __func__,
                        clp->cl_hostname);
 }
@@ -2231,10 +2241,11 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
 
        if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
                nfs41_handle_server_reboot(clp);
-       if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED |
-                           SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
+       if (flags & (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED))
+               nfs41_handle_all_state_revoked(clp);
+       if (flags & (SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED |
                            SEQ4_STATUS_ADMIN_STATE_REVOKED))
-               nfs41_handle_state_revoked(clp);
+               nfs41_handle_some_state_revoked(clp);
        if (flags & SEQ4_STATUS_LEASE_MOVED)
                nfs4_schedule_lease_moved_recovery(clp);
        if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
index 1da68d3b1edabdb78c60527f502af5f40d6cf69b..4984bbe55ff1eed1623df2196bc0de0de41a4304 100644 (file)
@@ -1100,8 +1100,6 @@ static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
                mirror->pg_base = 0;
                mirror->pg_recoalesce = 0;
 
-               desc->pg_moreio = 0;
-
                while (!list_empty(&head)) {
                        struct nfs_page *req;
 
@@ -1109,8 +1107,11 @@ static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
                        nfs_list_remove_request(req);
                        if (__nfs_pageio_add_request(desc, req))
                                continue;
-                       if (desc->pg_error < 0)
+                       if (desc->pg_error < 0) {
+                               list_splice_tail(&head, &mirror->pg_list);
+                               mirror->pg_recoalesce = 1;
                                return 0;
+                       }
                        break;
                }
        } while (mirror->pg_recoalesce);
index 0ba9a02c95664960f8c0f46ea97249bd8653fe16..70bf706b10904e156affe9dd4bea2ec9a17776c5 100644 (file)
@@ -352,7 +352,7 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
 {
        struct pnfs_layout_segment *s;
 
-       if (!test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags))
+       if (!test_and_clear_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags))
                return false;
 
        list_for_each_entry(s, &lo->plh_segs, pls_list)
@@ -362,6 +362,18 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
        return true;
 }
 
+static bool
+pnfs_prepare_layoutreturn(struct pnfs_layout_hdr *lo)
+{
+       if (test_and_set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
+               return false;
+       lo->plh_return_iomode = 0;
+       lo->plh_block_lgets++;
+       pnfs_get_layout_hdr(lo);
+       clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE, &lo->plh_flags);
+       return true;
+}
+
 static void pnfs_layoutreturn_before_put_lseg(struct pnfs_layout_segment *lseg,
                struct pnfs_layout_hdr *lo, struct inode *inode)
 {
@@ -372,17 +384,16 @@ static void pnfs_layoutreturn_before_put_lseg(struct pnfs_layout_segment *lseg,
        if (pnfs_layout_need_return(lo, lseg)) {
                nfs4_stateid stateid;
                enum pnfs_iomode iomode;
+               bool send;
 
                stateid = lo->plh_stateid;
                iomode = lo->plh_return_iomode;
-               /* decreased in pnfs_send_layoutreturn() */
-               lo->plh_block_lgets++;
-               lo->plh_return_iomode = 0;
+               send = pnfs_prepare_layoutreturn(lo);
                spin_unlock(&inode->i_lock);
-               pnfs_get_layout_hdr(lo);
-
-               /* Send an async layoutreturn so we dont deadlock */
-               pnfs_send_layoutreturn(lo, stateid, iomode, false);
+               if (send) {
+                       /* Send an async layoutreturn so we dont deadlock */
+                       pnfs_send_layoutreturn(lo, stateid, iomode, false);
+               }
        } else
                spin_unlock(&inode->i_lock);
 }
@@ -411,6 +422,10 @@ pnfs_put_lseg(struct pnfs_layout_segment *lseg)
                pnfs_layoutreturn_before_put_lseg(lseg, lo, inode);
 
        if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) {
+               if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags)) {
+                       spin_unlock(&inode->i_lock);
+                       return;
+               }
                pnfs_get_layout_hdr(lo);
                pnfs_layout_remove_lseg(lo, lseg);
                spin_unlock(&inode->i_lock);
@@ -451,6 +466,8 @@ pnfs_put_lseg_locked(struct pnfs_layout_segment *lseg)
                test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
        if (atomic_dec_and_test(&lseg->pls_refcount)) {
                struct pnfs_layout_hdr *lo = lseg->pls_layout;
+               if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags))
+                       return;
                pnfs_get_layout_hdr(lo);
                pnfs_layout_remove_lseg(lo, lseg);
                pnfs_free_lseg_async(lseg);
@@ -924,6 +941,7 @@ void pnfs_clear_layoutreturn_waitbit(struct pnfs_layout_hdr *lo)
        clear_bit_unlock(NFS_LAYOUT_RETURN, &lo->plh_flags);
        smp_mb__after_atomic();
        wake_up_bit(&lo->plh_flags, NFS_LAYOUT_RETURN);
+       rpc_wake_up(&NFS_SERVER(lo->plh_inode)->roc_rpcwaitq);
 }
 
 static int
@@ -978,6 +996,7 @@ _pnfs_return_layout(struct inode *ino)
        LIST_HEAD(tmp_list);
        nfs4_stateid stateid;
        int status = 0, empty;
+       bool send;
 
        dprintk("NFS: %s for inode %lu\n", __func__, ino->i_ino);
 
@@ -1007,17 +1026,18 @@ _pnfs_return_layout(struct inode *ino)
        /* Don't send a LAYOUTRETURN if list was initially empty */
        if (empty) {
                spin_unlock(&ino->i_lock);
-               pnfs_put_layout_hdr(lo);
                dprintk("NFS: %s no layout segments to return\n", __func__);
-               goto out;
+               goto out_put_layout_hdr;
        }
 
        set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
-       lo->plh_block_lgets++;
+       send = pnfs_prepare_layoutreturn(lo);
        spin_unlock(&ino->i_lock);
        pnfs_free_lseg_list(&tmp_list);
-
-       status = pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, true);
+       if (send)
+               status = pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, true);
+out_put_layout_hdr:
+       pnfs_put_layout_hdr(lo);
 out:
        dprintk("<-- %s status: %d\n", __func__, status);
        return status;
@@ -1097,13 +1117,9 @@ bool pnfs_roc(struct inode *ino)
 out_noroc:
        if (lo) {
                stateid = lo->plh_stateid;
-               layoutreturn =
-                       test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE,
-                                          &lo->plh_flags);
-               if (layoutreturn) {
-                       lo->plh_block_lgets++;
-                       pnfs_get_layout_hdr(lo);
-               }
+               if (test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE,
+                                          &lo->plh_flags))
+                       layoutreturn = pnfs_prepare_layoutreturn(lo);
        }
        spin_unlock(&ino->i_lock);
        if (layoutreturn) {
@@ -1146,15 +1162,18 @@ bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task)
        struct pnfs_layout_segment *lseg;
        nfs4_stateid stateid;
        u32 current_seqid;
-       bool found = false, layoutreturn = false;
+       bool layoutreturn = false;
 
        spin_lock(&ino->i_lock);
-       list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list)
-               if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) {
-                       rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
-                       found = true;
-                       goto out;
-               }
+       list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list) {
+               if (!test_bit(NFS_LSEG_ROC, &lseg->pls_flags))
+                       continue;
+               if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags))
+                       continue;
+               rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
+               spin_unlock(&ino->i_lock);
+               return true;
+       }
        lo = nfsi->layout;
        current_seqid = be32_to_cpu(lo->plh_stateid.seqid);
 
@@ -1162,23 +1181,19 @@ bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task)
         * a barrier, we choose the worst-case barrier.
         */
        *barrier = current_seqid + atomic_read(&lo->plh_outstanding);
-out:
-       if (!found) {
-               stateid = lo->plh_stateid;
-               layoutreturn =
-                       test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE,
-                                          &lo->plh_flags);
-               if (layoutreturn) {
-                       lo->plh_block_lgets++;
-                       pnfs_get_layout_hdr(lo);
-               }
-       }
+       stateid = lo->plh_stateid;
+       if (test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE,
+                                          &lo->plh_flags))
+               layoutreturn = pnfs_prepare_layoutreturn(lo);
+       if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
+               rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
+
        spin_unlock(&ino->i_lock);
        if (layoutreturn) {
-               rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
                pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, false);
+               return true;
        }
-       return found;
+       return false;
 }
 
 /*
@@ -1695,7 +1710,6 @@ void pnfs_error_mark_layout_for_return(struct inode *inode,
        spin_lock(&inode->i_lock);
        /* set failure bit so that pnfs path will be retried later */
        pnfs_layout_set_fail_bit(lo, iomode);
-       set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags);
        if (lo->plh_return_iomode == 0)
                lo->plh_return_iomode = range.iomode;
        else if (lo->plh_return_iomode != range.iomode)
@@ -2207,13 +2221,12 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync)
        if (ld->prepare_layoutcommit) {
                status = ld->prepare_layoutcommit(&data->args);
                if (status) {
+                       put_rpccred(data->cred);
                        spin_lock(&inode->i_lock);
                        set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags);
                        if (end_pos > nfsi->layout->plh_lwb)
                                nfsi->layout->plh_lwb = end_pos;
-                       spin_unlock(&inode->i_lock);
-                       put_rpccred(data->cred);
-                       goto clear_layoutcommitting;
+                       goto out_unlock;
                }
        }
 
index 65869ca9c851dbf4f0b289ca84865a018c2b6e57..75a35a1afa7944d4ac54bd94994cddf1fd05ab54 100644 (file)
@@ -1379,24 +1379,27 @@ static void nfs_writeback_check_extend(struct nfs_pgio_header *hdr,
 {
        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);
index f91b5ade30c98fe8b03d06bc40deb0c434edb633..874b77228fb96285fb2024f07fa99d1ed7dd6dda 100644 (file)
@@ -292,9 +292,12 @@ static inline void nfs_mark_for_revalidate(struct inode *inode)
        struct nfs_inode *nfsi = NFS_I(inode);
 
        spin_lock(&inode->i_lock);
-       nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
+       nfsi->cache_validity |= NFS_INO_INVALID_ATTR |
+                               NFS_INO_REVAL_PAGECACHE |
+                               NFS_INO_INVALID_ACCESS |
+                               NFS_INO_INVALID_ACL;
        if (S_ISDIR(inode->i_mode))
-               nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
+               nfsi->cache_validity |= NFS_INO_INVALID_DATA;
        spin_unlock(&inode->i_lock);
 }
 
index a2ea1491d3dfc487611445490fb10adf9972d777..20bc8e51b16124496326274e84fc12d61996a4fd 100644 (file)
@@ -220,7 +220,7 @@ struct nfs_server {
 #define NFS_CAP_SYMLINKS       (1U << 2)
 #define NFS_CAP_ACLS           (1U << 3)
 #define NFS_CAP_ATOMIC_OPEN    (1U << 4)
-#define NFS_CAP_CHANGE_ATTR    (1U << 5)
+/* #define NFS_CAP_CHANGE_ATTR (1U << 5) */
 #define NFS_CAP_FILEID         (1U << 6)
 #define NFS_CAP_MODE           (1U << 7)
 #define NFS_CAP_NLINK          (1U << 8)
index 9825ff0f91d6c0bde819105f639cae21883bbfad..6255d141133bb0ccabc195c8b4d9bce8cbe39c51 100644 (file)
@@ -240,8 +240,8 @@ static struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt, __be32 xid)
                req = xprt_alloc_bc_req(xprt, GFP_ATOMIC);
                if (!req)
                        goto not_found;
-               /* Note: this 'free' request adds it to xprt->bc_pa_list */
-               xprt_free_bc_request(req);
+               list_add_tail(&req->rq_bc_pa_list, &xprt->bc_pa_list);
+               xprt->bc_alloc_count++;
        }
        req = list_first_entry(&xprt->bc_pa_list, struct rpc_rqst,
                                rq_bc_pa_list);
@@ -336,7 +336,7 @@ void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied)
 
        spin_lock(&xprt->bc_pa_lock);
        list_del(&req->rq_bc_pa_list);
-       xprt->bc_alloc_count--;
+       xprt_dec_alloc_count(xprt, 1);
        spin_unlock(&xprt->bc_pa_lock);
 
        req->rq_private_buf.len = copied;
index cbc6af923dd1cb0baabc95161989133150269d4f..23608eb0ded2c94e363ed06e35384b08417e04ed 100644 (file)
@@ -1902,6 +1902,7 @@ call_transmit_status(struct rpc_task *task)
 
        switch (task->tk_status) {
        case -EAGAIN:
+       case -ENOBUFS:
                break;
        default:
                dprint_status(task);
@@ -1928,7 +1929,6 @@ call_transmit_status(struct rpc_task *task)
        case -ECONNABORTED:
        case -EADDRINUSE:
        case -ENOTCONN:
-       case -ENOBUFS:
        case -EPIPE:
                rpc_task_force_reencode(task);
        }
@@ -2057,12 +2057,13 @@ call_status(struct rpc_task *task)
        case -ECONNABORTED:
                rpc_force_rebind(clnt);
        case -EADDRINUSE:
-       case -ENOBUFS:
                rpc_delay(task, 3*HZ);
        case -EPIPE:
        case -ENOTCONN:
                task->tk_action = call_bind;
                break;
+       case -ENOBUFS:
+               rpc_delay(task, HZ>>2);
        case -EAGAIN:
                task->tk_action = call_transmit;
                break;
index e193c2b5476b3a83973e9799e2e826fdcd2b842c..0030376327b77f0a08d4af887286a0616a60069e 100644 (file)
@@ -527,6 +527,10 @@ static int xs_local_send_request(struct rpc_task *task)
                              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;
@@ -539,6 +543,7 @@ static int xs_local_send_request(struct rpc_task *task)
 
        switch (status) {
        case -ENOBUFS:
+               break;
        case -EAGAIN:
                status = xs_nospace(task);
                break;
@@ -589,6 +594,9 @@ static int xs_udp_send_request(struct rpc_task *task)
        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 @@ static int xs_tcp_send_request(struct rpc_task *task)
                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;
@@ -681,18 +686,21 @@ static int xs_tcp_send_request(struct rpc_task *task)
                        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;
@@ -703,6 +711,7 @@ static int xs_tcp_send_request(struct rpc_task *task)
        case -ECONNREFUSED:
        case -ENOTCONN:
        case -EADDRINUSE:
+       case -ENOBUFS:
        case -EPIPE:
                clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
        }