3740 Poor ZFS send / receive performance due to snapshot hold / release processing
Submitted by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
1 /*
2 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2013 by Delphix. All rights reserved.
4 */
5
6 /*
7 * BSD 3 Clause License
8 *
9 * Copyright (c) 2007, The Storage Networking Industry Association.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * - Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * - Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in
19 * the documentation and/or other materials provided with the
20 * distribution.
21 *
22 * - Neither the name of The Storage Networking Industry Association (SNIA)
23 * nor the names of its contributors may be used to endorse or promote
24 * products derived from this software without specific prior written
25 * permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #include <stdio.h>
41 #include <string.h>
42 #include "ndmpd.h"
43 #include <libzfs.h>
44
45 typedef struct snap_param {
46 char *snp_name;
47 boolean_t snp_found;
48 } snap_param_t;
49
50 static int cleanup_fd = -1;
51
52 /*
53 * ndmp_has_backup
54 *
55 * Call backup function which looks for backup snapshot.
56 * This is a callback function used with zfs_iter_snapshots.
57 *
58 * Parameters:
59 * zhp (input) - ZFS handle pointer
60 * data (output) - 0 - no backup snapshot
61 * 1 - has backup snapshot
62 *
63 * Returns:
64 * 0: on success
65 * -1: otherwise
66 */
67 static int
68 ndmp_has_backup(zfs_handle_t *zhp, void *data)
69 {
70 const char *name;
71 snap_param_t *chp = (snap_param_t *)data;
72
73 name = zfs_get_name(zhp);
74 if (name == NULL ||
75 strstr(name, chp->snp_name) == NULL) {
76 zfs_close(zhp);
77 return (-1);
78 }
79
80 chp->snp_found = 1;
81 zfs_close(zhp);
82
83 return (0);
84 }
85
86 /*
87 * ndmp_has_backup_snapshot
88 *
89 * Returns TRUE if the volume has an active backup snapshot, otherwise,
90 * returns FALSE.
91 *
92 * Parameters:
93 * volname (input) - name of the volume
94 *
95 * Returns:
96 * 0: on success
97 * -1: otherwise
98 */
99 static int
100 ndmp_has_backup_snapshot(char *volname, char *jobname)
101 {
102 zfs_handle_t *zhp;
103 snap_param_t snp;
104 char chname[ZFS_MAXNAMELEN];
105
106 (void) mutex_lock(&zlib_mtx);
107 if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
108 NDMP_LOG(LOG_ERR, "Cannot open snapshot %s.", volname);
109 (void) mutex_unlock(&zlib_mtx);
110 return (-1);
111 }
112
113 snp.snp_found = 0;
114 (void) snprintf(chname, ZFS_MAXNAMELEN, "@%s", jobname);
115 snp.snp_name = chname;
116
117 (void) zfs_iter_snapshots(zhp, ndmp_has_backup, &snp);
118 zfs_close(zhp);
119 (void) mutex_unlock(&zlib_mtx);
120
121 return (snp.snp_found);
122 }
123
124 /*
125 * ndmp_create_snapshot
126 *
127 * This function will parse the path to get the real volume name.
128 * It will then create a snapshot based on volume and job name.
129 * This function should be called before the NDMP backup is started.
130 *
131 * Parameters:
132 * vol_name (input) - name of the volume
133 *
134 * Returns:
135 * 0: on success
136 * -1: otherwise
137 */
138 int
139 ndmp_create_snapshot(char *vol_name, char *jname)
140 {
141 char vol[ZFS_MAXNAMELEN];
142
143 if (vol_name == 0 ||
144 get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
145 return (0);
146
147 /*
148 * If there is an old snapshot left from the previous
149 * backup it could be stale one and it must be
150 * removed before using it.
151 */
152 if (ndmp_has_backup_snapshot(vol, jname))
153 (void) snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL);
154
155 return (snapshot_create(vol, jname, B_FALSE, B_TRUE));
156 }
157
158 /*
159 * ndmp_remove_snapshot
160 *
161 * This function will parse the path to get the real volume name.
162 * It will then remove the snapshot for that volume and job name.
163 * This function should be called after NDMP backup is finished.
164 *
165 * Parameters:
166 * vol_name (input) - name of the volume
167 *
168 * Returns:
169 * 0: on success
170 * -1: otherwise
171 */
172 int
173 ndmp_remove_snapshot(char *vol_name, char *jname)
174 {
175 char vol[ZFS_MAXNAMELEN];
176
177 if (vol_name == 0 ||
178 get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
179 return (0);
180
181 return (snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL));
182 }
183
184 /*
185 * Put a hold on snapshot
186 */
187 int
188 snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive)
189 {
190 zfs_handle_t *zhp;
191 char *p;
192
193 if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
194 NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname);
195 return (-1);
196 }
197
198 if (cleanup_fd == -1 && (cleanup_fd = open(ZFS_DEV,
199 O_RDWR|O_EXCL)) < 0) {
200 NDMP_LOG(LOG_ERR, "Cannot open dev %d", errno);
201 zfs_close(zhp);
202 return (-1);
203 }
204
205 p = strchr(snapname, '@') + 1;
206 if (zfs_hold(zhp, p, jname, recursive, B_FALSE, cleanup_fd) != 0) {
207 NDMP_LOG(LOG_ERR, "Cannot hold snapshot %s", p);
208 zfs_close(zhp);
209 return (-1);
210 }
211 zfs_close(zhp);
212 return (0);
213 }
214
215 int
216 snapshot_release(char *volname, char *snapname, char *jname,
217 boolean_t recursive)
218 {
219 zfs_handle_t *zhp;
220 char *p;
221 int rv = 0;
222
223 if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
224 NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname);
225 return (-1);
226 }
227
228 p = strchr(snapname, '@') + 1;
229 if (zfs_release(zhp, p, jname, recursive) != 0) {
230 NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p);
231 rv = -1;
232 }
233 if (cleanup_fd != -1) {
234 (void) close(cleanup_fd);
235 cleanup_fd = -1;
236 }
237 zfs_close(zhp);
238 return (rv);
239 }
240
241 /*
242 * Create a snapshot on the volume
243 */
244 int
245 snapshot_create(char *volname, char *jname, boolean_t recursive,
246 boolean_t hold)
247 {
248 char snapname[ZFS_MAXNAMELEN];
249 int rv;
250
251 if (!volname || !*volname)
252 return (-1);
253
254 (void) snprintf(snapname, ZFS_MAXNAMELEN, "%s@%s", volname, jname);
255
256 (void) mutex_lock(&zlib_mtx);
257 if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL))
258 == -1) {
259 if (errno == EEXIST) {
260 (void) mutex_unlock(&zlib_mtx);
261 return (0);
262 }
263 NDMP_LOG(LOG_DEBUG,
264 "snapshot_create: %s failed (err=%d): %s",
265 snapname, errno, libzfs_error_description(zlibh));
266 (void) mutex_unlock(&zlib_mtx);
267 return (rv);
268 }
269 if (hold && snapshot_hold(volname, snapname, jname, recursive) != 0) {
270 NDMP_LOG(LOG_DEBUG,
271 "snapshot_create: %s hold failed (err=%d): %s",
272 snapname, errno, libzfs_error_description(zlibh));
273 (void) mutex_unlock(&zlib_mtx);
274 return (-1);
275 }
276
277 (void) mutex_unlock(&zlib_mtx);
278 return (0);
279 }
280
281 /*
282 * Remove and release the backup snapshot
283 */
284 int
285 snapshot_destroy(char *volname, char *jname, boolean_t recursive,
286 boolean_t hold, int *zfs_err)
287 {
288 char snapname[ZFS_MAXNAMELEN];
289 zfs_handle_t *zhp;
290 zfs_type_t ztype;
291 char *namep;
292 int err;
293
294 if (zfs_err)
295 *zfs_err = 0;
296
297 if (!volname || !*volname)
298 return (-1);
299
300 if (recursive) {
301 ztype = ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM;
302 namep = volname;
303 } else {
304 (void) snprintf(snapname, ZFS_MAXNAMELEN, "%s@%s", volname,
305 jname);
306 namep = snapname;
307 ztype = ZFS_TYPE_SNAPSHOT;
308 }
309
310 (void) mutex_lock(&zlib_mtx);
311 if (hold &&
312 snapshot_release(volname, namep, jname, recursive) != 0) {
313 NDMP_LOG(LOG_DEBUG,
314 "snapshot_destroy: %s release failed (err=%d): %s",
315 namep, errno, libzfs_error_description(zlibh));
316 (void) mutex_unlock(&zlib_mtx);
317 return (-1);
318 }
319
320 if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) {
321 NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed",
322 namep);
323 (void) mutex_unlock(&zlib_mtx);
324 return (-1);
325 }
326
327 if (recursive) {
328 err = zfs_destroy_snaps(zhp, jname, B_TRUE);
329 } else {
330 err = zfs_destroy(zhp, B_TRUE);
331 }
332
333 if (err) {
334 NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s",
335 namep,
336 recursive,
337 libzfs_errno(zlibh),
338 libzfs_error_action(zlibh),
339 libzfs_error_description(zlibh));
340
341 if (zfs_err)
342 *zfs_err = err;
343 }
344
345 zfs_close(zhp);
346 (void) mutex_unlock(&zlib_mtx);
347
348 return (0);
349 }
--- EOF ---