NFS client bugfixes for Linux 4.2
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVt6RGAAoJEGcL54qWCgDyiDIP/2+fUM7Tc1llCxYbM2WLC6Ar 34v5yVwO96MqhI4L2mXB5FJvr4LP2/EZ4ZExMcf4ymT7pgJnjFK4nEv9IHUSy6xb ea+oS9GjvFSeGdkukJLRniNER5/ZG3GWkojlHNJCgByoIVRK4ISXF/qL9w2sedGw +5ejvjqie9NmBnBXMq8DRlU+kXhVYCF6E9qWATwUNK5Eq2eeQnDbA2w9ACSBVK3W LhCvZi0eBq7krSbHob018PmlQ0VPvmYwk5xL4d//FvcaNj/utk82VjAZCdKOK1sH qn8hcKgVeVko/3jwcUp6m3zAkKZ1IX/XaXJeHbosnKG/g0vy3hQirpa/g2iDTQ4H NXOSwcsd6syReZDZbQTxbvaSOp5ACxZAQKYLnlPerJ/hMpXDQCEAwyeAFKzEaKz4 FfF0VJF+30w9PJk3wgk2DF66xbYVfHyvrLtVcb/ki8gb91cH09i+nFFSSfHQBMLh +ciHg7rOyXnbXoCaW9fBvONz2sCYDwbHATmhpWWZIx/3UTDf5owxHFa3BFDgGKnD jyiPjMh6I3JUE+Qm1zwInsfsskBKRSl2BdJgTHBGY5ODuQGF/sogOmvgbrT7Ox3t kbL8nzCydqLixM+4aw61nYakZqgDsKNER5Ggr+lkv4AZ2dH6IeP2IZjuoHLLylvZ dyqHwpCjoUtmYAUr166U =wlUD -----END PGP SIGNATURE----- Merge tag 'nfs-for-4.2-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs 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. ...
This commit is contained in:
commit
d8132e08d2
15 changed files with 189 additions and 103 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
101
fs/nfs/pnfs.c
101
fs/nfs/pnfs.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue