Print this page
2882 implement libzfs_core
2883 changing "canmount" property to "on" should not always remount dataset
2900 "zfs snapshot" should be able to create multiple, arbitrary snapshots at once
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Chris Siden <christopher.siden@delphix.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Bill Pijewski <wdp@joyent.com>
Reviewed by: Dan Kruchinin <dan.kruchinin@gmail.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/zfs/spa_history.c
          +++ new/usr/src/uts/common/fs/zfs/spa_history.c
↓ 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) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  24      - * Copyright (c) 2011 by Delphix. All rights reserved.
       24 + * Copyright (c) 2012 by Delphix. All rights reserved.
  25   25   */
  26   26  
  27   27  #include <sys/spa.h>
  28   28  #include <sys/spa_impl.h>
  29   29  #include <sys/zap.h>
  30   30  #include <sys/dsl_synctask.h>
  31   31  #include <sys/dmu_tx.h>
  32   32  #include <sys/dmu_objset.h>
       33 +#include <sys/dsl_dataset.h>
       34 +#include <sys/dsl_dir.h>
  33   35  #include <sys/utsname.h>
  34   36  #include <sys/cmn_err.h>
  35   37  #include <sys/sunddi.h>
       38 +#include <sys/cred.h>
  36   39  #include "zfs_comutil.h"
  37   40  #ifdef _KERNEL
  38   41  #include <sys/zone.h>
  39   42  #endif
  40   43  
  41   44  /*
  42   45   * Routines to manage the on-disk history log.
  43   46   *
  44   47   * The history log is stored as a dmu object containing
  45   48   * <packed record length, record nvlist> tuples.
↓ open down ↓ 123 lines elided ↑ open up ↑
 169  172          if (len > 0) {
 170  173                  /* write out the rest at the beginning of physical file */
 171  174                  dmu_write(mos, spa->spa_history, shpp->sh_pool_create_len,
 172  175                      len, (char *)buf + firstwrite, tx);
 173  176          }
 174  177  
 175  178          return (0);
 176  179  }
 177  180  
 178  181  static char *
 179      -spa_history_zone()
      182 +spa_history_zone(void)
 180  183  {
 181  184  #ifdef _KERNEL
      185 +        if (INGLOBALZONE(curproc))
      186 +                return (NULL);
 182  187          return (curproc->p_zone->zone_name);
 183  188  #else
 184      -        return ("global");
      189 +        return (NULL);
 185  190  #endif
 186  191  }
 187  192  
 188  193  /*
 189  194   * Write out a history event.
 190  195   */
 191  196  /*ARGSUSED*/
 192  197  static void
 193  198  spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
 194  199  {
 195  200          spa_t           *spa = arg1;
 196      -        history_arg_t   *hap = arg2;
 197      -        const char      *history_str = hap->ha_history_str;
      201 +        nvlist_t        *nvl = arg2;
 198  202          objset_t        *mos = spa->spa_meta_objset;
 199  203          dmu_buf_t       *dbp;
 200  204          spa_history_phys_t *shpp;
 201  205          size_t          reclen;
 202  206          uint64_t        le_len;
 203      -        nvlist_t        *nvrecord;
 204  207          char            *record_packed = NULL;
 205  208          int             ret;
 206  209  
 207  210          /*
 208  211           * If we have an older pool that doesn't have a command
 209  212           * history object, create it now.
 210  213           */
 211  214          mutex_enter(&spa->spa_history_lock);
 212  215          if (!spa->spa_history)
 213  216                  spa_history_create_obj(spa, tx);
↓ open down ↓ 9 lines elided ↑ open up ↑
 223  226          dmu_buf_will_dirty(dbp, tx);
 224  227  
 225  228  #ifdef ZFS_DEBUG
 226  229          {
 227  230                  dmu_object_info_t doi;
 228  231                  dmu_object_info_from_db(dbp, &doi);
 229  232                  ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_SPA_HISTORY_OFFSETS);
 230  233          }
 231  234  #endif
 232  235  
 233      -        VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 234      -        VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TIME,
 235      -            gethrestime_sec()) == 0);
 236      -        VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_WHO, hap->ha_uid) == 0);
 237      -        if (hap->ha_zone != NULL)
 238      -                VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_ZONE,
 239      -                    hap->ha_zone) == 0);
      236 +        fnvlist_add_uint64(nvl, ZPOOL_HIST_TIME, gethrestime_sec());
 240  237  #ifdef _KERNEL
 241      -        VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_HOST,
 242      -            utsname.nodename) == 0);
      238 +        fnvlist_add_string(nvl, ZPOOL_HIST_HOST, utsname.nodename);
 243  239  #endif
 244      -        if (hap->ha_log_type == LOG_CMD_POOL_CREATE ||
 245      -            hap->ha_log_type == LOG_CMD_NORMAL) {
 246      -                VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD,
 247      -                    history_str) == 0);
 248      -
 249      -                zfs_dbgmsg("command: %s", history_str);
 250      -        } else {
 251      -                VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_INT_EVENT,
 252      -                    hap->ha_event) == 0);
 253      -                VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TXG,
 254      -                    tx->tx_txg) == 0);
 255      -                VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_INT_STR,
 256      -                    history_str) == 0);
 257      -
 258      -                zfs_dbgmsg("internal %s pool:%s txg:%llu %s",
 259      -                    zfs_history_event_names[hap->ha_event], spa_name(spa),
 260      -                    (longlong_t)tx->tx_txg, history_str);
 261      -
      240 +        if (nvlist_exists(nvl, ZPOOL_HIST_CMD)) {
      241 +                zfs_dbgmsg("command: %s",
      242 +                    fnvlist_lookup_string(nvl, ZPOOL_HIST_CMD));
      243 +        } else if (nvlist_exists(nvl, ZPOOL_HIST_INT_NAME)) {
      244 +                if (nvlist_exists(nvl, ZPOOL_HIST_DSNAME)) {
      245 +                        zfs_dbgmsg("txg %lld %s %s (id %llu) %s",
      246 +                            fnvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG),
      247 +                            fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME),
      248 +                            fnvlist_lookup_string(nvl, ZPOOL_HIST_DSNAME),
      249 +                            fnvlist_lookup_uint64(nvl, ZPOOL_HIST_DSID),
      250 +                            fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR));
      251 +                } else {
      252 +                        zfs_dbgmsg("txg %lld %s %s",
      253 +                            fnvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG),
      254 +                            fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME),
      255 +                            fnvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR));
      256 +                }
      257 +        } else if (nvlist_exists(nvl, ZPOOL_HIST_IOCTL)) {
      258 +                zfs_dbgmsg("ioctl %s",
      259 +                    fnvlist_lookup_string(nvl, ZPOOL_HIST_IOCTL));
 262  260          }
 263  261  
 264      -        VERIFY(nvlist_size(nvrecord, &reclen, NV_ENCODE_XDR) == 0);
 265      -        record_packed = kmem_alloc(reclen, KM_SLEEP);
 266      -
 267      -        VERIFY(nvlist_pack(nvrecord, &record_packed, &reclen,
 268      -            NV_ENCODE_XDR, KM_SLEEP) == 0);
      262 +        record_packed = fnvlist_pack(nvl, &reclen);
 269  263  
 270  264          mutex_enter(&spa->spa_history_lock);
 271      -        if (hap->ha_log_type == LOG_CMD_POOL_CREATE)
 272      -                VERIFY(shpp->sh_eof == shpp->sh_pool_create_len);
 273  265  
 274  266          /* write out the packed length as little endian */
 275  267          le_len = LE_64((uint64_t)reclen);
 276  268          ret = spa_history_write(spa, &le_len, sizeof (le_len), shpp, tx);
 277  269          if (!ret)
 278  270                  ret = spa_history_write(spa, record_packed, reclen, shpp, tx);
 279  271  
 280      -        if (!ret && hap->ha_log_type == LOG_CMD_POOL_CREATE) {
 281      -                shpp->sh_pool_create_len += sizeof (le_len) + reclen;
 282      -                shpp->sh_bof = shpp->sh_pool_create_len;
      272 +        /* The first command is the create, which we keep forever */
      273 +        if (ret == 0 && shpp->sh_pool_create_len == 0 &&
      274 +            nvlist_exists(nvl, ZPOOL_HIST_CMD)) {
      275 +                shpp->sh_pool_create_len = shpp->sh_bof = shpp->sh_eof;
 283  276          }
 284  277  
 285  278          mutex_exit(&spa->spa_history_lock);
 286      -        nvlist_free(nvrecord);
 287      -        kmem_free(record_packed, reclen);
      279 +        fnvlist_pack_free(record_packed, reclen);
 288  280          dmu_buf_rele(dbp, FTAG);
 289      -
 290      -        strfree(hap->ha_history_str);
 291      -        if (hap->ha_zone != NULL)
 292      -                strfree(hap->ha_zone);
 293      -        kmem_free(hap, sizeof (history_arg_t));
      281 +        fnvlist_free(nvl);
 294  282  }
 295  283  
 296  284  /*
 297  285   * Write out a history event.
 298  286   */
 299  287  int
 300      -spa_history_log(spa_t *spa, const char *history_str, history_log_type_t what)
      288 +spa_history_log(spa_t *spa, const char *msg)
      289 +{
      290 +        int err;
      291 +        nvlist_t *nvl = fnvlist_alloc();
      292 +
      293 +        fnvlist_add_string(nvl, ZPOOL_HIST_CMD, msg);
      294 +        err = spa_history_log_nvl(spa, nvl);
      295 +        fnvlist_free(nvl);
      296 +        return (err);
      297 +}
      298 +
      299 +int
      300 +spa_history_log_nvl(spa_t *spa, nvlist_t *nvl)
 301  301  {
 302      -        history_arg_t *ha;
 303  302          int err = 0;
 304  303          dmu_tx_t *tx;
      304 +        nvlist_t *nvarg;
 305  305  
 306      -        ASSERT(what != LOG_INTERNAL);
      306 +        if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY)
      307 +                return (EINVAL);
 307  308  
 308  309          tx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
 309  310          err = dmu_tx_assign(tx, TXG_WAIT);
 310  311          if (err) {
 311  312                  dmu_tx_abort(tx);
 312  313                  return (err);
 313  314          }
 314  315  
 315      -        ha = kmem_alloc(sizeof (history_arg_t), KM_SLEEP);
 316      -        ha->ha_history_str = strdup(history_str);
 317      -        ha->ha_zone = strdup(spa_history_zone());
 318      -        ha->ha_log_type = what;
 319      -        ha->ha_uid = crgetuid(CRED());
      316 +        nvarg = fnvlist_dup(nvl);
      317 +        if (spa_history_zone() != NULL) {
      318 +                fnvlist_add_string(nvarg, ZPOOL_HIST_ZONE,
      319 +                    spa_history_zone());
      320 +        }
      321 +        fnvlist_add_uint64(nvarg, ZPOOL_HIST_WHO, crgetruid(CRED()));
 320  322  
 321  323          /* Kick this off asynchronously; errors are ignored. */
 322  324          dsl_sync_task_do_nowait(spa_get_dsl(spa), NULL,
 323      -            spa_history_log_sync, spa, ha, 0, tx);
      325 +            spa_history_log_sync, spa, nvarg, 0, tx);
 324  326          dmu_tx_commit(tx);
 325  327  
 326      -        /* spa_history_log_sync will free ha and strings */
      328 +        /* spa_history_log_sync will free nvl */
 327  329          return (err);
      330 +
 328  331  }
 329  332  
 330  333  /*
 331  334   * Read out the command history.
 332  335   */
 333  336  int
 334  337  spa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
 335  338  {
 336  339          objset_t *mos = spa->spa_meta_objset;
 337  340          dmu_buf_t *dbp;
 338  341          uint64_t read_len, phys_read_off, phys_eof;
 339  342          uint64_t leftover = 0;
 340  343          spa_history_phys_t *shpp;
 341  344          int err;
 342  345  
 343  346          /*
 344      -         * If the command history  doesn't exist (older pool),
      347 +         * If the command history doesn't exist (older pool),
 345  348           * that's ok, just return ENOENT.
 346  349           */
 347  350          if (!spa->spa_history)
 348  351                  return (ENOENT);
 349  352  
 350  353          /*
 351  354           * The history is logged asynchronously, so when they request
 352  355           * the first chunk of history, make sure everything has been
 353  356           * synced to disk so that we get it.
 354  357           */
↓ open down ↓ 62 lines elided ↑ open up ↑
 417  420          if (leftover && err == 0) {
 418  421                  err = dmu_read(mos, spa->spa_history, shpp->sh_pool_create_len,
 419  422                      leftover, buf + read_len, DMU_READ_PREFETCH);
 420  423          }
 421  424          mutex_exit(&spa->spa_history_lock);
 422  425  
 423  426          dmu_buf_rele(dbp, FTAG);
 424  427          return (err);
 425  428  }
 426  429  
      430 +/*
      431 + * The nvlist will be consumed by this call.
      432 + */
 427  433  static void
 428      -log_internal(history_internal_events_t event, spa_t *spa,
      434 +log_internal(nvlist_t *nvl, const char *operation, spa_t *spa,
 429  435      dmu_tx_t *tx, const char *fmt, va_list adx)
 430  436  {
 431      -        history_arg_t *ha;
      437 +        char *msg;
 432  438  
 433  439          /*
 434  440           * If this is part of creating a pool, not everything is
 435  441           * initialized yet, so don't bother logging the internal events.
 436  442           */
 437  443          if (tx->tx_txg == TXG_INITIAL)
 438  444                  return;
 439  445  
 440      -        ha = kmem_alloc(sizeof (history_arg_t), KM_SLEEP);
 441      -        ha->ha_history_str = kmem_alloc(vsnprintf(NULL, 0, fmt, adx) + 1,
 442      -            KM_SLEEP);
 443      -
 444      -        (void) vsprintf(ha->ha_history_str, fmt, adx);
 445      -
 446      -        ha->ha_log_type = LOG_INTERNAL;
 447      -        ha->ha_event = event;
 448      -        ha->ha_zone = NULL;
 449      -        ha->ha_uid = 0;
      446 +        msg = kmem_alloc(vsnprintf(NULL, 0, fmt, adx) + 1, KM_SLEEP);
      447 +        (void) vsprintf(msg, fmt, adx);
      448 +        fnvlist_add_string(nvl, ZPOOL_HIST_INT_STR, msg);
      449 +        strfree(msg);
      450 +
      451 +        fnvlist_add_string(nvl, ZPOOL_HIST_INT_NAME, operation);
      452 +        fnvlist_add_uint64(nvl, ZPOOL_HIST_TXG, tx->tx_txg);
 450  453  
 451  454          if (dmu_tx_is_syncing(tx)) {
 452      -                spa_history_log_sync(spa, ha, tx);
      455 +                spa_history_log_sync(spa, nvl, tx);
 453  456          } else {
 454  457                  dsl_sync_task_do_nowait(spa_get_dsl(spa), NULL,
 455      -                    spa_history_log_sync, spa, ha, 0, tx);
      458 +                    spa_history_log_sync, spa, nvl, 0, tx);
 456  459          }
 457      -        /* spa_history_log_sync() will free ha and strings */
      460 +        /* spa_history_log_sync() will free nvl */
 458  461  }
 459  462  
 460  463  void
 461      -spa_history_log_internal(history_internal_events_t event, spa_t *spa,
      464 +spa_history_log_internal(spa_t *spa, const char *operation,
 462  465      dmu_tx_t *tx, const char *fmt, ...)
 463  466  {
 464  467          dmu_tx_t *htx = tx;
 465  468          va_list adx;
 466  469  
 467  470          /* create a tx if we didn't get one */
 468  471          if (tx == NULL) {
 469  472                  htx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
 470  473                  if (dmu_tx_assign(htx, TXG_WAIT) != 0) {
 471  474                          dmu_tx_abort(htx);
 472  475                          return;
 473  476                  }
 474  477          }
 475  478  
 476  479          va_start(adx, fmt);
 477      -        log_internal(event, spa, htx, fmt, adx);
      480 +        log_internal(fnvlist_alloc(), operation, spa, htx, fmt, adx);
 478  481          va_end(adx);
 479  482  
 480  483          /* if we didn't get a tx from the caller, commit the one we made */
 481  484          if (tx == NULL)
 482  485                  dmu_tx_commit(htx);
 483  486  }
 484  487  
 485  488  void
 486      -spa_history_log_version(spa_t *spa, history_internal_events_t event)
      489 +spa_history_log_internal_ds(dsl_dataset_t *ds, const char *operation,
      490 +    dmu_tx_t *tx, const char *fmt, ...)
      491 +{
      492 +        va_list adx;
      493 +        char namebuf[MAXNAMELEN];
      494 +        nvlist_t *nvl = fnvlist_alloc();
      495 +
      496 +        ASSERT(tx != NULL);
      497 +
      498 +        dsl_dataset_name(ds, namebuf);
      499 +        fnvlist_add_string(nvl, ZPOOL_HIST_DSNAME, namebuf);
      500 +        fnvlist_add_uint64(nvl, ZPOOL_HIST_DSID, ds->ds_object);
      501 +
      502 +        va_start(adx, fmt);
      503 +        log_internal(nvl, operation, dsl_dataset_get_spa(ds), tx, fmt, adx);
      504 +        va_end(adx);
      505 +}
      506 +
      507 +void
      508 +spa_history_log_internal_dd(dsl_dir_t *dd, const char *operation,
      509 +    dmu_tx_t *tx, const char *fmt, ...)
      510 +{
      511 +        va_list adx;
      512 +        char namebuf[MAXNAMELEN];
      513 +        nvlist_t *nvl = fnvlist_alloc();
      514 +
      515 +        ASSERT(tx != NULL);
      516 +
      517 +        dsl_dir_name(dd, namebuf);
      518 +        fnvlist_add_string(nvl, ZPOOL_HIST_DSNAME, namebuf);
      519 +        fnvlist_add_uint64(nvl, ZPOOL_HIST_DSID,
      520 +            dd->dd_phys->dd_head_dataset_obj);
      521 +
      522 +        va_start(adx, fmt);
      523 +        log_internal(nvl, operation, dd->dd_pool->dp_spa, tx, fmt, adx);
      524 +        va_end(adx);
      525 +}
      526 +
      527 +void
      528 +spa_history_log_version(spa_t *spa, const char *operation)
 487  529  {
 488  530  #ifdef _KERNEL
 489  531          uint64_t current_vers = spa_version(spa);
 490  532  
 491      -        if (current_vers >= SPA_VERSION_ZPOOL_HISTORY) {
 492      -                spa_history_log_internal(event, spa, NULL,
 493      -                    "pool spa %llu; zfs spa %llu; zpl %d; uts %s %s %s %s",
 494      -                    (u_longlong_t)current_vers, SPA_VERSION, ZPL_VERSION,
 495      -                    utsname.nodename, utsname.release, utsname.version,
 496      -                    utsname.machine);
 497      -        }
 498      -        cmn_err(CE_CONT, "!%s version %llu pool %s using %llu",
 499      -            event == LOG_POOL_IMPORT ? "imported" :
 500      -            event == LOG_POOL_CREATE ? "created" : "accessed",
      533 +        spa_history_log_internal(spa, operation, NULL,
      534 +            "pool version %llu; software version %llu/%d; uts %s %s %s %s",
      535 +            (u_longlong_t)current_vers, SPA_VERSION, ZPL_VERSION,
      536 +            utsname.nodename, utsname.release, utsname.version,
      537 +            utsname.machine);
      538 +        cmn_err(CE_CONT, "!%s version %llu pool %s using %llu", operation,
 501  539              (u_longlong_t)current_vers, spa_name(spa), SPA_VERSION);
 502  540  #endif
 503  541  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX