/* * Get data to generate a TX_WRITE intent log record. */ static int zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) { zvol_state_t *zv = arg; objset_t *os = zv->zv_objset; uint64_t offset = lr->lr_offset; uint64_t size = lr->lr_length; dmu_buf_t *db; zgd_t *zgd; int error; ASSERT(zio != NULL); ASSERT(size != 0); zgd = (zgd_t *)kmem_zalloc(sizeof (zgd_t), KM_PUSHPAGE); zgd->zgd_zilog = zv->zv_zilog; zgd->zgd_rl = zfs_range_lock(&zv->zv_znode, offset, size, RL_READER); /* * Write records come in two flavors: immediate and indirect. * For small writes it's cheaper to store the data with the * log record (immediate); for large writes it's cheaper to * sync the data and get a pointer to it (indirect) so that * we don't have to write the data twice. */ if (buf != NULL) { /* immediate write */ error = dmu_read(os, ZVOL_OBJ, offset, size, buf, DMU_READ_NO_PREFETCH); } else { size = zv->zv_volblocksize; offset = P2ALIGN_TYPED(offset, size, uint64_t); error = dmu_buf_hold(os, ZVOL_OBJ, offset, zgd, &db, DMU_READ_NO_PREFETCH); if (error == 0) { zgd->zgd_db = db; zgd->zgd_bp = &lr->lr_blkptr; ASSERT(db != NULL); ASSERT(db->db_offset == offset); ASSERT(db->db_size == size); error = dmu_sync(zio, lr->lr_common.lrc_txg, zvol_get_done, zgd); if (error == 0) return (0); } } zvol_get_done(zgd, error); return (error); }
int zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, offset_t off, ssize_t len, char *addr) { dmu_object_info_t doi; ssize_t nbytes; itx_t *itx; lr_write_t *lr; objset_t *os; dmu_buf_t *db; uint64_t txg; uint64_t boff; int error; uint32_t blocksize; /* handle common case */ if (len <= zvol_immediate_write_sz) { itx = zvol_immediate_itx(off, len, addr); (void) zil_itx_assign(zv->zv_zilog, itx, tx); return (0); } txg = dmu_tx_get_txg(tx); os = zv->zv_objset; /* * We need to dmu_sync() each block in the range. * For this we need the blocksize. */ error = dmu_object_info(os, ZVOL_OBJ, &doi); if (error) return (error); blocksize = doi.doi_data_block_size; /* * We need to immediate write or dmu_sync() each block in the range. */ while (len) { nbytes = MIN(len, blocksize - P2PHASE(off, blocksize)); if (nbytes <= zvol_immediate_write_sz) { itx = zvol_immediate_itx(off, nbytes, addr); } else { boff = P2ALIGN_TYPED(off, blocksize, uint64_t); itx = zil_itx_create(TX_WRITE, sizeof (*lr)); lr = (lr_write_t *)&itx->itx_lr; lr->lr_foid = ZVOL_OBJ; lr->lr_offset = off; lr->lr_length = nbytes; lr->lr_blkoff = off - boff; BP_ZERO(&lr->lr_blkptr); /* XXX - we should do these IOs in parallel */ VERIFY(0 == dmu_buf_hold(os, ZVOL_OBJ, boff, FTAG, &db)); ASSERT(boff == db->db_offset); error = dmu_sync(NULL, db, &lr->lr_blkptr, txg, NULL, NULL); dmu_buf_rele(db, FTAG); if (error) { kmem_free(itx, offsetof(itx_t, itx_lr)); return (error); } itx->itx_wr_state = WR_COPIED; } (void) zil_itx_assign(zv->zv_zilog, itx, tx); len -= nbytes; off += nbytes; } return (0); }