diff mbox

[v3] spapr-vscsi: add task management

Message ID 1374654558-17815-1-git-send-email-aik@ozlabs.ru
State New
Headers show

Commit Message

Alexey Kardashevskiy July 24, 2013, 8:29 a.m. UTC
At the moment the guest kernel issues two types of task management
requests to the hypervisor - task about and lun reset. This adds
handling for these tasks. As spapr-vscsi starts calling scsi_req_cancel(),
free_request callback was implemented.

As virtio-vscsi, spapr-vscsi does not handle CLEAR_ACA either as CDB
control byte does not seem to be used at all so NACA bit is not
set to the guest so the guest has no good reason to call CLEAR_ACA task.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
Changes:
2013/07/23:
* remove unnecessary free_request callback

2013/07/22:
* fixed LUN_RESET (it used to clear requests while it should reset a device)
* added handling of ABORT_TASK_SET/CLEAR_TASK_SET

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 hw/scsi/spapr_vscsi.c | 65 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 48 insertions(+), 17 deletions(-)

Comments

Paolo Bonzini July 24, 2013, 11:44 a.m. UTC | #1
Il 24/07/2013 10:29, Alexey Kardashevskiy ha scritto:
> At the moment the guest kernel issues two types of task management
> requests to the hypervisor - task about and lun reset. This adds
> handling for these tasks. As spapr-vscsi starts calling scsi_req_cancel(),
> free_request callback was implemented.
> 
> As virtio-vscsi, spapr-vscsi does not handle CLEAR_ACA either as CDB
> control byte does not seem to be used at all so NACA bit is not
> set to the guest so the guest has no good reason to call CLEAR_ACA task.
> 
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> Changes:
> 2013/07/23:
> * remove unnecessary free_request callback
> 
> 2013/07/22:
> * fixed LUN_RESET (it used to clear requests while it should reset a device)
> * added handling of ABORT_TASK_SET/CLEAR_TASK_SET
> 
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
>  hw/scsi/spapr_vscsi.c | 65 +++++++++++++++++++++++++++++++++++++--------------
>  1 file changed, 48 insertions(+), 17 deletions(-)
> 
> diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
> index 46f4455..50993fa 100644
> --- a/hw/scsi/spapr_vscsi.c
> +++ b/hw/scsi/spapr_vscsi.c
> @@ -117,6 +117,20 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
>      return NULL;
>  }
>  
> +static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag)
> +{
> +    vscsi_req *req;
> +    int i;
> +
> +    for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
> +        req = &s->reqs[i];
> +        if (req->iu.srp.cmd.tag == srp_tag) {
> +            return req;
> +        }
> +    }
> +    return NULL;
> +}
> +
>  static void vscsi_put_req(vscsi_req *req)
>  {
>      if (req->sreq != NULL) {
> @@ -753,40 +767,57 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
>  static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
>  {
>      union viosrp_iu *iu = &req->iu;
> -    int fn;
> +    vscsi_req *tmpreq;
> +    SCSIDevice *sdev;
> +    int i, lun = 0, error = 0;
>  
>      fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
>              iu->srp.tsk_mgmt.tsk_mgmt_func);
>  
>      switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
> -#if 0 /* We really don't deal with these for now */
>      case SRP_TSK_ABORT_TASK:

Here it should also be an error if the LUN does not exist ("request
fields invalid", see below).

> -        fn = ABORT_TASK;
> +        tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag);
> +        if (tmpreq && tmpreq->sreq) {
> +            assert(tmpreq->sreq->hba_private);
> +            scsi_req_cancel(tmpreq->sreq);
> +        }
>          break;
> +
> +    case SRP_TSK_LUN_RESET:
> +        sdev = vscsi_device_find(&s->bus, req->iu.srp.tsk_mgmt.lun, &lun);
> +        if (sdev) {
> +            qdev_reset_all(&sdev->qdev);
> +        }

Same here.

> +        break;
> +
>      case SRP_TSK_ABORT_TASK_SET:
> -        fn = ABORT_TASK_SET;
> -        break;
>      case SRP_TSK_CLEAR_TASK_SET:
> -        fn = CLEAR_TASK_SET;
> -        break;
> -    case SRP_TSK_LUN_RESET:
> -        fn = LOGICAL_UNIT_RESET;

And here.

> +        for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
> +            tmpreq = &s->reqs[i];
> +            if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) {
> +                continue;
> +            }
> +            if (!tmpreq->active || !tmpreq->sreq) {
> +                continue;
> +            }
> +            assert(tmpreq->sreq->hba_private);
> +            scsi_req_cancel(tmpreq->sreq);
> +        }
>          break;
> +
>      case SRP_TSK_CLEAR_ACA:
> -        fn = CLEAR_ACA;
> -        break;
> -#endif
>      default:
> -        fn = 0;
> +        error = 1;
>      }
> -    if (fn) {
> -        /* XXX Send/Handle target task management */
> -        ;
> +
> +    if (!error) {
> +        vscsi_send_rsp(s, req, GOOD, 0, 0);
>      } else {
>          vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
>          vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);

I now checked the SRP standard and indeed this is not the format you
should send for task management functions.  You should not send sense
data, you should send "response data" instead (filling in resp_data_len).

The format is:

byte 0: reserved
byte 1: reserved
byte 2: reserved
byte 3: 0 = TASK MANAGEMENT FUNCTION COMPLETE
        2 = REQUEST FIELDS INVALID
        4 = TASK MANAGEMENT FUNCTION NOT SUPPORTED
        5 = TASK MANAGEMENT FUNCTION FAILED

If byte 3 is 0 you do not need to send it, thus what you're
doing in the "if (!error) case" is correct (because GOOD == 0).

Source:
http://www.csit-sun.pub.ro/~cpop/Documentatie_SM/Standarde_magistrale/SCSI/srp-r16a.pdf

Paolo

>      }
> -    return !fn;
> +
> +    return 1;
>  }
>  
>  static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
>
diff mbox

Patch

diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index 46f4455..50993fa 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -117,6 +117,20 @@  static struct vscsi_req *vscsi_get_req(VSCSIState *s)
     return NULL;
 }
 
+static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag)
+{
+    vscsi_req *req;
+    int i;
+
+    for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+        req = &s->reqs[i];
+        if (req->iu.srp.cmd.tag == srp_tag) {
+            return req;
+        }
+    }
+    return NULL;
+}
+
 static void vscsi_put_req(vscsi_req *req)
 {
     if (req->sreq != NULL) {
@@ -753,40 +767,57 @@  static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
 static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
 {
     union viosrp_iu *iu = &req->iu;
-    int fn;
+    vscsi_req *tmpreq;
+    SCSIDevice *sdev;
+    int i, lun = 0, error = 0;
 
     fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
             iu->srp.tsk_mgmt.tsk_mgmt_func);
 
     switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
-#if 0 /* We really don't deal with these for now */
     case SRP_TSK_ABORT_TASK:
-        fn = ABORT_TASK;
+        tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag);
+        if (tmpreq && tmpreq->sreq) {
+            assert(tmpreq->sreq->hba_private);
+            scsi_req_cancel(tmpreq->sreq);
+        }
         break;
+
+    case SRP_TSK_LUN_RESET:
+        sdev = vscsi_device_find(&s->bus, req->iu.srp.tsk_mgmt.lun, &lun);
+        if (sdev) {
+            qdev_reset_all(&sdev->qdev);
+        }
+        break;
+
     case SRP_TSK_ABORT_TASK_SET:
-        fn = ABORT_TASK_SET;
-        break;
     case SRP_TSK_CLEAR_TASK_SET:
-        fn = CLEAR_TASK_SET;
-        break;
-    case SRP_TSK_LUN_RESET:
-        fn = LOGICAL_UNIT_RESET;
+        for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+            tmpreq = &s->reqs[i];
+            if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) {
+                continue;
+            }
+            if (!tmpreq->active || !tmpreq->sreq) {
+                continue;
+            }
+            assert(tmpreq->sreq->hba_private);
+            scsi_req_cancel(tmpreq->sreq);
+        }
         break;
+
     case SRP_TSK_CLEAR_ACA:
-        fn = CLEAR_ACA;
-        break;
-#endif
     default:
-        fn = 0;
+        error = 1;
     }
-    if (fn) {
-        /* XXX Send/Handle target task management */
-        ;
+
+    if (!error) {
+        vscsi_send_rsp(s, req, GOOD, 0, 0);
     } else {
         vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
         vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
     }
-    return !fn;
+
+    return 1;
 }
 
 static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)