brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 18a7085 Raw
358 lines · c
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 two5 * licenses.  You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * BSD license below:9 *10 *     Redistribution and use in source and binary forms, with or11 *     without modification, are permitted provided that the following12 *     conditions are met:13 *14 *      - Redistributions of source code must retain the above15 *        copyright notice, this list of conditions and the following16 *        disclaimer.17 *18 *      - Redistributions in binary form must reproduce the above19 *        copyright notice, this list of conditions and the following20 *        disclaimer in the documentation and/or other materials21 *        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 OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * 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		else177			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 if291		 * a flow fails to be deleted in the firmware, it is an292		 * 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}358