static int multipath_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, uint8_t *data, int sz) { int rq_servact = cdb[1]; struct prin_resp resp; size_t written; int r; switch (rq_servact) { case MPATH_PRIN_RKEY_SA: case MPATH_PRIN_RRES_SA: case MPATH_PRIN_RCAP_SA: break; case MPATH_PRIN_RFSTAT_SA: /* Nobody implements it anyway, so bail out. */ default: /* Cannot parse any other output. */ scsi_build_sense(sense, SENSE_CODE(INVALID_FIELD)); return CHECK_CONDITION; } r = mpath_persistent_reserve_in(fd, rq_servact, &resp, noisy, verbose); if (r == MPATH_PR_SUCCESS) { switch (rq_servact) { case MPATH_PRIN_RKEY_SA: case MPATH_PRIN_RRES_SA: { struct prin_readdescr *out = &resp.prin_descriptor.prin_readkeys; assert(sz >= 8); written = MIN(out->additional_length + 8, sz); stl_be_p(&data[0], out->prgeneration); stl_be_p(&data[4], out->additional_length); memcpy(&data[8], out->key_list, written - 8); break; } case MPATH_PRIN_RCAP_SA: { struct prin_capdescr *out = &resp.prin_descriptor.prin_readcap; assert(sz >= 6); written = 6; stw_be_p(&data[0], out->length); data[2] = out->flags[0]; data[3] = out->flags[1]; stw_be_p(&data[4], out->pr_type_mask); break; } default: scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE)); return CHECK_CONDITION; } assert(written <= sz); memset(data + written, 0, sz - written); } return mpath_reconstruct_sense(fd, r, sense); }
/* Helper function for command completion. */ static void scsi_command_complete(void *opaque, int ret) { int status; SCSIGenericReq *r = (SCSIGenericReq *)opaque; r->req.aiocb = NULL; if (r->req.io_canceled) { scsi_req_cancel_complete(&r->req); goto done; } if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { r->req.sense_len = r->io_header.sb_len_wr; } if (ret != 0) { switch (ret) { case -EDOM: status = TASK_SET_FULL; break; case -ENOMEM: status = CHECK_CONDITION; scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); break; default: status = CHECK_CONDITION; scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); break; } } else { if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || r->io_header.host_status == SG_ERR_DID_BUS_BUSY || r->io_header.host_status == SG_ERR_DID_TIME_OUT || (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { status = BUSY; BADF("Driver Timeout\n"); } else if (r->io_header.host_status) { status = CHECK_CONDITION; scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); } else if (r->io_header.status) { status = r->io_header.status; } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { status = CHECK_CONDITION; } else { status = GOOD; } } DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", r, r->req.tag, status); scsi_req_complete(&r->req, status); done: scsi_req_unref(&r->req); }
static int mpath_reconstruct_sense(int fd, int r, uint8_t *sense) { switch (r) { case MPATH_PR_SUCCESS: return GOOD; case MPATH_PR_SENSE_NOT_READY: case MPATH_PR_SENSE_MEDIUM_ERROR: case MPATH_PR_SENSE_HARDWARE_ERROR: case MPATH_PR_SENSE_ABORTED_COMMAND: { /* libmpathpersist ate the exact sense. Try to find it by * issuing TEST UNIT READY. */ uint8_t cdb[6] = { TEST_UNIT_READY }; int sz = 0; int r = do_sgio(fd, cdb, sense, NULL, &sz, SG_DXFER_NONE); if (r != GOOD) { return r; } scsi_build_sense(sense, mpath_generic_sense(r)); return CHECK_CONDITION; } case MPATH_PR_SENSE_UNIT_ATTENTION: /* Congratulations libmpathpersist, you ruined the Unit Attention... * Return a heavyweight one. */ scsi_build_sense(sense, SENSE_CODE(SCSI_BUS_RESET)); return CHECK_CONDITION; case MPATH_PR_SENSE_INVALID_OP: /* Only one valid sense. */ scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE)); return CHECK_CONDITION; case MPATH_PR_ILLEGAL_REQ: /* Guess. */ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); return CHECK_CONDITION; case MPATH_PR_NO_SENSE: scsi_build_sense(sense, SENSE_CODE(NO_SENSE)); return CHECK_CONDITION; case MPATH_PR_RESERV_CONFLICT: return RESERVATION_CONFLICT; case MPATH_PR_OTHER: default: scsi_build_sense(sense, SENSE_CODE(LUN_COMM_FAILURE)); return CHECK_CONDITION; } }
static SCSISense mpath_generic_sense(int r) { switch (r) { case MPATH_PR_SENSE_NOT_READY: return SENSE_CODE(NOT_READY); case MPATH_PR_SENSE_MEDIUM_ERROR: return SENSE_CODE(READ_ERROR); case MPATH_PR_SENSE_HARDWARE_ERROR: return SENSE_CODE(TARGET_FAILURE); case MPATH_PR_SENSE_ABORTED_COMMAND: return SENSE_CODE(IO_ERROR); default: abort(); } }
/* Helper function for command completion. */ static void scsi_command_complete(void *opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); r->req.aiocb = NULL; s->driver_status = r->io_header.driver_status; if (s->driver_status & SG_ERR_DRIVER_SENSE) s->senselen = r->io_header.sb_len_wr; if (ret != 0) { switch (ret) { case -EDOM: r->req.status = TASK_SET_FULL; break; case -EINVAL: r->req.status = CHECK_CONDITION; scsi_set_sense(s, SENSE_CODE(INVALID_FIELD)); break; case -ENOMEM: r->req.status = CHECK_CONDITION; scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE)); break; default: r->req.status = CHECK_CONDITION; scsi_set_sense(s, SENSE_CODE(IO_ERROR)); break; } } else { if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { r->req.status = BUSY; BADF("Driver Timeout\n"); } else if (r->io_header.status) r->req.status = r->io_header.status; else if (s->driver_status & SG_ERR_DRIVER_SENSE) r->req.status = CHECK_CONDITION; else r->req.status = GOOD; } DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", r, r->req.tag, r->req.status); scsi_req_complete(&r->req); }
static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); int size = SCSI_SENSE_BUF_SIZE; if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) { size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); } if (size > len) { size = len; } memcpy(outbuf, s->sensebuf, size); return size; }
static void scsi_generic_reset(DeviceState *dev) { SCSIDevice *s = SCSI_DEVICE(dev); scsi_device_purge_requests(s, SENSE_CODE(RESET)); }
static void scsi_destroy(SCSIDevice *s) { scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE)); blockdev_mark_auto_del(s->conf.bs); }
static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, const uint8_t *param, int sz) { int rq_servact = cdb[1]; int rq_scope = cdb[2] >> 4; int rq_type = cdb[2] & 0xf; struct prout_param_descriptor paramp; char transportids[PR_HELPER_DATA_SIZE]; int r; if (sz < PR_OUT_FIXED_PARAM_SIZE) { /* Illegal request, Parameter list length error. This isn't fatal; * we have read the data, send an error without closing the socket. */ scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN)); return CHECK_CONDITION; } switch (rq_servact) { case MPATH_PROUT_REG_SA: case MPATH_PROUT_RES_SA: case MPATH_PROUT_REL_SA: case MPATH_PROUT_CLEAR_SA: case MPATH_PROUT_PREE_SA: case MPATH_PROUT_PREE_AB_SA: case MPATH_PROUT_REG_IGN_SA: break; case MPATH_PROUT_REG_MOV_SA: /* Not supported by struct prout_param_descriptor. */ default: /* Cannot parse any other input. */ scsi_build_sense(sense, SENSE_CODE(INVALID_FIELD)); return CHECK_CONDITION; } /* Convert input data, especially transport IDs, to the structs * used by libmpathpersist (which, of course, will immediately * do the opposite). */ memset(¶mp, 0, sizeof(paramp)); memcpy(¶mp.key, ¶m[0], 8); memcpy(¶mp.sa_key, ¶m[8], 8); paramp.sa_flags = param[20]; if (sz > PR_OUT_FIXED_PARAM_SIZE) { size_t transportid_len; int i, j; if (sz < PR_OUT_FIXED_PARAM_SIZE + 4) { scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN)); return CHECK_CONDITION; } transportid_len = ldl_be_p(¶m[24]) + PR_OUT_FIXED_PARAM_SIZE + 4; if (transportid_len > sz) { scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); return CHECK_CONDITION; } for (i = PR_OUT_FIXED_PARAM_SIZE + 4, j = 0; i < transportid_len; ) { struct transportid *id = (struct transportid *) &transportids[j]; int len; id->format_code = param[i] & 0xc0; id->protocol_id = param[i] & 0x0f; switch (param[i] & 0xcf) { case 0: /* FC transport. */ if (i + 24 > transportid_len) { goto illegal_req; } memcpy(id->n_port_name, ¶m[i + 8], 8); j += offsetof(struct transportid, n_port_name[8]); i += 24; break; case 5: case 0x45: /* iSCSI transport. */ len = lduw_be_p(¶m[i + 2]); if (len > 252 || (len & 3) || i + len + 4 > transportid_len) { /* For format code 00, the standard says the maximum is 223 * plus the NUL terminator. For format code 01 there is no * maximum length, but libmpathpersist ignores the first * byte of id->iscsi_name so our maximum is 252. */ goto illegal_req; } if (memchr(¶m[i + 4], 0, len) == NULL) { goto illegal_req; } memcpy(id->iscsi_name, ¶m[i + 2], len + 2); j += offsetof(struct transportid, iscsi_name[len + 2]); i += len + 4; break; case 6: /* SAS transport. */ if (i + 24 > transportid_len) { goto illegal_req; } memcpy(id->sas_address, ¶m[i + 4], 8); j += offsetof(struct transportid, sas_address[8]); i += 24; break; default: illegal_req: scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM)); return CHECK_CONDITION; } paramp.trnptid_list[paramp.num_transportid++] = id; } }
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) { DPRINTF("Unimplemented LUN %d\n", req->lun); scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED)); r->req.status = CHECK_CONDITION; scsi_req_complete(&r->req); return 0; } if (-1 == scsi_req_parse(&r->req, cmd)) { BADF("Unsupported command length, command %x\n", cmd[0]); scsi_command_complete(r, -EINVAL); return 0; } scsi_req_fixup(&r->req); DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag, r->req.cmd.xfer, cmd[0]); #ifdef DEBUG_SCSI { int i; for (i = 1; i < r->req.cmd.len; i++) { printf(" 0x%02x", cmd[i]); } printf("\n"); } #endif if (r->req.cmd.xfer == 0) { if (r->buf != NULL) qemu_free(r->buf); r->buflen = 0; r->buf = NULL; ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); if (ret < 0) { scsi_command_complete(r, ret); return 0; } return 0; } if (r->buflen != r->req.cmd.xfer) { if (r->buf != NULL) qemu_free(r->buf); r->buf = qemu_malloc(r->req.cmd.xfer); r->buflen = r->req.cmd.xfer; } memset(r->buf, 0, r->buflen); r->len = r->req.cmd.xfer; if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { r->len = 0; return -r->req.cmd.xfer; } else { return r->req.cmd.xfer; } }
static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf) { scsi_req_build_sense(req, SENSE_CODE(INVALID_OPCODE)); scsi_req_complete(req, CHECK_CONDITION); return 0; }
static void scsi_unrealize(SCSIDevice *s, Error **errp) { scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE)); blockdev_mark_auto_del(s->conf.blk); }