Skip to content
This repository
Browse code

Speed up 'zfs list -t snapshot -o name -s name'

FreeBSD #xxx:  Dramatically optimize listing snapshots when user
requests only snapshot names and wants to sort them by name, ie.
when executes:

  # zfs list -t snapshot -o name -s name

Because only name is needed we don't have to read all snapshot
properties.

Below you can find how long does it take to list 34509 snapshots
from a single disk pool before and after this change with cold and
warm cache:

    before:

        # time zfs list -t snapshot -o name -s name > /dev/null
        cold cache: 525s
        warm cache: 218s

    after:

        # time zfs list -t snapshot -o name -s name > /dev/null
        cold cache: 1.7s
        warm cache: 1.1s

NOTE: This patch only appears in FreeBSD.  If/when Illumos picks up
the change we may want to drop this patch and adopt their version.
However, for now this addresses a real issue.

Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
Issue #450
  • Loading branch information...
commit 0cee24064a79f9c01fc4521543c37acea538405f 1 parent 74497b7
Pawel Jakub Dawidek pjd authored behlendorf committed
30 cmd/zfs/zfs_iter.c
@@ -20,6 +20,7 @@
20 20 */
21 21 /*
22 22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23 + * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
23 24 */
24 25
25 26 #include <libintl.h>
@@ -129,8 +130,11 @@ zfs_callback(zfs_handle_t *zhp, void *data)
129 130 cb->cb_depth++;
130 131 if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
131 132 (void) zfs_iter_filesystems(zhp, zfs_callback, data);
132   - if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps)
133   - (void) zfs_iter_snapshots(zhp, zfs_callback, data);
  133 + if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps) {
  134 + (void) zfs_iter_snapshots(zhp,
  135 + (cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback,
  136 + data);
  137 + }
134 138 cb->cb_depth--;
135 139 }
136 140
@@ -184,6 +188,13 @@ zfs_free_sort_columns(zfs_sort_column_t *sc)
184 188 }
185 189 }
186 190
  191 +int
  192 +zfs_sort_only_by_name(const zfs_sort_column_t *sc)
  193 +{
  194 + return (sc != NULL && sc->sc_next == NULL &&
  195 + sc->sc_prop == ZFS_PROP_NAME);
  196 +}
  197 +
187 198 /* ARGSUSED */
188 199 static int
189 200 zfs_compare(const void *larg, const void *rarg, void *unused)
@@ -224,7 +235,13 @@ zfs_compare(const void *larg, const void *rarg, void *unused)
224 235 lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
225 236 rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
226 237
227   - if (lcreate < rcreate)
  238 + /*
  239 + * Both lcreate and rcreate being 0 means we don't have
  240 + * properties and we should compare full name.
  241 + */
  242 + if (lcreate == 0 && rcreate == 0)
  243 + ret = strcmp(lat + 1, rat + 1);
  244 + else if (lcreate < rcreate)
228 245 ret = -1;
229 246 else if (lcreate > rcreate)
230 247 ret = 1;
@@ -290,7 +307,14 @@ zfs_sort(const void *larg, const void *rarg, void *data)
290 307 if (rvalid)
291 308 verify(nvlist_lookup_string(rval,
292 309 ZPROP_VALUE, &rstr) == 0);
  310 + } else if (psc->sc_prop == ZFS_PROP_NAME) {
  311 + lvalid = rvalid = B_TRUE;
  312 +
  313 + (void) strlcpy(lbuf, zfs_get_name(l), sizeof(lbuf));
  314 + (void) strlcpy(rbuf, zfs_get_name(r), sizeof(rbuf));
293 315
  316 + lstr = lbuf;
  317 + rstr = rbuf;
294 318 } else if (zfs_prop_is_string(psc->sc_prop)) {
295 319 lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,
296 320 sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0);
2  cmd/zfs/zfs_iter.h
@@ -43,11 +43,13 @@ typedef struct zfs_sort_column {
43 43 #define ZFS_ITER_PROP_LISTSNAPS (1 << 2)
44 44 #define ZFS_ITER_DEPTH_LIMIT (1 << 3)
45 45 #define ZFS_ITER_RECVD_PROPS (1 << 4)
  46 +#define ZFS_ITER_SIMPLE (1 << 5)
46 47
47 48 int zfs_for_each(int, char **, int options, zfs_type_t,
48 49 zfs_sort_column_t *, zprop_list_t **, int, zfs_iter_f, void *);
49 50 int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t);
50 51 void zfs_free_sort_columns(zfs_sort_column_t *);
  52 +int zfs_sort_only_by_name(const zfs_sort_column_t *);
51 53
52 54 #ifdef __cplusplus
53 55 }
14 cmd/zfs/zfs_main.c
@@ -2650,7 +2650,12 @@ print_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted)
2650 2650 first = B_FALSE;
2651 2651 }
2652 2652
2653   - if (pl->pl_prop != ZPROP_INVAL) {
  2653 + if (pl->pl_prop == ZFS_PROP_NAME) {
  2654 + (void) strlcpy(property, zfs_get_name(zhp),
  2655 + sizeof(property));
  2656 + propstr = property;
  2657 + right_justify = zfs_prop_align_right(pl->pl_prop);
  2658 + } else if (pl->pl_prop != ZPROP_INVAL) {
2654 2659 if (zfs_prop_get(zhp, pl->pl_prop, property,
2655 2660 sizeof (property), NULL, NULL, 0, B_FALSE) != 0)
2656 2661 propstr = "-";
@@ -2811,6 +2816,13 @@ zfs_do_list(int argc, char **argv)
2811 2816 fields = default_fields;
2812 2817
2813 2818 /*
  2819 + * If we are only going to list snapshot names and sort by name,
  2820 + * then we can use faster version.
  2821 + */
  2822 + if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol))
  2823 + flags |= ZFS_ITER_SIMPLE;
  2824 +
  2825 + /*
2814 2826 * If "-o space" and no types were specified, don't display snapshots.
2815 2827 */
2816 2828 if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
2  include/libzfs.h
@@ -516,7 +516,7 @@ extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *);
516 516 extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
517 517 extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
518 518 extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
519   -extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
  519 +extern int zfs_iter_snapshots(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
520 520 extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
521 521
522 522 typedef struct get_all_cb {
3  include/sys/zfs_ioctl.h
@@ -286,7 +286,8 @@ typedef struct zfs_cmd {
286 286 boolean_t zc_temphold;
287 287 uint64_t zc_action_handle;
288 288 int zc_cleanup_fd;
289   - uint8_t zc_pad[4]; /* alignment */
  289 + uint8_t zc_simple;
  290 + uint8_t zc_pad[3]; /* alignment */
290 291 uint64_t zc_sendobj;
291 292 uint64_t zc_fromobj;
292 293 uint64_t zc_createtxg;
41 lib/libzfs/libzfs_dataset.c
@@ -23,6 +23,7 @@
23 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 24 * Copyright (c) 2010 Nexenta Systems, Inc. All rights reserved.
25 25 * Copyright (c) 2011 by Delphix. All rights reserved.
  26 + * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
26 27 */
27 28
28 29 #include <ctype.h>
@@ -480,6 +481,23 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
480 481 return (zhp);
481 482 }
482 483
  484 +static zfs_handle_t *
  485 +make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
  486 +{
  487 + zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
  488 +
  489 + if (zhp == NULL)
  490 + return (NULL);
  491 +
  492 + zhp->zfs_hdl = pzhp->zfs_hdl;
  493 + (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
  494 + zhp->zfs_head_type = pzhp->zfs_type;
  495 + zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
  496 + zhp->zpool_hdl = zpool_handle(zhp);
  497 +
  498 + return (zhp);
  499 +}
  500 +
483 501 /*
484 502 * Opens the given snapshot, filesystem, or volume. The 'types'
485 503 * argument is a mask of acceptable types. The function will print an
@@ -2518,7 +2536,8 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2518 2536 * Iterate over all snapshots
2519 2537 */
2520 2538 int
2521   -zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
  2539 +zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
  2540 + void *data)
2522 2541 {
2523 2542 zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
2524 2543 zfs_handle_t *nzhp;
@@ -2527,15 +2546,19 @@ zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2527 2546 if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
2528 2547 return (0);
2529 2548
  2549 + zc.zc_simple = simple;
  2550 +
2530 2551 if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2531 2552 return (-1);
2532 2553 while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
2533 2554 &zc)) == 0) {
2534 2555
2535   - if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2536   - &zc)) == NULL) {
  2556 + if (simple)
  2557 + nzhp = make_dataset_simple_handle_zc(zhp, &zc);
  2558 + else
  2559 + nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
  2560 + if (nzhp == NULL)
2537 2561 continue;
2538   - }
2539 2562
2540 2563 if ((ret = func(nzhp, data)) != 0) {
2541 2564 zcmd_free_nvlists(&zc);
@@ -2557,7 +2580,7 @@ zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2557 2580 if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
2558 2581 return (ret);
2559 2582
2560   - return (zfs_iter_snapshots(zhp, func, data));
  2583 + return (zfs_iter_snapshots(zhp, B_FALSE, func, data));
2561 2584 }
2562 2585
2563 2586 /*
@@ -3287,7 +3310,7 @@ zfs_promote(zfs_handle_t *zhp)
3287 3310 return (-1);
3288 3311 (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
3289 3312 sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
3290   - ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
  3313 + ret = zfs_iter_snapshots(pzhp, B_FALSE, promote_snap_cb, &pd);
3291 3314 if (ret != 0) {
3292 3315 zfs_close(pzhp);
3293 3316 return (-1);
@@ -3302,7 +3325,8 @@ zfs_promote(zfs_handle_t *zhp)
3302 3325 if (ret != 0) {
3303 3326 int save_errno = errno;
3304 3327
3305   - (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
  3328 + (void) zfs_iter_snapshots(pzhp, B_FALSE, promote_snap_done_cb,
  3329 + &pd);
3306 3330 zfs_close(pzhp);
3307 3331
3308 3332 switch (save_errno) {
@@ -3321,7 +3345,8 @@ zfs_promote(zfs_handle_t *zhp)
3321 3345 return (zfs_standard_error(hdl, save_errno, errbuf));
3322 3346 }
3323 3347 } else {
3324   - (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
  3348 + (void) zfs_iter_snapshots(zhp, B_FALSE, promote_snap_done_cb,
  3349 + &pd);
3325 3350 }
3326 3351
3327 3352 zfs_close(pzhp);
6 lib/libzfs/libzfs_sendrecv.c
@@ -21,6 +21,8 @@
21 21
22 22 /*
23 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24 + * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
  25 + * All rights reserved
24 26 */
25 27
26 28 #include <assert.h>
@@ -717,7 +719,7 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
717 719 sd->parent_fromsnap_guid = 0;
718 720 VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
719 721 VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
720   - (void) zfs_iter_snapshots(zhp, send_iterate_snap, sd);
  722 + (void) zfs_iter_snapshots(zhp, B_FALSE, send_iterate_snap, sd);
721 723 VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
722 724 VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
723 725 nvlist_free(sd->parent_snaps);
@@ -843,7 +845,7 @@ zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
843 845 avl_create(&avl, zfs_snapshot_compare,
844 846 sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
845 847
846   - ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
  848 + ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl);
847 849
848 850 for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
849 851 ret |= callback(node->zn_handle, data);
5 module/zfs/zfs_ioctl.c
@@ -21,6 +21,7 @@
21 21 /*
22 22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 23 * Portions Copyright 2011 Martin Matuska
  24 + * Portions Copyright 2012 Pawel Jakub Dawidek <pawel@dawidek.net>
24 25 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
25 26 */
26 27
@@ -1973,7 +1974,7 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1973 1974 int error;
1974 1975
1975 1976 top:
1976   - if (zc->zc_cookie == 0)
  1977 + if (zc->zc_cookie == 0 && !zc->zc_simple)
1977 1978 (void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
1978 1979 NULL, DS_FIND_SNAPSHOTS);
1979 1980
@@ -1995,7 +1996,7 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1995 1996 zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie,
1996 1997 NULL);
1997 1998
1998   - if (error == 0) {
  1999 + if (error == 0 && !zc->zc_simple) {
1999 2000 dsl_dataset_t *ds;
2000 2001 dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
2001 2002

0 comments on commit 0cee240

Please sign in to comment.
Something went wrong with that request. Please try again.