From 264e48a0bda2d6cd5d0607acd2669894ee95b3b5 Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Fri, 11 Nov 2016 08:17:51 +0200 Subject: iscsiuio: Add support for the new qedi transport Signed-off-by: Manish Rangankar Signed-off-by: Adheer Chandravanshi Signed-off-by: Nilesh Javali --- iscsiuio/src/unix/libs/Makefile.am | 3 +- iscsiuio/src/unix/libs/cnic.c | 9 + iscsiuio/src/unix/libs/cnic.h | 2 + iscsiuio/src/unix/libs/qedi.c | 1151 ++++++++++++++++++++++++++++++++++++ iscsiuio/src/unix/libs/qedi.h | 159 +++++ iscsiuio/src/unix/nic.c | 6 +- iscsiuio/src/unix/nic.h | 1 + iscsiuio/src/unix/nic_utils.c | 147 ++++- iscsiuio/src/unix/nic_utils.h | 2 + iscsiuio/src/unix/options.h | 1 + 10 files changed, 1475 insertions(+), 6 deletions(-) create mode 100644 iscsiuio/src/unix/libs/qedi.c create mode 100644 iscsiuio/src/unix/libs/qedi.h diff --git a/iscsiuio/src/unix/libs/Makefile.am b/iscsiuio/src/unix/libs/Makefile.am index 890415f5a79a..737546b04917 100644 --- a/iscsiuio/src/unix/libs/Makefile.am +++ b/iscsiuio/src/unix/libs/Makefile.am @@ -10,4 +10,5 @@ noinst_LIBRARIES = lib_iscsiuio_hw_cnic.a lib_iscsiuio_hw_cnic_a_SOURCES = ../build_date.c \ cnic.c \ bnx2.c \ - bnx2x.c + bnx2x.c \ + qedi.c diff --git a/iscsiuio/src/unix/libs/cnic.c b/iscsiuio/src/unix/libs/cnic.c index 228c4b9e25b1..5d60f898ad57 100644 --- a/iscsiuio/src/unix/libs/cnic.c +++ b/iscsiuio/src/unix/libs/cnic.c @@ -55,6 +55,7 @@ #include #include #include +#include #include "uip_arp.h" #include "nic.h" @@ -65,6 +66,7 @@ #include "cnic.h" #include "iscsi_if.h" #include "ipv6_ndpc.h" +#include "qedi.h" /******************************************************************************* * Constants @@ -81,6 +83,13 @@ const char bnx2i_library_transport_name[] = "bnx2i"; const size_t bnx2i_library_transport_name_size = sizeof(bnx2i_library_transport_name); +/******************************************************************************* + * Constants for qedi module + ******************************************************************************/ +const char qedi_library_transport_name[] = "qedi"; +const size_t qedi_library_transport_name_size = + sizeof(qedi_library_transport_name); + /****************************************************************************** * Netlink Functions ******************************************************************************/ diff --git a/iscsiuio/src/unix/libs/cnic.h b/iscsiuio/src/unix/libs/cnic.h index 6244a94012c1..c86595c512b0 100644 --- a/iscsiuio/src/unix/libs/cnic.h +++ b/iscsiuio/src/unix/libs/cnic.h @@ -44,6 +44,8 @@ ******************************************************************************/ extern const char bnx2i_library_transport_name[]; extern const size_t bnx2i_library_transport_name_size; +extern const char qedi_library_transport_name[]; +extern const size_t qedi_library_transport_name_size; int cnic_nl_open(); void cnic_nl_close(); diff --git a/iscsiuio/src/unix/libs/qedi.c b/iscsiuio/src/unix/libs/qedi.c new file mode 100644 index 000000000000..c2096e59dad1 --- /dev/null +++ b/iscsiuio/src/unix/libs/qedi.c @@ -0,0 +1,1151 @@ +/* + * Copyright (c) 2016, Cavium Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * qedi.c - qedi user space driver + * This file handles different qedi NIC operations, + * qedi_open - initializes all hardware resources under NIC device + * qedi_close - closes the NIC device + * qedi_read - reads data to the hardware + * qedi_write - writes data to the hardware + * qedi_start_xmit - sends a pkt of data on NIC device + * qedi_get_tx_pkt - gets a Tx pkt from NIC + * qedi_clear_tx_intr - clears the Tx interrupt + * NOTE: nic_t is used as NIC device, + * qedi is not attached to netdev hence it is not mandatory + * for netdev to be upd + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "build_date.h" +#include "bnx2x.h" +#include "qedi.h" +#include "cnic.h" +#include "logger.h" +#include "nic.h" +#include "nic_id.h" +#include "nic_utils.h" +#include "options.h" + +#define PFX "qedi " + +extern int nl_sock; + +/* Foward struct declarations */ +struct nic_ops qedi_op; + +/******************************************************************************* + * NIC Library Strings + ******************************************************************************/ +static const char library_name[] = "qedi"; +static const char library_version[] = PACKAGE_VERSION; +static const char library_uio_name[] = "qedi_uio"; + +/* The name that should be returned from /sys/class/uio/uio0/name */ +static const char cnic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name"; +static const char qedi_uio_sysfs_name[] = "qedi_uio"; +static const char qedi_host_mac_template[] = + "/sys/class/iscsi_host/host%i/hwaddress"; + +struct qedi_driver_version qedi_version = { + QEDI_UNKNOWN_MAJOR_VERSION, + QEDI_UNKNOWN_MINOR_VERSION, + QEDI_UNKNOWN_SUB_MINOR_VERSION, +}; + +static int qedi_clear_tx_intr(nic_t *nic); + +/******************************************************************************* + * QEDI Library Functions + ******************************************************************************/ +/** + * qedi_get_library_name() - Used to get the name of this NIC library + * @param name - This function will return the pointer to this NIC + * library name + * @param name_size + */ +static void qedi_get_library_name(char **name, size_t *name_size) +{ + *name = (char *)library_name; + *name_size = sizeof(library_name); +} + +/** + * qedi_get_library_version() - Used to get the version string of this + * NIC library + * @param version - This function will return the pointer to this NIC + * library version string + * @param version_size - This will be set with the version size + */ +static void qedi_get_library_version(char **version, size_t *version_size) +{ + *version = (char *)library_version; + *version_size = sizeof(library_version); +} + +/** + * qedi_get_build_date() - Used to get the build date string of this library + * @param version - This function will return the pointer to this NIC + * library build date string + * @param version_size - This will be set with the build date string size + */ +static void qedi_get_build_date(char **build, size_t *build_size) +{ + *build = (char *)build_date; + *build_size = sizeof(build_date); +} + +/** + * qedi_get_transport_name() - Used to get the transport name associated + * with this this NIC library + * @param transport_name - This function will return the pointer to this NIC + * library's associated transport string + * @param transport_name_size - This will be set with the transport name size + */ +static void qedi_get_transport_name(char **transport_name, + size_t *transport_name_size) +{ + *transport_name = (char *)qedi_library_transport_name; + *transport_name_size = qedi_library_transport_name_size; +} + +/** + * qedi_get_uio_name() - Used to get the uio name associated with this this + * NIC library + * @param uio_name - This function will return the pointer to this NIC + * library's associated uio string + * @param transport_name_size - This will be set with the uio name size + */ +static void qedi_get_uio_name(char **uio_name, size_t *uio_name_size) +{ + *uio_name = (char *)library_uio_name; + *uio_name_size = sizeof(library_uio_name); +} + +/** + * qedi_get_ops() - Used to get the NIC library op table + * @param op - The op table of this NIC library + */ +struct nic_ops *qedi_get_ops() +{ + return &qedi_op; +} + +/******************************************************************************* + * qedi Utility Functions + ******************************************************************************/ +/******************************************************************************* + * Utility Functions Used to read register from the qedi device + ******************************************************************************/ +static void qedi_set_drv_version_unknown(qedi_t *bp) +{ + bp->version.major = QEDI_UNKNOWN_MAJOR_VERSION; + bp->version.minor = QEDI_UNKNOWN_MINOR_VERSION; + bp->version.sub_minor = QEDI_UNKNOWN_SUB_MINOR_VERSION; +} + +/* Return: 1 = Unknown, 0 = Known */ +static int qedi_is_drv_version_unknown(struct qedi_driver_version *version) +{ + if ((version->major == (uint16_t)QEDI_UNKNOWN_MAJOR_VERSION) && + (version->minor == (uint16_t)QEDI_UNKNOWN_MINOR_VERSION) && + (version->sub_minor == (uint16_t)QEDI_UNKNOWN_SUB_MINOR_VERSION)) { + return 1; + } + + return 0; +} + +/** + * qedi_get_drv_version() - Used to determine the driver version + * @param bp - Device used to determine qedi driver version + */ +static int qedi_get_drv_version(qedi_t *bp) +{ + nic_t *nic = bp->parent; + + /* + * CAPABILITIES: Get the iscsi driver version from qedi + * This may be obtained from sysfs + */ + LOG_INFO(PFX "%s: qedi driver using version %d.%d.%d", + nic->log_name, + bp->version.major, bp->version.minor, bp->version.sub_minor); + + return 0; +} + +/******************************************************************************/ + +/** + * qedi_get_chip_id() - Used to retrieve the chip ID from the nic + * @param dev - Device used to determin NIC type + * @return Chip ID read from the MISC ID register + */ +static int qedi_get_chip_id(qedi_t *bp) +{ + /* int val, id; */ + + /* Get the chip revision id and number. */ + /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ + /* + * CAPABILITIES: Get the CHIP info from qedi through sysfs or uio struct. + */ + return 0; +} + +/** + * qedi_uio_verify() + * + */ +static int qedi_uio_verify(nic_t *nic) +{ + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(cnic_uio_sysfs_name_tempate) + 8]; + int rc = 0; + + /* Build the path to determine uio name */ + snprintf(temp_path, sizeof(temp_path), + cnic_uio_sysfs_name_tempate, nic->uio_minor); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while (*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + if (strncmp(raw, qedi_uio_sysfs_name, + sizeof(qedi_uio_sysfs_name)) != 0) { + LOG_ERR(PFX "%s: uio names not equal: expecting %s got %s from %s", + nic->log_name, qedi_uio_sysfs_name, raw, temp_path); + rc = -EIO; + } + + free(raw); + + LOG_INFO(PFX "%s: Verified is a qedi_uio device", nic->log_name); + +error: + return rc; +} + +static int qedi_get_mac_addr(qedi_t *bp) +{ + nic_t *nic = bp->parent; + char *raw = NULL, *raw_tmp; + uint32_t raw_size = 0; + char temp_path[sizeof(qedi_host_mac_template) + 8]; + int rc = 0; + + /* Build the path to determine mac address */ + snprintf(temp_path, sizeof(temp_path), + qedi_host_mac_template, nic->host_no); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + /* sanitize name string by replacing newline with null termination */ + raw_tmp = raw; + while (*raw_tmp != '\n') + raw_tmp++; + *raw_tmp = '\0'; + + rc = sscanf(raw, "%02x:%02x:%02x:%02x:%02x:%02x", + (uint32_t *)&nic->mac_addr[0], (uint32_t *)&nic->mac_addr[1], + (uint32_t *)&nic->mac_addr[2], (uint32_t *)&nic->mac_addr[3], + (uint32_t *)&nic->mac_addr[4], (uint32_t *)&nic->mac_addr[5]); + if (rc != 1) { + LOG_WARN(PFX "%s: Could not parse mac_addr", + nic->log_name); + rc = -ENODEV; + goto error; + } + +error: + if (raw) + free(raw); + return rc; +} + +/******************************************************************************* + * qedi Utility Functions to get to the hardware consumer indexes + ******************************************************************************/ + +static __u32 qedi_get_rx(qedi_t *bp) +{ + return ((struct qedi_uio_ctrl *)bp->uctrl_map)->host_rx_cons; +} + +static __u32 qedi_get_tx(qedi_t *bp) +{ + return ((struct qedi_uio_ctrl *)bp->uctrl_map)->hw_tx_cons; +} + +/** + * qedi_free() - Used to free a qedi structure + */ +static void qedi_free(nic_t *nic) +{ + if (nic->priv) + free(nic->priv); + nic->priv = NULL; +} + +/** + * qedi_alloc() - Used to allocate a qedi structure + */ +static qedi_t *qedi_alloc(nic_t *nic) +{ + qedi_t *bp = malloc(sizeof(*bp)); + + if (!bp) { + LOG_ERR(PFX "%s: Could not allocate QEDI space", + nic->log_name); + return NULL; + } + + /* Clear out the CNIC contents */ + memset(bp, 0, sizeof(*bp)); + + bp->parent = nic; + nic->priv = (void *)bp; + get_iscsi_transport_handle(nic, &nic->transport_handle); + qedi_set_drv_version_unknown(bp); + + return bp; +} + +int uio_get_map_offset(nic_t *nic, uint8_t map, uint32_t *offset) +{ + char *raw = NULL; + uint32_t raw_size = 0; + ssize_t elements_read; + char temp_path[sizeof(UIO_OFFSET_TMPL) + 8]; + int rc = 0; + + /* Capture RX buffer size */ + snprintf(temp_path, sizeof(temp_path), + UIO_OFFSET_TMPL, nic->uio_minor, map); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + elements_read = sscanf(raw, "0x%x", offset); + if (elements_read != 1) { + LOG_ERR(PFX "%s: Couldn't get the offset from %s", + nic->log_name, temp_path); + rc = -EIO; + goto error; + } + + rc = 0; +error: + if (raw) + free(raw); + + return rc; +} + +int uio_get_map_info(nic_t *nic, uint8_t map, char *attr, uint32_t *val) +{ + char *raw = NULL; + uint32_t raw_size = 0; + ssize_t elements_read; + char temp_path[sizeof(UIO_ATTR_TMPL) + 8]; + int rc = 0; + + /* Capture RX buffer size */ + snprintf(temp_path, sizeof(temp_path), + UIO_ATTR_TMPL, nic->uio_minor, map, attr); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + elements_read = sscanf(raw, "0x%x", val); + if (elements_read != 1) { + LOG_ERR(PFX "%s: Couldn't get the offset from %s", + nic->log_name, temp_path); + rc = -EIO; + goto error; + } + + rc = 0; +error: + if (raw) + free(raw); + + return rc; +} + +/** + * qedi_open() - This will initialize all the hardware resources underneath + * a struct cnic_uio device + * @param dev - The struct cnic_uio device to attach the hardware with + * @return 0 on success, on failure a errno will be returned + */ +static int qedi_open(nic_t *nic) +{ + qedi_t *bp = NULL; + struct stat uio_stat; + int i, rc; + int count; + uint32_t bus; + uint32_t slot; + uint32_t func; + uint32_t offset; + + /* Sanity Check: validate the parameters */ + if (!nic) { + LOG_ERR(PFX "nic == NULL"); + return -EINVAL; + } + + if ((nic->priv) != NULL && + (((qedi_t *)(nic->priv))->flags & QEDI_OPENED)) { + return 0; + } + + if (nic->host_no == INVALID_HOST_NO) { + rc = sscanf(nic->config_device_name, "host%d", &nic->host_no); + if (rc != 1) { + LOG_WARN(PFX "%s: Could not parse for host number", + nic->config_device_name); + rc = -ENODEV; + goto open_error; + } + } + + bp = qedi_alloc(nic); + if (!bp) + return -ENOMEM; + + if (qedi_is_drv_version_unknown(&qedi_version)) { + /* If version is unknown, go read from ethtool */ + rc = qedi_get_drv_version(bp); + if (rc) + goto open_error; + } else { + /* Version is not unknown, just use it */ + qedi_version.major = bp->version.major; + qedi_version.minor = bp->version.minor; + qedi_version.sub_minor = bp->version.sub_minor; + } + + count = 0; + while ((nic->fd < 0) && count < 15) { + /* udev might not have created the file yet */ + pthread_mutex_unlock(&nic->nic_mutex); + sleep(1); + pthread_mutex_lock(&nic->nic_mutex); + + nic->fd = open(nic->uio_device_name, O_RDWR | O_NONBLOCK); + if (nic->fd != INVALID_FD) { + LOG_ERR(PFX "%s: uio device has been brought up via pid: %d on fd: %d", + nic->uio_device_name, getpid(), nic->fd); + + rc = qedi_uio_verify(nic); + if (rc != 0) + continue; + + break; + } else { + LOG_WARN(PFX "%s: Could not open device: %s, [%s]", + nic->log_name, nic->uio_device_name, + strerror(errno)); + + manually_trigger_uio_event(nic, nic->uio_minor); + + /* udev might not have created the file yet */ + pthread_mutex_unlock(&nic->nic_mutex); + sleep(1); + pthread_mutex_lock(&nic->nic_mutex); + + count++; + } + } + if (fstat(nic->fd, &uio_stat) < 0) { + LOG_ERR(PFX "%s: Could not fstat device", nic->log_name); + rc = -ENODEV; + goto open_error; + } + nic->uio_minor = minor(uio_stat.st_rdev); + + /* + * CAPABILITIES: acquire the rx buffer size and rx ring size from qedi + */ + + bp->rx_ring_size = RX_RING_SIZE; + bp->rx_buffer_size = PKT_BUF_SIZE; + + LOG_DEBUG(PFX "%s: using rx ring size: %d, rx buffer size: %d", + nic->log_name, bp->rx_ring_size, bp->rx_buffer_size); + + /* Determine the number of UIO events that have already occurred */ + rc = detemine_initial_uio_events(nic, &nic->intr_count); + if (rc != 0) { + LOG_ERR(PFX "Could not get the no. of initial UIO events"); + nic->intr_count = 0; + } + + /* Allocate space for rx pkt ring */ + bp->rx_pkt_ring = malloc(sizeof(void *) * bp->rx_ring_size); + if (!bp->rx_pkt_ring) { + LOG_ERR(PFX "%s: Could not allocate space for rx_pkt_ring", + nic->log_name); + rc = errno; + goto open_error; + } + + /* + * Map the uio struct and packet buffer + */ + offset = 0; + rc = uio_get_map_info(nic, QEDI_UCTRL_MAP_REG, "size", &offset); + if (rc) { + LOG_INFO(PFX "Failed to get the map size rc=%d", rc); + goto open_error; + } + LOG_INFO(PFX "uctrl map size=%u", offset); + + offset = 0; + rc = uio_get_map_info(nic, QEDI_RING_MAP_REG, "size", &offset); + if (rc) { + LOG_INFO(PFX "Failed to get the map size rc=%d", rc); + goto open_error; + } + LOG_INFO(PFX "ring map size=%u", offset); + + offset = 0; + rc = uio_get_map_info(nic, QEDI_BUF_MAP_REG, "size", &offset); + if (rc) { + LOG_INFO(PFX "Failed to get the map size rc=%d", rc); + goto open_error; + } + LOG_INFO(PFX "buf map size=%u", offset); + + offset = 0; + rc = uio_get_map_offset(nic, QEDI_UCTRL_MAP_REG, &offset); + if (rc) { + LOG_INFO(PFX "Failed to get the map offset rc=%d", rc); + goto open_error; + } + + bp->uctrl_map = mmap(NULL, sizeof(struct qedi_uio_ctrl), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_LOCKED, + nic->fd, (off_t)0); + if (bp->uctrl_map == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap uio ctrl struct: %s", + nic->log_name, strerror(errno)); + bp->uctrl_map = NULL; + rc = errno; + goto open_error; + } + + bp->uctrl_map_offset = offset; + bp->uctrl_map += offset; + + bp->rx_comp_ring = mmap(NULL, nic->page_size, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, + nic->fd, (off_t)nic->page_size); + if (bp->rx_comp_ring == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap rx_comp_ring: %s", + nic->log_name, strerror(errno)); + bp->rx_comp_ring = NULL; + rc = errno; + goto open_error; + } + + bp->bufs = mmap(NULL, (bp->rx_ring_size + 1) * bp->rx_buffer_size, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, + nic->fd, (off_t)2 * nic->page_size); + if (bp->bufs == MAP_FAILED) { + LOG_INFO(PFX "%s: Could not mmap pkt buffers: %s", + nic->log_name, strerror(errno)); + bp->bufs = NULL; + rc = errno; + goto open_error; + } + + /* + * Get all CHIP related info from qedi + */ + bp->chip_id = qedi_get_chip_id(bp); + LOG_DEBUG(PFX "Chip ID: %x", bp->chip_id); + + rc = get_bus_slot_func_num(nic, &bus, &slot, &func); + if (rc != 0) { + LOG_INFO(PFX "%s: Couldn't determine bus:slot.func", + nic->log_name); + goto open_error; + } + + /* + * Get all function, pfid, client_id and cid info from qedi + */ + LOG_INFO(PFX "%s: func 0x%x, pfid 0x%x, client_id 0x%x, cid 0x%x", + nic->log_name, bp->func, bp->pfid, bp->client_id, bp->cid); + + bp->get_rx_cons = qedi_get_rx; + bp->get_tx_cons = qedi_get_tx; + bp->tx_cons = 0; + bp->tx_prod = 0; + bp->tx_bd_prod = 0; + bp->tx_pkt = bp->bufs; + bp->rx_pkts = bp->bufs + bp->rx_buffer_size; + + bp->rx_index = 0; + bp->rx_cons = 0; + bp->rx_bd_cons = 0; + bp->rx_prod = 127; + bp->rx_bd_prod = bp->rx_ring_size; + + for (i = 0; i < bp->rx_ring_size; i++) { + void *ptr = bp->bufs + (bp->rx_buffer_size * (i + 1)); + + bp->rx_pkt_ring[i] = ptr; + } + + qedi_get_mac_addr(bp); + LOG_INFO(PFX "%s: Using mac address: %02x:%02x:%02x:%02x:%02x:%02x", + nic->log_name, + nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2], + nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]); + + qedi_get_library_name(&nic->library_name, (size_t *)&count); + LOG_INFO("%s: qedi initialized", nic->log_name); + + bp->flags |= QEDI_OPENED; + + return 0; + +open_error: + + if (bp->bufs) { + munmap(bp->bufs, (bp->rx_ring_size + 1) * bp->rx_buffer_size); + bp->bufs = NULL; + } + + if (bp->rx_comp_ring) { + munmap(bp->rx_comp_ring, nic->page_size); + bp->rx_comp_ring = NULL; + } + + if (bp->uctrl_map) { + bp->uctrl_map -= bp->uctrl_map_offset; + munmap(bp->uctrl_map, sizeof(struct qedi_uio_ctrl)); + bp->uctrl_map = NULL; + } + + if (bp->rx_pkt_ring) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + if (nic->fd != INVALID_FD) { + close(nic->fd); + nic->fd = INVALID_FD; + } + + qedi_free(nic); + + return rc; +} + +/** + * qedi_uio_close_resources() - Used to free resource for the NIC/CNIC + * @param nic - NIC device to free resource + * @param graceful - whether to wait to close gracefully + * @return 0 on success, <0 on failure + */ +static int qedi_uio_close_resources(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + qedi_t *bp = (qedi_t *)nic->priv; + int rc = 0; + + /* Check if there is an assoicated qedi device */ + if (!bp) { + LOG_WARN(PFX "%s: when closing resources there is no assoicated qedi", + nic->log_name); + return -EIO; + } + + /* Clean up allocated memory */ + + if (bp->rx_pkt_ring) { + free(bp->rx_pkt_ring); + bp->rx_pkt_ring = NULL; + } + + /* Clean up mapped registers */ + if (bp->bufs) { + rc = munmap(bp->bufs, + (bp->rx_ring_size + 1) * bp->rx_buffer_size); + if (rc != 0) + LOG_ERR(PFX "%s: Couldn't unmap bufs", nic->log_name); + bp->bufs = NULL; + } + + if (bp->rx_comp_ring) { + rc = munmap(bp->rx_comp_ring, nic->page_size); + if (rc != 0) + LOG_ERR(PFX "%s: Couldn't unmap ring", nic->log_name); + bp->rx_comp_ring = NULL; + } + + if (bp->uctrl_map) { + bp->uctrl_map -= bp->uctrl_map_offset; + rc = munmap(bp->uctrl_map, sizeof(struct qedi_uio_ctrl)); + if (rc != 0) { + LOG_ERR(PFX "%s: Couldn't unmap uio ctrl", + nic->log_name); + } + bp->uctrl_map = NULL; + } + + if (nic->fd != INVALID_FD) { + rc = close(nic->fd); + if (rc != 0) { + LOG_ERR(PFX + "%s: Couldn't close uio file descriptor: %d", + nic->log_name, nic->fd); + } else { + LOG_DEBUG(PFX "%s: Closed uio file descriptor: %d", + nic->log_name, nic->fd); + } + + nic->fd = INVALID_FD; + } else { + LOG_ERR(PFX "%s: Invalid uio file descriptor: %d", + nic->log_name, nic->fd); + } + + qedi_set_drv_version_unknown(bp); + + LOG_INFO(PFX "%s: Closed all resources", nic->log_name); + + return 0; +} + +/** + * qedi_close() - Used to close the NIC device + * @param nic - NIC device to close + * @param graceful - whether to wait to close gracefully + * @return 0 if successful, <0 if there is an error + */ +static int qedi_close(nic_t *nic, NIC_SHUTDOWN_T graceful) +{ + /* Sanity Check: validate the parameters */ + if (!nic) { + LOG_ERR(PFX "%s: nic == NULL", __func__); + return -EINVAL; + } + if (!nic->priv) { + LOG_ERR(PFX "%s: nic->priv == NULL", __func__); + return -EINVAL; + } + + LOG_INFO(PFX "Closing NIC device: %s", nic->log_name); + + qedi_uio_close_resources(nic, graceful); + qedi_free(nic); + + return 0; +} + +static void qedi_prepare_xmit_packet(nic_t *nic, + nic_interface_t *nic_iface, + struct packet *pkt) +{ + qedi_t *bp = (qedi_t *)nic->priv; + struct uip_vlan_eth_hdr *eth_vlan = (struct uip_vlan_eth_hdr *)pkt->buf; + struct uip_eth_hdr *eth = (struct uip_eth_hdr *)bp->tx_pkt; + + if (eth_vlan->tpid == htons(UIP_ETHTYPE_8021Q)) { + memcpy(bp->tx_pkt, pkt->buf, sizeof(struct uip_eth_hdr)); + eth->type = eth_vlan->type; + pkt->buf_size -= (sizeof(struct uip_vlan_eth_hdr) - + sizeof(struct uip_eth_hdr)); + memcpy(bp->tx_pkt + sizeof(struct uip_eth_hdr), + pkt->buf + sizeof(struct uip_vlan_eth_hdr), + pkt->buf_size - sizeof(struct uip_eth_hdr)); + } else { + memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size); + } + + msync(bp->tx_pkt, pkt->buf_size, MS_SYNC); +} + +/** + * qedi_get_tx_pkt() - This function is used to a TX packet from the NIC + * @param nic - The NIC device to send the packet + */ +void *qedi_get_tx_pkt(nic_t *nic) +{ + qedi_t *bp = (qedi_t *)nic->priv; + + return bp->tx_pkt; +} + +/** + * qedi_start_xmit() - This function is used to send a packet of data + * @param nic - The NIC device to send the packet + * @param len - the length of the TX packet + * + */ +void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id) +{ + qedi_t *bp = (qedi_t *)nic->priv; + uint8_t *ubuf; + struct iscsi_uevent *ev; + struct iscsi_path *path_data; + struct qedi_uio_ctrl *uctrl; + int rc = 0; + uint16_t buflen; + + uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map; + + buflen = sizeof(struct iscsi_uevent) + sizeof(struct iscsi_path); + ubuf = calloc(1, NLMSG_SPACE(buflen)); + if (!ubuf) { + LOG_ERR(PFX "%s: alloc failed for uevent buf", __func__); + return; + } + + memset(ubuf, 0, NLMSG_SPACE(buflen)); + + /* prepare the iscsi_uevent buffer */ + ev = (struct iscsi_uevent *)ubuf; + ev->type = ISCSI_UEVENT_PATH_UPDATE; + ev->transport_handle = nic->transport_handle; + ev->u.set_path.host_no = nic->host_no; + + /* Prepare the iscsi_path buffer */ + path_data = (struct iscsi_path *)(ubuf + sizeof(struct iscsi_uevent)); + path_data->handle = QEDI_PATH_HANDLE; + path_data->vlan_id = vlan_id; + uctrl->host_tx_pkt_len = len; + + rc = __kipc_call(nl_sock, ev, buflen); + if (rc > 0) { + bp->tx_prod++; + uctrl->host_tx_prod++; + msync(uctrl, sizeof(struct qedi_uio_ctrl), MS_SYNC); + LOG_PACKET(PFX "%s: sent %d bytes using bp->tx_prod: %d", + nic->log_name, len, bp->tx_prod); + } else { + LOG_ERR(PFX "Pkt transmission failed: %d", rc); + pthread_mutex_unlock(&nic->xmit_mutex); + } + + free(ubuf); +} + +/** + * qedi_write() - Used to write the data to the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data to be sent on the wire + * @return 0 if successful, <0 if failed + */ +int qedi_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt) +{ + qedi_t *bp; + struct uip_stack *uip; + int i = 0; + + /* Sanity Check: validate the parameters */ + if (!nic || !nic_iface || !pkt) { + LOG_ERR(PFX "%s: qedi_write() nic == 0x%p || nic_iface == 0x%p || pkt == 0x%x", + nic, nic_iface, pkt); + return -EINVAL; + } + bp = (qedi_t *)nic->priv; + uip = &nic_iface->ustack; + + if (pkt->buf_size == 0) { + LOG_ERR(PFX "%s: Trying to transmitted 0 sized packet", + nic->log_name); + return -EINVAL; + } + + /* Try to wait for a TX completion */ + for (i = 0; i < 15; i++) { + struct timespec sleep_req = {.tv_sec = 0, .tv_nsec = 5000000 }, + sleep_rem; + + if (qedi_clear_tx_intr(nic) == 0) + break; + + nanosleep(&sleep_req, &sleep_rem); + } + + if (pthread_mutex_trylock(&nic->xmit_mutex) != 0) { + LOG_PACKET(PFX "%s: Dropped previous transmitted packet", + nic->log_name); + return -EINVAL; + } + + qedi_prepare_xmit_packet(nic, nic_iface, pkt); + qedi_start_xmit(nic, pkt->buf_size, + (nic_iface->vlan_priority << 12) | + nic_iface->vlan_id); + + /* bump up the tx stats */ + nic->stats.tx.packets++; + nic->stats.tx.bytes += uip->uip_len; + + LOG_PACKET(PFX "%s: transmitted %d bytes dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + + return 0; +} + +/** + * qedi_read() - Used to read the data from the hardware + * @param nic - NIC hardware to read from + * @param pkt - The packet which will hold the data + * @return 0 if successful, < 0 if failed + */ +static int qedi_read(nic_t *nic, packet_t *pkt) +{ + qedi_t *bp; + void *rx_pkt; + int rc = 0; + uint32_t sw_cons, bd_cons; + uint32_t hw_prod; + uint32_t rx_pkt_idx; + int len; + struct qedi_rx_bd *rx_bd; + struct qedi_uio_ctrl *uctrl; + uint16_t vlan_id; + + /* Sanity Check: validate the parameters */ + if (!nic || !pkt) { + LOG_ERR(PFX "%s: qedi_read() nic == 0x%p || pkt == 0x%x", + nic, pkt); + return -EINVAL; + } + + bp = (qedi_t *)nic->priv; + msync(bp->uctrl_map, sizeof(struct qedi_uio_ctrl), MS_SYNC); + msync(bp->rx_comp_ring, nic->page_size, MS_SYNC); + uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map; + hw_prod = uctrl->hw_rx_prod; + sw_cons = uctrl->host_rx_cons; + bd_cons = uctrl->host_rx_bd_cons; + rx_bd = bp->rx_comp_ring + (bd_cons * sizeof(*rx_bd)); + len = rx_bd->rx_pkt_len; + rx_pkt_idx = rx_bd->rx_pkt_index; + vlan_id = rx_bd->vlan_id; + + if (sw_cons != hw_prod) { + LOG_DEBUG(PFX "%s: clearing rx interrupt: %d %d", + nic->log_name, sw_cons, hw_prod); + rc = 1; + rx_pkt = bp->rx_pkts + (bp->rx_buffer_size * rx_pkt_idx); + + if (len > 0) { + msync(rx_pkt, len, MS_SYNC); + /* Copy the data */ + memcpy(pkt->buf, rx_pkt, len); + pkt->buf_size = len; + if (vlan_id) { + pkt->vlan_tag = vlan_id; + pkt->flags |= VLAN_TAGGED; + } else { + pkt->vlan_tag = 0; + } + + LOG_DEBUG(PFX "%s: processing packet length: %d", + nic->log_name, len); + + /* bump up the recv stats */ + nic->stats.rx.packets++; + nic->stats.rx.bytes += pkt->buf_size; + } else { + rc = 0; + } + + sw_cons = (sw_cons + 1) % RX_RING_SIZE; + bd_cons = (bd_cons + 1) % QEDI_NUM_RX_BD; + uctrl->host_rx_cons_cnt++; + } + + uctrl->host_rx_bd_cons = bd_cons; + uctrl->host_rx_cons = sw_cons; + + msync(uctrl, sizeof(struct qedi_uio_ctrl), MS_SYNC); + msync(bp->rx_comp_ring, nic->page_size, MS_SYNC); + return rc; +} + +/******************************************************************************* + * Clearing TX interrupts + ******************************************************************************/ +/** + * qedi_clear_tx_intr() - This routine is called when a TX interrupt occurs + * @param nic - the nic the interrupt occurred on + * @return 0 on success + */ + +static int qedi_clear_tx_intr(nic_t *nic) +{ + qedi_t *bp; + uint32_t hw_cons; + struct qedi_uio_ctrl *uctrl; + + /* Sanity check: ensure the parameters passed in are valid */ + if (unlikely(!nic)) { + LOG_ERR(PFX "%s: nic == NULL", __func__); + return -EINVAL; + } + + bp = (qedi_t *)nic->priv; + uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map; + msync(bp->uctrl_map, sizeof(struct qedi_uio_ctrl), MS_SYNC); + hw_cons = uctrl->hw_tx_cons; + + if (bp->tx_cons == hw_cons) { + if (bp->tx_cons == bp->tx_prod) { + /* Make sure the xmit_mutex lock is unlock */ + if (pthread_mutex_trylock(&nic->xmit_mutex)) + LOG_ERR(PFX "qedi tx lock with prod == cons"); + + pthread_mutex_unlock(&nic->xmit_mutex); + return 0; + } + return -EAGAIN; + } + + LOG_PACKET(PFX "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; + + /* There is a queued TX packet that needs to be sent out. The usual + * case is when stack will send an ARP packet out before sending the + * intended packet + */ + if (nic->tx_packet_queue) { + packet_t *pkt; + int i; + + LOG_PACKET(PFX "%s: sending queued tx packet", nic->log_name); + pkt = nic_dequeue_tx_packet(nic); + + /* Got a TX packet buffer of the TX queue and put it onto + * the hardware + */ + if (pkt) { + qedi_prepare_xmit_packet(nic, pkt->nic_iface, pkt); + + qedi_start_xmit(nic, pkt->buf_size, + (pkt->nic_iface->vlan_priority << 12) | + pkt->nic_iface->vlan_id); + + LOG_PACKET(PFX "%s: transmitted queued packet %d bytes, dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d", + nic->log_name, pkt->buf_size, + bp->tx_cons, bp->tx_prod, bp->tx_bd_prod); + + return 0; + } + + /* Try to wait for a TX completion */ + for (i = 0; i < 15; i++) { + struct timespec sleep_req = {.tv_sec = 0, + .tv_nsec = 5000000 + }, sleep_rem; + + hw_cons = uctrl->hw_tx_cons; + if (bp->tx_cons != hw_cons) { + LOG_PACKET(PFX + "%s: clearing tx interrupt [%d %d]", + nic->log_name, bp->tx_cons, hw_cons); + bp->tx_cons = hw_cons; + + break; + } + + nanosleep(&sleep_req, &sleep_rem); + } + } + + pthread_mutex_unlock(&nic->xmit_mutex); + + return 0; +} + +/******************************************************************************* + * qedi NIC op's table + ******************************************************************************/ +struct nic_ops qedi_op = { + .description = "qedi", + .open = qedi_open, + .close = qedi_close, + .write = qedi_write, + .get_tx_pkt = qedi_get_tx_pkt, + .start_xmit = qedi_start_xmit, + .read = qedi_read, + .clear_tx_intr = qedi_clear_tx_intr, + .handle_iscsi_path_req = cnic_handle_iscsi_path_req, + + .lib_ops = { + .get_library_name = qedi_get_library_name, + .get_library_version = qedi_get_library_version, + .get_build_date = qedi_get_build_date, + .get_transport_name = qedi_get_transport_name, + .get_uio_name = qedi_get_uio_name, + }, +}; diff --git a/iscsiuio/src/unix/libs/qedi.h b/iscsiuio/src/unix/libs/qedi.h new file mode 100644 index 000000000000..7e0140adb2ba --- /dev/null +++ b/iscsiuio/src/unix/libs/qedi.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016, Cavium Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Dunkels. + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * qedi.h - qedi user space driver + * + */ +#ifndef __QEDI_H__ +#define __QEDI_H__ + +#include "nic.h" + +#define RX_RING_SIZE 15 +#define PKT_BUF_SIZE 0X400 +#define QEDI_PAGE_SIZE 4096 + +#define QEDI_UNKNOWN_MAJOR_VERSION -1 +#define QEDI_UNKNOWN_MINOR_VERSION -1 +#define QEDI_UNKNOWN_SUB_MINOR_VERSION -1 +struct qedi_driver_version { + uint16_t major; + uint16_t minor; + uint16_t sub_minor; +}; + +#define QEDI_UCTRL_MAP_REG 0 +#define QEDI_RING_MAP_REG 1 +#define QEDI_BUF_MAP_REG 2 +#define UIO_ATTR_TMPL "/sys/class/uio/uio%u/maps/map%u/%s" +#define UIO_ADDR_TMPL "/sys/class/uio/uio%u/maps/map%u/addr" +#define UIO_OFFSET_TMPL "/sys/class/uio/uio%u/maps/map%u/offset" +#define UIO_SIZE_TMPL "/sys/class/uio/uio%u/maps/map%u/size" + +struct qedi_uio_ctrl { + /* meta data */ + __u32 uio_hsi_version; + + /* user writes */ + __u32 host_tx_prod; + __u32 host_rx_cons; + __u32 host_rx_bd_cons; + __u32 host_tx_pkt_len; + __u32 host_rx_cons_cnt; + + /* driver writes */ + __u32 hw_tx_cons; + __u32 hw_rx_prod; + __u32 hw_rx_bd_prod; + __u32 hw_rx_prod_cnt; + + /* other */ + __u8 mac_addr[6]; + __u8 reserve[2]; +}; + +struct qedi_rx_bd { + __u32 rx_pkt_index; + __u32 rx_pkt_len; + __u16 vlan_id; +}; + +#define QEDI_RX_DESC_CNT (QEDI_PAGE_SIZE / sizeof(struct qedi_rx_bd)) +#define QEDI_MAX_RX_DESC_CNT (QEDI_RX_DESC_CNT - 1) +#define QEDI_NUM_RX_BD (QEDI_RX_DESC_CNT * 1) +#define QEDI_MAX_RX_BD (QEDI_NUM_RX_BD - 1) + +#define QEDI_NEXT_RX_IDX(x) ((((x) & (QEDI_MAX_RX_DESC_CNT)) == \ + (QEDI_MAX_RX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1) + +#define QEDI_PATH_HANDLE 0xFE0000000 + +typedef struct qedi { + nic_t *parent; + + struct qedi_driver_version version; + + uint16_t flags; +#define CNIC_UIO_UNITIALIZED 0x0001 +#define CNIC_UIO_INITIALIZED 0x0002 +#define CNIC_UIO_ENABLED 0x0004 +#define CNIC_UIO_DISABLED 0x0008 +#define CNIC_UIO_IPv6_ENABLED 0x0010 +#define CNIC_UIO_ADDED_MULICAST 0x0020 +#define CNIC_UIO_MSIX_ENABLED 0x0200 +#define CNIC_UIO_TX_HAS_SENT 0x0400 +#define QEDI_OPENED 0x0800 + + __u32 chip_id; + int func; + int port; + int pfid; + __u32 cid; + __u32 client_id; + + __u32 tx_prod; + __u32 tx_bd_prod; + __u32 tx_cons; + __u8 tx_vlan_tag_bit; + + __u32 rx_prod; + __u32 rx_bd_prod; + __u32 rx_cons; + __u32 rx_bd_cons; + __u32 rx_hw_prod; + + __u32 (*get_rx_cons)(struct qedi *); + __u32 (*get_tx_cons)(struct qedi *); + + /* RX ring parameters */ + uint32_t rx_ring_size; + uint32_t rx_buffer_size; + + void *bufs; /* Pointer to the mapped buffer space */ + void *uctrl_map; /* UIO control structure */ + uint32_t uctrl_map_offset; /* UIO control structure mmap offset */ + + uint32_t rx_index; + void *rx_comp_ring; + void **rx_pkt_ring; + void *tx_pkt; + void *rx_pkts; +} qedi_t; + +/****************************************************************************** + * qedi Function Declarations + ******************************************************************************/ +void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id); +struct nic_ops *qedi_get_ops(); + +#endif /* __QEDI_H__ */ diff --git a/iscsiuio/src/unix/nic.c b/iscsiuio/src/unix/nic.c index 8825f00b7216..a5f714a91923 100644 --- a/iscsiuio/src/unix/nic.c +++ b/iscsiuio/src/unix/nic.c @@ -65,6 +65,7 @@ #include "bnx2.h" #include "bnx2x.h" +#include "qedi.h" #include "ipv6.h" /****************************************************************************** @@ -180,7 +181,7 @@ error: } static struct nic_ops *(*nic_get_ops[]) () = { -bnx2_get_ops, bnx2x_get_ops,}; +bnx2_get_ops, bnx2x_get_ops, qedi_get_ops}; int load_all_nic_libraries() { @@ -404,6 +405,7 @@ nic_t *nic_init() memset(nic, 0, sizeof(*nic)); nic->uio_minor = -1; nic->fd = INVALID_FD; + nic->host_no = INVALID_HOST_NO; nic->next = NULL; nic->thread = INVALID_THREAD; nic->enable_thread = INVALID_THREAD; @@ -1070,6 +1072,8 @@ int process_packets(nic_t *nic, } nic_iface_present: pkt->nic_iface = nic_iface; + LOG_DEBUG(PFX "%s: found nic iface, type=0x%x, bufsize=%d", + nic->log_name, type, pkt->buf_size); ustack = &nic_iface->ustack; diff --git a/iscsiuio/src/unix/nic.h b/iscsiuio/src/unix/nic.h index ec157815a375..2c41e6e3c25a 100644 --- a/iscsiuio/src/unix/nic.h +++ b/iscsiuio/src/unix/nic.h @@ -334,6 +334,7 @@ typedef struct nic { /* The thread used to perform ping */ pthread_t ping_thread; + uint64_t transport_handle; } nic_t; /****************************************************************************** diff --git a/iscsiuio/src/unix/nic_utils.c b/iscsiuio/src/unix/nic_utils.c index 0daffd2fcb6d..6e580f862eea 100644 --- a/iscsiuio/src/unix/nic_utils.c +++ b/iscsiuio/src/unix/nic_utils.c @@ -81,6 +81,10 @@ static const char iscsi_host_path_netdev_template[] = "/sys/class/iscsi_host/host%d/netdev"; static const char cnic_uio_sysfs_resc_template[] = "/sys/class/uio/uio%i/device/resource%i"; +static const char iscsi_transport_handle_template[] = + "/sys/class/iscsi_transport/%s/handle"; +static const char eth_pfx[] = "eth"; +static const char host_pfx[] = "host"; /** * manually_trigger_uio_event() - If the uio file node doesn't exist then @@ -250,6 +254,7 @@ int nic_discover_iscsi_hosts() strncpy(nic->eth_device_name, raw, raw_size); nic->config_device_name = nic->eth_device_name; nic->log_name = nic->eth_device_name; + nic->host_no = host_no; if (nic_fill_name(nic) != 0) { free(nic); @@ -557,6 +562,102 @@ error: } /** + * from_uio_find_associated_host() - Given the uio minor number + * this function will try to find the assoicated iscsi host + * @param uio_minor - minor number of the UIO device + * @param name - char buffer which will be filled if successful + * @param name_size - size of the name buffer + * @return >0 minor number <0 an error + */ +static int from_uio_find_associated_host(nic_t *nic, int uio_minor, + char *name, size_t name_size) +{ + char *path; + int rc; + int count; + struct dirent **files; + char *parsed_name; + int i; + int path_iterator; + char *search_paths[] = { "/sys/class/uio/uio%i/device/" }; + int path_to[] = { 5, 1 }; + int (*search_filters[]) (const struct dirent *) = { filter_host_name, }; + char *(*extract_name[]) (struct dirent **files) = { extract_none, }; + int extract_name_offset[] = { 0 }; + + path = malloc(PATH_MAX); + if (!path) { + LOG_ERR(PFX "Could not allocate memory for path"); + rc = -ENOMEM; + goto error; + } + + for (path_iterator = 0; + path_iterator < sizeof(search_paths) / sizeof(search_paths[0]); + path_iterator++) { + /* Build the path to determine uio name */ + rc = sprintf(path, search_paths[path_iterator], uio_minor); + + wait_for_file_node_timed(nic, path, path_to[path_iterator]); + + count = scandir(path, &files, + search_filters[path_iterator], alphasort); + + switch (count) { + case 1: + parsed_name = (*extract_name[path_iterator]) (files); + if (!parsed_name) { + LOG_WARN(PFX "Couldn't find delimiter in: %s", + files[0]->d_name); + + break; + } + + strncpy(name, + parsed_name + + extract_name_offset[path_iterator], name_size); + + free(files[0]); + free(files); + + rc = 0; + break; + + case 0: + rc = -EINVAL; + break; + + case -1: + LOG_WARN(PFX "Error when scanning path: %s[%s]", + path, strerror(errno)); + rc = -EINVAL; + break; + + default: + LOG_WARN(PFX + "Too many entries when looking for device: %s", + path); + + /* Cleanup the scandir() call */ + for (i = 0; i < count; i++) + free(files[i]); + free(files); + + rc = -EINVAL; + break; + } + + if (rc == 0) + break; + } + +error: + free(path); + + return rc; +} + +/** * filter_uio_name() - This is the callback used by scandir when looking for * the number of uio entries */ @@ -652,10 +753,16 @@ int from_phys_name_find_assoicated_uio_device(nic_t *nic) continue; } - rc = from_uio_find_associated_eth_device(nic, - uio_minor, - eth_name, - sizeof(eth_name)); + if (!memcmp(host_pfx, nic->config_device_name, + strlen(host_pfx))) { + rc = from_uio_find_associated_host(nic, uio_minor, + eth_name, + sizeof(eth_name)); + } else { + rc = from_uio_find_associated_eth_device(nic, uio_minor, + eth_name, + sizeof(eth_name)); + } if (rc != 0) { LOG_WARN("uio minor: %d not valid [%D]", uio_minor, rc); continue; @@ -1124,6 +1231,38 @@ int detemine_initial_uio_events(nic_t *nic, uint32_t *num_of_events) rc = 0; error: + if (raw) + free(raw); + + return rc; +} + +int get_iscsi_transport_handle(nic_t *nic, uint64_t *handle) +{ + char *raw = NULL; + uint32_t raw_size = 0; + ssize_t elements_read; + char temp_path[sizeof(iscsi_transport_handle_template) + 8]; + int rc; + + /* Capture RX buffer size */ + snprintf(temp_path, sizeof(temp_path), + iscsi_transport_handle_template, nic->library_name); + + rc = capture_file(&raw, &raw_size, temp_path); + if (rc != 0) + goto error; + + elements_read = sscanf(raw, "%lu", handle); + if (elements_read != 1) { + LOG_ERR(PFX "%s: Couldn't parse transport handle from %s", + nic->log_name, temp_path); + rc = -EIO; + goto error; + } + + rc = 0; +error: if (raw != NULL) free(raw); diff --git a/iscsiuio/src/unix/nic_utils.h b/iscsiuio/src/unix/nic_utils.h index d5c1b580f340..e4cf5c13177e 100644 --- a/iscsiuio/src/unix/nic_utils.h +++ b/iscsiuio/src/unix/nic_utils.h @@ -99,4 +99,6 @@ void dump_packet_to_log(struct nic_interface *iface, int determine_file_size_read(const char *filepath); int capture_file(char **raw, uint32_t *raw_size, const char *path); +int get_iscsi_transport_handle(nic_t *nic, uint64_t *handle); + #endif /* __NIC_UTILS_H__ */ diff --git a/iscsiuio/src/unix/options.h b/iscsiuio/src/unix/options.h index df03255f7a87..63b86357aafa 100644 --- a/iscsiuio/src/unix/options.h +++ b/iscsiuio/src/unix/options.h @@ -87,6 +87,7 @@ #define INVALID_FD -1 #define INVALID_THREAD -1 +#define INVALID_HOST_NO -1 struct options { char debug; -- 2.9.3