Print this page
5056 ZFS deadlock on db_mtx and dn_holds
Reviewed by: Will Andrews <willa@spectralogic.com>
Reviewed by: Matt Ahrens <mahrens@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Approved by: Dan McDonald <danmcd@omniti.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/zfs/dsl_bookmark.c
+++ new/usr/src/uts/common/fs/zfs/dsl_bookmark.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * This file and its contents are supplied under the terms of the
5 5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 6 * You may only use this file in accordance with the terms of version
7 7 * 1.0 of the CDDL.
8 8 *
9 9 * A full copy of the text of the CDDL should have accompanied this
10 10 * source. A copy of the CDDL is also available via the Internet at
11 11 * http://www.illumos.org/license/CDDL.
12 12 *
13 13 * CDDL HEADER END
14 14 */
15 15 /*
16 16 * Copyright (c) 2013, 2014 by Delphix. All rights reserved.
17 17 */
18 18
19 19 #include <sys/zfs_context.h>
20 20 #include <sys/dsl_dataset.h>
21 21 #include <sys/dsl_dir.h>
22 22 #include <sys/dsl_prop.h>
23 23 #include <sys/dsl_synctask.h>
24 24 #include <sys/dmu_impl.h>
25 25 #include <sys/dmu_tx.h>
26 26 #include <sys/arc.h>
27 27 #include <sys/zap.h>
28 28 #include <sys/zfeature.h>
29 29 #include <sys/spa.h>
30 30 #include <sys/dsl_bookmark.h>
31 31 #include <zfs_namecheck.h>
32 32
33 33 static int
34 34 dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname,
35 35 dsl_dataset_t **dsp, void *tag, char **shortnamep)
36 36 {
37 37 char buf[MAXNAMELEN];
38 38 char *hashp;
39 39
40 40 if (strlen(fullname) >= MAXNAMELEN)
41 41 return (SET_ERROR(ENAMETOOLONG));
42 42 hashp = strchr(fullname, '#');
43 43 if (hashp == NULL)
44 44 return (SET_ERROR(EINVAL));
45 45
46 46 *shortnamep = hashp + 1;
47 47 if (zfs_component_namecheck(*shortnamep, NULL, NULL))
48 48 return (SET_ERROR(EINVAL));
49 49 (void) strlcpy(buf, fullname, hashp - fullname + 1);
50 50 return (dsl_dataset_hold(dp, buf, tag, dsp));
51 51 }
52 52
53 53 /*
54 54 * Returns ESRCH if bookmark is not found.
55 55 */
56 56 static int
57 57 dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname,
58 58 zfs_bookmark_phys_t *bmark_phys)
59 59 {
60 60 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
61 61 uint64_t bmark_zapobj = ds->ds_bookmarks;
62 62 matchtype_t mt;
63 63 int err;
64 64
65 65 if (bmark_zapobj == 0)
66 66 return (SET_ERROR(ESRCH));
67 67
68 68 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
69 69 mt = MT_FIRST;
70 70 else
71 71 mt = MT_EXACT;
72 72
73 73 err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t),
74 74 sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt,
75 75 NULL, 0, NULL);
76 76
77 77 return (err == ENOENT ? ESRCH : err);
78 78 }
79 79
80 80 /*
81 81 * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark
82 82 * does not represents an earlier point in later_ds's timeline.
83 83 *
84 84 * Returns ENOENT if the dataset containing the bookmark does not exist.
85 85 * Returns ESRCH if the dataset exists but the bookmark was not found in it.
86 86 */
87 87 int
88 88 dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname,
89 89 dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp)
90 90 {
91 91 char *shortname;
92 92 dsl_dataset_t *ds;
93 93 int error;
94 94
95 95 error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname);
96 96 if (error != 0)
97 97 return (error);
98 98
99 99 error = dsl_dataset_bmark_lookup(ds, shortname, bmp);
100 100 if (error == 0 && later_ds != NULL) {
101 101 if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg))
102 102 error = SET_ERROR(EXDEV);
103 103 }
104 104 dsl_dataset_rele(ds, FTAG);
105 105 return (error);
106 106 }
107 107
108 108 typedef struct dsl_bookmark_create_arg {
109 109 nvlist_t *dbca_bmarks;
110 110 nvlist_t *dbca_errors;
111 111 } dsl_bookmark_create_arg_t;
112 112
|
↓ open down ↓ |
112 lines elided |
↑ open up ↑ |
113 113 static int
114 114 dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name,
115 115 dmu_tx_t *tx)
116 116 {
117 117 dsl_pool_t *dp = dmu_tx_pool(tx);
118 118 dsl_dataset_t *bmark_fs;
119 119 char *shortname;
120 120 int error;
121 121 zfs_bookmark_phys_t bmark_phys;
122 122
123 - if (!dsl_dataset_is_snapshot(snapds))
123 + if (!snapds->ds_is_snapshot)
124 124 return (SET_ERROR(EINVAL));
125 125
126 126 error = dsl_bookmark_hold_ds(dp, bookmark_name,
127 127 &bmark_fs, FTAG, &shortname);
128 128 if (error != 0)
129 129 return (error);
130 130
131 131 if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) {
132 132 dsl_dataset_rele(bmark_fs, FTAG);
133 133 return (SET_ERROR(EINVAL));
134 134 }
135 135
136 136 error = dsl_dataset_bmark_lookup(bmark_fs, shortname,
137 137 &bmark_phys);
138 138 dsl_dataset_rele(bmark_fs, FTAG);
139 139 if (error == 0)
140 140 return (SET_ERROR(EEXIST));
141 141 if (error == ESRCH)
142 142 return (0);
143 143 return (error);
144 144 }
145 145
146 146 static int
147 147 dsl_bookmark_create_check(void *arg, dmu_tx_t *tx)
148 148 {
149 149 dsl_bookmark_create_arg_t *dbca = arg;
150 150 dsl_pool_t *dp = dmu_tx_pool(tx);
151 151 int rv = 0;
152 152
153 153 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
154 154 return (SET_ERROR(ENOTSUP));
155 155
156 156 for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
157 157 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
158 158 dsl_dataset_t *snapds;
159 159 int error;
160 160
161 161 /* note: validity of nvlist checked by ioctl layer */
162 162 error = dsl_dataset_hold(dp, fnvpair_value_string(pair),
163 163 FTAG, &snapds);
164 164 if (error == 0) {
165 165 error = dsl_bookmark_create_check_impl(snapds,
166 166 nvpair_name(pair), tx);
167 167 dsl_dataset_rele(snapds, FTAG);
168 168 }
169 169 if (error != 0) {
170 170 fnvlist_add_int32(dbca->dbca_errors,
171 171 nvpair_name(pair), error);
172 172 rv = error;
173 173 }
174 174 }
175 175
176 176 return (rv);
177 177 }
178 178
179 179 static void
180 180 dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
181 181 {
182 182 dsl_bookmark_create_arg_t *dbca = arg;
183 183 dsl_pool_t *dp = dmu_tx_pool(tx);
184 184 objset_t *mos = dp->dp_meta_objset;
185 185
186 186 ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
187 187
188 188 for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
189 189 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
190 190 dsl_dataset_t *snapds, *bmark_fs;
191 191 zfs_bookmark_phys_t bmark_phys;
192 192 char *shortname;
193 193
194 194 VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair),
195 195 FTAG, &snapds));
196 196 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
197 197 &bmark_fs, FTAG, &shortname));
198 198 if (bmark_fs->ds_bookmarks == 0) {
199 199 bmark_fs->ds_bookmarks =
200 200 zap_create_norm(mos, U8_TEXTPREP_TOUPPER,
201 201 DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
202 202 spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
203 203
204 204 dsl_dataset_zapify(bmark_fs, tx);
205 205 VERIFY0(zap_add(mos, bmark_fs->ds_object,
206 206 DS_FIELD_BOOKMARK_NAMES,
207 207 sizeof (bmark_fs->ds_bookmarks), 1,
208 208 &bmark_fs->ds_bookmarks, tx));
209 209 }
210 210
211 211 bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid;
212 212 bmark_phys.zbm_creation_txg =
213 213 dsl_dataset_phys(snapds)->ds_creation_txg;
214 214 bmark_phys.zbm_creation_time =
215 215 dsl_dataset_phys(snapds)->ds_creation_time;
216 216
217 217 VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
218 218 shortname, sizeof (uint64_t),
219 219 sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t),
220 220 &bmark_phys, tx));
221 221
222 222 spa_history_log_internal_ds(bmark_fs, "bookmark", tx,
223 223 "name=%s creation_txg=%llu target_snap=%llu",
224 224 shortname,
225 225 (longlong_t)bmark_phys.zbm_creation_txg,
226 226 (longlong_t)snapds->ds_object);
227 227
228 228 dsl_dataset_rele(bmark_fs, FTAG);
229 229 dsl_dataset_rele(snapds, FTAG);
230 230 }
231 231 }
232 232
233 233 /*
234 234 * The bookmarks must all be in the same pool.
235 235 */
236 236 int
237 237 dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors)
238 238 {
239 239 nvpair_t *pair;
240 240 dsl_bookmark_create_arg_t dbca;
241 241
242 242 pair = nvlist_next_nvpair(bmarks, NULL);
243 243 if (pair == NULL)
244 244 return (0);
245 245
246 246 dbca.dbca_bmarks = bmarks;
247 247 dbca.dbca_errors = errors;
248 248
249 249 return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check,
250 250 dsl_bookmark_create_sync, &dbca,
251 251 fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL));
252 252 }
253 253
254 254 int
255 255 dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
256 256 {
257 257 int err = 0;
258 258 zap_cursor_t zc;
259 259 zap_attribute_t attr;
260 260 dsl_pool_t *dp = ds->ds_dir->dd_pool;
261 261
262 262 uint64_t bmark_zapobj = ds->ds_bookmarks;
263 263 if (bmark_zapobj == 0)
264 264 return (0);
265 265
266 266 for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj);
267 267 zap_cursor_retrieve(&zc, &attr) == 0;
268 268 zap_cursor_advance(&zc)) {
269 269 char *bmark_name = attr.za_name;
270 270 zfs_bookmark_phys_t bmark_phys;
271 271
272 272 err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
273 273 ASSERT3U(err, !=, ENOENT);
274 274 if (err != 0)
275 275 break;
276 276
277 277 nvlist_t *out_props = fnvlist_alloc();
278 278 if (nvlist_exists(props,
279 279 zfs_prop_to_name(ZFS_PROP_GUID))) {
280 280 dsl_prop_nvlist_add_uint64(out_props,
281 281 ZFS_PROP_GUID, bmark_phys.zbm_guid);
282 282 }
283 283 if (nvlist_exists(props,
284 284 zfs_prop_to_name(ZFS_PROP_CREATETXG))) {
285 285 dsl_prop_nvlist_add_uint64(out_props,
286 286 ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg);
287 287 }
288 288 if (nvlist_exists(props,
289 289 zfs_prop_to_name(ZFS_PROP_CREATION))) {
290 290 dsl_prop_nvlist_add_uint64(out_props,
291 291 ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
292 292 }
293 293
294 294 fnvlist_add_nvlist(outnvl, bmark_name, out_props);
295 295 fnvlist_free(out_props);
296 296 }
297 297 zap_cursor_fini(&zc);
298 298 return (err);
299 299 }
300 300
301 301 /*
302 302 * Retrieve the bookmarks that exist in the specified dataset, and the
303 303 * requested properties of each bookmark.
304 304 *
305 305 * The "props" nvlist specifies which properties are requested.
306 306 * See lzc_get_bookmarks() for the list of valid properties.
307 307 */
308 308 int
309 309 dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl)
310 310 {
311 311 dsl_pool_t *dp;
312 312 dsl_dataset_t *ds;
313 313 int err;
314 314
315 315 err = dsl_pool_hold(dsname, FTAG, &dp);
316 316 if (err != 0)
317 317 return (err);
318 318 err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
319 319 if (err != 0) {
320 320 dsl_pool_rele(dp, FTAG);
321 321 return (err);
322 322 }
323 323
324 324 err = dsl_get_bookmarks_impl(ds, props, outnvl);
325 325
326 326 dsl_dataset_rele(ds, FTAG);
327 327 dsl_pool_rele(dp, FTAG);
328 328 return (err);
329 329 }
330 330
331 331 typedef struct dsl_bookmark_destroy_arg {
332 332 nvlist_t *dbda_bmarks;
333 333 nvlist_t *dbda_success;
334 334 nvlist_t *dbda_errors;
335 335 } dsl_bookmark_destroy_arg_t;
336 336
337 337 static int
338 338 dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
339 339 {
340 340 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
341 341 uint64_t bmark_zapobj = ds->ds_bookmarks;
342 342 matchtype_t mt;
343 343
344 344 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
345 345 mt = MT_FIRST;
346 346 else
347 347 mt = MT_EXACT;
348 348
349 349 return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
350 350 }
351 351
352 352 static int
353 353 dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
354 354 {
355 355 dsl_bookmark_destroy_arg_t *dbda = arg;
356 356 dsl_pool_t *dp = dmu_tx_pool(tx);
357 357 int rv = 0;
358 358
359 359 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
360 360 return (0);
361 361
362 362 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
363 363 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
364 364 const char *fullname = nvpair_name(pair);
365 365 dsl_dataset_t *ds;
366 366 zfs_bookmark_phys_t bm;
367 367 int error;
368 368 char *shortname;
369 369
370 370 error = dsl_bookmark_hold_ds(dp, fullname, &ds,
371 371 FTAG, &shortname);
372 372 if (error == ENOENT) {
373 373 /* ignore it; the bookmark is "already destroyed" */
374 374 continue;
375 375 }
376 376 if (error == 0) {
377 377 error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
378 378 dsl_dataset_rele(ds, FTAG);
379 379 if (error == ESRCH) {
380 380 /*
381 381 * ignore it; the bookmark is
382 382 * "already destroyed"
383 383 */
384 384 continue;
385 385 }
386 386 }
387 387 if (error == 0) {
388 388 fnvlist_add_boolean(dbda->dbda_success, fullname);
389 389 } else {
390 390 fnvlist_add_int32(dbda->dbda_errors, fullname, error);
391 391 rv = error;
392 392 }
393 393 }
394 394 return (rv);
395 395 }
396 396
397 397 static void
398 398 dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
399 399 {
400 400 dsl_bookmark_destroy_arg_t *dbda = arg;
401 401 dsl_pool_t *dp = dmu_tx_pool(tx);
402 402 objset_t *mos = dp->dp_meta_objset;
403 403
404 404 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
405 405 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
406 406 dsl_dataset_t *ds;
407 407 char *shortname;
408 408 uint64_t zap_cnt;
409 409
410 410 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
411 411 &ds, FTAG, &shortname));
412 412 VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
413 413
414 414 /*
415 415 * If all of this dataset's bookmarks have been destroyed,
416 416 * free the zap object and decrement the feature's use count.
417 417 */
418 418 VERIFY0(zap_count(mos, ds->ds_bookmarks,
419 419 &zap_cnt));
420 420 if (zap_cnt == 0) {
421 421 dmu_buf_will_dirty(ds->ds_dbuf, tx);
422 422 VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
423 423 ds->ds_bookmarks = 0;
424 424 spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
425 425 VERIFY0(zap_remove(mos, ds->ds_object,
426 426 DS_FIELD_BOOKMARK_NAMES, tx));
427 427 }
428 428
429 429 spa_history_log_internal_ds(ds, "remove bookmark", tx,
430 430 "name=%s", shortname);
431 431
432 432 dsl_dataset_rele(ds, FTAG);
433 433 }
434 434 }
435 435
436 436 /*
437 437 * The bookmarks must all be in the same pool.
438 438 */
439 439 int
440 440 dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
441 441 {
442 442 int rv;
443 443 dsl_bookmark_destroy_arg_t dbda;
444 444 nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
445 445 if (pair == NULL)
446 446 return (0);
447 447
448 448 dbda.dbda_bmarks = bmarks;
449 449 dbda.dbda_errors = errors;
450 450 dbda.dbda_success = fnvlist_alloc();
451 451
452 452 rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
453 453 dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks),
454 454 ZFS_SPACE_CHECK_RESERVED);
455 455 fnvlist_free(dbda.dbda_success);
456 456 return (rv);
457 457 }
|
↓ open down ↓ |
324 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX