diff -r f7734d6b8e80 -r 4b9dc4ca8e9f usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c --- a/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c Fri Mar 04 15:27:25 2011 -0500 +++ b/usr/src/uts/common/io/comstar/lu/stmf_sbd/sbd_scsi.c Fri Mar 04 13:57:09 2011 -0800 @@ -20,6 +20,8 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. */ #include @@ -98,6 +100,11 @@ void sbd_handle_mode_select(scsi_task_t *task, stmf_data_buf_t *dbuf); void sbd_handle_identifying_info(scsi_task_t *task, stmf_data_buf_t *dbuf); +static void sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf, + uint32_t buflen); +static void sbd_handle_unmap(scsi_task_t *task, stmf_data_buf_t *dbuf); +static void sbd_handle_write_same(scsi_task_t *task); + extern void sbd_pgr_initialize_it(scsi_task_t *, sbd_it_data_t *); extern int sbd_pgr_reservation_conflict(scsi_task_t *); extern void sbd_pgr_reset(sbd_lu_t *); @@ -1754,6 +1761,10 @@ dbuf->db_sglist[0].seg_addr, dbuf->db_data_size); } break; + case SCMD_UNMAP: + sbd_handle_unmap_xfer(task, + dbuf->db_sglist[0].seg_addr, dbuf->db_data_size); + break; case SCMD_PERSISTENT_RESERVE_OUT: if (sl->sl_access_state == SBD_LU_STANDBY) { st_ret = stmf_proxy_scsi_cmd(task, dbuf); @@ -1815,6 +1826,9 @@ p[7] = s & 0xff; p[10] = (blksize >> 8) & 0xff; p[11] = blksize & 0xff; + if (sl->sl_flags & SL_UNMAP_ENABLED) { + p[14] = 0x80; + } sbd_handle_short_read_transfers(task, initial_dbuf, p, cdb_len, 32); break; @@ -2216,6 +2230,105 @@ return (url_length); } +static void +sbd_handle_write_same(scsi_task_t *task) +{ + sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private; + uint64_t addr, len; + uint8_t *p; + + task->task_cmd_xfer_length = 0; + if (task->task_additional_flags & + TASK_AF_NO_EXPECTED_XFER_LENGTH) { + task->task_expected_xfer_length = 0; + } + if (task->task_cdb[1] & 0xF7) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_INVALID_FIELD_IN_CDB); + return; + } + p = &task->task_cdb[2]; + addr = READ_SCSI64(p, uint64_t); + addr <<= sl->sl_data_blocksize_shift; + len = READ_SCSI32(p+8, uint64_t); + len <<= sl->sl_data_blocksize_shift; + + /* TODO -> full write_same support with data checks... */ + if (sbd_unmap(sl, addr, len) != 0) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_LBA_OUT_OF_RANGE); + return; + } + stmf_scsilib_send_status(task, STATUS_GOOD, 0); +} + +static void +sbd_handle_unmap(scsi_task_t *task, stmf_data_buf_t *dbuf) +{ + uint32_t cmd_xfer_len; + + cmd_xfer_len = READ_SCSI16(&task->task_cdb[7], uint32_t); + + if (task->task_cdb[1] & 1) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_INVALID_FIELD_IN_CDB); + return; + } + + if (cmd_xfer_len == 0) { + task->task_cmd_xfer_length = 0; + if (task->task_additional_flags & + TASK_AF_NO_EXPECTED_XFER_LENGTH) { + task->task_expected_xfer_length = 0; + } + stmf_scsilib_send_status(task, STATUS_GOOD, 0); + return; + } + + sbd_handle_short_write_transfers(task, dbuf, cmd_xfer_len); +} + +static void +sbd_handle_unmap_xfer(scsi_task_t *task, uint8_t *buf, uint32_t buflen) +{ + sbd_lu_t *sl = (sbd_lu_t *)task->task_lu->lu_provider_private; + uint32_t ulen, dlen, num_desc; + uint64_t addr, len; + uint8_t *p; + int ret; + + if (buflen < 24) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_INVALID_FIELD_IN_CDB); + return; + } + ulen = READ_SCSI16(buf, uint32_t); + dlen = READ_SCSI16(buf + 2, uint32_t); + num_desc = dlen >> 4; + if (((ulen + 2) != buflen) || ((dlen + 8) != buflen) || (dlen & 0xf) || + (num_desc == 0)) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_INVALID_FIELD_IN_CDB); + return; + } + + for (p = buf + 8; num_desc; num_desc--, p += 16) { + addr = READ_SCSI64(p, uint64_t); + addr <<= sl->sl_data_blocksize_shift; + len = READ_SCSI32(p+8, uint64_t); + len <<= sl->sl_data_blocksize_shift; + ret = sbd_unmap(sl, addr, len); + if (ret != 0) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_LBA_OUT_OF_RANGE); + return; + } + } + +unmap_done: + stmf_scsilib_send_status(task, STATUS_GOOD, 0); +} + void sbd_handle_inquiry(struct scsi_task *task, struct stmf_data_buf *initial_dbuf) { @@ -2228,6 +2341,8 @@ uint16_t cmd_size; uint32_t xfer_size = 4; uint32_t mgmt_url_size = 0; + uint8_t exp; + uint64_t s; char *mgmt_url = NULL; @@ -2380,6 +2495,8 @@ switch (cdbp[2]) { case 0x00: page_length = 4 + (mgmt_url_size ? 1 : 0); + if (sl->sl_flags & SL_UNMAP_ENABLED) + page_length += 2; p[0] = byte0; p[3] = page_length; @@ -2392,6 +2509,10 @@ if (mgmt_url_size != 0) p[i++] = 0x85; p[i++] = 0x86; + if (sl->sl_flags & SL_UNMAP_ENABLED) { + p[i++] = 0xb0; + p[i++] = 0xb2; + } } xfer_size = page_length + 4; break; @@ -2482,6 +2603,43 @@ xfer_size = page_length + 4; break; + case 0xb0: + if ((sl->sl_flags & SL_UNMAP_ENABLED) == 0) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_INVALID_FIELD_IN_CDB); + goto err_done; + } + page_length = 0x3c; + p[0] = byte0; + p[1] = 0xb0; + p[3] = page_length; + p[20] = p[21] = p[22] = p[23] = 0xFF; + p[24] = p[25] = p[26] = p[27] = 0xFF; + xfer_size = page_length + 4; + break; + + case 0xb2: + if ((sl->sl_flags & SL_UNMAP_ENABLED) == 0) { + stmf_scsilib_send_status(task, STATUS_CHECK, + STMF_SAA_INVALID_FIELD_IN_CDB); + goto err_done; + } + page_length = 4; + p[0] = byte0; + p[1] = 0xb2; + p[3] = page_length; + + exp = (uint8_t)sl->sl_data_blocksize_shift; + s = sl->sl_lu_size >> sl->sl_data_blocksize_shift; + while (s & ((uint64_t)0xFFFFFFFF80000000ull)) { + s >>= 1; + exp++; + } + p[4] = exp; + p[5] = 0xc0; + xfer_size = page_length + 4; + break; + default: stmf_scsilib_send_status(task, STATUS_CHECK, STMF_SAA_INVALID_FIELD_IN_CDB); @@ -2908,6 +3066,16 @@ return; } + if ((cdb0 == SCMD_UNMAP) && (sl->sl_flags & SL_UNMAP_ENABLED)) { + sbd_handle_unmap(task, initial_dbuf); + return; + } + + if ((cdb0 == SCMD_WRITE_SAME_G4) && (sl->sl_flags & SL_UNMAP_ENABLED)) { + sbd_handle_write_same(task); + return; + } + if (cdb0 == SCMD_TEST_UNIT_READY) { /* Test unit ready */ task->task_cmd_xfer_length = 0; stmf_scsilib_send_status(task, STATUS_GOOD, 0);