[SCSI] be2iscsi: Add support for iscsi boot

This patch contains changes for adding support for iscsi_boot.
Have modified to make read of mac address from chip as a function.

  The mac_address is being cached after teh first call as it is
not expected to change

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: Jayamohan Kallickal <jayamohank@serverengines.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 7436c5a..8220bde 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -26,6 +26,7 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <linux/semaphore.h>
+#include <linux/iscsi_boot_sysfs.h>
 
 #include <scsi/libiscsi.h>
 #include <scsi/scsi_transport_iscsi.h>
@@ -211,6 +212,218 @@
 	return rc;
 }
 
+static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf)
+{
+	struct beiscsi_hba *phba = data;
+	char *str = buf;
+	int rc;
+
+	switch (type) {
+	case ISCSI_BOOT_TGT_NAME:
+		rc = sprintf(buf, "%.*s\n",
+				(int)strlen(phba->boot_sess.target_name),
+				(char *)&phba->boot_sess.target_name);
+		break;
+	case ISCSI_BOOT_TGT_IP_ADDR:
+		if (phba->boot_sess.conn_list[0].dest_ipaddr.ip_type == 0x1)
+			rc = sprintf(buf, "%pI4\n",
+				(char *)&phba->boot_sess.conn_list[0].
+				dest_ipaddr.ip_address);
+		else
+			rc = sprintf(str, "%pI6\n",
+				(char *)&phba->boot_sess.conn_list[0].
+				dest_ipaddr.ip_address);
+		break;
+	case ISCSI_BOOT_TGT_PORT:
+		rc = sprintf(str, "%d\n", phba->boot_sess.conn_list[0].
+				  dest_port);
+		break;
+
+	case ISCSI_BOOT_TGT_CHAP_NAME:
+		rc = sprintf(str,  "%.*s\n",
+				      phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      target_chap_name_length,
+				      (char *)&phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      target_chap_name);
+		break;
+	case ISCSI_BOOT_TGT_CHAP_SECRET:
+		rc = sprintf(str,  "%.*s\n",
+				      phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      target_secret_length,
+				      (char *)&phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      target_secret);
+
+		break;
+	case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+		rc = sprintf(str,  "%.*s\n",
+				      phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      intr_chap_name_length,
+				      (char *)&phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      intr_chap_name);
+
+		break;
+	case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+			rc = sprintf(str,  "%.*s\n",
+				      phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      intr_secret_length,
+				      (char *)&phba->boot_sess.conn_list[0].
+				      negotiated_login_options.auth_data.chap.
+				      intr_secret);
+		break;
+	case ISCSI_BOOT_TGT_FLAGS:
+			rc = sprintf(str, "2\n");
+		break;
+	case ISCSI_BOOT_TGT_NIC_ASSOC:
+			rc = sprintf(str, "0\n");
+		break;
+	default:
+		rc = -ENOSYS;
+		break;
+	}
+	return rc;
+}
+
+static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf)
+{
+	struct beiscsi_hba *phba = data;
+	char *str = buf;
+	int rc;
+
+	switch (type) {
+	case ISCSI_BOOT_INI_INITIATOR_NAME:
+		rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname);
+		break;
+	default:
+		rc = -ENOSYS;
+		break;
+	}
+	return rc;
+}
+
+static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf)
+{
+	struct beiscsi_hba *phba = data;
+	char *str = buf;
+	int rc;
+
+	switch (type) {
+	case ISCSI_BOOT_ETH_FLAGS:
+			rc = sprintf(str, "2\n");
+		break;
+	case ISCSI_BOOT_ETH_INDEX:
+			rc = sprintf(str, "0\n");
+		break;
+	case ISCSI_BOOT_ETH_MAC:
+		rc  = beiscsi_get_macaddr(buf, phba);
+		if (rc < 0) {
+			SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n");
+			return rc;
+		}
+	break;
+	default:
+		rc = -ENOSYS;
+		break;
+	}
+	return rc;
+}
+
+
+static mode_t beiscsi_tgt_get_attr_visibility(void *data, int type)
+{
+	int rc;
+
+	switch (type) {
+	case ISCSI_BOOT_TGT_NAME:
+	case ISCSI_BOOT_TGT_IP_ADDR:
+	case ISCSI_BOOT_TGT_PORT:
+	case ISCSI_BOOT_TGT_CHAP_NAME:
+	case ISCSI_BOOT_TGT_CHAP_SECRET:
+	case ISCSI_BOOT_TGT_REV_CHAP_NAME:
+	case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
+	case ISCSI_BOOT_TGT_NIC_ASSOC:
+	case ISCSI_BOOT_TGT_FLAGS:
+		rc = S_IRUGO;
+		break;
+	default:
+		rc = 0;
+		break;
+	}
+	return rc;
+}
+
+static mode_t beiscsi_ini_get_attr_visibility(void *data, int type)
+{
+	int rc;
+
+	switch (type) {
+	case ISCSI_BOOT_INI_INITIATOR_NAME:
+		rc = S_IRUGO;
+		break;
+	default:
+		rc = 0;
+		break;
+	}
+	return rc;
+}
+
+
+static mode_t beiscsi_eth_get_attr_visibility(void *data, int type)
+{
+	int rc;
+
+	switch (type) {
+	case ISCSI_BOOT_ETH_FLAGS:
+	case ISCSI_BOOT_ETH_MAC:
+	case ISCSI_BOOT_ETH_INDEX:
+		rc = S_IRUGO;
+		break;
+	default:
+		rc = 0;
+		break;
+	}
+	return rc;
+}
+
+static int beiscsi_setup_boot_info(struct beiscsi_hba *phba)
+{
+	struct iscsi_boot_kobj *boot_kobj;
+
+	phba->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no);
+	if (!phba->boot_kset)
+		return -ENOMEM;
+
+	/* get boot info using mgmt cmd */
+	boot_kobj = iscsi_boot_create_target(phba->boot_kset, 0, phba,
+					     beiscsi_show_boot_tgt_info,
+					     beiscsi_tgt_get_attr_visibility);
+	if (!boot_kobj)
+		goto free_kset;
+
+	boot_kobj = iscsi_boot_create_initiator(phba->boot_kset, 0, phba,
+					     beiscsi_show_boot_ini_info,
+					     beiscsi_ini_get_attr_visibility);
+	if (!boot_kobj)
+		goto free_kset;
+
+	boot_kobj = iscsi_boot_create_ethernet(phba->boot_kset, 0, phba,
+					     beiscsi_show_boot_eth_info,
+					     beiscsi_eth_get_attr_visibility);
+	if (!boot_kobj)
+		goto free_kset;
+	return 0;
+
+free_kset:
+	iscsi_boot_destroy_kset(phba->boot_kset);
+	return -ENOMEM;
+}
+
 /*------------------- PCI Driver operations and data ----------------- */
 static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
 	{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
@@ -268,6 +481,15 @@
 
 	if (iscsi_host_add(shost, &phba->pcidev->dev))
 		goto free_devices;
+
+	if (beiscsi_setup_boot_info(phba))
+		/*
+		 * log error but continue, because we may not be using
+		 * iscsi boot.
+		 */
+		shost_printk(KERN_ERR, phba->shost, "Could not set up "
+		"iSCSI boot info.");
+
 	return phba;
 
 free_devices:
@@ -3279,6 +3501,89 @@
 			     "In hwi_disable_intr, Already Disabled\n");
 }
 
+static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
+{
+	struct be_cmd_resp_get_boot_target *boot_resp;
+	struct be_cmd_resp_get_session *session_resp;
+	struct be_mcc_wrb *wrb;
+	struct be_dma_mem nonemb_cmd;
+	unsigned int tag, wrb_num;
+	unsigned short status, extd_status;
+	struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+
+	tag = beiscsi_get_boot_target(phba);
+	if (!tag) {
+		SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed\n");
+		return -EAGAIN;
+	} else
+		wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+					 phba->ctrl.mcc_numtag[tag]);
+
+	wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
+	extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
+	status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+	if (status || extd_status) {
+		SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed"
+				    " status = %d extd_status = %d\n",
+				    status, extd_status);
+		free_mcc_tag(&phba->ctrl, tag);
+		return -EBUSY;
+	}
+	wrb = queue_get_wrb(mccq, wrb_num);
+	free_mcc_tag(&phba->ctrl, tag);
+	boot_resp = embedded_payload(wrb);
+
+	if (boot_resp->boot_session_handle < 0) {
+		printk(KERN_ERR "No Boot Session for this pci_func,"
+			"session Hndl = %d\n", boot_resp->boot_session_handle);
+		return -ENXIO;
+	}
+
+	nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
+				sizeof(*session_resp),
+				&nonemb_cmd.dma);
+	if (nonemb_cmd.va == NULL) {
+		SE_DEBUG(DBG_LVL_1,
+			 "Failed to allocate memory for"
+			 "beiscsi_get_session_info\n");
+		return -ENOMEM;
+	}
+
+	memset(nonemb_cmd.va, 0, sizeof(*session_resp));
+	tag = beiscsi_get_session_info(phba,
+		boot_resp->boot_session_handle, &nonemb_cmd);
+	if (!tag) {
+		SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info"
+			" Failed\n");
+		goto boot_freemem;
+	} else
+		wait_event_interruptible(phba->ctrl.mcc_wait[tag],
+					 phba->ctrl.mcc_numtag[tag]);
+
+	wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
+	extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
+	status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
+	if (status || extd_status) {
+		SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info Failed"
+				    " status = %d extd_status = %d\n",
+				    status, extd_status);
+		free_mcc_tag(&phba->ctrl, tag);
+		goto boot_freemem;
+	}
+	wrb = queue_get_wrb(mccq, wrb_num);
+	free_mcc_tag(&phba->ctrl, tag);
+	session_resp = nonemb_cmd.va ;
+	memcpy(&phba->boot_sess, &session_resp->session_info,
+	       sizeof(struct mgmt_session_info));
+	pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+		    nonemb_cmd.va, nonemb_cmd.dma);
+	return 0;
+boot_freemem:
+	pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
+		    nonemb_cmd.va, nonemb_cmd.dma);
+	return -ENOMEM;
+}
+
 static int beiscsi_init_port(struct beiscsi_hba *phba)
 {
 	int ret;
@@ -3841,6 +4146,7 @@
 	iscsi_host_remove(phba->shost);
 	pci_dev_put(phba->pcidev);
 	iscsi_host_free(phba->shost);
+	iscsi_boot_destroy_kset(phba->boot_kset);
 }
 
 static void beiscsi_msix_enable(struct beiscsi_hba *phba)
@@ -3996,6 +4302,11 @@
 		goto free_blkenbld;
 	}
 	hwi_enable_intr(phba);
+	ret = beiscsi_get_boot_info(phba);
+	if (ret < 0) {
+		shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
+			     "No Boot Devices !!!!!\n");
+	}
 	SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED\n\n\n");
 	return 0;