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