mirror of https://gitee.com/openkylin/linux.git
NFS client bugfixes for 4.8
Highlights include: Stable patches: - Fix a refcount leak in nfs_callback_up_net - Fix an Oopsable condition when the flexfile pNFS driver connection to the DS fails - Fix an Oopsable condition in NFSv4.1 server callback races - Ensure pNFS clients stop doing I/O to the DS if their lease has expired, as required by the NFSv4.1 protocol Bugfixes: - Fix potential looping in the NFSv4.x migration code - Patch series to close callback races for OPEN, LAYOUTGET and LAYOUTRETURN - Silence WARN_ON when NFSv4.1 over RDMA is in use - Fix a LAYOUTCOMMIT race in the pNFS/blocks client - Fix pNFS timeout issues when the DS fails -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJXxbnyAAoJEGcL54qWCgDykWoP/jqgBBR/cSaOtx+5m39wlf0P pTdQkgcpWnhBS90tKZtC6zfJ2DFVt8sUNVn9+mVzT4Q7TgEcAmENQ//s0igxHLbl bkXPvULydvD05Db8m1xmq2snj72tWbpg3CaA7nfx6yiP63k237QxhyNZVkmEQDur ynU8dPzmxRaSTQdVgatdS0zqx8sF47OFnXVxkV0ssBKORGsWj3yKDcs293NZNFAM Ztkih5oW1mm+BtWUQVNrjRnfZFG+PxAxWv090JM6wABDRbDHwSaKmwmI0kWRKXoH DHrj4i/Wzws65Fg5AyVPSRkF8YvHSVsLnw/FlwKKZFsrWjU6WtLdLSzgzwQ47x98 tQk/YGgNyiiD1cAcw+l0d3Ct1SO4AptNuisdJK0cn3iCdsbh6Y0eW6yRRtQY6jQI 8qOyMTT8fp9ooEQK+nMNOhJVVlsG0hbvWAt/uiiBdPhjAfVB0UFRuua/vNKUO7yv hJkDY9i7EkMXKACf5BCpBuvYdU7rwqp43K9x34029A5vFTKOhJZS4hnAIocDd/WF Hw7yqHdpkvI5RgFbBV5tmfZPyS65k8AzzTtT1QHKlH0qEtN2iMaXsXM9EzK5bKfW 85Cc6yzRk7NzDZKmZFs/T8zCYdzet48sCY7wVyOQjL0aIkIDNNcZhex+C1GuD1dp Ld0H5f9eZdwv/OAqJ8tm =U+XK -----END PGP SIGNATURE----- Merge tag 'nfs-for-4.8-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs Pull NFS client bugfixes from Trond Myklebust: "Highlights include: Stable patches: - Fix a refcount leak in nfs_callback_up_net - Fix an Oopsable condition when the flexfile pNFS driver connection to the DS fails - Fix an Oopsable condition in NFSv4.1 server callback races - Ensure pNFS clients stop doing I/O to the DS if their lease has expired, as required by the NFSv4.1 protocol Bugfixes: - Fix potential looping in the NFSv4.x migration code - Patch series to close callback races for OPEN, LAYOUTGET and LAYOUTRETURN - Silence WARN_ON when NFSv4.1 over RDMA is in use - Fix a LAYOUTCOMMIT race in the pNFS/blocks client - Fix pNFS timeout issues when the DS fails" * tag 'nfs-for-4.8-3' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: NFSv4.x: Fix a refcount leak in nfs_callback_up_net NFS4: Avoid migration loops pNFS/flexfiles: Fix an Oopsable condition when connection to the DS fails NFSv4.1: Remove obsolete and incorrrect assignment in nfs4_callback_sequence NFSv4.1: Close callback races for OPEN, LAYOUTGET and LAYOUTRETURN NFSv4.1: Defer bumping the slot sequence number until we free the slot NFSv4.1: Delay callback processing when there are referring triples NFSv4.1: Fix Oopsable condition in server callback races SUNRPC: Silence WARN_ON when NFSv4.1 over RDMA is in use pnfs/blocklayout: update last_write_offset atomically with extents pNFS: The client must not do I/O to the DS if it's lease has expired pNFS: Handle NFS4ERR_OLD_STATEID correctly in LAYOUTSTAT calls pNFS/flexfiles: Set reasonable default retrans values for the data channel NFS: Allow the mount option retrans=0 pNFS/flexfiles: Fix layoutstat periodic reporting
This commit is contained in:
commit
0cf21c6609
|
@ -346,7 +346,7 @@ static void bl_write_cleanup(struct work_struct *work)
|
|||
PAGE_SIZE - 1) & (loff_t)PAGE_MASK;
|
||||
|
||||
ext_tree_mark_written(bl, start >> SECTOR_SHIFT,
|
||||
(end - start) >> SECTOR_SHIFT);
|
||||
(end - start) >> SECTOR_SHIFT, end);
|
||||
}
|
||||
|
||||
pnfs_ld_write_done(hdr);
|
||||
|
|
|
@ -141,6 +141,7 @@ struct pnfs_block_layout {
|
|||
struct rb_root bl_ext_ro;
|
||||
spinlock_t bl_ext_lock; /* Protects list manipulation */
|
||||
bool bl_scsi_layout;
|
||||
u64 bl_lwb;
|
||||
};
|
||||
|
||||
static inline struct pnfs_block_layout *
|
||||
|
@ -182,7 +183,7 @@ int ext_tree_insert(struct pnfs_block_layout *bl,
|
|||
int ext_tree_remove(struct pnfs_block_layout *bl, bool rw, sector_t start,
|
||||
sector_t end);
|
||||
int ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
|
||||
sector_t len);
|
||||
sector_t len, u64 lwb);
|
||||
bool ext_tree_lookup(struct pnfs_block_layout *bl, sector_t isect,
|
||||
struct pnfs_block_extent *ret, bool rw);
|
||||
int ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg);
|
||||
|
|
|
@ -402,7 +402,7 @@ ext_tree_split(struct rb_root *root, struct pnfs_block_extent *be,
|
|||
|
||||
int
|
||||
ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
|
||||
sector_t len)
|
||||
sector_t len, u64 lwb)
|
||||
{
|
||||
struct rb_root *root = &bl->bl_ext_rw;
|
||||
sector_t end = start + len;
|
||||
|
@ -471,6 +471,8 @@ ext_tree_mark_written(struct pnfs_block_layout *bl, sector_t start,
|
|||
}
|
||||
}
|
||||
out:
|
||||
if (bl->bl_lwb < lwb)
|
||||
bl->bl_lwb = lwb;
|
||||
spin_unlock(&bl->bl_ext_lock);
|
||||
|
||||
__ext_put_deviceids(&tmp);
|
||||
|
@ -518,7 +520,7 @@ static __be32 *encode_scsi_range(struct pnfs_block_extent *be, __be32 *p)
|
|||
}
|
||||
|
||||
static int ext_tree_encode_commit(struct pnfs_block_layout *bl, __be32 *p,
|
||||
size_t buffer_size, size_t *count)
|
||||
size_t buffer_size, size_t *count, __u64 *lastbyte)
|
||||
{
|
||||
struct pnfs_block_extent *be;
|
||||
int ret = 0;
|
||||
|
@ -542,6 +544,8 @@ static int ext_tree_encode_commit(struct pnfs_block_layout *bl, __be32 *p,
|
|||
p = encode_block_extent(be, p);
|
||||
be->be_tag = EXTENT_COMMITTING;
|
||||
}
|
||||
*lastbyte = bl->bl_lwb - 1;
|
||||
bl->bl_lwb = 0;
|
||||
spin_unlock(&bl->bl_ext_lock);
|
||||
|
||||
return ret;
|
||||
|
@ -564,7 +568,7 @@ ext_tree_prepare_commit(struct nfs4_layoutcommit_args *arg)
|
|||
arg->layoutupdate_pages = &arg->layoutupdate_page;
|
||||
|
||||
retry:
|
||||
ret = ext_tree_encode_commit(bl, start_p + 1, buffer_size, &count);
|
||||
ret = ext_tree_encode_commit(bl, start_p + 1, buffer_size, &count, &arg->lastbytewritten);
|
||||
if (unlikely(ret)) {
|
||||
ext_tree_free_commitdata(arg, buffer_size);
|
||||
|
||||
|
|
|
@ -275,6 +275,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
|
|||
err_socks:
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
err_bind:
|
||||
nn->cb_users[minorversion]--;
|
||||
dprintk("NFS: Couldn't create callback socket: err = %d; "
|
||||
"net = %p\n", ret, net);
|
||||
return ret;
|
||||
|
|
|
@ -454,11 +454,8 @@ static bool referring_call_exists(struct nfs_client *clp,
|
|||
((u32 *)&rclist->rcl_sessionid.data)[3],
|
||||
ref->rc_sequenceid, ref->rc_slotid);
|
||||
|
||||
spin_lock(&tbl->slot_tbl_lock);
|
||||
status = (test_bit(ref->rc_slotid, tbl->used_slots) &&
|
||||
tbl->slots[ref->rc_slotid].seq_nr ==
|
||||
ref->rc_sequenceid);
|
||||
spin_unlock(&tbl->slot_tbl_lock);
|
||||
status = nfs4_slot_wait_on_seqid(tbl, ref->rc_slotid,
|
||||
ref->rc_sequenceid, HZ >> 1) < 0;
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
|
@ -487,7 +484,6 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
|
|||
goto out;
|
||||
|
||||
tbl = &clp->cl_session->bc_slot_table;
|
||||
slot = tbl->slots + args->csa_slotid;
|
||||
|
||||
/* Set up res before grabbing the spinlock */
|
||||
memcpy(&res->csr_sessionid, &args->csa_sessionid,
|
||||
|
|
|
@ -426,7 +426,7 @@ EXPORT_SYMBOL_GPL(nfs_mark_client_ready);
|
|||
* Initialise the timeout values for a connection
|
||||
*/
|
||||
void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
|
||||
unsigned int timeo, unsigned int retrans)
|
||||
int timeo, int retrans)
|
||||
{
|
||||
to->to_initval = timeo * HZ / 10;
|
||||
to->to_retries = retrans;
|
||||
|
@ -434,9 +434,9 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
|
|||
switch (proto) {
|
||||
case XPRT_TRANSPORT_TCP:
|
||||
case XPRT_TRANSPORT_RDMA:
|
||||
if (to->to_retries == 0)
|
||||
if (retrans == NFS_UNSPEC_RETRANS)
|
||||
to->to_retries = NFS_DEF_TCP_RETRANS;
|
||||
if (to->to_initval == 0)
|
||||
if (timeo == NFS_UNSPEC_TIMEO || to->to_retries == 0)
|
||||
to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10;
|
||||
if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
|
||||
to->to_initval = NFS_MAX_TCP_TIMEOUT;
|
||||
|
@ -449,9 +449,9 @@ void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
|
|||
to->to_exponential = 0;
|
||||
break;
|
||||
case XPRT_TRANSPORT_UDP:
|
||||
if (to->to_retries == 0)
|
||||
if (retrans == NFS_UNSPEC_RETRANS)
|
||||
to->to_retries = NFS_DEF_UDP_RETRANS;
|
||||
if (!to->to_initval)
|
||||
if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0)
|
||||
to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10;
|
||||
if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
|
||||
to->to_initval = NFS_MAX_UDP_TIMEOUT;
|
||||
|
|
|
@ -37,6 +37,7 @@ ff_layout_alloc_layout_hdr(struct inode *inode, gfp_t gfp_flags)
|
|||
if (ffl) {
|
||||
INIT_LIST_HEAD(&ffl->error_list);
|
||||
INIT_LIST_HEAD(&ffl->mirrors);
|
||||
ffl->last_report_time = ktime_get();
|
||||
return &ffl->generic_hdr;
|
||||
} else
|
||||
return NULL;
|
||||
|
@ -640,19 +641,18 @@ nfs4_ff_layoutstat_start_io(struct nfs4_ff_layout_mirror *mirror,
|
|||
{
|
||||
static const ktime_t notime = {0};
|
||||
s64 report_interval = FF_LAYOUTSTATS_REPORT_INTERVAL;
|
||||
struct nfs4_flexfile_layout *ffl = FF_LAYOUT_FROM_HDR(mirror->layout);
|
||||
|
||||
nfs4_ff_start_busy_timer(&layoutstat->busy_timer, now);
|
||||
if (ktime_equal(mirror->start_time, notime))
|
||||
mirror->start_time = now;
|
||||
if (ktime_equal(mirror->last_report_time, notime))
|
||||
mirror->last_report_time = now;
|
||||
if (mirror->report_interval != 0)
|
||||
report_interval = (s64)mirror->report_interval * 1000LL;
|
||||
else if (layoutstats_timer != 0)
|
||||
report_interval = (s64)layoutstats_timer * 1000LL;
|
||||
if (ktime_to_ms(ktime_sub(now, mirror->last_report_time)) >=
|
||||
if (ktime_to_ms(ktime_sub(now, ffl->last_report_time)) >=
|
||||
report_interval) {
|
||||
mirror->last_report_time = now;
|
||||
ffl->last_report_time = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -806,11 +806,14 @@ ff_layout_choose_best_ds_for_read(struct pnfs_layout_segment *lseg,
|
|||
{
|
||||
struct nfs4_ff_layout_segment *fls = FF_LAYOUT_LSEG(lseg);
|
||||
struct nfs4_pnfs_ds *ds;
|
||||
bool fail_return = false;
|
||||
int idx;
|
||||
|
||||
/* mirrors are sorted by efficiency */
|
||||
for (idx = start_idx; idx < fls->mirror_array_cnt; idx++) {
|
||||
ds = nfs4_ff_layout_prepare_ds(lseg, idx, false);
|
||||
if (idx+1 == fls->mirror_array_cnt)
|
||||
fail_return = true;
|
||||
ds = nfs4_ff_layout_prepare_ds(lseg, idx, fail_return);
|
||||
if (ds) {
|
||||
*best_idx = idx;
|
||||
return ds;
|
||||
|
@ -859,6 +862,7 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio,
|
|||
struct nfs4_pnfs_ds *ds;
|
||||
int ds_idx;
|
||||
|
||||
retry:
|
||||
/* Use full layout for now */
|
||||
if (!pgio->pg_lseg)
|
||||
ff_layout_pg_get_read(pgio, req, false);
|
||||
|
@ -871,10 +875,13 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio,
|
|||
|
||||
ds = ff_layout_choose_best_ds_for_read(pgio->pg_lseg, 0, &ds_idx);
|
||||
if (!ds) {
|
||||
if (ff_layout_no_fallback_to_mds(pgio->pg_lseg))
|
||||
goto out_pnfs;
|
||||
else
|
||||
if (!ff_layout_no_fallback_to_mds(pgio->pg_lseg))
|
||||
goto out_mds;
|
||||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
/* Sleep for 1 second before retrying */
|
||||
ssleep(1);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
mirror = FF_LAYOUT_COMP(pgio->pg_lseg, ds_idx);
|
||||
|
@ -890,12 +897,6 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio,
|
|||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
nfs_pageio_reset_read_mds(pgio);
|
||||
return;
|
||||
|
||||
out_pnfs:
|
||||
pnfs_set_lo_fail(pgio->pg_lseg);
|
||||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -909,6 +910,7 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio,
|
|||
int i;
|
||||
int status;
|
||||
|
||||
retry:
|
||||
if (!pgio->pg_lseg) {
|
||||
pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
|
||||
req->wb_context,
|
||||
|
@ -940,10 +942,13 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio,
|
|||
for (i = 0; i < pgio->pg_mirror_count; i++) {
|
||||
ds = nfs4_ff_layout_prepare_ds(pgio->pg_lseg, i, true);
|
||||
if (!ds) {
|
||||
if (ff_layout_no_fallback_to_mds(pgio->pg_lseg))
|
||||
goto out_pnfs;
|
||||
else
|
||||
if (!ff_layout_no_fallback_to_mds(pgio->pg_lseg))
|
||||
goto out_mds;
|
||||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
/* Sleep for 1 second before retrying */
|
||||
ssleep(1);
|
||||
goto retry;
|
||||
}
|
||||
pgm = &pgio->pg_mirrors[i];
|
||||
mirror = FF_LAYOUT_COMP(pgio->pg_lseg, i);
|
||||
|
@ -956,12 +961,6 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio,
|
|||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
nfs_pageio_reset_write_mds(pgio);
|
||||
return;
|
||||
|
||||
out_pnfs:
|
||||
pnfs_set_lo_fail(pgio->pg_lseg);
|
||||
pnfs_put_lseg(pgio->pg_lseg);
|
||||
pgio->pg_lseg = NULL;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
|
|
|
@ -84,7 +84,6 @@ struct nfs4_ff_layout_mirror {
|
|||
struct nfs4_ff_layoutstat read_stat;
|
||||
struct nfs4_ff_layoutstat write_stat;
|
||||
ktime_t start_time;
|
||||
ktime_t last_report_time;
|
||||
u32 report_interval;
|
||||
};
|
||||
|
||||
|
@ -101,6 +100,7 @@ struct nfs4_flexfile_layout {
|
|||
struct pnfs_ds_commit_info commit_info;
|
||||
struct list_head mirrors;
|
||||
struct list_head error_list; /* nfs4_ff_layout_ds_err */
|
||||
ktime_t last_report_time; /* Layoutstat report times */
|
||||
};
|
||||
|
||||
static inline struct nfs4_flexfile_layout *
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
|
||||
#define NFSDBG_FACILITY NFSDBG_PNFS_LD
|
||||
|
||||
static unsigned int dataserver_timeo = NFS4_DEF_DS_TIMEO;
|
||||
static unsigned int dataserver_retrans = NFS4_DEF_DS_RETRANS;
|
||||
static unsigned int dataserver_timeo = NFS_DEF_TCP_RETRANS;
|
||||
static unsigned int dataserver_retrans;
|
||||
|
||||
void nfs4_ff_layout_put_deviceid(struct nfs4_ff_layout_ds *mirror_ds)
|
||||
{
|
||||
|
@ -379,7 +379,7 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
|
|||
|
||||
devid = &mirror->mirror_ds->id_node;
|
||||
if (ff_layout_test_devid_unavailable(devid))
|
||||
goto out;
|
||||
goto out_fail;
|
||||
|
||||
ds = mirror->mirror_ds->ds;
|
||||
/* matching smp_wmb() in _nfs4_pnfs_v3/4_ds_connect */
|
||||
|
@ -405,15 +405,16 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
|
|||
mirror->mirror_ds->ds_versions[0].rsize = max_payload;
|
||||
if (mirror->mirror_ds->ds_versions[0].wsize > max_payload)
|
||||
mirror->mirror_ds->ds_versions[0].wsize = max_payload;
|
||||
} else {
|
||||
ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout),
|
||||
mirror, lseg->pls_range.offset,
|
||||
lseg->pls_range.length, NFS4ERR_NXIO,
|
||||
OP_ILLEGAL, GFP_NOIO);
|
||||
if (fail_return || !ff_layout_has_available_ds(lseg))
|
||||
pnfs_error_mark_layout_for_return(ino, lseg);
|
||||
ds = NULL;
|
||||
goto out;
|
||||
}
|
||||
ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout),
|
||||
mirror, lseg->pls_range.offset,
|
||||
lseg->pls_range.length, NFS4ERR_NXIO,
|
||||
OP_ILLEGAL, GFP_NOIO);
|
||||
out_fail:
|
||||
if (fail_return || !ff_layout_has_available_ds(lseg))
|
||||
pnfs_error_mark_layout_for_return(ino, lseg);
|
||||
ds = NULL;
|
||||
out:
|
||||
return ds;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,9 @@ struct nfs_clone_mount {
|
|||
*/
|
||||
#define NFS_UNSPEC_PORT (-1)
|
||||
|
||||
#define NFS_UNSPEC_RETRANS (UINT_MAX)
|
||||
#define NFS_UNSPEC_TIMEO (UINT_MAX)
|
||||
|
||||
/*
|
||||
* Maximum number of pages that readdir can use for creating
|
||||
* a vmapped array of pages.
|
||||
|
@ -156,7 +159,7 @@ struct nfs_client *nfs_get_client(const struct nfs_client_initdata *,
|
|||
int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *, struct nfs_fattr *);
|
||||
void nfs_server_insert_lists(struct nfs_server *);
|
||||
void nfs_server_remove_lists(struct nfs_server *);
|
||||
void nfs_init_timeout_values(struct rpc_timeout *, int, unsigned int, unsigned int);
|
||||
void nfs_init_timeout_values(struct rpc_timeout *to, int proto, int timeo, int retrans);
|
||||
int nfs_init_server_rpcclient(struct nfs_server *, const struct rpc_timeout *t,
|
||||
rpc_authflavor_t);
|
||||
struct nfs_server *nfs_alloc_server(void);
|
||||
|
|
|
@ -318,10 +318,22 @@ static void
|
|||
nfs42_layoutstat_prepare(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs42_layoutstat_data *data = calldata;
|
||||
struct nfs_server *server = NFS_SERVER(data->args.inode);
|
||||
struct inode *inode = data->inode;
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
struct pnfs_layout_hdr *lo;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
lo = NFS_I(inode)->layout;
|
||||
if (!pnfs_layout_is_valid(lo)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
rpc_exit(task, 0);
|
||||
return;
|
||||
}
|
||||
nfs4_stateid_copy(&data->args.stateid, &lo->plh_stateid);
|
||||
spin_unlock(&inode->i_lock);
|
||||
nfs41_setup_sequence(nfs4_get_session(server), &data->args.seq_args,
|
||||
&data->res.seq_res, task);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -341,11 +353,11 @@ nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
|
|||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
case -NFS4ERR_OLD_STATEID:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
spin_lock(&inode->i_lock);
|
||||
lo = NFS_I(inode)->layout;
|
||||
if (lo && nfs4_stateid_match(&data->args.stateid,
|
||||
if (pnfs_layout_is_valid(lo) &&
|
||||
nfs4_stateid_match(&data->args.stateid,
|
||||
&lo->plh_stateid)) {
|
||||
LIST_HEAD(head);
|
||||
|
||||
|
@ -359,11 +371,23 @@ nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
|
|||
} else
|
||||
spin_unlock(&inode->i_lock);
|
||||
break;
|
||||
case -NFS4ERR_OLD_STATEID:
|
||||
spin_lock(&inode->i_lock);
|
||||
lo = NFS_I(inode)->layout;
|
||||
if (pnfs_layout_is_valid(lo) &&
|
||||
nfs4_stateid_match_other(&data->args.stateid,
|
||||
&lo->plh_stateid)) {
|
||||
/* Do we need to delay before resending? */
|
||||
if (!nfs4_stateid_is_newer(&lo->plh_stateid,
|
||||
&data->args.stateid))
|
||||
rpc_delay(task, HZ);
|
||||
rpc_restart_call_prepare(task);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
break;
|
||||
case -ENOTSUPP:
|
||||
case -EOPNOTSUPP:
|
||||
NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dprintk("%s server returns %d\n", __func__, task->tk_status);
|
||||
|
|
|
@ -817,6 +817,11 @@ static int nfs4_set_client(struct nfs_server *server,
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (server->nfs_client == clp) {
|
||||
error = -ELOOP;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Query for the lease time on clientid setup or renewal
|
||||
*
|
||||
|
|
|
@ -634,15 +634,11 @@ int nfs40_setup_sequence(struct nfs4_slot_table *tbl,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nfs40_setup_sequence);
|
||||
|
||||
static int nfs40_sequence_done(struct rpc_task *task,
|
||||
struct nfs4_sequence_res *res)
|
||||
static void nfs40_sequence_free_slot(struct nfs4_sequence_res *res)
|
||||
{
|
||||
struct nfs4_slot *slot = res->sr_slot;
|
||||
struct nfs4_slot_table *tbl;
|
||||
|
||||
if (slot == NULL)
|
||||
goto out;
|
||||
|
||||
tbl = slot->table;
|
||||
spin_lock(&tbl->slot_tbl_lock);
|
||||
if (!nfs41_wake_and_assign_slot(tbl, slot))
|
||||
|
@ -650,7 +646,13 @@ static int nfs40_sequence_done(struct rpc_task *task,
|
|||
spin_unlock(&tbl->slot_tbl_lock);
|
||||
|
||||
res->sr_slot = NULL;
|
||||
out:
|
||||
}
|
||||
|
||||
static int nfs40_sequence_done(struct rpc_task *task,
|
||||
struct nfs4_sequence_res *res)
|
||||
{
|
||||
if (res->sr_slot != NULL)
|
||||
nfs40_sequence_free_slot(res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -666,6 +668,11 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
|
|||
tbl = slot->table;
|
||||
session = tbl->session;
|
||||
|
||||
/* Bump the slot sequence number */
|
||||
if (slot->seq_done)
|
||||
slot->seq_nr++;
|
||||
slot->seq_done = 0;
|
||||
|
||||
spin_lock(&tbl->slot_tbl_lock);
|
||||
/* Be nice to the server: try to ensure that the last transmitted
|
||||
* value for highest_user_slotid <= target_highest_slotid
|
||||
|
@ -686,9 +693,12 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
|
|||
res->sr_slot = NULL;
|
||||
if (send_new_highest_used_slotid)
|
||||
nfs41_notify_server(session->clp);
|
||||
if (waitqueue_active(&tbl->slot_waitq))
|
||||
wake_up_all(&tbl->slot_waitq);
|
||||
}
|
||||
|
||||
int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
||||
static int nfs41_sequence_process(struct rpc_task *task,
|
||||
struct nfs4_sequence_res *res)
|
||||
{
|
||||
struct nfs4_session *session;
|
||||
struct nfs4_slot *slot = res->sr_slot;
|
||||
|
@ -714,7 +724,7 @@ int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
|||
switch (res->sr_status) {
|
||||
case 0:
|
||||
/* Update the slot's sequence and clientid lease timer */
|
||||
++slot->seq_nr;
|
||||
slot->seq_done = 1;
|
||||
clp = session->clp;
|
||||
do_renew_lease(clp, res->sr_timestamp);
|
||||
/* Check sequence flags */
|
||||
|
@ -769,16 +779,16 @@ int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
|||
goto retry_nowait;
|
||||
default:
|
||||
/* Just update the slot sequence no. */
|
||||
++slot->seq_nr;
|
||||
slot->seq_done = 1;
|
||||
}
|
||||
out:
|
||||
/* The session may be reset by one of the error handlers. */
|
||||
dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
|
||||
nfs41_sequence_free_slot(res);
|
||||
out_noaction:
|
||||
return ret;
|
||||
retry_nowait:
|
||||
if (rpc_restart_call_prepare(task)) {
|
||||
nfs41_sequence_free_slot(res);
|
||||
task->tk_status = 0;
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -789,8 +799,37 @@ int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
|||
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
||||
{
|
||||
if (!nfs41_sequence_process(task, res))
|
||||
return 0;
|
||||
if (res->sr_slot != NULL)
|
||||
nfs41_sequence_free_slot(res);
|
||||
return 1;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs41_sequence_done);
|
||||
|
||||
static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res)
|
||||
{
|
||||
if (res->sr_slot == NULL)
|
||||
return 1;
|
||||
if (res->sr_slot->table->session != NULL)
|
||||
return nfs41_sequence_process(task, res);
|
||||
return nfs40_sequence_done(task, res);
|
||||
}
|
||||
|
||||
static void nfs4_sequence_free_slot(struct nfs4_sequence_res *res)
|
||||
{
|
||||
if (res->sr_slot != NULL) {
|
||||
if (res->sr_slot->table->session != NULL)
|
||||
nfs41_sequence_free_slot(res);
|
||||
else
|
||||
nfs40_sequence_free_slot(res);
|
||||
}
|
||||
}
|
||||
|
||||
int nfs4_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
||||
{
|
||||
if (res->sr_slot == NULL)
|
||||
|
@ -920,6 +959,17 @@ static int nfs4_setup_sequence(const struct nfs_server *server,
|
|||
args, res, task);
|
||||
}
|
||||
|
||||
static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res)
|
||||
{
|
||||
return nfs40_sequence_done(task, res);
|
||||
}
|
||||
|
||||
static void nfs4_sequence_free_slot(struct nfs4_sequence_res *res)
|
||||
{
|
||||
if (res->sr_slot != NULL)
|
||||
nfs40_sequence_free_slot(res);
|
||||
}
|
||||
|
||||
int nfs4_sequence_done(struct rpc_task *task,
|
||||
struct nfs4_sequence_res *res)
|
||||
{
|
||||
|
@ -1197,6 +1247,7 @@ static void nfs4_opendata_free(struct kref *kref)
|
|||
struct super_block *sb = p->dentry->d_sb;
|
||||
|
||||
nfs_free_seqid(p->o_arg.seqid);
|
||||
nfs4_sequence_free_slot(&p->o_res.seq_res);
|
||||
if (p->state != NULL)
|
||||
nfs4_put_open_state(p->state);
|
||||
nfs4_put_state_owner(p->owner);
|
||||
|
@ -1656,9 +1707,14 @@ _nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
|
|||
static struct nfs4_state *
|
||||
nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
|
||||
{
|
||||
struct nfs4_state *ret;
|
||||
|
||||
if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS)
|
||||
return _nfs4_opendata_reclaim_to_nfs4_state(data);
|
||||
return _nfs4_opendata_to_nfs4_state(data);
|
||||
ret =_nfs4_opendata_reclaim_to_nfs4_state(data);
|
||||
else
|
||||
ret = _nfs4_opendata_to_nfs4_state(data);
|
||||
nfs4_sequence_free_slot(&data->o_res.seq_res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state)
|
||||
|
@ -2056,7 +2112,7 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
|
|||
|
||||
data->rpc_status = task->tk_status;
|
||||
|
||||
if (!nfs4_sequence_done(task, &data->o_res.seq_res))
|
||||
if (!nfs4_sequence_process(task, &data->o_res.seq_res))
|
||||
return;
|
||||
|
||||
if (task->tk_status == 0) {
|
||||
|
@ -7864,7 +7920,7 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
|
|||
struct nfs4_layoutget *lgp = calldata;
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
nfs41_sequence_done(task, &lgp->res.seq_res);
|
||||
nfs41_sequence_process(task, &lgp->res.seq_res);
|
||||
dprintk("<-- %s\n", __func__);
|
||||
}
|
||||
|
||||
|
@ -8080,6 +8136,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout, gfp_t gfp_flags)
|
|||
/* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
|
||||
if (status == 0 && lgp->res.layoutp->len)
|
||||
lseg = pnfs_layout_process(lgp);
|
||||
nfs4_sequence_free_slot(&lgp->res.seq_res);
|
||||
rpc_put_task(task);
|
||||
dprintk("<-- %s status=%d\n", __func__, status);
|
||||
if (status)
|
||||
|
@ -8106,7 +8163,7 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
|
|||
|
||||
dprintk("--> %s\n", __func__);
|
||||
|
||||
if (!nfs41_sequence_done(task, &lrp->res.seq_res))
|
||||
if (!nfs41_sequence_process(task, &lrp->res.seq_res))
|
||||
return;
|
||||
|
||||
server = NFS_SERVER(lrp->args.inode);
|
||||
|
@ -8118,6 +8175,7 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
|
|||
case -NFS4ERR_DELAY:
|
||||
if (nfs4_async_handle_error(task, server, NULL, NULL) != -EAGAIN)
|
||||
break;
|
||||
nfs4_sequence_free_slot(&lrp->res.seq_res);
|
||||
rpc_restart_call_prepare(task);
|
||||
return;
|
||||
}
|
||||
|
@ -8138,6 +8196,7 @@ static void nfs4_layoutreturn_release(void *calldata)
|
|||
pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
|
||||
pnfs_clear_layoutreturn_waitbit(lo);
|
||||
spin_unlock(&lo->plh_inode->i_lock);
|
||||
nfs4_sequence_free_slot(&lrp->res.seq_res);
|
||||
pnfs_free_lseg_list(&freeme);
|
||||
pnfs_put_layout_hdr(lrp->args.layout);
|
||||
nfs_iput_and_deactive(lrp->inode);
|
||||
|
|
|
@ -28,6 +28,7 @@ static void nfs4_init_slot_table(struct nfs4_slot_table *tbl, const char *queue)
|
|||
tbl->highest_used_slotid = NFS4_NO_SLOT;
|
||||
spin_lock_init(&tbl->slot_tbl_lock);
|
||||
rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, queue);
|
||||
init_waitqueue_head(&tbl->slot_waitq);
|
||||
init_completion(&tbl->complete);
|
||||
}
|
||||
|
||||
|
@ -172,6 +173,58 @@ struct nfs4_slot *nfs4_lookup_slot(struct nfs4_slot_table *tbl, u32 slotid)
|
|||
return ERR_PTR(-E2BIG);
|
||||
}
|
||||
|
||||
static int nfs4_slot_get_seqid(struct nfs4_slot_table *tbl, u32 slotid,
|
||||
u32 *seq_nr)
|
||||
__must_hold(&tbl->slot_tbl_lock)
|
||||
{
|
||||
struct nfs4_slot *slot;
|
||||
|
||||
slot = nfs4_lookup_slot(tbl, slotid);
|
||||
if (IS_ERR(slot))
|
||||
return PTR_ERR(slot);
|
||||
*seq_nr = slot->seq_nr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs4_slot_seqid_in_use - test if a slot sequence id is still in use
|
||||
*
|
||||
* Given a slot table, slot id and sequence number, determine if the
|
||||
* RPC call in question is still in flight. This function is mainly
|
||||
* intended for use by the callback channel.
|
||||
*/
|
||||
static bool nfs4_slot_seqid_in_use(struct nfs4_slot_table *tbl,
|
||||
u32 slotid, u32 seq_nr)
|
||||
{
|
||||
u32 cur_seq;
|
||||
bool ret = false;
|
||||
|
||||
spin_lock(&tbl->slot_tbl_lock);
|
||||
if (nfs4_slot_get_seqid(tbl, slotid, &cur_seq) == 0 &&
|
||||
cur_seq == seq_nr && test_bit(slotid, tbl->used_slots))
|
||||
ret = true;
|
||||
spin_unlock(&tbl->slot_tbl_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs4_slot_wait_on_seqid - wait until a slot sequence id is complete
|
||||
*
|
||||
* Given a slot table, slot id and sequence number, wait until the
|
||||
* corresponding RPC call completes. This function is mainly
|
||||
* intended for use by the callback channel.
|
||||
*/
|
||||
int nfs4_slot_wait_on_seqid(struct nfs4_slot_table *tbl,
|
||||
u32 slotid, u32 seq_nr,
|
||||
unsigned long timeout)
|
||||
{
|
||||
if (wait_event_timeout(tbl->slot_waitq,
|
||||
!nfs4_slot_seqid_in_use(tbl, slotid, seq_nr),
|
||||
timeout) == 0)
|
||||
return -ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* nfs4_alloc_slot - efficiently look for a free slot
|
||||
*
|
||||
|
|
|
@ -21,7 +21,8 @@ struct nfs4_slot {
|
|||
unsigned long generation;
|
||||
u32 slot_nr;
|
||||
u32 seq_nr;
|
||||
unsigned int interrupted : 1;
|
||||
unsigned int interrupted : 1,
|
||||
seq_done : 1;
|
||||
};
|
||||
|
||||
/* Sessions */
|
||||
|
@ -36,6 +37,7 @@ struct nfs4_slot_table {
|
|||
unsigned long used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */
|
||||
spinlock_t slot_tbl_lock;
|
||||
struct rpc_wait_queue slot_tbl_waitq; /* allocators may wait here */
|
||||
wait_queue_head_t slot_waitq; /* Completion wait on slot */
|
||||
u32 max_slots; /* # slots in table */
|
||||
u32 max_slotid; /* Max allowed slotid value */
|
||||
u32 highest_used_slotid; /* sent to server on each SEQ.
|
||||
|
@ -78,6 +80,9 @@ extern int nfs4_setup_slot_table(struct nfs4_slot_table *tbl,
|
|||
extern void nfs4_shutdown_slot_table(struct nfs4_slot_table *tbl);
|
||||
extern struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl);
|
||||
extern struct nfs4_slot *nfs4_lookup_slot(struct nfs4_slot_table *tbl, u32 slotid);
|
||||
extern int nfs4_slot_wait_on_seqid(struct nfs4_slot_table *tbl,
|
||||
u32 slotid, u32 seq_nr,
|
||||
unsigned long timeout);
|
||||
extern bool nfs4_try_to_lock_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot);
|
||||
extern void nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot);
|
||||
extern void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl);
|
||||
|
|
|
@ -1555,6 +1555,7 @@ pnfs_update_layout(struct inode *ino,
|
|||
}
|
||||
|
||||
lookup_again:
|
||||
nfs4_client_recover_expired_lease(clp);
|
||||
first = false;
|
||||
spin_lock(&ino->i_lock);
|
||||
lo = pnfs_find_alloc_layout(ino, ctx, gfp_flags);
|
||||
|
@ -2510,7 +2511,6 @@ pnfs_report_layoutstat(struct inode *inode, gfp_t gfp_flags)
|
|||
|
||||
data->args.fh = NFS_FH(inode);
|
||||
data->args.inode = inode;
|
||||
nfs4_stateid_copy(&data->args.stateid, &hdr->plh_stateid);
|
||||
status = ld->prepare_layoutstats(&data->args);
|
||||
if (status)
|
||||
goto out_free;
|
||||
|
|
|
@ -923,6 +923,8 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
|
|||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data) {
|
||||
data->timeo = NFS_UNSPEC_TIMEO;
|
||||
data->retrans = NFS_UNSPEC_RETRANS;
|
||||
data->acregmin = NFS_DEF_ACREGMIN;
|
||||
data->acregmax = NFS_DEF_ACREGMAX;
|
||||
data->acdirmin = NFS_DEF_ACDIRMIN;
|
||||
|
@ -1189,6 +1191,19 @@ static int nfs_get_option_ul(substring_t args[], unsigned long *option)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int nfs_get_option_ul_bound(substring_t args[], unsigned long *option,
|
||||
unsigned long l_bound, unsigned long u_bound)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nfs_get_option_ul(args, option);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
if (*option < l_bound || *option > u_bound)
|
||||
return -ERANGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Error-check and convert a string of mount options from user space into
|
||||
* a data structure. The whole mount string is processed; bad options are
|
||||
|
@ -1352,12 +1367,12 @@ static int nfs_parse_mount_options(char *raw,
|
|||
mnt->bsize = option;
|
||||
break;
|
||||
case Opt_timeo:
|
||||
if (nfs_get_option_ul(args, &option) || option == 0)
|
||||
if (nfs_get_option_ul_bound(args, &option, 1, INT_MAX))
|
||||
goto out_invalid_value;
|
||||
mnt->timeo = option;
|
||||
break;
|
||||
case Opt_retrans:
|
||||
if (nfs_get_option_ul(args, &option) || option == 0)
|
||||
if (nfs_get_option_ul_bound(args, &option, 0, INT_MAX))
|
||||
goto out_invalid_value;
|
||||
mnt->retrans = option;
|
||||
break;
|
||||
|
|
|
@ -453,7 +453,7 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
|
|||
struct rpc_xprt_switch *xps;
|
||||
|
||||
if (args->bc_xprt && args->bc_xprt->xpt_bc_xps) {
|
||||
WARN_ON(args->protocol != XPRT_TRANSPORT_BC_TCP);
|
||||
WARN_ON_ONCE(!(args->protocol & XPRT_TRANSPORT_BC));
|
||||
xps = args->bc_xprt->xpt_bc_xps;
|
||||
xprt_switch_get(xps);
|
||||
} else {
|
||||
|
@ -520,7 +520,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
|
|||
char servername[48];
|
||||
|
||||
if (args->bc_xprt) {
|
||||
WARN_ON(args->protocol != XPRT_TRANSPORT_BC_TCP);
|
||||
WARN_ON_ONCE(!(args->protocol & XPRT_TRANSPORT_BC));
|
||||
xprt = args->bc_xprt->xpt_bc_xprt;
|
||||
if (xprt) {
|
||||
xprt_get(xprt);
|
||||
|
|
Loading…
Reference in New Issue