diff --git a/include/fw_context.h b/include/fw_context.h index abdff42..770b41a 100644 --- a/include/fw_context.h +++ b/include/fw_context.h @@ -54,6 +54,8 @@ struct boot_context { char mask[18]; char lun[17]; char vlan[15]; + + char scsi_host_name[64]; }; extern int fw_get_entry(struct boot_context *context); diff --git a/usr/iface.c b/usr/iface.c index 27b59d0..9c74117 100644 --- a/usr/iface.c +++ b/usr/iface.c @@ -778,31 +778,62 @@ void iface_link_ifaces(struct list_head *ifaces) iface_for_each_iface(ifaces, 1, &nr_found, iface_link); } -void iface_setup_from_boot_context(struct iface_rec *iface, +/** + * iface_setup_from_boot_context - setup iface from boot context info + * @iface: iface t setup + * @context: boot context info + * + * Returns 1 if setup for offload. + */ +int iface_setup_from_boot_context(struct iface_rec *iface, struct boot_context *context) { if (strlen(context->initiatorname)) strlcpy(iface->iname, context->initiatorname, sizeof(iface->iname)); - if (strlen(context->iface)) { - if (!net_get_transport_name_from_netdev(context->iface, - iface->transport_name)) { - /* set up for access through offload card */ - memset(iface->name, 0, sizeof(iface->name)); - snprintf(iface->name, sizeof(iface->name), - "%s.%s", iface->transport_name, - context->mac); - - strlcpy(iface->netdev, context->iface, - sizeof(iface->netdev)); - strlcpy(iface->hwaddress, context->mac, - sizeof(iface->hwaddress)); - strlcpy(iface->ipaddress, context->ipaddr, - sizeof(iface->ipaddress)); + if (strlen(context->scsi_host_name)) { + struct iscsi_transport *t; + uint32_t hostno; + + if (sscanf(context->scsi_host_name, "iscsi_boot%u", &hostno) != 1) { + log_error("Could not parse %s's host no.", + context->scsi_host_name); + return 0; } - } + t = iscsi_sysfs_get_transport_by_hba(hostno); + if (!t) { + log_error("Could not get transport for %s. " + "Make sure the iSCSI driver is loaded.", + context->scsi_host_name); + return 0; + } + + log_debug(3, "boot context has %s transport %s", + context->scsi_host_name, t->name); + strcpy(iface->transport_name, t->name); + } else if (strlen(context->iface) && + (!net_get_transport_name_from_netdev(context->iface, + iface->transport_name))) { + log_debug(3, "boot context has netdev %s", + context->iface); + strlcpy(iface->netdev, context->iface, + sizeof(iface->netdev)); + } else + return 0; + /* + * set up for access through a offload card. + */ + memset(iface->name, 0, sizeof(iface->name)); + snprintf(iface->name, sizeof(iface->name), "%s.%s", + iface->transport_name, context->mac); + + strlcpy(iface->hwaddress, context->mac, + sizeof(iface->hwaddress)); + strlcpy(iface->ipaddress, context->ipaddr, + sizeof(iface->ipaddress)); log_debug(1, "iface " iface_fmt "\n", iface_str(iface)); + return 1; } /** @@ -817,32 +848,24 @@ void iface_setup_from_boot_context(struct iface_rec *iface, int iface_create_ifaces_from_boot_contexts(struct list_head *ifaces, struct list_head *targets) { - char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; - char iface_name[ISCSI_MAX_IFACE_LEN]; struct boot_context *context; struct iface_rec *iface, *tmp_iface; int rc = 0; list_for_each_entry(context, targets, list) { - memset(transport_name, 0, ISCSI_TRANSPORT_NAME_MAXLEN); - - if (net_get_transport_name_from_netdev(context->iface, - transport_name)) - continue; - - /* offload + ibft support */ - memset(iface_name, 0, ISCSI_MAX_IFACE_LEN); - snprintf(iface_name, ISCSI_MAX_IFACE_LEN, - "%s.%s", transport_name, context->mac); - rc = 0; - iface = iface_alloc(iface_name, &rc); + /* use dummy name. If valid it will get overwritten below */ + iface = iface_alloc(DEFAULT_IFACENAME, &rc); if (!iface) { log_error("Could not setup iface %s for boot\n", - iface_name); + context->iface); goto fail; } - iface_setup_from_boot_context(iface, context); + if (!iface_setup_from_boot_context(iface, context)) { + /* no offload so forget it */ + free(iface); + continue; + } rc = iface_conf_write(iface); if (rc) { diff --git a/usr/iface.h b/usr/iface.h index f948686..9f6d47e 100644 --- a/usr/iface.h +++ b/usr/iface.h @@ -50,7 +50,7 @@ extern int iface_conf_write(struct iface_rec *iface); extern int iface_conf_delete(struct iface_rec *iface); extern int iface_is_valid(struct iface_rec *iface); extern void iface_link_ifaces(struct list_head *ifaces); -extern void iface_setup_from_boot_context(struct iface_rec *iface, +extern int iface_setup_from_boot_context(struct iface_rec *iface, struct boot_context *context); extern int iface_create_ifaces_from_boot_contexts(struct list_head *ifaces, struct list_head *targets); diff --git a/utils/fwparam_ibft/Makefile b/utils/fwparam_ibft/Makefile index b9e7988..ca07b76 100644 --- a/utils/fwparam_ibft/Makefile +++ b/utils/fwparam_ibft/Makefile @@ -22,7 +22,7 @@ # SYSDEPS_OBJS = $(wildcard ../sysdeps/*.o) -OBJS := fw_entry.o fwparam_ibft_sysfs.o $(SYSDEPS_OBJS) ../../usr/iscsi_net_util.o +OBJS := fw_entry.o fwparam_sysfs.o $(SYSDEPS_OBJS) ../../usr/iscsi_net_util.o OBJS += prom_lex.o prom_parse.tab.o fwparam_ppc.o CLEANFILES = $(OBJS) *.output *~ diff --git a/utils/fwparam_ibft/fw_entry.c b/utils/fwparam_ibft/fw_entry.c index ae5d34a..bbdb6d2 100644 --- a/utils/fwparam_ibft/fw_entry.c +++ b/utils/fwparam_ibft/fw_entry.c @@ -102,8 +102,7 @@ int fw_get_entry(struct boot_context *context) ret = fwparam_ppc_boot_info(context); if (ret) - ret = fwparam_ibft_sysfs_boot_info(context); - + ret = fwparam_sysfs_boot_info(context); return ret; } @@ -124,8 +123,7 @@ int fw_get_targets(struct list_head *list) ret = fwparam_ppc_get_targets(list); if (ret) - ret = fwparam_ibft_sysfs_get_targets(list); - + ret = fwparam_sysfs_get_targets(list); return ret; } diff --git a/utils/fwparam_ibft/fwparam.h b/utils/fwparam_ibft/fwparam.h index a79213b..32e4961 100644 --- a/utils/fwparam_ibft/fwparam.h +++ b/utils/fwparam_ibft/fwparam.h @@ -24,8 +24,8 @@ struct boot_context; -int fwparam_ibft_sysfs_boot_info(struct boot_context *context); -int fwparam_ibft_sysfs_get_targets(struct list_head *list); +int fwparam_sysfs_boot_info(struct boot_context *context); +int fwparam_sysfs_get_targets(struct list_head *list); int fwparam_ppc_boot_info(struct boot_context *context); int fwparam_ppc_get_targets(struct list_head *list); diff --git a/utils/fwparam_ibft/fwparam_sysfs.c b/utils/fwparam_ibft/fwparam_sysfs.c new file mode 100644 index 0000000..9b73d1a --- /dev/null +++ b/utils/fwparam_ibft/fwparam_sysfs.c @@ -0,0 +1,484 @@ +/* + * Copyright (C) IBM Corporation. 2007 + * Author: Konrad Rzeszutek + * Copyright (C) Red Hat, Inc. All rights reserved. 2008 - 2010 + * Copyright (C) Mike Christie 2008 - 2010 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define _XOPEN_SOURCE 500 +#define _SVID_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysfs.h" +#include "fw_context.h" +#include "fwparam.h" +#include "sysdeps.h" +#include "iscsi_net_util.h" + +#define ISCSI_BOOT_MAX 255 +#define IBFT_SYSFS_ROOT "/sys/firmware/ibft/" +#define IBFT_SUBSYS "ibft" + +#define ISCSI_LLD_ROOT "/sys/firmware/" +#define ISCSI_LLD_SUBSYS_PREFIX "iscsi_boot" + +static char *target_list[ISCSI_BOOT_MAX]; +static char *nic_list[ISCSI_BOOT_MAX]; +static int nic_cnt; +static int tgt_cnt; + +static int file_exist(const char *file) +{ + struct stat bootpath_stat; + + return !stat(file, &bootpath_stat); +} + +/* + * Finds the etherrnetX and targetX under the sysfs directory. + */ +static int find_sysfs_dirs(const char *fpath, const struct stat *sb, + int tflag, struct FTW *ftw) +{ + if (tflag == FTW_D && (strstr(fpath + ftw->base, "target"))) { + if (tgt_cnt == ISCSI_BOOT_MAX) { + printf("Too many targets found in iSCSI boot data." + "Max number of targets %d\n", ISCSI_BOOT_MAX); + return 0; + } + target_list[tgt_cnt++] = strdup(strstr(fpath, "target")); + } + + if (tflag == FTW_D && (strstr(fpath + ftw->base, "ethernet"))) { + if (nic_cnt == ISCSI_BOOT_MAX) { + printf("Too many nics found in iSCSI boot data." + "Max number of nics %d\n", ISCSI_BOOT_MAX); + return 0; + } + nic_list[nic_cnt++] = strdup(strstr(fpath, "ethernet")); + } + + return 0; +} + +static int get_iface_from_device(char *id, struct boot_context *context) +{ + char dev_dir[FILENAMESZ]; + int rc = ENODEV; + DIR *dirfd; + struct dirent *dent; + + memset(dev_dir, 0, FILENAMESZ); + snprintf(dev_dir, FILENAMESZ, IBFT_SYSFS_ROOT"/%s/device", id); + + if (!file_exist(dev_dir)) + return 0; + + dirfd = opendir(dev_dir); + if (!dirfd) + return errno; + + while ((dent = readdir(dirfd))) { + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..") || + strncmp(dent->d_name, "net:", 4)) + continue; + + if (!strncmp(dent->d_name, "net:", 4)) { + if ((strlen(dent->d_name) - 4) > + (sizeof(context->iface) - 1)) { + rc = EINVAL; + printf("Net device %s too big for iface " + "buffer.\n", dent->d_name); + break; + } + + if (sscanf(dent->d_name, "net:%s", context->iface) != 1) + rc = EINVAL; + rc = 0; + break; + } else { + printf("Could not read ethernet to net link.\n"); + rc = EOPNOTSUPP; + break; + } + } + + closedir(dirfd); + + if (rc != ENODEV) + return rc; + + /* If not found try again with newer kernel networkdev sysfs layout */ + strlcat(dev_dir, "/net", FILENAMESZ); + + if (!file_exist(dev_dir)) + return rc; + + dirfd = opendir(dev_dir); + if (!dirfd) + return errno; + + while ((dent = readdir(dirfd))) { + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) + continue; + + /* Take the first "regular" directory entry */ + if (strlen(dent->d_name) > (sizeof(context->iface) - 1)) { + rc = EINVAL; + printf("Net device %s too big for iface buffer.\n", + dent->d_name); + break; + } + + strcpy(context->iface, dent->d_name); + rc = 0; + break; + } + + closedir(dirfd); + return rc; +} + +/* + * Routines to fill in the context values. + */ +static int fill_nic_context(char *subsys, char *id, + struct boot_context *context) +{ + int rc; + + rc = sysfs_get_str(id, subsys, "mac", context->mac, + sizeof(context->mac)); + if (rc) + return rc; + + /* + * Some offload cards like bnx2i use different MACs for the net and + * iscsi functions, so we have to follow the sysfs links. + * + * Other ibft implementations may not be tied to a pci function, + * so there will not be any device/net link, so we drop down to + * the MAC matching. + * + * And finally, some cards like be2iscsi and qla4xxx do not have + * any linux network subsys representation. These hosts will + * not have the ibft subsys. Instead the subsys is the scsi host + * number. + */ + if (!strcmp(IBFT_SUBSYS, subsys)) { + rc = get_iface_from_device(id, context); + if (rc) { + rc = net_get_netdev_from_hwaddress(context->mac, + context->iface); + if (rc) + return rc; + } + } else + strlcpy(context->scsi_host_name, subsys, + sizeof(context->scsi_host_name)); + + sysfs_get_str(id, subsys, "ip-addr", context->ipaddr, + sizeof(context->ipaddr)); + sysfs_get_str(id, subsys, "vlan", context->vlan, + sizeof(context->vlan)); + sysfs_get_str(id, subsys, "subnet-mask", context->mask, + sizeof(context->mask)); + sysfs_get_str(id, subsys, "gateway", context->gateway, + sizeof(context->gateway)); + sysfs_get_str(id, subsys, "primary-dns", context->primary_dns, + sizeof(context->primary_dns)); + sysfs_get_str(id, subsys, "secondary-dns", context->secondary_dns, + sizeof(context->secondary_dns)); + sysfs_get_str(id, subsys, "dhcp", context->dhcp, + sizeof(context->dhcp)); + return 0; +} + +static void fill_initiator_context(char *subsys, struct boot_context *context) +{ + sysfs_get_str("initiator", subsys, "initiator-name", + context->initiatorname, + sizeof(context->initiatorname)); + sysfs_get_str("initiator", subsys, "isid", context->isid, + sizeof(context->isid)); +} +static int fill_tgt_context(char *subsys, char *id, + struct boot_context *context) +{ + int rc; + + rc = sysfs_get_str(id, subsys, "target-name", context->targetname, + sizeof(context->targetname)); + if (rc) + return rc; + + rc = sysfs_get_str(id, subsys, "ip-addr", context->target_ipaddr, + sizeof(context->target_ipaddr)); + if (rc) + return rc; + + /* + * We can live without the rest of they do not exist. If we + * failed to get them we will figure it out when we login. + */ + if (sysfs_get_int(id, subsys, "port", &context->target_port)) + context->target_port = ISCSI_LISTEN_PORT; + + sysfs_get_str(id, subsys, "lun", context->lun, + sizeof(context->lun)); + sysfs_get_str(id, subsys, "chap-name", context->chap_name, + sizeof(context->chap_name)); + sysfs_get_str(id, subsys, "chap-secret", context->chap_password, + sizeof(context->chap_password)); + sysfs_get_str(id, subsys, "rev-chap-name", context->chap_name_in, + sizeof(context->chap_name_in)); + sysfs_get_str(id, subsys, "rev-chap-name-secret", + context->chap_password_in, + sizeof(context->chap_password_in)); + return 0; +} + +#define IBFT_SYSFS_FLAG_FW_SEL_BOOT 2 + +static int find_boot_flag(char *subsys, char *list[], ssize_t size, + int *boot_idx) +{ + int rc = ENODEV; + int i, flag = 0; + + for (i = 0; i < size; i++, flag = -1) { + rc = sysfs_get_int(list[i], subsys, "flags", &flag); + if (rc) + continue; + + if (flag & IBFT_SYSFS_FLAG_FW_SEL_BOOT) { + *boot_idx = i; + rc = 0; + break; + } + rc = ENODEV; + flag = 0; + + } + + return rc; +} + +static void deallocate_lists(void) +{ + int i; + + for (i = 0; i < nic_cnt; i++) + free(nic_list[i]); + + nic_cnt = 0; + for (i = 0; i < tgt_cnt; i++) + free(target_list[i]); + + tgt_cnt = 0; + +} + +static int get_boot_info(struct boot_context *context, char *rootdir, + char *subsys) +{ + char initiator_dir[FILENAMESZ]; + int rc = ENODEV; + int nic_idx = -1, tgt_idx = -1; + + memset(&initiator_dir, 0 , FILENAMESZ); + snprintf(initiator_dir, FILENAMESZ, "%sinitiator", rootdir); + + nic_cnt = 0; + tgt_cnt = 0; + if (file_exist(initiator_dir)) { + /* Find the target's and the ethernet's */ + rc = nftw(rootdir, find_sysfs_dirs, 20, 1); + + /* Find wihch target and which ethernet have + the boot flag set. */ + rc = find_boot_flag(subsys, nic_list, nic_cnt, &nic_idx); + if (rc) + goto free; + + rc = find_boot_flag(subsys, target_list, tgt_cnt, &tgt_idx); + if (rc) + goto free; + + /* Fill in the context values */ + rc = fill_nic_context(subsys, nic_list[nic_idx], context); + rc |= fill_tgt_context(subsys, target_list[tgt_idx], context); + fill_initiator_context(subsys, context); + } +free: + deallocate_lists(); + return rc; +} + +int fwparam_sysfs_boot_info(struct boot_context *context) +{ + struct dirent *dent; + DIR *dirfd; + int rc = 0; + + if (!get_boot_info(context, IBFT_SYSFS_ROOT, IBFT_SUBSYS)) + return 0; + /* + * We could have multiple iscsi llds and each lld could have + * multiple targets/ethernet ports + */ + dirfd = opendir(ISCSI_LLD_ROOT); + if (!dirfd) + return errno; + + while ((dent = readdir(dirfd))) { + char lld_root[FILENAMESZ]; + + memset(&lld_root, 0 , FILENAMESZ); + + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) + continue; + + if (strncmp(dent->d_name, ISCSI_LLD_SUBSYS_PREFIX, 10)) + continue; + + snprintf(lld_root, FILENAMESZ, ISCSI_LLD_ROOT"%s", + dent->d_name); + if (!get_boot_info(context, lld_root, dent->d_name)) + goto done; + } + rc = ENODEV; +done: + closedir(dirfd); + return rc; +} + +static int get_targets(struct list_head *list, char *rootdir, char *subsys) +{ + struct boot_context *context; + int rc = 0, i, nic_idx, nic; + char initiator_dir[FILENAMESZ]; + + memset(&initiator_dir, 0 , FILENAMESZ); + snprintf(initiator_dir, FILENAMESZ, "%sinitiator", rootdir); + + if (!file_exist(initiator_dir)) + return ENODEV; + + nic_cnt = 0; + tgt_cnt = 0; + + /* Find the target's and the ethernet's */ + nftw(rootdir, find_sysfs_dirs, 20, 1); + for (i = 0; i < tgt_cnt; i++) { + context = calloc(1, sizeof(*context)); + if (!context) { + rc = ENOMEM; + break; + } + + rc = fill_tgt_context(subsys, target_list[i], context); + if (rc) + break; + + rc = sysfs_get_int(target_list[i], subsys, "nic-assoc", + &nic_idx); + if (rc) + break; + + for (nic = 0; nic < nic_cnt; nic++) { + int id; + + rc = sysfs_get_int(nic_list[nic], subsys, "index", + &id); + if (!rc && (id == nic_idx)) + break; + } + + if (nic == nic_cnt) { + printf("Invalid nic-assoc of %d. Max id %d.\n", + nic_idx, nic_cnt); + break; + } + + rc = fill_nic_context(subsys, nic_list[nic], context); + if (rc) + break; + + fill_initiator_context(subsys, context); + list_add_tail(&context->list, list); + } + + if (rc) { + if (context) + free(context); + fw_free_targets(list); + } + + deallocate_lists(); + return rc; +} + +int fwparam_sysfs_get_targets(struct list_head *list) +{ + struct dirent *dent; + DIR *dirfd; + int rc = 0; + + /* ibft only has one instance */ + get_targets(list, IBFT_SYSFS_ROOT, IBFT_SUBSYS); + /* + * We could have multiple iscsi llds and each lld could have + * multiple targets/ethernet ports + */ + dirfd = opendir(ISCSI_LLD_ROOT); + if (!dirfd) { + rc = errno; + goto done; + } + + while ((dent = readdir(dirfd))) { + char lld_root[FILENAMESZ]; + + memset(&lld_root, 0 , FILENAMESZ); + + if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) + continue; + + if (strncmp(dent->d_name, ISCSI_LLD_SUBSYS_PREFIX, 10)) + continue; + + snprintf(lld_root, FILENAMESZ, ISCSI_LLD_ROOT"%s", + dent->d_name); + get_targets(list, lld_root, dent->d_name); + } + closedir(dirfd); +done: + if (!rc && list_empty(list)) + rc = ENODEV; + if (rc) + fw_free_targets(list); + return rc; +} -- 1.6.6.1