Home | History | Annotate | Download | only in zfs
      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 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <sys/dmu.h>
     29 #include <sys/dmu_tx.h>
     30 #include <sys/dsl_pool.h>
     31 #include <sys/dsl_dir.h>
     32 #include <sys/dsl_synctask.h>
     33 #include <sys/cred.h>
     34 
     35 #define	DST_AVG_BLKSHIFT 14
     36 
     37 /* ARGSUSED */
     38 static int
     39 dsl_null_checkfunc(void *arg1, void *arg2, dmu_tx_t *tx)
     40 {
     41 	return (0);
     42 }
     43 
     44 dsl_sync_task_group_t *
     45 dsl_sync_task_group_create(dsl_pool_t *dp)
     46 {
     47 	dsl_sync_task_group_t *dstg;
     48 
     49 	dstg = kmem_zalloc(sizeof (dsl_sync_task_group_t), KM_SLEEP);
     50 	list_create(&dstg->dstg_tasks, sizeof (dsl_sync_task_t),
     51 	    offsetof(dsl_sync_task_t, dst_node));
     52 	dstg->dstg_pool = dp;
     53 	dstg->dstg_cr = CRED();
     54 
     55 	return (dstg);
     56 }
     57 
     58 void
     59 dsl_sync_task_create(dsl_sync_task_group_t *dstg,
     60     dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
     61     void *arg1, void *arg2, int blocks_modified)
     62 {
     63 	dsl_sync_task_t *dst;
     64 
     65 	if (checkfunc == NULL)
     66 		checkfunc = dsl_null_checkfunc;
     67 	dst = kmem_zalloc(sizeof (dsl_sync_task_t), KM_SLEEP);
     68 	dst->dst_checkfunc = checkfunc;
     69 	dst->dst_syncfunc = syncfunc;
     70 	dst->dst_arg1 = arg1;
     71 	dst->dst_arg2 = arg2;
     72 	list_insert_tail(&dstg->dstg_tasks, dst);
     73 
     74 	dstg->dstg_space += blocks_modified << DST_AVG_BLKSHIFT;
     75 }
     76 
     77 int
     78 dsl_sync_task_group_wait(dsl_sync_task_group_t *dstg)
     79 {
     80 	dmu_tx_t *tx;
     81 	uint64_t txg;
     82 	dsl_sync_task_t *dst;
     83 
     84 top:
     85 	tx = dmu_tx_create_dd(dstg->dstg_pool->dp_mos_dir);
     86 	VERIFY(0 == dmu_tx_assign(tx, TXG_WAIT));
     87 
     88 	txg = dmu_tx_get_txg(tx);
     89 
     90 	/* Do a preliminary error check. */
     91 	dstg->dstg_err = 0;
     92 	rw_enter(&dstg->dstg_pool->dp_config_rwlock, RW_READER);
     93 	for (dst = list_head(&dstg->dstg_tasks); dst;
     94 	    dst = list_next(&dstg->dstg_tasks, dst)) {
     95 #ifdef ZFS_DEBUG
     96 		/*
     97 		 * Only check half the time, otherwise, the sync-context
     98 		 * check will almost never fail.
     99 		 */
    100 		if (spa_get_random(2) == 0)
    101 			continue;
    102 #endif
    103 		dst->dst_err =
    104 		    dst->dst_checkfunc(dst->dst_arg1, dst->dst_arg2, tx);
    105 		if (dst->dst_err)
    106 			dstg->dstg_err = dst->dst_err;
    107 	}
    108 	rw_exit(&dstg->dstg_pool->dp_config_rwlock);
    109 
    110 	if (dstg->dstg_err) {
    111 		dmu_tx_commit(tx);
    112 		return (dstg->dstg_err);
    113 	}
    114 
    115 	VERIFY(0 == txg_list_add(&dstg->dstg_pool->dp_sync_tasks, dstg, txg));
    116 
    117 	dmu_tx_commit(tx);
    118 
    119 	txg_wait_synced(dstg->dstg_pool, txg);
    120 
    121 	if (dstg->dstg_err == EAGAIN)
    122 		goto top;
    123 
    124 	return (dstg->dstg_err);
    125 }
    126 
    127 void
    128 dsl_sync_task_group_nowait(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
    129 {
    130 	uint64_t txg;
    131 
    132 	dstg->dstg_nowaiter = B_TRUE;
    133 	txg = dmu_tx_get_txg(tx);
    134 	VERIFY(0 == txg_list_add(&dstg->dstg_pool->dp_sync_tasks, dstg, txg));
    135 }
    136 
    137 void
    138 dsl_sync_task_group_destroy(dsl_sync_task_group_t *dstg)
    139 {
    140 	dsl_sync_task_t *dst;
    141 
    142 	while (dst = list_head(&dstg->dstg_tasks)) {
    143 		list_remove(&dstg->dstg_tasks, dst);
    144 		kmem_free(dst, sizeof (dsl_sync_task_t));
    145 	}
    146 	kmem_free(dstg, sizeof (dsl_sync_task_group_t));
    147 }
    148 
    149 void
    150 dsl_sync_task_group_sync(dsl_sync_task_group_t *dstg, dmu_tx_t *tx)
    151 {
    152 	dsl_sync_task_t *dst;
    153 	void *tr_cookie;
    154 
    155 	ASSERT3U(dstg->dstg_err, ==, 0);
    156 
    157 	/*
    158 	 * Check for sufficient space.
    159 	 */
    160 	dstg->dstg_err = dsl_dir_tempreserve_space(dstg->dstg_pool->dp_mos_dir,
    161 	    dstg->dstg_space, dstg->dstg_space * 3, 0, 0, &tr_cookie, tx);
    162 	/* don't bother trying again */
    163 	if (dstg->dstg_err == ERESTART)
    164 		dstg->dstg_err = EAGAIN;
    165 	if (dstg->dstg_err)
    166 		return;
    167 
    168 	/*
    169 	 * Check for errors by calling checkfuncs.
    170 	 */
    171 	rw_enter(&dstg->dstg_pool->dp_config_rwlock, RW_WRITER);
    172 	for (dst = list_head(&dstg->dstg_tasks); dst;
    173 	    dst = list_next(&dstg->dstg_tasks, dst)) {
    174 		dst->dst_err =
    175 		    dst->dst_checkfunc(dst->dst_arg1, dst->dst_arg2, tx);
    176 		if (dst->dst_err)
    177 			dstg->dstg_err = dst->dst_err;
    178 	}
    179 
    180 	if (dstg->dstg_err == 0) {
    181 		/*
    182 		 * Execute sync tasks.
    183 		 */
    184 		for (dst = list_head(&dstg->dstg_tasks); dst;
    185 		    dst = list_next(&dstg->dstg_tasks, dst)) {
    186 			dst->dst_syncfunc(dst->dst_arg1, dst->dst_arg2,
    187 			    dstg->dstg_cr, tx);
    188 		}
    189 	}
    190 	rw_exit(&dstg->dstg_pool->dp_config_rwlock);
    191 
    192 	dsl_dir_tempreserve_clear(tr_cookie, tx);
    193 
    194 	if (dstg->dstg_nowaiter)
    195 		dsl_sync_task_group_destroy(dstg);
    196 }
    197 
    198 int
    199 dsl_sync_task_do(dsl_pool_t *dp,
    200     dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
    201     void *arg1, void *arg2, int blocks_modified)
    202 {
    203 	dsl_sync_task_group_t *dstg;
    204 	int err;
    205 
    206 	dstg = dsl_sync_task_group_create(dp);
    207 	dsl_sync_task_create(dstg, checkfunc, syncfunc,
    208 	    arg1, arg2, blocks_modified);
    209 	err = dsl_sync_task_group_wait(dstg);
    210 	dsl_sync_task_group_destroy(dstg);
    211 	return (err);
    212 }
    213 
    214 void
    215 dsl_sync_task_do_nowait(dsl_pool_t *dp,
    216     dsl_checkfunc_t *checkfunc, dsl_syncfunc_t *syncfunc,
    217     void *arg1, void *arg2, int blocks_modified, dmu_tx_t *tx)
    218 {
    219 	dsl_sync_task_group_t *dstg;
    220 
    221 	dstg = dsl_sync_task_group_create(dp);
    222 	dsl_sync_task_create(dstg, checkfunc, syncfunc,
    223 	    arg1, arg2, blocks_modified);
    224 	dsl_sync_task_group_nowait(dstg, tx);
    225 }
    226