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;



    if (s->dataplane_started) {

        assert(blk_get_aio_context(d->conf.blk) == s->ctx);

    }

    /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE".  */

    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 that the request has not been completed yet, we

             * check for it in the loop above.

             */

            assert(r->hba_private);

            if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {

                /* "If the specified command is present in the task set, then

                 * return a service response set to FUNCTION SUCCEEDED".

                 */

                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;

        }



        /* 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.

         */

        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) {

                    /* "If there is any command present in the task set, then

                     * return a service response set to FUNCTION SUCCEEDED".

                     */

                    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 = DO_UPCAST(SCSIDevice, qdev, 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;



    if (s->dataplane_started) {

        assert(blk_get_aio_context(d->conf.blk) == s->ctx);

    }

    

    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 = DO_UPCAST(SCSIDevice, qdev, 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;

}

    if (s->dataplane_started) {
static int FUNC_0(VirtIOSCSI *VAR_0, VirtIOSCSIReq *VAR_1)

{

    SCSIDevice *d = virtio_scsi_device_find(VAR_0, VAR_1->VAR_1.tmf.lun);

    SCSIRequest *r, *next;

    BusChild *kid;

    int VAR_2;

    int VAR_3 = 0;



    if (VAR_0->dataplane_started) {

        assert(blk_get_aio_context(d->conf.blk) == VAR_0->ctx);

    }

    

    VAR_1->resp.tmf.response = VIRTIO_SCSI_S_OK;



    virtio_tswap32s(VIRTIO_DEVICE(VAR_0), &VAR_1->VAR_1.tmf.subtype);

    switch (VAR_1->VAR_1.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(VAR_1->VAR_1.tmf.lun)) {

            goto incorrect_lun;

        }

        QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {

            VirtIOSCSIReq *cmd_req = r->hba_private;

            if (cmd_req && cmd_req->VAR_1.cmd.tag == VAR_1->VAR_1.tmf.tag) {

                break;

            }

        }

        if (r) {

            







            assert(r->hba_private);

            if (VAR_1->VAR_1.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) {

                





                VAR_1->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;

            } else {

                VirtIOSCSICancelNotifier *notifier;



                VAR_1->remaining = 1;

                notifier = g_new(VirtIOSCSICancelNotifier, 1);

                notifier->tmf_req = VAR_1;

                notifier->notifier.notify = virtio_scsi_cancel_notify;

                scsi_req_cancel_async(r, &notifier->notifier);

                VAR_3 = -EINPROGRESS;

            }

        }

        break;



    case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:

        if (!d) {

            goto fail;

        }

        if (d->lun != virtio_scsi_get_lun(VAR_1->VAR_1.tmf.lun)) {

            goto incorrect_lun;

        }

        VAR_0->resetting++;

        qdev_reset_all(&d->qdev);

        VAR_0->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(VAR_1->VAR_1.tmf.lun)) {

            goto incorrect_lun;

        }



        









        VAR_1->remaining = 1;

        QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {

            if (r->hba_private) {

                if (VAR_1->VAR_1.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {

                    





                    VAR_1->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;

                    break;

                } else {

                    VirtIOSCSICancelNotifier *notifier;



                    VAR_1->remaining++;

                    notifier = g_new(VirtIOSCSICancelNotifier, 1);

                    notifier->notifier.notify = virtio_scsi_cancel_notify;

                    notifier->tmf_req = VAR_1;

                    scsi_req_cancel_async(r, &notifier->notifier);

                }

            }

        }

        if (--VAR_1->remaining > 0) {

            VAR_3 = -EINPROGRESS;

        }

        break;



    case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:

        VAR_2 = VAR_1->VAR_1.tmf.lun[1];

        VAR_0->resetting++;

        QTAILQ_FOREACH(kid, &VAR_0->bus.qbus.children, sibling) {

             d = DO_UPCAST(SCSIDevice, qdev, kid->child);

             if (d->channel == 0 && d->id == VAR_2) {

                qdev_reset_all(&d->qdev);

             }

        }

        VAR_0->resetting--;

        break;



    case VIRTIO_SCSI_T_TMF_CLEAR_ACA:

    default:

        VAR_1->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED;

        break;

    }



    return VAR_3;



incorrect_lun:

    VAR_1->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN;

    return VAR_3;



fail:

    VAR_1->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;

    return VAR_3;

}

static int FUNC_0(VirtIOSCSI *VAR_0, VirtIOSCSIReq *VAR_1)
{ SCSIDevice *d = virtio_scsi_device_find(VAR_0, VAR_1->VAR_1.tmf.lun); SCSIRequest *r, *next; BusChild *kid; int VAR_2; int VAR_3 = 0; if (VAR_0->dataplane_started) { assert(blk_get_aio_context(d->conf.blk) == VAR_0->ctx); } VAR_1->resp.tmf.response = VIRTIO_SCSI_S_OK; virtio_tswap32s(VIRTIO_DEVICE(VAR_0), &VAR_1->VAR_1.tmf.subtype); switch (VAR_1->VAR_1.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(VAR_1->VAR_1.tmf.lun)) { goto incorrect_lun; } QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { VirtIOSCSIReq *cmd_req = r->hba_private; if (cmd_req && cmd_req->VAR_1.cmd.tag == VAR_1->VAR_1.tmf.tag) { break; } } if (r) { assert(r->hba_private); if (VAR_1->VAR_1.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { VAR_1->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; } else { VirtIOSCSICancelNotifier *notifier; VAR_1->remaining = 1; notifier = g_new(VirtIOSCSICancelNotifier, 1); notifier->tmf_req = VAR_1; notifier->notifier.notify = virtio_scsi_cancel_notify; scsi_req_cancel_async(r, &notifier->notifier); VAR_3 = -EINPROGRESS; } } break; case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
if (!d) { goto fail; } if (d->lun != virtio_scsi_get_lun(VAR_1->VAR_1.tmf.lun)) { goto incorrect_lun; } VAR_0->resetting++; qdev_reset_all(&d->qdev); VAR_0->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(VAR_1->VAR_1.tmf.lun)) { goto incorrect_lun; } VAR_1->remaining = 1; QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { if (r->hba_private) { if (VAR_1->VAR_1.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { VAR_1->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; break; } else { VirtIOSCSICancelNotifier *notifier; VAR_1->remaining++; notifier = g_new(VirtIOSCSICancelNotifier, 1); notifier->notifier.notify = virtio_scsi_cancel_notify; notifier->tmf_req = VAR_1; scsi_req_cancel_async(r, &notifier->notifier); } } } if (--VAR_1->remaining > 0) { VAR_3 = -EINPROGRESS; } break; case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
VAR_2 = VAR_1->VAR_1.tmf.lun[1]; VAR_0->resetting++; QTAILQ_FOREACH(kid, &VAR_0->bus.qbus.children, sibling) { d = DO_UPCAST(SCSIDevice, qdev, kid->child); if (d->channel == 0 && d->id == VAR_2) { qdev_reset_all(&d->qdev); } } VAR_0->resetting--; break; case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
default:
VAR_1->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; break; } return VAR_3; incorrect_lun:
VAR_1->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; return VAR_3; fail:
VAR_1->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; return VAR_3; }