]>
Commit | Line | Data |
---|---|---|
34dc7c2f BB |
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 | /* | |
428870ff | 22 | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. |
3d45fdd6 | 23 | * Copyright (c) 2012, 2014 by Delphix. All rights reserved. |
34dc7c2f BB |
24 | */ |
25 | ||
34dc7c2f BB |
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> | |
428870ff | 31 | #include <sys/metaslab.h> |
34dc7c2f BB |
32 | |
33 | #define DST_AVG_BLKSHIFT 14 | |
34 | ||
35 | /* ARGSUSED */ | |
36 | static int | |
13fe0198 | 37 | dsl_null_checkfunc(void *arg, dmu_tx_t *tx) |
34dc7c2f BB |
38 | { |
39 | return (0); | |
40 | } | |
41 | ||
13fe0198 MA |
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 | */ | |
34dc7c2f | 65 | int |
13fe0198 | 66 | dsl_sync_task(const char *pool, dsl_checkfunc_t *checkfunc, |
3d45fdd6 MA |
67 | dsl_syncfunc_t *syncfunc, void *arg, |
68 | int blocks_modified, zfs_space_check_t space_check) | |
34dc7c2f | 69 | { |
13fe0198 | 70 | spa_t *spa; |
34dc7c2f | 71 | dmu_tx_t *tx; |
13fe0198 MA |
72 | int err; |
73 | dsl_sync_task_t dst = { { { NULL } } }; | |
74 | dsl_pool_t *dp; | |
34dc7c2f | 75 | |
13fe0198 MA |
76 | err = spa_open(pool, &spa, FTAG); |
77 | if (err != 0) | |
78 | return (err); | |
79 | dp = spa_get_dsl(spa); | |
34dc7c2f | 80 | |
13fe0198 MA |
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; | |
3d45fdd6 | 88 | dst.dst_space_check = space_check; |
13fe0198 MA |
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) { | |
34dc7c2f | 100 | dmu_tx_commit(tx); |
13fe0198 MA |
101 | spa_close(spa, FTAG); |
102 | return (err); | |
34dc7c2f BB |
103 | } |
104 | ||
13fe0198 | 105 | VERIFY(txg_list_add_tail(&dp->dp_sync_tasks, &dst, dst.dst_txg)); |
34dc7c2f BB |
106 | |
107 | dmu_tx_commit(tx); | |
108 | ||
13fe0198 | 109 | txg_wait_synced(dp, dst.dst_txg); |
34dc7c2f | 110 | |
13fe0198 MA |
111 | if (dst.dst_error == EAGAIN) { |
112 | txg_wait_synced(dp, dst.dst_txg + TXG_DEFER_SIZE); | |
34dc7c2f | 113 | goto top; |
428870ff | 114 | } |
34dc7c2f | 115 | |
13fe0198 MA |
116 | spa_close(spa, FTAG); |
117 | return (dst.dst_error); | |
34dc7c2f BB |
118 | } |
119 | ||
120 | void | |
13fe0198 | 121 | dsl_sync_task_nowait(dsl_pool_t *dp, dsl_syncfunc_t *syncfunc, void *arg, |
3d45fdd6 | 122 | int blocks_modified, zfs_space_check_t space_check, dmu_tx_t *tx) |
34dc7c2f | 123 | { |
13fe0198 | 124 | dsl_sync_task_t *dst = kmem_zalloc(sizeof (*dst), KM_SLEEP); |
34dc7c2f | 125 | |
13fe0198 MA |
126 | dst->dst_pool = dp; |
127 | dst->dst_txg = dmu_tx_get_txg(tx); | |
128 | dst->dst_space = blocks_modified << DST_AVG_BLKSHIFT; | |
3d45fdd6 | 129 | dst->dst_space_check = space_check; |
13fe0198 MA |
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; | |
34dc7c2f | 135 | |
13fe0198 | 136 | VERIFY(txg_list_add_tail(&dp->dp_sync_tasks, dst, dst->dst_txg)); |
34dc7c2f BB |
137 | } |
138 | ||
13fe0198 MA |
139 | /* |
140 | * Called in syncing context to execute the synctask. | |
141 | */ | |
34dc7c2f | 142 | void |
13fe0198 | 143 | dsl_sync_task_sync(dsl_sync_task_t *dst, dmu_tx_t *tx) |
34dc7c2f | 144 | { |
13fe0198 | 145 | dsl_pool_t *dp = dst->dst_pool; |
34dc7c2f | 146 | |
13fe0198 | 147 | ASSERT0(dst->dst_error); |
34dc7c2f BB |
148 | |
149 | /* | |
3d45fdd6 MA |
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). | |
34dc7c2f | 161 | */ |
3d45fdd6 MA |
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 | } | |
428870ff | 174 | } |
34dc7c2f BB |
175 | |
176 | /* | |
13fe0198 | 177 | * Check for errors by calling checkfunc. |
34dc7c2f | 178 | */ |
13fe0198 MA |
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)); | |
34dc7c2f | 186 | } |
c28b2279 | 187 | |
93ce2b4c | 188 | #if defined(_KERNEL) |
e7440015 BB |
189 | EXPORT_SYMBOL(dsl_sync_task); |
190 | EXPORT_SYMBOL(dsl_sync_task_nowait); | |
c28b2279 | 191 | #endif |