|
|
17b94a |
From 48f6929590157d9a1697e11c02441207afdc1bed Mon Sep 17 00:00:00 2001
|
|
|
17b94a |
From: Xavi Hernandez <xhernandez@redhat.com>
|
|
|
17b94a |
Date: Fri, 27 Mar 2020 23:56:15 +0100
|
|
|
17b94a |
Subject: [PATCH 362/362] write-behind: fix data corruption
|
|
|
17b94a |
|
|
|
17b94a |
There was a bug in write-behind that allowed a previous completed write
|
|
|
17b94a |
to overwrite the overlapping region of data from a future write.
|
|
|
17b94a |
|
|
|
17b94a |
Suppose we want to send three writes (W1, W2 and W3). W1 and W2 are
|
|
|
17b94a |
sequential, and W3 writes at the same offset of W2:
|
|
|
17b94a |
|
|
|
17b94a |
W2.offset = W3.offset = W1.offset + W1.size
|
|
|
17b94a |
|
|
|
17b94a |
Both W1 and W2 are sent in parallel. W3 is only sent after W2 completes.
|
|
|
17b94a |
So W3 should *always* overwrite the overlapping part of W2.
|
|
|
17b94a |
|
|
|
17b94a |
Suppose write-behind processes the requests from 2 concurrent threads:
|
|
|
17b94a |
|
|
|
17b94a |
Thread 1 Thread 2
|
|
|
17b94a |
|
|
|
17b94a |
<received W1>
|
|
|
17b94a |
<received W2>
|
|
|
17b94a |
wb_enqueue_tempted(W1)
|
|
|
17b94a |
/* W1 is assigned gen X */
|
|
|
17b94a |
wb_enqueue_tempted(W2)
|
|
|
17b94a |
/* W2 is assigned gen X */
|
|
|
17b94a |
|
|
|
17b94a |
wb_process_queue()
|
|
|
17b94a |
__wb_preprocess_winds()
|
|
|
17b94a |
/* W1 and W2 are sequential and all
|
|
|
17b94a |
* other requisites are met to merge
|
|
|
17b94a |
* both requests. */
|
|
|
17b94a |
__wb_collapse_small_writes(W1, W2)
|
|
|
17b94a |
__wb_fulfill_request(W2)
|
|
|
17b94a |
|
|
|
17b94a |
__wb_pick_unwinds() -> W2
|
|
|
17b94a |
/* In this case, since the request is
|
|
|
17b94a |
* already fulfilled, wb_inode->gen
|
|
|
17b94a |
* is not updated. */
|
|
|
17b94a |
|
|
|
17b94a |
wb_do_unwinds()
|
|
|
17b94a |
STACK_UNWIND(W2)
|
|
|
17b94a |
|
|
|
17b94a |
/* The application has received the
|
|
|
17b94a |
* result of W2, so it can send W3. */
|
|
|
17b94a |
<received W3>
|
|
|
17b94a |
|
|
|
17b94a |
wb_enqueue_tempted(W3)
|
|
|
17b94a |
/* W3 is assigned gen X */
|
|
|
17b94a |
|
|
|
17b94a |
wb_process_queue()
|
|
|
17b94a |
/* Here we have W1 (which contains
|
|
|
17b94a |
* the conflicting W2) and W3 with
|
|
|
17b94a |
* same gen, so they are interpreted
|
|
|
17b94a |
* as concurrent writes that do not
|
|
|
17b94a |
* conflict. */
|
|
|
17b94a |
__wb_pick_winds() -> W3
|
|
|
17b94a |
|
|
|
17b94a |
wb_do_winds()
|
|
|
17b94a |
STACK_WIND(W3)
|
|
|
17b94a |
|
|
|
17b94a |
wb_process_queue()
|
|
|
17b94a |
/* Eventually W1 will be
|
|
|
17b94a |
* ready to be sent */
|
|
|
17b94a |
__wb_pick_winds() -> W1
|
|
|
17b94a |
__wb_pick_unwinds() -> W1
|
|
|
17b94a |
/* Here wb_inode->gen is
|
|
|
17b94a |
* incremented. */
|
|
|
17b94a |
|
|
|
17b94a |
wb_do_unwinds()
|
|
|
17b94a |
STACK_UNWIND(W1)
|
|
|
17b94a |
|
|
|
17b94a |
wb_do_winds()
|
|
|
17b94a |
STACK_WIND(W1)
|
|
|
17b94a |
|
|
|
17b94a |
So, as we can see, W3 is sent before W1, which shouldn't happen.
|
|
|
17b94a |
|
|
|
17b94a |
The problem is that wb_inode->gen is only incremented for requests that
|
|
|
17b94a |
have not been fulfilled but, after a merge, the request is marked as
|
|
|
17b94a |
fulfilled even though it has not been sent to the brick. This allows
|
|
|
17b94a |
that future requests are assigned to the same generation, which could
|
|
|
17b94a |
be internally reordered.
|
|
|
17b94a |
|
|
|
17b94a |
Solution:
|
|
|
17b94a |
|
|
|
17b94a |
Increment wb_inode->gen before any unwind, even if it's for a fulfilled
|
|
|
17b94a |
request.
|
|
|
17b94a |
|
|
|
17b94a |
Special thanks to Stefan Ring for writing a reproducer that has been
|
|
|
17b94a |
crucial to identify the issue.
|
|
|
17b94a |
|
|
|
17b94a |
Upstream patch:
|
|
|
17b94a |
> Upstream patch link: https://review.gluster.org/c/glusterfs/+/24263
|
|
|
17b94a |
> Change-Id: Id4ab0f294a09aca9a863ecaeef8856474662ab45
|
|
|
17b94a |
> Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
|
|
|
17b94a |
> Fixes: #884
|
|
|
17b94a |
|
|
|
17b94a |
Change-Id: Id4ab0f294a09aca9a863ecaeef8856474662ab45
|
|
|
17b94a |
Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
|
|
|
17b94a |
BUG: 1819059
|
|
|
17b94a |
Reviewed-on: https://code.engineering.redhat.com/gerrit/196250
|
|
|
17b94a |
Tested-by: RHGS Build Bot <nigelb@redhat.com>
|
|
|
17b94a |
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
|
|
|
17b94a |
---
|
|
|
17b94a |
tests/bugs/write-behind/issue-884.c | 267 +++++++++++++++++++++
|
|
|
17b94a |
tests/bugs/write-behind/issue-884.t | 40 +++
|
|
|
17b94a |
.../performance/write-behind/src/write-behind.c | 4 +-
|
|
|
17b94a |
3 files changed, 309 insertions(+), 2 deletions(-)
|
|
|
17b94a |
create mode 100644 tests/bugs/write-behind/issue-884.c
|
|
|
17b94a |
create mode 100755 tests/bugs/write-behind/issue-884.t
|
|
|
17b94a |
|
|
|
17b94a |
diff --git a/tests/bugs/write-behind/issue-884.c b/tests/bugs/write-behind/issue-884.c
|
|
|
17b94a |
new file mode 100644
|
|
|
17b94a |
index 0000000..e9c33b3
|
|
|
17b94a |
--- /dev/null
|
|
|
17b94a |
+++ b/tests/bugs/write-behind/issue-884.c
|
|
|
17b94a |
@@ -0,0 +1,267 @@
|
|
|
17b94a |
+
|
|
|
17b94a |
+#define _GNU_SOURCE
|
|
|
17b94a |
+
|
|
|
17b94a |
+#include <stdlib.h>
|
|
|
17b94a |
+#include <stdio.h>
|
|
|
17b94a |
+#include <string.h>
|
|
|
17b94a |
+#include <time.h>
|
|
|
17b94a |
+#include <assert.h>
|
|
|
17b94a |
+#include <errno.h>
|
|
|
17b94a |
+#include <sys/types.h>
|
|
|
17b94a |
+#include <sys/stat.h>
|
|
|
17b94a |
+#include <pthread.h>
|
|
|
17b94a |
+
|
|
|
17b94a |
+#include <glusterfs/api/glfs.h>
|
|
|
17b94a |
+
|
|
|
17b94a |
+/* Based on a reproducer by Stefan Ring. It seems to be quite sensible to any
|
|
|
17b94a |
+ * timing modification, so the code has been maintained as is, only with minor
|
|
|
17b94a |
+ * changes. */
|
|
|
17b94a |
+
|
|
|
17b94a |
+struct glfs *glfs;
|
|
|
17b94a |
+
|
|
|
17b94a |
+pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
17b94a |
+pthread_cond_t the_cond = PTHREAD_COND_INITIALIZER;
|
|
|
17b94a |
+
|
|
|
17b94a |
+typedef struct _my_aiocb {
|
|
|
17b94a |
+ int64_t size;
|
|
|
17b94a |
+ volatile int64_t seq;
|
|
|
17b94a |
+ int which;
|
|
|
17b94a |
+} my_aiocb;
|
|
|
17b94a |
+
|
|
|
17b94a |
+typedef struct _worker_data {
|
|
|
17b94a |
+ my_aiocb cb;
|
|
|
17b94a |
+ struct iovec iov;
|
|
|
17b94a |
+ int64_t offset;
|
|
|
17b94a |
+} worker_data;
|
|
|
17b94a |
+
|
|
|
17b94a |
+typedef struct {
|
|
|
17b94a |
+ worker_data wdata[2];
|
|
|
17b94a |
+
|
|
|
17b94a |
+ volatile unsigned busy;
|
|
|
17b94a |
+} all_data_t;
|
|
|
17b94a |
+
|
|
|
17b94a |
+all_data_t all_data;
|
|
|
17b94a |
+
|
|
|
17b94a |
+static void
|
|
|
17b94a |
+completion_fnc(struct glfs_fd *fd, ssize_t ret, struct glfs_stat *pre,
|
|
|
17b94a |
+ struct glfs_stat *post, void *arg)
|
|
|
17b94a |
+{
|
|
|
17b94a |
+ void *the_thread;
|
|
|
17b94a |
+ my_aiocb *cb = (my_aiocb *)arg;
|
|
|
17b94a |
+ long seq = cb->seq;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ assert(ret == cb->size);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ pthread_mutex_lock(&the_mutex);
|
|
|
17b94a |
+ pthread_cond_broadcast(&the_cond);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ all_data.busy &= ~(1 << cb->which);
|
|
|
17b94a |
+ cb->seq = -1;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ the_thread = (void *)pthread_self();
|
|
|
17b94a |
+ printf("worker %d is done from thread %p, seq %ld!\n", cb->which,
|
|
|
17b94a |
+ the_thread, seq);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ pthread_mutex_unlock(&the_mutex);
|
|
|
17b94a |
+}
|
|
|
17b94a |
+
|
|
|
17b94a |
+static void
|
|
|
17b94a |
+init_wdata(worker_data *data, int which)
|
|
|
17b94a |
+{
|
|
|
17b94a |
+ data->cb.which = which;
|
|
|
17b94a |
+ data->cb.seq = -1;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ data->iov.iov_base = malloc(1024 * 1024);
|
|
|
17b94a |
+ memset(data->iov.iov_base, 6,
|
|
|
17b94a |
+ 1024 * 1024); /* tail part never overwritten */
|
|
|
17b94a |
+}
|
|
|
17b94a |
+
|
|
|
17b94a |
+static void
|
|
|
17b94a |
+init()
|
|
|
17b94a |
+{
|
|
|
17b94a |
+ all_data.busy = 0;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ init_wdata(&all_data.wdata[0], 0);
|
|
|
17b94a |
+ init_wdata(&all_data.wdata[1], 1);
|
|
|
17b94a |
+}
|
|
|
17b94a |
+
|
|
|
17b94a |
+static void
|
|
|
17b94a |
+do_write(struct glfs_fd *fd, int content, int size, int64_t seq,
|
|
|
17b94a |
+ worker_data *wdata, const char *name)
|
|
|
17b94a |
+{
|
|
|
17b94a |
+ int ret;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ wdata->cb.size = size;
|
|
|
17b94a |
+ wdata->cb.seq = seq;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ if (content >= 0)
|
|
|
17b94a |
+ memset(wdata->iov.iov_base, content, size);
|
|
|
17b94a |
+ wdata->iov.iov_len = size;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ pthread_mutex_lock(&the_mutex);
|
|
|
17b94a |
+ printf("(%d) dispatching write \"%s\", offset %lx, len %x, seq %ld\n",
|
|
|
17b94a |
+ wdata->cb.which, name, (long)wdata->offset, size, (long)seq);
|
|
|
17b94a |
+ pthread_mutex_unlock(&the_mutex);
|
|
|
17b94a |
+ ret = glfs_pwritev_async(fd, &wdata->iov, 1, wdata->offset, 0,
|
|
|
17b94a |
+ completion_fnc, &wdata->cb);
|
|
|
17b94a |
+ assert(ret >= 0);
|
|
|
17b94a |
+}
|
|
|
17b94a |
+
|
|
|
17b94a |
+#define IDLE 0 // both workers must be idle
|
|
|
17b94a |
+#define ANY 1 // use any worker, other one may be busy
|
|
|
17b94a |
+
|
|
|
17b94a |
+int
|
|
|
17b94a |
+get_worker(int waitfor, int64_t excl_seq)
|
|
|
17b94a |
+{
|
|
|
17b94a |
+ int which;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ pthread_mutex_lock(&the_mutex);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ while (waitfor == IDLE && (all_data.busy & 3) != 0 ||
|
|
|
17b94a |
+ waitfor == ANY &&
|
|
|
17b94a |
+ ((all_data.busy & 3) == 3 ||
|
|
|
17b94a |
+ excl_seq >= 0 && (all_data.wdata[0].cb.seq == excl_seq ||
|
|
|
17b94a |
+ all_data.wdata[1].cb.seq == excl_seq)))
|
|
|
17b94a |
+ pthread_cond_wait(&the_cond, &the_mutex);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ if (!(all_data.busy & 1))
|
|
|
17b94a |
+ which = 0;
|
|
|
17b94a |
+ else
|
|
|
17b94a |
+ which = 1;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ all_data.busy |= (1 << which);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ pthread_mutex_unlock(&the_mutex);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ return which;
|
|
|
17b94a |
+}
|
|
|
17b94a |
+
|
|
|
17b94a |
+static int
|
|
|
17b94a |
+doit(struct glfs_fd *fd)
|
|
|
17b94a |
+{
|
|
|
17b94a |
+ int ret;
|
|
|
17b94a |
+ int64_t seq = 0;
|
|
|
17b94a |
+ int64_t offset = 0; // position in file, in blocks
|
|
|
17b94a |
+ int64_t base = 0x1000; // where to place the data, in blocks
|
|
|
17b94a |
+
|
|
|
17b94a |
+ int async_mode = ANY;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ init();
|
|
|
17b94a |
+
|
|
|
17b94a |
+ for (;;) {
|
|
|
17b94a |
+ int which;
|
|
|
17b94a |
+ worker_data *wdata;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ // for growing to the first offset
|
|
|
17b94a |
+ for (;;) {
|
|
|
17b94a |
+ int gap = base + 0x42 - offset;
|
|
|
17b94a |
+ if (!gap)
|
|
|
17b94a |
+ break;
|
|
|
17b94a |
+ if (gap > 80)
|
|
|
17b94a |
+ gap = 80;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ which = get_worker(IDLE, -1);
|
|
|
17b94a |
+ wdata = &all_data.wdata[which];
|
|
|
17b94a |
+
|
|
|
17b94a |
+ wdata->offset = offset << 9;
|
|
|
17b94a |
+ do_write(fd, 0, gap << 9, seq++, wdata, "gap-filling");
|
|
|
17b94a |
+
|
|
|
17b94a |
+ offset += gap;
|
|
|
17b94a |
+ }
|
|
|
17b94a |
+
|
|
|
17b94a |
+ // 8700
|
|
|
17b94a |
+ which = get_worker(IDLE, -1);
|
|
|
17b94a |
+ wdata = &all_data.wdata[which];
|
|
|
17b94a |
+
|
|
|
17b94a |
+ wdata->offset = (base + 0x42) << 9;
|
|
|
17b94a |
+ do_write(fd, 1, 62 << 9, seq++, wdata, "!8700");
|
|
|
17b94a |
+
|
|
|
17b94a |
+ // 8701
|
|
|
17b94a |
+ which = get_worker(IDLE, -1);
|
|
|
17b94a |
+ wdata = &all_data.wdata[which];
|
|
|
17b94a |
+
|
|
|
17b94a |
+ wdata->offset = (base + 0x42) << 9;
|
|
|
17b94a |
+ do_write(fd, 2, 55 << 9, seq++, wdata, "!8701");
|
|
|
17b94a |
+
|
|
|
17b94a |
+ // 8702
|
|
|
17b94a |
+ which = get_worker(async_mode, -1);
|
|
|
17b94a |
+ wdata = &all_data.wdata[which];
|
|
|
17b94a |
+
|
|
|
17b94a |
+ wdata->offset = (base + 0x79) << 9;
|
|
|
17b94a |
+ do_write(fd, 3, 54 << 9, seq++, wdata, "!8702");
|
|
|
17b94a |
+
|
|
|
17b94a |
+ // 8703
|
|
|
17b94a |
+ which = get_worker(async_mode, -1);
|
|
|
17b94a |
+ wdata = &all_data.wdata[which];
|
|
|
17b94a |
+
|
|
|
17b94a |
+ wdata->offset = (base + 0xaf) << 9;
|
|
|
17b94a |
+ do_write(fd, 4, 81 << 9, seq++, wdata, "!8703");
|
|
|
17b94a |
+
|
|
|
17b94a |
+ // 8704
|
|
|
17b94a |
+ // this writes both 5s and 6s
|
|
|
17b94a |
+ // the range of 5s is the one that overwrites 8703
|
|
|
17b94a |
+
|
|
|
17b94a |
+ which = get_worker(async_mode, seq - 1);
|
|
|
17b94a |
+ wdata = &all_data.wdata[which];
|
|
|
17b94a |
+
|
|
|
17b94a |
+ memset(wdata->iov.iov_base, 5, 81 << 9);
|
|
|
17b94a |
+ wdata->offset = (base + 0xaf) << 9;
|
|
|
17b94a |
+ do_write(fd, -1, 1623 << 9, seq++, wdata, "!8704");
|
|
|
17b94a |
+
|
|
|
17b94a |
+ offset = base + 0x706;
|
|
|
17b94a |
+ base += 0x1000;
|
|
|
17b94a |
+ if (base >= 0x100000)
|
|
|
17b94a |
+ break;
|
|
|
17b94a |
+ }
|
|
|
17b94a |
+
|
|
|
17b94a |
+ printf("done!\n");
|
|
|
17b94a |
+ fflush(stdout);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ pthread_mutex_lock(&the_mutex);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ while ((all_data.busy & 3) != 0)
|
|
|
17b94a |
+ pthread_cond_wait(&the_cond, &the_mutex);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ pthread_mutex_unlock(&the_mutex);
|
|
|
17b94a |
+
|
|
|
17b94a |
+ ret = glfs_close(fd);
|
|
|
17b94a |
+ assert(ret >= 0);
|
|
|
17b94a |
+ /*
|
|
|
17b94a |
+ ret = glfs_fini(glfs);
|
|
|
17b94a |
+ assert(ret >= 0);
|
|
|
17b94a |
+ */
|
|
|
17b94a |
+ return 0;
|
|
|
17b94a |
+}
|
|
|
17b94a |
+
|
|
|
17b94a |
+int
|
|
|
17b94a |
+main(int argc, char *argv[])
|
|
|
17b94a |
+{
|
|
|
17b94a |
+ int ret;
|
|
|
17b94a |
+ int open_flags = O_RDWR | O_DIRECT | O_TRUNC;
|
|
|
17b94a |
+ struct glfs_fd *fd;
|
|
|
17b94a |
+
|
|
|
17b94a |
+ glfs = glfs_new(argv[1]);
|
|
|
17b94a |
+ if (!glfs) {
|
|
|
17b94a |
+ printf("glfs_new!\n");
|
|
|
17b94a |
+ goto out;
|
|
|
17b94a |
+ }
|
|
|
17b94a |
+ ret = glfs_set_volfile_server(glfs, "tcp", "localhost", 24007);
|
|
|
17b94a |
+ if (ret < 0) {
|
|
|
17b94a |
+ printf("set_volfile!\n");
|
|
|
17b94a |
+ goto out;
|
|
|
17b94a |
+ }
|
|
|
17b94a |
+ ret = glfs_init(glfs);
|
|
|
17b94a |
+ if (ret) {
|
|
|
17b94a |
+ printf("init!\n");
|
|
|
17b94a |
+ goto out;
|
|
|
17b94a |
+ }
|
|
|
17b94a |
+ fd = glfs_open(glfs, argv[2], open_flags);
|
|
|
17b94a |
+ if (!fd) {
|
|
|
17b94a |
+ printf("open!\n");
|
|
|
17b94a |
+ goto out;
|
|
|
17b94a |
+ }
|
|
|
17b94a |
+ srand(time(NULL));
|
|
|
17b94a |
+ return doit(fd);
|
|
|
17b94a |
+out:
|
|
|
17b94a |
+ return 1;
|
|
|
17b94a |
+}
|
|
|
17b94a |
diff --git a/tests/bugs/write-behind/issue-884.t b/tests/bugs/write-behind/issue-884.t
|
|
|
17b94a |
new file mode 100755
|
|
|
17b94a |
index 0000000..2bcf7d1
|
|
|
17b94a |
--- /dev/null
|
|
|
17b94a |
+++ b/tests/bugs/write-behind/issue-884.t
|
|
|
17b94a |
@@ -0,0 +1,40 @@
|
|
|
17b94a |
+#!/bin/bash
|
|
|
17b94a |
+
|
|
|
17b94a |
+. $(dirname $0)/../../include.rc
|
|
|
17b94a |
+. $(dirname $0)/../../volume.rc
|
|
|
17b94a |
+
|
|
|
17b94a |
+# This test tries to detect a race condition in write-behind. It's based on a
|
|
|
17b94a |
+# reproducer written by Stefan Ring that is able to hit it sometimes. On my
|
|
|
17b94a |
+# system, it happened around 10% of the runs. This means that if this bug
|
|
|
17b94a |
+# appears again, this test will fail once every 10 runs. Most probably this
|
|
|
17b94a |
+# failure will be hidden by the automatic test retry of the testing framework.
|
|
|
17b94a |
+#
|
|
|
17b94a |
+# Please, if this test fails, it needs to be analyzed in detail.
|
|
|
17b94a |
+
|
|
|
17b94a |
+function run() {
|
|
|
17b94a |
+ "${@}" >/dev/null
|
|
|
17b94a |
+}
|
|
|
17b94a |
+
|
|
|
17b94a |
+cleanup
|
|
|
17b94a |
+
|
|
|
17b94a |
+TEST glusterd
|
|
|
17b94a |
+TEST pidof glusterd
|
|
|
17b94a |
+
|
|
|
17b94a |
+TEST $CLI volume create $V0 $H0:$B0/$V0
|
|
|
17b94a |
+# This makes it easier to hit the issue
|
|
|
17b94a |
+TEST $CLI volume set $V0 client-log-level TRACE
|
|
|
17b94a |
+TEST $CLI volume start $V0
|
|
|
17b94a |
+
|
|
|
17b94a |
+TEST $GFS --volfile-server=$H0 --volfile-id=$V0 $M0
|
|
|
17b94a |
+
|
|
|
17b94a |
+build_tester $(dirname $0)/issue-884.c -lgfapi
|
|
|
17b94a |
+
|
|
|
17b94a |
+TEST touch $M0/testfile
|
|
|
17b94a |
+
|
|
|
17b94a |
+# This program generates a file of 535694336 bytes with a fixed pattern
|
|
|
17b94a |
+TEST run $(dirname $0)/issue-884 $V0 testfile
|
|
|
17b94a |
+
|
|
|
17b94a |
+# This is the md5sum of the expected pattern without corruption
|
|
|
17b94a |
+EXPECT "ad105f9349345a70fc697632cbb5eec8" echo "$(md5sum $B0/$V0/testfile | awk '{ print $1; }')"
|
|
|
17b94a |
+
|
|
|
17b94a |
+cleanup
|
|
|
17b94a |
diff --git a/xlators/performance/write-behind/src/write-behind.c b/xlators/performance/write-behind/src/write-behind.c
|
|
|
17b94a |
index 70e281a..90a0bcf 100644
|
|
|
17b94a |
--- a/xlators/performance/write-behind/src/write-behind.c
|
|
|
17b94a |
+++ b/xlators/performance/write-behind/src/write-behind.c
|
|
|
17b94a |
@@ -1284,14 +1284,14 @@ __wb_pick_unwinds(wb_inode_t *wb_inode, list_head_t *lies)
|
|
|
17b94a |
|
|
|
17b94a |
wb_inode->window_current += req->orig_size;
|
|
|
17b94a |
|
|
|
17b94a |
+ wb_inode->gen++;
|
|
|
17b94a |
+
|
|
|
17b94a |
if (!req->ordering.fulfilled) {
|
|
|
17b94a |
/* burden increased */
|
|
|
17b94a |
list_add_tail(&req->lie, &wb_inode->liability);
|
|
|
17b94a |
|
|
|
17b94a |
req->ordering.lied = 1;
|
|
|
17b94a |
|
|
|
17b94a |
- wb_inode->gen++;
|
|
|
17b94a |
-
|
|
|
17b94a |
uuid_utoa_r(req->gfid, gfid);
|
|
|
17b94a |
gf_msg_debug(wb_inode->this->name, 0,
|
|
|
17b94a |
"(unique=%" PRIu64
|
|
|
17b94a |
--
|
|
|
17b94a |
1.8.3.1
|
|
|
17b94a |
|