pnfs/filelayout: fix race between mark_request_commit and scan_commit_lists

We need to hold cinfo lock while setting bucket->wlseg and adding req to nwritten
list at the same time. Otherwise there might be a window where nwritten list
is empty yet we set bucket->wlseg, in which case ff_layout_scan_ds_commit_list()
may end up clearing bucket->wlseg incorrectly, casuing client to oops later on.

This was found when testing flexfile layout but filelayout has the same problem.

Signed-off-by: Peng Tao <tao.peng@primarydata.com>
Signed-off-by: Tom Haynes <Thomas.Haynes@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
This commit is contained in:
Peng Tao 2014-07-03 13:07:45 +08:00 коммит произвёл Trond Myklebust
Родитель f3792d63d2
Коммит c8a3292d24
1 изменённых файлов: 25 добавлений и 18 удалений

Просмотреть файл

@ -1035,18 +1035,22 @@ out:
pnfs_put_lseg(freeme); pnfs_put_lseg(freeme);
} }
static struct list_head * static void
filelayout_choose_commit_list(struct nfs_page *req, filelayout_mark_request_commit(struct nfs_page *req,
struct pnfs_layout_segment *lseg, struct pnfs_layout_segment *lseg,
struct nfs_commit_info *cinfo) struct nfs_commit_info *cinfo)
{ {
struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg); struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
u32 i, j; u32 i, j;
struct list_head *list; struct list_head *list;
struct pnfs_commit_bucket *buckets; struct pnfs_commit_bucket *buckets;
if (fl->commit_through_mds) if (fl->commit_through_mds) {
return &cinfo->mds->list; list = &cinfo->mds->list;
spin_lock(cinfo->lock);
goto mds_commit;
}
/* Note that we are calling nfs4_fl_calc_j_index on each page /* Note that we are calling nfs4_fl_calc_j_index on each page
* that ends up being committed to a data server. An attractive * that ends up being committed to a data server. An attractive
@ -1070,19 +1074,22 @@ filelayout_choose_commit_list(struct nfs_page *req,
} }
set_bit(PG_COMMIT_TO_DS, &req->wb_flags); set_bit(PG_COMMIT_TO_DS, &req->wb_flags);
cinfo->ds->nwritten++; 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); spin_unlock(cinfo->lock);
return list; if (!cinfo->dreq) {
} inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
inc_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info,
static void BDI_RECLAIMABLE);
filelayout_mark_request_commit(struct nfs_page *req, __mark_inode_dirty(req->wb_context->dentry->d_inode,
struct pnfs_layout_segment *lseg, I_DIRTY_DATASYNC);
struct nfs_commit_info *cinfo) }
{
struct list_head *list;
list = filelayout_choose_commit_list(req, lseg, cinfo);
nfs_request_add_commit_list(req, list, cinfo);
} }
static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i) static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i)