|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* Copyright (c) 2016, Cavium Inc.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* All rights reserved.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* Redistribution and use in source and binary forms, with or without
|
|
Packit |
eace71 |
* modification, are permitted provided that the following conditions
|
|
Packit |
eace71 |
* are met:
|
|
Packit |
eace71 |
* 1. Redistributions of source code must retain the above copyright
|
|
Packit |
eace71 |
* notice, this list of conditions and the following disclaimer.
|
|
Packit |
eace71 |
* 2. Redistributions in binary form must reproduce the above copyright
|
|
Packit |
eace71 |
* notice, this list of conditions and the following disclaimer in the
|
|
Packit |
eace71 |
* documentation and/or other materials provided with the distribution.
|
|
Packit |
eace71 |
* 3. All advertising materials mentioning features or use of this software
|
|
Packit |
eace71 |
* must display the following acknowledgement:
|
|
Packit |
eace71 |
* This product includes software developed by Adam Dunkels.
|
|
Packit |
eace71 |
* 4. The name of the author may not be used to endorse or promote
|
|
Packit |
eace71 |
* products derived from this software without specific prior
|
|
Packit |
eace71 |
* written permission.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
Packit |
eace71 |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
Packit |
eace71 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
eace71 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
Packit |
eace71 |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
Packit |
eace71 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
Packit |
eace71 |
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
Packit |
eace71 |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
Packit |
eace71 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
Packit |
eace71 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
Packit |
eace71 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* qedi.c - qedi user space driver
|
|
Packit |
eace71 |
* This file handles different qedi NIC operations,
|
|
Packit |
eace71 |
* qedi_open - initializes all hardware resources under NIC device
|
|
Packit |
eace71 |
* qedi_close - closes the NIC device
|
|
Packit |
eace71 |
* qedi_read - reads data to the hardware
|
|
Packit |
eace71 |
* qedi_write - writes data to the hardware
|
|
Packit |
eace71 |
* qedi_start_xmit - sends a pkt of data on NIC device
|
|
Packit |
eace71 |
* qedi_get_tx_pkt - gets a Tx pkt from NIC
|
|
Packit |
eace71 |
* qedi_clear_tx_intr - clears the Tx interrupt
|
|
Packit |
eace71 |
* NOTE: nic_t is used as NIC device,
|
|
Packit |
eace71 |
* qedi is not attached to netdev hence it is not mandatory
|
|
Packit |
eace71 |
* for netdev to be upd
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
#include <errno.h>
|
|
Packit |
eace71 |
#include <stdio.h>
|
|
Packit |
eace71 |
#include <string.h>
|
|
Packit |
eace71 |
#include <arpa/inet.h>
|
|
Packit |
eace71 |
#include <linux/types.h>
|
|
Packit |
eace71 |
#include <linux/sockios.h>
|
|
Packit |
eace71 |
#include <linux/netlink.h>
|
|
Packit |
eace71 |
#include <sys/mman.h>
|
|
Packit |
eace71 |
#include <sys/ioctl.h>
|
|
Packit |
eace71 |
#include <sys/types.h>
|
|
Packit |
eace71 |
#include <sys/stat.h>
|
|
Packit |
eace71 |
#include <sys/user.h>
|
|
Packit |
eace71 |
#include <fcntl.h>
|
|
Packit |
eace71 |
#include <unistd.h>
|
|
Packit |
eace71 |
#include <sys/sysmacros.h>
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
#include "config.h"
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
#include "build_date.h"
|
|
Packit |
eace71 |
#include "bnx2x.h"
|
|
Packit |
eace71 |
#include "qedi.h"
|
|
Packit |
eace71 |
#include "cnic.h"
|
|
Packit |
eace71 |
#include "logger.h"
|
|
Packit |
eace71 |
#include "nic.h"
|
|
Packit |
eace71 |
#include "nic_id.h"
|
|
Packit |
eace71 |
#include "nic_utils.h"
|
|
Packit |
eace71 |
#include "options.h"
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
#define PFX "qedi "
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
extern int nl_sock;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static pthread_mutex_t host_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Foward struct declarations */
|
|
Packit |
eace71 |
struct nic_ops qedi_op;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*******************************************************************************
|
|
Packit |
eace71 |
* NIC Library Strings
|
|
Packit |
eace71 |
******************************************************************************/
|
|
Packit |
eace71 |
static const char library_name[] = "qedi";
|
|
Packit |
eace71 |
static const char library_version[] = PACKAGE_VERSION;
|
|
Packit |
eace71 |
static const char library_uio_name[] = "qedi_uio";
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* The name that should be returned from /sys/class/uio/uio0/name */
|
|
Packit |
eace71 |
static const char cnic_uio_sysfs_name_tempate[] = "/sys/class/uio/uio%i/name";
|
|
Packit |
eace71 |
static const char qedi_uio_sysfs_name[] = "qedi_uio";
|
|
Packit |
eace71 |
static const char qedi_host_mac_template[] =
|
|
Packit |
eace71 |
"/sys/class/iscsi_host/host%i/hwaddress";
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
struct qedi_driver_version qedi_version = {
|
|
Packit |
eace71 |
QEDI_UNKNOWN_MAJOR_VERSION,
|
|
Packit |
eace71 |
QEDI_UNKNOWN_MINOR_VERSION,
|
|
Packit |
eace71 |
QEDI_UNKNOWN_SUB_MINOR_VERSION,
|
|
Packit |
eace71 |
};
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int qedi_clear_tx_intr(nic_t *nic);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*******************************************************************************
|
|
Packit |
eace71 |
* QEDI Library Functions
|
|
Packit |
eace71 |
******************************************************************************/
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_library_name() - Used to get the name of this NIC library
|
|
Packit |
eace71 |
* @param name - This function will return the pointer to this NIC
|
|
Packit |
eace71 |
* library name
|
|
Packit |
eace71 |
* @param name_size
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static void qedi_get_library_name(char **name, size_t *name_size)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
*name = (char *)library_name;
|
|
Packit |
eace71 |
*name_size = sizeof(library_name);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_library_version() - Used to get the version string of this
|
|
Packit |
eace71 |
* NIC library
|
|
Packit |
eace71 |
* @param version - This function will return the pointer to this NIC
|
|
Packit |
eace71 |
* library version string
|
|
Packit |
eace71 |
* @param version_size - This will be set with the version size
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static void qedi_get_library_version(char **version, size_t *version_size)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
*version = (char *)library_version;
|
|
Packit |
eace71 |
*version_size = sizeof(library_version);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_build_date() - Used to get the build date string of this library
|
|
Packit |
eace71 |
* @param version - This function will return the pointer to this NIC
|
|
Packit |
eace71 |
* library build date string
|
|
Packit |
eace71 |
* @param version_size - This will be set with the build date string size
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static void qedi_get_build_date(char **build, size_t *build_size)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
*build = (char *)build_date;
|
|
Packit |
eace71 |
*build_size = sizeof(build_date);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_transport_name() - Used to get the transport name associated
|
|
Packit |
eace71 |
* with this this NIC library
|
|
Packit |
eace71 |
* @param transport_name - This function will return the pointer to this NIC
|
|
Packit |
eace71 |
* library's associated transport string
|
|
Packit |
eace71 |
* @param transport_name_size - This will be set with the transport name size
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static void qedi_get_transport_name(char **transport_name,
|
|
Packit |
eace71 |
size_t *transport_name_size)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
*transport_name = (char *)qedi_library_transport_name;
|
|
Packit |
eace71 |
*transport_name_size = qedi_library_transport_name_size;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_uio_name() - Used to get the uio name associated with this this
|
|
Packit |
eace71 |
* NIC library
|
|
Packit |
eace71 |
* @param uio_name - This function will return the pointer to this NIC
|
|
Packit |
eace71 |
* library's associated uio string
|
|
Packit |
eace71 |
* @param transport_name_size - This will be set with the uio name size
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static void qedi_get_uio_name(char **uio_name, size_t *uio_name_size)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
*uio_name = (char *)library_uio_name;
|
|
Packit |
eace71 |
*uio_name_size = sizeof(library_uio_name);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_ops() - Used to get the NIC library op table
|
|
Packit |
eace71 |
* @param op - The op table of this NIC library
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
struct nic_ops *qedi_get_ops()
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
return &qedi_op;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*******************************************************************************
|
|
Packit |
eace71 |
* qedi Utility Functions
|
|
Packit |
eace71 |
******************************************************************************/
|
|
Packit |
eace71 |
/*******************************************************************************
|
|
Packit |
eace71 |
* Utility Functions Used to read register from the qedi device
|
|
Packit |
eace71 |
******************************************************************************/
|
|
Packit |
eace71 |
static void qedi_set_drv_version_unknown(qedi_t *bp)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
bp->version.major = QEDI_UNKNOWN_MAJOR_VERSION;
|
|
Packit |
eace71 |
bp->version.minor = QEDI_UNKNOWN_MINOR_VERSION;
|
|
Packit |
eace71 |
bp->version.sub_minor = QEDI_UNKNOWN_SUB_MINOR_VERSION;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Return: 1 = Unknown, 0 = Known */
|
|
Packit |
eace71 |
static int qedi_is_drv_version_unknown(struct qedi_driver_version *version)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
if ((version->major == (uint16_t)QEDI_UNKNOWN_MAJOR_VERSION) &&
|
|
Packit |
eace71 |
(version->minor == (uint16_t)QEDI_UNKNOWN_MINOR_VERSION) &&
|
|
Packit |
eace71 |
(version->sub_minor == (uint16_t)QEDI_UNKNOWN_SUB_MINOR_VERSION)) {
|
|
Packit |
eace71 |
return 1;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_drv_version() - Used to determine the driver version
|
|
Packit |
eace71 |
* @param bp - Device used to determine qedi driver version
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static int qedi_get_drv_version(qedi_t *bp)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
nic_t *nic = bp->parent;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* CAPABILITIES: Get the iscsi driver version from qedi
|
|
Packit |
eace71 |
* This may be obtained from sysfs
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: qedi driver using version %d.%d.%d",
|
|
Packit |
eace71 |
nic->log_name,
|
|
Packit |
eace71 |
bp->version.major, bp->version.minor, bp->version.sub_minor);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/******************************************************************************/
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_chip_id() - Used to retrieve the chip ID from the nic
|
|
Packit |
eace71 |
* @param dev - Device used to determin NIC type
|
|
Packit |
eace71 |
* @return Chip ID read from the MISC ID register
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static int qedi_get_chip_id(qedi_t *bp)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
/* int val, id; */
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Get the chip revision id and number. */
|
|
Packit |
eace71 |
/* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* CAPABILITIES: Get the CHIP info from qedi through sysfs or uio struct.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_uio_verify()
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static int qedi_uio_verify(nic_t *nic)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *raw = NULL, *raw_tmp;
|
|
Packit |
eace71 |
uint32_t raw_size = 0;
|
|
Packit |
eace71 |
char temp_path[sizeof(cnic_uio_sysfs_name_tempate) + 8];
|
|
Packit |
eace71 |
int rc = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Build the path to determine uio name */
|
|
Packit |
eace71 |
snprintf(temp_path, sizeof(temp_path),
|
|
Packit |
eace71 |
cnic_uio_sysfs_name_tempate, nic->uio_minor);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = capture_file(&raw, &raw_size, temp_path);
|
|
Packit |
eace71 |
if (rc != 0)
|
|
Packit |
eace71 |
goto error;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* sanitize name string by replacing newline with null termination */
|
|
Packit |
eace71 |
raw_tmp = raw;
|
|
Packit |
eace71 |
while (*raw_tmp != '\n')
|
|
Packit |
eace71 |
raw_tmp++;
|
|
Packit |
eace71 |
*raw_tmp = '\0';
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (strncmp(raw, qedi_uio_sysfs_name,
|
|
Packit |
eace71 |
sizeof(qedi_uio_sysfs_name)) != 0) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: uio names not equal: expecting %s got %s from %s",
|
|
Packit |
eace71 |
nic->log_name, qedi_uio_sysfs_name, raw, temp_path);
|
|
Packit |
eace71 |
rc = -EIO;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
free(raw);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: Verified is a qedi_uio device", nic->log_name);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
error:
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int qedi_get_mac_addr(qedi_t *bp)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
nic_t *nic = bp->parent;
|
|
Packit |
eace71 |
char *raw = NULL, *raw_tmp;
|
|
Packit |
eace71 |
uint32_t raw_size = 0;
|
|
Packit |
eace71 |
char temp_path[sizeof(qedi_host_mac_template) + 8];
|
|
Packit |
eace71 |
int rc = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Build the path to determine mac address */
|
|
Packit |
eace71 |
snprintf(temp_path, sizeof(temp_path),
|
|
Packit |
eace71 |
qedi_host_mac_template, nic->host_no);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = capture_file(&raw, &raw_size, temp_path);
|
|
Packit |
eace71 |
if (rc != 0)
|
|
Packit |
eace71 |
goto error;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* sanitize name string by replacing newline with null termination */
|
|
Packit |
eace71 |
raw_tmp = raw;
|
|
Packit |
eace71 |
while (*raw_tmp != '\n')
|
|
Packit |
eace71 |
raw_tmp++;
|
|
Packit |
eace71 |
*raw_tmp = '\0';
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = sscanf(raw, "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
Packit |
eace71 |
(uint32_t *)&nic->mac_addr[0], (uint32_t *)&nic->mac_addr[1],
|
|
Packit |
eace71 |
(uint32_t *)&nic->mac_addr[2], (uint32_t *)&nic->mac_addr[3],
|
|
Packit |
eace71 |
(uint32_t *)&nic->mac_addr[4], (uint32_t *)&nic->mac_addr[5]);
|
|
Packit |
eace71 |
if (rc != 1) {
|
|
Packit |
eace71 |
LOG_WARN(PFX "%s: Could not parse mac_addr",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
rc = -ENODEV;
|
|
Packit |
eace71 |
goto error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
error:
|
|
Packit |
eace71 |
if (raw)
|
|
Packit |
eace71 |
free(raw);
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*******************************************************************************
|
|
Packit |
eace71 |
* qedi Utility Functions to get to the hardware consumer indexes
|
|
Packit |
eace71 |
******************************************************************************/
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static __u32 qedi_get_rx(qedi_t *bp)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
return ((struct qedi_uio_ctrl *)bp->uctrl_map)->host_rx_cons;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static __u32 qedi_get_tx(qedi_t *bp)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
return ((struct qedi_uio_ctrl *)bp->uctrl_map)->hw_tx_cons;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_free() - Used to free a qedi structure
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static void qedi_free(nic_t *nic)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
if (nic->priv)
|
|
Packit |
eace71 |
free(nic->priv);
|
|
Packit |
eace71 |
nic->priv = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_alloc() - Used to allocate a qedi structure
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static qedi_t *qedi_alloc(nic_t *nic)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp = malloc(sizeof(*bp));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (!bp) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Could not allocate QEDI space",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
return NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Clear out the CNIC contents */
|
|
Packit |
eace71 |
memset(bp, 0, sizeof(*bp));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->parent = nic;
|
|
Packit |
eace71 |
nic->priv = (void *)bp;
|
|
Packit |
eace71 |
get_iscsi_transport_handle(nic, &nic->transport_handle);
|
|
Packit |
eace71 |
qedi_set_drv_version_unknown(bp);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return bp;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int uio_get_map_offset(nic_t *nic, uint8_t map, uint32_t *offset)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *raw = NULL;
|
|
Packit |
eace71 |
uint32_t raw_size = 0;
|
|
Packit |
eace71 |
ssize_t elements_read;
|
|
Packit |
eace71 |
char temp_path[sizeof(UIO_OFFSET_TMPL) + 8];
|
|
Packit |
eace71 |
int rc = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Capture RX buffer size */
|
|
Packit |
eace71 |
snprintf(temp_path, sizeof(temp_path),
|
|
Packit |
eace71 |
UIO_OFFSET_TMPL, nic->uio_minor, map);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = capture_file(&raw, &raw_size, temp_path);
|
|
Packit |
eace71 |
if (rc != 0)
|
|
Packit |
eace71 |
goto error;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
elements_read = sscanf(raw, "0x%x", offset);
|
|
Packit |
eace71 |
if (elements_read != 1) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Couldn't get the offset from %s",
|
|
Packit |
eace71 |
nic->log_name, temp_path);
|
|
Packit |
eace71 |
rc = -EIO;
|
|
Packit |
eace71 |
goto error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = 0;
|
|
Packit |
eace71 |
error:
|
|
Packit |
eace71 |
if (raw)
|
|
Packit |
eace71 |
free(raw);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int uio_get_map_info(nic_t *nic, uint8_t map, char *attr, uint32_t *val)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
char *raw = NULL;
|
|
Packit |
eace71 |
uint32_t raw_size = 0;
|
|
Packit |
eace71 |
ssize_t elements_read;
|
|
Packit |
eace71 |
char temp_path[sizeof(UIO_ATTR_TMPL) + 8];
|
|
Packit |
eace71 |
int rc = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Capture RX buffer size */
|
|
Packit |
eace71 |
snprintf(temp_path, sizeof(temp_path),
|
|
Packit |
eace71 |
UIO_ATTR_TMPL, nic->uio_minor, map, attr);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = capture_file(&raw, &raw_size, temp_path);
|
|
Packit |
eace71 |
if (rc != 0)
|
|
Packit |
eace71 |
goto error;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
elements_read = sscanf(raw, "0x%x", val);
|
|
Packit |
eace71 |
if (elements_read != 1) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Couldn't get the offset from %s",
|
|
Packit |
eace71 |
nic->log_name, temp_path);
|
|
Packit |
eace71 |
rc = -EIO;
|
|
Packit |
eace71 |
goto error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = 0;
|
|
Packit |
eace71 |
error:
|
|
Packit |
eace71 |
if (raw)
|
|
Packit |
eace71 |
free(raw);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_open() - This will initialize all the hardware resources underneath
|
|
Packit |
eace71 |
* a struct cnic_uio device
|
|
Packit |
eace71 |
* @param dev - The struct cnic_uio device to attach the hardware with
|
|
Packit |
eace71 |
* @return 0 on success, on failure a errno will be returned
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static int qedi_open(nic_t *nic)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp = NULL;
|
|
Packit |
eace71 |
struct stat uio_stat;
|
|
Packit |
eace71 |
int i, rc;
|
|
Packit |
eace71 |
size_t count;
|
|
Packit |
eace71 |
uint32_t bus;
|
|
Packit |
eace71 |
uint32_t slot;
|
|
Packit |
eace71 |
uint32_t func;
|
|
Packit |
eace71 |
uint32_t offset;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Sanity Check: validate the parameters */
|
|
Packit |
eace71 |
if (!nic) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "nic == NULL");
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if ((nic->priv) != NULL &&
|
|
Packit |
eace71 |
(((qedi_t *)(nic->priv))->flags & QEDI_OPENED)) {
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (nic->host_no == INVALID_HOST_NO) {
|
|
Packit |
eace71 |
rc = sscanf(nic->config_device_name, "host%d", &nic->host_no);
|
|
Packit |
eace71 |
if (rc != 1) {
|
|
Packit |
eace71 |
LOG_WARN(PFX "%s: Could not parse for host number",
|
|
Packit |
eace71 |
nic->config_device_name);
|
|
Packit |
eace71 |
rc = -ENODEV;
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp = qedi_alloc(nic);
|
|
Packit |
eace71 |
if (!bp)
|
|
Packit |
eace71 |
return -ENOMEM;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (qedi_is_drv_version_unknown(&qedi_version)) {
|
|
Packit |
eace71 |
/* If version is unknown, go read from ethtool */
|
|
Packit |
eace71 |
rc = qedi_get_drv_version(bp);
|
|
Packit |
eace71 |
if (rc)
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
/* Version is not unknown, just use it */
|
|
Packit |
eace71 |
qedi_version.major = bp->version.major;
|
|
Packit |
eace71 |
qedi_version.minor = bp->version.minor;
|
|
Packit |
eace71 |
qedi_version.sub_minor = bp->version.sub_minor;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
count = 0;
|
|
Packit |
eace71 |
while ((nic->fd < 0) && count < 15) {
|
|
Packit |
eace71 |
/* udev might not have created the file yet */
|
|
Packit |
eace71 |
pthread_mutex_unlock(&nic->nic_mutex);
|
|
Packit |
eace71 |
sleep(1);
|
|
Packit |
eace71 |
pthread_mutex_lock(&nic->nic_mutex);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
nic->fd = open(nic->uio_device_name, O_RDWR | O_NONBLOCK);
|
|
Packit |
eace71 |
if (nic->fd != INVALID_FD) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: uio device has been brought up via pid: %d on fd: %d",
|
|
Packit |
eace71 |
nic->uio_device_name, getpid(), nic->fd);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = qedi_uio_verify(nic);
|
|
Packit |
eace71 |
if (rc != 0)
|
|
Packit |
eace71 |
continue;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
LOG_WARN(PFX "%s: Could not open device: %s, [%s]",
|
|
Packit |
eace71 |
nic->log_name, nic->uio_device_name,
|
|
Packit |
eace71 |
strerror(errno));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
manually_trigger_uio_event(nic, nic->uio_minor);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* udev might not have created the file yet */
|
|
Packit |
eace71 |
pthread_mutex_unlock(&nic->nic_mutex);
|
|
Packit |
eace71 |
sleep(1);
|
|
Packit |
eace71 |
pthread_mutex_lock(&nic->nic_mutex);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
count++;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
if (fstat(nic->fd, &uio_stat) < 0) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Could not fstat device", nic->log_name);
|
|
Packit |
eace71 |
rc = -ENODEV;
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
nic->uio_minor = minor(uio_stat.st_rdev);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* CAPABILITIES: acquire the rx buffer size and rx ring size from qedi
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->rx_ring_size = RX_RING_SIZE;
|
|
Packit |
eace71 |
bp->rx_buffer_size = PKT_BUF_SIZE;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: using rx ring size: %d, rx buffer size: %d",
|
|
Packit |
eace71 |
nic->log_name, bp->rx_ring_size, bp->rx_buffer_size);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Determine the number of UIO events that have already occurred */
|
|
Packit |
eace71 |
rc = detemine_initial_uio_events(nic, &nic->intr_count);
|
|
Packit |
eace71 |
if (rc != 0) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "Could not get the no. of initial UIO events");
|
|
Packit |
eace71 |
nic->intr_count = 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Allocate space for rx pkt ring */
|
|
Packit |
eace71 |
bp->rx_pkt_ring = malloc(sizeof(void *) * bp->rx_ring_size);
|
|
Packit |
eace71 |
if (!bp->rx_pkt_ring) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Could not allocate space for rx_pkt_ring",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
rc = errno;
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* Map the uio struct and packet buffer
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
offset = 0;
|
|
Packit |
eace71 |
rc = uio_get_map_info(nic, QEDI_UCTRL_MAP_REG, "size", &offset);
|
|
Packit |
eace71 |
if (rc) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "Failed to get the map size rc=%d", rc);
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
LOG_INFO(PFX "uctrl map size=%u", offset);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
offset = 0;
|
|
Packit |
eace71 |
rc = uio_get_map_info(nic, QEDI_RING_MAP_REG, "size", &offset);
|
|
Packit |
eace71 |
if (rc) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "Failed to get the map size rc=%d", rc);
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
LOG_INFO(PFX "ring map size=%u", offset);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
offset = 0;
|
|
Packit |
eace71 |
rc = uio_get_map_info(nic, QEDI_BUF_MAP_REG, "size", &offset);
|
|
Packit |
eace71 |
if (rc) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "Failed to get the map size rc=%d", rc);
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
LOG_INFO(PFX "buf map size=%u", offset);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
offset = 0;
|
|
Packit |
eace71 |
rc = uio_get_map_offset(nic, QEDI_UCTRL_MAP_REG, &offset);
|
|
Packit |
eace71 |
if (rc) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "Failed to get the map offset rc=%d", rc);
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->uctrl_map = mmap(NULL, sizeof(struct qedi_uio_ctrl),
|
|
Packit |
eace71 |
PROT_READ | PROT_WRITE,
|
|
Packit |
eace71 |
MAP_SHARED | MAP_LOCKED,
|
|
Packit |
eace71 |
nic->fd, (off_t)0);
|
|
Packit |
eace71 |
if (bp->uctrl_map == MAP_FAILED) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: Could not mmap uio ctrl struct: %s",
|
|
Packit |
eace71 |
nic->log_name, strerror(errno));
|
|
Packit |
eace71 |
bp->uctrl_map = NULL;
|
|
Packit |
eace71 |
rc = errno;
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->uctrl_map_offset = offset;
|
|
Packit |
eace71 |
bp->uctrl_map += offset;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->rx_comp_ring = mmap(NULL, nic->page_size,
|
|
Packit |
eace71 |
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
|
|
Packit |
eace71 |
nic->fd, (off_t)nic->page_size);
|
|
Packit |
eace71 |
if (bp->rx_comp_ring == MAP_FAILED) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: Could not mmap rx_comp_ring: %s",
|
|
Packit |
eace71 |
nic->log_name, strerror(errno));
|
|
Packit |
eace71 |
bp->rx_comp_ring = NULL;
|
|
Packit |
eace71 |
rc = errno;
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->bufs = mmap(NULL, (bp->rx_ring_size + 1) * bp->rx_buffer_size,
|
|
Packit |
eace71 |
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
|
|
Packit |
eace71 |
nic->fd, (off_t)2 * nic->page_size);
|
|
Packit |
eace71 |
if (bp->bufs == MAP_FAILED) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: Could not mmap pkt buffers: %s",
|
|
Packit |
eace71 |
nic->log_name, strerror(errno));
|
|
Packit |
eace71 |
bp->bufs = NULL;
|
|
Packit |
eace71 |
rc = errno;
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* Get all CHIP related info from qedi
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
bp->chip_id = qedi_get_chip_id(bp);
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "Chip ID: %x", bp->chip_id);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
rc = get_bus_slot_func_num(nic, &bus, &slot, &func);
|
|
Packit |
eace71 |
if (rc != 0) {
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: Couldn't determine bus:slot.func",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
goto open_error;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* Get all function, pfid, client_id and cid info from qedi
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: func 0x%x, pfid 0x%x, client_id 0x%x, cid 0x%x",
|
|
Packit |
eace71 |
nic->log_name, bp->func, bp->pfid, bp->client_id, bp->cid);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->get_rx_cons = qedi_get_rx;
|
|
Packit |
eace71 |
bp->get_tx_cons = qedi_get_tx;
|
|
Packit |
eace71 |
bp->tx_cons = 0;
|
|
Packit |
eace71 |
bp->tx_prod = 0;
|
|
Packit |
eace71 |
bp->tx_bd_prod = 0;
|
|
Packit |
eace71 |
bp->tx_pkt = bp->bufs;
|
|
Packit |
eace71 |
bp->rx_pkts = bp->bufs + bp->rx_buffer_size;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->rx_index = 0;
|
|
Packit |
eace71 |
bp->rx_cons = 0;
|
|
Packit |
eace71 |
bp->rx_bd_cons = 0;
|
|
Packit |
eace71 |
bp->rx_prod = 127;
|
|
Packit |
eace71 |
bp->rx_bd_prod = bp->rx_ring_size;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
for (i = 0; i < bp->rx_ring_size; i++) {
|
|
Packit |
eace71 |
void *ptr = bp->bufs + (bp->rx_buffer_size * (i + 1));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->rx_pkt_ring[i] = ptr;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
qedi_get_mac_addr(bp);
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: Using mac address: %02x:%02x:%02x:%02x:%02x:%02x",
|
|
Packit |
eace71 |
nic->log_name,
|
|
Packit |
eace71 |
nic->mac_addr[0], nic->mac_addr[1], nic->mac_addr[2],
|
|
Packit |
eace71 |
nic->mac_addr[3], nic->mac_addr[4], nic->mac_addr[5]);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
qedi_get_library_name(&nic->library_name, &count);
|
|
Packit |
eace71 |
LOG_INFO("%s: qedi initialized", nic->log_name);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp->flags |= QEDI_OPENED;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
open_error:
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->bufs) {
|
|
Packit |
eace71 |
munmap(bp->bufs, (bp->rx_ring_size + 1) * bp->rx_buffer_size);
|
|
Packit |
eace71 |
bp->bufs = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->rx_comp_ring) {
|
|
Packit |
eace71 |
munmap(bp->rx_comp_ring, nic->page_size);
|
|
Packit |
eace71 |
bp->rx_comp_ring = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->uctrl_map) {
|
|
Packit |
eace71 |
bp->uctrl_map -= bp->uctrl_map_offset;
|
|
Packit |
eace71 |
munmap(bp->uctrl_map, sizeof(struct qedi_uio_ctrl));
|
|
Packit |
eace71 |
bp->uctrl_map = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->rx_pkt_ring) {
|
|
Packit |
eace71 |
free(bp->rx_pkt_ring);
|
|
Packit |
eace71 |
bp->rx_pkt_ring = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (nic->fd != INVALID_FD) {
|
|
Packit |
eace71 |
close(nic->fd);
|
|
Packit |
eace71 |
nic->fd = INVALID_FD;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
qedi_free(nic);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_uio_close_resources() - Used to free resource for the NIC/CNIC
|
|
Packit |
eace71 |
* @param nic - NIC device to free resource
|
|
Packit |
eace71 |
* @param graceful - whether to wait to close gracefully
|
|
Packit |
eace71 |
* @return 0 on success, <0 on failure
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static int qedi_uio_close_resources(nic_t *nic, NIC_SHUTDOWN_T graceful)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp = (qedi_t *)nic->priv;
|
|
Packit |
eace71 |
int rc = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Check if there is an assoicated qedi device */
|
|
Packit |
eace71 |
if (!bp) {
|
|
Packit |
eace71 |
LOG_WARN(PFX "%s: when closing resources there is no assoicated qedi",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
return -EIO;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Clean up allocated memory */
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->rx_pkt_ring) {
|
|
Packit |
eace71 |
free(bp->rx_pkt_ring);
|
|
Packit |
eace71 |
bp->rx_pkt_ring = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Clean up mapped registers */
|
|
Packit |
eace71 |
if (bp->bufs) {
|
|
Packit |
eace71 |
rc = munmap(bp->bufs,
|
|
Packit |
eace71 |
(bp->rx_ring_size + 1) * bp->rx_buffer_size);
|
|
Packit |
eace71 |
if (rc != 0)
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Couldn't unmap bufs", nic->log_name);
|
|
Packit |
eace71 |
bp->bufs = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->rx_comp_ring) {
|
|
Packit |
eace71 |
rc = munmap(bp->rx_comp_ring, nic->page_size);
|
|
Packit |
eace71 |
if (rc != 0)
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Couldn't unmap ring", nic->log_name);
|
|
Packit |
eace71 |
bp->rx_comp_ring = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->uctrl_map) {
|
|
Packit |
eace71 |
bp->uctrl_map -= bp->uctrl_map_offset;
|
|
Packit |
eace71 |
rc = munmap(bp->uctrl_map, sizeof(struct qedi_uio_ctrl));
|
|
Packit |
eace71 |
if (rc != 0) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Couldn't unmap uio ctrl",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
bp->uctrl_map = NULL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (nic->fd != INVALID_FD) {
|
|
Packit |
eace71 |
rc = close(nic->fd);
|
|
Packit |
eace71 |
if (rc != 0) {
|
|
Packit |
eace71 |
LOG_ERR(PFX
|
|
Packit |
eace71 |
"%s: Couldn't close uio file descriptor: %d",
|
|
Packit |
eace71 |
nic->log_name, nic->fd);
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: Closed uio file descriptor: %d",
|
|
Packit |
eace71 |
nic->log_name, nic->fd);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
nic->fd = INVALID_FD;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Invalid uio file descriptor: %d",
|
|
Packit |
eace71 |
nic->log_name, nic->fd);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
qedi_set_drv_version_unknown(bp);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_INFO(PFX "%s: Closed all resources", nic->log_name);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_close() - Used to close the NIC device
|
|
Packit |
eace71 |
* @param nic - NIC device to close
|
|
Packit |
eace71 |
* @param graceful - whether to wait to close gracefully
|
|
Packit |
eace71 |
* @return 0 if successful, <0 if there is an error
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static int qedi_close(nic_t *nic, NIC_SHUTDOWN_T graceful)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
/* Sanity Check: validate the parameters */
|
|
Packit |
eace71 |
if (!nic) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: nic == NULL", __func__);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
if (!nic->priv) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: nic->priv == NULL", __func__);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_INFO(PFX "Closing NIC device: %s", nic->log_name);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
qedi_uio_close_resources(nic, graceful);
|
|
Packit |
eace71 |
qedi_free(nic);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static void qedi_prepare_xmit_packet(nic_t *nic,
|
|
Packit |
eace71 |
nic_interface_t *nic_iface,
|
|
Packit |
eace71 |
struct packet *pkt)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp = (qedi_t *)nic->priv;
|
|
Packit |
eace71 |
struct uip_vlan_eth_hdr *eth_vlan = (struct uip_vlan_eth_hdr *)pkt->buf;
|
|
Packit |
eace71 |
struct uip_eth_hdr *eth = (struct uip_eth_hdr *)bp->tx_pkt;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: pkt->buf_size=%d tpid=0x%x", nic->log_name,
|
|
Packit |
eace71 |
pkt->buf_size, eth_vlan->tpid);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (eth_vlan->tpid == htons(UIP_ETHTYPE_8021Q)) {
|
|
Packit |
eace71 |
memcpy(bp->tx_pkt, pkt->buf, sizeof(struct uip_eth_hdr));
|
|
Packit |
eace71 |
eth->type = eth_vlan->type;
|
|
Packit |
eace71 |
pkt->buf_size -= (sizeof(struct uip_vlan_eth_hdr) -
|
|
Packit |
eace71 |
sizeof(struct uip_eth_hdr));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: pkt->buf_size=%d type=0x%x", nic->log_name,
|
|
Packit |
eace71 |
pkt->buf_size, eth->type);
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: pkt->buf_size - eth_hdr_size = %d", nic->log_name,
|
|
Packit |
eace71 |
pkt->buf_size - sizeof(struct uip_eth_hdr));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
memcpy(bp->tx_pkt + sizeof(struct uip_eth_hdr),
|
|
Packit |
eace71 |
pkt->buf + sizeof(struct uip_vlan_eth_hdr),
|
|
Packit |
eace71 |
pkt->buf_size - sizeof(struct uip_eth_hdr));
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: NO VLAN pkt->buf_size=%d", nic->log_name,
|
|
Packit |
eace71 |
pkt->buf_size);
|
|
Packit |
eace71 |
memcpy(bp->tx_pkt, pkt->buf, pkt->buf_size);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
msync(bp->tx_pkt, pkt->buf_size, MS_SYNC);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_get_tx_pkt() - This function is used to a TX packet from the NIC
|
|
Packit |
eace71 |
* @param nic - The NIC device to send the packet
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
void *qedi_get_tx_pkt(nic_t *nic)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp = (qedi_t *)nic->priv;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return bp->tx_pkt;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_start_xmit() - This function is used to send a packet of data
|
|
Packit |
eace71 |
* @param nic - The NIC device to send the packet
|
|
Packit |
eace71 |
* @param len - the length of the TX packet
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
void qedi_start_xmit(nic_t *nic, size_t len, u16_t vlan_id)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp = (qedi_t *)nic->priv;
|
|
Packit |
eace71 |
uint8_t *ubuf;
|
|
Packit |
eace71 |
struct iscsi_uevent *ev;
|
|
Packit |
eace71 |
struct iscsi_path *path_data;
|
|
Packit |
eace71 |
struct qedi_uio_ctrl *uctrl;
|
|
Packit |
eace71 |
int rc = 0;
|
|
Packit |
eace71 |
uint16_t buflen;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
buflen = sizeof(struct iscsi_uevent) + sizeof(struct iscsi_path);
|
|
Packit |
eace71 |
ubuf = calloc(1, NLMSG_SPACE(buflen));
|
|
Packit |
eace71 |
if (!ubuf) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: alloc failed for uevent buf", __func__);
|
|
Packit |
eace71 |
return;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
memset(ubuf, 0, NLMSG_SPACE(buflen));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* prepare the iscsi_uevent buffer */
|
|
Packit |
eace71 |
ev = (struct iscsi_uevent *)ubuf;
|
|
Packit |
eace71 |
ev->type = ISCSI_UEVENT_PATH_UPDATE;
|
|
Packit |
eace71 |
ev->transport_handle = nic->transport_handle;
|
|
Packit |
eace71 |
ev->u.set_path.host_no = nic->host_no;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Prepare the iscsi_path buffer */
|
|
Packit |
eace71 |
path_data = (struct iscsi_path *)(ubuf + sizeof(struct iscsi_uevent));
|
|
Packit |
eace71 |
path_data->handle = QEDI_PATH_HANDLE;
|
|
Packit |
eace71 |
path_data->vlan_id = vlan_id;
|
|
Packit |
eace71 |
uctrl->host_tx_pkt_len = len;
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: host_no:%d vlan_id=%d, tx_pkt_len=%d",
|
|
Packit |
eace71 |
nic->log_name, ev->u.set_path.host_no, path_data->vlan_id, uctrl->host_tx_pkt_len);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: ACQUIRE HOST MUTEX", nic->log_name);
|
|
Packit |
eace71 |
pthread_mutex_lock(&host_mutex);
|
|
Packit |
eace71 |
rc = __kipc_call(nl_sock, ev, buflen);
|
|
Packit |
eace71 |
if (rc > 0) {
|
|
Packit |
eace71 |
bp->tx_prod++;
|
|
Packit |
eace71 |
uctrl->host_tx_prod++;
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: bp->tx_prod: %d, uctrl->host_tx_prod=%d",
|
|
Packit |
eace71 |
nic->log_name, bp->tx_prod, uctrl->host_tx_prod);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
msync(uctrl, sizeof(struct qedi_uio_ctrl), MS_SYNC);
|
|
Packit |
eace71 |
LOG_PACKET(PFX "%s: sent %d bytes using bp->tx_prod: %d",
|
|
Packit |
eace71 |
nic->log_name, len, bp->tx_prod);
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
LOG_ERR(PFX "Pkt transmission failed: %d", rc);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: RELEASE HOST MUTEX", nic->log_name);
|
|
Packit |
eace71 |
pthread_mutex_unlock(&host_mutex);
|
|
Packit |
eace71 |
free(ubuf);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_write() - Used to write the data to the hardware
|
|
Packit |
eace71 |
* @param nic - NIC hardware to read from
|
|
Packit |
eace71 |
* @param pkt - The packet which will hold the data to be sent on the wire
|
|
Packit |
eace71 |
* @return 0 if successful, <0 if failed
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
int qedi_write(nic_t *nic, nic_interface_t *nic_iface, packet_t *pkt)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp;
|
|
Packit |
eace71 |
struct uip_stack *uip;
|
|
Packit |
eace71 |
int i = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Sanity Check: validate the parameters */
|
|
Packit |
eace71 |
if (!nic || !nic_iface || !pkt) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: qedi_write() nic == 0x%p || nic_iface == 0x%p || pkt == 0x%x",
|
|
Packit |
eace71 |
nic, nic_iface, pkt);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
bp = (qedi_t *)nic->priv;
|
|
Packit |
eace71 |
uip = &nic_iface->ustack;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (pkt->buf_size == 0) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: Trying to transmitted 0 sized packet",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Try to wait for a TX completion */
|
|
Packit |
eace71 |
for (i = 0; i < 15; i++) {
|
|
Packit |
eace71 |
struct timespec sleep_req = {.tv_sec = 0, .tv_nsec = 5000000 },
|
|
Packit |
eace71 |
sleep_rem;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: host:%d - calling clear_tx_intr from qedi_write",
|
|
Packit |
eace71 |
nic->log_name, nic->host_no);
|
|
Packit |
eace71 |
if (qedi_clear_tx_intr(nic) == 0)
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
nanosleep(&sleep_req, &sleep_rem);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: host:%d - try getting xmit mutex",
|
|
Packit |
eace71 |
nic->log_name, nic->host_no);
|
|
Packit |
eace71 |
if (pthread_mutex_trylock(&nic->xmit_mutex) != 0) {
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: Dropped previous transmitted packet",
|
|
Packit |
eace71 |
nic->log_name);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
qedi_prepare_xmit_packet(nic, nic_iface, pkt);
|
|
Packit |
eace71 |
qedi_start_xmit(nic, pkt->buf_size,
|
|
Packit |
eace71 |
(nic_iface->vlan_priority << 12) |
|
|
Packit |
eace71 |
nic_iface->vlan_id);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* bump up the tx stats */
|
|
Packit |
eace71 |
nic->stats.tx.packets++;
|
|
Packit |
eace71 |
nic->stats.tx.bytes += uip->uip_len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: transmitted %d bytes dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d",
|
|
Packit |
eace71 |
nic->log_name, pkt->buf_size,
|
|
Packit |
eace71 |
bp->tx_cons, bp->tx_prod, bp->tx_bd_prod);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: host:%d - releasing xmit mutex",
|
|
Packit |
eace71 |
nic->log_name, nic->host_no);
|
|
Packit |
eace71 |
pthread_mutex_unlock(&nic->xmit_mutex);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_read() - Used to read the data from the hardware
|
|
Packit |
eace71 |
* @param nic - NIC hardware to read from
|
|
Packit |
eace71 |
* @param pkt - The packet which will hold the data
|
|
Packit |
eace71 |
* @return 0 if successful, < 0 if failed
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
static int qedi_read(nic_t *nic, packet_t *pkt)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp;
|
|
Packit |
eace71 |
void *rx_pkt;
|
|
Packit |
eace71 |
int rc = 0;
|
|
Packit |
eace71 |
uint32_t sw_cons, bd_cons;
|
|
Packit |
eace71 |
uint32_t hw_prod, bd_prod;
|
|
Packit |
eace71 |
uint32_t rx_pkt_idx;
|
|
Packit |
eace71 |
int len;
|
|
Packit |
eace71 |
struct qedi_rx_bd *rx_bd;
|
|
Packit |
eace71 |
struct qedi_uio_ctrl *uctrl;
|
|
Packit |
eace71 |
uint16_t vlan_id;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Sanity Check: validate the parameters */
|
|
Packit |
eace71 |
if (!nic || !pkt) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: qedi_read() nic == 0x%p || pkt == 0x%x",
|
|
Packit |
eace71 |
nic, pkt);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp = (qedi_t *)nic->priv;
|
|
Packit |
eace71 |
msync(bp->uctrl_map, sizeof(struct qedi_uio_ctrl), MS_SYNC);
|
|
Packit |
eace71 |
msync(bp->rx_comp_ring, nic->page_size, MS_SYNC);
|
|
Packit |
eace71 |
uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map;
|
|
Packit |
eace71 |
hw_prod = uctrl->hw_rx_prod;
|
|
Packit |
eace71 |
bd_prod = uctrl->hw_rx_bd_prod;
|
|
Packit |
eace71 |
sw_cons = uctrl->host_rx_cons;
|
|
Packit |
eace71 |
bd_cons = uctrl->host_rx_bd_cons;
|
|
Packit |
eace71 |
rx_bd = bp->rx_comp_ring + (bd_prod * sizeof(*rx_bd));
|
|
Packit |
eace71 |
len = rx_bd->rx_pkt_len;
|
|
Packit |
eace71 |
rx_pkt_idx = rx_bd->rx_pkt_index;
|
|
Packit |
eace71 |
vlan_id = rx_bd->vlan_id;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s:hw_prod %d bd_prod %d, rx_pkt_idx %d, rxlen %d",
|
|
Packit |
eace71 |
nic->log_name, hw_prod, bd_prod, rx_bd->rx_pkt_index, len);
|
|
Packit |
429637 |
LOG_DEBUG(PFX "%s: sw_con %d bd_cons %d num BD %lu",
|
|
Packit |
eace71 |
nic->log_name, sw_cons, bd_cons, QEDI_NUM_RX_BD);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bd_cons != bd_prod) {
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: clearing rx interrupt: %d %d",
|
|
Packit |
eace71 |
nic->log_name, sw_cons, hw_prod);
|
|
Packit |
eace71 |
rc = 1;
|
|
Packit |
eace71 |
rx_pkt = bp->rx_pkts + (bp->rx_buffer_size * rx_pkt_idx);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (len > 0) {
|
|
Packit |
eace71 |
msync(rx_pkt, len, MS_SYNC);
|
|
Packit |
eace71 |
/* Copy the data */
|
|
Packit |
eace71 |
memcpy(pkt->buf, rx_pkt, len);
|
|
Packit |
eace71 |
pkt->buf_size = len;
|
|
Packit |
eace71 |
if (vlan_id) {
|
|
Packit |
eace71 |
pkt->vlan_tag = vlan_id;
|
|
Packit |
eace71 |
pkt->flags |= VLAN_TAGGED;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
pkt->vlan_tag = 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: processing packet length: %d",
|
|
Packit |
eace71 |
nic->log_name, len);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* bump up the recv stats */
|
|
Packit |
eace71 |
nic->stats.rx.packets++;
|
|
Packit |
eace71 |
nic->stats.rx.bytes += pkt->buf_size;
|
|
Packit |
eace71 |
} else {
|
|
Packit |
eace71 |
rc = 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
sw_cons = (sw_cons + 1) % RX_RING_SIZE;
|
|
Packit |
eace71 |
bd_cons = (bd_cons + 1) % QEDI_NUM_RX_BD;
|
|
Packit |
eace71 |
uctrl->host_rx_cons_cnt++;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
uctrl->host_rx_bd_cons = bd_cons;
|
|
Packit |
eace71 |
uctrl->host_rx_cons = sw_cons;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
msync(uctrl, sizeof(struct qedi_uio_ctrl), MS_SYNC);
|
|
Packit |
eace71 |
msync(bp->rx_comp_ring, nic->page_size, MS_SYNC);
|
|
Packit |
eace71 |
return rc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*******************************************************************************
|
|
Packit |
eace71 |
* Clearing TX interrupts
|
|
Packit |
eace71 |
******************************************************************************/
|
|
Packit |
eace71 |
/**
|
|
Packit |
eace71 |
* qedi_clear_tx_intr() - This routine is called when a TX interrupt occurs
|
|
Packit |
eace71 |
* @param nic - the nic the interrupt occurred on
|
|
Packit |
eace71 |
* @return 0 on success
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int qedi_clear_tx_intr(nic_t *nic)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
qedi_t *bp;
|
|
Packit |
eace71 |
uint32_t hw_cons;
|
|
Packit |
eace71 |
struct qedi_uio_ctrl *uctrl;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Sanity check: ensure the parameters passed in are valid */
|
|
Packit |
eace71 |
if (unlikely(!nic)) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: nic == NULL", __func__);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
bp = (qedi_t *)nic->priv;
|
|
Packit |
eace71 |
uctrl = (struct qedi_uio_ctrl *)bp->uctrl_map;
|
|
Packit |
eace71 |
msync(bp->uctrl_map, sizeof(struct qedi_uio_ctrl), MS_SYNC);
|
|
Packit |
eace71 |
hw_cons = uctrl->hw_tx_cons;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (bp->tx_cons == hw_cons) {
|
|
Packit |
eace71 |
if (bp->tx_cons == bp->tx_prod)
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
return -EAGAIN;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (pthread_mutex_trylock(&nic->xmit_mutex)) {
|
|
Packit |
eace71 |
LOG_ERR(PFX "%s: unable to get xmit_mutex.", nic->log_name);
|
|
Packit |
eace71 |
return -EINVAL;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: clearing tx interrupt [%d %d]",
|
|
Packit |
eace71 |
nic->log_name, bp->tx_cons, hw_cons);
|
|
Packit |
eace71 |
bp->tx_cons = hw_cons;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* There is a queued TX packet that needs to be sent out. The usual
|
|
Packit |
eace71 |
* case is when stack will send an ARP packet out before sending the
|
|
Packit |
eace71 |
* intended packet
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (nic->tx_packet_queue) {
|
|
Packit |
eace71 |
packet_t *pkt;
|
|
Packit |
eace71 |
int i;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: sending queued tx packet", nic->log_name);
|
|
Packit |
eace71 |
pkt = nic_dequeue_tx_packet(nic);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Got a TX packet buffer of the TX queue and put it onto
|
|
Packit |
eace71 |
* the hardware
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
if (pkt) {
|
|
Packit |
eace71 |
qedi_prepare_xmit_packet(nic, pkt->nic_iface, pkt);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
qedi_start_xmit(nic, pkt->buf_size,
|
|
Packit |
eace71 |
(pkt->nic_iface->vlan_priority << 12) |
|
|
Packit |
eace71 |
pkt->nic_iface->vlan_id);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: transmitted queued packet %d bytes, dev->tx_cons: %d, dev->tx_prod: %d, dev->tx_bd_prod:%d",
|
|
Packit |
eace71 |
nic->log_name, pkt->buf_size,
|
|
Packit |
eace71 |
bp->tx_cons, bp->tx_prod, bp->tx_bd_prod);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
pthread_mutex_unlock(&nic->xmit_mutex);
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Try to wait for a TX completion */
|
|
Packit |
eace71 |
for (i = 0; i < 15; i++) {
|
|
Packit |
eace71 |
struct timespec sleep_req = {.tv_sec = 0,
|
|
Packit |
eace71 |
.tv_nsec = 5000000
|
|
Packit |
eace71 |
}, sleep_rem;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
hw_cons = uctrl->hw_tx_cons;
|
|
Packit |
eace71 |
if (bp->tx_cons != hw_cons) {
|
|
Packit |
eace71 |
LOG_PACKET(PFX
|
|
Packit |
eace71 |
"%s: clearing tx interrupt [%d %d]",
|
|
Packit |
eace71 |
nic->log_name, bp->tx_cons, hw_cons);
|
|
Packit |
eace71 |
bp->tx_cons = hw_cons;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
nanosleep(&sleep_req, &sleep_rem);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG(PFX "%s: host:%d - releasing xmit mutex",
|
|
Packit |
eace71 |
nic->log_name, nic->host_no);
|
|
Packit |
eace71 |
pthread_mutex_unlock(&nic->xmit_mutex);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*******************************************************************************
|
|
Packit |
eace71 |
* qedi NIC op's table
|
|
Packit |
eace71 |
******************************************************************************/
|
|
Packit |
eace71 |
struct nic_ops qedi_op = {
|
|
Packit |
eace71 |
.description = "qedi",
|
|
Packit |
eace71 |
.open = qedi_open,
|
|
Packit |
eace71 |
.close = qedi_close,
|
|
Packit |
eace71 |
.write = qedi_write,
|
|
Packit |
eace71 |
.get_tx_pkt = qedi_get_tx_pkt,
|
|
Packit |
eace71 |
.start_xmit = qedi_start_xmit,
|
|
Packit |
eace71 |
.read = qedi_read,
|
|
Packit |
eace71 |
.clear_tx_intr = qedi_clear_tx_intr,
|
|
Packit |
eace71 |
.handle_iscsi_path_req = cnic_handle_iscsi_path_req,
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
.lib_ops = {
|
|
Packit |
eace71 |
.get_library_name = qedi_get_library_name,
|
|
Packit |
eace71 |
.get_library_version = qedi_get_library_version,
|
|
Packit |
eace71 |
.get_build_date = qedi_get_build_date,
|
|
Packit |
eace71 |
.get_transport_name = qedi_get_transport_name,
|
|
Packit |
eace71 |
.get_uio_name = qedi_get_uio_name,
|
|
Packit |
eace71 |
},
|
|
Packit |
eace71 |
};
|