162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#include <linux/netdevice.h> 3462306a36Sopenharmony_ci#include <linux/pci.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "enic_api.h" 3762306a36Sopenharmony_ci#include "usnic_common_pkt_hdr.h" 3862306a36Sopenharmony_ci#include "usnic_fwd.h" 3962306a36Sopenharmony_ci#include "usnic_log.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int usnic_fwd_devcmd_locked(struct usnic_fwd_dev *ufdev, int vnic_idx, 4262306a36Sopenharmony_ci enum vnic_devcmd_cmd cmd, u64 *a0, 4362306a36Sopenharmony_ci u64 *a1) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci int status; 4662306a36Sopenharmony_ci struct net_device *netdev = ufdev->netdev; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci lockdep_assert_held(&ufdev->lock); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci status = enic_api_devcmd_proxy_by_index(netdev, 5162306a36Sopenharmony_ci vnic_idx, 5262306a36Sopenharmony_ci cmd, 5362306a36Sopenharmony_ci a0, a1, 5462306a36Sopenharmony_ci 1000); 5562306a36Sopenharmony_ci if (status) { 5662306a36Sopenharmony_ci if (status == ERR_EINVAL && cmd == CMD_DEL_FILTER) { 5762306a36Sopenharmony_ci usnic_dbg("Dev %s vnic idx %u cmd %u already deleted", 5862306a36Sopenharmony_ci ufdev->name, vnic_idx, cmd); 5962306a36Sopenharmony_ci } else { 6062306a36Sopenharmony_ci usnic_err("Dev %s vnic idx %u cmd %u failed with status %d\n", 6162306a36Sopenharmony_ci ufdev->name, vnic_idx, cmd, 6262306a36Sopenharmony_ci status); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } else { 6562306a36Sopenharmony_ci usnic_dbg("Dev %s vnic idx %u cmd %u success", 6662306a36Sopenharmony_ci ufdev->name, vnic_idx, cmd); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return status; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int usnic_fwd_devcmd(struct usnic_fwd_dev *ufdev, int vnic_idx, 7362306a36Sopenharmony_ci enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int status; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci spin_lock(&ufdev->lock); 7862306a36Sopenharmony_ci status = usnic_fwd_devcmd_locked(ufdev, vnic_idx, cmd, a0, a1); 7962306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return status; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct usnic_fwd_dev *usnic_fwd_dev_alloc(struct pci_dev *pdev) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct usnic_fwd_dev *ufdev; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ufdev = kzalloc(sizeof(*ufdev), GFP_KERNEL); 8962306a36Sopenharmony_ci if (!ufdev) 9062306a36Sopenharmony_ci return NULL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ufdev->pdev = pdev; 9362306a36Sopenharmony_ci ufdev->netdev = pci_get_drvdata(pdev); 9462306a36Sopenharmony_ci spin_lock_init(&ufdev->lock); 9562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(ufdev->name) != sizeof(ufdev->netdev->name)); 9662306a36Sopenharmony_ci strcpy(ufdev->name, ufdev->netdev->name); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return ufdev; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_civoid usnic_fwd_dev_free(struct usnic_fwd_dev *ufdev) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci kfree(ufdev); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid usnic_fwd_set_mac(struct usnic_fwd_dev *ufdev, const char mac[ETH_ALEN]) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci spin_lock(&ufdev->lock); 10962306a36Sopenharmony_ci memcpy(&ufdev->mac, mac, sizeof(ufdev->mac)); 11062306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_civoid usnic_fwd_add_ipaddr(struct usnic_fwd_dev *ufdev, __be32 inaddr) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci spin_lock(&ufdev->lock); 11662306a36Sopenharmony_ci if (!ufdev->inaddr) 11762306a36Sopenharmony_ci ufdev->inaddr = inaddr; 11862306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_civoid usnic_fwd_del_ipaddr(struct usnic_fwd_dev *ufdev) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci spin_lock(&ufdev->lock); 12462306a36Sopenharmony_ci ufdev->inaddr = 0; 12562306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid usnic_fwd_carrier_up(struct usnic_fwd_dev *ufdev) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci spin_lock(&ufdev->lock); 13162306a36Sopenharmony_ci ufdev->link_up = 1; 13262306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid usnic_fwd_carrier_down(struct usnic_fwd_dev *ufdev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci spin_lock(&ufdev->lock); 13862306a36Sopenharmony_ci ufdev->link_up = 0; 13962306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_civoid usnic_fwd_set_mtu(struct usnic_fwd_dev *ufdev, unsigned int mtu) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci spin_lock(&ufdev->lock); 14562306a36Sopenharmony_ci ufdev->mtu = mtu; 14662306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int usnic_fwd_dev_ready_locked(struct usnic_fwd_dev *ufdev) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci lockdep_assert_held(&ufdev->lock); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (!ufdev->link_up) 15462306a36Sopenharmony_ci return -EPERM; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int validate_filter_locked(struct usnic_fwd_dev *ufdev, 16062306a36Sopenharmony_ci struct filter *filter) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci lockdep_assert_held(&ufdev->lock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (filter->type == FILTER_IPV4_5TUPLE) { 16662306a36Sopenharmony_ci if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_AD)) 16762306a36Sopenharmony_ci return -EACCES; 16862306a36Sopenharmony_ci if (!(filter->u.ipv4.flags & FILTER_FIELD_5TUP_DST_PT)) 16962306a36Sopenharmony_ci return -EBUSY; 17062306a36Sopenharmony_ci else if (ufdev->inaddr == 0) 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci else if (filter->u.ipv4.dst_port == 0) 17362306a36Sopenharmony_ci return -ERANGE; 17462306a36Sopenharmony_ci else if (ntohl(ufdev->inaddr) != filter->u.ipv4.dst_addr) 17562306a36Sopenharmony_ci return -EFAULT; 17662306a36Sopenharmony_ci else 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void fill_tlv(struct filter_tlv *tlv, struct filter *filter, 18462306a36Sopenharmony_ci struct filter_action *action) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci tlv->type = CLSF_TLV_FILTER; 18762306a36Sopenharmony_ci tlv->length = sizeof(struct filter); 18862306a36Sopenharmony_ci *((struct filter *)&tlv->val) = *filter; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci tlv = (struct filter_tlv *)((char *)tlv + sizeof(struct filter_tlv) + 19162306a36Sopenharmony_ci sizeof(struct filter)); 19262306a36Sopenharmony_ci tlv->type = CLSF_TLV_ACTION; 19362306a36Sopenharmony_ci tlv->length = sizeof(struct filter_action); 19462306a36Sopenharmony_ci *((struct filter_action *)&tlv->val) = *action; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistruct usnic_fwd_flow* 19862306a36Sopenharmony_ciusnic_fwd_alloc_flow(struct usnic_fwd_dev *ufdev, struct filter *filter, 19962306a36Sopenharmony_ci struct usnic_filter_action *uaction) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct filter_tlv *tlv; 20262306a36Sopenharmony_ci struct pci_dev *pdev; 20362306a36Sopenharmony_ci struct usnic_fwd_flow *flow; 20462306a36Sopenharmony_ci uint64_t a0, a1; 20562306a36Sopenharmony_ci uint64_t tlv_size; 20662306a36Sopenharmony_ci dma_addr_t tlv_pa; 20762306a36Sopenharmony_ci int status; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci pdev = ufdev->pdev; 21062306a36Sopenharmony_ci tlv_size = (2*sizeof(struct filter_tlv) + sizeof(struct filter) + 21162306a36Sopenharmony_ci sizeof(struct filter_action)); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci flow = kzalloc(sizeof(*flow), GFP_ATOMIC); 21462306a36Sopenharmony_ci if (!flow) 21562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci tlv = dma_alloc_coherent(&pdev->dev, tlv_size, &tlv_pa, GFP_ATOMIC); 21862306a36Sopenharmony_ci if (!tlv) { 21962306a36Sopenharmony_ci usnic_err("Failed to allocate memory\n"); 22062306a36Sopenharmony_ci status = -ENOMEM; 22162306a36Sopenharmony_ci goto out_free_flow; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci fill_tlv(tlv, filter, &uaction->action); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci spin_lock(&ufdev->lock); 22762306a36Sopenharmony_ci status = usnic_fwd_dev_ready_locked(ufdev); 22862306a36Sopenharmony_ci if (status) { 22962306a36Sopenharmony_ci usnic_err("Forwarding dev %s not ready with status %d\n", 23062306a36Sopenharmony_ci ufdev->name, status); 23162306a36Sopenharmony_ci goto out_free_tlv; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci status = validate_filter_locked(ufdev, filter); 23562306a36Sopenharmony_ci if (status) { 23662306a36Sopenharmony_ci usnic_err("Failed to validate filter with status %d\n", 23762306a36Sopenharmony_ci status); 23862306a36Sopenharmony_ci goto out_free_tlv; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Issue Devcmd */ 24262306a36Sopenharmony_ci a0 = tlv_pa; 24362306a36Sopenharmony_ci a1 = tlv_size; 24462306a36Sopenharmony_ci status = usnic_fwd_devcmd_locked(ufdev, uaction->vnic_idx, 24562306a36Sopenharmony_ci CMD_ADD_FILTER, &a0, &a1); 24662306a36Sopenharmony_ci if (status) { 24762306a36Sopenharmony_ci usnic_err("VF %s Filter add failed with status:%d", 24862306a36Sopenharmony_ci ufdev->name, status); 24962306a36Sopenharmony_ci status = -EFAULT; 25062306a36Sopenharmony_ci goto out_free_tlv; 25162306a36Sopenharmony_ci } else { 25262306a36Sopenharmony_ci usnic_dbg("VF %s FILTER ID:%llu", ufdev->name, a0); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci flow->flow_id = (uint32_t) a0; 25662306a36Sopenharmony_ci flow->vnic_idx = uaction->vnic_idx; 25762306a36Sopenharmony_ci flow->ufdev = ufdev; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciout_free_tlv: 26062306a36Sopenharmony_ci spin_unlock(&ufdev->lock); 26162306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, tlv_size, tlv, tlv_pa); 26262306a36Sopenharmony_ci if (!status) 26362306a36Sopenharmony_ci return flow; 26462306a36Sopenharmony_ciout_free_flow: 26562306a36Sopenharmony_ci kfree(flow); 26662306a36Sopenharmony_ci return ERR_PTR(status); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ciint usnic_fwd_dealloc_flow(struct usnic_fwd_flow *flow) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int status; 27262306a36Sopenharmony_ci u64 a0, a1; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci a0 = flow->flow_id; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci status = usnic_fwd_devcmd(flow->ufdev, flow->vnic_idx, 27762306a36Sopenharmony_ci CMD_DEL_FILTER, &a0, &a1); 27862306a36Sopenharmony_ci if (status) { 27962306a36Sopenharmony_ci if (status == ERR_EINVAL) { 28062306a36Sopenharmony_ci usnic_dbg("Filter %u already deleted for VF Idx %u pf: %s status: %d", 28162306a36Sopenharmony_ci flow->flow_id, flow->vnic_idx, 28262306a36Sopenharmony_ci flow->ufdev->name, status); 28362306a36Sopenharmony_ci } else { 28462306a36Sopenharmony_ci usnic_err("PF %s VF Idx %u Filter: %u FILTER DELETE failed with status %d", 28562306a36Sopenharmony_ci flow->ufdev->name, flow->vnic_idx, 28662306a36Sopenharmony_ci flow->flow_id, status); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci status = 0; 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * Log the error and fake success to the caller because if 29162306a36Sopenharmony_ci * a flow fails to be deleted in the firmware, it is an 29262306a36Sopenharmony_ci * unrecoverable error. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci } else { 29562306a36Sopenharmony_ci usnic_dbg("PF %s VF Idx %u Filter: %u FILTER DELETED", 29662306a36Sopenharmony_ci flow->ufdev->name, flow->vnic_idx, 29762306a36Sopenharmony_ci flow->flow_id); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci kfree(flow); 30162306a36Sopenharmony_ci return status; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint usnic_fwd_enable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci int status; 30762306a36Sopenharmony_ci struct net_device *pf_netdev; 30862306a36Sopenharmony_ci u64 a0, a1; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci pf_netdev = ufdev->netdev; 31162306a36Sopenharmony_ci a0 = qp_idx; 31262306a36Sopenharmony_ci a1 = CMD_QP_RQWQ; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_ENABLE, 31562306a36Sopenharmony_ci &a0, &a1); 31662306a36Sopenharmony_ci if (status) { 31762306a36Sopenharmony_ci usnic_err("PF %s VNIC Index %u RQ Index: %u ENABLE Failed with status %d", 31862306a36Sopenharmony_ci netdev_name(pf_netdev), 31962306a36Sopenharmony_ci vnic_idx, 32062306a36Sopenharmony_ci qp_idx, 32162306a36Sopenharmony_ci status); 32262306a36Sopenharmony_ci } else { 32362306a36Sopenharmony_ci usnic_dbg("PF %s VNIC Index %u RQ Index: %u ENABLED", 32462306a36Sopenharmony_ci netdev_name(pf_netdev), 32562306a36Sopenharmony_ci vnic_idx, qp_idx); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return status; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciint usnic_fwd_disable_qp(struct usnic_fwd_dev *ufdev, int vnic_idx, int qp_idx) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci int status; 33462306a36Sopenharmony_ci u64 a0, a1; 33562306a36Sopenharmony_ci struct net_device *pf_netdev; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci pf_netdev = ufdev->netdev; 33862306a36Sopenharmony_ci a0 = qp_idx; 33962306a36Sopenharmony_ci a1 = CMD_QP_RQWQ; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci status = usnic_fwd_devcmd(ufdev, vnic_idx, CMD_QP_DISABLE, 34262306a36Sopenharmony_ci &a0, &a1); 34362306a36Sopenharmony_ci if (status) { 34462306a36Sopenharmony_ci usnic_err("PF %s VNIC Index %u RQ Index: %u DISABLE Failed with status %d", 34562306a36Sopenharmony_ci netdev_name(pf_netdev), 34662306a36Sopenharmony_ci vnic_idx, 34762306a36Sopenharmony_ci qp_idx, 34862306a36Sopenharmony_ci status); 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci usnic_dbg("PF %s VNIC Index %u RQ Index: %u DISABLED", 35162306a36Sopenharmony_ci netdev_name(pf_netdev), 35262306a36Sopenharmony_ci vnic_idx, 35362306a36Sopenharmony_ci qp_idx); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return status; 35762306a36Sopenharmony_ci} 358