]>
Commit | Line | Data |
---|---|---|
70e083d2 TG |
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) 2012, 2014 by Delphix. All rights reserved. | |
24 | */ | |
25 | ||
26 | #include <sys/dmu.h> | |
27 | #include <sys/dmu_tx.h> | |
28 | #include <sys/dsl_pool.h> | |
29 | #include <sys/dsl_dir.h> | |
30 | #include <sys/dsl_synctask.h> | |
31 | #include <sys/metaslab.h> | |
32 | ||
33 | #define DST_AVG_BLKSHIFT 14 | |
34 | ||
35 | /* ARGSUSED */ | |
36 | static int | |
37 | dsl_null_checkfunc(void *arg, dmu_tx_t *tx) | |
38 | { | |
39 | return (0); | |
40 | } | |
41 | ||
42 | /* | |
43 | * Called from open context to perform a callback in syncing context. Waits | |
44 | * for the operation to complete. | |
45 | * | |
46 | * The checkfunc will be called from open context as a preliminary check | |
47 | * which can quickly fail. If it succeeds, it will be called again from | |
48 | * syncing context. The checkfunc should generally be designed to work | |
49 | * properly in either context, but if necessary it can check | |
50 | * dmu_tx_is_syncing(tx). | |
51 | * | |
52 | * The synctask infrastructure enforces proper locking strategy with respect | |
53 | * to the dp_config_rwlock -- the lock will always be held when the callbacks | |
54 | * are called. It will be held for read during the open-context (preliminary) | |
55 | * call to the checkfunc, and then held for write from syncing context during | |
56 | * the calls to the check and sync funcs. | |
57 | * | |
58 | * A dataset or pool name can be passed as the first argument. Typically, | |
59 | * the check func will hold, check the return value of the hold, and then | |
60 | * release the dataset. The sync func will VERIFYO(hold()) the dataset. | |
61 | * This is safe because no changes can be made between the check and sync funcs, | |
62 | * and the sync func will only be called if the check func successfully opened | |
63 | * the dataset. | |
64 | */ | |
65 | int | |
66 | dsl_sync_task(const char *pool, dsl_checkfunc_t *checkfunc, | |
67 | dsl_syncfunc_t *syncfunc, void *arg, | |
68 | int blocks_modified, zfs_space_check_t space_check) | |
69 | { | |
70 | spa_t *spa; | |
71 | dmu_tx_t *tx; | |
72 | int err; | |
73 | dsl_sync_task_t dst = { { { NULL } } }; | |
74 | dsl_pool_t *dp; | |
75 | ||
76 | err = spa_open(pool, &spa, FTAG); | |
77 | if (err != 0) | |
78 | return (err); | |
79 | dp = spa_get_dsl(spa); | |
80 | ||
81 | top: | |
82 | tx = dmu_tx_create_dd(dp->dp_mos_dir); | |
83 | VERIFY0(dmu_tx_assign(tx, TXG_WAIT)); | |
84 | ||
85 | dst.dst_pool = dp; | |
86 | dst.dst_txg = dmu_tx_get_txg(tx); | |
87 | dst.dst_space = blocks_modified << DST_AVG_BLKSHIFT; | |
88 | dst.dst_space_check = space_check; | |
89 | dst.dst_checkfunc = checkfunc != NULL ? checkfunc : dsl_null_checkfunc; | |
90 | dst.dst_syncfunc = syncfunc; | |
91 | dst.dst_arg = arg; | |
92 | dst.dst_error = 0; | |
93 | dst.dst_nowaiter = B_FALSE; | |
94 | ||
95 | dsl_pool_config_enter(dp, FTAG); | |
96 | err = dst.dst_checkfunc(arg, tx); | |
97 | dsl_pool_config_exit(dp, FTAG); | |
98 | ||
99 | if (err != 0) { | |
100 | dmu_tx_commit(tx); | |
101 | spa_close(spa, FTAG); | |
102 | return (err); | |
103 | } | |
104 | ||
105 | VERIFY(txg_list_add_tail(&dp->dp_sync_tasks, &dst, dst.dst_txg)); | |
106 | ||
107 | dmu_tx_commit(tx); | |
108 | ||
109 | txg_wait_synced(dp, dst.dst_txg); | |
110 | ||
111 | if (dst.dst_error == EAGAIN) { | |
112 | txg_wait_synced(dp, dst.dst_txg + TXG_DEFER_SIZE); | |
113 | goto top; | |
114 | } | |
115 | ||
116 | spa_close(spa, FTAG); | |
117 | return (dst.dst_error); | |
118 | } | |
119 | ||
120 | void | |
121 | dsl_sync_task_nowait(dsl_pool_t *dp, dsl_syncfunc_t *syncfunc, void *arg, | |
122 | int blocks_modified, zfs_space_check_t space_check, dmu_tx_t *tx) | |
123 | { | |
124 | dsl_sync_task_t *dst = kmem_zalloc(sizeof (*dst), KM_SLEEP); | |
125 | ||
126 | dst->dst_pool = dp; | |
127 | dst->dst_txg = dmu_tx_get_txg(tx); | |
128 | dst->dst_space = blocks_modified << DST_AVG_BLKSHIFT; | |
129 | dst->dst_space_check = space_check; | |
130 | dst->dst_checkfunc = dsl_null_checkfunc; | |
131 | dst->dst_syncfunc = syncfunc; | |
132 | dst->dst_arg = arg; | |
133 | dst->dst_error = 0; | |
134 | dst->dst_nowaiter = B_TRUE; | |
135 | ||
136 | VERIFY(txg_list_add_tail(&dp->dp_sync_tasks, dst, dst->dst_txg)); | |
137 | } | |
138 | ||
139 | /* | |
140 | * Called in syncing context to execute the synctask. | |
141 | */ | |
142 | void | |
143 | dsl_sync_task_sync(dsl_sync_task_t *dst, dmu_tx_t *tx) | |
144 | { | |
145 | dsl_pool_t *dp = dst->dst_pool; | |
146 | ||
147 | ASSERT0(dst->dst_error); | |
148 | ||
149 | /* | |
150 | * Check for sufficient space. | |
151 | * | |
152 | * When the sync task was created, the caller specified the | |
153 | * type of space checking required. See the comment in | |
154 | * zfs_space_check_t for details on the semantics of each | |
155 | * type of space checking. | |
156 | * | |
157 | * We just check against what's on-disk; we don't want any | |
158 | * in-flight accounting to get in our way, because open context | |
159 | * may have already used up various in-core limits | |
160 | * (arc_tempreserve, dsl_pool_tempreserve). | |
161 | */ | |
162 | if (dst->dst_space_check != ZFS_SPACE_CHECK_NONE) { | |
163 | uint64_t quota = dsl_pool_adjustedsize(dp, | |
164 | dst->dst_space_check == ZFS_SPACE_CHECK_RESERVED) - | |
165 | metaslab_class_get_deferred(spa_normal_class(dp->dp_spa)); | |
166 | uint64_t used = dsl_dir_phys(dp->dp_root_dir)->dd_used_bytes; | |
167 | /* MOS space is triple-dittoed, so we multiply by 3. */ | |
168 | if (dst->dst_space > 0 && used + dst->dst_space * 3 > quota) { | |
169 | dst->dst_error = SET_ERROR(ENOSPC); | |
170 | if (dst->dst_nowaiter) | |
171 | kmem_free(dst, sizeof (*dst)); | |
172 | return; | |
173 | } | |
174 | } | |
175 | ||
176 | /* | |
177 | * Check for errors by calling checkfunc. | |
178 | */ | |
179 | rrw_enter(&dp->dp_config_rwlock, RW_WRITER, FTAG); | |
180 | dst->dst_error = dst->dst_checkfunc(dst->dst_arg, tx); | |
181 | if (dst->dst_error == 0) | |
182 | dst->dst_syncfunc(dst->dst_arg, tx); | |
183 | rrw_exit(&dp->dp_config_rwlock, FTAG); | |
184 | if (dst->dst_nowaiter) | |
185 | kmem_free(dst, sizeof (*dst)); | |
186 | } | |
187 | ||
188 | #if defined(_KERNEL) && defined(HAVE_SPL) | |
189 | EXPORT_SYMBOL(dsl_sync_task); | |
190 | EXPORT_SYMBOL(dsl_sync_task_nowait); | |
191 | #endif |