Print this page
3740 Poor ZFS send / receive performance due to snapshot hold / release processing
Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>


   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2012 by Delphix. All rights reserved.

  24  */
  25 
  26 /*
  27  * LibZFS_Core (lzc) is intended to replace most functionality in libzfs.
  28  * It has the following characteristics:
  29  *
  30  *  - Thread Safe.  libzfs_core is accessible concurrently from multiple
  31  *  threads.  This is accomplished primarily by avoiding global data
  32  *  (e.g. caching).  Since it's thread-safe, there is no reason for a
  33  *  process to have multiple libzfs "instances".  Therefore, we store
  34  *  our few pieces of data (e.g. the file descriptor) in global
  35  *  variables.  The fd is reference-counted so that the libzfs_core
  36  *  library can be "initialized" multiple times (e.g. by different
  37  *  consumers within the same process).
  38  *
  39  *  - Committed Interface.  The libzfs_core interface will be committed,
  40  *  therefore consumers can compile against it and be confident that
  41  *  their code will continue to work on future releases of this code.
  42  *  Currently, the interface is Evolving (not Committed), but we intend
  43  *  to commit to it once it is more complete and we determine that it


 237 
 238         return (error);
 239 }
 240 
 241 /*
 242  * Destroys snapshots.
 243  *
 244  * The keys in the snaps nvlist are the snapshots to be destroyed.
 245  * They must all be in the same pool.
 246  *
 247  * Snapshots that do not exist will be silently ignored.
 248  *
 249  * If 'defer' is not set, and a snapshot has user holds or clones, the
 250  * destroy operation will fail and none of the snapshots will be
 251  * destroyed.
 252  *
 253  * If 'defer' is set, and a snapshot has user holds or clones, it will be
 254  * marked for deferred destruction, and will be destroyed when the last hold
 255  * or clone is removed/destroyed.
 256  *


 257  * The return value will be 0 if all snapshots were destroyed (or marked for
 258  * later destruction if 'defer' is set) or didn't exist to begin with.

 259  *
 260  * Otherwise the return value will be the errno of a (unspecified) snapshot
 261  * that failed, no snapshots will be destroyed, and the errlist will have an
 262  * entry for each snapshot that failed.  The value in the errlist will be
 263  * the (int32) error code.
 264  */
 265 int
 266 lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist)
 267 {
 268         nvpair_t *elem;
 269         nvlist_t *args;
 270         int error;
 271         char pool[MAXNAMELEN];
 272 
 273         /* determine the pool name */
 274         elem = nvlist_next_nvpair(snaps, NULL);
 275         if (elem == NULL)
 276                 return (0);
 277         (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
 278         pool[strcspn(pool, "/@")] = '\0';
 279 
 280         args = fnvlist_alloc();
 281         fnvlist_add_nvlist(args, "snaps", snaps);
 282         if (defer)
 283                 fnvlist_add_boolean(args, "defer");
 284 
 285         error = lzc_ioctl(ZFS_IOC_DESTROY_SNAPS, pool, args, errlist);
 286         nvlist_free(args);
 287 
 288         return (error);
 289 
 290 }
 291 
 292 int
 293 lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
 294     uint64_t *usedp)
 295 {
 296         nvlist_t *args;
 297         nvlist_t *result;
 298         int err;
 299         char fs[MAXNAMELEN];
 300         char *atp;
 301 
 302         /* determine the fs name */
 303         (void) strlcpy(fs, firstsnap, sizeof (fs));
 304         atp = strchr(fs, '@');
 305         if (atp == NULL)
 306                 return (EINVAL);
 307         *atp = '\0';
 308 
 309         args = fnvlist_alloc();


 329 
 330         (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 331         return (ioctl(g_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0);
 332 }
 333 
 334 /*
 335  * Create "user holds" on snapshots.  If there is a hold on a snapshot,
 336  * the snapshot can not be destroyed.  (However, it can be marked for deletion
 337  * by lzc_destroy_snaps(defer=B_TRUE).)
 338  *
 339  * The keys in the nvlist are snapshot names.
 340  * The snapshots must all be in the same pool.
 341  * The value is the name of the hold (string type).
 342  *
 343  * If cleanup_fd is not -1, it must be the result of open("/dev/zfs", O_EXCL).
 344  * In this case, when the cleanup_fd is closed (including on process
 345  * termination), the holds will be released.  If the system is shut down
 346  * uncleanly, the holds will be released when the pool is next opened
 347  * or imported.
 348  *
 349  * The return value will be 0 if all holds were created. Otherwise the return
 350  * value will be the errno of a (unspecified) hold that failed, no holds will
 351  * be created, and the errlist will have an entry for each hold that
 352  * failed (name = snapshot).  The value in the errlist will be the error
 353  * code (int32).











 354  */
 355 int
 356 lzc_hold(nvlist_t *holds, int cleanup_fd, nvlist_t **errlist)
 357 {
 358         char pool[MAXNAMELEN];
 359         nvlist_t *args;
 360         nvpair_t *elem;
 361         int error;
 362 
 363         /* determine the pool name */
 364         elem = nvlist_next_nvpair(holds, NULL);
 365         if (elem == NULL)
 366                 return (0);
 367         (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
 368         pool[strcspn(pool, "/@")] = '\0';
 369 
 370         args = fnvlist_alloc();
 371         fnvlist_add_nvlist(args, "holds", holds);
 372         if (cleanup_fd != -1)
 373                 fnvlist_add_int32(args, "cleanup_fd", cleanup_fd);
 374 
 375         error = lzc_ioctl(ZFS_IOC_HOLD, pool, args, errlist);
 376         nvlist_free(args);
 377         return (error);
 378 }
 379 
 380 /*
 381  * Release "user holds" on snapshots.  If the snapshot has been marked for
 382  * deferred destroy (by lzc_destroy_snaps(defer=B_TRUE)), it does not have
 383  * any clones, and all the user holds are removed, then the snapshot will be
 384  * destroyed.
 385  *
 386  * The keys in the nvlist are snapshot names.
 387  * The snapshots must all be in the same pool.
 388  * The value is a nvlist whose keys are the holds to remove.
 389  *
 390  * The return value will be 0 if all holds were removed.
 391  * Otherwise the return value will be the errno of a (unspecified) release
 392  * that failed, no holds will be released, and the errlist will have an
 393  * entry for each snapshot that has failed releases (name = snapshot).
 394  * The value in the errlist will be the error code (int32) of a failed release.









 395  */
 396 int
 397 lzc_release(nvlist_t *holds, nvlist_t **errlist)
 398 {
 399         char pool[MAXNAMELEN];
 400         nvpair_t *elem;
 401 
 402         /* determine the pool name */
 403         elem = nvlist_next_nvpair(holds, NULL);
 404         if (elem == NULL)
 405                 return (0);
 406         (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
 407         pool[strcspn(pool, "/@")] = '\0';
 408 
 409         return (lzc_ioctl(ZFS_IOC_RELEASE, pool, holds, errlist));
 410 }
 411 
 412 /*
 413  * Retrieve list of user holds on the specified snapshot.
 414  *




   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2012 by Delphix. All rights reserved.
  24  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  25  */
  26 
  27 /*
  28  * LibZFS_Core (lzc) is intended to replace most functionality in libzfs.
  29  * It has the following characteristics:
  30  *
  31  *  - Thread Safe.  libzfs_core is accessible concurrently from multiple
  32  *  threads.  This is accomplished primarily by avoiding global data
  33  *  (e.g. caching).  Since it's thread-safe, there is no reason for a
  34  *  process to have multiple libzfs "instances".  Therefore, we store
  35  *  our few pieces of data (e.g. the file descriptor) in global
  36  *  variables.  The fd is reference-counted so that the libzfs_core
  37  *  library can be "initialized" multiple times (e.g. by different
  38  *  consumers within the same process).
  39  *
  40  *  - Committed Interface.  The libzfs_core interface will be committed,
  41  *  therefore consumers can compile against it and be confident that
  42  *  their code will continue to work on future releases of this code.
  43  *  Currently, the interface is Evolving (not Committed), but we intend
  44  *  to commit to it once it is more complete and we determine that it


 238 
 239         return (error);
 240 }
 241 
 242 /*
 243  * Destroys snapshots.
 244  *
 245  * The keys in the snaps nvlist are the snapshots to be destroyed.
 246  * They must all be in the same pool.
 247  *
 248  * Snapshots that do not exist will be silently ignored.
 249  *
 250  * If 'defer' is not set, and a snapshot has user holds or clones, the
 251  * destroy operation will fail and none of the snapshots will be
 252  * destroyed.
 253  *
 254  * If 'defer' is set, and a snapshot has user holds or clones, it will be
 255  * marked for deferred destruction, and will be destroyed when the last hold
 256  * or clone is removed/destroyed.
 257  *
 258  * The return value will be ENOENT if none of the snapshots existed.
 259  *
 260  * The return value will be 0 if all snapshots were destroyed (or marked for
 261  * later destruction if 'defer' is set) or didn't exist to begin with and
 262  * at least one snapshot was destroyed.
 263  *
 264  * Otherwise the return value will be the errno of a (unspecified) snapshot
 265  * that failed, no snapshots will be destroyed, and the errlist will have an
 266  * entry for each snapshot that failed.  The value in the errlist will be
 267  * the (int32) error code.
 268  */
 269 int
 270 lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist)
 271 {
 272         nvpair_t *elem;
 273         nvlist_t *args;
 274         int error;
 275         char pool[MAXNAMELEN];
 276 
 277         /* determine the pool name */
 278         elem = nvlist_next_nvpair(snaps, NULL);
 279         if (elem == NULL)
 280                 return (0);
 281         (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
 282         pool[strcspn(pool, "/@")] = '\0';
 283 
 284         args = fnvlist_alloc();
 285         fnvlist_add_nvlist(args, "snaps", snaps);
 286         if (defer)
 287                 fnvlist_add_boolean(args, "defer");
 288 
 289         error = lzc_ioctl(ZFS_IOC_DESTROY_SNAPS, pool, args, errlist);
 290         nvlist_free(args);
 291 
 292         return (error);

 293 }
 294 
 295 int
 296 lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
 297     uint64_t *usedp)
 298 {
 299         nvlist_t *args;
 300         nvlist_t *result;
 301         int err;
 302         char fs[MAXNAMELEN];
 303         char *atp;
 304 
 305         /* determine the fs name */
 306         (void) strlcpy(fs, firstsnap, sizeof (fs));
 307         atp = strchr(fs, '@');
 308         if (atp == NULL)
 309                 return (EINVAL);
 310         *atp = '\0';
 311 
 312         args = fnvlist_alloc();


 332 
 333         (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 334         return (ioctl(g_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0);
 335 }
 336 
 337 /*
 338  * Create "user holds" on snapshots.  If there is a hold on a snapshot,
 339  * the snapshot can not be destroyed.  (However, it can be marked for deletion
 340  * by lzc_destroy_snaps(defer=B_TRUE).)
 341  *
 342  * The keys in the nvlist are snapshot names.
 343  * The snapshots must all be in the same pool.
 344  * The value is the name of the hold (string type).
 345  *
 346  * If cleanup_fd is not -1, it must be the result of open("/dev/zfs", O_EXCL).
 347  * In this case, when the cleanup_fd is closed (including on process
 348  * termination), the holds will be released.  If the system is shut down
 349  * uncleanly, the holds will be released when the pool is next opened
 350  * or imported.
 351  *
 352  * Holds for snapshots which don't exist will be skipped and have an entry
 353  * added to errlist, but will not cause an overall failure, except in the
 354  * case that all holds where skipped.
 355  *
 356  * The return value will be ENOENT if none of the snapshots for the requested
 357  * holds existed.
 358  *
 359  * The return value will be 0 if the nvl holds was empty or all holds, for
 360  * snapshots that existed, were succesfully created and at least one hold
 361  * was created.
 362  *
 363  * Otherwise the return value will be the errno of a (unspecified) hold that
 364  * failed and no holds will be created.
 365  *
 366  * In all cases the errlist will have an entry for each hold that failed
 367  * (name = snapshot), with its value being the error code (int32).
 368  */
 369 int
 370 lzc_hold(nvlist_t *holds, int cleanup_fd, nvlist_t **errlist)
 371 {
 372         char pool[MAXNAMELEN];
 373         nvlist_t *args;
 374         nvpair_t *elem;
 375         int error;
 376 
 377         /* determine the pool name */
 378         elem = nvlist_next_nvpair(holds, NULL);
 379         if (elem == NULL)
 380                 return (0);
 381         (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
 382         pool[strcspn(pool, "/@")] = '\0';
 383 
 384         args = fnvlist_alloc();
 385         fnvlist_add_nvlist(args, "holds", holds);
 386         if (cleanup_fd != -1)
 387                 fnvlist_add_int32(args, "cleanup_fd", cleanup_fd);
 388 
 389         error = lzc_ioctl(ZFS_IOC_HOLD, pool, args, errlist);
 390         nvlist_free(args);
 391         return (error);
 392 }
 393 
 394 /*
 395  * Release "user holds" on snapshots.  If the snapshot has been marked for
 396  * deferred destroy (by lzc_destroy_snaps(defer=B_TRUE)), it does not have
 397  * any clones, and all the user holds are removed, then the snapshot will be
 398  * destroyed.
 399  *
 400  * The keys in the nvlist are snapshot names.
 401  * The snapshots must all be in the same pool.
 402  * The value is a nvlist whose keys are the holds to remove.
 403  *
 404  * Holds which failed to release because they didn't exist will have an entry
 405  * added to errlist, but will not cause an overall failure, except in the
 406  * case that all releases where skipped.
 407  *
 408  * The return value will be ENOENT if none of the specified holds existed.
 409  *
 410  * The return value will be 0 if the nvl holds was empty or all holds that
 411  * existed, were successfully removed and at least one hold was removed.
 412  *
 413  * Otherwise the return value will be the errno of a (unspecified) hold that
 414  * failed to release and no holds will be released.
 415  *
 416  * In all cases the errlist will have an entry for each hold that failed to
 417  * to release.
 418  */
 419 int
 420 lzc_release(nvlist_t *holds, nvlist_t **errlist)
 421 {
 422         char pool[MAXNAMELEN];
 423         nvpair_t *elem;
 424 
 425         /* determine the pool name */
 426         elem = nvlist_next_nvpair(holds, NULL);
 427         if (elem == NULL)
 428                 return (0);
 429         (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
 430         pool[strcspn(pool, "/@")] = '\0';
 431 
 432         return (lzc_ioctl(ZFS_IOC_RELEASE, pool, holds, errlist));
 433 }
 434 
 435 /*
 436  * Retrieve list of user holds on the specified snapshot.
 437  *