[SCSI] libosd: osd_dev_info: Unique Identification of an OSD device
authorBoaz Harrosh <bharrosh@panasas.com>
Sun, 29 Nov 2009 14:26:45 +0000 (16:26 +0200)
committerJames Bottomley <James.Bottomley@suse.de>
Fri, 4 Dec 2009 18:01:46 +0000 (12:01 -0600)
Define an osd_dev_info structure that Uniquely identifies an OSD
device lun on the network. The identification is built from unique
target attributes and is the same for all network/SAN machines.

osduld_info_lookup() - NEW
    New API that will lookup an osd_dev by its osd_dev_info.
    This is used by pNFS-objects for cross network global device
    identification. And by exofs multy-device support, the device
    info is specified in the on-disk exofs device table.

osduld_device_info() - NEW
    Given an osd_dev handle returns its associated osd_dev_info.
    The ULD fetches this information at startup and hangs it on
    each OSD device. (This is a fast operation that can be called
    at any condition)

osduld_device_same() - NEW
    With a given osd_dev at one hand and an osd_dev_info
    at another, we would like to know if they are the same
    device.
    Two osd_dev handles can be checked by:
        osduld_device_same(od1, osduld_device_info(od2));

osd_auto_detect_ver() - REVISED
    Now returns an osd_dev_info structure. Is only called once
    by ULD as before. See added comments for how to use.

Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/osd/osd_initiator.c
drivers/scsi/osd/osd_uld.c
include/scsi/osd_initiator.h

index 7a117c18114cd757de416045da2d453327742131..60b7ca1e9bc037387ba3a0bc2248ea17400b8de4 100644 (file)
@@ -73,7 +73,8 @@ static const char *_osd_ver_desc(struct osd_request *or)
 
 #define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
 
-static int _osd_print_system_info(struct osd_dev *od, void *caps)
+static int _osd_get_print_system_info(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi)
 {
        struct osd_request *or;
        struct osd_attr get_attrs[] = {
@@ -137,8 +138,12 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
        OSD_INFO("PRODUCT_SERIAL_NUMBER  [%s]\n",
                (char *)pFirst);
 
-       pFirst = get_attrs[a].val_ptr;
-       OSD_INFO("OSD_NAME               [%s]\n", (char *)pFirst);
+       odi->osdname_len = get_attrs[a].len;
+       /* Avoid NULL for memcmp optimization 0-length is good enough */
+       odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
+       if (odi->osdname_len)
+               memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
+       OSD_INFO("OSD_NAME               [%s]\n", odi->osdname);
        a++;
 
        pFirst = get_attrs[a++].val_ptr;
@@ -171,6 +176,14 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
                                   sid_dump, sizeof(sid_dump), true);
                OSD_INFO("OSD_SYSTEM_ID(%d)\n"
                         "        [%s]\n", len, sid_dump);
+
+               if (unlikely(len > sizeof(odi->systemid))) {
+                       OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
+                               "device idetification might not work\n", len);
+                       len = sizeof(odi->systemid);
+               }
+               odi->systemid_len = len;
+               memcpy(odi->systemid, get_attrs[a].val_ptr, len);
                a++;
        }
 out:
@@ -178,16 +191,17 @@ out:
        return ret;
 }
 
-int osd_auto_detect_ver(struct osd_dev *od, void *caps)
+int osd_auto_detect_ver(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi)
 {
        int ret;
 
        /* Auto-detect the osd version */
-       ret = _osd_print_system_info(od, caps);
+       ret = _osd_get_print_system_info(od, caps, odi);
        if (ret) {
                osd_dev_set_ver(od, OSD_VER1);
                OSD_DEBUG("converting to OSD1\n");
-               ret = _osd_print_system_info(od, caps);
+               ret = _osd_get_print_system_info(od, caps, odi);
        }
 
        return ret;
index fc6fc1c4d4d1659629a7876a9df593cad33c8a4b..0a90702b3d710101904b73defb89a9a8d5580c67 100644 (file)
@@ -84,6 +84,7 @@ struct osd_uld_device {
        struct device class_dev;
        struct cdev cdev;
        struct osd_dev od;
+       struct osd_dev_info odi;
        struct gendisk *disk;
 };
 
@@ -225,6 +226,72 @@ free_od:
 }
 EXPORT_SYMBOL(osduld_path_lookup);
 
+static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len,
+                                    const u8 *a2, unsigned a2_len)
+{
+       if (!a2_len) /* User string is Empty means don't care */
+               return true;
+
+       if (a1_len != a2_len)
+               return false;
+
+       return 0 == memcmp(a1, a2, a1_len);
+}
+
+struct find_oud_t {
+       const struct osd_dev_info *odi;
+       struct device *dev;
+       struct osd_uld_device *oud;
+} ;
+
+int _mach_odi(struct device *dev, void *find_data)
+{
+       struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
+                                                 class_dev);
+       struct find_oud_t *fot = find_data;
+       const struct osd_dev_info *odi = fot->odi;
+
+       if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+                             odi->systemid, odi->systemid_len) &&
+           _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+                             odi->osdname, odi->osdname_len)) {
+               OSD_DEBUG("found device sysid_len=%d osdname=%d\n",
+                         odi->systemid_len, odi->osdname_len);
+               fot->oud = oud;
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+/* osduld_info_lookup - Loop through all devices, return the requested osd_dev.
+ *
+ * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't
+ * care. .e.g if they're both zero /dev/osd0 is returned.
+ */
+struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi)
+{
+       struct find_oud_t find = {.odi = odi};
+
+       find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi);
+       if (likely(find.dev)) {
+               struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL);
+
+               if (unlikely(!odh)) {
+                       put_device(find.dev);
+                       return ERR_PTR(-ENOMEM);
+               }
+
+               odh->od = find.oud->od;
+               odh->oud = find.oud;
+
+               return &odh->od;
+       }
+
+       return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL(osduld_info_lookup);
+
 void osduld_put_device(struct osd_dev *od)
 {
        if (od && !IS_ERR(od)) {
@@ -240,14 +307,39 @@ void osduld_put_device(struct osd_dev *od)
                 * is called after the fops->release. A get_/put_ pair makes
                 * sure we have a cdev for the duration of fput
                 */
-               get_device(&oud->class_dev);
-               fput(odh->file);
+               if (odh->file) {
+                       get_device(&oud->class_dev);
+                       fput(odh->file);
+               }
                put_device(&oud->class_dev);
                kfree(odh);
        }
 }
 EXPORT_SYMBOL(osduld_put_device);
 
+const struct osd_dev_info *osduld_device_info(struct osd_dev *od)
+{
+       struct osd_dev_handle *odh =
+                               container_of(od, struct osd_dev_handle, od);
+       return &odh->oud->odi;
+}
+EXPORT_SYMBOL(osduld_device_info);
+
+bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi)
+{
+       struct osd_dev_handle *odh =
+                               container_of(od, struct osd_dev_handle, od);
+       struct osd_uld_device *oud = odh->oud;
+
+       return (oud->odi.systemid_len == odi->systemid_len) &&
+               _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+                                odi->systemid, odi->systemid_len) &&
+               (oud->odi.osdname_len == odi->osdname_len) &&
+               _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+                                 odi->osdname, odi->osdname_len);
+}
+EXPORT_SYMBOL(osduld_device_same);
+
 /*
  * Scsi Device operations
  */
@@ -268,7 +360,7 @@ static int __detect_osd(struct osd_uld_device *oud)
                OSD_ERR("warning: scsi_test_unit_ready failed\n");
 
        osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
-       if (osd_auto_detect_ver(&oud->od, caps))
+       if (osd_auto_detect_ver(&oud->od, caps, &oud->odi))
                return -ENODEV;
 
        return 0;
@@ -280,6 +372,8 @@ static void __remove(struct device *dev)
                                                  class_dev);
        struct scsi_device *scsi_device = oud->od.scsi_device;
 
+       kfree(oud->odi.osdname);
+
        if (oud->cdev.owner)
                cdev_del(&oud->cdev);
 
index 589e5f0d67b111f3843d0e61ad6bed339374cb07..3ec346e15ddad8cb6027b630ff8c056dd21d87ea 100644 (file)
@@ -55,10 +55,24 @@ struct osd_dev {
 #endif
 };
 
-/* Retrieve/return osd_dev(s) for use by Kernel clients */
-struct osd_dev *osduld_path_lookup(const char *dev_name); /*Use IS_ERR/ERR_PTR*/
+/* Unique Identification of an OSD device */
+struct osd_dev_info {
+       unsigned systemid_len;
+       u8 systemid[OSD_SYSTEMID_LEN];
+       unsigned osdname_len;
+       u8 *osdname;
+};
+
+/* Retrieve/return osd_dev(s) for use by Kernel clients
+ * Use IS_ERR/ERR_PTR on returned "osd_dev *".
+ */
+struct osd_dev *osduld_path_lookup(const char *dev_name);
+struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi);
 void osduld_put_device(struct osd_dev *od);
 
+const struct osd_dev_info *osduld_device_info(struct osd_dev *od);
+bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi);
+
 /* Add/remove test ioctls from external modules */
 typedef int (do_test_fn)(struct osd_dev *od, unsigned cmd, unsigned long arg);
 int osduld_register_test(unsigned ioctl, do_test_fn *do_test);
@@ -68,8 +82,24 @@ void osduld_unregister_test(unsigned ioctl);
 void osd_dev_init(struct osd_dev *od, struct scsi_device *scsi_device);
 void osd_dev_fini(struct osd_dev *od);
 
-/* some hi level device operations */
-int osd_auto_detect_ver(struct osd_dev *od, void *caps);    /* GFP_KERNEL */
+/**
+ * osd_auto_detect_ver - Detect the OSD version, return Unique Identification
+ *
+ * @od:     OSD target lun handle
+ * @caps:   Capabilities authorizing OSD root read attributes access
+ * @odi:    Retrieved information uniquely identifying the osd target lun
+ *          Note: odi->osdname must be kfreed by caller.
+ *
+ * Auto detects the OSD version of the OSD target and sets the @od
+ * accordingly. Meanwhile also returns the "system id" and "osd name" root
+ * attributes which uniquely identify the OSD target. This member is usually
+ * called by the ULD. ULD users should call osduld_device_info().
+ * This rutine allocates osd requests and memory at GFP_KERNEL level and might
+ * sleep.
+ */
+int osd_auto_detect_ver(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi);
+
 static inline struct request_queue *osd_request_queue(struct osd_dev *od)
 {
        return od->scsi_device->request_queue;