Skip to content

Commit

Permalink
feat(list_snap): LIST_SNAP uzfs ioctl implementation (#49)
Browse files Browse the repository at this point in the history
For command zfs listsnap <dataset>, output will be in json format as:

vitta@vitta-laptop:~/openebs/lzfs$ ./cmd/zfs/zfs listsnap pool1/ds0 | jq
{
  "name": "pool1/ds0",
  "snaplist": {
    "istgt1": null,
    "snap4": null,
    "istgt2": null,
    "snap1": null,
    "snap2": null,
    "istgt3": null,
    "snap3": null
  }
}

This will NOT list the snapshots created through 'zfs snapshot' until the zrepl is restarted. However, it lists the snapshots created through istgt iscsi target.

This command implementation reads the snapshot list from disk once and caches the list. This cache will be updated with snapshot name for every SNAP_CREATE command from target. This cache will be served for every trigger of this ioctl.

Signed-off-by: Vitta <[email protected]>
  • Loading branch information
vishnuitta authored Mar 30, 2020
1 parent 91169dc commit 85dcd42
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 5 deletions.
2 changes: 2 additions & 0 deletions include/libuzfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ typedef struct uzfs_monitor {
_UZFS_IOC(ZFS_IOC_CLONE, 1, 1, "clone the volume") \
_UZFS_IOC(ZFS_IOC_ERROR_LOG, 0, 0, "get the error log") \
_UZFS_IOC(ZFS_IOC_STATS, 0, 0, "get the zfs volume stats") \
_UZFS_IOC(ZFS_IOC_LIST_SNAP, 0, 0, "get the volume snapshots list") \
_UZFS_IOC(ZFS_IOC_CLEAR, 1, 0, "clear the zpool error counters")


Expand All @@ -108,6 +109,7 @@ extern int uzfs_recv_response(int fd, zfs_cmd_t *zc);
extern int uzfs_client_init(const char *sock_path);
extern int is_main_thread(void);
int uzfs_ioc_stats(zfs_cmd_t *zc, nvlist_t *nvl);
int uzfs_ioc_list_snap(zfs_cmd_t *zc, nvlist_t *nvl);

extern int do_sendfd(int sock, int fd);
extern int do_recvfd(int sock);
Expand Down
1 change: 1 addition & 0 deletions include/mgmt_conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ int uzfs_get_snap_zv_ionum(zvol_info_t *, uint64_t, zvol_state_t **);
int uzfs_zvol_get_snap_dataset_with_io(zvol_info_t *zinfo,
char *snapname, uint64_t *snapshot_io_num, zvol_state_t **snap_zv);

extern int uzfs_zvol_add_nvl_snapshot_list(zvol_info_t *, nvlist_t *);
void disable_zinfo_mgmt_conn(zvol_info_t *zinfo);
void enable_zinfo_mgmt_conn(zvol_info_t *zinfo);

Expand Down
3 changes: 3 additions & 0 deletions include/zrepl_mgmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ typedef struct zvol_info_s {
ZFS_HISTOGRAM_IO_BLOCK + 1];
zfs_histogram_t uzfs_wio_histogram[ZFS_HISTOGRAM_IO_SIZE /
ZFS_HISTOGRAM_IO_BLOCK + 1];

struct json_object *snap_map;
kmutex_t snap_map_mutex;
} zvol_info_t;

typedef struct zvol_rebuild_scanner_info_s {
Expand Down
192 changes: 187 additions & 5 deletions src/mgmt_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,174 @@ internal_snapshot(char *snap)
return (B_FALSE);
}

/*
* This fn returns map of snapshots by walking through datasets in json
* format. Returns 0 on success.
* This will return EAGAIN if the replica is NOT connected to target.
*/
static struct json_object *
uzfs_zvol_fetch_snapshot_jmap(zvol_info_t *zinfo, int *err)
{
char *snapname;
boolean_t case_conflict;
uint64_t id, pos = 0;
int error = 0;
zvol_state_t *zv = (zvol_state_t *)zinfo->main_zv;
objset_t *os = zv->zv_objset;
dsl_dataset_t *ds;
dsl_pool_t *dp;
struct json_object *jmap = NULL;

snapname = kmem_zalloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP);

if ((os == NULL) || (os->os_dsl_dataset == NULL) ||
(os->os_dsl_dataset->ds_dir == NULL) ||
(os->os_dsl_dataset->ds_dir->dd_pool == NULL)) {
error = EAGAIN;
goto out;
}

jmap = json_object_new_object();
dp = os->os_dsl_dataset->ds_dir->dd_pool;

while (error == 0) {
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);
break;
}

if (internal_snapshot(snapname)) {
dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
continue;
}

dsl_pool_config_exit(dmu_objset_pool(os), FTAG);

json_object_object_add(jmap, snapname, NULL);
}

if ((error == ENOENT) || (error == 0)) {
error = 0;
} else {
json_object_put(jmap);
jmap = NULL;
}
out:
kmem_free(snapname, ZFS_MAX_DATASET_NAME_LEN);
*err = error;

return (jmap);
}

/*
* This fn returns reference to zinfo's map of snap json_object.
* If its NULL, it creates one and returns it
* Its the responsibility of caller to release the reference.
* Also, this fn need to be called with snap_map_mutex.
*/
int
uzfs_zvol_get_snap_jmap_ref(zvol_info_t *zinfo, struct json_object **jmapp)
{
struct json_object *jmap;
int error = 0;

ASSERT(MUTEX_HELD(&zinfo->snap_map_mutex));
*jmapp = NULL;
if (zinfo->snap_map == NULL) {

jmap = uzfs_zvol_fetch_snapshot_jmap(zinfo, &error);

if (error)
return (error);

zinfo->snap_map = jmap;
}
jmap = json_object_get(zinfo->snap_map);

*jmapp = jmap;
return (0);
}

/*
* This fn adds/deletes snapname to zinfo's snap_map in json format.
* If errors, sets snap_map to NULL as update failed
* This fn is currently called only if snapshot is created / deleted through
* istgt, but, not through CLI
*/
int
uzfs_zvol_update_snapshot_jmap(zvol_info_t *zinfo, char *snapname,
boolean_t add)
{
int ret = 0;
struct json_object *jmap;

mutex_enter(&zinfo->snap_map_mutex);
ret = uzfs_zvol_get_snap_jmap_ref(zinfo, &jmap);
if (ret == 0) {
if (add)
json_object_object_add(jmap, snapname, NULL);
else
json_object_object_del(jmap, snapname);
json_object_put(jmap);
} else {
if (zinfo->snap_map != NULL)
json_object_put(zinfo->snap_map);
zinfo->snap_map = NULL;
}
mutex_exit(&zinfo->snap_map_mutex);
return (0);
}

/*
* This fn gets the list of snapshots in json format, and adds it to nvl.
* `json_object_to_json_string_ext` is a single threaded fn, i.e., interally,
* this library uses a single buffer
* https://html.developreference.com/article/23032444/, and thus,
* lock has been added while using the jarray which is ref of snap_list jobj.
*
* Caller need to take refcount on zinfo so that snap_list is safe to use.
*/
int
uzfs_zvol_add_nvl_snapshot_list(zvol_info_t *zinfo, nvlist_t *nvl)
{
struct json_object *jmap, *jobj;
int error = 0;
const char *json_string;

mutex_enter(&zinfo->snap_map_mutex);
error = uzfs_zvol_get_snap_jmap_ref(zinfo, &jmap);

if (error != 0) {
mutex_exit(&zinfo->snap_map_mutex);
LOG_ERR("err %d for %s listsnap", error, zinfo->name);
return (error);
}

jobj = json_object_new_object();
json_object_object_add(jobj, "name",
json_object_new_string(zinfo->name));
json_object_object_add(jobj, "snaplist", jmap);

json_string = json_object_to_json_string_ext(jobj,
JSON_C_TO_STRING_PLAIN);
fnvlist_add_string(nvl, "listsnap", json_string);

/* This will decrement refcount of jmap as well */
json_object_put(jobj);
mutex_exit(&zinfo->snap_map_mutex);

return (0);
}

/*
* This fn returns list of snapshots by walking through datasets in string
* format. Returns 0 on success.
* This will return EAGAIN if the replica is NOT connected to target.
*/
static int
uzfs_zvol_fetch_snapshot_list(zvol_info_t *zinfo, char *snap, void **buf,
size_t *buflen)
Expand All @@ -729,17 +897,26 @@ uzfs_zvol_fetch_snapshot_list(zvol_info_t *zinfo, char *snap, void **buf,
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;
dsl_pool_t *dp;
objset_t *snap_os;
nvlist_t *nv;
struct json_object *jobj, *jarray, *jprop;
struct json_object *jobj, *jarray = NULL, *jprop;
const char *json_string;
uint64_t total_len;
char err_msg[128];

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

if ((os == NULL) || (os->os_dsl_dataset == NULL) ||
(os->os_dsl_dataset->ds_dir == NULL) ||
(os->os_dsl_dataset->ds_dir->dd_pool == NULL)) {
error = EAGAIN;
goto out;
}

dp = os->os_dsl_dataset->ds_dir->dd_pool;

while (error == 0) {
prop_error = TRUE;
dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
Expand All @@ -748,7 +925,7 @@ uzfs_zvol_fetch_snapshot_list(zvol_info_t *zinfo, char *snap, void **buf,
&case_conflict);
if (error) {
dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
goto out;
break;
}

if (internal_snapshot(snapname)) {
Expand Down Expand Up @@ -787,8 +964,8 @@ uzfs_zvol_fetch_snapshot_list(zvol_info_t *zinfo, char *snap, void **buf,
nvlist_free(nv);
} else {
snprintf(err_msg, sizeof (err_msg),
"Failed to fetch snapshot details.. err(%d)",
error);
"Failed to fetch snap %s details.. err(%d)",
snapname, error);
json_object_object_add(jprop, "error",
json_object_new_string(err_msg));
}
Expand Down Expand Up @@ -894,6 +1071,10 @@ uzfs_zvol_create_snapshot_update_zap(zvol_info_t *zinfo,
snapshot_io_num - 1);

ret = dmu_objset_snapshot_one(zinfo->name, snapname);

if (ret == 0)
uzfs_zvol_update_snapshot_jmap(zinfo, snapname, B_TRUE);

return (ret);
}

Expand Down Expand Up @@ -1063,6 +1244,7 @@ uzfs_zvol_execute_async_command(void *arg)
snap = async_task->payload;
dataset = kmem_asprintf("%s@%s", zinfo->name, snap);
rc = dsl_destroy_snapshot(dataset, B_FALSE);
uzfs_zvol_update_snapshot_jmap(zinfo, snap, B_FALSE);
strfree(dataset);
if (rc != 0) {
LOG_ERR("Failed to destroy %s@%s: %d",
Expand Down
6 changes: 6 additions & 0 deletions src/zrepl_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <uzfs_rebuilding.h>
#include <json-c/json_object.h>

#define ZVOL_THREAD_STACKSIZE (2 * 1024 * 1024)

Expand Down Expand Up @@ -322,6 +323,7 @@ uzfs_zinfo_init_mutex(zvol_info_t *zinfo)
(void) pthread_mutex_init(&zinfo->zinfo_mutex, NULL);
(void) pthread_cond_init(&zinfo->io_ack_cond, NULL);
(void) pthread_mutex_init(&zinfo->zinfo_ionum_mutex, NULL);
mutex_init(&zinfo->snap_map_mutex, NULL, MUTEX_DEFAULT, NULL);
}

static void
Expand All @@ -331,6 +333,7 @@ uzfs_zinfo_destroy_mutex(zvol_info_t *zinfo)
(void) pthread_mutex_destroy(&zinfo->zinfo_mutex);
(void) pthread_cond_destroy(&zinfo->io_ack_cond);
(void) pthread_mutex_destroy(&zinfo->zinfo_ionum_mutex);
mutex_destroy(&zinfo->snap_map_mutex);
}

int
Expand Down Expand Up @@ -441,6 +444,9 @@ uzfs_zinfo_free(zvol_info_t *zinfo)
(void) uzfs_zinfo_destroy_mutex(zinfo);
ASSERT(STAILQ_EMPTY(&zinfo->complete_queue));

if (zinfo->snap_map)
json_object_put(zinfo->snap_map);

free(zinfo);
return (0);
}
Expand Down

0 comments on commit 85dcd42

Please sign in to comment.