Print this page
3744 zfs shouldn't ignore errors unmounting snapshots
Submitted by: Will Andrews <willa@spectralogic.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/fs/zfs/dsl_userhold.c
+++ new/usr/src/uts/common/fs/zfs/dsl_userhold.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
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 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 23 * Copyright (c) 2013 by Delphix. All rights reserved.
24 24 */
25 25
26 26 #include <sys/zfs_context.h>
27 27 #include <sys/dsl_userhold.h>
28 28 #include <sys/dsl_dataset.h>
29 29 #include <sys/dsl_destroy.h>
30 30 #include <sys/dsl_synctask.h>
31 31 #include <sys/dmu_tx.h>
32 32 #include <sys/zfs_onexit.h>
33 33 #include <sys/dsl_pool.h>
34 34 #include <sys/dsl_dir.h>
35 35 #include <sys/zfs_ioctl.h>
36 36 #include <sys/zap.h>
37 37
38 38 typedef struct dsl_dataset_user_hold_arg {
39 39 nvlist_t *dduha_holds;
40 40 nvlist_t *dduha_errlist;
41 41 minor_t dduha_minor;
42 42 } dsl_dataset_user_hold_arg_t;
43 43
44 44 /*
45 45 * If you add new checks here, you may need to add additional checks to the
46 46 * "temporary" case in snapshot_check() in dmu_objset.c.
47 47 */
48 48 int
49 49 dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag,
50 50 boolean_t temphold, dmu_tx_t *tx)
51 51 {
52 52 dsl_pool_t *dp = dmu_tx_pool(tx);
53 53 objset_t *mos = dp->dp_meta_objset;
54 54 int error = 0;
55 55
56 56 if (strlen(htag) > MAXNAMELEN)
57 57 return (E2BIG);
58 58 /* Tempholds have a more restricted length */
59 59 if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
60 60 return (E2BIG);
61 61
62 62 /* tags must be unique (if ds already exists) */
63 63 if (ds != NULL) {
64 64 mutex_enter(&ds->ds_lock);
65 65 if (ds->ds_phys->ds_userrefs_obj != 0) {
66 66 uint64_t value;
67 67 error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj,
68 68 htag, 8, 1, &value);
69 69 if (error == 0)
70 70 error = SET_ERROR(EEXIST);
71 71 else if (error == ENOENT)
72 72 error = 0;
73 73 }
74 74 mutex_exit(&ds->ds_lock);
75 75 }
76 76
77 77 return (error);
78 78 }
79 79
80 80 static int
81 81 dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx)
82 82 {
83 83 dsl_dataset_user_hold_arg_t *dduha = arg;
84 84 dsl_pool_t *dp = dmu_tx_pool(tx);
85 85 nvpair_t *pair;
86 86 int rv = 0;
87 87
88 88 if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
89 89 return (SET_ERROR(ENOTSUP));
90 90
91 91 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL;
92 92 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
93 93 int error = 0;
94 94 dsl_dataset_t *ds;
95 95 char *htag;
96 96
97 97 /* must be a snapshot */
98 98 if (strchr(nvpair_name(pair), '@') == NULL)
99 99 error = SET_ERROR(EINVAL);
100 100
101 101 if (error == 0)
102 102 error = nvpair_value_string(pair, &htag);
103 103 if (error == 0) {
104 104 error = dsl_dataset_hold(dp,
105 105 nvpair_name(pair), FTAG, &ds);
106 106 }
107 107 if (error == 0) {
108 108 error = dsl_dataset_user_hold_check_one(ds, htag,
109 109 dduha->dduha_minor != 0, tx);
110 110 dsl_dataset_rele(ds, FTAG);
111 111 }
112 112
113 113 if (error != 0) {
114 114 rv = error;
115 115 fnvlist_add_int32(dduha->dduha_errlist,
116 116 nvpair_name(pair), error);
117 117 }
118 118 }
119 119 return (rv);
120 120 }
121 121
122 122 void
123 123 dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag,
124 124 minor_t minor, uint64_t now, dmu_tx_t *tx)
125 125 {
126 126 dsl_pool_t *dp = ds->ds_dir->dd_pool;
127 127 objset_t *mos = dp->dp_meta_objset;
128 128 uint64_t zapobj;
129 129
130 130 mutex_enter(&ds->ds_lock);
131 131 if (ds->ds_phys->ds_userrefs_obj == 0) {
132 132 /*
133 133 * This is the first user hold for this dataset. Create
134 134 * the userrefs zap object.
135 135 */
136 136 dmu_buf_will_dirty(ds->ds_dbuf, tx);
137 137 zapobj = ds->ds_phys->ds_userrefs_obj =
138 138 zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx);
139 139 } else {
140 140 zapobj = ds->ds_phys->ds_userrefs_obj;
141 141 }
142 142 ds->ds_userrefs++;
143 143 mutex_exit(&ds->ds_lock);
144 144
145 145 VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx));
146 146
147 147 if (minor != 0) {
148 148 VERIFY0(dsl_pool_user_hold(dp, ds->ds_object,
149 149 htag, now, tx));
150 150 dsl_register_onexit_hold_cleanup(ds, htag, minor);
151 151 }
152 152
153 153 spa_history_log_internal_ds(ds, "hold", tx,
154 154 "tag=%s temp=%d refs=%llu",
155 155 htag, minor != 0, ds->ds_userrefs);
156 156 }
157 157
158 158 static void
159 159 dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx)
160 160 {
161 161 dsl_dataset_user_hold_arg_t *dduha = arg;
162 162 dsl_pool_t *dp = dmu_tx_pool(tx);
163 163 nvpair_t *pair;
164 164 uint64_t now = gethrestime_sec();
165 165
166 166 for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL;
167 167 pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
168 168 dsl_dataset_t *ds;
169 169 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
170 170 dsl_dataset_user_hold_sync_one(ds, fnvpair_value_string(pair),
171 171 dduha->dduha_minor, now, tx);
172 172 dsl_dataset_rele(ds, FTAG);
173 173 }
174 174 }
175 175
176 176 /*
177 177 * holds is nvl of snapname -> holdname
178 178 * errlist will be filled in with snapname -> error
179 179 * if cleanup_minor is not 0, the holds will be temporary, cleaned up
180 180 * when the process exits.
181 181 *
182 182 * if any fails, all will fail.
183 183 */
184 184 int
185 185 dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist)
186 186 {
187 187 dsl_dataset_user_hold_arg_t dduha;
188 188 nvpair_t *pair;
189 189
190 190 pair = nvlist_next_nvpair(holds, NULL);
191 191 if (pair == NULL)
192 192 return (0);
193 193
194 194 dduha.dduha_holds = holds;
195 195 dduha.dduha_errlist = errlist;
196 196 dduha.dduha_minor = cleanup_minor;
197 197
198 198 return (dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
199 199 dsl_dataset_user_hold_sync, &dduha, fnvlist_num_pairs(holds)));
200 200 }
201 201
202 202 typedef struct dsl_dataset_user_release_arg {
203 203 nvlist_t *ddura_holds;
204 204 nvlist_t *ddura_todelete;
205 205 nvlist_t *ddura_errlist;
206 206 } dsl_dataset_user_release_arg_t;
207 207
208 208 static int
209 209 dsl_dataset_user_release_check_one(dsl_dataset_t *ds,
210 210 nvlist_t *holds, boolean_t *todelete)
211 211 {
212 212 uint64_t zapobj;
213 213 nvpair_t *pair;
214 214 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
215 215 int error;
216 216 int numholds = 0;
217 217
218 218 *todelete = B_FALSE;
219 219
220 220 if (!dsl_dataset_is_snapshot(ds))
221 221 return (SET_ERROR(EINVAL));
222 222
223 223 zapobj = ds->ds_phys->ds_userrefs_obj;
224 224 if (zapobj == 0)
225 225 return (SET_ERROR(ESRCH));
226 226
227 227 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
228 228 pair = nvlist_next_nvpair(holds, pair)) {
229 229 /* Make sure the hold exists */
230 230 uint64_t tmp;
231 231 error = zap_lookup(mos, zapobj, nvpair_name(pair), 8, 1, &tmp);
232 232 if (error == ENOENT)
233 233 error = SET_ERROR(ESRCH);
234 234 if (error != 0)
235 235 return (error);
236 236 numholds++;
237 237 }
238 238
239 239 if (DS_IS_DEFER_DESTROY(ds) && ds->ds_phys->ds_num_children == 1 &&
240 240 ds->ds_userrefs == numholds) {
241 241 /* we need to destroy the snapshot as well */
242 242
243 243 if (dsl_dataset_long_held(ds))
244 244 return (SET_ERROR(EBUSY));
245 245 *todelete = B_TRUE;
246 246 }
247 247 return (0);
248 248 }
249 249
250 250 static int
251 251 dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx)
252 252 {
253 253 dsl_dataset_user_release_arg_t *ddura = arg;
254 254 dsl_pool_t *dp = dmu_tx_pool(tx);
255 255 nvpair_t *pair;
256 256 int rv = 0;
257 257
258 258 if (!dmu_tx_is_syncing(tx))
259 259 return (0);
260 260
261 261 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL;
262 262 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
263 263 const char *name = nvpair_name(pair);
264 264 int error;
265 265 dsl_dataset_t *ds;
266 266 nvlist_t *holds;
267 267
268 268 error = nvpair_value_nvlist(pair, &holds);
269 269 if (error != 0)
270 270 return (SET_ERROR(EINVAL));
271 271
272 272 error = dsl_dataset_hold(dp, name, FTAG, &ds);
273 273 if (error == 0) {
274 274 boolean_t deleteme;
275 275 error = dsl_dataset_user_release_check_one(ds,
276 276 holds, &deleteme);
277 277 if (error == 0 && deleteme) {
278 278 fnvlist_add_boolean(ddura->ddura_todelete,
279 279 name);
280 280 }
281 281 dsl_dataset_rele(ds, FTAG);
282 282 }
283 283 if (error != 0) {
284 284 if (ddura->ddura_errlist != NULL) {
285 285 fnvlist_add_int32(ddura->ddura_errlist,
286 286 name, error);
287 287 }
288 288 rv = error;
289 289 }
290 290 }
291 291 return (rv);
292 292 }
293 293
294 294 static void
295 295 dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds,
296 296 dmu_tx_t *tx)
297 297 {
298 298 dsl_pool_t *dp = ds->ds_dir->dd_pool;
299 299 objset_t *mos = dp->dp_meta_objset;
300 300 uint64_t zapobj;
301 301 int error;
302 302 nvpair_t *pair;
303 303
304 304 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
305 305 pair = nvlist_next_nvpair(holds, pair)) {
306 306 ds->ds_userrefs--;
307 307 error = dsl_pool_user_release(dp, ds->ds_object,
308 308 nvpair_name(pair), tx);
309 309 VERIFY(error == 0 || error == ENOENT);
310 310 zapobj = ds->ds_phys->ds_userrefs_obj;
311 311 VERIFY0(zap_remove(mos, zapobj, nvpair_name(pair), tx));
312 312
313 313 spa_history_log_internal_ds(ds, "release", tx,
314 314 "tag=%s refs=%lld", nvpair_name(pair),
315 315 (longlong_t)ds->ds_userrefs);
316 316 }
317 317 }
318 318
319 319 static void
320 320 dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx)
321 321 {
322 322 dsl_dataset_user_release_arg_t *ddura = arg;
323 323 dsl_pool_t *dp = dmu_tx_pool(tx);
324 324 nvpair_t *pair;
325 325
326 326 for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL;
327 327 pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
328 328 dsl_dataset_t *ds;
329 329
330 330 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
331 331 dsl_dataset_user_release_sync_one(ds,
332 332 fnvpair_value_nvlist(pair), tx);
333 333 if (nvlist_exists(ddura->ddura_todelete,
334 334 nvpair_name(pair))) {
335 335 ASSERT(ds->ds_userrefs == 0 &&
336 336 ds->ds_phys->ds_num_children == 1 &&
337 337 DS_IS_DEFER_DESTROY(ds));
338 338 dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
339 339 }
340 340 dsl_dataset_rele(ds, FTAG);
341 341 }
342 342 }
343 343
344 344 /*
345 345 * holds is nvl of snapname -> { holdname, ... }
346 346 * errlist will be filled in with snapname -> error
347 347 *
348 348 * if any fails, all will fail.
349 349 */
350 350 int
351 351 dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist)
352 352 {
353 353 dsl_dataset_user_release_arg_t ddura;
354 354 nvpair_t *pair;
355 355 int error;
356 356
357 357 pair = nvlist_next_nvpair(holds, NULL);
358 358 if (pair == NULL)
359 359 return (0);
360 360
361 361 ddura.ddura_holds = holds;
362 362 ddura.ddura_errlist = errlist;
363 363 ddura.ddura_todelete = fnvlist_alloc();
364 364
365 365 error = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_release_check,
366 366 dsl_dataset_user_release_sync, &ddura, fnvlist_num_pairs(holds));
367 367 fnvlist_free(ddura.ddura_todelete);
368 368 return (error);
369 369 }
370 370
371 371 typedef struct dsl_dataset_user_release_tmp_arg {
372 372 uint64_t ddurta_dsobj;
373 373 nvlist_t *ddurta_holds;
374 374 boolean_t ddurta_deleteme;
375 375 } dsl_dataset_user_release_tmp_arg_t;
376 376
377 377 static int
378 378 dsl_dataset_user_release_tmp_check(void *arg, dmu_tx_t *tx)
379 379 {
380 380 dsl_dataset_user_release_tmp_arg_t *ddurta = arg;
381 381 dsl_pool_t *dp = dmu_tx_pool(tx);
382 382 dsl_dataset_t *ds;
383 383 int error;
384 384
385 385 if (!dmu_tx_is_syncing(tx))
386 386 return (0);
387 387
388 388 error = dsl_dataset_hold_obj(dp, ddurta->ddurta_dsobj, FTAG, &ds);
389 389 if (error)
390 390 return (error);
391 391
392 392 error = dsl_dataset_user_release_check_one(ds,
393 393 ddurta->ddurta_holds, &ddurta->ddurta_deleteme);
394 394 dsl_dataset_rele(ds, FTAG);
395 395 return (error);
396 396 }
397 397
398 398 static void
399 399 dsl_dataset_user_release_tmp_sync(void *arg, dmu_tx_t *tx)
400 400 {
401 401 dsl_dataset_user_release_tmp_arg_t *ddurta = arg;
402 402 dsl_pool_t *dp = dmu_tx_pool(tx);
403 403 dsl_dataset_t *ds;
404 404
405 405 VERIFY0(dsl_dataset_hold_obj(dp, ddurta->ddurta_dsobj, FTAG, &ds));
406 406 dsl_dataset_user_release_sync_one(ds, ddurta->ddurta_holds, tx);
407 407 if (ddurta->ddurta_deleteme) {
408 408 ASSERT(ds->ds_userrefs == 0 &&
409 409 ds->ds_phys->ds_num_children == 1 &&
410 410 DS_IS_DEFER_DESTROY(ds));
411 411 dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
412 412 }
413 413 dsl_dataset_rele(ds, FTAG);
414 414 }
415 415
416 416 /*
417 417 * Called at spa_load time to release a stale temporary user hold.
418 418 * Also called by the onexit code.
419 419 */
420 420 void
421 421 dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, const char *htag)
422 422 {
423 423 dsl_dataset_user_release_tmp_arg_t ddurta;
424 424 dsl_dataset_t *ds;
425 425 int error;
↓ open down ↓ |
425 lines elided |
↑ open up ↑ |
426 426
427 427 #ifdef _KERNEL
428 428 /* Make sure it is not mounted. */
429 429 dsl_pool_config_enter(dp, FTAG);
430 430 error = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
431 431 if (error == 0) {
432 432 char name[MAXNAMELEN];
433 433 dsl_dataset_name(ds, name);
434 434 dsl_dataset_rele(ds, FTAG);
435 435 dsl_pool_config_exit(dp, FTAG);
436 - zfs_unmount_snap(name);
436 + (void) zfs_unmount_snap(name);
437 437 } else {
438 438 dsl_pool_config_exit(dp, FTAG);
439 439 }
440 440 #endif
441 441
442 442 ddurta.ddurta_dsobj = dsobj;
443 443 ddurta.ddurta_holds = fnvlist_alloc();
444 444 fnvlist_add_boolean(ddurta.ddurta_holds, htag);
445 445
446 446 (void) dsl_sync_task(spa_name(dp->dp_spa),
447 447 dsl_dataset_user_release_tmp_check,
448 448 dsl_dataset_user_release_tmp_sync, &ddurta, 1);
449 449 fnvlist_free(ddurta.ddurta_holds);
450 450 }
451 451
452 452 typedef struct zfs_hold_cleanup_arg {
453 453 char zhca_spaname[MAXNAMELEN];
454 454 uint64_t zhca_spa_load_guid;
455 455 uint64_t zhca_dsobj;
456 456 char zhca_htag[MAXNAMELEN];
457 457 } zfs_hold_cleanup_arg_t;
458 458
459 459 static void
460 460 dsl_dataset_user_release_onexit(void *arg)
461 461 {
462 462 zfs_hold_cleanup_arg_t *ca = arg;
463 463 spa_t *spa;
464 464 int error;
465 465
466 466 error = spa_open(ca->zhca_spaname, &spa, FTAG);
467 467 if (error != 0) {
468 468 zfs_dbgmsg("couldn't release hold on pool=%s ds=%llu tag=%s "
469 469 "because pool is no longer loaded",
470 470 ca->zhca_spaname, ca->zhca_dsobj, ca->zhca_htag);
471 471 return;
472 472 }
473 473 if (spa_load_guid(spa) != ca->zhca_spa_load_guid) {
474 474 zfs_dbgmsg("couldn't release hold on pool=%s ds=%llu tag=%s "
475 475 "because pool is no longer loaded (guid doesn't match)",
476 476 ca->zhca_spaname, ca->zhca_dsobj, ca->zhca_htag);
477 477 spa_close(spa, FTAG);
478 478 return;
479 479 }
480 480
481 481 dsl_dataset_user_release_tmp(spa_get_dsl(spa),
482 482 ca->zhca_dsobj, ca->zhca_htag);
483 483 kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
484 484 spa_close(spa, FTAG);
485 485 }
486 486
487 487 void
488 488 dsl_register_onexit_hold_cleanup(dsl_dataset_t *ds, const char *htag,
489 489 minor_t minor)
490 490 {
491 491 zfs_hold_cleanup_arg_t *ca = kmem_alloc(sizeof (*ca), KM_SLEEP);
492 492 spa_t *spa = dsl_dataset_get_spa(ds);
493 493 (void) strlcpy(ca->zhca_spaname, spa_name(spa),
494 494 sizeof (ca->zhca_spaname));
495 495 ca->zhca_spa_load_guid = spa_load_guid(spa);
496 496 ca->zhca_dsobj = ds->ds_object;
497 497 (void) strlcpy(ca->zhca_htag, htag, sizeof (ca->zhca_htag));
498 498 VERIFY0(zfs_onexit_add_cb(minor,
499 499 dsl_dataset_user_release_onexit, ca, NULL));
500 500 }
501 501
502 502 int
503 503 dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl)
504 504 {
505 505 dsl_pool_t *dp;
506 506 dsl_dataset_t *ds;
507 507 int err;
508 508
509 509 err = dsl_pool_hold(dsname, FTAG, &dp);
510 510 if (err != 0)
511 511 return (err);
512 512 err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
513 513 if (err != 0) {
514 514 dsl_pool_rele(dp, FTAG);
515 515 return (err);
516 516 }
517 517
518 518 if (ds->ds_phys->ds_userrefs_obj != 0) {
519 519 zap_attribute_t *za;
520 520 zap_cursor_t zc;
521 521
522 522 za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
523 523 for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
524 524 ds->ds_phys->ds_userrefs_obj);
525 525 zap_cursor_retrieve(&zc, za) == 0;
526 526 zap_cursor_advance(&zc)) {
527 527 fnvlist_add_uint64(nvl, za->za_name,
528 528 za->za_first_integer);
529 529 }
530 530 zap_cursor_fini(&zc);
531 531 kmem_free(za, sizeof (zap_attribute_t));
532 532 }
533 533 dsl_dataset_rele(ds, FTAG);
534 534 dsl_pool_rele(dp, FTAG);
535 535 return (0);
536 536 }
↓ open down ↓ |
90 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX