Skip to content

Commit

Permalink
[US2703] support list snapshots capability in zrepl (openzfs#112)
Browse files Browse the repository at this point in the history
Signed-off-by: mayank <mayank.patel@cloudbyte.com>
  • Loading branch information
mynktl authored and vishnuitta committed Sep 21, 2018
1 parent 78703fc commit 571f8d7
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
sudo apt-get update -qq
sudo apt-get install --yes -qq gcc-6 g++-6
sudo apt-get install --yes -qq build-essential autoconf libtool gawk alien fakeroot linux-headers-$(uname -r) libaio-dev
sudo apt-get install --yes -qq zlib1g-dev uuid-dev libattr1-dev libblkid-dev libselinux-dev libudev-dev libssl-dev
sudo apt-get install --yes -qq zlib1g-dev uuid-dev libattr1-dev libblkid-dev libselinux-dev libudev-dev libssl-dev libjson-c-dev
sudo apt-get install --yes -qq lcov libjemalloc-dev
sudo apt-get install --yes -qq parted lsscsi ksh attr acl nfs-kernel-server fio
sudo apt-get install --yes -qq libgtest-dev cmake
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ before_install:
- sudo apt-get update -qq
- sudo apt-get install --yes -qq gcc-6 g++-6
- sudo apt-get install --yes -qq build-essential autoconf libtool gawk alien fakeroot linux-headers-$(uname -r) libaio-dev
- sudo apt-get install --yes -qq zlib1g-dev uuid-dev libattr1-dev libblkid-dev libselinux-dev libudev-dev libssl-dev
- sudo apt-get install --yes -qq zlib1g-dev uuid-dev libattr1-dev libblkid-dev libselinux-dev libudev-dev libssl-dev libjson-c-dev
- sudo apt-get install --yes -qq lcov libjemalloc-dev
# packages for tests
- sudo apt-get install --yes -qq parted lsscsi ksh attr acl nfs-kernel-server fio
Expand Down
2 changes: 2 additions & 0 deletions cmd/zrepl/zrepl.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ main(int argc, char **argv)
io_receiver = uzfs_zvol_io_receiver;
rebuild_scanner = uzfs_zvol_rebuild_scanner;

SLIST_INIT(&uzfs_mgmt_conns);

rc = uzfs_init();
if (rc != 0) {
LOG_ERR("initialization errored: %d", rc);
Expand Down
4 changes: 3 additions & 1 deletion include/mgmt_conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ typedef struct async_task {
boolean_t finished; // async cmd has finished
zvol_info_t *zinfo;
zvol_io_hdr_t hdr; // header of the incoming request
void *payload; // snapshot name
void *payload; // snapshot name
void *response; // response of async task
int payload_length; // length of payload in bytes
int response_length; // length of response data in bytes
int status; // status which should be sent back
} async_task_t;

Expand Down
13 changes: 13 additions & 0 deletions include/zrepl_prot.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ enum zvol_op_code {
ZVOL_OPCODE_REBUILD_COMPLETE,
ZVOL_OPCODE_SNAP_CREATE,
ZVOL_OPCODE_SNAP_DESTROY,
ZVOL_OPCODE_SNAP_LIST,
ZVOL_OPCODE_RESIZE,
ZVOL_OPCODE_STATS,
} __attribute__((packed));
Expand Down Expand Up @@ -200,6 +201,18 @@ struct zvol_io_rw_hdr {
uint64_t len;
} __attribute__((packed));

struct zvol_snapshot_list {
uint64_t zvol_guid; /* Replica identity */
uint64_t data_len; /* SNAP_LIST response data length */

/*
* Error code, if any error happened while
* executing SNAP_LIST opcode at replica
*/
int error;
char data[0]; /* SNAP_LIST response data */
};

#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST((head)); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
Expand Down
2 changes: 1 addition & 1 deletion lib/libzrepl/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ libzrepl_la_LIBADD = \
$(top_builddir)/lib/libzpool/libzpool.la \
$(top_builddir)/lib/libicp/libicp.la

libzrepl_la_LIBADD += $(ZLIB) -ldl -lm -laio $(JEMALLOCLIB)
libzrepl_la_LIBADD += $(ZLIB) -ldl -lm -laio $(JEMALLOCLIB) -ljson-c
libzrepl_la_LDFLAGS = -version-info 2:0:0

EXTRA_DIST = $(USER_C)
227 changes: 212 additions & 15 deletions lib/libzrepl/mgmt_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@
#include <sys/dsl_destroy.h>
#include <sys/dsl_dir.h>
#include <sys/dmu_objset.h>
#include <sys/dsl_prop.h>
#include <string.h>
#include <zrepl_prot.h>
#include <uzfs_mgmt.h>
#include <json-c/json_object.h>
#include <libnvpair.h>

#include <mgmt_conn.h>
#include "data_conn.h"
#include "uzfs_rebuilding.h"

/*
* This file contains implementation of event loop (uzfs_zvol_mgmt_thread).
Expand Down Expand Up @@ -573,12 +577,174 @@ uzfs_zvol_stats(uzfs_mgmt_conn_t *conn, zvol_io_hdr_t *hdrp, zvol_info_t *zinfo)
return (reply_data(conn, &hdr, &stat, sizeof (stat)));
}

static void
uzfs_append_snapshot_properties(nvlist_t *nv, struct json_object *robj,
char *prop_name)
{
nvpair_t *elem = NULL;
nvlist_t *nvlist_value;
uint64_t value = 0;
char *str_value;
int len;

if (nv == NULL) {
return;
}

while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
switch (nvpair_type(elem)) {
case DATA_TYPE_UINT64:
nvpair_value_uint64(elem, &value);
if (!prop_name) {
if (strcmp(nvpair_name(elem), "source"))
LOG_ERR("property name not set.. "
"elem:%s val:%lu",
nvpair_name(elem), value);
} else {
len = snprintf(NULL, 0, "%lu", value) + 1;
str_value = kmem_zalloc(len, KM_SLEEP);
snprintf(str_value, len, "%lu", value);
json_object_object_add(robj, prop_name,
json_object_new_string(str_value));
kmem_free(str_value, len);
}
break;

case DATA_TYPE_STRING:
nvpair_value_string(elem, &str_value);
if (!prop_name) {
if (strcmp(nvpair_name(elem), "source"))
LOG_ERR("property name not set.. "
"elem:%s val:%lu",
nvpair_name(elem), value);
} else
json_object_object_add(robj, prop_name,
json_object_new_string(str_value));
break;

case DATA_TYPE_NVLIST:
(void) nvpair_value_nvlist(elem, &nvlist_value);
uzfs_append_snapshot_properties(nvlist_value, robj,
nvpair_name(elem));
break;

default:
LOG_ERR("nvpair type : %d name:%s\n",
nvpair_type(elem), nvpair_name(elem));
}
prop_name = NULL;
}
}

static int
uzfs_zvol_fetch_snapshot_list(zvol_info_t *zinfo, void **buf,
size_t *buflen)
{
char *snapname;
boolean_t case_conflict, prop_error;
uint64_t id, pos = 0;
int error = 0;
zvol_state_t *zv = (zvol_state_t *)zinfo->main_zv;
objset_t *os = zv->zv_objset;
struct zvol_snapshot_list *snap_list;
dsl_dataset_t *ds;
dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
objset_t *snap_os;
nvlist_t *nv;
struct json_object *jobj, *jarray, *jprop;
const char *json_string;
uint64_t total_len;
char err_msg[128];

snapname = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP);
jarray = json_object_new_array();

while (error == 0) {
prop_error = TRUE;
dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
error = dmu_snapshot_list_next(os,
ZFS_MAX_DATASET_NAME_LEN, snapname, &id, &pos,
&case_conflict);
if (error) {
dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
goto out;
}

error = dsl_dataset_hold_obj(dp, id, FTAG, &ds);
if (error == 0) {
error = dmu_objset_from_ds(ds, &snap_os);
if (error == 0 &&
!dsl_prop_get_all(snap_os, &nv)) {
dmu_objset_stats(snap_os, nv);
if (zvol_get_stats(snap_os, nv))
LOG_ERR("Failed to get zvol "
"stats");
prop_error = FALSE;
} else
prop_error = TRUE;
dsl_dataset_rele(ds, FTAG);
}
dsl_pool_config_exit(dmu_objset_pool(os), FTAG);

if (strcmp(snapname, REBUILD_SNAPSHOT_SNAPNAME) == 0 ||
strncmp(snapname, IO_DIFF_SNAPNAME,
sizeof (IO_DIFF_SNAPNAME)) == 0) {
if (!prop_error)
nvlist_free(nv);
continue;
}

jobj = json_object_new_object();
json_object_object_add(jobj, "name",
json_object_new_string(snapname));

jprop = json_object_new_object();
if (error == 0 && !prop_error) {
uzfs_append_snapshot_properties(nv, jprop, NULL);
nvlist_free(nv);
} else {
snprintf(err_msg, sizeof (err_msg),
"Failed to fetch snapshot details.. err(%d)",
error);
json_object_object_add(jprop, "error",
json_object_new_string(err_msg));
}
json_object_object_add(jobj, "properties", jprop);
json_object_array_add(jarray, jobj);
}

out:
jobj = json_object_new_object();
json_object_object_add(jobj, "snapshot", jarray);

kmem_free(snapname, ZFS_MAX_DATASET_NAME_LEN);

json_string = json_object_to_json_string_ext(jobj,
JSON_C_TO_STRING_PLAIN);
total_len = strlen(json_string);
snap_list = malloc(total_len + sizeof (*snap_list));
memset(snap_list, 0, total_len + sizeof (*snap_list));
snap_list->zvol_guid = zinfo->zvol_guid;
snap_list->error = (error == ENOENT) ? 0 : error;
snap_list->data_len = total_len;
strncpy(snap_list->data, json_string, total_len);
json_object_put(jobj);

*buf = snap_list;
*buflen = total_len + sizeof (*snap_list);

return (0);
}

static void
free_async_task(async_task_t *async_task)
{
ASSERT(MUTEX_HELD(&async_tasks_mtx));
uzfs_zinfo_drop_refcnt(async_task->zinfo);
kmem_free(async_task->payload, async_task->payload_length);
if (async_task->payload)
kmem_free(async_task->payload, async_task->payload_length);
if (async_task->response)
kmem_free(async_task->response, async_task->response_length);
kmem_free(async_task, sizeof (*async_task));
}

Expand All @@ -600,8 +766,17 @@ finish_async_tasks(void)
continue;
/* connection could have been closed in the meantime */
if (!async_task->conn_closed) {
rc = reply_nodata(async_task->conn, async_task->status,
async_task->hdr.opcode, async_task->hdr.io_seq);
if (async_task->response) {
async_task->hdr.status = async_task->status;
async_task->hdr.len =
async_task->response_length;
rc = reply_data(async_task->conn,
&async_task->hdr, async_task->response,
async_task->response_length);
} else
rc = reply_nodata(async_task->conn,
async_task->status, async_task->hdr.opcode,
async_task->hdr.io_seq);
}
SLIST_REMOVE(&async_tasks, async_task, async_task, task_next);
free_async_task(async_task);
Expand Down Expand Up @@ -781,6 +956,17 @@ uzfs_zvol_execute_async_command(void *arg)
async_task->status = ZVOL_OP_STATUS_OK;
}
break;
case ZVOL_OPCODE_SNAP_LIST:
rc = uzfs_zvol_fetch_snapshot_list(zinfo, &async_task->response,
(size_t *)&async_task->response_length);
if (rc != 0) {
LOG_ERR("Failed to fetch snapshot list for zvol %s\n",
zinfo->name);
async_task->status = ZVOL_OP_STATUS_FAILED;
} else {
async_task->status = ZVOL_OP_STATUS_OK;
}
break;
default:
ASSERT(0);
}
Expand Down Expand Up @@ -816,9 +1002,11 @@ uzfs_zvol_dispatch_command(uzfs_mgmt_conn_t *conn, zvol_io_hdr_t *hdrp,
arg->conn = conn;
arg->zinfo = zinfo;
arg->hdr = *hdrp;
arg->payload_length = length;
arg->payload = kmem_zalloc(arg->payload_length, KM_SLEEP);
memcpy(arg->payload, payload, arg->payload_length);
if (length) {
arg->payload_length = length;
arg->payload = kmem_zalloc(arg->payload_length, KM_SLEEP);
memcpy(arg->payload, payload, arg->payload_length);
}

mutex_enter(&async_tasks_mtx);
SLIST_INSERT_HEAD(&async_tasks, arg, task_next);
Expand Down Expand Up @@ -1006,7 +1194,7 @@ process_message(uzfs_mgmt_conn_t *conn)
size_t payload_size = conn->conn_bufsiz;
zvol_op_resize_data_t *resize_data;
zvol_info_t *zinfo;
char *snap;
char *snap = NULL;
int rc = 0;

conn->conn_hdr = NULL;
Expand Down Expand Up @@ -1073,22 +1261,25 @@ process_message(uzfs_mgmt_conn_t *conn)

case ZVOL_OPCODE_SNAP_CREATE:
case ZVOL_OPCODE_SNAP_DESTROY:
case ZVOL_OPCODE_SNAP_LIST:
if (payload_size == 0 || payload_size >= MAX_NAME_LEN) {
rc = reply_nodata(conn, ZVOL_OP_STATUS_FAILED,
hdrp->opcode, hdrp->io_seq);
break;
}
strlcpy(zvol_name, payload, payload_size);
zvol_name[payload_size] = '\0';
snap = strchr(zvol_name, '@');
if (snap == NULL) {
LOG_ERR("Invalid snapshot name: %s",
zvol_name);
rc = reply_nodata(conn, ZVOL_OP_STATUS_FAILED,
hdrp->opcode, hdrp->io_seq);
break;
if (hdrp->opcode != ZVOL_OPCODE_SNAP_LIST) {
snap = strchr(zvol_name, '@');
if (snap == NULL) {
LOG_ERR("Invalid snapshot name: %s",
zvol_name);
rc = reply_nodata(conn, ZVOL_OP_STATUS_FAILED,
hdrp->opcode, hdrp->io_seq);
break;
}
*snap++ = '\0';
}
*snap++ = '\0';
/* ref will be released when async command has finished */
if ((zinfo = uzfs_zinfo_lookup(zvol_name)) == NULL) {
LOGERRCONN(conn, "Unknown zvol: %s", zvol_name);
Expand All @@ -1104,6 +1295,12 @@ process_message(uzfs_mgmt_conn_t *conn)
hdrp->opcode, hdrp->io_seq);
break;
}
if (hdrp->opcode == ZVOL_OPCODE_SNAP_LIST) {
LOGCONN(conn, "Snaplist command for %s", zinfo->name);
rc = uzfs_zvol_dispatch_command(conn, hdrp, NULL, 0,
zinfo);
break;
}
if (uzfs_zvol_get_status(zinfo->main_zv) !=
ZVOL_STATUS_HEALTHY) {
uzfs_zinfo_drop_refcnt(zinfo);
Expand Down
2 changes: 1 addition & 1 deletion tests/cbtest/gtest/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ test_uzfsserver_CXXFLAGS = -std=c++11
test_uzfsserver_LDFLAGS = -pthread -lgtest -lgtest_main
test_uzfs_CXXFLAGS = -std=c++11
test_uzfs_LDFLAGS = -pthread -lgtest -lgtest_main
test_zrepl_prot_LDFLAGS = -pthread -lgtest -lgtest_main
test_zrepl_prot_LDFLAGS = -pthread -lgtest -lgtest_main -ljson-c
test_zfs_LDFLAGS = -pthread -lgtest -lgtest_main
Loading

0 comments on commit 571f8d7

Please sign in to comment.