pnfs: prepare layout under a single lock

when starting io, both pnfs_read() and pnfs_write() need a guarantee that their range is covered by layout segments.  because we have to drop the lock for LAYOUTGET and GETDEVICEINFO, earlier layout segments may be recalled during this process.  to avoid this, new function pnfs_layout_state_prepare() gets called repeatedly until it can verify under a single lock that 1) the entire desired range is covered with layouts and 2) each of these layouts has an associated device.  whenever pnfs_layout_state_prepare() has to drop its lock for LAYOUTGET or GETDEVICEINFO, it returns PNFS_PENDING

on PNFS_SUCCESS, the caller knows that all segments in the range are valid and can dispatch io to those segments without worrying about recalls, because it still holds the pnfs_layout_state lock

Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
Casey Bodley 2012-01-10 16:15:49 -05:00 committed by unknown
parent c66771cda6
commit 91609640df
4 changed files with 256 additions and 276 deletions

View file

@ -428,8 +428,8 @@ out:
enum pnfs_status pnfs_read(
IN nfs41_root *root,
IN struct __nfs41_open_state *state,
IN const stateid_arg *stateid,
IN nfs41_open_state *state,
IN stateid_arg *stateid,
IN pnfs_layout_state *layout,
IN uint64_t offset,
IN uint64_t length,
@ -443,14 +443,29 @@ enum pnfs_status pnfs_read(
*len_out = 0;
status = pattern_init(&pattern, root, &state->file, stateid, layout,
buffer_out, offset, length, state->session->lease_time);
if (status) {
eprintf("pattern_init() failed with %s\n",
pnfs_error_string(status));
goto out;
AcquireSRWLockExclusive(&layout->lock);
/* get layouts/devices for the entire range; PNFS_PENDING means we
* dropped the lock to send an rpc, so repeat until it succeeds */
do {
status = pnfs_layout_state_prepare(layout, state->session,
&state->file, stateid, PNFS_IOMODE_READ, offset, length);
} while (status == PNFS_PENDING);
if (status == PNFS_SUCCESS) {
/* interpret the layout and set up threads for io */
status = pattern_init(&pattern, root, &state->file, stateid, layout,
buffer_out, offset, length, state->session->lease_time);
if (status)
eprintf("pattern_init() failed with %s\n",
pnfs_error_string(status));
}
ReleaseSRWLockExclusive(&layout->lock);
if (status)
goto out;
status = pattern_fork(&pattern, file_layout_read_thread);
if (status != PNFS_SUCCESS && status != PNFS_READ_EOF)
goto out_free_pattern;
@ -507,7 +522,7 @@ static enum pnfs_status layout_commit(
enum pnfs_status pnfs_write(
IN nfs41_root *root,
IN nfs41_open_state *state,
IN const stateid_arg *stateid,
IN stateid_arg *stateid,
IN pnfs_layout_state *layout,
IN uint64_t offset,
IN uint64_t length,
@ -524,14 +539,29 @@ enum pnfs_status pnfs_write(
*len_out = 0;
status = pattern_init(&pattern, root, &state->file, stateid, layout,
buffer, offset, length, state->session->lease_time);
if (status) {
eprintf("pattern_init() failed with %s\n",
pnfs_error_string(status));
goto out;
AcquireSRWLockExclusive(&layout->lock);
/* get layouts/devices for the entire range; PNFS_PENDING means we
* dropped the lock to send an rpc, so repeat until it succeeds */
do {
status = pnfs_layout_state_prepare(layout, state->session,
&state->file, stateid, PNFS_IOMODE_RW, offset, length);
} while (status == PNFS_PENDING);
if (status == PNFS_SUCCESS) {
/* interpret the layout and set up threads for io */
status = pattern_init(&pattern, root, &state->file, stateid, layout,
buffer, offset, length, state->session->lease_time);
if (status)
eprintf("pattern_init() failed with %s\n",
pnfs_error_string(status));
}
ReleaseSRWLockExclusive(&layout->lock);
if (status)
goto out;
status = pattern_fork(&pattern, file_layout_write_thread);
/* on layout recall, we still attempt to commit what we wrote */
if (status != PNFS_SUCCESS && status != PNFSERR_LAYOUT_RECALLED)