cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

usnic_fwd.c (8836B)


      1/*
      2 * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved.
      3 *
      4 * This software is available to you under a choice of one of two
      5 * licenses.  You may choose to be licensed under the terms of the GNU
      6 * General Public License (GPL) Version 2, available from the file
      7 * COPYING in the main directory of this source tree, or the
      8 * BSD license below:
      9 *
     10 *     Redistribution and use in source and binary forms, with or
     11 *     without modification, are permitted provided that the following
     12 *     conditions are met:
     13 *
     14 *      - Redistributions of source code must retain the above
     15 *        copyright notice, this list of conditions and the following
     16 *        disclaimer.
     17 *
     18 *      - Redistributions in binary form must reproduce the above
     19 *        copyright notice, this list of conditions and the following
     20 *        disclaimer in the documentation and/or other materials
     21 *        provided with the distribution.
     22 *
     23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     30 * SOFTWARE.
     31 *
     32 */
     33#include <linux/netdevice.h>
     34#include <linux/pci.h>
     35
     36#include "enic_api.h"
     37#include "usnic_common_pkt_hdr.h"
     38#include "usnic_fwd.h"
     39#include "usnic_log.h"
     40
     41static int usnic_fwd_devcmd_locked(struct usnic_fwd_dev *ufdev, int vnic_idx,
     42					enum vnic_devcmd_cmd cmd, u64 *a0,
     43					u64 *a1)
     44{
     45	int status;
     46	struct net_device *netdev = ufdev->netdev;
     47
     48	lockdep_assert_held(&ufdev->lock);
     49
     50	status = enic_api_devcmd_proxy_by_index(netdev,
     51			vnic_idx,
     52			cmd,
     53			a0, a1,
     54			1000);
     55	if (status) {
     56		if (status == ERR_EINVAL && cmd == CMD_DEL_FILTER) {
     57			usnic_dbg("Dev %s vnic idx %u cmd %u already deleted",
     58					ufdev->name, vnic_idx, cmd);
     59		} else {
     60			usnic_err("Dev %s vnic idx %u cmd %u failed with status %d\n",
     61					ufdev->name, vnic_idx, cmd,
     62					status);
     63		}
     64	} else {
     65		usnic_dbg("Dev %s vnic idx %u cmd %u success",
     66				ufdev->name, vnic_idx, cmd);
     67	}
     68
     69	return status;
     70}
     71
     72static int usnic_fwd_devcmd(struct usnic_fwd_dev *ufdev, int vnic_idx,
     73				enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1)
     74{
     75	int status;
     76
     77	spin_lock(&ufdev->lock);
     78	status = usnic_fwd_devcmd_locked(ufdev, vnic_idx, cmd, a0, a1);
     79	spin_unlock(&ufdev->lock);
     80
     81	return status;
     82}
     83
     84struct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev)
     85{
     86	struct usnic_fwd_dev *ufdev;
     87
     88	ufdev = kzalloc(sizeof(*ufdev), GFP_KERNEL);
     89	if (!ufdev)
     90		return NULL;
     91
     92	ufdev->pdev = pdev;
     93	ufdev->netdev = pci_get_drvdata(pdev);
     94	spin_lock_init(&ufdev->lock);
     95	BUILD_BUG_ON(sizeof(ufdev->name) != sizeof(ufdev->netdev->name));
     96	strcpy(ufdev->name, ufdev->netdev->name);
     97
     98	return ufdev;
     99}
    100
    101void usnic_fwd_dev_free(struct usnic_fwd_dev *ufdev)
    102{
    103	kfree(ufdev);
    104}
    105
    106void usnic_fwd_set_mac(struct usnic_fwd_dev *ufdev, const char mac[ETH_ALEN])
    107{
    108	spin_lock(&ufdev->lock);
    109	memcpy(&ufdev->mac, mac, sizeof(ufdev->mac));
    110	spin_unlock(&ufdev->lock);
    111}
    112
    113void usnic_fwd_add_ipaddr(struct usnic_fwd_dev *ufdev, __be32 inaddr)
    114{
    115	spin_lock(&ufdev->lock);
    116	if (!ufdev->inaddr)
    117		ufdev->inaddr = inaddr;
    118	spin_unlock(&ufdev->lock);
    119}
    120
    121void usnic_fwd_del_ipaddr(struct usnic_fwd_dev *ufdev)
    122{
    123	spin_lock(&ufdev->lock);
    124	ufdev->inaddr = 0;
    125	spin_unlock(&ufdev->lock);
    126}
    127
    128void usnic_fwd_carrier_up(struct usnic_fwd_dev *ufdev)
    129{
    130	spin_lock(&ufdev->lock);
    131	ufdev->link_up = 1;
    132	spin_unlock(&ufdev->lock);
    133}
    134
    135void usnic_fwd_carrier_down(struct usnic_fwd_dev *ufdev)
    136{
    137	spin_lock(&ufdev->lock);
    138	ufdev->link_up = 0;
    139	spin_unlock(&ufdev->lock);
    140}
    141
    142void usnic_fwd_set_mtu(struct usnic_fwd_dev *ufdev, unsigned int mtu)
    143{
    144	spin_lock(&ufdev->lock);
    145	ufdev->mtu = mtu;
    146	spin_unlock(&ufdev->lock);
    147}
    148
    149static int usnic_fwd_dev_ready_locked(struct usnic_fwd_dev *ufdev)
    150{
    151	lockdep_assert_held(&ufdev->lock);
    152
    153	if (!ufdev->link_up)
    154		return -EPERM;
    155
    156	return 0;
    157}
    158
    159static int validate_filter_locked(struct usnic_fwd_dev *ufdev,
    160					struct filter *filter)
    161{
    162
    163	lockdep_assert_held(&ufdev->lock);
    164
    165	if (filter->type == FILTER_IPV4_5TUPLE) {
    166		if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_AD))
    167			return -EACCES;
    168		if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_PT))
    169			return -EBUSY;
    170		else if (ufdev->inaddr == 0)
    171			return -EINVAL;
    172		else if (filter->u.ipv4.dst_port == 0)
    173			return -ERANGE;
    174		else if (ntohl(ufdev->inaddr) != filter->u.ipv4.dst_addr)
    175			return -EFAULT;
    176		else
    177			return 0;
    178	}
    179
    180	return 0;
    181}
    182
    183static void fill_tlv(struct filter_tlv *tlv, struct filter *filter,
    184		struct filter_action *action)
    185{
    186	tlv->type = CLSF_TLV_FILTER;
    187	tlv->length = sizeof(struct filter);
    188	*((struct filter *)&tlv->val) = *filter;
    189
    190	tlv = (struct filter_tlv *)((char *)tlv + sizeof(struct filter_tlv) +
    191			sizeof(struct filter));
    192	tlv->type = CLSF_TLV_ACTION;
    193	tlv->length = sizeof(struct filter_action);
    194	*((struct filter_action *)&tlv->val) = *action;
    195}
    196
    197struct usnic_fwd_flow*
    198usnic_fwd_alloc_flow(struct usnic_fwd_dev *ufdev, struct filter *filter,
    199				struct usnic_filter_action *uaction)
    200{
    201	struct filter_tlv *tlv;
    202	struct pci_dev *pdev;
    203	struct usnic_fwd_flow *flow;
    204	uint64_t a0, a1;
    205	uint64_t tlv_size;
    206	dma_addr_t tlv_pa;
    207	int status;
    208
    209	pdev = ufdev->pdev;
    210	tlv_size = (2*sizeof(struct filter_tlv) + sizeof(struct filter) +
    211			sizeof(struct filter_action));
    212
    213	flow = kzalloc(sizeof(*flow), GFP_ATOMIC);
    214	if (!flow)
    215		return ERR_PTR(-ENOMEM);
    216
    217	tlv = dma_alloc_coherent(&pdev->dev, tlv_size, &tlv_pa, GFP_ATOMIC);
    218	if (!tlv) {
    219		usnic_err("Failed to allocate memory\n");
    220		status = -ENOMEM;
    221		goto out_free_flow;
    222	}
    223
    224	fill_tlv(tlv, filter, &uaction->action);
    225
    226	spin_lock(&ufdev->lock);
    227	status = usnic_fwd_dev_ready_locked(ufdev);
    228	if (status) {
    229		usnic_err("Forwarding dev %s not ready with status %d\n",
    230				ufdev->name, status);
    231		goto out_free_tlv;
    232	}
    233
    234	status = validate_filter_locked(ufdev, filter);
    235	if (status) {
    236		usnic_err("Failed to validate filter with status %d\n",
    237				status);
    238		goto out_free_tlv;
    239	}
    240
    241	/* Issue Devcmd */
    242	a0 = tlv_pa;
    243	a1 = tlv_size;
    244	status = usnic_fwd_devcmd_locked(ufdev, uaction->vnic_idx,
    245						CMD_ADD_FILTER, &a0, &a1);
    246	if (status) {
    247		usnic_err("VF %s Filter add failed with status:%d",
    248				ufdev->name, status);
    249		status = -EFAULT;
    250		goto out_free_tlv;
    251	} else {
    252		usnic_dbg("VF %s FILTER ID:%llu", ufdev->name, a0);
    253	}
    254
    255	flow->flow_id = (uint32_t) a0;
    256	flow->vnic_idx = uaction->vnic_idx;
    257	flow->ufdev = ufdev;
    258
    259out_free_tlv:
    260	spin_unlock(&ufdev->lock);
    261	dma_free_coherent(&pdev->dev, tlv_size, tlv, tlv_pa);
    262	if (!status)
    263		return flow;
    264out_free_flow:
    265	kfree(flow);
    266	return ERR_PTR(status);
    267}
    268
    269int usnic_fwd_dealloc_flow(struct usnic_fwd_flow *flow)
    270{
    271	int status;
    272	u64 a0, a1;
    273
    274	a0 = flow->flow_id;
    275
    276	status = usnic_fwd_devcmd(flow->ufdev, flow->vnic_idx,
    277					CMD_DEL_FILTER, &a0, &a1);
    278	if (status) {
    279		if (status == ERR_EINVAL) {
    280			usnic_dbg("Filter %u already deleted for VF Idx %u pf: %s status: %d",
    281					flow->flow_id, flow->vnic_idx,
    282					flow->ufdev->name, status);
    283		} else {
    284			usnic_err("PF %s VF Idx %u Filter: %u FILTER DELETE failed with status %d",
    285					flow->ufdev->name, flow->vnic_idx,
    286					flow->flow_id, status);
    287		}
    288		status = 0;
    289		/*
    290		 * Log the error and fake success to the caller because if
    291		 * a flow fails to be deleted in the firmware, it is an
    292		 * unrecoverable error.
    293		 */
    294	} else {
    295		usnic_dbg("PF %s VF Idx %u Filter: %u FILTER DELETED",
    296				flow->ufdev->name, flow->vnic_idx,
    297				flow->flow_id);
    298	}
    299
    300	kfree(flow);
    301	return status;
    302}
    303
    304int usnic_fwd_enable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx)
    305{
    306	int status;
    307	struct net_device *pf_netdev;
    308	u64 a0, a1;
    309
    310	pf_netdev = ufdev->netdev;
    311	a0 = qp_idx;
    312	a1 = CMD_QP_RQWQ;
    313
    314	status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_ENABLE,
    315						&a0, &a1);
    316	if (status) {
    317		usnic_err("PF %s VNIC Index %u RQ Index: %u ENABLE Failed with status %d",
    318				netdev_name(pf_netdev),
    319				vnic_idx,
    320				qp_idx,
    321				status);
    322	} else {
    323		usnic_dbg("PF %s VNIC Index %u RQ Index: %u ENABLED",
    324				netdev_name(pf_netdev),
    325				vnic_idx, qp_idx);
    326	}
    327
    328	return status;
    329}
    330
    331int usnic_fwd_disable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx)
    332{
    333	int status;
    334	u64 a0, a1;
    335	struct net_device *pf_netdev;
    336
    337	pf_netdev = ufdev->netdev;
    338	a0 = qp_idx;
    339	a1 = CMD_QP_RQWQ;
    340
    341	status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_DISABLE,
    342			&a0, &a1);
    343	if (status) {
    344		usnic_err("PF %s VNIC Index %u RQ Index: %u DISABLE Failed with status %d",
    345				netdev_name(pf_netdev),
    346				vnic_idx,
    347				qp_idx,
    348				status);
    349	} else {
    350		usnic_dbg("PF %s VNIC Index %u RQ Index: %u DISABLED",
    351				netdev_name(pf_netdev),
    352				vnic_idx,
    353				qp_idx);
    354	}
    355
    356	return status;
    357}