Skip to content

Commit 869bf8d

Browse files
authored
[ISS2062] fix(vol_destroy): race fixed between IO receiver and ack sender threads (#130)
Signed-off-by: Vishnu Itta <[email protected]>
1 parent 64a1f30 commit 869bf8d

File tree

4 files changed

+73
-27
lines changed

4 files changed

+73
-27
lines changed

include/zrepl_mgmt.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct zvol_io_cmd_s;
7272
typedef struct inject_delay_s {
7373
int helping_replica_rebuild_step;
7474
int pre_uzfs_write_data;
75+
int io_receiver_exit;
7576
} inject_delay_t;
7677

7778
typedef struct inject_error_s {
@@ -96,6 +97,11 @@ typedef struct zvol_info_s {
9697
zvol_state_t *zv;
9798
uint64_t refcnt;
9899

100+
/*
101+
* While checking for these big flags, do as below,
102+
* if (zinfo->is_io_ack_sender_created) (or)
103+
* if (!zinfo->is_io_ack_sender_created)
104+
*/
99105
union {
100106
struct {
101107
int is_io_ack_sender_created : 1;

lib/libzpool/zrepl_mgmt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ uzfs_mark_offline_and_free_zinfo(zvol_info_t *zinfo)
213213

214214
/* Wait for refcounts to be drained */
215215
while (zinfo->refcnt > 0) {
216-
LOG_DEBUG("Waiting for refcount to go down to"
217-
" zero on zvol:%s", zinfo->name);
216+
LOG_INFO("Waiting for refcount (%d) to go down to"
217+
" zero on zvol:%s", zinfo->refcnt, zinfo->name);
218218
sleep(5);
219219
}
220220

lib/libzrepl/data_conn.c

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ uzfs_zvol_worker(void *arg)
276276
rebuild_cmd_req = hdr->flags & ZVOL_OP_FLAG_REBUILD;
277277
read_metadata = hdr->flags & ZVOL_OP_FLAG_READ_METADATA;
278278

279-
if (zinfo->is_io_ack_sender_created == B_FALSE) {
279+
if (!zinfo->is_io_ack_sender_created) {
280280
if (!(rebuild_cmd_req && (hdr->opcode == ZVOL_OPCODE_WRITE)))
281281
zio_cmd_free(&zio_cmd);
282282
if (hdr->opcode == ZVOL_OPCODE_WRITE)
@@ -992,7 +992,7 @@ uzfs_zvol_rebuild_scanner_callback(off_t offset, size_t len,
992992

993993
while (1) {
994994
if ((zinfo->state == ZVOL_INFO_STATE_OFFLINE) ||
995-
(zinfo->is_io_ack_sender_created == B_FALSE))
995+
(!zinfo->is_io_ack_sender_created))
996996
return (-1);
997997
if (IS_REBUILD_HIT_MAX_CMD_LIMIT(zinfo))
998998
usleep(100);
@@ -1043,14 +1043,14 @@ uzfs_zvol_rebuild_scanner(void *arg)
10431043
read_socket:
10441044
if ((zinfo != NULL) &&
10451045
((zinfo->state == ZVOL_INFO_STATE_OFFLINE) ||
1046-
(zinfo->is_io_ack_sender_created == B_FALSE)))
1046+
(!zinfo->is_io_ack_sender_created)))
10471047
goto exit;
10481048

10491049
rc = uzfs_zvol_read_header(fd, &hdr);
10501050
if ((rc != 0) ||
10511051
((zinfo != NULL) &&
10521052
((zinfo->state == ZVOL_INFO_STATE_OFFLINE) ||
1053-
(zinfo->is_io_ack_sender_created == B_FALSE))))
1053+
(!zinfo->is_io_ack_sender_created))))
10541054
goto exit;
10551055

10561056
LOG_DEBUG("op_code=%d io_seq=%ld", hdr.opcode, hdr.io_seq);
@@ -1351,19 +1351,14 @@ uzfs_zvol_io_ack_sender(void *arg)
13511351
exit:
13521352
zinfo->zio_cmd_in_ack = NULL;
13531353
shutdown(fd, SHUT_RDWR);
1354-
LOG_INFO("Data connection for zvol %s closed on fd: %d",
1355-
zinfo->name, fd);
1356-
1357-
(void) pthread_mutex_lock(&zinfo->zinfo_mutex);
1358-
zinfo->is_io_ack_sender_created = B_FALSE;
1359-
(void) pthread_mutex_unlock(&zinfo->zinfo_mutex);
1360-
1361-
remove_pending_cmds_to_ack(fd, zinfo);
13621354

13631355
(void) pthread_mutex_lock(&zinfo->zinfo_mutex);
1356+
zinfo->is_io_ack_sender_created = 0;
13641357
zinfo->conn_closed = B_FALSE;
13651358
(void) pthread_mutex_unlock(&zinfo->zinfo_mutex);
13661359

1360+
LOG_INFO("Data connection for zvol %s closed on fd: %d",
1361+
zinfo->name, fd);
13671362
uzfs_zinfo_drop_refcnt(zinfo);
13681363

13691364
zk_thread_exit();
@@ -1424,14 +1419,14 @@ open_zvol(int fd, zvol_info_t **zinfopp)
14241419
(void) pthread_mutex_unlock(&zinfo->zinfo_mutex);
14251420
goto open_reply;
14261421
}
1427-
if (zinfo->is_io_ack_sender_created != B_FALSE) {
1422+
if (zinfo->is_io_ack_sender_created) {
14281423
LOG_ERR("zvol %s ack sender already present",
14291424
open_data.volname);
14301425
hdr.status = ZVOL_OP_STATUS_FAILED;
14311426
(void) pthread_mutex_unlock(&zinfo->zinfo_mutex);
14321427
goto open_reply;
14331428
}
1434-
if (zinfo->is_io_receiver_created != B_FALSE) {
1429+
if (zinfo->is_io_receiver_created) {
14351430
LOG_ERR("zvol %s io receiver already present",
14361431
open_data.volname);
14371432
hdr.status = ZVOL_OP_STATUS_FAILED;
@@ -1498,8 +1493,8 @@ open_zvol(int fd, zvol_info_t **zinfopp)
14981493
*zinfopp = zinfo;
14991494

15001495
zinfo->conn_closed = B_FALSE;
1501-
zinfo->is_io_ack_sender_created = B_TRUE;
1502-
zinfo->is_io_receiver_created = B_TRUE;
1496+
zinfo->is_io_ack_sender_created = 1;
1497+
zinfo->is_io_receiver_created = 1;
15031498
(void) pthread_mutex_unlock(&zinfo->zinfo_mutex);
15041499
thrd_arg = kmem_alloc(sizeof (thread_args_t), KM_SLEEP);
15051500
thrd_arg->fd = fd;
@@ -1602,8 +1597,15 @@ uzfs_zvol_io_receiver(void *arg)
16021597
zio_cmd, TQ_SLEEP);
16031598
}
16041599
exit:
1600+
#if DEBUG
1601+
if (inject_error.delay.io_receiver_exit == 1)
1602+
sleep(5);
1603+
#endif
16051604
(void) pthread_mutex_lock(&zinfo->zinfo_mutex);
1606-
zinfo->conn_closed = B_TRUE;
1605+
1606+
if (zinfo->is_io_ack_sender_created)
1607+
zinfo->conn_closed = B_TRUE;
1608+
16071609
/*
16081610
* Send signal to ack sender so that it can free
16091611
* zio_cmd, close fd and exit.
@@ -1617,18 +1619,23 @@ uzfs_zvol_io_receiver(void *arg)
16171619
*/
16181620
while (zinfo->conn_closed || zinfo->is_io_ack_sender_created) {
16191621
(void) pthread_mutex_unlock(&zinfo->zinfo_mutex);
1620-
usleep(1000);
1622+
LOG_INFO("Waiting for conn_closed (%d) and ack_sender (%d)"
1623+
"to be false for %s", zinfo->conn_closed,
1624+
zinfo->is_io_ack_sender_created, zinfo->name);
1625+
sleep(1);
16211626
(void) pthread_mutex_lock(&zinfo->zinfo_mutex);
16221627
}
16231628
(void) pthread_mutex_unlock(&zinfo->zinfo_mutex);
16241629

1630+
remove_pending_cmds_to_ack(fd, zinfo);
1631+
16251632
shutdown_fds_related_to_zinfo(zinfo);
16261633

16271634
zinfo->io_ack_waiting = 0;
16281635

16291636
taskq_wait(zinfo->uzfs_zvol_taskq);
16301637
reinitialize_zv_state(zinfo->zv);
1631-
zinfo->is_io_receiver_created = B_FALSE;
1638+
zinfo->is_io_receiver_created = 0;
16321639
uzfs_zinfo_drop_refcnt(zinfo);
16331640
thread_exit:
16341641
close(fd);

tests/cbtest/gtest/test_uzfs.cc

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <mgmt_conn.h>
3838
#include <data_conn.h>
3939
#include <uzfs_mgmt.h>
40+
#include <sys/eventfd.h>
4041
#include <sys/epoll.h>
4142

4243
#include <uzfs_rebuilding.h>
@@ -45,14 +46,17 @@
4546

4647
char *ds_name;
4748
char *ds_name2;
49+
char *ds_name_todelete;
4850
char *pool;
4951
spa_t *spa;
5052
zvol_state_t *zv;
5153
zvol_state_t *zv2;
54+
zvol_state_t *zv_todelete;
5255
zvol_info_t *zinfo;
5356
zvol_info_t *zinfo2;
5457
int rebuild_test_case = 0;
5558
int data_conn_fd = -1;
59+
int data_conn_fd_todelete = -1;
5660

5761
extern void (*zinfo_create_hook)(zvol_info_t *, nvlist_t *);
5862
extern void (*zinfo_destroy_hook)(zvol_info_t *);
@@ -202,7 +206,7 @@ uzfs_mock_rebuild_scanner(void *arg)
202206

203207
if (rebuild_test_case == 6) {
204208
close(data_conn_fd);
205-
while (zinfo->is_io_receiver_created == B_TRUE)
209+
while (zinfo->is_io_receiver_created)
206210
sleep(2);
207211
sleep(5);
208212
}
@@ -257,25 +261,31 @@ TEST(uZFS, Setup) {
257261
int ret;
258262
char *pool_ds;
259263
char *pool_ds2;
264+
char *pool_ds_todelete;
260265
ds_name = (char *)malloc(MAXNAMELEN);
261266
ds_name2 = (char *)malloc(MAXNAMELEN);
267+
ds_name_todelete = (char *)malloc(MAXNAMELEN);
262268
pool_ds = (char *)malloc(MAXNAMELEN);
263269
pool_ds2 = (char *)malloc(MAXNAMELEN);
270+
pool_ds_todelete = (char *)malloc(MAXNAMELEN);
264271
path = (char *)malloc(MAXNAMELEN);
265272
pool = (char *)malloc(MAXNAMELEN);
266273

267274
GtestUtils::strlcpy(path, "/tmp/uztest.1a", MAXNAMELEN);
268275
GtestUtils::strlcpy(pool, "pool1", MAXNAMELEN);
269276
GtestUtils::strlcpy(ds_name, "vol1", MAXNAMELEN);
270277
GtestUtils::strlcpy(ds_name2, "vol3", MAXNAMELEN);
278+
GtestUtils::strlcpy(ds_name_todelete, "vol_todelete", MAXNAMELEN);
271279
GtestUtils::strlcpy(pool_ds, "pool1/vol1", MAXNAMELEN);
272280
GtestUtils::strlcpy(pool_ds2, "pool1/vol3", MAXNAMELEN);
281+
GtestUtils::strlcpy(pool_ds_todelete, "pool1/vol_todelete", MAXNAMELEN);
273282
signal(SIGPIPE, SIG_IGN);
274283

275284
mutex_init(&conn_list_mtx, NULL, MUTEX_DEFAULT, NULL);
276285
SLIST_INIT(&uzfs_mgmt_conns);
277286
mutex_init(&async_tasks_mtx, NULL, MUTEX_DEFAULT, NULL);
278-
mgmt_eventfd = -1;
287+
288+
mgmt_eventfd = eventfd(0, EFD_NONBLOCK);
279289

280290
uzfs_init();
281291
init_zrepl();
@@ -306,6 +316,12 @@ TEST(uZFS, Setup) {
306316
zinfo2 = uzfs_zinfo_lookup(ds_name2);
307317
EXPECT_EQ(0, !zinfo2);
308318

319+
uzfs_create_dataset(spa, ds_name_todelete, 1024*1024*1024, 512, &zv_todelete);
320+
uzfs_hold_dataset(zv_todelete);
321+
uzfs_update_metadata_granularity(zv_todelete, 512);
322+
strncpy(zv_todelete->zv_target_host,"127.0.0.1:5959", MAXNAMELEN);
323+
uzfs_zinfo_init(zv_todelete, pool_ds_todelete, NULL);
324+
309325
uzfs_zinfo_init(zv, pool_ds, NULL);
310326
zinfo = uzfs_zinfo_lookup(ds_name);
311327
EXPECT_EQ(0, !zinfo);
@@ -416,12 +432,12 @@ TEST(uZFS, asyncTaskProps) {
416432
TEST(uZFS, EmptyCreateProps) {
417433
uzfs_mgmt_conn_t *conn;
418434

419-
EXPECT_EQ(2, uzfs_mgmt_conn_list_count(&uzfs_mgmt_conns));
435+
EXPECT_EQ(3, uzfs_mgmt_conn_list_count(&uzfs_mgmt_conns));
420436
conn = SLIST_FIRST(&uzfs_mgmt_conns);
421437
EXPECT_EQ(1, conn->conn_refcount);
422438

423439
zinfo_create_cb(zinfo, NULL);
424-
EXPECT_EQ(2, uzfs_mgmt_conn_list_count(&uzfs_mgmt_conns));
440+
EXPECT_EQ(3, uzfs_mgmt_conn_list_count(&uzfs_mgmt_conns));
425441
conn = SLIST_FIRST(&uzfs_mgmt_conns);
426442
EXPECT_EQ(2, conn->conn_refcount);
427443
}
@@ -514,7 +530,7 @@ TEST(uZFS, TestStartRebuild) {
514530
rebuild_status[3] = ZVOL_REBUILDING_ERRORED;
515531
rebuild_status[4] = ZVOL_REBUILDING_FAILED;
516532

517-
EXPECT_EQ(2, uzfs_mgmt_conn_list_count(&uzfs_mgmt_conns));
533+
EXPECT_EQ(3, uzfs_mgmt_conn_list_count(&uzfs_mgmt_conns));
518534
EXPECT_EQ(2, zinfo->refcnt);
519535
conn = SLIST_FIRST(&uzfs_mgmt_conns);
520536

@@ -1174,6 +1190,23 @@ static void do_data_connection(int &data_fd, std::string host, uint16_t port,
11741190
data_fd = fd;
11751191
}
11761192

1193+
/*
1194+
* This testcase is to let ack sender to exit before io_receiver to detect any races
1195+
*/
1196+
TEST(uZFS, CreateAndDestroyDelayIOReceiverExit) {
1197+
io_receiver = &uzfs_zvol_io_receiver;
1198+
do_data_connection(data_conn_fd_todelete, "127.0.0.1", IO_SERVER_PORT, "vol_todelete");
1199+
#if DEBUG
1200+
inject_error.delay.io_receiver_exit = 1;
1201+
#endif
1202+
close(data_conn_fd_todelete);
1203+
uzfs_zinfo_destroy("pool1/vol_todelete", spa);
1204+
#if DEBUG
1205+
sleep(5);
1206+
inject_error.delay.io_receiver_exit = 0;
1207+
#endif
1208+
}
1209+
11771210
TEST(uZFS, TestRebuildCompleteWithDataConn) {
11781211
io_receiver = &uzfs_zvol_io_receiver;
11791212

@@ -1191,7 +1224,7 @@ TEST(uZFS, TestRebuildComplete) {
11911224
EXPECT_EQ(ZVOL_STATUS_HEALTHY, uzfs_zvol_get_status(zinfo->zv));
11921225

11931226
close(data_conn_fd);
1194-
while (zinfo->is_io_receiver_created == B_TRUE)
1227+
while (zinfo->is_io_receiver_created)
11951228
sleep(2);
11961229
sleep(5);
11971230
memset(&zinfo->zv->rebuild_info, 0, sizeof (zvol_rebuild_info_t));

0 commit comments

Comments
 (0)