From: root <ro...@ou...> - 2012-04-03 07:02:57
|
>From ad1c0b0a86e90eb95dc0699392783abbbc84eaa2 Mon Sep 17 00:00:00 2001 From: Yucong Sun <sun...@gm...> Date: Mon, 2 Apr 2012 23:24:37 -0700 Subject: [PATCH] Implement WRITE_SAME_16(0x93) WRITE_SAME_16 provides 1 block of data and iscsi target writes the same data to the offset and length specified. If length is 0, the target should write to end of media. This change is tested with ESXi5 lazy eager zero disk. Signed-off-by: Yucong Sun <sun...@gm...> --- trunk/kernel/iscsi.c | 37 ++++++++++++++++++++-- trunk/kernel/iscsi_dbg.h | 1 + trunk/kernel/target_disk.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ trunk/kernel/volume.c | 1 + 4 files changed, 106 insertions(+), 4 deletions(-) diff --git a/trunk/kernel/iscsi.c b/trunk/kernel/iscsi.c index f4d21d7..9ad3047 100644 --- a/trunk/kernel/iscsi.c +++ b/trunk/kernel/iscsi.c @@ -773,6 +773,7 @@ static void set_offset_and_length(const struct iet_volume *lu, break; case READ_16: case WRITE_16: + case WRITE_SAME_16: *off = (u64)cmd[2] << 56 | (u64)cmd[3] << 48 | (u64)cmd[4] << 40 | (u64)cmd[5] << 32 | (u64)cmd[6] << 24 | (u64)cmd[7] << 16 | @@ -1035,6 +1036,7 @@ static void scsi_cmnd_start(struct iscsi_conn *conn, struct iscsi_cmnd *req) case WRITE_10: case WRITE_16: case WRITE_VERIFY: + case WRITE_SAME_16: { struct iscsi_sess_param *param = &conn->session->param; loff_t offset; @@ -1063,11 +1065,38 @@ static void scsi_cmnd_start(struct iscsi_conn *conn, struct iscsi_cmnd *req) eprintk("Verification is ignored %x\n", cmnd_itt(req)); set_offset_and_length(req->lun, req_hdr->scb, &offset, &length); - if (cmnd_write_size(req) != length) - eprintk("%x %u %u\n", cmnd_itt(req), cmnd_write_size(req), length); - req->tio = tio_alloc(get_pgcnt(length)); - tio_set(req->tio, length, offset); + if (req_hdr->scb[0] == WRITE_SAME_16) { + dprintk(D_VAAI, "WRITE_SAME_16: D: %u L:%u O:%llu\n", + cmnd_write_size(req), length, offset); + + if (unlikely(cmnd_write_size(req) > PAGE_CACHE_SIZE)) { + eprintk("WRITE_SAME data is bigger than page size" + ", this is unsupported: D:%u P:%lu\n", + cmnd_write_size(req), PAGE_CACHE_SIZE); + goto error; + } + + if (unlikely(length % cmnd_write_size(req) != 0)) { + eprintk("WRITE_SAME data size error: L:%u D:%u\n", + length, cmnd_write_size(req)); + goto error; + } + + if (unlikely((req_hdr->scb[1] & 0x06) != 0)) + eprintk("WRITE_SAME LB/PBDATA ignored: %x\n", + req_hdr->scb[1]); + + req->tio = tio_alloc(1); + tio_set(req->tio, length, offset); + } else { + if (unlikely(cmnd_write_size(req) != length)) + eprintk("%x %u %u\n", + cmnd_itt(req), cmnd_write_size(req), + length); + req->tio = tio_alloc(get_pgcnt(length)); + tio_set(req->tio, length, offset); + } if (req->pdu.datasize) { if (cmnd_recv_pdu(conn, req->tio, 0, req->pdu.datasize) < 0) diff --git a/trunk/kernel/iscsi_dbg.h b/trunk/kernel/iscsi_dbg.h index 69581d2..8c9b928 100644 --- a/trunk/kernel/iscsi_dbg.h +++ b/trunk/kernel/iscsi_dbg.h @@ -12,6 +12,7 @@ #define D_IOMODE (1UL << 8) #define D_UAC (1UL << 9) #define D_PR (1UL << 10) +#define D_VAAI (1UL << 11) #define D_DATA (D_READ | D_WRITE) diff --git a/trunk/kernel/target_disk.c b/trunk/kernel/target_disk.c index 00b73b2..52f11c4 100644 --- a/trunk/kernel/target_disk.c +++ b/trunk/kernel/target_disk.c @@ -408,6 +408,74 @@ static void build_write_response(struct iscsi_cmnd *cmnd) iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0); } +static void build_write_same_response(struct iscsi_cmnd *cmnd) { + int err; + struct tio *target_tio; + struct iet_volume *lu = cmnd->lun; + struct tio_iterator iter; + u32 MAX_IO_SIZE = 1 << 30; /* 1MByte */ + u32 length, medium_length; + loff_t offset; + u8 *data_addr; + u32 data_size; + + length = cmnd->tio->size; + medium_length = (u32)((loff_t) lu->blk_cnt << lu->blk_shift); + offset = cmnd->tio->offset; + + data_addr = page_address(cmnd->tio->pvec[0]); + data_size = be32_to_cpu(cmnd_hdr(cmnd)->data_length); + + /* When length = 0, it means we need to write to the end. */ + if (length == 0) { + length = medium_length - offset + 1; + dprintk(D_VAAI, + "write to end, calculated length = %u\n", length); + } + + if (length + offset > medium_length) { + /* Write out of boundary, Invalid Field in CDB */ + iscsi_cmnd_set_sense(cmnd, ILLEGAL_REQUEST, 0x24, 0x0); + return; + } + + list_del_init(&cmnd->list); + + /* Fill target_tio with data from request, because it's all + same data, we can just reuse it later with differnt offset. */ + target_tio = tio_alloc(get_pgcnt(min_t(u32, length, MAX_IO_SIZE))); + tio_get(target_tio); + + tio_init_iterator(target_tio, &iter); + while(iter.pg_idx < target_tio->pg_cnt) { + tio_add_data(&iter, data_addr, data_size); + } + + while (length > 0) { + u32 to_write = min_t(u32, length, MAX_IO_SIZE); + tio_set(target_tio, to_write, offset); + + /* submit to IO layer */ + err = tio_write(lu, target_tio); + if (!err && !LUWCache(lu)) + err = tio_sync(lu, target_tio); + + if (err) { + /* Medium Error/Write Fault */ + iscsi_cmnd_set_sense(cmnd, MEDIUM_ERROR, 0x03, 0x0); + break; + } + + length -= to_write; + offset += to_write; + dprintk(D_VAAI, + "Committed %u bytes, %u left, offset %llu\n", + to_write, length, offset); + } + + tio_put(target_tio); +} + static void build_sync_cache_response(struct iscsi_cmnd *cmnd) { assert(cmnd->lun); @@ -550,6 +618,9 @@ static int disk_execute_cmnd(struct iscsi_cmnd *cmnd) case WRITE_VERIFY: send_scsi_rsp(cmnd, build_write_response); break; + case WRITE_SAME_16: + send_scsi_rsp(cmnd, build_write_same_response); + break; case SYNCHRONIZE_CACHE: send_scsi_rsp(cmnd, build_sync_cache_response); break; diff --git a/trunk/kernel/volume.c b/trunk/kernel/volume.c index 93bd958..8beeb1e 100644 --- a/trunk/kernel/volume.c +++ b/trunk/kernel/volume.c @@ -446,6 +446,7 @@ int is_volume_reserved(struct iet_volume *volume, u64 sid, u8 *scb) case WRITE_10: case WRITE_12: case WRITE_16: + case WRITE_SAME_16: case WRITE_VERIFY: case SYNCHRONIZE_CACHE: if (write_excl || excl_access) -- 1.7.5.4 |