Blame bind.c

Packit Service 35c908
/*
Packit Service 35c908
 * Copyright (c) 2008, Intel Corporation.
Packit Service 35c908
 *
Packit Service 35c908
 * This program is free software; you can redistribute it and/or modify it
Packit Service 35c908
 * under the terms and conditions of the GNU Lesser General Public License,
Packit Service 35c908
 * version 2.1, as published by the Free Software Foundation.
Packit Service 35c908
 *
Packit Service 35c908
 * This program is distributed in the hope it will be useful, but WITHOUT
Packit Service 35c908
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
Packit Service 35c908
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
Packit Service 35c908
 * for more details.
Packit Service 35c908
 *
Packit Service 35c908
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 35c908
 * along with this program; if not, write to the Free Software Foundation, Inc.,
Packit Service 35c908
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service 35c908
 *
Packit Service 35c908
 */
Packit Service 35c908
Packit Service 35c908
#include "utils.h"
Packit Service 35c908
#include "api_lib.h"
Packit Service 35c908
#include "adapt_impl.h"
Packit Service 35c908
#include "bind_impl.h"
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Binding capabilities we understand.
Packit Service 35c908
 */
Packit Service 35c908
#define BINDING_CAPABILITIES   (HBA_CAN_BIND_TO_D_ID | \
Packit Service 35c908
				HBA_CAN_BIND_TO_WWPN | \
Packit Service 35c908
				HBA_CAN_BIND_TO_WWNN)
Packit Service 35c908
#define SYSFS_BIND		"tgtid_bind_type"
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Name-value strings for kernel bindings.
Packit Service 35c908
 * The first word of the strings must exactly match those in
Packit Service 35c908
 * Linux's drivers/scsi/scsi_transport_fc
Packit Service 35c908
 */
Packit Service 35c908
static struct sa_nameval binding_types_table[] = {
Packit Service 35c908
	{ "none",                           0 },
Packit Service 35c908
	{ "wwpn (World Wide Port Name)",    HBA_CAN_BIND_TO_WWPN },
Packit Service 35c908
	{ "wwnn (World Wide Node Name)",    HBA_CAN_BIND_TO_WWNN },
Packit Service 35c908
	{ "port_id (FC Address)",           HBA_CAN_BIND_TO_D_ID },
Packit Service 35c908
	{ NULL,                             0 }
Packit Service 35c908
};
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Context for LUN binding reader.
Packit Service 35c908
 */
Packit Service 35c908
struct binding_context {
Packit Service 35c908
	HBA_HANDLE            oc_handle;
Packit Service 35c908
	int                   oc_kern_hba;  /* kernel HBA number */
Packit Service 35c908
	int                   oc_port;
Packit Service 35c908
	int                   oc_target;
Packit Service 35c908
	int                   oc_lun;
Packit Service 35c908
	u_int32_t             oc_count;
Packit Service 35c908
	u_int32_t             oc_limit;
Packit Service 35c908
	u_int32_t             oc_ver;
Packit Service 35c908
	void                  *oc_entries;
Packit Service 35c908
	HBA_STATUS            oc_status;
Packit Service 35c908
	char                  oc_sg[32];    /* SCSI-generic dev name */
Packit Service 35c908
	HBA_SCSIID            *oc_scp;      /* place for OS device name */
Packit Service 35c908
	struct port_info      *oc_rport;    /* target remote port, if known */
Packit Service 35c908
	char                  oc_path[256]; /* parent dir save area */
Packit Service 35c908
};
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Get binding capability.
Packit Service 35c908
 * We currently don't have a way to get this from the driver.
Packit Service 35c908
 * Instead, we hardcode what we know about Linux's capabilities.
Packit Service 35c908
 * We don't care which HBA is specified, except to return the correct error.
Packit Service 35c908
 */
Packit Service 35c908
HBA_STATUS
Packit Service 35c908
get_binding_capability(HBA_HANDLE handle, HBA_WWN wwn, HBA_BIND_CAPABILITY *cp)
Packit Service 35c908
{
Packit Service 35c908
	struct port_info *pp;
Packit Service 35c908
	int count = 0;
Packit Service 35c908
Packit Service 35c908
	pp = adapter_get_port_by_wwn(handle, wwn, &count);
Packit Service 35c908
	if (count > 1)
Packit Service 35c908
		return HBA_STATUS_ERROR_AMBIGUOUS_WWN;
Packit Service 35c908
	else if (pp == NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_ILLEGAL_WWN;
Packit Service 35c908
	*cp = BINDING_CAPABILITIES;
Packit Service 35c908
	return HBA_STATUS_OK;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Get binding support.
Packit Service 35c908
 */
Packit Service 35c908
HBA_STATUS
Packit Service 35c908
get_binding_support(HBA_HANDLE handle, HBA_WWN wwn, HBA_BIND_CAPABILITY *cp)
Packit Service 35c908
{
Packit Service 35c908
	struct port_info *pp;
Packit Service 35c908
	char dir[50];
Packit Service 35c908
	char bind[50];
Packit Service 35c908
	int count = 0;
Packit Service 35c908
Packit Service 35c908
	pp = adapter_get_port_by_wwn(handle, wwn, &count);
Packit Service 35c908
	if (count > 1)
Packit Service 35c908
		return HBA_STATUS_ERROR_AMBIGUOUS_WWN;
Packit Service 35c908
	else if (pp == NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_ILLEGAL_WWN;
Packit Service 35c908
	snprintf(dir, sizeof(dir), SYSFS_HOST_DIR "/host%u", pp->ap_kern_hba);
Packit Service 35c908
	if (sa_sys_read_line(dir, SYSFS_BIND, bind, sizeof(bind)) == 0)
Packit Service 35c908
		sa_enum_encode(binding_types_table, bind, cp);
Packit Service 35c908
	return HBA_STATUS_OK;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Set binding support.
Packit Service 35c908
 */
Packit Service 35c908
HBA_STATUS
Packit Service 35c908
set_binding_support(HBA_HANDLE handle, HBA_WWN wwn, HBA_BIND_CAPABILITY flags)
Packit Service 35c908
{
Packit Service 35c908
	struct port_info *pp;
Packit Service 35c908
	int count = 0;
Packit Service 35c908
	char dir[50];
Packit Service 35c908
	char buf[50];
Packit Service 35c908
	const char *bind;
Packit Service 35c908
Packit Service 35c908
	pp = adapter_get_port_by_wwn(handle, wwn, &count);
Packit Service 35c908
	if (count > 1)
Packit Service 35c908
		return HBA_STATUS_ERROR_AMBIGUOUS_WWN;
Packit Service 35c908
	if (pp == NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_ILLEGAL_WWN;
Packit Service 35c908
	if ((flags & BINDING_CAPABILITIES) != flags)
Packit Service 35c908
		return HBA_STATUS_ERROR_NOT_SUPPORTED;
Packit Service 35c908
	bind = sa_enum_decode(buf, sizeof(buf), binding_types_table, flags);
Packit Service 35c908
	snprintf(dir, sizeof(dir), SYSFS_HOST_DIR "/host%u", pp->ap_kern_hba);
Packit Service 35c908
	if (strstr(bind, "Unknown") != NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_NOT_SUPPORTED;
Packit Service 35c908
	if (sa_sys_write_line(dir, SYSFS_BIND, bind) == 0)
Packit Service 35c908
		return HBA_STATUS_ERROR_INCAPABLE;
Packit Service 35c908
	return HBA_STATUS_OK;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
static int
Packit Service 35c908
get_deprecated_device_name(struct dirent *dp, void *arg)
Packit Service 35c908
{
Packit Service 35c908
	struct binding_context *cp = arg;
Packit Service 35c908
Packit Service 35c908
	if (strstr(cp->oc_path, "block"))
Packit Service 35c908
		snprintf(cp->oc_scp->OSDeviceName,
Packit Service 35c908
			 sizeof(cp->oc_scp->OSDeviceName),
Packit Service 35c908
			 "/dev/%s", dp->d_name);
Packit Service 35c908
	if (strstr(cp->oc_path, "scsi_generic"))
Packit Service 35c908
		snprintf(cp->oc_sg, sizeof(cp->oc_sg),
Packit Service 35c908
			 "/dev/%s", dp->d_name);
Packit Service 35c908
	return 0;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
static int
Packit Service 35c908
get_binding_os_names(struct dirent *dp, void *arg)
Packit Service 35c908
{
Packit Service 35c908
	struct binding_context *cp = arg;
Packit Service 35c908
	char *name = dp->d_name;
Packit Service 35c908
	char *sep;
Packit Service 35c908
	char buf[sizeof(cp->oc_scp->OSDeviceName)];
Packit Service 35c908
Packit Service 35c908
	sep = strchr(name, ':');
Packit Service 35c908
	if (dp->d_type == DT_LNK && sep != NULL) {
Packit Service 35c908
		*sep = '\0';                /* replace colon */
Packit Service 35c908
		if (strcmp(name, "block") == 0) {
Packit Service 35c908
			snprintf(cp->oc_scp->OSDeviceName,
Packit Service 35c908
				 sizeof(cp->oc_scp->OSDeviceName),
Packit Service 35c908
				 "/dev/%s", sep + 1);
Packit Service 35c908
		} else if (strcmp(name, "scsi_generic") == 0) {
Packit Service 35c908
			snprintf(cp->oc_sg,
Packit Service 35c908
				 sizeof(cp->oc_sg),
Packit Service 35c908
				 "/dev/%s", sep + 1);
Packit Service 35c908
		}
Packit Service 35c908
		*sep = ':';                 /* not really needed */
Packit Service 35c908
	} else if (dp->d_type == DT_DIR && sep == NULL) {
Packit Service 35c908
		if ((!strcmp(name, "block")) ||
Packit Service 35c908
		    (!strcmp(name, "scsi_generic"))) {
Packit Service 35c908
			/* save the original path */
Packit Service 35c908
			sa_strncpy_safe(buf, sizeof(buf),
Packit Service 35c908
					cp->oc_path, sizeof(cp->oc_path));
Packit Service 35c908
Packit Service 35c908
			snprintf(cp->oc_path, sizeof(cp->oc_path),
Packit Service 35c908
				"%s/%s", buf, name);
Packit Service 35c908
			sa_dir_read(cp->oc_path,
Packit Service 35c908
				get_deprecated_device_name, cp);
Packit Service 35c908
Packit Service 35c908
			/* restore the original path */
Packit Service 35c908
			sa_strncpy_safe(cp->oc_path, sizeof(cp->oc_path),
Packit Service 35c908
					buf, sizeof(buf));
Packit Service 35c908
		}
Packit Service 35c908
	}
Packit Service 35c908
	return 0;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
static int
Packit Service 35c908
get_binding_target_mapping(struct dirent *dp, void *ctxt_arg)
Packit Service 35c908
{
Packit Service 35c908
	struct binding_context *cp = ctxt_arg;
Packit Service 35c908
	struct port_info *pp;
Packit Service 35c908
	HBA_FCPSCSIENTRY *sp;
Packit Service 35c908
	HBA_FCPSCSIENTRYV2 *s2p;
Packit Service 35c908
	HBA_SCSIID *scp = NULL;
Packit Service 35c908
	HBA_FCPID *fcp = NULL;
Packit Service 35c908
	HBA_LUID *luid = NULL;
Packit Service 35c908
	char name[50];
Packit Service 35c908
	u_int32_t hba = -1;
Packit Service 35c908
	u_int32_t port = -1;
Packit Service 35c908
	u_int32_t tgt = -1;
Packit Service 35c908
	u_int32_t lun = -1;
Packit Service 35c908
Packit Service 35c908
	/*
Packit Service 35c908
	 * Parse directory entry name to see if it matches
Packit Service 35c908
	 * <hba>:<port>:<target>:<lun>.
Packit Service 35c908
	 */
Packit Service 35c908
	if (sscanf(dp->d_name, "%u:%u:%u:%u", &hba, &port, &tgt, &lun) != 4)
Packit Service 35c908
		return 0;
Packit Service 35c908
Packit Service 35c908
	if (hba != cp->oc_kern_hba ||
Packit Service 35c908
	    (port != cp->oc_port && cp->oc_port != -1) ||
Packit Service 35c908
	    (tgt != cp->oc_target && cp->oc_target != -1) ||
Packit Service 35c908
	    (lun != cp->oc_lun && cp->oc_lun != -1)) {
Packit Service 35c908
		return 0;
Packit Service 35c908
	}
Packit Service 35c908
Packit Service 35c908
	/*
Packit Service 35c908
	 * Name matches.  Add to count and to mapping list if there's room.
Packit Service 35c908
	 */
Packit Service 35c908
	if (cp->oc_count < cp->oc_limit) {
Packit Service 35c908
Packit Service 35c908
		switch (cp->oc_ver) {
Packit Service 35c908
		case 1:
Packit Service 35c908
			sp = &((HBA_FCPSCSIENTRY *)
Packit Service 35c908
				cp->oc_entries)[cp->oc_count];
Packit Service 35c908
			scp = &sp->ScsiId;
Packit Service 35c908
			fcp = &sp->FcpId;
Packit Service 35c908
			luid = NULL;
Packit Service 35c908
			break;
Packit Service 35c908
		case 2:
Packit Service 35c908
			s2p = &((HBA_FCPSCSIENTRYV2 *)
Packit Service 35c908
				cp->oc_entries)[cp->oc_count];
Packit Service 35c908
			scp = &s2p->ScsiId;
Packit Service 35c908
			fcp = &s2p->FcpId;
Packit Service 35c908
			luid = &s2p->LUID;
Packit Service 35c908
			break;
Packit Service 35c908
		default:
Packit Service 35c908
			fprintf(stderr, "*** Fatal! ***\n");
Packit Service 35c908
			break;
Packit Service 35c908
		}
Packit Service 35c908
		pp = cp->oc_rport;
Packit Service 35c908
		if (pp == NULL)
Packit Service 35c908
			pp = adapter_get_rport_target(cp->oc_handle,
Packit Service 35c908
							port, tgt);
Packit Service 35c908
		if (pp != NULL) {
Packit Service 35c908
			fcp->FcId = pp->ap_attr.PortFcId;
Packit Service 35c908
			fcp->NodeWWN = pp->ap_attr.NodeWWN;
Packit Service 35c908
			fcp->PortWWN = pp->ap_attr.PortWWN;
Packit Service 35c908
			fcp->FcpLun = (HBA_UINT64) lun;
Packit Service 35c908
		}
Packit Service 35c908
Packit Service 35c908
		/*
Packit Service 35c908
		 * Find OS device name by searching for symlink block:<device>
Packit Service 35c908
		 * and SG name by searching for scsi_generic:<name>
Packit Service 35c908
		 * in device subdirectory.
Packit Service 35c908
		 */
Packit Service 35c908
		snprintf(name, sizeof(name),
Packit Service 35c908
			 SYSFS_LUN_DIR "/%s/device", dp->d_name);
Packit Service 35c908
		cp->oc_sg[0] = '\0';
Packit Service 35c908
		cp->oc_scp = scp;
Packit Service 35c908
		scp->OSDeviceName[0] = '\0';
Packit Service 35c908
		/* save a copy of the dir name */
Packit Service 35c908
		sa_strncpy_safe(cp->oc_path, sizeof(cp->oc_path),
Packit Service 35c908
				name, sizeof(name));
Packit Service 35c908
		sa_dir_read(name, get_binding_os_names, cp);
Packit Service 35c908
		scp->ScsiBusNumber = hba;
Packit Service 35c908
		scp->ScsiTargetNumber = tgt;
Packit Service 35c908
		scp->ScsiOSLun = lun;
Packit Service 35c908
Packit Service 35c908
		/*
Packit Service 35c908
		 * find the LUN ID information by using scsi_generic I/O.
Packit Service 35c908
		 */
Packit Service 35c908
		if (luid != NULL && cp->oc_sg[0] != '\0')
Packit Service 35c908
			sg_get_dev_id(cp->oc_sg, luid->buffer,
Packit Service 35c908
					  sizeof(luid->buffer));
Packit Service 35c908
	}
Packit Service 35c908
	cp->oc_count++;
Packit Service 35c908
	return 0;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Get FCP target mapping.
Packit Service 35c908
 */
Packit Service 35c908
HBA_STATUS
Packit Service 35c908
get_binding_target_mapping_v1(HBA_HANDLE handle, HBA_FCPTARGETMAPPING *map)
Packit Service 35c908
{
Packit Service 35c908
	struct binding_context ctxt;
Packit Service 35c908
	struct adapter_info *ap;
Packit Service 35c908
Packit Service 35c908
	ap = adapter_open_handle(handle);
Packit Service 35c908
	if (ap == NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_INVALID_HANDLE;
Packit Service 35c908
	memset(&ctxt, 0, sizeof(ctxt));
Packit Service 35c908
	ctxt.oc_handle = handle;
Packit Service 35c908
	ctxt.oc_kern_hba = ap->ad_kern_index;
Packit Service 35c908
	ctxt.oc_port = -1;
Packit Service 35c908
	ctxt.oc_target = -1;
Packit Service 35c908
	ctxt.oc_lun = -1;
Packit Service 35c908
	ctxt.oc_limit = map->NumberOfEntries;
Packit Service 35c908
	ctxt.oc_ver = 1;
Packit Service 35c908
	ctxt.oc_entries = map->entry;
Packit Service 35c908
	ctxt.oc_status = HBA_STATUS_OK;
Packit Service 35c908
	memset(map->entry, 0, sizeof(map->entry[0]) * ctxt.oc_limit);
Packit Service 35c908
	sa_dir_read(SYSFS_LUN_DIR, get_binding_target_mapping, &ctxt);
Packit Service 35c908
	map->NumberOfEntries = ctxt.oc_count;
Packit Service 35c908
	if (ctxt.oc_status == HBA_STATUS_OK && ctxt.oc_count > ctxt.oc_limit)
Packit Service 35c908
		ctxt.oc_status = HBA_STATUS_ERROR_MORE_DATA;
Packit Service 35c908
	return ctxt.oc_status;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Get FCP target mapping.
Packit Service 35c908
 */
Packit Service 35c908
HBA_STATUS
Packit Service 35c908
get_binding_target_mapping_v2(HBA_HANDLE handle, HBA_WWN wwn,
Packit Service 35c908
			       HBA_FCPTARGETMAPPINGV2 *map)
Packit Service 35c908
{
Packit Service 35c908
	struct binding_context ctxt;
Packit Service 35c908
	struct adapter_info *ap;
Packit Service 35c908
	struct port_info *pp;
Packit Service 35c908
Packit Service 35c908
	pp = adapter_get_port_by_wwn(handle, wwn, NULL);
Packit Service 35c908
	if (pp == NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_INVALID_HANDLE;
Packit Service 35c908
	ap = pp->ap_adapt;
Packit Service 35c908
	if (ap == NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_INVALID_HANDLE;
Packit Service 35c908
	memset(&ctxt, 0, sizeof(ctxt));
Packit Service 35c908
	ctxt.oc_handle = handle;
Packit Service 35c908
	ctxt.oc_kern_hba = ap->ad_kern_index;
Packit Service 35c908
	ctxt.oc_port = pp->ap_index;
Packit Service 35c908
	ctxt.oc_target = -1;
Packit Service 35c908
	ctxt.oc_lun = -1;
Packit Service 35c908
	ctxt.oc_limit = map->NumberOfEntries;
Packit Service 35c908
	ctxt.oc_ver = 2;
Packit Service 35c908
	ctxt.oc_entries = map->entry;
Packit Service 35c908
	ctxt.oc_status = HBA_STATUS_OK;
Packit Service 35c908
	memset(map->entry, 0, sizeof(map->entry[0]) * ctxt.oc_limit);
Packit Service 35c908
	sa_dir_read(SYSFS_LUN_DIR, get_binding_target_mapping, &ctxt);
Packit Service 35c908
	map->NumberOfEntries = ctxt.oc_count;
Packit Service 35c908
	if (ctxt.oc_status == HBA_STATUS_OK && ctxt.oc_count > ctxt.oc_limit)
Packit Service 35c908
		ctxt.oc_status = HBA_STATUS_ERROR_MORE_DATA;
Packit Service 35c908
	return ctxt.oc_status;
Packit Service 35c908
}
Packit Service 35c908
Packit Service 35c908
/*
Packit Service 35c908
 * Get LUN scsi-generic device name.
Packit Service 35c908
 */
Packit Service 35c908
int
Packit Service 35c908
get_binding_sg_name(struct port_info *lp, HBA_WWN disc_wwpn,
Packit Service 35c908
		     HBA_UINT64 fc_lun, char *buf, size_t len)
Packit Service 35c908
{
Packit Service 35c908
	struct binding_context ctxt;
Packit Service 35c908
	struct port_info *rp;
Packit Service 35c908
	HBA_FCPSCSIENTRYV2 entry;
Packit Service 35c908
Packit Service 35c908
	/*
Packit Service 35c908
	 * find discovered (remote) port.
Packit Service 35c908
	 */
Packit Service 35c908
	rp = adapter_get_rport_by_wwn(lp, disc_wwpn);
Packit Service 35c908
	if (rp == NULL)
Packit Service 35c908
		return HBA_STATUS_ERROR_ILLEGAL_WWN;
Packit Service 35c908
Packit Service 35c908
	memset(&ctxt, 0, sizeof(ctxt));
Packit Service 35c908
	memset(&entry, 0, sizeof(entry));
Packit Service 35c908
	ctxt.oc_rport = rp;
Packit Service 35c908
	ctxt.oc_kern_hba = rp->ap_kern_hba;
Packit Service 35c908
	ctxt.oc_port = rp->ap_index;
Packit Service 35c908
	ctxt.oc_target = rp->ap_scsi_target;
Packit Service 35c908
	if (ctxt.oc_target == -1)
Packit Service 35c908
		return ENOENT;
Packit Service 35c908
	ctxt.oc_lun = (int) fc_lun;
Packit Service 35c908
	ctxt.oc_limit = 1;
Packit Service 35c908
	ctxt.oc_ver = 1;
Packit Service 35c908
	ctxt.oc_entries = &entry;
Packit Service 35c908
	sa_dir_read(SYSFS_LUN_DIR, get_binding_target_mapping, &ctxt);
Packit Service 35c908
	if (ctxt.oc_count != 1)
Packit Service 35c908
		return ENOENT;
Packit Service 35c908
	sa_strncpy_safe(buf, len, ctxt.oc_sg, sizeof(ctxt.oc_sg));
Packit Service 35c908
	return 0;
Packit Service 35c908
}