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