Print this page
    
3740 Poor ZFS send / receive performance due to snapshot hold / release processing
Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
    
      
        | Split | Close | 
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/zhack/zhack.c
          +++ new/usr/src/cmd/zhack/zhack.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  
    | ↓ open down ↓ | 13 lines elided | ↑ open up ↑ | 
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2012 by Delphix. All rights reserved.
       24 + * Copyright (c) 2013 Steven Hartland. All rights reserved.
  24   25   */
  25   26  
  26   27  /*
  27   28   * zhack is a debugging tool that can write changes to ZFS pool using libzpool
  28   29   * for testing purposes. Altering pools with zhack is unsupported and may
  29   30   * result in corrupted pools.
  30   31   */
  31   32  
  32   33  #include <stdio.h>
  33   34  #include <stdlib.h>
  34   35  #include <ctype.h>
  35   36  #include <sys/zfs_context.h>
  36   37  #include <sys/spa.h>
  37   38  #include <sys/spa_impl.h>
  38   39  #include <sys/dmu.h>
  39   40  #include <sys/zap.h>
  40   41  #include <sys/zfs_znode.h>
  41   42  #include <sys/dsl_synctask.h>
  42   43  #include <sys/vdev.h>
  43   44  #include <sys/fs/zfs.h>
  44   45  #include <sys/dmu_objset.h>
  45   46  #include <sys/dsl_pool.h>
  46   47  #include <sys/zio_checksum.h>
  47   48  #include <sys/zio_compress.h>
  48   49  #include <sys/zfeature.h>
  49   50  #include <sys/dmu_tx.h>
  50   51  #undef ZFS_MAXNAMELEN
  51   52  #undef verify
  52   53  #include <libzfs.h>
  53   54  
  54   55  extern boolean_t zfeature_checks_disable;
  55   56  
  56   57  const char cmdname[] = "zhack";
  57   58  libzfs_handle_t *g_zfs;
  58   59  static importargs_t g_importargs;
  59   60  static char *g_pool;
  60   61  static boolean_t g_readonly;
  61   62  
  62   63  static void
  63   64  usage(void)
  64   65  {
  65   66          (void) fprintf(stderr,
  66   67              "Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
  67   68              "where <subcommand> <args> is one of the following:\n"
  68   69              "\n", cmdname);
  69   70  
  70   71          (void) fprintf(stderr,
  71   72              "    feature stat <pool>\n"
  72   73              "        print information about enabled features\n"
  73   74              "    feature enable [-d desc] <pool> <feature>\n"
  74   75              "        add a new enabled feature to the pool\n"
  75   76              "        -d <desc> sets the feature's description\n"
  76   77              "    feature ref [-md] <pool> <feature>\n"
  77   78              "        change the refcount on the given feature\n"
  78   79              "        -d decrease instead of increase the refcount\n"
  79   80              "        -m add the feature to the label if increasing refcount\n"
  80   81              "\n"
  81   82              "    <feature> : should be a feature guid\n");
  82   83          exit(1);
  83   84  }
  84   85  
  85   86  
  86   87  static void
  87   88  fatal(const char *fmt, ...)
  88   89  {
  89   90          va_list ap;
  90   91  
  91   92          va_start(ap, fmt);
  92   93          (void) fprintf(stderr, "%s: ", cmdname);
  93   94          (void) vfprintf(stderr, fmt, ap);
  94   95          va_end(ap);
  95   96          (void) fprintf(stderr, "\n");
  96   97  
  97   98          exit(1);
  98   99  }
  99  100  
 100  101  /* ARGSUSED */
 101  102  static int
 102  103  space_delta_cb(dmu_object_type_t bonustype, void *data,
 103  104      uint64_t *userp, uint64_t *groupp)
 104  105  {
 105  106          /*
 106  107           * Is it a valid type of object to track?
 107  108           */
 108  109          if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
 109  110                  return (ENOENT);
 110  111          (void) fprintf(stderr, "modifying object that needs user accounting");
 111  112          abort();
 112  113          /* NOTREACHED */
 113  114  }
 114  115  
 115  116  /*
 116  117   * Target is the dataset whose pool we want to open.
 117  118   */
 118  119  static void
 119  120  import_pool(const char *target, boolean_t readonly)
 120  121  {
 121  122          nvlist_t *config;
 122  123          nvlist_t *pools;
 123  124          int error;
 124  125          char *sepp;
 125  126          spa_t *spa;
 126  127          nvpair_t *elem;
 127  128          nvlist_t *props;
 128  129          const char *name;
 129  130  
 130  131          kernel_init(readonly ? FREAD : (FREAD | FWRITE));
 131  132          g_zfs = libzfs_init();
 132  133          ASSERT(g_zfs != NULL);
 133  134  
 134  135          dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
 135  136  
 136  137          g_readonly = readonly;
 137  138  
 138  139          /*
 139  140           * If we only want readonly access, it's OK if we find
 140  141           * a potentially-active (ie, imported into the kernel) pool from the
 141  142           * default cachefile.
 142  143           */
 143  144          if (readonly && spa_open(target, &spa, FTAG) == 0) {
 144  145                  spa_close(spa, FTAG);
 145  146                  return;
  
    | ↓ open down ↓ | 112 lines elided | ↑ open up ↑ | 
 146  147          }
 147  148  
 148  149          g_importargs.unique = B_TRUE;
 149  150          g_importargs.can_be_active = readonly;
 150  151          g_pool = strdup(target);
 151  152          if ((sepp = strpbrk(g_pool, "/@")) != NULL)
 152  153                  *sepp = '\0';
 153  154          g_importargs.poolname = g_pool;
 154  155          pools = zpool_search_import(g_zfs, &g_importargs);
 155  156  
 156      -        if (pools == NULL || nvlist_next_nvpair(pools, NULL) == NULL) {
      157 +        if (nvlist_empty(pools)) {
 157  158                  if (!g_importargs.can_be_active) {
 158  159                          g_importargs.can_be_active = B_TRUE;
 159  160                          if (zpool_search_import(g_zfs, &g_importargs) != NULL ||
 160  161                              spa_open(target, &spa, FTAG) == 0) {
 161  162                                  fatal("cannot import '%s': pool is active; run "
 162  163                                      "\"zpool export %s\" first\n",
 163  164                                      g_pool, g_pool);
 164  165                          }
 165  166                  }
 166  167  
 167  168                  fatal("cannot import '%s': no such pool available\n", g_pool);
 168  169          }
 169  170  
 170  171          elem = nvlist_next_nvpair(pools, NULL);
 171  172          name = nvpair_name(elem);
 172  173          verify(nvpair_value_nvlist(elem, &config) == 0);
 173  174  
 174  175          props = NULL;
 175  176          if (readonly) {
 176  177                  verify(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
 177  178                  verify(nvlist_add_uint64(props,
 178  179                      zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
 179  180          }
 180  181  
 181  182          zfeature_checks_disable = B_TRUE;
 182  183          error = spa_import(name, config, props, ZFS_IMPORT_NORMAL);
 183  184          zfeature_checks_disable = B_FALSE;
 184  185          if (error == EEXIST)
 185  186                  error = 0;
 186  187  
 187  188          if (error)
 188  189                  fatal("can't import '%s': %s", name, strerror(error));
 189  190  }
 190  191  
 191  192  static void
 192  193  zhack_spa_open(const char *target, boolean_t readonly, void *tag, spa_t **spa)
 193  194  {
 194  195          int err;
 195  196  
 196  197          import_pool(target, readonly);
 197  198  
 198  199          zfeature_checks_disable = B_TRUE;
 199  200          err = spa_open(target, spa, tag);
 200  201          zfeature_checks_disable = B_FALSE;
 201  202  
 202  203          if (err != 0)
 203  204                  fatal("cannot open '%s': %s", target, strerror(err));
 204  205          if (spa_version(*spa) < SPA_VERSION_FEATURES) {
 205  206                  fatal("'%s' has version %d, features not enabled", target,
 206  207                      (int)spa_version(*spa));
 207  208          }
 208  209  }
 209  210  
 210  211  static void
 211  212  dump_obj(objset_t *os, uint64_t obj, const char *name)
 212  213  {
 213  214          zap_cursor_t zc;
 214  215          zap_attribute_t za;
 215  216  
 216  217          (void) printf("%s_obj:\n", name);
 217  218  
 218  219          for (zap_cursor_init(&zc, os, obj);
 219  220              zap_cursor_retrieve(&zc, &za) == 0;
 220  221              zap_cursor_advance(&zc)) {
 221  222                  if (za.za_integer_length == 8) {
 222  223                          ASSERT(za.za_num_integers == 1);
 223  224                          (void) printf("\t%s = %llu\n",
 224  225                              za.za_name, (u_longlong_t)za.za_first_integer);
 225  226                  } else {
 226  227                          ASSERT(za.za_integer_length == 1);
 227  228                          char val[1024];
 228  229                          VERIFY(zap_lookup(os, obj, za.za_name,
 229  230                              1, sizeof (val), val) == 0);
 230  231                          (void) printf("\t%s = %s\n", za.za_name, val);
 231  232                  }
 232  233          }
 233  234          zap_cursor_fini(&zc);
 234  235  }
 235  236  
 236  237  static void
 237  238  dump_mos(spa_t *spa)
 238  239  {
 239  240          nvlist_t *nv = spa->spa_label_features;
 240  241  
 241  242          (void) printf("label config:\n");
 242  243          for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
 243  244              pair != NULL;
 244  245              pair = nvlist_next_nvpair(nv, pair)) {
 245  246                  (void) printf("\t%s\n", nvpair_name(pair));
 246  247          }
 247  248  }
 248  249  
 249  250  static void
 250  251  zhack_do_feature_stat(int argc, char **argv)
 251  252  {
 252  253          spa_t *spa;
 253  254          objset_t *os;
 254  255          char *target;
 255  256  
 256  257          argc--;
 257  258          argv++;
 258  259  
 259  260          if (argc < 1) {
 260  261                  (void) fprintf(stderr, "error: missing pool name\n");
 261  262                  usage();
 262  263          }
 263  264          target = argv[0];
 264  265  
 265  266          zhack_spa_open(target, B_TRUE, FTAG, &spa);
 266  267          os = spa->spa_meta_objset;
 267  268  
 268  269          dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
 269  270          dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
 270  271          dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
 271  272          dump_mos(spa);
 272  273  
 273  274          spa_close(spa, FTAG);
 274  275  }
 275  276  
 276  277  static void
 277  278  feature_enable_sync(void *arg, dmu_tx_t *tx)
 278  279  {
 279  280          spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 280  281          zfeature_info_t *feature = arg;
 281  282  
 282  283          spa_feature_enable(spa, feature, tx);
 283  284          spa_history_log_internal(spa, "zhack enable feature", tx,
 284  285              "guid=%s can_readonly=%u",
 285  286              feature->fi_guid, feature->fi_can_readonly);
 286  287  }
 287  288  
 288  289  static void
 289  290  zhack_do_feature_enable(int argc, char **argv)
 290  291  {
 291  292          char c;
 292  293          char *desc, *target;
 293  294          spa_t *spa;
 294  295          objset_t *mos;
 295  296          zfeature_info_t feature;
 296  297          zfeature_info_t *nodeps[] = { NULL };
 297  298  
 298  299          /*
 299  300           * Features are not added to the pool's label until their refcounts
 300  301           * are incremented, so fi_mos can just be left as false for now.
 301  302           */
 302  303          desc = NULL;
 303  304          feature.fi_uname = "zhack";
 304  305          feature.fi_mos = B_FALSE;
 305  306          feature.fi_can_readonly = B_FALSE;
 306  307          feature.fi_depends = nodeps;
 307  308  
 308  309          optind = 1;
 309  310          while ((c = getopt(argc, argv, "rmd:")) != -1) {
 310  311                  switch (c) {
 311  312                  case 'r':
 312  313                          feature.fi_can_readonly = B_TRUE;
 313  314                          break;
 314  315                  case 'd':
 315  316                          desc = strdup(optarg);
 316  317                          break;
 317  318                  default:
 318  319                          usage();
 319  320                          break;
 320  321                  }
 321  322          }
 322  323  
 323  324          if (desc == NULL)
 324  325                  desc = strdup("zhack injected");
 325  326          feature.fi_desc = desc;
 326  327  
 327  328          argc -= optind;
 328  329          argv += optind;
 329  330  
 330  331          if (argc < 2) {
 331  332                  (void) fprintf(stderr, "error: missing feature or pool name\n");
 332  333                  usage();
 333  334          }
 334  335          target = argv[0];
 335  336          feature.fi_guid = argv[1];
 336  337  
 337  338          if (!zfeature_is_valid_guid(feature.fi_guid))
 338  339                  fatal("invalid feature guid: %s", feature.fi_guid);
 339  340  
 340  341          zhack_spa_open(target, B_FALSE, FTAG, &spa);
 341  342          mos = spa->spa_meta_objset;
 342  343  
 343  344          if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
 344  345                  fatal("'%s' is a real feature, will not enable");
 345  346          if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
 346  347                  fatal("feature already enabled: %s", feature.fi_guid);
 347  348  
 348  349          VERIFY0(dsl_sync_task(spa_name(spa), NULL,
 349  350              feature_enable_sync, &feature, 5));
 350  351  
 351  352          spa_close(spa, FTAG);
 352  353  
 353  354          free(desc);
 354  355  }
 355  356  
 356  357  static void
 357  358  feature_incr_sync(void *arg, dmu_tx_t *tx)
 358  359  {
 359  360          spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 360  361          zfeature_info_t *feature = arg;
 361  362  
 362  363          spa_feature_incr(spa, feature, tx);
 363  364          spa_history_log_internal(spa, "zhack feature incr", tx,
 364  365              "guid=%s", feature->fi_guid);
 365  366  }
 366  367  
 367  368  static void
 368  369  feature_decr_sync(void *arg, dmu_tx_t *tx)
 369  370  {
 370  371          spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 371  372          zfeature_info_t *feature = arg;
 372  373  
 373  374          spa_feature_decr(spa, feature, tx);
 374  375          spa_history_log_internal(spa, "zhack feature decr", tx,
 375  376              "guid=%s", feature->fi_guid);
 376  377  }
 377  378  
 378  379  static void
 379  380  zhack_do_feature_ref(int argc, char **argv)
 380  381  {
 381  382          char c;
 382  383          char *target;
 383  384          boolean_t decr = B_FALSE;
 384  385          spa_t *spa;
 385  386          objset_t *mos;
 386  387          zfeature_info_t feature;
 387  388          zfeature_info_t *nodeps[] = { NULL };
 388  389  
 389  390          /*
 390  391           * fi_desc does not matter here because it was written to disk
 391  392           * when the feature was enabled, but we need to properly set the
 392  393           * feature for read or write based on the information we read off
 393  394           * disk later.
 394  395           */
 395  396          feature.fi_uname = "zhack";
 396  397          feature.fi_mos = B_FALSE;
 397  398          feature.fi_desc = NULL;
 398  399          feature.fi_depends = nodeps;
 399  400  
 400  401          optind = 1;
 401  402          while ((c = getopt(argc, argv, "md")) != -1) {
 402  403                  switch (c) {
 403  404                  case 'm':
 404  405                          feature.fi_mos = B_TRUE;
 405  406                          break;
 406  407                  case 'd':
 407  408                          decr = B_TRUE;
 408  409                          break;
 409  410                  default:
 410  411                          usage();
 411  412                          break;
 412  413                  }
 413  414          }
 414  415          argc -= optind;
 415  416          argv += optind;
 416  417  
 417  418          if (argc < 2) {
 418  419                  (void) fprintf(stderr, "error: missing feature or pool name\n");
 419  420                  usage();
 420  421          }
 421  422          target = argv[0];
 422  423          feature.fi_guid = argv[1];
 423  424  
 424  425          if (!zfeature_is_valid_guid(feature.fi_guid))
 425  426                  fatal("invalid feature guid: %s", feature.fi_guid);
 426  427  
 427  428          zhack_spa_open(target, B_FALSE, FTAG, &spa);
 428  429          mos = spa->spa_meta_objset;
 429  430  
 430  431          if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
 431  432                  fatal("'%s' is a real feature, will not change refcount");
 432  433  
 433  434          if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
 434  435              feature.fi_guid)) {
 435  436                  feature.fi_can_readonly = B_FALSE;
 436  437          } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
 437  438              feature.fi_guid)) {
 438  439                  feature.fi_can_readonly = B_TRUE;
 439  440          } else {
 440  441                  fatal("feature is not enabled: %s", feature.fi_guid);
 441  442          }
 442  443  
 443  444          if (decr && !spa_feature_is_active(spa, &feature))
 444  445                  fatal("feature refcount already 0: %s", feature.fi_guid);
 445  446  
 446  447          VERIFY0(dsl_sync_task(spa_name(spa), NULL,
 447  448              decr ? feature_decr_sync : feature_incr_sync, &feature, 5));
 448  449  
 449  450          spa_close(spa, FTAG);
 450  451  }
 451  452  
 452  453  static int
 453  454  zhack_do_feature(int argc, char **argv)
 454  455  {
 455  456          char *subcommand;
 456  457  
 457  458          argc--;
 458  459          argv++;
 459  460          if (argc == 0) {
 460  461                  (void) fprintf(stderr,
 461  462                      "error: no feature operation specified\n");
 462  463                  usage();
 463  464          }
 464  465  
 465  466          subcommand = argv[0];
 466  467          if (strcmp(subcommand, "stat") == 0) {
 467  468                  zhack_do_feature_stat(argc, argv);
 468  469          } else if (strcmp(subcommand, "enable") == 0) {
 469  470                  zhack_do_feature_enable(argc, argv);
 470  471          } else if (strcmp(subcommand, "ref") == 0) {
 471  472                  zhack_do_feature_ref(argc, argv);
 472  473          } else {
 473  474                  (void) fprintf(stderr, "error: unknown subcommand: %s\n",
 474  475                      subcommand);
 475  476                  usage();
 476  477          }
 477  478  
 478  479          return (0);
 479  480  }
 480  481  
 481  482  #define MAX_NUM_PATHS 1024
 482  483  
 483  484  int
 484  485  main(int argc, char **argv)
 485  486  {
 486  487          extern void zfs_prop_init(void);
 487  488  
 488  489          char *path[MAX_NUM_PATHS];
 489  490          const char *subcommand;
 490  491          int rv = 0;
 491  492          char c;
 492  493  
 493  494          g_importargs.path = path;
 494  495  
 495  496          dprintf_setup(&argc, argv);
 496  497          zfs_prop_init();
 497  498  
 498  499          while ((c = getopt(argc, argv, "c:d:")) != -1) {
 499  500                  switch (c) {
 500  501                  case 'c':
 501  502                          g_importargs.cachefile = optarg;
 502  503                          break;
 503  504                  case 'd':
 504  505                          assert(g_importargs.paths < MAX_NUM_PATHS);
 505  506                          g_importargs.path[g_importargs.paths++] = optarg;
 506  507                          break;
 507  508                  default:
 508  509                          usage();
 509  510                          break;
 510  511                  }
 511  512          }
 512  513  
 513  514          argc -= optind;
 514  515          argv += optind;
 515  516          optind = 1;
 516  517  
 517  518          if (argc == 0) {
 518  519                  (void) fprintf(stderr, "error: no command specified\n");
 519  520                  usage();
 520  521          }
 521  522  
 522  523          subcommand = argv[0];
 523  524  
 524  525          if (strcmp(subcommand, "feature") == 0) {
 525  526                  rv = zhack_do_feature(argc, argv);
 526  527          } else {
 527  528                  (void) fprintf(stderr, "error: unknown subcommand: %s\n",
 528  529                      subcommand);
 529  530                  usage();
 530  531          }
 531  532  
 532  533          if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_TRUE) != 0) {
 533  534                  fatal("pool export failed; "
 534  535                      "changes may not be committed to disk\n");
 535  536          }
 536  537  
 537  538          libzfs_fini(g_zfs);
 538  539          kernel_fini();
 539  540  
 540  541          return (rv);
 541  542  }
  
    | ↓ open down ↓ | 375 lines elided | ↑ open up ↑ | 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX