Skip to content

Commit e00ed89

Browse files
author
Trond Myklebust
committed
NFS: Refactor nfs_lock_and_join_requests()
Refactor nfs_lock_and_join_requests() in order to separate out the subrequest merging into its own function nfs_lock_and_join_group() that can be used by O_DIRECT. Signed-off-by: Trond Myklebust <[email protected]>
1 parent 44a65a0 commit e00ed89

File tree

3 files changed

+123
-68
lines changed

3 files changed

+123
-68
lines changed

fs/nfs/pagelist.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,25 @@ nfs_async_iocounter_wait(struct rpc_task *task, struct nfs_lock_context *l_ctx)
130130
}
131131
EXPORT_SYMBOL_GPL(nfs_async_iocounter_wait);
132132

133+
/*
134+
* nfs_page_lock_head_request - page lock the head of the page group
135+
* @req: any member of the page group
136+
*/
137+
struct nfs_page *
138+
nfs_page_group_lock_head(struct nfs_page *req)
139+
{
140+
struct nfs_page *head = req->wb_head;
141+
142+
while (!nfs_lock_request(head)) {
143+
int ret = nfs_wait_on_request(head);
144+
if (ret < 0)
145+
return ERR_PTR(ret);
146+
}
147+
if (head != req)
148+
kref_get(&head->wb_kref);
149+
return head;
150+
}
151+
133152
/*
134153
* nfs_unroll_locks - unlock all newly locked reqs and wait on @req
135154
* @head: head request of page group, must be holding head lock
@@ -186,21 +205,24 @@ nfs_page_group_lock_subreq(struct nfs_page *head, struct nfs_page *subreq)
186205
* @head: head request of page group
187206
*
188207
* This is a helper function for nfs_lock_and_join_requests which
189-
* must be called with the head request and page group both locked.
190-
* On error, it returns with the page group unlocked.
208+
* must be called with the head request locked.
191209
*/
192210
int nfs_page_group_lock_subrequests(struct nfs_page *head)
193211
{
194212
struct nfs_page *subreq;
195213
int ret;
196214

215+
ret = nfs_page_group_lock(head);
216+
if (ret < 0)
217+
return ret;
197218
/* lock each request in the page group */
198219
for (subreq = head->wb_this_page; subreq != head;
199220
subreq = subreq->wb_this_page) {
200221
ret = nfs_page_group_lock_subreq(head, subreq);
201222
if (ret < 0)
202223
return ret;
203224
}
225+
nfs_page_group_unlock(head);
204226
return 0;
205227
}
206228

fs/nfs/write.c

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,31 @@ static void nfs_io_completion_put(struct nfs_io_completion *ioc)
149149
kref_put(&ioc->refcount, nfs_io_completion_release);
150150
}
151151

152+
static void
153+
nfs_page_set_inode_ref(struct nfs_page *req, struct inode *inode)
154+
{
155+
if (!test_and_set_bit(PG_INODE_REF, &req->wb_flags)) {
156+
kref_get(&req->wb_kref);
157+
atomic_long_inc(&NFS_I(inode)->nrequests);
158+
}
159+
}
160+
161+
static int
162+
nfs_cancel_remove_inode(struct nfs_page *req, struct inode *inode)
163+
{
164+
int ret;
165+
166+
if (!test_bit(PG_REMOVE, &req->wb_flags))
167+
return 0;
168+
ret = nfs_page_group_lock(req);
169+
if (ret)
170+
return ret;
171+
if (test_and_clear_bit(PG_REMOVE, &req->wb_flags))
172+
nfs_page_set_inode_ref(req, inode);
173+
nfs_page_group_unlock(req);
174+
return 0;
175+
}
176+
152177
static struct nfs_page *
153178
nfs_page_private_request(struct page *page)
154179
{
@@ -218,6 +243,36 @@ static struct nfs_page *nfs_page_find_head_request(struct page *page)
218243
return req;
219244
}
220245

246+
static struct nfs_page *nfs_find_and_lock_page_request(struct page *page)
247+
{
248+
struct inode *inode = page_file_mapping(page)->host;
249+
struct nfs_page *req, *head;
250+
int ret;
251+
252+
for (;;) {
253+
req = nfs_page_find_head_request(page);
254+
if (!req)
255+
return req;
256+
head = nfs_page_group_lock_head(req);
257+
if (head != req)
258+
nfs_release_request(req);
259+
if (IS_ERR(head))
260+
return head;
261+
ret = nfs_cancel_remove_inode(head, inode);
262+
if (ret < 0) {
263+
nfs_unlock_and_release_request(head);
264+
return ERR_PTR(ret);
265+
}
266+
/* Ensure that nobody removed the request before we locked it */
267+
if (head == nfs_page_private_request(page))
268+
break;
269+
if (PageSwapCache(page))
270+
break;
271+
nfs_unlock_and_release_request(head);
272+
}
273+
return head;
274+
}
275+
221276
/* Adjust the file length if we're writing beyond the end */
222277
static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
223278
{
@@ -436,65 +491,22 @@ nfs_destroy_unlinked_subrequests(struct nfs_page *destroy_list,
436491
}
437492

438493
/*
439-
* nfs_lock_and_join_requests - join all subreqs to the head req and return
440-
* a locked reference, cancelling any pending
441-
* operations for this page.
442-
*
443-
* @page - the page used to lookup the "page group" of nfs_page structures
494+
* nfs_join_page_group - destroy subrequests of the head req
495+
* @head: the page used to lookup the "page group" of nfs_page structures
496+
* @inode: Inode to which the request belongs.
444497
*
445498
* This function joins all sub requests to the head request by first
446499
* locking all requests in the group, cancelling any pending operations
447500
* and finally updating the head request to cover the whole range covered by
448501
* the (former) group. All subrequests are removed from any write or commit
449502
* lists, unlinked from the group and destroyed.
450-
*
451-
* Returns a locked, referenced pointer to the head request - which after
452-
* this call is guaranteed to be the only request associated with the page.
453-
* Returns NULL if no requests are found for @page, or a ERR_PTR if an
454-
* error was encountered.
455503
*/
456-
static struct nfs_page *
457-
nfs_lock_and_join_requests(struct page *page)
504+
static void
505+
nfs_join_page_group(struct nfs_page *head, struct inode *inode)
458506
{
459-
struct inode *inode = page_file_mapping(page)->host;
460-
struct nfs_page *head, *subreq;
507+
struct nfs_page *subreq;
461508
struct nfs_page *destroy_list = NULL;
462509
unsigned int pgbase, off, bytes;
463-
int ret;
464-
465-
try_again:
466-
/*
467-
* A reference is taken only on the head request which acts as a
468-
* reference to the whole page group - the group will not be destroyed
469-
* until the head reference is released.
470-
*/
471-
head = nfs_page_find_head_request(page);
472-
if (!head)
473-
return NULL;
474-
475-
/* lock the page head first in order to avoid an ABBA inefficiency */
476-
if (!nfs_lock_request(head)) {
477-
ret = nfs_wait_on_request(head);
478-
nfs_release_request(head);
479-
if (ret < 0)
480-
return ERR_PTR(ret);
481-
goto try_again;
482-
}
483-
484-
/* Ensure that nobody removed the request before we locked it */
485-
if (head != nfs_page_private_request(page) && !PageSwapCache(page)) {
486-
nfs_unlock_and_release_request(head);
487-
goto try_again;
488-
}
489-
490-
ret = nfs_page_group_lock(head);
491-
if (ret < 0)
492-
goto release_request;
493-
494-
/* lock each request in the page group */
495-
ret = nfs_page_group_lock_subrequests(head);
496-
if (ret < 0)
497-
goto release_request;
498510

499511
pgbase = head->wb_pgbase;
500512
bytes = head->wb_bytes;
@@ -531,30 +543,50 @@ nfs_lock_and_join_requests(struct page *page)
531543
head->wb_this_page = head;
532544
}
533545

534-
/* Postpone destruction of this request */
535-
if (test_and_clear_bit(PG_REMOVE, &head->wb_flags)) {
536-
set_bit(PG_INODE_REF, &head->wb_flags);
537-
kref_get(&head->wb_kref);
538-
atomic_long_inc(&NFS_I(inode)->nrequests);
539-
}
546+
nfs_destroy_unlinked_subrequests(destroy_list, head, inode);
547+
}
540548

541-
nfs_page_group_unlock(head);
549+
/*
550+
* nfs_lock_and_join_requests - join all subreqs to the head req
551+
* @page: the page used to lookup the "page group" of nfs_page structures
552+
*
553+
* This function joins all sub requests to the head request by first
554+
* locking all requests in the group, cancelling any pending operations
555+
* and finally updating the head request to cover the whole range covered by
556+
* the (former) group. All subrequests are removed from any write or commit
557+
* lists, unlinked from the group and destroyed.
558+
*
559+
* Returns a locked, referenced pointer to the head request - which after
560+
* this call is guaranteed to be the only request associated with the page.
561+
* Returns NULL if no requests are found for @page, or a ERR_PTR if an
562+
* error was encountered.
563+
*/
564+
static struct nfs_page *
565+
nfs_lock_and_join_requests(struct page *page)
566+
{
567+
struct inode *inode = page_file_mapping(page)->host;
568+
struct nfs_page *head;
569+
int ret;
542570

543-
nfs_destroy_unlinked_subrequests(destroy_list, head, inode);
571+
/*
572+
* A reference is taken only on the head request which acts as a
573+
* reference to the whole page group - the group will not be destroyed
574+
* until the head reference is released.
575+
*/
576+
head = nfs_find_and_lock_page_request(page);
577+
if (IS_ERR_OR_NULL(head))
578+
return head;
544579

545-
/* Did we lose a race with nfs_inode_remove_request()? */
546-
if (!(PagePrivate(page) || PageSwapCache(page))) {
580+
/* lock each request in the page group */
581+
ret = nfs_page_group_lock_subrequests(head);
582+
if (ret < 0) {
547583
nfs_unlock_and_release_request(head);
548-
return NULL;
584+
return ERR_PTR(ret);
549585
}
550586

551-
/* still holds ref on head from nfs_page_find_head_request
552-
* and still has lock on head from lock loop */
553-
return head;
587+
nfs_join_page_group(head, inode);
554588

555-
release_request:
556-
nfs_unlock_and_release_request(head);
557-
return ERR_PTR(ret);
589+
return head;
558590
}
559591

560592
static void nfs_write_error(struct nfs_page *req, int error)

include/linux/nfs_page.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ extern size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc,
139139
extern int nfs_wait_on_request(struct nfs_page *);
140140
extern void nfs_unlock_request(struct nfs_page *req);
141141
extern void nfs_unlock_and_release_request(struct nfs_page *);
142+
extern struct nfs_page *nfs_page_group_lock_head(struct nfs_page *req);
142143
extern int nfs_page_group_lock_subrequests(struct nfs_page *head);
143144
extern int nfs_page_group_lock(struct nfs_page *);
144145
extern void nfs_page_group_unlock(struct nfs_page *);

0 commit comments

Comments
 (0)