Patchwork [RFC,v2,07/13] scsi: add walking of hierarchical LUNs

login
register
mail settings
Submitter Paolo Bonzini
Date June 6, 2011, 4:04 p.m.
Message ID <1307376262-1255-8-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/98959/
State New
Headers show

Comments

Paolo Bonzini - June 6, 2011, 4:04 p.m.
This adds support for parsing a hierarchical LUN and mapping the
result to the qdev hierarchy.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/scsi-bus.c    |   83 +++++++++++++++++++++++++++++++++++++++++++----------
 hw/scsi.h        |    9 +++++-
 hw/spapr_vscsi.c |    6 ++-
 3 files changed, 79 insertions(+), 19 deletions(-)

Patch

diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index b64ed68..54f308d 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -841,33 +841,84 @@  retry:
     abort();
 }
 
-/* Extract bus and target from the given LUN and use it to identify a
-   SCSIDevice from a SCSIBus.  Right now, only 1 target per bus is
-   supported.  In the future a SCSIDevice could host its own SCSIBus,
-   in an alternation of devices that select a bus (target ports) and
-   devices that select a target (initiator ports).  */
-SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun)
+/* Reusable implementation of the decode_lun entry in SCSIBusOps.  */
+SCSIDevice *scsi_decode_bus_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+                                     uint64_t *next_lun)
 {
-    int bus, target, decoded_lun;
-    uint64_t next_lun;
+    int bus, target;
+    uint64_t my_next_lun;
+    SCSIDevice *sdev;
 
-    if (!scsi_decode_level(sam_lun, &bus, &target, &next_lun)) {
+    if (!scsi_decode_level(sam_lun, &bus, &target, &my_next_lun)) {
          /* Unsupported LUN format.  */
          return NULL;
     }
-    if (bus >= sbus->ndev || (bus == 0 && target > 0)) {
+    if (bus >= sbus->ndev) {
         /* Out of range.  */
         return NULL;
     }
-    if (target != 0) {
-        /* Only one target for now.  */
+
+    sdev = sbus->devs[bus];
+    if (!sdev) {
+	return NULL;
+    } else if (!sdev->children || !sdev->children->ops->decode_lun) {
+        *next_lun = my_next_lun;
+        return target ? NULL : sdev;
+    } else {
+        /* Next we'll find the target, so pass down the same LUN we got.  */
+        return sdev->children->ops->decode_lun(sdev->children, sam_lun,
+					       next_lun);
+    }
+}
+
+SCSIDevice *scsi_decode_target_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+                                        uint64_t *next_lun)
+{
+    int bus, target;
+    SCSIDevice *sdev;
+
+    if (!scsi_decode_level(sam_lun, &bus, &target, next_lun)) {
+         /* Unsupported LUN format.  */
+         return NULL;
+    }
+    if (target >= sbus->ndev) {
+        /* Out of range.  */
         return NULL;
     }
 
+    sdev = sbus->devs[target];
+    if (!sdev || !sdev->children || !sdev->children->ops->decode_lun ||
+	(*next_lun >> 56) == ADDR_WELL_KNOWN_LUN) {
+        return sdev;
+    } else {
+        return sdev->children->ops->decode_lun(sdev->children, *next_lun,
+					       next_lun);
+    }
+}
+
+/* Extract bus and target from the given LUN and use it to identify a
+   SCSIDevice from a SCSIBus.  Right now, only 1 target per bus is
+   supported.  In the future a SCSIDevice could host its own SCSIBus,
+   in an alternation of devices that select a bus (target ports) and
+   devices that select a target (initiator ports).  */
+SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun,
+                            uint8_t *cdb, int *lun)
+{
+    int decoded_lun;
+    uint64_t next_lun;
+    SCSIDevice *sdev;
+
+    sdev = sbus->ops->decode_lun(sbus, sam_lun, &next_lun);
+    if (!sdev) {
+        return NULL;
+    }
     decoded_lun = scsi_get_lun(next_lun);
-    if (decoded_lun != LUN_INVALID) {
-        *lun = decoded_lun;
-        return sbus->devs[bus];
+    if (decoded_lun == LUN_INVALID) {
+        return NULL;
+    }
+    if ((decoded_lun & ~LUN_WLUN_MASK) == LUN_WLUN_BASE) {
+        return sdev;
     }
-    return NULL;
+    *lun = decoded_lun;
+    return scsi_find_lun(sdev, decoded_lun, cdb);
 }
diff --git a/hw/scsi.h b/hw/scsi.h
index 67d18cc..9f70771 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -92,6 +92,8 @@  struct SCSIBusOps {
     void (*complete)(SCSIRequest *req, uint32_t arg);
     void (*cancel)(SCSIRequest *req);
     int (*get_child_lun)(SCSIDevice *dev);
+    SCSIDevice *(*decode_lun)(SCSIBus *sbus, uint64_t sam_lun,
+                              uint64_t *next_lun);
 };
 
 struct SCSIBus {
@@ -156,7 +158,12 @@  extern const struct SCSISense sense_code_LUN_FAILURE;
 int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
 int scsi_sense_valid(SCSISense sense);
 
-SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, int *lun);
+SCSIDevice *scsi_decode_lun(SCSIBus *sbus, uint64_t sam_lun, uint8_t *cdb,
+                            int *lun);
+SCSIDevice *scsi_decode_bus_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+                                     uint64_t *next_lun);
+SCSIDevice *scsi_decode_target_from_lun(SCSIBus *sbus, uint64_t sam_lun,
+                                        uint64_t *next_lun);
 SCSIDevice *scsi_find_lun(SCSIDevice *sdev, int lun, uint8_t *cdb);
 
 SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, DeviceState *initiator,
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 5b5fa87..ace9dac 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -637,7 +637,8 @@  static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
     SCSIDevice *sdev;
     int n, lun;
 
-    sdev = scsi_decode_lun(&s->bus, be64_to_cpu(srp->cmd.lun), &lun);
+    sdev = scsi_decode_lun(&s->bus, be64_to_cpu(srp->cmd.lun),
+			   srp->cmd.cdb, &lun);
     if (!sdev) {
         if (srp->cmd.cdb[0] == INQUIRY) {
             vscsi_inquiry_no_target(s, req);
@@ -915,7 +916,8 @@  static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
 static const struct SCSIBusOps vscsi_scsi_ops = {
     .transfer_data = vscsi_transfer_data,
     .complete = vscsi_command_complete,
-    .cancel = vscsi_request_cancelled
+    .cancel = vscsi_request_cancelled,
+    .decode_lun = scsi_decode_bus_from_lun
 };
 
 static int spapr_vscsi_init(VIOsPAPRDevice *dev)