/* 
 | 
 * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved. 
 | 
 * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved. 
 | 
 * 
 | 
 * This software is available to you under a choice of one of two 
 | 
 * licenses.  You may choose to be licensed under the terms of the GNU 
 | 
 * General Public License (GPL) Version 2, available from the file 
 | 
 * COPYING in the main directory of this source tree, or the 
 | 
 * OpenIB.org BSD license below: 
 | 
 * 
 | 
 *     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. 
 | 
 * 
 | 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 | 
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 | 
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 | 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
 | 
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
 | 
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
 | 
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 | 
 * SOFTWARE. 
 | 
 */ 
 | 
  
 | 
#include <linux/string.h> 
 | 
#include <linux/etherdevice.h> 
 | 
  
 | 
#include <linux/mlx4/cmd.h> 
 | 
#include <linux/mlx4/qp.h> 
 | 
#include <linux/export.h> 
 | 
  
 | 
#include "mlx4.h" 
 | 
  
 | 
int mlx4_get_mgm_entry_size(struct mlx4_dev *dev) 
 | 
{ 
 | 
    return 1 << dev->oper_log_mgm_entry_size; 
 | 
} 
 | 
  
 | 
int mlx4_get_qp_per_mgm(struct mlx4_dev *dev) 
 | 
{ 
 | 
    return 4 * (mlx4_get_mgm_entry_size(dev) / 16 - 2); 
 | 
} 
 | 
  
 | 
static int mlx4_QP_FLOW_STEERING_ATTACH(struct mlx4_dev *dev, 
 | 
                    struct mlx4_cmd_mailbox *mailbox, 
 | 
                    u32 size, 
 | 
                    u64 *reg_id) 
 | 
{ 
 | 
    u64 imm; 
 | 
    int err = 0; 
 | 
  
 | 
    err = mlx4_cmd_imm(dev, mailbox->dma, &imm, size, 0, 
 | 
               MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, 
 | 
               MLX4_CMD_NATIVE); 
 | 
    if (err) 
 | 
        return err; 
 | 
    *reg_id = imm; 
 | 
  
 | 
    return err; 
 | 
} 
 | 
  
 | 
static int mlx4_QP_FLOW_STEERING_DETACH(struct mlx4_dev *dev, u64 regid) 
 | 
{ 
 | 
    int err = 0; 
 | 
  
 | 
    err = mlx4_cmd(dev, regid, 0, 0, 
 | 
               MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, 
 | 
               MLX4_CMD_NATIVE); 
 | 
  
 | 
    return err; 
 | 
} 
 | 
  
 | 
static int mlx4_READ_ENTRY(struct mlx4_dev *dev, int index, 
 | 
               struct mlx4_cmd_mailbox *mailbox) 
 | 
{ 
 | 
    return mlx4_cmd_box(dev, 0, mailbox->dma, index, 0, MLX4_CMD_READ_MCG, 
 | 
                MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); 
 | 
} 
 | 
  
 | 
static int mlx4_WRITE_ENTRY(struct mlx4_dev *dev, int index, 
 | 
                struct mlx4_cmd_mailbox *mailbox) 
 | 
{ 
 | 
    return mlx4_cmd(dev, mailbox->dma, index, 0, MLX4_CMD_WRITE_MCG, 
 | 
            MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); 
 | 
} 
 | 
  
 | 
static int mlx4_WRITE_PROMISC(struct mlx4_dev *dev, u8 port, u8 steer, 
 | 
                  struct mlx4_cmd_mailbox *mailbox) 
 | 
{ 
 | 
    u32 in_mod; 
 | 
  
 | 
    in_mod = (u32) port << 16 | steer << 1; 
 | 
    return mlx4_cmd(dev, mailbox->dma, in_mod, 0x1, 
 | 
            MLX4_CMD_WRITE_MCG, MLX4_CMD_TIME_CLASS_A, 
 | 
            MLX4_CMD_NATIVE); 
 | 
} 
 | 
  
 | 
static int mlx4_GID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, 
 | 
             u16 *hash, u8 op_mod) 
 | 
{ 
 | 
    u64 imm; 
 | 
    int err; 
 | 
  
 | 
    err = mlx4_cmd_imm(dev, mailbox->dma, &imm, 0, op_mod, 
 | 
               MLX4_CMD_MGID_HASH, MLX4_CMD_TIME_CLASS_A, 
 | 
               MLX4_CMD_NATIVE); 
 | 
  
 | 
    if (!err) 
 | 
        *hash = imm; 
 | 
  
 | 
    return err; 
 | 
} 
 | 
  
 | 
static struct mlx4_promisc_qp *get_promisc_qp(struct mlx4_dev *dev, u8 port, 
 | 
                          enum mlx4_steer_type steer, 
 | 
                          u32 qpn) 
 | 
{ 
 | 
    struct mlx4_steer *s_steer; 
 | 
    struct mlx4_promisc_qp *pqp; 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return NULL; 
 | 
  
 | 
    s_steer = &mlx4_priv(dev)->steer[port - 1]; 
 | 
  
 | 
    list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) { 
 | 
        if (pqp->qpn == qpn) 
 | 
            return pqp; 
 | 
    } 
 | 
    /* not found */ 
 | 
    return NULL; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * Add new entry to steering data structure. 
 | 
 * All promisc QPs should be added as well 
 | 
 */ 
 | 
static int new_steering_entry(struct mlx4_dev *dev, u8 port, 
 | 
                  enum mlx4_steer_type steer, 
 | 
                  unsigned int index, u32 qpn) 
 | 
{ 
 | 
    struct mlx4_steer *s_steer; 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_mgm *mgm; 
 | 
    u32 members_count; 
 | 
    struct mlx4_steer_index *new_entry; 
 | 
    struct mlx4_promisc_qp *pqp; 
 | 
    struct mlx4_promisc_qp *dqp = NULL; 
 | 
    u32 prot; 
 | 
    int err; 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return -EINVAL; 
 | 
  
 | 
    s_steer = &mlx4_priv(dev)->steer[port - 1]; 
 | 
    new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); 
 | 
    if (!new_entry) 
 | 
        return -ENOMEM; 
 | 
  
 | 
    INIT_LIST_HEAD(&new_entry->duplicates); 
 | 
    new_entry->index = index; 
 | 
    list_add_tail(&new_entry->list, &s_steer->steer_entries[steer]); 
 | 
  
 | 
    /* If the given qpn is also a promisc qp, 
 | 
     * it should be inserted to duplicates list 
 | 
     */ 
 | 
    pqp = get_promisc_qp(dev, port, steer, qpn); 
 | 
    if (pqp) { 
 | 
        dqp = kmalloc(sizeof(*dqp), GFP_KERNEL); 
 | 
        if (!dqp) { 
 | 
            err = -ENOMEM; 
 | 
            goto out_alloc; 
 | 
        } 
 | 
        dqp->qpn = qpn; 
 | 
        list_add_tail(&dqp->list, &new_entry->duplicates); 
 | 
    } 
 | 
  
 | 
    /* if no promisc qps for this vep, we are done */ 
 | 
    if (list_empty(&s_steer->promisc_qps[steer])) 
 | 
        return 0; 
 | 
  
 | 
    /* now need to add all the promisc qps to the new 
 | 
     * steering entry, as they should also receive the packets 
 | 
     * destined to this address */ 
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) { 
 | 
        err = -ENOMEM; 
 | 
        goto out_alloc; 
 | 
    } 
 | 
    mgm = mailbox->buf; 
 | 
  
 | 
    err = mlx4_READ_ENTRY(dev, index, mailbox); 
 | 
    if (err) 
 | 
        goto out_mailbox; 
 | 
  
 | 
    members_count = be32_to_cpu(mgm->members_count) & 0xffffff; 
 | 
    prot = be32_to_cpu(mgm->members_count) >> 30; 
 | 
    list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) { 
 | 
        /* don't add already existing qpn */ 
 | 
        if (pqp->qpn == qpn) 
 | 
            continue; 
 | 
        if (members_count == dev->caps.num_qp_per_mgm) { 
 | 
            /* out of space */ 
 | 
            err = -ENOMEM; 
 | 
            goto out_mailbox; 
 | 
        } 
 | 
  
 | 
        /* add the qpn */ 
 | 
        mgm->qp[members_count++] = cpu_to_be32(pqp->qpn & MGM_QPN_MASK); 
 | 
    } 
 | 
    /* update the qps count and update the entry with all the promisc qps*/ 
 | 
    mgm->members_count = cpu_to_be32(members_count | (prot << 30)); 
 | 
    err = mlx4_WRITE_ENTRY(dev, index, mailbox); 
 | 
  
 | 
out_mailbox: 
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
    if (!err) 
 | 
        return 0; 
 | 
out_alloc: 
 | 
    if (dqp) { 
 | 
        list_del(&dqp->list); 
 | 
        kfree(dqp); 
 | 
    } 
 | 
    list_del(&new_entry->list); 
 | 
    kfree(new_entry); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
/* update the data structures with existing steering entry */ 
 | 
static int existing_steering_entry(struct mlx4_dev *dev, u8 port, 
 | 
                   enum mlx4_steer_type steer, 
 | 
                   unsigned int index, u32 qpn) 
 | 
{ 
 | 
    struct mlx4_steer *s_steer; 
 | 
    struct mlx4_steer_index *tmp_entry, *entry = NULL; 
 | 
    struct mlx4_promisc_qp *pqp; 
 | 
    struct mlx4_promisc_qp *dqp; 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return -EINVAL; 
 | 
  
 | 
    s_steer = &mlx4_priv(dev)->steer[port - 1]; 
 | 
  
 | 
    pqp = get_promisc_qp(dev, port, steer, qpn); 
 | 
    if (!pqp) 
 | 
        return 0; /* nothing to do */ 
 | 
  
 | 
    list_for_each_entry(tmp_entry, &s_steer->steer_entries[steer], list) { 
 | 
        if (tmp_entry->index == index) { 
 | 
            entry = tmp_entry; 
 | 
            break; 
 | 
        } 
 | 
    } 
 | 
    if (unlikely(!entry)) { 
 | 
        mlx4_warn(dev, "Steering entry at index %x is not registered\n", index); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    /* the given qpn is listed as a promisc qpn 
 | 
     * we need to add it as a duplicate to this entry 
 | 
     * for future references */ 
 | 
    list_for_each_entry(dqp, &entry->duplicates, list) { 
 | 
        if (qpn == dqp->qpn) 
 | 
            return 0; /* qp is already duplicated */ 
 | 
    } 
 | 
  
 | 
    /* add the qp as a duplicate on this index */ 
 | 
    dqp = kmalloc(sizeof(*dqp), GFP_KERNEL); 
 | 
    if (!dqp) 
 | 
        return -ENOMEM; 
 | 
    dqp->qpn = qpn; 
 | 
    list_add_tail(&dqp->list, &entry->duplicates); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
/* Check whether a qpn is a duplicate on steering entry 
 | 
 * If so, it should not be removed from mgm */ 
 | 
static bool check_duplicate_entry(struct mlx4_dev *dev, u8 port, 
 | 
                  enum mlx4_steer_type steer, 
 | 
                  unsigned int index, u32 qpn) 
 | 
{ 
 | 
    struct mlx4_steer *s_steer; 
 | 
    struct mlx4_steer_index *tmp_entry, *entry = NULL; 
 | 
    struct mlx4_promisc_qp *dqp, *tmp_dqp; 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return NULL; 
 | 
  
 | 
    s_steer = &mlx4_priv(dev)->steer[port - 1]; 
 | 
  
 | 
    /* if qp is not promisc, it cannot be duplicated */ 
 | 
    if (!get_promisc_qp(dev, port, steer, qpn)) 
 | 
        return false; 
 | 
  
 | 
    /* The qp is promisc qp so it is a duplicate on this index 
 | 
     * Find the index entry, and remove the duplicate */ 
 | 
    list_for_each_entry(tmp_entry, &s_steer->steer_entries[steer], list) { 
 | 
        if (tmp_entry->index == index) { 
 | 
            entry = tmp_entry; 
 | 
            break; 
 | 
        } 
 | 
    } 
 | 
    if (unlikely(!entry)) { 
 | 
        mlx4_warn(dev, "Steering entry for index %x is not registered\n", index); 
 | 
        return false; 
 | 
    } 
 | 
    list_for_each_entry_safe(dqp, tmp_dqp, &entry->duplicates, list) { 
 | 
        if (dqp->qpn == qpn) { 
 | 
            list_del(&dqp->list); 
 | 
            kfree(dqp); 
 | 
        } 
 | 
    } 
 | 
    return true; 
 | 
} 
 | 
  
 | 
/* Returns true if all the QPs != tqpn contained in this entry 
 | 
 * are Promisc QPs. Returns false otherwise. 
 | 
 */ 
 | 
static bool promisc_steering_entry(struct mlx4_dev *dev, u8 port, 
 | 
                   enum mlx4_steer_type steer, 
 | 
                   unsigned int index, u32 tqpn, 
 | 
                   u32 *members_count) 
 | 
{ 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_mgm *mgm; 
 | 
    u32 m_count; 
 | 
    bool ret = false; 
 | 
    int i; 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return false; 
 | 
  
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) 
 | 
        return false; 
 | 
    mgm = mailbox->buf; 
 | 
  
 | 
    if (mlx4_READ_ENTRY(dev, index, mailbox)) 
 | 
        goto out; 
 | 
    m_count = be32_to_cpu(mgm->members_count) & 0xffffff; 
 | 
    if (members_count) 
 | 
        *members_count = m_count; 
 | 
  
 | 
    for (i = 0;  i < m_count; i++) { 
 | 
        u32 qpn = be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK; 
 | 
        if (!get_promisc_qp(dev, port, steer, qpn) && qpn != tqpn) { 
 | 
            /* the qp is not promisc, the entry can't be removed */ 
 | 
            goto out; 
 | 
        } 
 | 
    } 
 | 
    ret = true; 
 | 
out: 
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
    return ret; 
 | 
} 
 | 
  
 | 
/* IF a steering entry contains only promisc QPs, it can be removed. */ 
 | 
static bool can_remove_steering_entry(struct mlx4_dev *dev, u8 port, 
 | 
                      enum mlx4_steer_type steer, 
 | 
                      unsigned int index, u32 tqpn) 
 | 
{ 
 | 
    struct mlx4_steer *s_steer; 
 | 
    struct mlx4_steer_index *entry = NULL, *tmp_entry; 
 | 
    u32 members_count; 
 | 
    bool ret = false; 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return NULL; 
 | 
  
 | 
    s_steer = &mlx4_priv(dev)->steer[port - 1]; 
 | 
  
 | 
    if (!promisc_steering_entry(dev, port, steer, index, 
 | 
                    tqpn, &members_count)) 
 | 
        goto out; 
 | 
  
 | 
    /* All the qps currently registered for this entry are promiscuous, 
 | 
      * Checking for duplicates */ 
 | 
    ret = true; 
 | 
    list_for_each_entry_safe(entry, tmp_entry, &s_steer->steer_entries[steer], list) { 
 | 
        if (entry->index == index) { 
 | 
            if (list_empty(&entry->duplicates) || 
 | 
                members_count == 1) { 
 | 
                struct mlx4_promisc_qp *pqp, *tmp_pqp; 
 | 
                /* If there is only 1 entry in duplicates then 
 | 
                 * this is the QP we want to delete, going over 
 | 
                 * the list and deleting the entry. 
 | 
                 */ 
 | 
                list_del(&entry->list); 
 | 
                list_for_each_entry_safe(pqp, tmp_pqp, 
 | 
                             &entry->duplicates, 
 | 
                             list) { 
 | 
                    list_del(&pqp->list); 
 | 
                    kfree(pqp); 
 | 
                } 
 | 
                kfree(entry); 
 | 
            } else { 
 | 
                /* This entry contains duplicates so it shouldn't be removed */ 
 | 
                ret = false; 
 | 
                goto out; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
out: 
 | 
    return ret; 
 | 
} 
 | 
  
 | 
static int add_promisc_qp(struct mlx4_dev *dev, u8 port, 
 | 
              enum mlx4_steer_type steer, u32 qpn) 
 | 
{ 
 | 
    struct mlx4_steer *s_steer; 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_mgm *mgm; 
 | 
    struct mlx4_steer_index *entry; 
 | 
    struct mlx4_promisc_qp *pqp; 
 | 
    struct mlx4_promisc_qp *dqp; 
 | 
    u32 members_count; 
 | 
    u32 prot; 
 | 
    int i; 
 | 
    bool found; 
 | 
    int err; 
 | 
    struct mlx4_priv *priv = mlx4_priv(dev); 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return -EINVAL; 
 | 
  
 | 
    s_steer = &mlx4_priv(dev)->steer[port - 1]; 
 | 
  
 | 
    mutex_lock(&priv->mcg_table.mutex); 
 | 
  
 | 
    if (get_promisc_qp(dev, port, steer, qpn)) { 
 | 
        err = 0;  /* Noting to do, already exists */ 
 | 
        goto out_mutex; 
 | 
    } 
 | 
  
 | 
    pqp = kmalloc(sizeof(*pqp), GFP_KERNEL); 
 | 
    if (!pqp) { 
 | 
        err = -ENOMEM; 
 | 
        goto out_mutex; 
 | 
    } 
 | 
    pqp->qpn = qpn; 
 | 
  
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) { 
 | 
        err = -ENOMEM; 
 | 
        goto out_alloc; 
 | 
    } 
 | 
    mgm = mailbox->buf; 
 | 
  
 | 
    if (!(mlx4_is_mfunc(dev) && steer == MLX4_UC_STEER)) { 
 | 
        /* The promisc QP needs to be added for each one of the steering 
 | 
         * entries. If it already exists, needs to be added as 
 | 
         * a duplicate for this entry. 
 | 
         */ 
 | 
        list_for_each_entry(entry, 
 | 
                    &s_steer->steer_entries[steer], 
 | 
                    list) { 
 | 
            err = mlx4_READ_ENTRY(dev, entry->index, mailbox); 
 | 
            if (err) 
 | 
                goto out_mailbox; 
 | 
  
 | 
            members_count = be32_to_cpu(mgm->members_count) & 
 | 
                    0xffffff; 
 | 
            prot = be32_to_cpu(mgm->members_count) >> 30; 
 | 
            found = false; 
 | 
            for (i = 0; i < members_count; i++) { 
 | 
                if ((be32_to_cpu(mgm->qp[i]) & 
 | 
                     MGM_QPN_MASK) == qpn) { 
 | 
                    /* Entry already exists. 
 | 
                     * Add to duplicates. 
 | 
                     */ 
 | 
                    dqp = kmalloc(sizeof(*dqp), GFP_KERNEL); 
 | 
                    if (!dqp) { 
 | 
                        err = -ENOMEM; 
 | 
                        goto out_mailbox; 
 | 
                    } 
 | 
                    dqp->qpn = qpn; 
 | 
                    list_add_tail(&dqp->list, 
 | 
                              &entry->duplicates); 
 | 
                    found = true; 
 | 
                } 
 | 
            } 
 | 
            if (!found) { 
 | 
                /* Need to add the qpn to mgm */ 
 | 
                if (members_count == 
 | 
                    dev->caps.num_qp_per_mgm) { 
 | 
                    /* entry is full */ 
 | 
                    err = -ENOMEM; 
 | 
                    goto out_mailbox; 
 | 
                } 
 | 
                mgm->qp[members_count++] = 
 | 
                    cpu_to_be32(qpn & MGM_QPN_MASK); 
 | 
                mgm->members_count = 
 | 
                    cpu_to_be32(members_count | 
 | 
                            (prot << 30)); 
 | 
                err = mlx4_WRITE_ENTRY(dev, entry->index, 
 | 
                               mailbox); 
 | 
                if (err) 
 | 
                    goto out_mailbox; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /* add the new qpn to list of promisc qps */ 
 | 
    list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]); 
 | 
    /* now need to add all the promisc qps to default entry */ 
 | 
    memset(mgm, 0, sizeof(*mgm)); 
 | 
    members_count = 0; 
 | 
    list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) { 
 | 
        if (members_count == dev->caps.num_qp_per_mgm) { 
 | 
            /* entry is full */ 
 | 
            err = -ENOMEM; 
 | 
            goto out_list; 
 | 
        } 
 | 
        mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK); 
 | 
    } 
 | 
    mgm->members_count = cpu_to_be32(members_count | MLX4_PROT_ETH << 30); 
 | 
  
 | 
    err = mlx4_WRITE_PROMISC(dev, port, steer, mailbox); 
 | 
    if (err) 
 | 
        goto out_list; 
 | 
  
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
    mutex_unlock(&priv->mcg_table.mutex); 
 | 
    return 0; 
 | 
  
 | 
out_list: 
 | 
    list_del(&pqp->list); 
 | 
out_mailbox: 
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
out_alloc: 
 | 
    kfree(pqp); 
 | 
out_mutex: 
 | 
    mutex_unlock(&priv->mcg_table.mutex); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static int remove_promisc_qp(struct mlx4_dev *dev, u8 port, 
 | 
                 enum mlx4_steer_type steer, u32 qpn) 
 | 
{ 
 | 
    struct mlx4_priv *priv = mlx4_priv(dev); 
 | 
    struct mlx4_steer *s_steer; 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_mgm *mgm; 
 | 
    struct mlx4_steer_index *entry, *tmp_entry; 
 | 
    struct mlx4_promisc_qp *pqp; 
 | 
    struct mlx4_promisc_qp *dqp; 
 | 
    u32 members_count; 
 | 
    bool found; 
 | 
    bool back_to_list = false; 
 | 
    int i; 
 | 
    int err; 
 | 
  
 | 
    if (port < 1 || port > dev->caps.num_ports) 
 | 
        return -EINVAL; 
 | 
  
 | 
    s_steer = &mlx4_priv(dev)->steer[port - 1]; 
 | 
    mutex_lock(&priv->mcg_table.mutex); 
 | 
  
 | 
    pqp = get_promisc_qp(dev, port, steer, qpn); 
 | 
    if (unlikely(!pqp)) { 
 | 
        mlx4_warn(dev, "QP %x is not promiscuous QP\n", qpn); 
 | 
        /* nothing to do */ 
 | 
        err = 0; 
 | 
        goto out_mutex; 
 | 
    } 
 | 
  
 | 
    /*remove from list of promisc qps */ 
 | 
    list_del(&pqp->list); 
 | 
  
 | 
    /* set the default entry not to include the removed one */ 
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) { 
 | 
        err = -ENOMEM; 
 | 
        back_to_list = true; 
 | 
        goto out_list; 
 | 
    } 
 | 
    mgm = mailbox->buf; 
 | 
    members_count = 0; 
 | 
    list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) 
 | 
        mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK); 
 | 
    mgm->members_count = cpu_to_be32(members_count | MLX4_PROT_ETH << 30); 
 | 
  
 | 
    err = mlx4_WRITE_PROMISC(dev, port, steer, mailbox); 
 | 
    if (err) 
 | 
        goto out_mailbox; 
 | 
  
 | 
    if (!(mlx4_is_mfunc(dev) && steer == MLX4_UC_STEER)) { 
 | 
        /* Remove the QP from all the steering entries */ 
 | 
        list_for_each_entry_safe(entry, tmp_entry, 
 | 
                     &s_steer->steer_entries[steer], 
 | 
                     list) { 
 | 
            found = false; 
 | 
            list_for_each_entry(dqp, &entry->duplicates, list) { 
 | 
                if (dqp->qpn == qpn) { 
 | 
                    found = true; 
 | 
                    break; 
 | 
                } 
 | 
            } 
 | 
            if (found) { 
 | 
                /* A duplicate, no need to change the MGM, 
 | 
                 * only update the duplicates list 
 | 
                 */ 
 | 
                list_del(&dqp->list); 
 | 
                kfree(dqp); 
 | 
            } else { 
 | 
                int loc = -1; 
 | 
  
 | 
                err = mlx4_READ_ENTRY(dev, 
 | 
                              entry->index, 
 | 
                              mailbox); 
 | 
                if (err) 
 | 
                    goto out_mailbox; 
 | 
                members_count = 
 | 
                    be32_to_cpu(mgm->members_count) & 
 | 
                    0xffffff; 
 | 
                if (!members_count) { 
 | 
                    mlx4_warn(dev, "QP %06x wasn't found in entry %x mcount=0. deleting entry...\n", 
 | 
                          qpn, entry->index); 
 | 
                    list_del(&entry->list); 
 | 
                    kfree(entry); 
 | 
                    continue; 
 | 
                } 
 | 
  
 | 
                for (i = 0; i < members_count; ++i) 
 | 
                    if ((be32_to_cpu(mgm->qp[i]) & 
 | 
                         MGM_QPN_MASK) == qpn) { 
 | 
                        loc = i; 
 | 
                        break; 
 | 
                    } 
 | 
  
 | 
                if (loc < 0) { 
 | 
                    mlx4_err(dev, "QP %06x wasn't found in entry %d\n", 
 | 
                         qpn, entry->index); 
 | 
                    err = -EINVAL; 
 | 
                    goto out_mailbox; 
 | 
                } 
 | 
  
 | 
                /* Copy the last QP in this MGM 
 | 
                 * over removed QP 
 | 
                 */ 
 | 
                mgm->qp[loc] = mgm->qp[members_count - 1]; 
 | 
                mgm->qp[members_count - 1] = 0; 
 | 
                mgm->members_count = 
 | 
                    cpu_to_be32(--members_count | 
 | 
                            (MLX4_PROT_ETH << 30)); 
 | 
  
 | 
                err = mlx4_WRITE_ENTRY(dev, 
 | 
                               entry->index, 
 | 
                               mailbox); 
 | 
                if (err) 
 | 
                    goto out_mailbox; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
out_mailbox: 
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
out_list: 
 | 
    if (back_to_list) 
 | 
        list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]); 
 | 
    else 
 | 
        kfree(pqp); 
 | 
out_mutex: 
 | 
    mutex_unlock(&priv->mcg_table.mutex); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
/* 
 | 
 * Caller must hold MCG table semaphore.  gid and mgm parameters must 
 | 
 * be properly aligned for command interface. 
 | 
 * 
 | 
 *  Returns 0 unless a firmware command error occurs. 
 | 
 * 
 | 
 * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1 
 | 
 * and *mgm holds MGM entry. 
 | 
 * 
 | 
 * if GID is found in AMGM, *index = index in AMGM, *prev = index of 
 | 
 * previous entry in hash chain and *mgm holds AMGM entry. 
 | 
 * 
 | 
 * If no AMGM exists for given gid, *index = -1, *prev = index of last 
 | 
 * entry in hash chain and *mgm holds end of hash chain. 
 | 
 */ 
 | 
static int find_entry(struct mlx4_dev *dev, u8 port, 
 | 
              u8 *gid, enum mlx4_protocol prot, 
 | 
              struct mlx4_cmd_mailbox *mgm_mailbox, 
 | 
              int *prev, int *index) 
 | 
{ 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_mgm *mgm = mgm_mailbox->buf; 
 | 
    u8 *mgid; 
 | 
    int err; 
 | 
    u16 hash; 
 | 
    u8 op_mod = (prot == MLX4_PROT_ETH) ? 
 | 
        !!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) : 0; 
 | 
  
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) 
 | 
        return -ENOMEM; 
 | 
    mgid = mailbox->buf; 
 | 
  
 | 
    memcpy(mgid, gid, 16); 
 | 
  
 | 
    err = mlx4_GID_HASH(dev, mailbox, &hash, op_mod); 
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    if (0) 
 | 
        mlx4_dbg(dev, "Hash for %pI6 is %04x\n", gid, hash); 
 | 
  
 | 
    *index = hash; 
 | 
    *prev  = -1; 
 | 
  
 | 
    do { 
 | 
        err = mlx4_READ_ENTRY(dev, *index, mgm_mailbox); 
 | 
        if (err) 
 | 
            return err; 
 | 
  
 | 
        if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) { 
 | 
            if (*index != hash) { 
 | 
                mlx4_err(dev, "Found zero MGID in AMGM\n"); 
 | 
                err = -EINVAL; 
 | 
            } 
 | 
            return err; 
 | 
        } 
 | 
  
 | 
        if (!memcmp(mgm->gid, gid, 16) && 
 | 
            be32_to_cpu(mgm->members_count) >> 30 == prot) 
 | 
            return err; 
 | 
  
 | 
        *prev = *index; 
 | 
        *index = be32_to_cpu(mgm->next_gid_index) >> 6; 
 | 
    } while (*index); 
 | 
  
 | 
    *index = -1; 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static const u8 __promisc_mode[] = { 
 | 
    [MLX4_FS_REGULAR]   = 0x0, 
 | 
    [MLX4_FS_ALL_DEFAULT] = 0x1, 
 | 
    [MLX4_FS_MC_DEFAULT] = 0x3, 
 | 
    [MLX4_FS_MIRROR_RX_PORT] = 0x4, 
 | 
    [MLX4_FS_MIRROR_SX_PORT] = 0x5, 
 | 
    [MLX4_FS_UC_SNIFFER] = 0x6, 
 | 
    [MLX4_FS_MC_SNIFFER] = 0x7, 
 | 
}; 
 | 
  
 | 
int mlx4_map_sw_to_hw_steering_mode(struct mlx4_dev *dev, 
 | 
                    enum mlx4_net_trans_promisc_mode flow_type) 
 | 
{ 
 | 
    if (flow_type >= MLX4_FS_MODE_NUM) { 
 | 
        mlx4_err(dev, "Invalid flow type. type = %d\n", flow_type); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
    return __promisc_mode[flow_type]; 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_map_sw_to_hw_steering_mode); 
 | 
  
 | 
static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl, 
 | 
                  struct mlx4_net_trans_rule_hw_ctrl *hw) 
 | 
{ 
 | 
    u8 flags = 0; 
 | 
  
 | 
    flags = ctrl->queue_mode == MLX4_NET_TRANS_Q_LIFO ? 1 : 0; 
 | 
    flags |= ctrl->exclusive ? (1 << 2) : 0; 
 | 
    flags |= ctrl->allow_loopback ? (1 << 3) : 0; 
 | 
  
 | 
    hw->flags = flags; 
 | 
    hw->type = __promisc_mode[ctrl->promisc_mode]; 
 | 
    hw->prio = cpu_to_be16(ctrl->priority); 
 | 
    hw->port = ctrl->port; 
 | 
    hw->qpn = cpu_to_be32(ctrl->qpn); 
 | 
} 
 | 
  
 | 
const u16 __sw_id_hw[] = { 
 | 
    [MLX4_NET_TRANS_RULE_ID_ETH]     = 0xE001, 
 | 
    [MLX4_NET_TRANS_RULE_ID_IB]      = 0xE005, 
 | 
    [MLX4_NET_TRANS_RULE_ID_IPV6]    = 0xE003, 
 | 
    [MLX4_NET_TRANS_RULE_ID_IPV4]    = 0xE002, 
 | 
    [MLX4_NET_TRANS_RULE_ID_TCP]     = 0xE004, 
 | 
    [MLX4_NET_TRANS_RULE_ID_UDP]     = 0xE006, 
 | 
    [MLX4_NET_TRANS_RULE_ID_VXLAN]     = 0xE008 
 | 
}; 
 | 
  
 | 
int mlx4_map_sw_to_hw_steering_id(struct mlx4_dev *dev, 
 | 
                  enum mlx4_net_trans_rule_id id) 
 | 
{ 
 | 
    if (id >= MLX4_NET_TRANS_RULE_NUM) { 
 | 
        mlx4_err(dev, "Invalid network rule id. id = %d\n", id); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
    return __sw_id_hw[id]; 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_map_sw_to_hw_steering_id); 
 | 
  
 | 
static const int __rule_hw_sz[] = { 
 | 
    [MLX4_NET_TRANS_RULE_ID_ETH] = 
 | 
        sizeof(struct mlx4_net_trans_rule_hw_eth), 
 | 
    [MLX4_NET_TRANS_RULE_ID_IB] = 
 | 
        sizeof(struct mlx4_net_trans_rule_hw_ib), 
 | 
    [MLX4_NET_TRANS_RULE_ID_IPV6] = 0, 
 | 
    [MLX4_NET_TRANS_RULE_ID_IPV4] = 
 | 
        sizeof(struct mlx4_net_trans_rule_hw_ipv4), 
 | 
    [MLX4_NET_TRANS_RULE_ID_TCP] = 
 | 
        sizeof(struct mlx4_net_trans_rule_hw_tcp_udp), 
 | 
    [MLX4_NET_TRANS_RULE_ID_UDP] = 
 | 
        sizeof(struct mlx4_net_trans_rule_hw_tcp_udp), 
 | 
    [MLX4_NET_TRANS_RULE_ID_VXLAN] = 
 | 
        sizeof(struct mlx4_net_trans_rule_hw_vxlan) 
 | 
}; 
 | 
  
 | 
int mlx4_hw_rule_sz(struct mlx4_dev *dev, 
 | 
           enum mlx4_net_trans_rule_id id) 
 | 
{ 
 | 
    if (id >= MLX4_NET_TRANS_RULE_NUM) { 
 | 
        mlx4_err(dev, "Invalid network rule id. id = %d\n", id); 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return __rule_hw_sz[id]; 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_hw_rule_sz); 
 | 
  
 | 
static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec, 
 | 
                struct _rule_hw *rule_hw) 
 | 
{ 
 | 
    if (mlx4_hw_rule_sz(dev, spec->id) < 0) 
 | 
        return -EINVAL; 
 | 
    memset(rule_hw, 0, mlx4_hw_rule_sz(dev, spec->id)); 
 | 
    rule_hw->id = cpu_to_be16(__sw_id_hw[spec->id]); 
 | 
    rule_hw->size = mlx4_hw_rule_sz(dev, spec->id) >> 2; 
 | 
  
 | 
    switch (spec->id) { 
 | 
    case MLX4_NET_TRANS_RULE_ID_ETH: 
 | 
        memcpy(rule_hw->eth.dst_mac, spec->eth.dst_mac, ETH_ALEN); 
 | 
        memcpy(rule_hw->eth.dst_mac_msk, spec->eth.dst_mac_msk, 
 | 
               ETH_ALEN); 
 | 
        memcpy(rule_hw->eth.src_mac, spec->eth.src_mac, ETH_ALEN); 
 | 
        memcpy(rule_hw->eth.src_mac_msk, spec->eth.src_mac_msk, 
 | 
               ETH_ALEN); 
 | 
        if (spec->eth.ether_type_enable) { 
 | 
            rule_hw->eth.ether_type_enable = 1; 
 | 
            rule_hw->eth.ether_type = spec->eth.ether_type; 
 | 
        } 
 | 
        rule_hw->eth.vlan_tag = spec->eth.vlan_id; 
 | 
        rule_hw->eth.vlan_tag_msk = spec->eth.vlan_id_msk; 
 | 
        break; 
 | 
  
 | 
    case MLX4_NET_TRANS_RULE_ID_IB: 
 | 
        rule_hw->ib.l3_qpn = spec->ib.l3_qpn; 
 | 
        rule_hw->ib.qpn_mask = spec->ib.qpn_msk; 
 | 
        memcpy(&rule_hw->ib.dst_gid, &spec->ib.dst_gid, 16); 
 | 
        memcpy(&rule_hw->ib.dst_gid_msk, &spec->ib.dst_gid_msk, 16); 
 | 
        break; 
 | 
  
 | 
    case MLX4_NET_TRANS_RULE_ID_IPV6: 
 | 
        return -EOPNOTSUPP; 
 | 
  
 | 
    case MLX4_NET_TRANS_RULE_ID_IPV4: 
 | 
        rule_hw->ipv4.src_ip = spec->ipv4.src_ip; 
 | 
        rule_hw->ipv4.src_ip_msk = spec->ipv4.src_ip_msk; 
 | 
        rule_hw->ipv4.dst_ip = spec->ipv4.dst_ip; 
 | 
        rule_hw->ipv4.dst_ip_msk = spec->ipv4.dst_ip_msk; 
 | 
        break; 
 | 
  
 | 
    case MLX4_NET_TRANS_RULE_ID_TCP: 
 | 
    case MLX4_NET_TRANS_RULE_ID_UDP: 
 | 
        rule_hw->tcp_udp.dst_port = spec->tcp_udp.dst_port; 
 | 
        rule_hw->tcp_udp.dst_port_msk = spec->tcp_udp.dst_port_msk; 
 | 
        rule_hw->tcp_udp.src_port = spec->tcp_udp.src_port; 
 | 
        rule_hw->tcp_udp.src_port_msk = spec->tcp_udp.src_port_msk; 
 | 
        break; 
 | 
  
 | 
    case MLX4_NET_TRANS_RULE_ID_VXLAN: 
 | 
        rule_hw->vxlan.vni = 
 | 
            cpu_to_be32(be32_to_cpu(spec->vxlan.vni) << 8); 
 | 
        rule_hw->vxlan.vni_mask = 
 | 
            cpu_to_be32(be32_to_cpu(spec->vxlan.vni_mask) << 8); 
 | 
        break; 
 | 
  
 | 
    default: 
 | 
        return -EINVAL; 
 | 
    } 
 | 
  
 | 
    return __rule_hw_sz[spec->id]; 
 | 
} 
 | 
  
 | 
static void mlx4_err_rule(struct mlx4_dev *dev, char *str, 
 | 
              struct mlx4_net_trans_rule *rule) 
 | 
{ 
 | 
#define BUF_SIZE 256 
 | 
    struct mlx4_spec_list *cur; 
 | 
    char buf[BUF_SIZE]; 
 | 
    int len = 0; 
 | 
  
 | 
    mlx4_err(dev, "%s", str); 
 | 
    len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
             "port = %d prio = 0x%x qp = 0x%x ", 
 | 
             rule->port, rule->priority, rule->qpn); 
 | 
  
 | 
    list_for_each_entry(cur, &rule->list, list) { 
 | 
        switch (cur->id) { 
 | 
        case MLX4_NET_TRANS_RULE_ID_ETH: 
 | 
            len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                     "dmac = %pM ", &cur->eth.dst_mac); 
 | 
            if (cur->eth.ether_type) 
 | 
                len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                         "ethertype = 0x%x ", 
 | 
                         be16_to_cpu(cur->eth.ether_type)); 
 | 
            if (cur->eth.vlan_id) 
 | 
                len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                         "vlan-id = %d ", 
 | 
                         be16_to_cpu(cur->eth.vlan_id)); 
 | 
            break; 
 | 
  
 | 
        case MLX4_NET_TRANS_RULE_ID_IPV4: 
 | 
            if (cur->ipv4.src_ip) 
 | 
                len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                         "src-ip = %pI4 ", 
 | 
                         &cur->ipv4.src_ip); 
 | 
            if (cur->ipv4.dst_ip) 
 | 
                len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                         "dst-ip = %pI4 ", 
 | 
                         &cur->ipv4.dst_ip); 
 | 
            break; 
 | 
  
 | 
        case MLX4_NET_TRANS_RULE_ID_TCP: 
 | 
        case MLX4_NET_TRANS_RULE_ID_UDP: 
 | 
            if (cur->tcp_udp.src_port) 
 | 
                len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                         "src-port = %d ", 
 | 
                         be16_to_cpu(cur->tcp_udp.src_port)); 
 | 
            if (cur->tcp_udp.dst_port) 
 | 
                len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                         "dst-port = %d ", 
 | 
                         be16_to_cpu(cur->tcp_udp.dst_port)); 
 | 
            break; 
 | 
  
 | 
        case MLX4_NET_TRANS_RULE_ID_IB: 
 | 
            len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                     "dst-gid = %pI6\n", cur->ib.dst_gid); 
 | 
            len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                     "dst-gid-mask = %pI6\n", 
 | 
                     cur->ib.dst_gid_msk); 
 | 
            break; 
 | 
  
 | 
        case MLX4_NET_TRANS_RULE_ID_VXLAN: 
 | 
            len += scnprintf(buf + len, BUF_SIZE - len, 
 | 
                     "VNID = %d ", be32_to_cpu(cur->vxlan.vni)); 
 | 
            break; 
 | 
        case MLX4_NET_TRANS_RULE_ID_IPV6: 
 | 
            break; 
 | 
  
 | 
        default: 
 | 
            break; 
 | 
        } 
 | 
    } 
 | 
    len += scnprintf(buf + len, BUF_SIZE - len, "\n"); 
 | 
    mlx4_err(dev, "%s", buf); 
 | 
  
 | 
    if (len >= BUF_SIZE) 
 | 
        mlx4_err(dev, "Network rule error message was truncated, print buffer is too small\n"); 
 | 
} 
 | 
  
 | 
int mlx4_flow_attach(struct mlx4_dev *dev, 
 | 
             struct mlx4_net_trans_rule *rule, u64 *reg_id) 
 | 
{ 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_spec_list *cur; 
 | 
    u32 size = 0; 
 | 
    int ret; 
 | 
  
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) 
 | 
        return PTR_ERR(mailbox); 
 | 
  
 | 
    if (!mlx4_qp_lookup(dev, rule->qpn)) { 
 | 
        mlx4_err_rule(dev, "QP doesn't exist\n", rule); 
 | 
        ret = -EINVAL; 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    trans_rule_ctrl_to_hw(rule, mailbox->buf); 
 | 
  
 | 
    size += sizeof(struct mlx4_net_trans_rule_hw_ctrl); 
 | 
  
 | 
    list_for_each_entry(cur, &rule->list, list) { 
 | 
        ret = parse_trans_rule(dev, cur, mailbox->buf + size); 
 | 
        if (ret < 0) 
 | 
            goto out; 
 | 
  
 | 
        size += ret; 
 | 
    } 
 | 
  
 | 
    ret = mlx4_QP_FLOW_STEERING_ATTACH(dev, mailbox, size >> 2, reg_id); 
 | 
    if (ret == -ENOMEM) { 
 | 
        mlx4_err_rule(dev, 
 | 
                  "mcg table is full. Fail to register network rule\n", 
 | 
                  rule); 
 | 
    } else if (ret) { 
 | 
        if (ret == -ENXIO) { 
 | 
            if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) 
 | 
                mlx4_err_rule(dev, 
 | 
                          "DMFS is not enabled, " 
 | 
                          "failed to register network rule.\n", 
 | 
                          rule); 
 | 
            else 
 | 
                mlx4_err_rule(dev, 
 | 
                          "Rule exceeds the dmfs_high_rate_mode limitations, " 
 | 
                          "failed to register network rule.\n", 
 | 
                          rule); 
 | 
  
 | 
        } else { 
 | 
            mlx4_err_rule(dev, "Fail to register network rule.\n", rule); 
 | 
        } 
 | 
    } 
 | 
  
 | 
out: 
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_flow_attach); 
 | 
  
 | 
int mlx4_flow_detach(struct mlx4_dev *dev, u64 reg_id) 
 | 
{ 
 | 
    int err; 
 | 
  
 | 
    err = mlx4_QP_FLOW_STEERING_DETACH(dev, reg_id); 
 | 
    if (err) 
 | 
        mlx4_err(dev, "Fail to detach network rule. registration id = 0x%llx\n", 
 | 
             reg_id); 
 | 
    return err; 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_flow_detach); 
 | 
  
 | 
int mlx4_tunnel_steer_add(struct mlx4_dev *dev, unsigned char *addr, 
 | 
              int port, int qpn, u16 prio, u64 *reg_id) 
 | 
{ 
 | 
    int err; 
 | 
    struct mlx4_spec_list spec_eth_outer = { {NULL} }; 
 | 
    struct mlx4_spec_list spec_vxlan     = { {NULL} }; 
 | 
    struct mlx4_spec_list spec_eth_inner = { {NULL} }; 
 | 
  
 | 
    struct mlx4_net_trans_rule rule = { 
 | 
        .queue_mode = MLX4_NET_TRANS_Q_FIFO, 
 | 
        .exclusive = 0, 
 | 
        .allow_loopback = 1, 
 | 
        .promisc_mode = MLX4_FS_REGULAR, 
 | 
    }; 
 | 
  
 | 
    __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); 
 | 
  
 | 
    rule.port = port; 
 | 
    rule.qpn = qpn; 
 | 
    rule.priority = prio; 
 | 
    INIT_LIST_HEAD(&rule.list); 
 | 
  
 | 
    spec_eth_outer.id = MLX4_NET_TRANS_RULE_ID_ETH; 
 | 
    memcpy(spec_eth_outer.eth.dst_mac, addr, ETH_ALEN); 
 | 
    memcpy(spec_eth_outer.eth.dst_mac_msk, &mac_mask, ETH_ALEN); 
 | 
  
 | 
    spec_vxlan.id = MLX4_NET_TRANS_RULE_ID_VXLAN;    /* any vxlan header */ 
 | 
    spec_eth_inner.id = MLX4_NET_TRANS_RULE_ID_ETH;     /* any inner eth header */ 
 | 
  
 | 
    list_add_tail(&spec_eth_outer.list, &rule.list); 
 | 
    list_add_tail(&spec_vxlan.list,     &rule.list); 
 | 
    list_add_tail(&spec_eth_inner.list, &rule.list); 
 | 
  
 | 
    err = mlx4_flow_attach(dev, &rule, reg_id); 
 | 
    return err; 
 | 
} 
 | 
EXPORT_SYMBOL(mlx4_tunnel_steer_add); 
 | 
  
 | 
int mlx4_FLOW_STEERING_IB_UC_QP_RANGE(struct mlx4_dev *dev, u32 min_range_qpn, 
 | 
                      u32 max_range_qpn) 
 | 
{ 
 | 
    int err; 
 | 
    u64 in_param; 
 | 
  
 | 
    in_param = ((u64) min_range_qpn) << 32; 
 | 
    in_param |= ((u64) max_range_qpn) & 0xFFFFFFFF; 
 | 
  
 | 
    err = mlx4_cmd(dev, in_param, 0, 0, 
 | 
            MLX4_FLOW_STEERING_IB_UC_QP_RANGE, 
 | 
            MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); 
 | 
  
 | 
    return err; 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_FLOW_STEERING_IB_UC_QP_RANGE); 
 | 
  
 | 
int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], 
 | 
              int block_mcast_loopback, enum mlx4_protocol prot, 
 | 
              enum mlx4_steer_type steer) 
 | 
{ 
 | 
    struct mlx4_priv *priv = mlx4_priv(dev); 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_mgm *mgm; 
 | 
    u32 members_count; 
 | 
    int index = -1, prev; 
 | 
    int link = 0; 
 | 
    int i; 
 | 
    int err; 
 | 
    u8 port = gid[5]; 
 | 
    u8 new_entry = 0; 
 | 
  
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) 
 | 
        return PTR_ERR(mailbox); 
 | 
    mgm = mailbox->buf; 
 | 
  
 | 
    mutex_lock(&priv->mcg_table.mutex); 
 | 
    err = find_entry(dev, port, gid, prot, 
 | 
             mailbox, &prev, &index); 
 | 
    if (err) 
 | 
        goto out; 
 | 
  
 | 
    if (index != -1) { 
 | 
        if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) { 
 | 
            new_entry = 1; 
 | 
            memcpy(mgm->gid, gid, 16); 
 | 
        } 
 | 
    } else { 
 | 
        link = 1; 
 | 
  
 | 
        index = mlx4_bitmap_alloc(&priv->mcg_table.bitmap); 
 | 
        if (index == -1) { 
 | 
            mlx4_err(dev, "No AMGM entries left\n"); 
 | 
            err = -ENOMEM; 
 | 
            goto out; 
 | 
        } 
 | 
        index += dev->caps.num_mgms; 
 | 
  
 | 
        new_entry = 1; 
 | 
        memset(mgm, 0, sizeof(*mgm)); 
 | 
        memcpy(mgm->gid, gid, 16); 
 | 
    } 
 | 
  
 | 
    members_count = be32_to_cpu(mgm->members_count) & 0xffffff; 
 | 
    if (members_count == dev->caps.num_qp_per_mgm) { 
 | 
        mlx4_err(dev, "MGM at index %x is full\n", index); 
 | 
        err = -ENOMEM; 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    for (i = 0; i < members_count; ++i) 
 | 
        if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) { 
 | 
            mlx4_dbg(dev, "QP %06x already a member of MGM\n", qp->qpn); 
 | 
            err = 0; 
 | 
            goto out; 
 | 
        } 
 | 
  
 | 
    if (block_mcast_loopback) 
 | 
        mgm->qp[members_count++] = cpu_to_be32((qp->qpn & MGM_QPN_MASK) | 
 | 
                               (1U << MGM_BLCK_LB_BIT)); 
 | 
    else 
 | 
        mgm->qp[members_count++] = cpu_to_be32(qp->qpn & MGM_QPN_MASK); 
 | 
  
 | 
    mgm->members_count = cpu_to_be32(members_count | (u32) prot << 30); 
 | 
  
 | 
    err = mlx4_WRITE_ENTRY(dev, index, mailbox); 
 | 
    if (err) 
 | 
        goto out; 
 | 
  
 | 
    if (!link) 
 | 
        goto out; 
 | 
  
 | 
    err = mlx4_READ_ENTRY(dev, prev, mailbox); 
 | 
    if (err) 
 | 
        goto out; 
 | 
  
 | 
    mgm->next_gid_index = cpu_to_be32(index << 6); 
 | 
  
 | 
    err = mlx4_WRITE_ENTRY(dev, prev, mailbox); 
 | 
    if (err) 
 | 
        goto out; 
 | 
  
 | 
out: 
 | 
    if (prot == MLX4_PROT_ETH && index != -1) { 
 | 
        /* manage the steering entry for promisc mode */ 
 | 
        if (new_entry) 
 | 
            err = new_steering_entry(dev, port, steer, 
 | 
                         index, qp->qpn); 
 | 
        else 
 | 
            err = existing_steering_entry(dev, port, steer, 
 | 
                              index, qp->qpn); 
 | 
    } 
 | 
    if (err && link && index != -1) { 
 | 
        if (index < dev->caps.num_mgms) 
 | 
            mlx4_warn(dev, "Got AMGM index %d < %d\n", 
 | 
                  index, dev->caps.num_mgms); 
 | 
        else 
 | 
            mlx4_bitmap_free(&priv->mcg_table.bitmap, 
 | 
                     index - dev->caps.num_mgms, MLX4_USE_RR); 
 | 
    } 
 | 
    mutex_unlock(&priv->mcg_table.mutex); 
 | 
  
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], 
 | 
              enum mlx4_protocol prot, enum mlx4_steer_type steer) 
 | 
{ 
 | 
    struct mlx4_priv *priv = mlx4_priv(dev); 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    struct mlx4_mgm *mgm; 
 | 
    u32 members_count; 
 | 
    int prev, index; 
 | 
    int i, loc = -1; 
 | 
    int err; 
 | 
    u8 port = gid[5]; 
 | 
    bool removed_entry = false; 
 | 
  
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) 
 | 
        return PTR_ERR(mailbox); 
 | 
    mgm = mailbox->buf; 
 | 
  
 | 
    mutex_lock(&priv->mcg_table.mutex); 
 | 
  
 | 
    err = find_entry(dev, port, gid, prot, 
 | 
             mailbox, &prev, &index); 
 | 
    if (err) 
 | 
        goto out; 
 | 
  
 | 
    if (index == -1) { 
 | 
        mlx4_err(dev, "MGID %pI6 not found\n", gid); 
 | 
        err = -EINVAL; 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    /* If this QP is also a promisc QP, it shouldn't be removed only if 
 | 
     * at least one none promisc QP is also attached to this MCG 
 | 
     */ 
 | 
    if (prot == MLX4_PROT_ETH && 
 | 
        check_duplicate_entry(dev, port, steer, index, qp->qpn) && 
 | 
        !promisc_steering_entry(dev, port, steer, index, qp->qpn, NULL)) 
 | 
            goto out; 
 | 
  
 | 
    members_count = be32_to_cpu(mgm->members_count) & 0xffffff; 
 | 
    for (i = 0; i < members_count; ++i) 
 | 
        if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) { 
 | 
            loc = i; 
 | 
            break; 
 | 
        } 
 | 
  
 | 
    if (loc == -1) { 
 | 
        mlx4_err(dev, "QP %06x not found in MGM\n", qp->qpn); 
 | 
        err = -EINVAL; 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    /* copy the last QP in this MGM over removed QP */ 
 | 
    mgm->qp[loc] = mgm->qp[members_count - 1]; 
 | 
    mgm->qp[members_count - 1] = 0; 
 | 
    mgm->members_count = cpu_to_be32(--members_count | (u32) prot << 30); 
 | 
  
 | 
    if (prot == MLX4_PROT_ETH) 
 | 
        removed_entry = can_remove_steering_entry(dev, port, steer, 
 | 
                                index, qp->qpn); 
 | 
    if (members_count && (prot != MLX4_PROT_ETH || !removed_entry)) { 
 | 
        err = mlx4_WRITE_ENTRY(dev, index, mailbox); 
 | 
        goto out; 
 | 
    } 
 | 
  
 | 
    /* We are going to delete the entry, members count should be 0 */ 
 | 
    mgm->members_count = cpu_to_be32((u32) prot << 30); 
 | 
  
 | 
    if (prev == -1) { 
 | 
        /* Remove entry from MGM */ 
 | 
        int amgm_index = be32_to_cpu(mgm->next_gid_index) >> 6; 
 | 
        if (amgm_index) { 
 | 
            err = mlx4_READ_ENTRY(dev, amgm_index, mailbox); 
 | 
            if (err) 
 | 
                goto out; 
 | 
        } else 
 | 
            memset(mgm->gid, 0, 16); 
 | 
  
 | 
        err = mlx4_WRITE_ENTRY(dev, index, mailbox); 
 | 
        if (err) 
 | 
            goto out; 
 | 
  
 | 
        if (amgm_index) { 
 | 
            if (amgm_index < dev->caps.num_mgms) 
 | 
                mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d\n", 
 | 
                      index, amgm_index, dev->caps.num_mgms); 
 | 
            else 
 | 
                mlx4_bitmap_free(&priv->mcg_table.bitmap, 
 | 
                         amgm_index - dev->caps.num_mgms, MLX4_USE_RR); 
 | 
        } 
 | 
    } else { 
 | 
        /* Remove entry from AMGM */ 
 | 
        int cur_next_index = be32_to_cpu(mgm->next_gid_index) >> 6; 
 | 
        err = mlx4_READ_ENTRY(dev, prev, mailbox); 
 | 
        if (err) 
 | 
            goto out; 
 | 
  
 | 
        mgm->next_gid_index = cpu_to_be32(cur_next_index << 6); 
 | 
  
 | 
        err = mlx4_WRITE_ENTRY(dev, prev, mailbox); 
 | 
        if (err) 
 | 
            goto out; 
 | 
  
 | 
        if (index < dev->caps.num_mgms) 
 | 
            mlx4_warn(dev, "entry %d had next AMGM index %d < %d\n", 
 | 
                  prev, index, dev->caps.num_mgms); 
 | 
        else 
 | 
            mlx4_bitmap_free(&priv->mcg_table.bitmap, 
 | 
                     index - dev->caps.num_mgms, MLX4_USE_RR); 
 | 
    } 
 | 
  
 | 
out: 
 | 
    mutex_unlock(&priv->mcg_table.mutex); 
 | 
  
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
    if (err && dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) 
 | 
        /* In case device is under an error, return success as a closing command */ 
 | 
        err = 0; 
 | 
    return err; 
 | 
} 
 | 
  
 | 
static int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp, 
 | 
              u8 gid[16], u8 attach, u8 block_loopback, 
 | 
              enum mlx4_protocol prot) 
 | 
{ 
 | 
    struct mlx4_cmd_mailbox *mailbox; 
 | 
    int err = 0; 
 | 
    int qpn; 
 | 
  
 | 
    if (!mlx4_is_mfunc(dev)) 
 | 
        return -EBADF; 
 | 
  
 | 
    mailbox = mlx4_alloc_cmd_mailbox(dev); 
 | 
    if (IS_ERR(mailbox)) 
 | 
        return PTR_ERR(mailbox); 
 | 
  
 | 
    memcpy(mailbox->buf, gid, 16); 
 | 
    qpn = qp->qpn; 
 | 
    qpn |= (prot << 28); 
 | 
    if (attach && block_loopback) 
 | 
        qpn |= (1 << 31); 
 | 
  
 | 
    err = mlx4_cmd(dev, mailbox->dma, qpn, attach, 
 | 
               MLX4_CMD_QP_ATTACH, MLX4_CMD_TIME_CLASS_A, 
 | 
               MLX4_CMD_WRAPPED); 
 | 
  
 | 
    mlx4_free_cmd_mailbox(dev, mailbox); 
 | 
    if (err && !attach && 
 | 
        dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) 
 | 
        err = 0; 
 | 
    return err; 
 | 
} 
 | 
  
 | 
int mlx4_trans_to_dmfs_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, 
 | 
                  u8 gid[16], u8 port, 
 | 
                  int block_mcast_loopback, 
 | 
                  enum mlx4_protocol prot, u64 *reg_id) 
 | 
{ 
 | 
        struct mlx4_spec_list spec = { {NULL} }; 
 | 
        __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); 
 | 
  
 | 
        struct mlx4_net_trans_rule rule = { 
 | 
            .queue_mode = MLX4_NET_TRANS_Q_FIFO, 
 | 
            .exclusive = 0, 
 | 
            .promisc_mode = MLX4_FS_REGULAR, 
 | 
            .priority = MLX4_DOMAIN_NIC, 
 | 
        }; 
 | 
  
 | 
        rule.allow_loopback = !block_mcast_loopback; 
 | 
        rule.port = port; 
 | 
        rule.qpn = qp->qpn; 
 | 
        INIT_LIST_HEAD(&rule.list); 
 | 
  
 | 
        switch (prot) { 
 | 
        case MLX4_PROT_ETH: 
 | 
            spec.id = MLX4_NET_TRANS_RULE_ID_ETH; 
 | 
            memcpy(spec.eth.dst_mac, &gid[10], ETH_ALEN); 
 | 
            memcpy(spec.eth.dst_mac_msk, &mac_mask, ETH_ALEN); 
 | 
            break; 
 | 
  
 | 
        case MLX4_PROT_IB_IPV6: 
 | 
            spec.id = MLX4_NET_TRANS_RULE_ID_IB; 
 | 
            memcpy(spec.ib.dst_gid, gid, 16); 
 | 
            memset(&spec.ib.dst_gid_msk, 0xff, 16); 
 | 
            break; 
 | 
        default: 
 | 
            return -EINVAL; 
 | 
        } 
 | 
        list_add_tail(&spec.list, &rule.list); 
 | 
  
 | 
        return mlx4_flow_attach(dev, &rule, reg_id); 
 | 
} 
 | 
  
 | 
int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], 
 | 
              u8 port, int block_mcast_loopback, 
 | 
              enum mlx4_protocol prot, u64 *reg_id) 
 | 
{ 
 | 
    switch (dev->caps.steering_mode) { 
 | 
    case MLX4_STEERING_MODE_A0: 
 | 
        if (prot == MLX4_PROT_ETH) 
 | 
            return 0; 
 | 
        fallthrough; 
 | 
  
 | 
    case MLX4_STEERING_MODE_B0: 
 | 
        if (prot == MLX4_PROT_ETH) 
 | 
            gid[7] |= (MLX4_MC_STEER << 1); 
 | 
  
 | 
        if (mlx4_is_mfunc(dev)) 
 | 
            return mlx4_QP_ATTACH(dev, qp, gid, 1, 
 | 
                          block_mcast_loopback, prot); 
 | 
        return mlx4_qp_attach_common(dev, qp, gid, 
 | 
                         block_mcast_loopback, prot, 
 | 
                         MLX4_MC_STEER); 
 | 
  
 | 
    case MLX4_STEERING_MODE_DEVICE_MANAGED: 
 | 
        return mlx4_trans_to_dmfs_attach(dev, qp, gid, port, 
 | 
                         block_mcast_loopback, 
 | 
                         prot, reg_id); 
 | 
    default: 
 | 
        return -EINVAL; 
 | 
    } 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_multicast_attach); 
 | 
  
 | 
int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], 
 | 
              enum mlx4_protocol prot, u64 reg_id) 
 | 
{ 
 | 
    switch (dev->caps.steering_mode) { 
 | 
    case MLX4_STEERING_MODE_A0: 
 | 
        if (prot == MLX4_PROT_ETH) 
 | 
            return 0; 
 | 
        fallthrough; 
 | 
  
 | 
    case MLX4_STEERING_MODE_B0: 
 | 
        if (prot == MLX4_PROT_ETH) 
 | 
            gid[7] |= (MLX4_MC_STEER << 1); 
 | 
  
 | 
        if (mlx4_is_mfunc(dev)) 
 | 
            return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot); 
 | 
  
 | 
        return mlx4_qp_detach_common(dev, qp, gid, prot, 
 | 
                         MLX4_MC_STEER); 
 | 
  
 | 
    case MLX4_STEERING_MODE_DEVICE_MANAGED: 
 | 
        return mlx4_flow_detach(dev, reg_id); 
 | 
  
 | 
    default: 
 | 
        return -EINVAL; 
 | 
    } 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_multicast_detach); 
 | 
  
 | 
int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port, 
 | 
                u32 qpn, enum mlx4_net_trans_promisc_mode mode) 
 | 
{ 
 | 
    struct mlx4_net_trans_rule rule = { 
 | 
        .queue_mode = MLX4_NET_TRANS_Q_FIFO, 
 | 
        .exclusive = 0, 
 | 
        .allow_loopback = 1, 
 | 
    }; 
 | 
  
 | 
    u64 *regid_p; 
 | 
  
 | 
    switch (mode) { 
 | 
    case MLX4_FS_ALL_DEFAULT: 
 | 
        regid_p = &dev->regid_promisc_array[port]; 
 | 
        break; 
 | 
    case MLX4_FS_MC_DEFAULT: 
 | 
        regid_p = &dev->regid_allmulti_array[port]; 
 | 
        break; 
 | 
    default: 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    if (*regid_p != 0) 
 | 
        return -1; 
 | 
  
 | 
    rule.promisc_mode = mode; 
 | 
    rule.port = port; 
 | 
    rule.qpn = qpn; 
 | 
    INIT_LIST_HEAD(&rule.list); 
 | 
    mlx4_info(dev, "going promisc on %x\n", port); 
 | 
  
 | 
    return  mlx4_flow_attach(dev, &rule, regid_p); 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_add); 
 | 
  
 | 
int mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port, 
 | 
                   enum mlx4_net_trans_promisc_mode mode) 
 | 
{ 
 | 
    int ret; 
 | 
    u64 *regid_p; 
 | 
  
 | 
    switch (mode) { 
 | 
    case MLX4_FS_ALL_DEFAULT: 
 | 
        regid_p = &dev->regid_promisc_array[port]; 
 | 
        break; 
 | 
    case MLX4_FS_MC_DEFAULT: 
 | 
        regid_p = &dev->regid_allmulti_array[port]; 
 | 
        break; 
 | 
    default: 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    if (*regid_p == 0) 
 | 
        return -1; 
 | 
  
 | 
    ret =  mlx4_flow_detach(dev, *regid_p); 
 | 
    if (ret == 0) 
 | 
        *regid_p = 0; 
 | 
  
 | 
    return ret; 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_remove); 
 | 
  
 | 
int mlx4_unicast_attach(struct mlx4_dev *dev, 
 | 
            struct mlx4_qp *qp, u8 gid[16], 
 | 
            int block_mcast_loopback, enum mlx4_protocol prot) 
 | 
{ 
 | 
    if (prot == MLX4_PROT_ETH) 
 | 
        gid[7] |= (MLX4_UC_STEER << 1); 
 | 
  
 | 
    if (mlx4_is_mfunc(dev)) 
 | 
        return mlx4_QP_ATTACH(dev, qp, gid, 1, 
 | 
                    block_mcast_loopback, prot); 
 | 
  
 | 
    return mlx4_qp_attach_common(dev, qp, gid, block_mcast_loopback, 
 | 
                    prot, MLX4_UC_STEER); 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_unicast_attach); 
 | 
  
 | 
int mlx4_unicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, 
 | 
                   u8 gid[16], enum mlx4_protocol prot) 
 | 
{ 
 | 
    if (prot == MLX4_PROT_ETH) 
 | 
        gid[7] |= (MLX4_UC_STEER << 1); 
 | 
  
 | 
    if (mlx4_is_mfunc(dev)) 
 | 
        return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot); 
 | 
  
 | 
    return mlx4_qp_detach_common(dev, qp, gid, prot, MLX4_UC_STEER); 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_unicast_detach); 
 | 
  
 | 
int mlx4_PROMISC_wrapper(struct mlx4_dev *dev, int slave, 
 | 
             struct mlx4_vhcr *vhcr, 
 | 
             struct mlx4_cmd_mailbox *inbox, 
 | 
             struct mlx4_cmd_mailbox *outbox, 
 | 
             struct mlx4_cmd_info *cmd) 
 | 
{ 
 | 
    u32 qpn = (u32) vhcr->in_param & 0xffffffff; 
 | 
    int port = mlx4_slave_convert_port(dev, slave, vhcr->in_param >> 62); 
 | 
    enum mlx4_steer_type steer = vhcr->in_modifier; 
 | 
  
 | 
    if (port < 0) 
 | 
        return -EINVAL; 
 | 
  
 | 
    /* Promiscuous unicast is not allowed in mfunc */ 
 | 
    if (mlx4_is_mfunc(dev) && steer == MLX4_UC_STEER) 
 | 
        return 0; 
 | 
  
 | 
    if (vhcr->op_modifier) 
 | 
        return add_promisc_qp(dev, port, steer, qpn); 
 | 
    else 
 | 
        return remove_promisc_qp(dev, port, steer, qpn); 
 | 
} 
 | 
  
 | 
static int mlx4_PROMISC(struct mlx4_dev *dev, u32 qpn, 
 | 
            enum mlx4_steer_type steer, u8 add, u8 port) 
 | 
{ 
 | 
    return mlx4_cmd(dev, (u64) qpn | (u64) port << 62, (u32) steer, add, 
 | 
            MLX4_CMD_PROMISC, MLX4_CMD_TIME_CLASS_A, 
 | 
            MLX4_CMD_WRAPPED); 
 | 
} 
 | 
  
 | 
int mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) 
 | 
{ 
 | 
    if (mlx4_is_mfunc(dev)) 
 | 
        return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 1, port); 
 | 
  
 | 
    return add_promisc_qp(dev, port, MLX4_MC_STEER, qpn); 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_add); 
 | 
  
 | 
int mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) 
 | 
{ 
 | 
    if (mlx4_is_mfunc(dev)) 
 | 
        return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 0, port); 
 | 
  
 | 
    return remove_promisc_qp(dev, port, MLX4_MC_STEER, qpn); 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove); 
 | 
  
 | 
int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) 
 | 
{ 
 | 
    if (mlx4_is_mfunc(dev)) 
 | 
        return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 1, port); 
 | 
  
 | 
    return add_promisc_qp(dev, port, MLX4_UC_STEER, qpn); 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add); 
 | 
  
 | 
int mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) 
 | 
{ 
 | 
    if (mlx4_is_mfunc(dev)) 
 | 
        return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 0, port); 
 | 
  
 | 
    return remove_promisc_qp(dev, port, MLX4_UC_STEER, qpn); 
 | 
} 
 | 
EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_remove); 
 | 
  
 | 
int mlx4_init_mcg_table(struct mlx4_dev *dev) 
 | 
{ 
 | 
    struct mlx4_priv *priv = mlx4_priv(dev); 
 | 
    int err; 
 | 
  
 | 
    /* No need for mcg_table when fw managed the mcg table*/ 
 | 
    if (dev->caps.steering_mode == 
 | 
        MLX4_STEERING_MODE_DEVICE_MANAGED) 
 | 
        return 0; 
 | 
    err = mlx4_bitmap_init(&priv->mcg_table.bitmap, dev->caps.num_amgms, 
 | 
                   dev->caps.num_amgms - 1, 0, 0); 
 | 
    if (err) 
 | 
        return err; 
 | 
  
 | 
    mutex_init(&priv->mcg_table.mutex); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
void mlx4_cleanup_mcg_table(struct mlx4_dev *dev) 
 | 
{ 
 | 
    if (dev->caps.steering_mode != 
 | 
        MLX4_STEERING_MODE_DEVICE_MANAGED) 
 | 
        mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap); 
 | 
} 
 |