Skip to content

Commit

Permalink
Merge tag 'nfs-for-3.20-2' of git://git.linux-nfs.org/projects/trondm…
Browse files Browse the repository at this point in the history
…y/linux-nfs

Pull more NFS client updates from Trond Myklebust:
 "Highlights include:

   - Fix a use-after-free in decode_cb_sequence_args()
   - Fix a compile error when #undef CONFIG_PROC_FS
   - NFSv4.1 backchannel spinlocking issue
   - Cleanups in the NFS unstable write code requested by Linus
   - NFSv4.1 fix issues when the server denies our backchannel request
   - Cleanups in create_session and bind_conn_to_session"

* tag 'nfs-for-3.20-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFSv4.1: Clean up bind_conn_to_session
  NFSv4.1: Always set up a forward channel when binding the session
  NFSv4.1: Don't set up a backchannel if the server didn't agree to do so
  NFSv4.1: Clean up create_session
  pnfs: Refactor the *_layout_mark_request_commit to use pnfs_layout_mark_request_commit
  NFSv4: Kill unused nfs_inode->delegation_state field
  NFS: struct nfs_commit_info.lock must always point to inode->i_lock
  nfs: Can call nfs_clear_page_commit() instead
  nfs: Provide and use helper functions for marking a page as unstable
  SUNRPC: Always manipulate rpc_rqst::rq_bc_pa_list under xprt->bc_pa_lock
  SUNRPC: Fix a compile error when #undef CONFIG_PROC_FS
  NFSv4.1: Convert open-coded array allocation calls to kmalloc_array()
  NFSv4.1: Fix a kfree() of uninitialised pointers in decode_cb_sequence_args
  • Loading branch information
torvalds committed Feb 21, 2015
2 parents cd50b70 + 71a097c commit 24a52e4
Show file tree
Hide file tree
Showing 19 changed files with 165 additions and 158 deletions.
2 changes: 2 additions & 0 deletions fs/nfs/callback_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
if (clp == NULL)
goto out;

if (!(clp->cl_session->flags & SESSION4_BACK_CHAN))
goto out;
tbl = &clp->cl_session->bc_slot_table;

spin_lock(&tbl->slot_tbl_lock);
Expand Down
8 changes: 5 additions & 3 deletions fs/nfs/callback_xdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ __be32 decode_devicenotify_args(struct svc_rqst *rqstp,
goto out;
}

args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL);
args->devs = kmalloc_array(n, sizeof(*args->devs), GFP_KERNEL);
if (!args->devs) {
status = htonl(NFS4ERR_DELAY);
goto out;
Expand Down Expand Up @@ -415,7 +415,7 @@ static __be32 decode_rc_list(struct xdr_stream *xdr,
rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
if (unlikely(p == NULL))
goto out;
rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls *
rc_list->rcl_refcalls = kmalloc_array(rc_list->rcl_nrefcalls,
sizeof(*rc_list->rcl_refcalls),
GFP_KERNEL);
if (unlikely(rc_list->rcl_refcalls == NULL))
Expand Down Expand Up @@ -464,8 +464,10 @@ static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp,

for (i = 0; i < args->csa_nrclists; i++) {
status = decode_rc_list(xdr, &args->csa_rclists[i]);
if (status)
if (status) {
args->csa_nrclists = i;
goto out_free;
}
}
}
status = 0;
Expand Down
4 changes: 0 additions & 4 deletions fs/nfs/delegation.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
delegation->cred = get_rpccred(cred);
clear_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags);
NFS_I(inode)->delegation_state = delegation->type;
spin_unlock(&delegation->lock);
put_rpccred(oldcred);
rcu_read_unlock();
Expand Down Expand Up @@ -275,7 +274,6 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
list_del_rcu(&delegation->super_list);
delegation->inode = NULL;
nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL);
spin_unlock(&delegation->lock);
return delegation;
Expand Down Expand Up @@ -355,7 +353,6 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
&delegation->stateid)) {
nfs_update_inplace_delegation(old_delegation,
delegation);
nfsi->delegation_state = old_delegation->type;
goto out;
}
/*
Expand All @@ -379,7 +376,6 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
goto out;
}
list_add_rcu(&delegation->super_list, &server->delegations);
nfsi->delegation_state = delegation->type;
rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL;

Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
struct nfs_direct_req *dreq)
{
cinfo->lock = &dreq->lock;
cinfo->lock = &dreq->inode->i_lock;
cinfo->mds = &dreq->mds_cinfo;
cinfo->ds = &dreq->ds_cinfo;
cinfo->dreq = dreq;
Expand Down
53 changes: 10 additions & 43 deletions fs/nfs/filelayout/filelayout.c
Original file line number Diff line number Diff line change
Expand Up @@ -960,52 +960,19 @@ filelayout_mark_request_commit(struct nfs_page *req,
{
struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
u32 i, j;
struct list_head *list;
struct pnfs_commit_bucket *buckets;

if (fl->commit_through_mds) {
list = &cinfo->mds->list;
spin_lock(cinfo->lock);
goto mds_commit;
}

/* Note that we are calling nfs4_fl_calc_j_index on each page
* that ends up being committed to a data server. An attractive
* alternative is to add a field to nfs_write_data and nfs_page
* to store the value calculated in filelayout_write_pagelist
* and just use that here.
*/
j = nfs4_fl_calc_j_index(lseg, req_offset(req));
i = select_bucket_index(fl, j);
spin_lock(cinfo->lock);
buckets = cinfo->ds->buckets;
list = &buckets[i].written;
if (list_empty(list)) {
/* Non-empty buckets hold a reference on the lseg. That ref
* is normally transferred to the COMMIT call and released
* there. It could also be released if the last req is pulled
* off due to a rewrite, in which case it will be done in
* pnfs_generic_clear_request_commit
nfs_request_add_commit_list(req, &cinfo->mds->list, cinfo);
} else {
/* Note that we are calling nfs4_fl_calc_j_index on each page
* that ends up being committed to a data server. An attractive
* alternative is to add a field to nfs_write_data and nfs_page
* to store the value calculated in filelayout_write_pagelist
* and just use that here.
*/
buckets[i].wlseg = pnfs_get_lseg(lseg);
}
set_bit(PG_COMMIT_TO_DS, &req->wb_flags);
cinfo->ds->nwritten++;

mds_commit:
/* nfs_request_add_commit_list(). We need to add req to list without
* dropping cinfo lock.
*/
set_bit(PG_CLEAN, &(req)->wb_flags);
nfs_list_add_request(req, list);
cinfo->mds->ncommit++;
spin_unlock(cinfo->lock);
if (!cinfo->dreq) {
inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
inc_bdi_stat(inode_to_bdi(page_file_mapping(req->wb_page)->host),
BDI_RECLAIMABLE);
__mark_inode_dirty(req->wb_context->dentry->d_inode,
I_DIRTY_DATASYNC);
j = nfs4_fl_calc_j_index(lseg, req_offset(req));
i = select_bucket_index(fl, j);
pnfs_layout_mark_request_commit(req, lseg, cinfo, i);
}
}

Expand Down
43 changes: 1 addition & 42 deletions fs/nfs/flexfilelayout/flexfilelayout.c
Original file line number Diff line number Diff line change
Expand Up @@ -1332,47 +1332,6 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
return PNFS_ATTEMPTED;
}

static void
ff_layout_mark_request_commit(struct nfs_page *req,
struct pnfs_layout_segment *lseg,
struct nfs_commit_info *cinfo,
u32 ds_commit_idx)
{
struct list_head *list;
struct pnfs_commit_bucket *buckets;

spin_lock(cinfo->lock);
buckets = cinfo->ds->buckets;
list = &buckets[ds_commit_idx].written;
if (list_empty(list)) {
/* Non-empty buckets hold a reference on the lseg. That ref
* is normally transferred to the COMMIT call and released
* there. It could also be released if the last req is pulled
* off due to a rewrite, in which case it will be done in
* pnfs_common_clear_request_commit
*/
WARN_ON_ONCE(buckets[ds_commit_idx].wlseg != NULL);
buckets[ds_commit_idx].wlseg = pnfs_get_lseg(lseg);
}
set_bit(PG_COMMIT_TO_DS, &req->wb_flags);
cinfo->ds->nwritten++;

/* nfs_request_add_commit_list(). We need to add req to list without
* dropping cinfo lock.
*/
set_bit(PG_CLEAN, &(req)->wb_flags);
nfs_list_add_request(req, list);
cinfo->mds->ncommit++;
spin_unlock(cinfo->lock);
if (!cinfo->dreq) {
inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
inc_bdi_stat(inode_to_bdi(page_file_mapping(req->wb_page)->host),
BDI_RECLAIMABLE);
__mark_inode_dirty(req->wb_context->dentry->d_inode,
I_DIRTY_DATASYNC);
}
}

static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i)
{
return i;
Expand Down Expand Up @@ -1540,7 +1499,7 @@ static struct pnfs_layoutdriver_type flexfilelayout_type = {
.pg_write_ops = &ff_layout_pg_write_ops,
.get_ds_info = ff_layout_get_ds_info,
.free_deviceid_node = ff_layout_free_deveiceid_node,
.mark_request_commit = ff_layout_mark_request_commit,
.mark_request_commit = pnfs_layout_mark_request_commit,
.clear_request_commit = pnfs_generic_clear_request_commit,
.scan_commit_lists = pnfs_generic_scan_commit_lists,
.recover_commit_reqs = pnfs_generic_recover_commit_reqs,
Expand Down
1 change: 0 additions & 1 deletion fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1775,7 +1775,6 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi)
#if IS_ENABLED(CONFIG_NFS_V4)
INIT_LIST_HEAD(&nfsi->open_states);
nfsi->delegation = NULL;
nfsi->delegation_state = 0;
init_rwsem(&nfsi->rwsem);
nfsi->layout = NULL;
#endif
Expand Down
13 changes: 13 additions & 0 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,19 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize)
sb->s_maxbytes = MAX_LFS_FILESIZE;
}

/*
* Record the page as unstable and mark its inode as dirty.
*/
static inline
void nfs_mark_page_unstable(struct page *page)
{
struct inode *inode = page_file_mapping(page)->host;

inc_zone_page_state(page, NR_UNSTABLE_NFS);
inc_bdi_stat(inode_to_bdi(inode), BDI_RECLAIMABLE);
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
}

/*
* Determine the number of bytes of data the page contains
*/
Expand Down
75 changes: 48 additions & 27 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6648,47 +6648,47 @@ nfs41_same_server_scope(struct nfs41_server_scope *a,
int nfs4_proc_bind_conn_to_session(struct nfs_client *clp, struct rpc_cred *cred)
{
int status;
struct nfs41_bind_conn_to_session_args args = {
.client = clp,
.dir = NFS4_CDFC4_FORE_OR_BOTH,
};
struct nfs41_bind_conn_to_session_res res;
struct rpc_message msg = {
.rpc_proc =
&nfs4_procedures[NFSPROC4_CLNT_BIND_CONN_TO_SESSION],
.rpc_argp = clp,
.rpc_argp = &args,
.rpc_resp = &res,
.rpc_cred = cred,
};

dprintk("--> %s\n", __func__);

res.session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
if (unlikely(res.session == NULL)) {
status = -ENOMEM;
goto out;
}
nfs4_copy_sessionid(&args.sessionid, &clp->cl_session->sess_id);
if (!(clp->cl_session->flags & SESSION4_BACK_CHAN))
args.dir = NFS4_CDFC4_FORE;

status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
trace_nfs4_bind_conn_to_session(clp, status);
if (status == 0) {
if (memcmp(res.session->sess_id.data,
if (memcmp(res.sessionid.data,
clp->cl_session->sess_id.data, NFS4_MAX_SESSIONID_LEN)) {
dprintk("NFS: %s: Session ID mismatch\n", __func__);
status = -EIO;
goto out_session;
goto out;
}
if (res.dir != NFS4_CDFS4_BOTH) {
if ((res.dir & args.dir) != res.dir || res.dir == 0) {
dprintk("NFS: %s: Unexpected direction from server\n",
__func__);
status = -EIO;
goto out_session;
goto out;
}
if (res.use_conn_in_rdma_mode) {
if (res.use_conn_in_rdma_mode != args.use_conn_in_rdma_mode) {
dprintk("NFS: %s: Server returned RDMA mode = true\n",
__func__);
status = -EIO;
goto out_session;
goto out;
}
}
out_session:
kfree(res.session);
out:
dprintk("<-- %s status= %d\n", __func__, status);
return status;
Expand Down Expand Up @@ -7166,10 +7166,11 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
args->bc_attrs.max_reqs);
}

static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session)
static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args,
struct nfs41_create_session_res *res)
{
struct nfs4_channel_attrs *sent = &args->fc_attrs;
struct nfs4_channel_attrs *rcvd = &session->fc_attrs;
struct nfs4_channel_attrs *rcvd = &res->fc_attrs;

if (rcvd->max_resp_sz > sent->max_resp_sz)
return -EINVAL;
Expand All @@ -7188,11 +7189,14 @@ static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args
return 0;
}

static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session)
static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args,
struct nfs41_create_session_res *res)
{
struct nfs4_channel_attrs *sent = &args->bc_attrs;
struct nfs4_channel_attrs *rcvd = &session->bc_attrs;
struct nfs4_channel_attrs *rcvd = &res->bc_attrs;

if (!(res->flags & SESSION4_BACK_CHAN))
goto out;
if (rcvd->max_rqst_sz > sent->max_rqst_sz)
return -EINVAL;
if (rcvd->max_resp_sz < sent->max_resp_sz)
Expand All @@ -7204,18 +7208,30 @@ static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args
return -EINVAL;
if (rcvd->max_reqs != sent->max_reqs)
return -EINVAL;
out:
return 0;
}

static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args,
struct nfs4_session *session)
struct nfs41_create_session_res *res)
{
int ret;

ret = nfs4_verify_fore_channel_attrs(args, session);
ret = nfs4_verify_fore_channel_attrs(args, res);
if (ret)
return ret;
return nfs4_verify_back_channel_attrs(args, session);
return nfs4_verify_back_channel_attrs(args, res);
}

static void nfs4_update_session(struct nfs4_session *session,
struct nfs41_create_session_res *res)
{
nfs4_copy_sessionid(&session->sess_id, &res->sessionid);
session->flags = res->flags;
memcpy(&session->fc_attrs, &res->fc_attrs, sizeof(session->fc_attrs));
if (res->flags & SESSION4_BACK_CHAN)
memcpy(&session->bc_attrs, &res->bc_attrs,
sizeof(session->bc_attrs));
}

static int _nfs4_proc_create_session(struct nfs_client *clp,
Expand All @@ -7224,11 +7240,12 @@ static int _nfs4_proc_create_session(struct nfs_client *clp,
struct nfs4_session *session = clp->cl_session;
struct nfs41_create_session_args args = {
.client = clp,
.clientid = clp->cl_clientid,
.seqid = clp->cl_seqid,
.cb_program = NFS4_CALLBACK,
};
struct nfs41_create_session_res res = {
.client = clp,
};
struct nfs41_create_session_res res;

struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
.rpc_argp = &args,
Expand All @@ -7245,11 +7262,15 @@ static int _nfs4_proc_create_session(struct nfs_client *clp,

if (!status) {
/* Verify the session's negotiated channel_attrs values */
status = nfs4_verify_channel_attrs(&args, session);
status = nfs4_verify_channel_attrs(&args, &res);
/* Increment the clientid slot sequence id */
clp->cl_seqid++;
if (clp->cl_seqid == res.seqid)
clp->cl_seqid++;
if (status)
goto out;
nfs4_update_session(session, &res);
}

out:
return status;
}

Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/nfs4session.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ int nfs4_setup_session_slot_tables(struct nfs4_session *ses)
tbl = &ses->fc_slot_table;
tbl->session = ses;
status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1);
if (status) /* -ENOMEM */
if (status || !(ses->flags & SESSION4_BACK_CHAN)) /* -ENOMEM */
return status;
/* Back channel */
tbl = &ses->bc_slot_table;
Expand Down
Loading

0 comments on commit 24a52e4

Please sign in to comment.