forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1055 lines
31 KiB
1055 lines
31 KiB
/* |
|
* Copyright(c) 2017 Intel Corporation. |
|
* |
|
* This file is provided under a dual BSD/GPLv2 license. When using or |
|
* redistributing this file, you may do so under either license. |
|
* |
|
* GPL LICENSE SUMMARY |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of version 2 of the GNU General Public License as |
|
* published by the Free Software Foundation. |
|
* |
|
* This program is distributed in the hope that it will be useful, but |
|
* WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
* General Public License for more details. |
|
* |
|
* BSD LICENSE |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* - Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* - 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. |
|
* - Neither the name of Intel Corporation nor the names of its |
|
* contributors may be used to endorse or promote products derived |
|
* from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
* "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 COPYRIGHT |
|
* OWNER OR CONTRIBUTORS 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. |
|
* |
|
*/ |
|
|
|
/* |
|
* This file contains OPA Virtual Network Interface Controller (VNIC) |
|
* Ethernet Management Agent (EMA) driver |
|
*/ |
|
|
|
#include <linux/module.h> |
|
#include <linux/xarray.h> |
|
#include <rdma/ib_addr.h> |
|
#include <rdma/ib_verbs.h> |
|
#include <rdma/opa_smi.h> |
|
#include <rdma/opa_port_info.h> |
|
|
|
#include "opa_vnic_internal.h" |
|
|
|
char opa_vnic_driver_name[] = "opa_vnic"; |
|
|
|
/* |
|
* The trap service level is kept in bits 3 to 7 in the trap_sl_rsvd |
|
* field in the class port info MAD. |
|
*/ |
|
#define GET_TRAP_SL_FROM_CLASS_PORT_INFO(x) (((x) >> 3) & 0x1f) |
|
|
|
/* Cap trap bursts to a reasonable limit good for normal cases */ |
|
#define OPA_VNIC_TRAP_BURST_LIMIT 4 |
|
|
|
/* |
|
* VNIC trap limit timeout. |
|
* Inverse of cap2_mask response time out (1.0737 secs) = 0.9 |
|
* secs approx IB spec 13.4.6.2.1 PortInfoSubnetTimeout and |
|
* 13.4.9 Traps. |
|
*/ |
|
#define OPA_VNIC_TRAP_TIMEOUT ((4096 * (1UL << 18)) / 1000) |
|
|
|
#define OPA_VNIC_UNSUP_ATTR \ |
|
cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB) |
|
|
|
#define OPA_VNIC_INVAL_ATTR \ |
|
cpu_to_be16(IB_MGMT_MAD_STATUS_INVALID_ATTRIB_VALUE) |
|
|
|
#define OPA_VNIC_CLASS_CAP_TRAP 0x1 |
|
|
|
/* Maximum number of VNIC ports supported */ |
|
#define OPA_VNIC_MAX_NUM_VPORT 255 |
|
|
|
/** |
|
* struct opa_vnic_vema_port -- VNIC VEMA port details |
|
* @cport: pointer to port |
|
* @mad_agent: pointer to mad agent for port |
|
* @class_port_info: Class port info information. |
|
* @tid: Transaction id |
|
* @port_num: OPA port number |
|
* @vports: vnic ports |
|
* @event_handler: ib event handler |
|
* @lock: adapter interface lock |
|
*/ |
|
struct opa_vnic_vema_port { |
|
struct opa_vnic_ctrl_port *cport; |
|
struct ib_mad_agent *mad_agent; |
|
struct opa_class_port_info class_port_info; |
|
u64 tid; |
|
u8 port_num; |
|
struct xarray vports; |
|
struct ib_event_handler event_handler; |
|
|
|
/* Lock to query/update network adapter */ |
|
struct mutex lock; |
|
}; |
|
|
|
static int opa_vnic_vema_add_one(struct ib_device *device); |
|
static void opa_vnic_vema_rem_one(struct ib_device *device, |
|
void *client_data); |
|
|
|
static struct ib_client opa_vnic_client = { |
|
.name = opa_vnic_driver_name, |
|
.add = opa_vnic_vema_add_one, |
|
.remove = opa_vnic_vema_rem_one, |
|
}; |
|
|
|
/** |
|
* vema_get_vport_num -- Get the vnic from the mad |
|
* @recvd_mad: Received mad |
|
* |
|
* Return: returns value of the vnic port number |
|
*/ |
|
static inline u8 vema_get_vport_num(struct opa_vnic_vema_mad *recvd_mad) |
|
{ |
|
return be32_to_cpu(recvd_mad->mad_hdr.attr_mod) & 0xff; |
|
} |
|
|
|
/** |
|
* vema_get_vport_adapter -- Get vnic port adapter from recvd mad |
|
* @recvd_mad: received mad |
|
* @port: ptr to port struct on which MAD was recvd |
|
* |
|
* Return: vnic adapter |
|
*/ |
|
static inline struct opa_vnic_adapter * |
|
vema_get_vport_adapter(struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_port *port) |
|
{ |
|
u8 vport_num = vema_get_vport_num(recvd_mad); |
|
|
|
return xa_load(&port->vports, vport_num); |
|
} |
|
|
|
/** |
|
* vema_mac_tbl_req_ok -- Check if mac request has correct values |
|
* @mac_tbl: mac table |
|
* |
|
* This function checks for the validity of the offset and number of |
|
* entries required. |
|
* |
|
* Return: true if offset and num_entries are valid |
|
*/ |
|
static inline bool vema_mac_tbl_req_ok(struct opa_veswport_mactable *mac_tbl) |
|
{ |
|
u16 offset, num_entries; |
|
u16 req_entries = ((OPA_VNIC_EMA_DATA - sizeof(*mac_tbl)) / |
|
sizeof(mac_tbl->tbl_entries[0])); |
|
|
|
offset = be16_to_cpu(mac_tbl->offset); |
|
num_entries = be16_to_cpu(mac_tbl->num_entries); |
|
|
|
return ((num_entries <= req_entries) && |
|
(offset + num_entries <= OPA_VNIC_MAC_TBL_MAX_ENTRIES)); |
|
} |
|
|
|
/* |
|
* Return the power on default values in the port info structure |
|
* in big endian format as required by MAD. |
|
*/ |
|
static inline void vema_get_pod_values(struct opa_veswport_info *port_info) |
|
{ |
|
memset(port_info, 0, sizeof(*port_info)); |
|
port_info->vport.max_mac_tbl_ent = |
|
cpu_to_be16(OPA_VNIC_MAC_TBL_MAX_ENTRIES); |
|
port_info->vport.max_smac_ent = |
|
cpu_to_be16(OPA_VNIC_MAX_SMAC_LIMIT); |
|
port_info->vport.oper_state = OPA_VNIC_STATE_DROP_ALL; |
|
port_info->vport.config_state = OPA_VNIC_STATE_DROP_ALL; |
|
port_info->vesw.eth_mtu = cpu_to_be16(ETH_DATA_LEN); |
|
} |
|
|
|
/** |
|
* vema_add_vport -- Add a new vnic port |
|
* @port: ptr to opa_vnic_vema_port struct |
|
* @vport_num: vnic port number (to be added) |
|
* |
|
* Return a pointer to the vnic adapter structure |
|
*/ |
|
static struct opa_vnic_adapter *vema_add_vport(struct opa_vnic_vema_port *port, |
|
u8 vport_num) |
|
{ |
|
struct opa_vnic_ctrl_port *cport = port->cport; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = opa_vnic_add_netdev(cport->ibdev, port->port_num, vport_num); |
|
if (!IS_ERR(adapter)) { |
|
int rc; |
|
|
|
adapter->cport = cport; |
|
rc = xa_insert(&port->vports, vport_num, adapter, GFP_KERNEL); |
|
if (rc < 0) { |
|
opa_vnic_rem_netdev(adapter); |
|
adapter = ERR_PTR(rc); |
|
} |
|
} |
|
|
|
return adapter; |
|
} |
|
|
|
/** |
|
* vema_get_class_port_info -- Get class info for port |
|
* @port: Port on whic MAD was received |
|
* @recvd_mad: pointer to the received mad |
|
* @rsp_mad: pointer to respose mad |
|
* |
|
* This function copies the latest class port info value set for the |
|
* port and stores it for generating traps |
|
*/ |
|
static void vema_get_class_port_info(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_class_port_info *port_info; |
|
|
|
port_info = (struct opa_class_port_info *)rsp_mad->data; |
|
memcpy(port_info, &port->class_port_info, sizeof(*port_info)); |
|
port_info->base_version = OPA_MGMT_BASE_VERSION; |
|
port_info->class_version = OPA_EMA_CLASS_VERSION; |
|
|
|
/* |
|
* Set capability mask bit indicating agent generates traps, |
|
* and set the maximum number of VNIC ports supported. |
|
*/ |
|
port_info->cap_mask = cpu_to_be16((OPA_VNIC_CLASS_CAP_TRAP | |
|
(OPA_VNIC_MAX_NUM_VPORT << 8))); |
|
|
|
/* |
|
* Since a get routine is always sent by the EM first we |
|
* set the expected response time to |
|
* 4.096 usec * 2^18 == 1.0737 sec here. |
|
*/ |
|
port_info->cap_mask2_resp_time = cpu_to_be32(18); |
|
} |
|
|
|
/** |
|
* vema_set_class_port_info -- Get class info for port |
|
* @port: Port on whic MAD was received |
|
* @recvd_mad: pointer to the received mad |
|
* @rsp_mad: pointer to respose mad |
|
* |
|
* This function updates the port class info for the specific vnic |
|
* and sets up the response mad data |
|
*/ |
|
static void vema_set_class_port_info(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
memcpy(&port->class_port_info, recvd_mad->data, |
|
sizeof(port->class_port_info)); |
|
|
|
vema_get_class_port_info(port, recvd_mad, rsp_mad); |
|
} |
|
|
|
/** |
|
* vema_get_veswport_info -- Get veswport info |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: pointer to the received mad |
|
* @rsp_mad: pointer to respose mad |
|
*/ |
|
static void vema_get_veswport_info(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_veswport_info *port_info = |
|
(struct opa_veswport_info *)rsp_mad->data; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (adapter) { |
|
memset(port_info, 0, sizeof(*port_info)); |
|
opa_vnic_get_vesw_info(adapter, &port_info->vesw); |
|
opa_vnic_get_per_veswport_info(adapter, |
|
&port_info->vport); |
|
} else { |
|
vema_get_pod_values(port_info); |
|
} |
|
} |
|
|
|
/** |
|
* vema_set_veswport_info -- Set veswport info |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: pointer to the received mad |
|
* @rsp_mad: pointer to respose mad |
|
* |
|
* This function gets the port class infor for vnic |
|
*/ |
|
static void vema_set_veswport_info(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_vnic_ctrl_port *cport = port->cport; |
|
struct opa_veswport_info *port_info; |
|
struct opa_vnic_adapter *adapter; |
|
u8 vport_num; |
|
|
|
vport_num = vema_get_vport_num(recvd_mad); |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (!adapter) { |
|
adapter = vema_add_vport(port, vport_num); |
|
if (IS_ERR(adapter)) { |
|
c_err("failed to add vport %d: %ld\n", |
|
vport_num, PTR_ERR(adapter)); |
|
goto err_exit; |
|
} |
|
} |
|
|
|
port_info = (struct opa_veswport_info *)recvd_mad->data; |
|
opa_vnic_set_vesw_info(adapter, &port_info->vesw); |
|
opa_vnic_set_per_veswport_info(adapter, &port_info->vport); |
|
|
|
/* Process the new config settings */ |
|
opa_vnic_process_vema_config(adapter); |
|
|
|
vema_get_veswport_info(port, recvd_mad, rsp_mad); |
|
return; |
|
|
|
err_exit: |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
} |
|
|
|
/** |
|
* vema_get_mac_entries -- Get MAC entries in VNIC MAC table |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: pointer to the received mad |
|
* @rsp_mad: pointer to respose mad |
|
* |
|
* This function gets the MAC entries that are programmed into |
|
* the VNIC MAC forwarding table. It checks for the validity of |
|
* the index into the MAC table and the number of entries that |
|
* are to be retrieved. |
|
*/ |
|
static void vema_get_mac_entries(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_veswport_mactable *mac_tbl_in, *mac_tbl_out; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (!adapter) { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
return; |
|
} |
|
|
|
mac_tbl_in = (struct opa_veswport_mactable *)recvd_mad->data; |
|
mac_tbl_out = (struct opa_veswport_mactable *)rsp_mad->data; |
|
|
|
if (vema_mac_tbl_req_ok(mac_tbl_in)) { |
|
mac_tbl_out->offset = mac_tbl_in->offset; |
|
mac_tbl_out->num_entries = mac_tbl_in->num_entries; |
|
opa_vnic_query_mac_tbl(adapter, mac_tbl_out); |
|
} else { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
} |
|
} |
|
|
|
/** |
|
* vema_set_mac_entries -- Set MAC entries in VNIC MAC table |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: pointer to the received mad |
|
* @rsp_mad: pointer to respose mad |
|
* |
|
* This function sets the MAC entries in the VNIC forwarding table |
|
* It checks for the validity of the index and the number of forwarding |
|
* table entries to be programmed. |
|
*/ |
|
static void vema_set_mac_entries(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_veswport_mactable *mac_tbl; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (!adapter) { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
return; |
|
} |
|
|
|
mac_tbl = (struct opa_veswport_mactable *)recvd_mad->data; |
|
if (vema_mac_tbl_req_ok(mac_tbl)) { |
|
if (opa_vnic_update_mac_tbl(adapter, mac_tbl)) |
|
rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; |
|
} else { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; |
|
} |
|
vema_get_mac_entries(port, recvd_mad, rsp_mad); |
|
} |
|
|
|
/** |
|
* vema_set_delete_vesw -- Reset VESW info to POD values |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: pointer to the received mad |
|
* @rsp_mad: pointer to respose mad |
|
* |
|
* This function clears all the fields of veswport info for the requested vesw |
|
* and sets them back to the power-on default values. It does not delete the |
|
* vesw. |
|
*/ |
|
static void vema_set_delete_vesw(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_veswport_info *port_info = |
|
(struct opa_veswport_info *)rsp_mad->data; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (!adapter) { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
return; |
|
} |
|
|
|
vema_get_pod_values(port_info); |
|
opa_vnic_set_vesw_info(adapter, &port_info->vesw); |
|
opa_vnic_set_per_veswport_info(adapter, &port_info->vport); |
|
|
|
/* Process the new config settings */ |
|
opa_vnic_process_vema_config(adapter); |
|
|
|
opa_vnic_release_mac_tbl(adapter); |
|
|
|
vema_get_veswport_info(port, recvd_mad, rsp_mad); |
|
} |
|
|
|
/** |
|
* vema_get_mac_list -- Get the unicast/multicast macs. |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: Received mad contains fields to set vnic parameters |
|
* @rsp_mad: Response mad to be built |
|
* @attr_id: Attribute ID indicating multicast or unicast mac list |
|
*/ |
|
static void vema_get_mac_list(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad, |
|
u16 attr_id) |
|
{ |
|
struct opa_veswport_iface_macs *macs_in, *macs_out; |
|
int max_entries = (OPA_VNIC_EMA_DATA - sizeof(*macs_out)) / ETH_ALEN; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (!adapter) { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
return; |
|
} |
|
|
|
macs_in = (struct opa_veswport_iface_macs *)recvd_mad->data; |
|
macs_out = (struct opa_veswport_iface_macs *)rsp_mad->data; |
|
|
|
macs_out->start_idx = macs_in->start_idx; |
|
if (macs_in->num_macs_in_msg) |
|
macs_out->num_macs_in_msg = macs_in->num_macs_in_msg; |
|
else |
|
macs_out->num_macs_in_msg = cpu_to_be16(max_entries); |
|
|
|
if (attr_id == OPA_EM_ATTR_IFACE_MCAST_MACS) |
|
opa_vnic_query_mcast_macs(adapter, macs_out); |
|
else |
|
opa_vnic_query_ucast_macs(adapter, macs_out); |
|
} |
|
|
|
/** |
|
* vema_get_summary_counters -- Gets summary counters. |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: Received mad contains fields to set vnic parameters |
|
* @rsp_mad: Response mad to be built |
|
*/ |
|
static void vema_get_summary_counters(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_veswport_summary_counters *cntrs; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (adapter) { |
|
cntrs = (struct opa_veswport_summary_counters *)rsp_mad->data; |
|
opa_vnic_get_summary_counters(adapter, cntrs); |
|
} else { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
} |
|
} |
|
|
|
/** |
|
* vema_get_error_counters -- Gets summary counters. |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: Received mad contains fields to set vnic parameters |
|
* @rsp_mad: Response mad to be built |
|
*/ |
|
static void vema_get_error_counters(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
struct opa_veswport_error_counters *cntrs; |
|
struct opa_vnic_adapter *adapter; |
|
|
|
adapter = vema_get_vport_adapter(recvd_mad, port); |
|
if (adapter) { |
|
cntrs = (struct opa_veswport_error_counters *)rsp_mad->data; |
|
opa_vnic_get_error_counters(adapter, cntrs); |
|
} else { |
|
rsp_mad->mad_hdr.status = OPA_VNIC_INVAL_ATTR; |
|
} |
|
} |
|
|
|
/** |
|
* vema_get -- Process received get MAD |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: Received mad |
|
* @rsp_mad: Response mad to be built |
|
*/ |
|
static void vema_get(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
u16 attr_id = be16_to_cpu(recvd_mad->mad_hdr.attr_id); |
|
|
|
switch (attr_id) { |
|
case OPA_EM_ATTR_CLASS_PORT_INFO: |
|
vema_get_class_port_info(port, recvd_mad, rsp_mad); |
|
break; |
|
case OPA_EM_ATTR_VESWPORT_INFO: |
|
vema_get_veswport_info(port, recvd_mad, rsp_mad); |
|
break; |
|
case OPA_EM_ATTR_VESWPORT_MAC_ENTRIES: |
|
vema_get_mac_entries(port, recvd_mad, rsp_mad); |
|
break; |
|
case OPA_EM_ATTR_IFACE_UCAST_MACS: |
|
case OPA_EM_ATTR_IFACE_MCAST_MACS: |
|
vema_get_mac_list(port, recvd_mad, rsp_mad, attr_id); |
|
break; |
|
case OPA_EM_ATTR_VESWPORT_SUMMARY_COUNTERS: |
|
vema_get_summary_counters(port, recvd_mad, rsp_mad); |
|
break; |
|
case OPA_EM_ATTR_VESWPORT_ERROR_COUNTERS: |
|
vema_get_error_counters(port, recvd_mad, rsp_mad); |
|
break; |
|
default: |
|
rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; |
|
break; |
|
} |
|
} |
|
|
|
/** |
|
* vema_set -- Process received set MAD |
|
* @port: source port on which MAD was received |
|
* @recvd_mad: Received mad contains fields to set vnic parameters |
|
* @rsp_mad: Response mad to be built |
|
*/ |
|
static void vema_set(struct opa_vnic_vema_port *port, |
|
struct opa_vnic_vema_mad *recvd_mad, |
|
struct opa_vnic_vema_mad *rsp_mad) |
|
{ |
|
u16 attr_id = be16_to_cpu(recvd_mad->mad_hdr.attr_id); |
|
|
|
switch (attr_id) { |
|
case OPA_EM_ATTR_CLASS_PORT_INFO: |
|
vema_set_class_port_info(port, recvd_mad, rsp_mad); |
|
break; |
|
case OPA_EM_ATTR_VESWPORT_INFO: |
|
vema_set_veswport_info(port, recvd_mad, rsp_mad); |
|
break; |
|
case OPA_EM_ATTR_VESWPORT_MAC_ENTRIES: |
|
vema_set_mac_entries(port, recvd_mad, rsp_mad); |
|
break; |
|
case OPA_EM_ATTR_DELETE_VESW: |
|
vema_set_delete_vesw(port, recvd_mad, rsp_mad); |
|
break; |
|
default: |
|
rsp_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; |
|
break; |
|
} |
|
} |
|
|
|
/** |
|
* vema_send -- Send handler for VEMA MAD agent |
|
* @mad_agent: pointer to the mad agent |
|
* @mad_wc: pointer to mad send work completion information |
|
* |
|
* Free all the data structures associated with the sent MAD |
|
*/ |
|
static void vema_send(struct ib_mad_agent *mad_agent, |
|
struct ib_mad_send_wc *mad_wc) |
|
{ |
|
rdma_destroy_ah(mad_wc->send_buf->ah, RDMA_DESTROY_AH_SLEEPABLE); |
|
ib_free_send_mad(mad_wc->send_buf); |
|
} |
|
|
|
/** |
|
* vema_recv -- Recv handler for VEMA MAD agent |
|
* @mad_agent: pointer to the mad agent |
|
* @send_buf: Send buffer if found, else NULL |
|
* @mad_wc: pointer to mad send work completion information |
|
* |
|
* Handle only set and get methods and respond to other methods |
|
* as unsupported. Allocate response buffer and address handle |
|
* for the response MAD. |
|
*/ |
|
static void vema_recv(struct ib_mad_agent *mad_agent, |
|
struct ib_mad_send_buf *send_buf, |
|
struct ib_mad_recv_wc *mad_wc) |
|
{ |
|
struct opa_vnic_vema_port *port; |
|
struct ib_ah *ah; |
|
struct ib_mad_send_buf *rsp; |
|
struct opa_vnic_vema_mad *vema_mad; |
|
|
|
if (!mad_wc || !mad_wc->recv_buf.mad) |
|
return; |
|
|
|
port = mad_agent->context; |
|
ah = ib_create_ah_from_wc(mad_agent->qp->pd, mad_wc->wc, |
|
mad_wc->recv_buf.grh, mad_agent->port_num); |
|
if (IS_ERR(ah)) |
|
goto free_recv_mad; |
|
|
|
rsp = ib_create_send_mad(mad_agent, mad_wc->wc->src_qp, |
|
mad_wc->wc->pkey_index, 0, |
|
IB_MGMT_VENDOR_HDR, OPA_VNIC_EMA_DATA, |
|
GFP_KERNEL, OPA_MGMT_BASE_VERSION); |
|
if (IS_ERR(rsp)) |
|
goto err_rsp; |
|
|
|
rsp->ah = ah; |
|
vema_mad = rsp->mad; |
|
memcpy(vema_mad, mad_wc->recv_buf.mad, IB_MGMT_VENDOR_HDR); |
|
vema_mad->mad_hdr.method = IB_MGMT_METHOD_GET_RESP; |
|
vema_mad->mad_hdr.status = 0; |
|
|
|
/* Lock ensures network adapter is not removed */ |
|
mutex_lock(&port->lock); |
|
|
|
switch (mad_wc->recv_buf.mad->mad_hdr.method) { |
|
case IB_MGMT_METHOD_GET: |
|
vema_get(port, (struct opa_vnic_vema_mad *)mad_wc->recv_buf.mad, |
|
vema_mad); |
|
break; |
|
case IB_MGMT_METHOD_SET: |
|
vema_set(port, (struct opa_vnic_vema_mad *)mad_wc->recv_buf.mad, |
|
vema_mad); |
|
break; |
|
default: |
|
vema_mad->mad_hdr.status = OPA_VNIC_UNSUP_ATTR; |
|
break; |
|
} |
|
mutex_unlock(&port->lock); |
|
|
|
if (!ib_post_send_mad(rsp, NULL)) { |
|
/* |
|
* with post send successful ah and send mad |
|
* will be destroyed in send handler |
|
*/ |
|
goto free_recv_mad; |
|
} |
|
|
|
ib_free_send_mad(rsp); |
|
|
|
err_rsp: |
|
rdma_destroy_ah(ah, RDMA_DESTROY_AH_SLEEPABLE); |
|
free_recv_mad: |
|
ib_free_recv_mad(mad_wc); |
|
} |
|
|
|
/** |
|
* vema_get_port -- Gets the opa_vnic_vema_port |
|
* @cport: pointer to control dev |
|
* @port_num: Port number |
|
* |
|
* This function loops through the ports and returns |
|
* the opa_vnic_vema port structure that is associated |
|
* with the OPA port number |
|
* |
|
* Return: ptr to requested opa_vnic_vema_port strucure |
|
* if success, NULL if not |
|
*/ |
|
static struct opa_vnic_vema_port * |
|
vema_get_port(struct opa_vnic_ctrl_port *cport, u8 port_num) |
|
{ |
|
struct opa_vnic_vema_port *port = (void *)cport + sizeof(*cport); |
|
|
|
if (port_num > cport->num_ports) |
|
return NULL; |
|
|
|
return port + (port_num - 1); |
|
} |
|
|
|
/** |
|
* opa_vnic_vema_send_trap -- This function sends a trap to the EM |
|
* @adapter: pointer to vnic adapter |
|
* @data: pointer to trap data filled by calling function |
|
* @lid: issuers lid (encap_slid from vesw_port_info) |
|
* |
|
* This function is called from the VNIC driver to send a trap if there |
|
* is somethng the EM should be notified about. These events currently |
|
* are |
|
* 1) UNICAST INTERFACE MACADDRESS changes |
|
* 2) MULTICAST INTERFACE MACADDRESS changes |
|
* 3) ETHERNET LINK STATUS changes |
|
* While allocating the send mad the remote site qpn used is 1 |
|
* as this is the well known QP. |
|
* |
|
*/ |
|
void opa_vnic_vema_send_trap(struct opa_vnic_adapter *adapter, |
|
struct __opa_veswport_trap *data, u32 lid) |
|
{ |
|
struct opa_vnic_ctrl_port *cport = adapter->cport; |
|
struct ib_mad_send_buf *send_buf; |
|
struct opa_vnic_vema_port *port; |
|
struct ib_device *ibp; |
|
struct opa_vnic_vema_mad_trap *trap_mad; |
|
struct opa_class_port_info *class; |
|
struct rdma_ah_attr ah_attr; |
|
struct ib_ah *ah; |
|
struct opa_veswport_trap *trap; |
|
u32 trap_lid; |
|
u16 pkey_idx; |
|
|
|
if (!cport) |
|
goto err_exit; |
|
ibp = cport->ibdev; |
|
port = vema_get_port(cport, data->opaportnum); |
|
if (!port || !port->mad_agent) |
|
goto err_exit; |
|
|
|
if (time_before(jiffies, adapter->trap_timeout)) { |
|
if (adapter->trap_count == OPA_VNIC_TRAP_BURST_LIMIT) { |
|
v_warn("Trap rate exceeded\n"); |
|
goto err_exit; |
|
} else { |
|
adapter->trap_count++; |
|
} |
|
} else { |
|
adapter->trap_count = 0; |
|
} |
|
|
|
class = &port->class_port_info; |
|
/* Set up address handle */ |
|
memset(&ah_attr, 0, sizeof(ah_attr)); |
|
ah_attr.type = rdma_ah_find_type(ibp, port->port_num); |
|
rdma_ah_set_sl(&ah_attr, |
|
GET_TRAP_SL_FROM_CLASS_PORT_INFO(class->trap_sl_rsvd)); |
|
rdma_ah_set_port_num(&ah_attr, port->port_num); |
|
trap_lid = be32_to_cpu(class->trap_lid); |
|
/* |
|
* check for trap lid validity, must not be zero |
|
* The trap sink could change after we fashion the MAD but since traps |
|
* are not guaranteed we won't use a lock as anyway the change will take |
|
* place even with locking. |
|
*/ |
|
if (!trap_lid) { |
|
c_err("%s: Invalid dlid\n", __func__); |
|
goto err_exit; |
|
} |
|
|
|
rdma_ah_set_dlid(&ah_attr, trap_lid); |
|
ah = rdma_create_ah(port->mad_agent->qp->pd, &ah_attr, 0); |
|
if (IS_ERR(ah)) { |
|
c_err("%s:Couldn't create new AH = %p\n", __func__, ah); |
|
c_err("%s:dlid = %d, sl = %d, port = %d\n", __func__, |
|
rdma_ah_get_dlid(&ah_attr), rdma_ah_get_sl(&ah_attr), |
|
rdma_ah_get_port_num(&ah_attr)); |
|
goto err_exit; |
|
} |
|
|
|
if (ib_find_pkey(ibp, data->opaportnum, IB_DEFAULT_PKEY_FULL, |
|
&pkey_idx) < 0) { |
|
c_err("%s:full key not found, defaulting to partial\n", |
|
__func__); |
|
if (ib_find_pkey(ibp, data->opaportnum, IB_DEFAULT_PKEY_PARTIAL, |
|
&pkey_idx) < 0) |
|
pkey_idx = 1; |
|
} |
|
|
|
send_buf = ib_create_send_mad(port->mad_agent, 1, pkey_idx, 0, |
|
IB_MGMT_VENDOR_HDR, IB_MGMT_MAD_DATA, |
|
GFP_ATOMIC, OPA_MGMT_BASE_VERSION); |
|
if (IS_ERR(send_buf)) { |
|
c_err("%s:Couldn't allocate send buf\n", __func__); |
|
goto err_sndbuf; |
|
} |
|
|
|
send_buf->ah = ah; |
|
|
|
/* Set up common MAD hdr */ |
|
trap_mad = send_buf->mad; |
|
trap_mad->mad_hdr.base_version = OPA_MGMT_BASE_VERSION; |
|
trap_mad->mad_hdr.mgmt_class = OPA_MGMT_CLASS_INTEL_EMA; |
|
trap_mad->mad_hdr.class_version = OPA_EMA_CLASS_VERSION; |
|
trap_mad->mad_hdr.method = IB_MGMT_METHOD_TRAP; |
|
port->tid++; |
|
trap_mad->mad_hdr.tid = cpu_to_be64(port->tid); |
|
trap_mad->mad_hdr.attr_id = IB_SMP_ATTR_NOTICE; |
|
|
|
/* Set up vendor OUI */ |
|
trap_mad->oui[0] = INTEL_OUI_1; |
|
trap_mad->oui[1] = INTEL_OUI_2; |
|
trap_mad->oui[2] = INTEL_OUI_3; |
|
|
|
/* Setup notice attribute portion */ |
|
trap_mad->notice.gen_type = OPA_INTEL_EMA_NOTICE_TYPE_INFO << 1; |
|
trap_mad->notice.oui_1 = INTEL_OUI_1; |
|
trap_mad->notice.oui_2 = INTEL_OUI_2; |
|
trap_mad->notice.oui_3 = INTEL_OUI_3; |
|
trap_mad->notice.issuer_lid = cpu_to_be32(lid); |
|
|
|
/* copy the actual trap data */ |
|
trap = (struct opa_veswport_trap *)trap_mad->notice.raw_data; |
|
trap->fabric_id = cpu_to_be16(data->fabric_id); |
|
trap->veswid = cpu_to_be16(data->veswid); |
|
trap->veswportnum = cpu_to_be32(data->veswportnum); |
|
trap->opaportnum = cpu_to_be16(data->opaportnum); |
|
trap->veswportindex = data->veswportindex; |
|
trap->opcode = data->opcode; |
|
|
|
/* If successful send set up rate limit timeout else bail */ |
|
if (ib_post_send_mad(send_buf, NULL)) { |
|
ib_free_send_mad(send_buf); |
|
} else { |
|
if (adapter->trap_count) |
|
return; |
|
adapter->trap_timeout = jiffies + |
|
usecs_to_jiffies(OPA_VNIC_TRAP_TIMEOUT); |
|
return; |
|
} |
|
|
|
err_sndbuf: |
|
rdma_destroy_ah(ah, 0); |
|
err_exit: |
|
v_err("Aborting trap\n"); |
|
} |
|
|
|
static void opa_vnic_event(struct ib_event_handler *handler, |
|
struct ib_event *record) |
|
{ |
|
struct opa_vnic_vema_port *port = |
|
container_of(handler, struct opa_vnic_vema_port, event_handler); |
|
struct opa_vnic_ctrl_port *cport = port->cport; |
|
struct opa_vnic_adapter *adapter; |
|
unsigned long index; |
|
|
|
if (record->element.port_num != port->port_num) |
|
return; |
|
|
|
c_dbg("OPA_VNIC received event %d on device %s port %d\n", |
|
record->event, dev_name(&record->device->dev), |
|
record->element.port_num); |
|
|
|
if (record->event != IB_EVENT_PORT_ERR && |
|
record->event != IB_EVENT_PORT_ACTIVE) |
|
return; |
|
|
|
xa_for_each(&port->vports, index, adapter) { |
|
if (record->event == IB_EVENT_PORT_ACTIVE) |
|
netif_carrier_on(adapter->netdev); |
|
else |
|
netif_carrier_off(adapter->netdev); |
|
} |
|
} |
|
|
|
/** |
|
* vema_unregister -- Unregisters agent |
|
* @cport: pointer to control port |
|
* |
|
* This deletes the registration by VEMA for MADs |
|
*/ |
|
static void vema_unregister(struct opa_vnic_ctrl_port *cport) |
|
{ |
|
struct opa_vnic_adapter *adapter; |
|
unsigned long index; |
|
int i; |
|
|
|
for (i = 1; i <= cport->num_ports; i++) { |
|
struct opa_vnic_vema_port *port = vema_get_port(cport, i); |
|
|
|
if (!port->mad_agent) |
|
continue; |
|
|
|
/* Lock ensures no MAD is being processed */ |
|
mutex_lock(&port->lock); |
|
xa_for_each(&port->vports, index, adapter) |
|
opa_vnic_rem_netdev(adapter); |
|
mutex_unlock(&port->lock); |
|
|
|
ib_unregister_mad_agent(port->mad_agent); |
|
port->mad_agent = NULL; |
|
mutex_destroy(&port->lock); |
|
xa_destroy(&port->vports); |
|
ib_unregister_event_handler(&port->event_handler); |
|
} |
|
} |
|
|
|
/** |
|
* vema_register -- Registers agent |
|
* @cport: pointer to control port |
|
* |
|
* This function registers the handlers for the VEMA MADs |
|
* |
|
* Return: returns 0 on success. non zero otherwise |
|
*/ |
|
static int vema_register(struct opa_vnic_ctrl_port *cport) |
|
{ |
|
struct ib_mad_reg_req reg_req = { |
|
.mgmt_class = OPA_MGMT_CLASS_INTEL_EMA, |
|
.mgmt_class_version = OPA_MGMT_BASE_VERSION, |
|
.oui = { INTEL_OUI_1, INTEL_OUI_2, INTEL_OUI_3 } |
|
}; |
|
int i; |
|
|
|
set_bit(IB_MGMT_METHOD_GET, reg_req.method_mask); |
|
set_bit(IB_MGMT_METHOD_SET, reg_req.method_mask); |
|
|
|
/* register ib event handler and mad agent for each port on dev */ |
|
for (i = 1; i <= cport->num_ports; i++) { |
|
struct opa_vnic_vema_port *port = vema_get_port(cport, i); |
|
int ret; |
|
|
|
port->cport = cport; |
|
port->port_num = i; |
|
|
|
INIT_IB_EVENT_HANDLER(&port->event_handler, |
|
cport->ibdev, opa_vnic_event); |
|
ib_register_event_handler(&port->event_handler); |
|
|
|
xa_init(&port->vports); |
|
mutex_init(&port->lock); |
|
port->mad_agent = ib_register_mad_agent(cport->ibdev, i, |
|
IB_QPT_GSI, ®_req, |
|
IB_MGMT_RMPP_VERSION, |
|
vema_send, vema_recv, |
|
port, 0); |
|
if (IS_ERR(port->mad_agent)) { |
|
ret = PTR_ERR(port->mad_agent); |
|
port->mad_agent = NULL; |
|
mutex_destroy(&port->lock); |
|
vema_unregister(cport); |
|
return ret; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* opa_vnic_ctrl_config_dev -- This function sends a trap to the EM |
|
* by way of ib_modify_port to indicate support for ethernet on the |
|
* fabric. |
|
* @cport: pointer to control port |
|
* @en: enable or disable ethernet on fabric support |
|
*/ |
|
static void opa_vnic_ctrl_config_dev(struct opa_vnic_ctrl_port *cport, bool en) |
|
{ |
|
struct ib_port_modify pm = { 0 }; |
|
int i; |
|
|
|
if (en) |
|
pm.set_port_cap_mask = OPA_CAP_MASK3_IsEthOnFabricSupported; |
|
else |
|
pm.clr_port_cap_mask = OPA_CAP_MASK3_IsEthOnFabricSupported; |
|
|
|
for (i = 1; i <= cport->num_ports; i++) |
|
ib_modify_port(cport->ibdev, i, IB_PORT_OPA_MASK_CHG, &pm); |
|
} |
|
|
|
/** |
|
* opa_vnic_vema_add_one -- Handle new ib device |
|
* @device: ib device pointer |
|
* |
|
* Allocate the vnic control port and initialize it. |
|
*/ |
|
static int opa_vnic_vema_add_one(struct ib_device *device) |
|
{ |
|
struct opa_vnic_ctrl_port *cport; |
|
int rc, size = sizeof(*cport); |
|
|
|
if (!rdma_cap_opa_vnic(device)) |
|
return -EOPNOTSUPP; |
|
|
|
size += device->phys_port_cnt * sizeof(struct opa_vnic_vema_port); |
|
cport = kzalloc(size, GFP_KERNEL); |
|
if (!cport) |
|
return -ENOMEM; |
|
|
|
cport->num_ports = device->phys_port_cnt; |
|
cport->ibdev = device; |
|
|
|
/* Initialize opa vnic management agent (vema) */ |
|
rc = vema_register(cport); |
|
if (!rc) |
|
c_info("VNIC client initialized\n"); |
|
|
|
ib_set_client_data(device, &opa_vnic_client, cport); |
|
opa_vnic_ctrl_config_dev(cport, true); |
|
return 0; |
|
} |
|
|
|
/** |
|
* opa_vnic_vema_rem_one -- Handle ib device removal |
|
* @device: ib device pointer |
|
* @client_data: ib client data |
|
* |
|
* Uninitialize and free the vnic control port. |
|
*/ |
|
static void opa_vnic_vema_rem_one(struct ib_device *device, |
|
void *client_data) |
|
{ |
|
struct opa_vnic_ctrl_port *cport = client_data; |
|
|
|
c_info("removing VNIC client\n"); |
|
opa_vnic_ctrl_config_dev(cport, false); |
|
vema_unregister(cport); |
|
kfree(cport); |
|
} |
|
|
|
static int __init opa_vnic_init(void) |
|
{ |
|
int rc; |
|
|
|
rc = ib_register_client(&opa_vnic_client); |
|
if (rc) |
|
pr_err("VNIC driver register failed %d\n", rc); |
|
|
|
return rc; |
|
} |
|
module_init(opa_vnic_init); |
|
|
|
static void opa_vnic_deinit(void) |
|
{ |
|
ib_unregister_client(&opa_vnic_client); |
|
} |
|
module_exit(opa_vnic_deinit); |
|
|
|
MODULE_LICENSE("Dual BSD/GPL"); |
|
MODULE_AUTHOR("Intel Corporation"); |
|
MODULE_DESCRIPTION("Intel OPA Virtual Network driver");
|
|
|