diff options
Diffstat (limited to 'xlators/performance')
-rw-r--r-- | xlators/performance/write-behind/src/write-behind.c | 54 |
1 files changed, 45 insertions, 9 deletions
diff --git a/xlators/performance/write-behind/src/write-behind.c b/xlators/performance/write-behind/src/write-behind.c index c6bc5ef573e..c3c42cd1a4f 100644 --- a/xlators/performance/write-behind/src/write-behind.c +++ b/xlators/performance/write-behind/src/write-behind.c @@ -837,7 +837,7 @@ out: } int -__wb_fulfill_short_write (wb_request_t *req, int size) +__wb_fulfill_short_write (wb_request_t *req, int size, gf_boolean_t *fulfilled) { int accounted_size = 0; @@ -847,6 +847,7 @@ __wb_fulfill_short_write (wb_request_t *req, int size) if (req->write_size <= size) { accounted_size = req->write_size; __wb_fulfill_request (req); + *fulfilled = 1; } else { accounted_size = size; __wb_modify_write_request (req, size); @@ -859,9 +860,10 @@ out: void wb_fulfill_short_write (wb_request_t *head, int size) { - wb_inode_t *wb_inode = NULL; - wb_request_t *req = NULL, *tmp = NULL; - int accounted_size = 0; + wb_inode_t *wb_inode = NULL; + wb_request_t *req = NULL, *next = NULL; + int accounted_size = 0; + gf_boolean_t fulfilled = _gf_false; if (!head) goto out; @@ -872,25 +874,48 @@ wb_fulfill_short_write (wb_request_t *head, int size) LOCK (&wb_inode->lock); { - accounted_size = __wb_fulfill_short_write (head, size); + /* hold a reference to head so that __wb_fulfill_short_write + * won't free it. We need head for a cleaner list traversal as + * list_for_each_entry_safe doesn't iterate over "head" member. + * So, if we pass "next->winds" as head to list_for_each_entry, + * "next" is skipped. For a simpler logic we need to traverse + * the list in the order. So, we start traversal from + * "head->winds" and hence we want head to be alive. + */ + __wb_request_ref (head); + + next = list_entry (head->winds.next, wb_request_t, winds); + + accounted_size = __wb_fulfill_short_write (head, size, + &fulfilled); size -= accounted_size; - if (size == 0) + if (size == 0) { + if (fulfilled) + req = next; + goto done; + } - list_for_each_entry_safe (req, tmp, &head->winds, winds) { - accounted_size = __wb_fulfill_short_write (req, size); + list_for_each_entry_safe (req, next, &head->winds, winds) { + accounted_size = __wb_fulfill_short_write (req, size, + &fulfilled); size -= accounted_size; - if (size == 0) + if (size == 0) { + if (fulfilled) + req = next; break; + } } } done: UNLOCK (&wb_inode->lock); + __wb_request_unref (head); + wb_fulfill_err (req, EIO); out: return; @@ -1314,6 +1339,16 @@ __wb_handle_failed_conflict (wb_request_t *req, wb_request_t *conflict, list_del_init (&req->todo); list_add_tail (&req->winds, tasks); + + if (req->ordering.tempted) { + /* make sure that it won't be unwound in + * wb_do_unwinds too. Otherwise there'll be + * a double wind. + */ + list_del_init (&req->lie); + __wb_fulfill_request (req); + } + } } else { /* flush and fsync (without conf->resync_after_fsync) act as @@ -1421,6 +1456,7 @@ wb_do_winds (wb_inode_t *wb_inode, list_head_t *tasks) call_resume (req->stub); } + req->stub = NULL; wb_request_unref (req); } } |