Exemplo n.º 1
0
/*!	Check scsi status, using sense if available. */
static err_res
check_scsi_status(scsi_periph_device_info *device, scsi_ccb *request)
{
	SHOW_FLOW(3, "%d", request->device_status & SCSI_STATUS_MASK);

	switch (request->device_status & SCSI_STATUS_MASK) {
		case SCSI_STATUS_GOOD:
			// shouldn't happen (cam_status should be CAM_REQ_CMP)
			return MK_ERROR(err_act_ok, B_OK);

		case SCSI_STATUS_CHECK_CONDITION:
			return check_sense(device, request);

		case SCSI_STATUS_QUEUE_FULL:
			// SIM should have automatically requeued request, fall through
		case SCSI_STATUS_BUSY:
			// take deep breath and try again
			snooze(1000000);
			return MK_ERROR(err_act_retry, B_DEV_TIMEOUT);

		case SCSI_STATUS_COMMAND_TERMINATED:
			return MK_ERROR(err_act_retry, B_INTERRUPTED);

		default:
			return MK_ERROR(err_act_retry, B_ERROR);
	}
}
Exemplo n.º 2
0
static err_res
wait_for_ready(scsi_periph_device_info *device, scsi_ccb *request)
{	
	int retries = 0;
	
	while (true) {
		err_res res;

		// we send TURs until the device is OK or all hope is lost
		res = send_tur(device, request);

		switch (res.action) {
			case err_act_ok:
				return MK_ERROR(err_act_ok, B_OK);

			case err_act_retry:
				if (++retries >= 3)
					return MK_ERROR(err_act_fail, res.error_code);
				break;

			case err_act_many_retries:
				if (++retries >= 30)
					return MK_ERROR(err_act_fail, res.error_code);
				break;

			default:
				SHOW_FLOW( 3, "action: %x, error: %x", (int)res.action, (int)res.error_code);
				return res;
		}
	}
}
Exemplo n.º 3
0
static void
H_stats(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
        lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_error_t rc;
    lcb_server_stat_resp_t resp;
    mc_REQDATAEX *exdata;

    MK_ERROR(root, rc, response, immerr);
    resp.version = 0;

    exdata = request->u_rdata.exdata;
    if (rc != LCB_SUCCESS || PACKET_NKEY(response) == 0) {
        /* Call the handler without a response, this indicates that this server
         * has finished responding */
        exdata->callback(pipeline, request, rc, NULL);
        return;
    }

    if ((resp.v.v0.nkey = PACKET_NKEY(response))) {
        resp.v.v0.key = PACKET_KEY(response);
        if ((resp.v.v0.nbytes = PACKET_NVALUE(response))) {
            resp.v.v0.bytes = PACKET_VALUE(response);
        }
    }

    exdata->callback(pipeline, request, rc, &resp);
}
Exemplo n.º 4
0
static void
H_stats(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
        lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->cqdata;
    lcb_RESPSTATS resp = { 0 };
    mc_REQDATAEX *exdata;

    MK_ERROR(root, &resp, response, immerr);
    resp.version = 0;

    exdata = request->u_rdata.exdata;
    if (resp.rc != LCB_SUCCESS || PACKET_NKEY(response) == 0) {
        /* Call the handler without a response, this indicates that this server
         * has finished responding */
        exdata->procs->handler(pipeline, request, resp.rc, NULL);
        return;
    }

    if ((resp.nkey = PACKET_NKEY(response))) {
        resp.key = PACKET_KEY(response);
        if ((resp.value = PACKET_VALUE(response))) {
            resp.nvalue = PACKET_NVALUE(response);
        }
    }

    exdata->procs->handler(pipeline, request, resp.rc, &resp);
}
Exemplo n.º 5
0
static void
H_getreplica(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
             lcb_error_t immerr)
{
    lcb_error_t rc;
    lcb_get_resp_t resp;
    lcb_t instance = pipeline->parent->instance;
    void *freeptr = NULL;

    MK_RESPKEY(&resp, 0, request);
    MK_ERROR(instance, rc, response, immerr);
    resp.version = 0;

    if (rc == LCB_SUCCESS) {
        const protocol_binary_response_get *get = PACKET_EPHEMERAL_START(response);
        resp.v.v0.cas = PACKET_CAS(response);
        resp.v.v0.datatype = PACKET_DATATYPE(response);
        resp.v.v0.flags = ntohl(get->message.body.flags);
        resp.v.v0.bytes = PACKET_VALUE(response);
        resp.v.v0.nbytes = PACKET_NVALUE(response);
    }
    maybe_decompress(instance, response, &resp, &freeptr);
    request->u_rdata.exdata->callback(pipeline, request, rc, &resp);
    free(freeptr);
}
Exemplo n.º 6
0
static void
init_resp3(lcb_t instance, const packet_info *mc_resp, const mc_PACKET *req,
    lcb_error_t immerr, lcb_RESPBASE *resp)
{
    MK_ERROR(instance, resp, mc_resp, immerr);
    resp->cas = PACKET_CAS(mc_resp);
    resp->cookie = (void *)MCREQ_PKT_COOKIE(req);
    mcreq_get_key(req, &resp->key, &resp->nkey);
}
Exemplo n.º 7
0
static void
H_flush(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
        lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->cqdata;
    lcb_RESPFLUSH resp = { 0 };
    mc_REQDATAEX *exdata = request->u_rdata.exdata;
    MK_ERROR(root, &resp, response, immerr);
    exdata->procs->handler(pipeline, request, resp.rc, &resp);
}
Exemplo n.º 8
0
static void
H_config(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
         lcb_error_t immerr)
{
    /** We just jump to the normal config handler */
    lcb_error_t rc;
    mc_REQDATAEX *exdata = request->u_rdata.exdata;
    MK_ERROR(pipeline->parent->instance, rc, response, immerr);

    exdata->callback(pipeline, request, rc, response);
}
Exemplo n.º 9
0
static void
H_config(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
         lcb_error_t immerr)
{
    /** We just jump to the normal config handler */
    lcb_RESPBASE dummy;
    mc_REQDATAEX *exdata = request->u_rdata.exdata;
    MK_ERROR(pipeline->parent->cqdata, &dummy, response, immerr);

    exdata->procs->handler(pipeline, request, dummy.rc, response);
}
Exemplo n.º 10
0
static void
H_flush(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
        lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_error_t rc;
    lcb_flush_resp_t resp;
    mc_REQDATAEX *exdata = request->u_rdata.exdata;
    MK_ERROR(root, rc, response, immerr);
    exdata->callback(pipeline, request, rc, &resp);
}
Exemplo n.º 11
0
static void
H_verbosity(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
            lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->cqdata;
    lcb_RESPBASE dummy = { 0 };
    mc_REQDATAEX *exdata = request->u_rdata.exdata;
    MK_ERROR(root, &dummy, response, immerr);

    exdata->procs->handler(pipeline, request, dummy.rc, NULL);
}
Exemplo n.º 12
0
static void
H_unlock(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
         lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_unlock_resp_t resp;
    lcb_error_t rc;

    MK_ERROR(root, rc, response, immerr);
    MK_RESPKEY(&resp, 0, request);
    root->callbacks.unlock(root, MCREQ_PKT_RDATA(request)->cookie, rc, &resp);
}
Exemplo n.º 13
0
static void
H_touch(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
        lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_touch_resp_t resp;
    lcb_error_t rc;

    MK_ERROR(root, rc, response, immerr);
    MK_RESPKEY(&resp, 0, request);
    SET_RESP_CAS(&resp, 0, PACKET_CAS(response));
    resp.version = 0;
    root->callbacks.touch(root, MCREQ_PKT_RDATA(request)->cookie, rc, &resp);
}
Exemplo n.º 14
0
static void
H_delete(mc_PIPELINE *pipeline, mc_PACKET *packet, packet_info *response,
         lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_error_t rc;
    lcb_remove_resp_t resp;

    resp.version = 0;
    SET_RESP_CAS(&resp, 0, PACKET_CAS(response));
    MK_RESPKEY(&resp, 0, packet);
    MK_ERROR(root, rc, response, immerr);

    INVOKE_CALLBACK(packet, root->callbacks.remove,
                    (root, MCREQ_PKT_COOKIE(packet), rc, &resp));
}
Exemplo n.º 15
0
static void
H_version(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
          lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->cqdata;
    lcb_RESPMCVERSION resp = { 0 };
    mc_REQDATAEX *exdata = request->u_rdata.exdata;

    MK_ERROR(root, &resp, response, immerr);

    if (PACKET_NBODY(response)) {
        resp.mcversion = response->payload;
        resp.nversion = PACKET_NBODY(response);
    }


    exdata->procs->handler(pipeline, request, resp.rc, &resp);
}
Exemplo n.º 16
0
static void
H_arithmetic(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
             lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_error_t rc;
    lcb_arithmetic_resp_t resp;

    MK_ERROR(root, rc, response, immerr);
    if (rc == LCB_SUCCESS) {
        memcpy(&resp.v.v0.value, response->payload, sizeof(resp.v.v0.value));
        resp.v.v0.value = ntohll(resp.v.v0.value);
    }

    MK_RESPKEY(&resp, 0, request);
    SET_RESP_CAS(&resp, 0, PACKET_CAS(response));
    INVOKE_CALLBACK(request, root->callbacks.arithmetic,
                   (root, MCREQ_PKT_COOKIE(request), rc, &resp));
}
Exemplo n.º 17
0
static void
H_version(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
          lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_error_t rc;
    lcb_server_version_resp_t resp;
    mc_REQDATAEX *exdata = request->u_rdata.exdata;

    MK_ERROR(root, rc, response, immerr);

    if (PACKET_NBODY(response)) {
        resp.v.v0.vstring = response->payload;
        resp.v.v0.nvstring = PACKET_NBODY(response);
    }


    exdata->callback(pipeline, request, rc, &resp);
}
Exemplo n.º 18
0
static void
H_store(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
        lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_storage_t op;
    lcb_error_t rc;
    lcb_store_resp_t resp;

    MK_ERROR(root, rc, response, immerr);

    switch (PACKET_OPCODE(response)) {
    case PROTOCOL_BINARY_CMD_ADD:
        op = LCB_ADD;
        break;
    case PROTOCOL_BINARY_CMD_REPLACE:
        op = LCB_REPLACE;
        break;
    case PROTOCOL_BINARY_CMD_SET:
        op = LCB_SET;
        break;
    case PROTOCOL_BINARY_CMD_APPEND:
        op = LCB_APPEND;
        break;
    case PROTOCOL_BINARY_CMD_PREPEND:
        op = LCB_PREPEND;
        break;
    default:
        abort();
        break;
    }

    resp.version = 0;
    MK_RESPKEY(&resp, 0, request);
    SET_RESP_CAS(&resp, 0, PACKET_CAS(response));

    INVOKE_CALLBACK(request, root->callbacks.store,
                    (root, MCREQ_PKT_COOKIE(request), op, rc, &resp));
}
Exemplo n.º 19
0
static void
H_get(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
      lcb_error_t immerr)
{
    lcb_error_t rc;
    lcb_t o;
    lcb_get_resp_t resp;
    void *freeptr = NULL;

    o = pipeline->parent->instance;
    MK_RESPKEY(&resp, 0, request);
    MK_ERROR(o, rc, response, immerr);
    resp.version = 0;

    if (rc == LCB_SUCCESS) {
        const protocol_binary_response_getq *getq =
                PACKET_EPHEMERAL_START(response);

        resp.v.v0.cas = PACKET_CAS(response);
        resp.v.v0.datatype = PACKET_DATATYPE(response);
        resp.v.v0.flags = ntohl(getq->message.body.flags);
        resp.v.v0.bytes = PACKET_VALUE(response);
        resp.v.v0.nbytes = PACKET_NVALUE(response);
        rc = LCB_SUCCESS;
    } else {
        resp.v.v0.cas = 0;
        resp.v.v0.nbytes = 0;
        resp.v.v0.bytes = NULL;
        resp.v.v0.flags = 0;
    }

    maybe_decompress(o, response, &resp, &freeptr);
    INVOKE_CALLBACK(request, o->callbacks.get,
                    (o, MCREQ_PKT_COOKIE(request), rc, &resp));
    free(freeptr);
}
Exemplo n.º 20
0
static void
H_observe(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
          lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->instance;
    lcb_error_t rc;
    uint32_t ttp;
    uint32_t ttr;
    lcb_size_t pos;
    VBUCKET_CONFIG_HANDLE config;
    const char *end, *ptr;
    mc_REQDATAEX *rd = request->u_rdata.exdata;

    MK_ERROR(root, rc, response, immerr);

    if (rc != LCB_SUCCESS) {
        if (! (request->flags & MCREQ_F_INVOKED)) {
            rd->callback(pipeline, request, rc, NULL);
        }
        return;
    }

    /** The CAS field is split into TTP/TTR values */
    ptr = (char *)&response->res.response.cas;
    memcpy(&ttp, ptr, sizeof(ttp));
    memcpy(&ttr, ptr + sizeof(ttp), sizeof(ttp));

    ttp = ntohl(ttp);
    ttr = ntohl(ttr);

    /** Actual payload sequence of (vb, nkey, key). Repeats multiple times */
    ptr = response->payload;
    end = (char *)ptr + PACKET_NBODY(response);
    config = pipeline->parent->config;

    for (pos = 0; ptr < end; pos++) {
        lcb_cas_t cas;
        lcb_uint8_t obs;
        lcb_uint16_t nkey, vb;
        const char *key;
        lcb_observe_resp_t resp;

        memset(&resp, 0, sizeof(resp));

        memcpy(&vb, ptr, sizeof(vb));
        vb = ntohs(vb);
        ptr += sizeof(vb);
        memcpy(&nkey, ptr, sizeof(nkey));
        nkey = ntohs(nkey);
        ptr += sizeof(nkey);
        key = (const char *)ptr;
        ptr += nkey;
        obs = *((lcb_uint8_t *)ptr);
        ptr += sizeof(obs);
        memcpy(&cas, ptr, sizeof(cas));
        ptr += sizeof(cas);

        resp.v.v0.key = key;
        resp.v.v0.nkey = nkey;
        resp.v.v0.cas = cas;
        resp.v.v0.status = obs;
        resp.v.v0.ttp = 0;
        resp.v.v0.ttr = 0;
        resp.v.v0.from_master = pipeline->index == vbucket_get_master(config, vb);
        if (! (request->flags & MCREQ_F_INVOKED)) {
            rd->callback(pipeline, request, rc, &resp);
        }
    }
}
Exemplo n.º 21
0
static void
H_observe(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response,
          lcb_error_t immerr)
{
    lcb_t root = pipeline->parent->cqdata;
    uint32_t ttp;
    uint32_t ttr;
    lcb_size_t pos;
    lcbvb_CONFIG* config;
    const char *end, *ptr;
    mc_REQDATAEX *rd = request->u_rdata.exdata;
    lcb_RESPOBSERVE resp = { 0 };
    MK_ERROR(root, &resp, response, immerr);

    if (resp.rc != LCB_SUCCESS) {
        if (! (request->flags & MCREQ_F_INVOKED)) {
            rd->procs->handler(pipeline, request, resp.rc, NULL);
        }
        return;
    }

    /** The CAS field is split into TTP/TTR values */
    ptr = (char *)&response->res.response.cas;
    memcpy(&ttp, ptr, sizeof(ttp));
    memcpy(&ttr, ptr + sizeof(ttp), sizeof(ttp));

    ttp = ntohl(ttp);
    ttr = ntohl(ttr);

    /** Actual payload sequence of (vb, nkey, key). Repeats multiple times */
    ptr = response->payload;
    end = (char *)ptr + PACKET_NBODY(response);
    config = pipeline->parent->config;

    for (pos = 0; ptr < end; pos++) {
        lcb_cas_t cas;
        lcb_uint8_t obs;
        lcb_uint16_t nkey, vb;
        const char *key;

        memcpy(&vb, ptr, sizeof(vb));
        vb = ntohs(vb);
        ptr += sizeof(vb);
        memcpy(&nkey, ptr, sizeof(nkey));
        nkey = ntohs(nkey);
        ptr += sizeof(nkey);
        key = (const char *)ptr;
        ptr += nkey;
        obs = *((lcb_uint8_t *)ptr);
        ptr += sizeof(obs);
        memcpy(&cas, ptr, sizeof(cas));
        ptr += sizeof(cas);

        resp.key = key;
        resp.nkey = nkey;
        resp.cas = cas;
        resp.status = obs;
        resp.ismaster = pipeline->index == lcbvb_vbmaster(config, vb);
        resp.ttp = 0;
        resp.ttr = 0;
        TRACE_OBSERVE_PROGRESS(response, &resp);
        if (! (request->flags & MCREQ_F_INVOKED)) {
            rd->procs->handler(pipeline, request, resp.rc, &resp);
        }
    }
    TRACE_OBSERVE_END(response);
}
Exemplo n.º 22
0
/*!	Check result of request
 *	1. check SCSI subsystem problems
 *	2. if request hit device, check SCSI status
 *	3. if request got executed, check sense
 */
err_res
periph_check_error(scsi_periph_device_info *device, scsi_ccb *request)
{
	SHOW_FLOW(4, "%d", request->subsys_status & SCSI_SUBSYS_STATUS_MASK);

	switch (request->subsys_status & SCSI_SUBSYS_STATUS_MASK) {
		// everything is ok
		case SCSI_REQ_CMP:
			return MK_ERROR(err_act_ok, B_OK);

		// no device
		case SCSI_LUN_INVALID:
		case SCSI_TID_INVALID:
		case SCSI_PATH_INVALID:
		case SCSI_DEV_NOT_THERE:
		case SCSI_NO_HBA:
			SHOW_ERROR0(2, "No device");
			return MK_ERROR(err_act_fail, B_DEV_BAD_DRIVE_NUM);

		// device temporary unavailable
		case SCSI_SEL_TIMEOUT:
		case SCSI_BUSY:
		case SCSI_SCSI_BUSY:
		case SCSI_HBA_ERR:
		case SCSI_MSG_REJECT_REC:
		case SCSI_NO_NEXUS:
		case SCSI_FUNC_NOTAVAIL:
		case SCSI_RESRC_UNAVAIL:
			// take a deep breath and hope device becomes ready
			snooze(1000000);
			return MK_ERROR(err_act_retry, B_DEV_TIMEOUT);

		// data transmission went wrong
		case SCSI_DATA_RUN_ERR:
		case SCSI_UNCOR_PARITY:
			SHOW_ERROR0(2, "Data transmission failed");
			// retry immediately
			return MK_ERROR(err_act_retry, B_DEV_READ_ERROR);

		// request broken
		case SCSI_REQ_INVALID:
			SHOW_ERROR0(2, "Invalid request");
			return MK_ERROR(err_act_fail, B_ERROR);

		// request aborted
		case SCSI_REQ_ABORTED:
		case SCSI_SCSI_BUS_RESET:
		case SCSI_REQ_TERMIO:
		case SCSI_UNEXP_BUSFREE:
		case SCSI_BDR_SENT:
		case SCSI_CMD_TIMEOUT:
		case SCSI_IID_INVALID:
		case SCSI_UNACKED_EVENT:
		case SCSI_IDE:
		case SCSI_SEQUENCE_FAIL:
			// take a small breath and retry
			snooze(100000);
			return MK_ERROR(err_act_retry, B_DEV_TIMEOUT);

		// device error
		case SCSI_REQ_CMP_ERR:
			return check_scsi_status(device, request);

		// device error, but we don't know what happened
		case SCSI_AUTOSENSE_FAIL:
			SHOW_ERROR0(2, "Auto-sense failed, don't know what really happened");
			return MK_ERROR(err_act_fail, B_ERROR);

		// should not happen, give up
		case SCSI_BUS_RESET_DENIED:
		case SCSI_PROVIDE_FAIL:
		case SCSI_UA_TERMIO:
		case SCSI_CDB_RECVD:
		case SCSI_LUN_ALLREADY_ENAB:
			// supposed to fall through
		default:
			return MK_ERROR(err_act_fail, B_ERROR);
	}
}
Exemplo n.º 23
0
/*! Decode sense data and generate error code. */
static
err_res check_sense(scsi_periph_device_info *device, scsi_ccb *request)
{
	scsi_sense *sense = (scsi_sense *)request->sense;

	if ((request->subsys_status & SCSI_AUTOSNS_VALID) == 0) {
		SHOW_ERROR0(2, "No auto-sense (but there should be)");

		// shouldn't happen (cam_status should be CAM_AUTOSENSE_FAIL
		// as we asked for autosense)
		return MK_ERROR(err_act_fail, B_ERROR);
	}

	if (SCSI_MAX_SENSE_SIZE - request->sense_resid
			< (int)offsetof(scsi_sense, add_sense_length) + 1) {
		SHOW_ERROR(2, "sense too short (%d bytes)", SCSI_MAX_SENSE_SIZE - request->sense_resid);

		// that's a bit too short
		return MK_ERROR(err_act_fail, B_ERROR);
	}

	switch (sense->error_code) {
		case SCSIS_DEFERRED_ERROR:
			// we are doomed - some previous request turned out to have failed
			// we neither know which one nor can we resubmit it		
			SHOW_ERROR0(2, "encountered DEFERRED ERROR - bye, bye");
			return MK_ERROR(err_act_ok, B_OK);

		case SCSIS_CURR_ERROR:
			// we start with very specific and finish very general error infos
			switch ((sense->asc << 8) | sense->ascq) {
				case SCSIS_ASC_AUDIO_PLAYING:
					SHOW_INFO0(2, "busy because playing audio");

					// we need something like "busy"
					return MK_ERROR(err_act_fail, B_DEV_NOT_READY);

				case SCSIS_ASC_LUN_NEED_INIT:
					SHOW_INFO0(2, "LUN needs init");

					// reported by some devices that are idle and spun down
					// sending START UNIT should awake them
					return MK_ERROR(err_act_start, B_NO_INIT);

				case SCSIS_ASC_LUN_NEED_MANUAL_HELP:
					SHOW_ERROR0(2, "LUN needs manual help");

					return MK_ERROR(err_act_fail, B_DEV_NOT_READY);

				case SCSIS_ASC_LUN_FORMATTING:
					SHOW_INFO0(2, "LUN is formatting");

					// we could wait, but as formatting normally takes quite long,
					// we give up without any further retries
					return MK_ERROR(err_act_fail, B_DEV_NOT_READY);

				case SCSIS_ASC_MEDIUM_CHANGED:
					SHOW_FLOW0(3, "Medium changed");
					periph_media_changed(device, request);
					return MK_ERROR(err_act_fail, B_DEV_MEDIA_CHANGED);

				case SCSIS_ASC_WRITE_ERR_AUTOREALLOC:
					SHOW_ERROR0(2, "Recovered write error - block got reallocated automatically");
					return MK_ERROR(err_act_ok, B_OK);

				case SCSIS_ASC_ID_RECOV:
					SHOW_ERROR0(2, "Recovered ID with ECC");
					return MK_ERROR(err_act_ok, B_OK);

				case SCSIS_ASC_REMOVAL_REQUESTED:
					SHOW_INFO0(2, "Removal requested");
					mutex_lock(&device->mutex);
					device->removal_requested = true;
					mutex_unlock(&device->mutex);

					return MK_ERROR(err_act_retry, B_DEV_MEDIA_CHANGE_REQUESTED);

				case SCSIS_ASC_LUN_BECOMING_READY:
					SHOW_INFO0(2, "Becoming ready");
					// wait a bit - the device needs some time
					snooze(100000);
					return MK_ERROR(err_act_many_retries, B_DEV_NOT_READY);

				case SCSIS_ASC_WAS_RESET:
					SHOW_INFO0(2, "Unit was reset");
					// TBD: need a better error code here
					// as some earlier command led to the reset, we are innocent
					return MK_ERROR(err_act_retry, B_DEV_NOT_READY);
			}

			switch (sense->asc) {
				case SCSIS_ASC_DATA_RECOV_NO_ERR_CORR >> 8:
				case SCSIS_ASC_DATA_RECOV_WITH_CORR >> 8:
					// these are the groups of recovered data with or without correction
					// we should print at least a warning here
					SHOW_ERROR(0, "Recovered data, asc=0x%2x, ascq=0x%2x", 
						sense->asc, sense->ascq);
					return MK_ERROR(err_act_ok, B_OK);

				case SCSIS_ASC_WRITE_PROTECTED >> 8:
					SHOW_ERROR0( 2, "Write protected" );

					// isn't there any proper "write protected" error code?
					return MK_ERROR(err_act_fail, B_READ_ONLY_DEVICE);
					
				case SCSIS_ASC_NO_MEDIUM >> 8:
					SHOW_FLOW0(2, "No medium");
					return MK_ERROR(err_act_fail, B_DEV_NO_MEDIA);
			}

			// we issue this info very late, so we don't clutter syslog with
			// messages about changed or missing media
			SHOW_ERROR(3, "0x%04x", (sense->asc << 8) | sense->ascq);

			switch (sense->sense_key) {
				case SCSIS_KEY_NO_SENSE:
					SHOW_ERROR0(2, "No sense");

					// we thought there was an error, huh?
					return MK_ERROR(err_act_ok, B_OK);
	
				case SCSIS_KEY_RECOVERED_ERROR:
					SHOW_ERROR0(2, "Recovered error");

					// we should probably tell about that; perhaps tomorrow
					return MK_ERROR(err_act_ok, B_OK);

				case SCSIS_KEY_NOT_READY:
					return MK_ERROR(err_act_retry, B_DEV_NOT_READY);

				case SCSIS_KEY_MEDIUM_ERROR:
					SHOW_ERROR0(2, "Medium error");
					return MK_ERROR( err_act_retry, B_DEV_RECALIBRATE_ERROR);

				case SCSIS_KEY_HARDWARE_ERROR:
					SHOW_ERROR0(2, "Hardware error");
					return MK_ERROR(err_act_retry, B_DEV_SEEK_ERROR);

				case SCSIS_KEY_ILLEGAL_REQUEST:
					SHOW_ERROR0(2, "Illegal request");
					return MK_ERROR(err_act_invalid_req, B_ERROR);

				case SCSIS_KEY_UNIT_ATTENTION:
					SHOW_ERROR0(2, "Unit attention");
					return MK_ERROR( err_act_retry, B_DEV_NOT_READY);

				case SCSIS_KEY_DATA_PROTECT:
					SHOW_ERROR0(2, "Data protect");

					// we could set "permission denied", but that's probably
					// irritating to the user
					return MK_ERROR(err_act_fail, B_NOT_ALLOWED);

				case SCSIS_KEY_BLANK_CHECK:
					SHOW_ERROR0(2, "Is blank");

					return MK_ERROR(err_act_fail, B_DEV_UNREADABLE);

				case SCSIS_KEY_VENDOR_SPECIFIC:
					return MK_ERROR(err_act_fail, B_ERROR);

				case SCSIS_KEY_COPY_ABORTED:
					// we don't use copy, so this is really wrong
					return MK_ERROR(err_act_fail, B_ERROR);

				case SCSIS_KEY_ABORTED_COMMAND:
					// proper error code?
					return MK_ERROR(err_act_retry, B_ERROR);

				case SCSIS_KEY_EQUAL:
				case SCSIS_KEY_MISCOMPARE:
					// we don't search, so this is really wrong
					return MK_ERROR(err_act_fail, B_ERROR);

				case SCSIS_KEY_VOLUME_OVERFLOW:
					// not the best return code, but this error doesn't apply
					// to devices we currently support
					return MK_ERROR(err_act_fail, B_DEV_SEEK_ERROR);

				case SCSIS_KEY_RESERVED:
				default:
					return MK_ERROR(err_act_fail, B_ERROR);
			}

		default:
			// shouldn't happen - there are only 2 error codes defined
			SHOW_ERROR(2, "Invalid sense type (0x%x)", sense->error_code);
			return MK_ERROR(err_act_fail, B_ERROR);
	}
}