qemu/hw/scsi/virtio-scsi.c
/* Return 0 if the request is ready to be completed and return to guest;
 * -EINPROGRESS if the request is submitted and will be completed later, in the
 *  case of async cancellation. */
Return 0 if the request is ready to be completed and return to guest;
-EINPROGRESS if the request is submitted and will be completed later, in the
case of async cancellation.
Return 0 if the request is ready to be completed and return to guest ; - EINPROGRESS if the request is submitted and will be completed later in the case of async cancellation .
static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
    SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun);
    SCSIRequest *r, *next;
    BusChild *kid;
    int target;
    int ret = 0;
    virtio_scsi_ctx_check(s, d);
    req->resp.tmf.response = VIRTIO_SCSI_S_OK;
    virtio_tswap32s(VIRTIO_DEVICE(s), &req->req.tmf.subtype);
    switch (req->req.tmf.subtype) {
    case VIRTIO_SCSI_T_TMF_ABORT_TASK:
    case VIRTIO_SCSI_T_TMF_QUERY_TASK:
        if (!d) {
            goto fail;
        }
        if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) {
            goto incorrect_lun;
        }
        QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
            VirtIOSCSIReq *cmd_req = r->hba_private;
            if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) {
                break;
            }
        }
        if (r) {
            assert(r->hba_private);
            if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {
                req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
            } else {
                VirtIOSCSICancelNotifier *notifier;
                req->remaining = 1;
                notifier = g_new(VirtIOSCSICancelNotifier, 1);
                notifier->tmf_req = req;
                notifier->notifier.notify = virtio_scsi_cancel_notify;
                scsi_req_cancel_async(r, &notifier->notifier);
                ret = -EINPROGRESS;
            }
        }
        break;
    case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
        if (!d) {
            goto fail;
        }
        if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) {
            goto incorrect_lun;
        }
        s->resetting++;
        qdev_reset_all(&d->qdev);
        s->resetting--;
        break;
    case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET:
    case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET:
    case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET:
        if (!d) {
            goto fail;
        }
        if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) {
            goto incorrect_lun;
        }
        req->remaining = 1;
        QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
            if (r->hba_private) {
                if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
                    req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
                    break;
                } else {
                    VirtIOSCSICancelNotifier *notifier;
                    req->remaining++;
                    notifier = g_new(VirtIOSCSICancelNotifier, 1);
                    notifier->notifier.notify = virtio_scsi_cancel_notify;
                    notifier->tmf_req = req;
                    scsi_req_cancel_async(r, &notifier->notifier);
                }
            }
        }
        if (--req->remaining > 0) {
            ret = -EINPROGRESS;
        }
        break;
    case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
        target = req->req.tmf.lun[1];
        s->resetting++;
        QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
             d = SCSI_DEVICE(kid->child);
             if (d->channel == 0 && d->id == target) {
                qdev_reset_all(&d->qdev);
             }
        }
        s->resetting--;
        break;
    case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
    default:
        req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
        break;
    }
    return ret;
incorrect_lun:
    req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN;
    return ret;
fail:
    req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;
    return ret;
}
static int virtio_scsi_do_tmf ( VirtIOSCSI * s , VirtIOSCSIReq * req ) { SCSIDevice * d = virtio_scsi_device_find ( s , req -> req . tmf . lun ) ; SCSIRequest * r , * next ; BusChild * kid ; int target ; int ret = 0 ; virtio_scsi_ctx_check ( s , d ) ; req -> resp . tmf . response = VIRTIO_SCSI_S_OK ; virtio_tswap32s ( VIRTIO_DEVICE ( s ) , & req -> req . tmf . subtype ) ; switch ( req -> req . tmf . subtype ) { case VIRTIO_SCSI_T_TMF_ABORT_TASK : case VIRTIO_SCSI_T_TMF_QUERY_TASK : if ( ! d ) { goto fail ; } if ( d -> lun != virtio_scsi_get_lun ( req -> req . tmf . lun ) ) { goto incorrect_lun ; } QTAILQ_FOREACH_SAFE ( r , & d -> requests , next , next ) { VirtIOSCSIReq * cmd_req = r -> hba_private ; if ( cmd_req && cmd_req -> req . cmd . tag == req -> req . tmf . tag ) { break ; } } if ( r ) { assert ( r -> hba_private ) ; if ( req -> req . tmf . subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK ) { req -> resp . tmf . response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED ; } else { VirtIOSCSICancelNotifier * notifier ; req -> remaining = 1 ; notifier = g_new ( VirtIOSCSICancelNotifier , 1 ) ; notifier -> tmf_req = req ; notifier -> notifier . notify = virtio_scsi_cancel_notify ; scsi_req_cancel_async ( r , & notifier -> notifier ) ; ret = - EINPROGRESS ; } } break ; case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET : if ( ! d ) { goto fail ; } if ( d -> lun != virtio_scsi_get_lun ( req -> req . tmf . lun ) ) { goto incorrect_lun ; } s -> resetting ++ ; qdev_reset_all ( & d -> qdev ) ; s -> resetting -- ; break ; case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET : case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET : case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET : if ( ! d ) { goto fail ; } if ( d -> lun != virtio_scsi_get_lun ( req -> req . tmf . lun ) ) { goto incorrect_lun ; } req -> remaining = 1 ; QTAILQ_FOREACH_SAFE ( r , & d -> requests , next , next ) { if ( r -> hba_private ) { if ( req -> req . tmf . subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET ) { req -> resp . tmf . response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED ; break ; } else { VirtIOSCSICancelNotifier * notifier ; req -> remaining ++ ; notifier = g_new ( VirtIOSCSICancelNotifier , 1 ) ; notifier -> notifier . notify = virtio_scsi_cancel_notify ; notifier -> tmf_req = req ; scsi_req_cancel_async ( r , & notifier -> notifier ) ; } } } if ( -- req -> remaining > 0 ) { ret = - EINPROGRESS ; } break ; case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET : target = req -> req . tmf . lun [ 1 ] ; s -> resetting ++ ; QTAILQ_FOREACH ( kid , & s -> bus . qbus . children , sibling ) { d = SCSI_DEVICE ( kid -> child ) ; if ( d -> channel == 0 && d -> id == target ) { qdev_reset_all ( & d -> qdev ) ; } } s -> resetting -- ; break ; case VIRTIO_SCSI_T_TMF_CLEAR_ACA : default : req -> resp . tmf . response = VIRTIO_SCSI_S_FUNCTION_REJECTED ; break ; } return ret ; incorrect_lun : req -> resp . tmf . response = VIRTIO_SCSI_S_INCORRECT_LUN ; return ret ; fail : req -> resp . tmf . response = VIRTIO_SCSI_S_BAD_TARGET ; return ret ; }
Return 0 if the request is ready to be completed and return to guest;
 -EINPROGRESS if the request is submitted and will be completed later, in the
  case of async cancellation.
Return 0 if the request is ready to be completed and return to guest ; - EINPROGRESS if the request is submitted and will be completed later in the case of async cancellation .
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE".  */ /*
             * Assert that the request has not been completed yet, we
             * check for it in the loop above.
             */ /* "If the specified command is present in the task set, then
                 * return a service response set to FUNCTION SUCCEEDED".
                 */ /* Add 1 to "remaining" until virtio_scsi_do_tmf returns.
         * This way, if the bus starts calling back to the notifiers
         * even before we finish the loop, virtio_scsi_cancel_notify
         * will not complete the TMF too early.
         */ /* "If there is any command present in the task set, then
                     * return a service response set to FUNCTION SUCCEEDED".
                     */