ibft: Expose iBFT acpi header via sysfs
authorDavid Bond <dbond@suse.com>
Thu, 24 Mar 2016 01:49:26 +0000 (21:49 -0400)
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Mon, 16 May 2016 15:14:29 +0000 (11:14 -0400)
Some ethernet adapter vendors are supplying products which support optional
(payed license) features. On some adapters this includes a hardware iscsi
initiator.  The same adapters in a normal (no extra licenses) mode of
operation can be used as a software iscsi initiator.  In addition, software
iscsi boot initiators are becoming a standard part of many vendors uefi
implementations.  This is creating difficulties during early boot/install
determining the proper configuration method for these adapters when they
are used as a boot device.

The attached patch creates sysfs entries to expose information from the
acpi header of the ibft table.  This information allows for a method to
easily determining if an ibft table was created by a ethernet card's
firmware or the system uefi/bios.  In the case of a hardware initiator this
information in combination with the pci vendor and device id can be used
to ascertain any vendor specific behaviors that need to be accommodated.

Reviewed-by: Lee Duncan <lduncan@suse.com>
Signed-off-by: David Bond <dbond@suse.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Documentation/ABI/testing/sysfs-ibft
drivers/firmware/iscsi_ibft.c
drivers/scsi/iscsi_boot_sysfs.c
include/linux/iscsi_boot_sysfs.h

index cac3930..7d6725f 100644 (file)
@@ -21,3 +21,13 @@ Contact:     Konrad Rzeszutek <ketuzsezr@darnok.org>
 Description:   The /sys/firmware/ibft/ethernetX directory will contain
                files that expose the iSCSI Boot Firmware Table NIC data.
                Usually this contains the IP address, MAC, and gateway of the NIC.
+
+What:          /sys/firmware/ibft/acpi_header
+Date:          March 2016
+Contact:       David Bond <dbond@suse.com>
+Description:   The /sys/firmware/ibft/acpi_header directory will contain files
+               that expose the SIGNATURE, OEM_ID, and OEM_TABLE_ID fields of the
+               acpi table header of the iBFT structure.  This will allow for
+               identification of the creator of the table which is useful in
+               determining quirks associated with some adapters when used in
+               hardware vs software iscsi initiator mode.
index 81037e5..14042a6 100644 (file)
@@ -418,6 +418,31 @@ static ssize_t ibft_attr_show_target(void *data, int type, char *buf)
        return str - buf;
 }
 
+static ssize_t ibft_attr_show_acpitbl(void *data, int type, char *buf)
+{
+       struct ibft_kobject *entry = data;
+       char *str = buf;
+
+       switch (type) {
+       case ISCSI_BOOT_ACPITBL_SIGNATURE:
+               str += sprintf_string(str, ACPI_NAME_SIZE,
+                                     entry->header->header.signature);
+               break;
+       case ISCSI_BOOT_ACPITBL_OEM_ID:
+               str += sprintf_string(str, ACPI_OEM_ID_SIZE,
+                                     entry->header->header.oem_id);
+               break;
+       case ISCSI_BOOT_ACPITBL_OEM_TABLE_ID:
+               str += sprintf_string(str, ACPI_OEM_TABLE_ID_SIZE,
+                                     entry->header->header.oem_table_id);
+               break;
+       default:
+               break;
+       }
+
+       return str - buf;
+}
+
 static int __init ibft_check_device(void)
 {
        int len;
@@ -576,6 +601,24 @@ static umode_t __init ibft_check_initiator_for(void *data, int type)
        return rc;
 }
 
+static umode_t __init ibft_check_acpitbl_for(void *data, int type)
+{
+
+       umode_t rc = 0;
+
+       switch (type) {
+       case ISCSI_BOOT_ACPITBL_SIGNATURE:
+       case ISCSI_BOOT_ACPITBL_OEM_ID:
+       case ISCSI_BOOT_ACPITBL_OEM_TABLE_ID:
+               rc = S_IRUGO;
+               break;
+       default:
+               break;
+       }
+
+       return rc;
+}
+
 static void ibft_kobj_release(void *data)
 {
        kfree(data);
@@ -699,6 +742,8 @@ free_ibft_obj:
 static int __init ibft_register_kobjects(struct acpi_table_ibft *header)
 {
        struct ibft_control *control = NULL;
+       struct iscsi_boot_kobj *boot_kobj;
+       struct ibft_kobject *ibft_kobj;
        void *ptr, *end;
        int rc = 0;
        u16 offset;
@@ -726,6 +771,25 @@ static int __init ibft_register_kobjects(struct acpi_table_ibft *header)
                                break;
                }
        }
+       if (rc)
+               return rc;
+
+       ibft_kobj = kzalloc(sizeof(*ibft_kobj), GFP_KERNEL);
+       if (!ibft_kobj)
+               return -ENOMEM;
+
+       ibft_kobj->header = header;
+       ibft_kobj->hdr = NULL; /*for ibft_unregister*/
+
+       boot_kobj = iscsi_boot_create_acpitbl(boot_kset, 0,
+                                       ibft_kobj,
+                                       ibft_attr_show_acpitbl,
+                                       ibft_check_acpitbl_for,
+                                       ibft_kobj_release);
+       if (!boot_kobj)  {
+               kfree(ibft_kobj);
+               rc = -ENOMEM;
+       }
 
        return rc;
 }
@@ -738,7 +802,7 @@ static void ibft_unregister(void)
        list_for_each_entry_safe(boot_kobj, tmp_kobj,
                                 &boot_kset->kobj_list, list) {
                ibft_kobj = boot_kobj->data;
-               if (ibft_kobj->hdr->id == id_nic)
+               if (ibft_kobj->hdr && ibft_kobj->hdr->id == id_nic)
                        sysfs_remove_link(&boot_kobj->kobj, "device");
        };
 }
index 8f0ea97..d453667 100644 (file)
@@ -306,6 +306,42 @@ static struct attribute_group iscsi_boot_initiator_attr_group = {
        .is_visible = iscsi_boot_ini_attr_is_visible,
 };
 
+/* iBFT ACPI Table attributes */
+iscsi_boot_rd_attr(acpitbl_signature, signature, ISCSI_BOOT_ACPITBL_SIGNATURE);
+iscsi_boot_rd_attr(acpitbl_oem_id, oem_id, ISCSI_BOOT_ACPITBL_OEM_ID);
+iscsi_boot_rd_attr(acpitbl_oem_table_id, oem_table_id,
+                  ISCSI_BOOT_ACPITBL_OEM_TABLE_ID);
+
+static struct attribute *acpitbl_attrs[] = {
+       &iscsi_boot_attr_acpitbl_signature.attr,
+       &iscsi_boot_attr_acpitbl_oem_id.attr,
+       &iscsi_boot_attr_acpitbl_oem_table_id.attr,
+       NULL
+};
+
+static umode_t iscsi_boot_acpitbl_attr_is_visible(struct kobject *kobj,
+                                            struct attribute *attr, int i)
+{
+       struct iscsi_boot_kobj *boot_kobj =
+                       container_of(kobj, struct iscsi_boot_kobj, kobj);
+
+       if (attr ==  &iscsi_boot_attr_acpitbl_signature.attr)
+               return boot_kobj->is_visible(boot_kobj->data,
+                                            ISCSI_BOOT_ACPITBL_SIGNATURE);
+       if (attr ==  &iscsi_boot_attr_acpitbl_oem_id.attr)
+               return boot_kobj->is_visible(boot_kobj->data,
+                                            ISCSI_BOOT_ACPITBL_OEM_ID);
+       if (attr ==  &iscsi_boot_attr_acpitbl_oem_table_id.attr)
+               return boot_kobj->is_visible(boot_kobj->data,
+                                            ISCSI_BOOT_ACPITBL_OEM_TABLE_ID);
+       return 0;
+}
+
+static struct attribute_group iscsi_boot_acpitbl_attr_group = {
+       .attrs = acpitbl_attrs,
+       .is_visible = iscsi_boot_acpitbl_attr_is_visible,
+};
+
 static struct iscsi_boot_kobj *
 iscsi_boot_create_kobj(struct iscsi_boot_kset *boot_kset,
                       struct attribute_group *attr_group,
@@ -435,6 +471,32 @@ iscsi_boot_create_ethernet(struct iscsi_boot_kset *boot_kset, int index,
 }
 EXPORT_SYMBOL_GPL(iscsi_boot_create_ethernet);
 
+/**
+ * iscsi_boot_create_acpitbl() - create boot acpi table sysfs dir
+ * @boot_kset: boot kset
+ * @index: not used
+ * @data: driver specific data
+ * @show: attr show function
+ * @is_visible: attr visibility function
+ * @release: release function
+ *
+ * Note: The boot sysfs lib will free the data passed in for the caller
+ * when all refs to the acpitbl kobject have been released.
+ */
+struct iscsi_boot_kobj *
+iscsi_boot_create_acpitbl(struct iscsi_boot_kset *boot_kset, int index,
+                          void *data,
+                          ssize_t (*show)(void *data, int type, char *buf),
+                          umode_t (*is_visible)(void *data, int type),
+                          void (*release)(void *data))
+{
+       return iscsi_boot_create_kobj(boot_kset,
+                                     &iscsi_boot_acpitbl_attr_group,
+                                     "acpi_header", index, data, show,
+                                     is_visible, release);
+}
+EXPORT_SYMBOL_GPL(iscsi_boot_create_acpitbl);
+
 /**
  * iscsi_boot_create_kset() - creates root sysfs tree
  * @set_name: name of root dir
index 548d553..10923d7 100644 (file)
@@ -64,6 +64,12 @@ enum iscsi_boot_initiator_properties_enum {
        ISCSI_BOOT_INI_END_MARKER,
 };
 
+enum iscsi_boot_acpitbl_properties_enum {
+       ISCSI_BOOT_ACPITBL_SIGNATURE,
+       ISCSI_BOOT_ACPITBL_OEM_ID,
+       ISCSI_BOOT_ACPITBL_OEM_TABLE_ID,
+};
+
 struct attribute_group;
 
 struct iscsi_boot_kobj {
@@ -127,6 +133,13 @@ iscsi_boot_create_target(struct iscsi_boot_kset *boot_kset, int index,
                         umode_t (*is_visible) (void *data, int type),
                         void (*release) (void *data));
 
+struct iscsi_boot_kobj *
+iscsi_boot_create_acpitbl(struct iscsi_boot_kset *boot_kset, int index,
+                         void *data,
+                         ssize_t (*show)(void *data, int type, char *buf),
+                         umode_t (*is_visible)(void *data, int type),
+                         void (*release)(void *data));
+
 struct iscsi_boot_kset *iscsi_boot_create_kset(const char *set_name);
 struct iscsi_boot_kset *iscsi_boot_create_host_kset(unsigned int hostno);
 void iscsi_boot_destroy_kset(struct iscsi_boot_kset *boot_kset);