| /* bnx2x_sp.c: Broadcom Everest network driver. |
| * |
| * Copyright 2011 Broadcom Corporation |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed to you |
| * under the terms of the GNU General Public License version 2, available |
| * at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL"). |
| * |
| * Notwithstanding the above, under no circumstances may you combine this |
| * software in any way with any other Broadcom software provided under a |
| * license other than the GPL, without Broadcom's express prior written |
| * consent. |
| * |
| * Maintained by: Eilon Greenstein <eilong@broadcom.com> |
| * Written by: Vladislav Zolotarov |
| * |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #include <linux/module.h> |
| #include <linux/crc32.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| #include <linux/crc32c.h> |
| #include "bnx2x.h" |
| #include "bnx2x_cmn.h" |
| #include "bnx2x_sp.h" |
| |
| #define BNX2X_MAX_EMUL_MULTI 16 |
| |
| #define MAC_LEADING_ZERO_CNT (ALIGN(ETH_ALEN, sizeof(u32)) - ETH_ALEN) |
| |
| /**** Exe Queue interfaces ****/ |
| |
| /** |
| * bnx2x_exe_queue_init - init the Exe Queue object |
| * |
| * @o: poiter to the object |
| * @exe_len: length |
| * @owner: poiter to the owner |
| * @validate: validate function pointer |
| * @optimize: optimize function pointer |
| * @exec: execute function pointer |
| * @get: get function pointer |
| */ |
| static inline void bnx2x_exe_queue_init(struct bnx2x *bp, |
| struct bnx2x_exe_queue_obj *o, |
| int exe_len, |
| union bnx2x_qable_obj *owner, |
| exe_q_validate validate, |
| exe_q_remove remove, |
| exe_q_optimize optimize, |
| exe_q_execute exec, |
| exe_q_get get) |
| { |
| memset(o, 0, sizeof(*o)); |
| |
| INIT_LIST_HEAD(&o->exe_queue); |
| INIT_LIST_HEAD(&o->pending_comp); |
| |
| spin_lock_init(&o->lock); |
| |
| o->exe_chunk_len = exe_len; |
| o->owner = owner; |
| |
| /* Owner specific callbacks */ |
| o->validate = validate; |
| o->remove = remove; |
| o->optimize = optimize; |
| o->execute = exec; |
| o->get = get; |
| |
| DP(BNX2X_MSG_SP, "Setup the execution queue with the chunk " |
| "length of %d\n", exe_len); |
| } |
| |
| static inline void bnx2x_exe_queue_free_elem(struct bnx2x *bp, |
| struct bnx2x_exeq_elem *elem) |
| { |
| DP(BNX2X_MSG_SP, "Deleting an exe_queue element\n"); |
| kfree(elem); |
| } |
| |
| static inline int bnx2x_exe_queue_length(struct bnx2x_exe_queue_obj *o) |
| { |
| struct bnx2x_exeq_elem *elem; |
| int cnt = 0; |
| |
| spin_lock_bh(&o->lock); |
| |
| list_for_each_entry(elem, &o->exe_queue, link) |
| cnt++; |
| |
| spin_unlock_bh(&o->lock); |
| |
| return cnt; |
| } |
| |
| /** |
| * bnx2x_exe_queue_add - add a new element to the execution queue |
| * |
| * @bp: driver handle |
| * @o: queue |
| * @cmd: new command to add |
| * @restore: true - do not optimize the command |
| * |
| * If the element is optimized or is illegal, frees it. |
| */ |
| static inline int bnx2x_exe_queue_add(struct bnx2x *bp, |
| struct bnx2x_exe_queue_obj *o, |
| struct bnx2x_exeq_elem *elem, |
| bool restore) |
| { |
| int rc; |
| |
| spin_lock_bh(&o->lock); |
| |
| if (!restore) { |
| /* Try to cancel this element queue */ |
| rc = o->optimize(bp, o->owner, elem); |
| if (rc) |
| goto free_and_exit; |
| |
| /* Check if this request is ok */ |
| rc = o->validate(bp, o->owner, elem); |
| if (rc) { |
| BNX2X_ERR("Preamble failed: %d\n", rc); |
| goto free_and_exit; |
| } |
| } |
| |
| /* If so, add it to the execution queue */ |
| list_add_tail(&elem->link, &o->exe_queue); |
| |
| spin_unlock_bh(&o->lock); |
| |
| return 0; |
| |
| free_and_exit: |
| bnx2x_exe_queue_free_elem(bp, elem); |
| |
| spin_unlock_bh(&o->lock); |
| |
| return rc; |
| |
| } |
| |
| static inline void __bnx2x_exe_queue_reset_pending( |
| struct bnx2x *bp, |
| struct bnx2x_exe_queue_obj *o) |
| { |
| struct bnx2x_exeq_elem *elem; |
| |
| while (!list_empty(&o->pending_comp)) { |
| elem = list_first_entry(&o->pending_comp, |
| struct bnx2x_exeq_elem, link); |
| |
| list_del(&elem->link); |
| bnx2x_exe_queue_free_elem(bp, elem); |
| } |
| } |
| |
| static inline void bnx2x_exe_queue_reset_pending(struct bnx2x *bp, |
| struct bnx2x_exe_queue_obj *o) |
| { |
| |
| spin_lock_bh(&o->lock); |
| |
| __bnx2x_exe_queue_reset_pending(bp, o); |
| |
| spin_unlock_bh(&o->lock); |
| |
| } |
| |
| /** |
| * bnx2x_exe_queue_step - execute one execution chunk atomically |
| * |
| * @bp: driver handle |
| * @o: queue |
| * @ramrod_flags: flags |
| * |
| * (Atomicy is ensured using the exe_queue->lock). |
| */ |
| static inline int bnx2x_exe_queue_step(struct bnx2x *bp, |
| struct bnx2x_exe_queue_obj *o, |
| unsigned long *ramrod_flags) |
| { |
| struct bnx2x_exeq_elem *elem, spacer; |
| int cur_len = 0, rc; |
| |
| memset(&spacer, 0, sizeof(spacer)); |
| |
| spin_lock_bh(&o->lock); |
| |
| /* |
| * Next step should not be performed until the current is finished, |
| * unless a DRV_CLEAR_ONLY bit is set. In this case we just want to |
| * properly clear object internals without sending any command to the FW |
| * which also implies there won't be any completion to clear the |
| * 'pending' list. |
| */ |
| if (!list_empty(&o->pending_comp)) { |
| if (test_bit(RAMROD_DRV_CLR_ONLY, ramrod_flags)) { |
| DP(BNX2X_MSG_SP, "RAMROD_DRV_CLR_ONLY requested: " |
| "resetting pending_comp\n"); |
| __bnx2x_exe_queue_reset_pending(bp, o); |
| } else { |
| spin_unlock_bh(&o->lock); |
| return 1; |
| } |
| } |
| |
| /* |
| * Run through the pending commands list and create a next |
| * execution chunk. |
| */ |
| while (!list_empty(&o->exe_queue)) { |
| elem = list_first_entry(&o->exe_queue, struct bnx2x_exeq_elem, |
| link); |
| WARN_ON(!elem->cmd_len); |
| |
| if (cur_len + elem->cmd_len <= o->exe_chunk_len) { |
| cur_len += elem->cmd_len; |
| /* |
| * Prevent from both lists being empty when moving an |
| * element. This will allow the call of |
| * bnx2x_exe_queue_empty() without locking. |
| */ |
| list_add_tail(&spacer.link, &o->pending_comp); |
| mb(); |
| list_del(&elem->link); |
| list_add_tail(&elem->link, &o->pending_comp); |
| list_del(&spacer.link); |
| } else |
| break; |
| } |
| |
| /* Sanity check */ |
| if (!cur_len) { |
| spin_unlock_bh(&o->lock); |
| return 0; |
| } |
| |
| rc = o->execute(bp, o->owner, &o->pending_comp, ramrod_flags); |
| if (rc < 0) |
| /* |
| * In case of an error return the commands back to the queue |
| * and reset the pending_comp. |
| */ |
| list_splice_init(&o->pending_comp, &o->exe_queue); |
| else if (!rc) |
| /* |
| * If zero is returned, means there are no outstanding pending |
| * completions and we may dismiss the pending list. |
| */ |
| __bnx2x_exe_queue_reset_pending(bp, o); |
| |
| spin_unlock_bh(&o->lock); |
| return rc; |
| } |
| |
| static inline bool bnx2x_exe_queue_empty(struct bnx2x_exe_queue_obj *o) |
| { |
| bool empty = list_empty(&o->exe_queue); |
| |
| /* Don't reorder!!! */ |
| mb(); |
| |
| return empty && list_empty(&o->pending_comp); |
| } |
| |
| static inline struct bnx2x_exeq_elem *bnx2x_exe_queue_alloc_elem( |
| struct bnx2x *bp) |
| { |
| DP(BNX2X_MSG_SP, "Allocating a new exe_queue element\n"); |
| return kzalloc(sizeof(struct bnx2x_exeq_elem), GFP_ATOMIC); |
| } |
| |
| /************************ raw_obj functions ***********************************/ |
| static bool bnx2x_raw_check_pending(struct bnx2x_raw_obj *o) |
| { |
| return !!test_bit(o->state, o->pstate); |
| } |
| |
| static void bnx2x_raw_clear_pending(struct bnx2x_raw_obj *o) |
| { |
| smp_mb__before_clear_bit(); |
| clear_bit(o->state, o->pstate); |
| smp_mb__after_clear_bit(); |
| } |
| |
| static void bnx2x_raw_set_pending(struct bnx2x_raw_obj *o) |
| { |
| smp_mb__before_clear_bit(); |
| set_bit(o->state, o->pstate); |
| smp_mb__after_clear_bit(); |
| } |
| |
| /** |
| * bnx2x_state_wait - wait until the given bit(state) is cleared |
| * |
| * @bp: device handle |
| * @state: state which is to be cleared |
| * @state_p: state buffer |
| * |
| */ |
| static inline int bnx2x_state_wait(struct bnx2x *bp, int state, |
| unsigned long *pstate) |
| { |
| /* can take a while if any port is running */ |
| int cnt = 5000; |
| |
| |
| if (CHIP_REV_IS_EMUL(bp)) |
| cnt *= 20; |
| |
| DP(BNX2X_MSG_SP, "waiting for state to become %d\n", state); |
| |
| might_sleep(); |
| while (cnt--) { |
| if (!test_bit(state, pstate)) { |
| #ifdef BNX2X_STOP_ON_ERROR |
| DP(BNX2X_MSG_SP, "exit (cnt %d)\n", 5000 - cnt); |
| #endif |
| return 0; |
| } |
| |
| usleep_range(1000, 1000); |
| |
| if (bp->panic) |
| return -EIO; |
| } |
| |
| /* timeout! */ |
| BNX2X_ERR("timeout waiting for state %d\n", state); |
| #ifdef BNX2X_STOP_ON_ERROR |
| bnx2x_panic(); |
| #endif |
| |
| return -EBUSY; |
| } |
| |
| static int bnx2x_raw_wait(struct bnx2x *bp, struct bnx2x_raw_obj *raw) |
| { |
| return bnx2x_state_wait(bp, raw->state, raw->pstate); |
| } |
| |
| /***************** Classification verbs: Set/Del MAC/VLAN/VLAN-MAC ************/ |
| /* credit handling callbacks */ |
| static bool bnx2x_get_cam_offset_mac(struct bnx2x_vlan_mac_obj *o, int *offset) |
| { |
| struct bnx2x_credit_pool_obj *mp = o->macs_pool; |
| |
| WARN_ON(!mp); |
| |
| return mp->get_entry(mp, offset); |
| } |
| |
| static bool bnx2x_get_credit_mac(struct bnx2x_vlan_mac_obj *o) |
| { |
| struct bnx2x_credit_pool_obj *mp = o->macs_pool; |
| |
| WARN_ON(!mp); |
| |
| return mp->get(mp, 1); |
| } |
| |
| static bool bnx2x_get_cam_offset_vlan(struct bnx2x_vlan_mac_obj *o, int *offset) |
| { |
| struct bnx2x_credit_pool_obj *vp = o->vlans_pool; |
| |
| WARN_ON(!vp); |
| |
| return vp->get_entry(vp, offset); |
| } |
| |
| static bool bnx2x_get_credit_vlan(struct bnx2x_vlan_mac_obj *o) |
| { |
| struct bnx2x_credit_pool_obj *vp = o->vlans_pool; |
| |
| WARN_ON(!vp); |
| |
| return vp->get(vp, 1); |
| } |
| |
| static bool bnx2x_get_credit_vlan_mac(struct bnx2x_vlan_mac_obj *o) |
| { |
| struct bnx2x_credit_pool_obj *mp = o->macs_pool; |
| struct bnx2x_credit_pool_obj *vp = o->vlans_pool; |
| |
| if (!mp->get(mp, 1)) |
| return false; |
| |
| if (!vp->get(vp, 1)) { |
| mp->put(mp, 1); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool bnx2x_put_cam_offset_mac(struct bnx2x_vlan_mac_obj *o, int offset) |
| { |
| struct bnx2x_credit_pool_obj *mp = o->macs_pool; |
| |
| return mp->put_entry(mp, offset); |
| } |
| |
| static bool bnx2x_put_credit_mac(struct bnx2x_vlan_mac_obj *o) |
| { |
| struct bnx2x_credit_pool_obj *mp = o->macs_pool; |
| |
| return mp->put(mp, 1); |
| } |
| |
| static bool bnx2x_put_cam_offset_vlan(struct bnx2x_vlan_mac_obj *o, int offset) |
| { |
| struct bnx2x_credit_pool_obj *vp = o->vlans_pool; |
| |
| return vp->put_entry(vp, offset); |
| } |
| |
| static bool bnx2x_put_credit_vlan(struct bnx2x_vlan_mac_obj *o) |
| { |
| struct bnx2x_credit_pool_obj *vp = o->vlans_pool; |
| |
| return vp->put(vp, 1); |
| } |
| |
| static bool bnx2x_put_credit_vlan_mac(struct bnx2x_vlan_mac_obj *o) |
| { |
| struct bnx2x_credit_pool_obj *mp = o->macs_pool; |
| struct bnx2x_credit_pool_obj *vp = o->vlans_pool; |
| |
| if (!mp->put(mp, 1)) |
| return false; |
| |
| if (!vp->put(vp, 1)) { |
| mp->get(mp, 1); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static int bnx2x_get_n_elements(struct bnx2x *bp, struct bnx2x_vlan_mac_obj *o, |
| int n, u8 *buf) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| u8 *next = buf; |
| int counter = 0; |
| |
| /* traverse list */ |
| list_for_each_entry(pos, &o->head, link) { |
| if (counter < n) { |
| /* place leading zeroes in buffer */ |
| memset(next, 0, MAC_LEADING_ZERO_CNT); |
| |
| /* place mac after leading zeroes*/ |
| memcpy(next + MAC_LEADING_ZERO_CNT, pos->u.mac.mac, |
| ETH_ALEN); |
| |
| /* calculate address of next element and |
| * advance counter |
| */ |
| counter++; |
| next = buf + counter * ALIGN(ETH_ALEN, sizeof(u32)); |
| |
| DP(BNX2X_MSG_SP, "copied element number %d to address %p element was %pM\n", |
| counter, next, pos->u.mac.mac); |
| } |
| } |
| return counter * ETH_ALEN; |
| } |
| |
| /* check_add() callbacks */ |
| static int bnx2x_check_mac_add(struct bnx2x_vlan_mac_obj *o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| |
| if (!is_valid_ether_addr(data->mac.mac)) |
| return -EINVAL; |
| |
| /* Check if a requested MAC already exists */ |
| list_for_each_entry(pos, &o->head, link) |
| if (!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN)) |
| return -EEXIST; |
| |
| return 0; |
| } |
| |
| static int bnx2x_check_vlan_add(struct bnx2x_vlan_mac_obj *o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| |
| list_for_each_entry(pos, &o->head, link) |
| if (data->vlan.vlan == pos->u.vlan.vlan) |
| return -EEXIST; |
| |
| return 0; |
| } |
| |
| static int bnx2x_check_vlan_mac_add(struct bnx2x_vlan_mac_obj *o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| |
| list_for_each_entry(pos, &o->head, link) |
| if ((data->vlan_mac.vlan == pos->u.vlan_mac.vlan) && |
| (!memcmp(data->vlan_mac.mac, pos->u.vlan_mac.mac, |
| ETH_ALEN))) |
| return -EEXIST; |
| |
| return 0; |
| } |
| |
| |
| /* check_del() callbacks */ |
| static struct bnx2x_vlan_mac_registry_elem * |
| bnx2x_check_mac_del(struct bnx2x_vlan_mac_obj *o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| |
| list_for_each_entry(pos, &o->head, link) |
| if (!memcmp(data->mac.mac, pos->u.mac.mac, ETH_ALEN)) |
| return pos; |
| |
| return NULL; |
| } |
| |
| static struct bnx2x_vlan_mac_registry_elem * |
| bnx2x_check_vlan_del(struct bnx2x_vlan_mac_obj *o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| |
| list_for_each_entry(pos, &o->head, link) |
| if (data->vlan.vlan == pos->u.vlan.vlan) |
| return pos; |
| |
| return NULL; |
| } |
| |
| static struct bnx2x_vlan_mac_registry_elem * |
| bnx2x_check_vlan_mac_del(struct bnx2x_vlan_mac_obj *o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| |
| list_for_each_entry(pos, &o->head, link) |
| if ((data->vlan_mac.vlan == pos->u.vlan_mac.vlan) && |
| (!memcmp(data->vlan_mac.mac, pos->u.vlan_mac.mac, |
| ETH_ALEN))) |
| return pos; |
| |
| return NULL; |
| } |
| |
| /* check_move() callback */ |
| static bool bnx2x_check_move(struct bnx2x_vlan_mac_obj *src_o, |
| struct bnx2x_vlan_mac_obj *dst_o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| int rc; |
| |
| /* Check if we can delete the requested configuration from the first |
| * object. |
| */ |
| pos = src_o->check_del(src_o, data); |
| |
| /* check if configuration can be added */ |
| rc = dst_o->check_add(dst_o, data); |
| |
| /* If this classification can not be added (is already set) |
| * or can't be deleted - return an error. |
| */ |
| if (rc || !pos) |
| return false; |
| |
| return true; |
| } |
| |
| static bool bnx2x_check_move_always_err( |
| struct bnx2x_vlan_mac_obj *src_o, |
| struct bnx2x_vlan_mac_obj *dst_o, |
| union bnx2x_classification_ramrod_data *data) |
| { |
| return false; |
| } |
| |
| |
| static inline u8 bnx2x_vlan_mac_get_rx_tx_flag(struct bnx2x_vlan_mac_obj *o) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| u8 rx_tx_flag = 0; |
| |
| if ((raw->obj_type == BNX2X_OBJ_TYPE_TX) || |
| (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX)) |
| rx_tx_flag |= ETH_CLASSIFY_CMD_HEADER_TX_CMD; |
| |
| if ((raw->obj_type == BNX2X_OBJ_TYPE_RX) || |
| (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX)) |
| rx_tx_flag |= ETH_CLASSIFY_CMD_HEADER_RX_CMD; |
| |
| return rx_tx_flag; |
| } |
| |
| /* LLH CAM line allocations */ |
| enum { |
| LLH_CAM_ISCSI_ETH_LINE = 0, |
| LLH_CAM_ETH_LINE, |
| LLH_CAM_MAX_PF_LINE = NIG_REG_LLH1_FUNC_MEM_SIZE / 2 |
| }; |
| |
| static inline void bnx2x_set_mac_in_nig(struct bnx2x *bp, |
| bool add, unsigned char *dev_addr, int index) |
| { |
| u32 wb_data[2]; |
| u32 reg_offset = BP_PORT(bp) ? NIG_REG_LLH1_FUNC_MEM : |
| NIG_REG_LLH0_FUNC_MEM; |
| |
| if (!IS_MF_SI(bp) || index > LLH_CAM_MAX_PF_LINE) |
| return; |
| |
| DP(BNX2X_MSG_SP, "Going to %s LLH configuration at entry %d\n", |
| (add ? "ADD" : "DELETE"), index); |
| |
| if (add) { |
| /* LLH_FUNC_MEM is a u64 WB register */ |
| reg_offset += 8*index; |
| |
| wb_data[0] = ((dev_addr[2] << 24) | (dev_addr[3] << 16) | |
| (dev_addr[4] << 8) | dev_addr[5]); |
| wb_data[1] = ((dev_addr[0] << 8) | dev_addr[1]); |
| |
| REG_WR_DMAE(bp, reg_offset, wb_data, 2); |
| } |
| |
| REG_WR(bp, (BP_PORT(bp) ? NIG_REG_LLH1_FUNC_MEM_ENABLE : |
| NIG_REG_LLH0_FUNC_MEM_ENABLE) + 4*index, add); |
| } |
| |
| /** |
| * bnx2x_vlan_mac_set_cmd_hdr_e2 - set a header in a single classify ramrod |
| * |
| * @bp: device handle |
| * @o: queue for which we want to configure this rule |
| * @add: if true the command is an ADD command, DEL otherwise |
| * @opcode: CLASSIFY_RULE_OPCODE_XXX |
| * @hdr: pointer to a header to setup |
| * |
| */ |
| static inline void bnx2x_vlan_mac_set_cmd_hdr_e2(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, bool add, int opcode, |
| struct eth_classify_cmd_header *hdr) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| |
| hdr->client_id = raw->cl_id; |
| hdr->func_id = raw->func_id; |
| |
| /* Rx or/and Tx (internal switching) configuration ? */ |
| hdr->cmd_general_data |= |
| bnx2x_vlan_mac_get_rx_tx_flag(o); |
| |
| if (add) |
| hdr->cmd_general_data |= ETH_CLASSIFY_CMD_HEADER_IS_ADD; |
| |
| hdr->cmd_general_data |= |
| (opcode << ETH_CLASSIFY_CMD_HEADER_OPCODE_SHIFT); |
| } |
| |
| /** |
| * bnx2x_vlan_mac_set_rdata_hdr_e2 - set the classify ramrod data header |
| * |
| * @cid: connection id |
| * @type: BNX2X_FILTER_XXX_PENDING |
| * @hdr: poiter to header to setup |
| * @rule_cnt: |
| * |
| * currently we always configure one rule and echo field to contain a CID and an |
| * opcode type. |
| */ |
| static inline void bnx2x_vlan_mac_set_rdata_hdr_e2(u32 cid, int type, |
| struct eth_classify_header *hdr, int rule_cnt) |
| { |
| hdr->echo = (cid & BNX2X_SWCID_MASK) | (type << BNX2X_SWCID_SHIFT); |
| hdr->rule_cnt = (u8)rule_cnt; |
| } |
| |
| |
| /* hw_config() callbacks */ |
| static void bnx2x_set_one_mac_e2(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| struct bnx2x_exeq_elem *elem, int rule_idx, |
| int cam_offset) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| struct eth_classify_rules_ramrod_data *data = |
| (struct eth_classify_rules_ramrod_data *)(raw->rdata); |
| int rule_cnt = rule_idx + 1, cmd = elem->cmd_data.vlan_mac.cmd; |
| union eth_classify_rule_cmd *rule_entry = &data->rules[rule_idx]; |
| bool add = (cmd == BNX2X_VLAN_MAC_ADD) ? true : false; |
| unsigned long *vlan_mac_flags = &elem->cmd_data.vlan_mac.vlan_mac_flags; |
| u8 *mac = elem->cmd_data.vlan_mac.u.mac.mac; |
| |
| /* |
| * Set LLH CAM entry: currently only iSCSI and ETH macs are |
| * relevant. In addition, current implementation is tuned for a |
| * single ETH MAC. |
| * |
| * When multiple unicast ETH MACs PF configuration in switch |
| * independent mode is required (NetQ, multiple netdev MACs, |
| * etc.), consider better utilisation of 8 per function MAC |
| * entries in the LLH register. There is also |
| * NIG_REG_P[01]_LLH_FUNC_MEM2 registers that complete the |
| * total number of CAM entries to 16. |
| * |
| * Currently we won't configure NIG for MACs other than a primary ETH |
| * MAC and iSCSI L2 MAC. |
| * |
| * If this MAC is moving from one Queue to another, no need to change |
| * NIG configuration. |
| */ |
| if (cmd != BNX2X_VLAN_MAC_MOVE) { |
| if (test_bit(BNX2X_ISCSI_ETH_MAC, vlan_mac_flags)) |
| bnx2x_set_mac_in_nig(bp, add, mac, |
| LLH_CAM_ISCSI_ETH_LINE); |
| else if (test_bit(BNX2X_ETH_MAC, vlan_mac_flags)) |
| bnx2x_set_mac_in_nig(bp, add, mac, LLH_CAM_ETH_LINE); |
| } |
| |
| /* Reset the ramrod data buffer for the first rule */ |
| if (rule_idx == 0) |
| memset(data, 0, sizeof(*data)); |
| |
| /* Setup a command header */ |
| bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_MAC, |
| &rule_entry->mac.header); |
| |
| DP(BNX2X_MSG_SP, "About to %s MAC %pM for Queue %d\n", |
| add ? "add" : "delete", mac, raw->cl_id); |
| |
| /* Set a MAC itself */ |
| bnx2x_set_fw_mac_addr(&rule_entry->mac.mac_msb, |
| &rule_entry->mac.mac_mid, |
| &rule_entry->mac.mac_lsb, mac); |
| |
| /* MOVE: Add a rule that will add this MAC to the target Queue */ |
| if (cmd == BNX2X_VLAN_MAC_MOVE) { |
| rule_entry++; |
| rule_cnt++; |
| |
| /* Setup ramrod data */ |
| bnx2x_vlan_mac_set_cmd_hdr_e2(bp, |
| elem->cmd_data.vlan_mac.target_obj, |
| true, CLASSIFY_RULE_OPCODE_MAC, |
| &rule_entry->mac.header); |
| |
| /* Set a MAC itself */ |
| bnx2x_set_fw_mac_addr(&rule_entry->mac.mac_msb, |
| &rule_entry->mac.mac_mid, |
| &rule_entry->mac.mac_lsb, mac); |
| } |
| |
| /* Set the ramrod data header */ |
| /* TODO: take this to the higher level in order to prevent multiple |
| writing */ |
| bnx2x_vlan_mac_set_rdata_hdr_e2(raw->cid, raw->state, &data->header, |
| rule_cnt); |
| } |
| |
| /** |
| * bnx2x_vlan_mac_set_rdata_hdr_e1x - set a header in a single classify ramrod |
| * |
| * @bp: device handle |
| * @o: queue |
| * @type: |
| * @cam_offset: offset in cam memory |
| * @hdr: pointer to a header to setup |
| * |
| * E1/E1H |
| */ |
| static inline void bnx2x_vlan_mac_set_rdata_hdr_e1x(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, int type, int cam_offset, |
| struct mac_configuration_hdr *hdr) |
| { |
| struct bnx2x_raw_obj *r = &o->raw; |
| |
| hdr->length = 1; |
| hdr->offset = (u8)cam_offset; |
| hdr->client_id = 0xff; |
| hdr->echo = ((r->cid & BNX2X_SWCID_MASK) | (type << BNX2X_SWCID_SHIFT)); |
| } |
| |
| static inline void bnx2x_vlan_mac_set_cfg_entry_e1x(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, bool add, int opcode, u8 *mac, |
| u16 vlan_id, struct mac_configuration_entry *cfg_entry) |
| { |
| struct bnx2x_raw_obj *r = &o->raw; |
| u32 cl_bit_vec = (1 << r->cl_id); |
| |
| cfg_entry->clients_bit_vector = cpu_to_le32(cl_bit_vec); |
| cfg_entry->pf_id = r->func_id; |
| cfg_entry->vlan_id = cpu_to_le16(vlan_id); |
| |
| if (add) { |
| SET_FLAG(cfg_entry->flags, MAC_CONFIGURATION_ENTRY_ACTION_TYPE, |
| T_ETH_MAC_COMMAND_SET); |
| SET_FLAG(cfg_entry->flags, |
| MAC_CONFIGURATION_ENTRY_VLAN_FILTERING_MODE, opcode); |
| |
| /* Set a MAC in a ramrod data */ |
| bnx2x_set_fw_mac_addr(&cfg_entry->msb_mac_addr, |
| &cfg_entry->middle_mac_addr, |
| &cfg_entry->lsb_mac_addr, mac); |
| } else |
| SET_FLAG(cfg_entry->flags, MAC_CONFIGURATION_ENTRY_ACTION_TYPE, |
| T_ETH_MAC_COMMAND_INVALIDATE); |
| } |
| |
| static inline void bnx2x_vlan_mac_set_rdata_e1x(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, int type, int cam_offset, bool add, |
| u8 *mac, u16 vlan_id, int opcode, struct mac_configuration_cmd *config) |
| { |
| struct mac_configuration_entry *cfg_entry = &config->config_table[0]; |
| struct bnx2x_raw_obj *raw = &o->raw; |
| |
| bnx2x_vlan_mac_set_rdata_hdr_e1x(bp, o, type, cam_offset, |
| &config->hdr); |
| bnx2x_vlan_mac_set_cfg_entry_e1x(bp, o, add, opcode, mac, vlan_id, |
| cfg_entry); |
| |
| DP(BNX2X_MSG_SP, "%s MAC %pM CLID %d CAM offset %d\n", |
| add ? "setting" : "clearing", |
| mac, raw->cl_id, cam_offset); |
| } |
| |
| /** |
| * bnx2x_set_one_mac_e1x - fill a single MAC rule ramrod data |
| * |
| * @bp: device handle |
| * @o: bnx2x_vlan_mac_obj |
| * @elem: bnx2x_exeq_elem |
| * @rule_idx: rule_idx |
| * @cam_offset: cam_offset |
| */ |
| static void bnx2x_set_one_mac_e1x(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| struct bnx2x_exeq_elem *elem, int rule_idx, |
| int cam_offset) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| struct mac_configuration_cmd *config = |
| (struct mac_configuration_cmd *)(raw->rdata); |
| /* |
| * 57710 and 57711 do not support MOVE command, |
| * so it's either ADD or DEL |
| */ |
| bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ? |
| true : false; |
| |
| /* Reset the ramrod data buffer */ |
| memset(config, 0, sizeof(*config)); |
| |
| bnx2x_vlan_mac_set_rdata_e1x(bp, o, BNX2X_FILTER_MAC_PENDING, |
| cam_offset, add, |
| elem->cmd_data.vlan_mac.u.mac.mac, 0, |
| ETH_VLAN_FILTER_ANY_VLAN, config); |
| } |
| |
| static void bnx2x_set_one_vlan_e2(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| struct bnx2x_exeq_elem *elem, int rule_idx, |
| int cam_offset) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| struct eth_classify_rules_ramrod_data *data = |
| (struct eth_classify_rules_ramrod_data *)(raw->rdata); |
| int rule_cnt = rule_idx + 1; |
| union eth_classify_rule_cmd *rule_entry = &data->rules[rule_idx]; |
| int cmd = elem->cmd_data.vlan_mac.cmd; |
| bool add = (cmd == BNX2X_VLAN_MAC_ADD) ? true : false; |
| u16 vlan = elem->cmd_data.vlan_mac.u.vlan.vlan; |
| |
| /* Reset the ramrod data buffer for the first rule */ |
| if (rule_idx == 0) |
| memset(data, 0, sizeof(*data)); |
| |
| /* Set a rule header */ |
| bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_VLAN, |
| &rule_entry->vlan.header); |
| |
| DP(BNX2X_MSG_SP, "About to %s VLAN %d\n", (add ? "add" : "delete"), |
| vlan); |
| |
| /* Set a VLAN itself */ |
| rule_entry->vlan.vlan = cpu_to_le16(vlan); |
| |
| /* MOVE: Add a rule that will add this MAC to the target Queue */ |
| if (cmd == BNX2X_VLAN_MAC_MOVE) { |
| rule_entry++; |
| rule_cnt++; |
| |
| /* Setup ramrod data */ |
| bnx2x_vlan_mac_set_cmd_hdr_e2(bp, |
| elem->cmd_data.vlan_mac.target_obj, |
| true, CLASSIFY_RULE_OPCODE_VLAN, |
| &rule_entry->vlan.header); |
| |
| /* Set a VLAN itself */ |
| rule_entry->vlan.vlan = cpu_to_le16(vlan); |
| } |
| |
| /* Set the ramrod data header */ |
| /* TODO: take this to the higher level in order to prevent multiple |
| writing */ |
| bnx2x_vlan_mac_set_rdata_hdr_e2(raw->cid, raw->state, &data->header, |
| rule_cnt); |
| } |
| |
| static void bnx2x_set_one_vlan_mac_e2(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| struct bnx2x_exeq_elem *elem, |
| int rule_idx, int cam_offset) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| struct eth_classify_rules_ramrod_data *data = |
| (struct eth_classify_rules_ramrod_data *)(raw->rdata); |
| int rule_cnt = rule_idx + 1; |
| union eth_classify_rule_cmd *rule_entry = &data->rules[rule_idx]; |
| int cmd = elem->cmd_data.vlan_mac.cmd; |
| bool add = (cmd == BNX2X_VLAN_MAC_ADD) ? true : false; |
| u16 vlan = elem->cmd_data.vlan_mac.u.vlan_mac.vlan; |
| u8 *mac = elem->cmd_data.vlan_mac.u.vlan_mac.mac; |
| |
| |
| /* Reset the ramrod data buffer for the first rule */ |
| if (rule_idx == 0) |
| memset(data, 0, sizeof(*data)); |
| |
| /* Set a rule header */ |
| bnx2x_vlan_mac_set_cmd_hdr_e2(bp, o, add, CLASSIFY_RULE_OPCODE_PAIR, |
| &rule_entry->pair.header); |
| |
| /* Set VLAN and MAC themselvs */ |
| rule_entry->pair.vlan = cpu_to_le16(vlan); |
| bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb, |
| &rule_entry->pair.mac_mid, |
| &rule_entry->pair.mac_lsb, mac); |
| |
| /* MOVE: Add a rule that will add this MAC to the target Queue */ |
| if (cmd == BNX2X_VLAN_MAC_MOVE) { |
| rule_entry++; |
| rule_cnt++; |
| |
| /* Setup ramrod data */ |
| bnx2x_vlan_mac_set_cmd_hdr_e2(bp, |
| elem->cmd_data.vlan_mac.target_obj, |
| true, CLASSIFY_RULE_OPCODE_PAIR, |
| &rule_entry->pair.header); |
| |
| /* Set a VLAN itself */ |
| rule_entry->pair.vlan = cpu_to_le16(vlan); |
| bnx2x_set_fw_mac_addr(&rule_entry->pair.mac_msb, |
| &rule_entry->pair.mac_mid, |
| &rule_entry->pair.mac_lsb, mac); |
| } |
| |
| /* Set the ramrod data header */ |
| /* TODO: take this to the higher level in order to prevent multiple |
| writing */ |
| bnx2x_vlan_mac_set_rdata_hdr_e2(raw->cid, raw->state, &data->header, |
| rule_cnt); |
| } |
| |
| /** |
| * bnx2x_set_one_vlan_mac_e1h - |
| * |
| * @bp: device handle |
| * @o: bnx2x_vlan_mac_obj |
| * @elem: bnx2x_exeq_elem |
| * @rule_idx: rule_idx |
| * @cam_offset: cam_offset |
| */ |
| static void bnx2x_set_one_vlan_mac_e1h(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| struct bnx2x_exeq_elem *elem, |
| int rule_idx, int cam_offset) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| struct mac_configuration_cmd *config = |
| (struct mac_configuration_cmd *)(raw->rdata); |
| /* |
| * 57710 and 57711 do not support MOVE command, |
| * so it's either ADD or DEL |
| */ |
| bool add = (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ? |
| true : false; |
| |
| /* Reset the ramrod data buffer */ |
| memset(config, 0, sizeof(*config)); |
| |
| bnx2x_vlan_mac_set_rdata_e1x(bp, o, BNX2X_FILTER_VLAN_MAC_PENDING, |
| cam_offset, add, |
| elem->cmd_data.vlan_mac.u.vlan_mac.mac, |
| elem->cmd_data.vlan_mac.u.vlan_mac.vlan, |
| ETH_VLAN_FILTER_CLASSIFY, config); |
| } |
| |
| #define list_next_entry(pos, member) \ |
| list_entry((pos)->member.next, typeof(*(pos)), member) |
| |
| /** |
| * bnx2x_vlan_mac_restore - reconfigure next MAC/VLAN/VLAN-MAC element |
| * |
| * @bp: device handle |
| * @p: command parameters |
| * @ppos: pointer to the cooky |
| * |
| * reconfigure next MAC/VLAN/VLAN-MAC element from the |
| * previously configured elements list. |
| * |
| * from command parameters only RAMROD_COMP_WAIT bit in ramrod_flags is taken |
| * into an account |
| * |
| * pointer to the cooky - that should be given back in the next call to make |
| * function handle the next element. If *ppos is set to NULL it will restart the |
| * iterator. If returned *ppos == NULL this means that the last element has been |
| * handled. |
| * |
| */ |
| static int bnx2x_vlan_mac_restore(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_ramrod_params *p, |
| struct bnx2x_vlan_mac_registry_elem **ppos) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| struct bnx2x_vlan_mac_obj *o = p->vlan_mac_obj; |
| |
| /* If list is empty - there is nothing to do here */ |
| if (list_empty(&o->head)) { |
| *ppos = NULL; |
| return 0; |
| } |
| |
| /* make a step... */ |
| if (*ppos == NULL) |
| *ppos = list_first_entry(&o->head, |
| struct bnx2x_vlan_mac_registry_elem, |
| link); |
| else |
| *ppos = list_next_entry(*ppos, link); |
| |
| pos = *ppos; |
| |
| /* If it's the last step - return NULL */ |
| if (list_is_last(&pos->link, &o->head)) |
| *ppos = NULL; |
| |
| /* Prepare a 'user_req' */ |
| memcpy(&p->user_req.u, &pos->u, sizeof(pos->u)); |
| |
| /* Set the command */ |
| p->user_req.cmd = BNX2X_VLAN_MAC_ADD; |
| |
| /* Set vlan_mac_flags */ |
| p->user_req.vlan_mac_flags = pos->vlan_mac_flags; |
| |
| /* Set a restore bit */ |
| __set_bit(RAMROD_RESTORE, &p->ramrod_flags); |
| |
| return bnx2x_config_vlan_mac(bp, p); |
| } |
| |
| /* |
| * bnx2x_exeq_get_mac/bnx2x_exeq_get_vlan/bnx2x_exeq_get_vlan_mac return a |
| * pointer to an element with a specific criteria and NULL if such an element |
| * hasn't been found. |
| */ |
| static struct bnx2x_exeq_elem *bnx2x_exeq_get_mac( |
| struct bnx2x_exe_queue_obj *o, |
| struct bnx2x_exeq_elem *elem) |
| { |
| struct bnx2x_exeq_elem *pos; |
| struct bnx2x_mac_ramrod_data *data = &elem->cmd_data.vlan_mac.u.mac; |
| |
| /* Check pending for execution commands */ |
| list_for_each_entry(pos, &o->exe_queue, link) |
| if (!memcmp(&pos->cmd_data.vlan_mac.u.mac, data, |
| sizeof(*data)) && |
| (pos->cmd_data.vlan_mac.cmd == elem->cmd_data.vlan_mac.cmd)) |
| return pos; |
| |
| return NULL; |
| } |
| |
| static struct bnx2x_exeq_elem *bnx2x_exeq_get_vlan( |
| struct bnx2x_exe_queue_obj *o, |
| struct bnx2x_exeq_elem *elem) |
| { |
| struct bnx2x_exeq_elem *pos; |
| struct bnx2x_vlan_ramrod_data *data = &elem->cmd_data.vlan_mac.u.vlan; |
| |
| /* Check pending for execution commands */ |
| list_for_each_entry(pos, &o->exe_queue, link) |
| if (!memcmp(&pos->cmd_data.vlan_mac.u.vlan, data, |
| sizeof(*data)) && |
| (pos->cmd_data.vlan_mac.cmd == elem->cmd_data.vlan_mac.cmd)) |
| return pos; |
| |
| return NULL; |
| } |
| |
| static struct bnx2x_exeq_elem *bnx2x_exeq_get_vlan_mac( |
| struct bnx2x_exe_queue_obj *o, |
| struct bnx2x_exeq_elem *elem) |
| { |
| struct bnx2x_exeq_elem *pos; |
| struct bnx2x_vlan_mac_ramrod_data *data = |
| &elem->cmd_data.vlan_mac.u.vlan_mac; |
| |
| /* Check pending for execution commands */ |
| list_for_each_entry(pos, &o->exe_queue, link) |
| if (!memcmp(&pos->cmd_data.vlan_mac.u.vlan_mac, data, |
| sizeof(*data)) && |
| (pos->cmd_data.vlan_mac.cmd == elem->cmd_data.vlan_mac.cmd)) |
| return pos; |
| |
| return NULL; |
| } |
| |
| /** |
| * bnx2x_validate_vlan_mac_add - check if an ADD command can be executed |
| * |
| * @bp: device handle |
| * @qo: bnx2x_qable_obj |
| * @elem: bnx2x_exeq_elem |
| * |
| * Checks that the requested configuration can be added. If yes and if |
| * requested, consume CAM credit. |
| * |
| * The 'validate' is run after the 'optimize'. |
| * |
| */ |
| static inline int bnx2x_validate_vlan_mac_add(struct bnx2x *bp, |
| union bnx2x_qable_obj *qo, |
| struct bnx2x_exeq_elem *elem) |
| { |
| struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac; |
| struct bnx2x_exe_queue_obj *exeq = &o->exe_queue; |
| int rc; |
| |
| /* Check the registry */ |
| rc = o->check_add(o, &elem->cmd_data.vlan_mac.u); |
| if (rc) { |
| DP(BNX2X_MSG_SP, "ADD command is not allowed considering " |
| "current registry state\n"); |
| return rc; |
| } |
| |
| /* |
| * Check if there is a pending ADD command for this |
| * MAC/VLAN/VLAN-MAC. Return an error if there is. |
| */ |
| if (exeq->get(exeq, elem)) { |
| DP(BNX2X_MSG_SP, "There is a pending ADD command already\n"); |
| return -EEXIST; |
| } |
| |
| /* |
| * TODO: Check the pending MOVE from other objects where this |
| * object is a destination object. |
| */ |
| |
| /* Consume the credit if not requested not to */ |
| if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT, |
| &elem->cmd_data.vlan_mac.vlan_mac_flags) || |
| o->get_credit(o))) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| /** |
| * bnx2x_validate_vlan_mac_del - check if the DEL command can be executed |
| * |
| * @bp: device handle |
| * @qo: quable object to check |
| * @elem: element that needs to be deleted |
| * |
| * Checks that the requested configuration can be deleted. If yes and if |
| * requested, returns a CAM credit. |
| * |
| * The 'validate' is run after the 'optimize'. |
| */ |
| static inline int bnx2x_validate_vlan_mac_del(struct bnx2x *bp, |
| union bnx2x_qable_obj *qo, |
| struct bnx2x_exeq_elem *elem) |
| { |
| struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac; |
| struct bnx2x_vlan_mac_registry_elem *pos; |
| struct bnx2x_exe_queue_obj *exeq = &o->exe_queue; |
| struct bnx2x_exeq_elem query_elem; |
| |
| /* If this classification can not be deleted (doesn't exist) |
| * - return a BNX2X_EXIST. |
| */ |
| pos = o->check_del(o, &elem->cmd_data.vlan_mac.u); |
| if (!pos) { |
| DP(BNX2X_MSG_SP, "DEL command is not allowed considering " |
| "current registry state\n"); |
| return -EEXIST; |
| } |
| |
| /* |
| * Check if there are pending DEL or MOVE commands for this |
| * MAC/VLAN/VLAN-MAC. Return an error if so. |
| */ |
| memcpy(&query_elem, elem, sizeof(query_elem)); |
| |
| /* Check for MOVE commands */ |
| query_elem.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_MOVE; |
| if (exeq->get(exeq, &query_elem)) { |
| BNX2X_ERR("There is a pending MOVE command already\n"); |
| return -EINVAL; |
| } |
| |
| /* Check for DEL commands */ |
| if (exeq->get(exeq, elem)) { |
| DP(BNX2X_MSG_SP, "There is a pending DEL command already\n"); |
| return -EEXIST; |
| } |
| |
| /* Return the credit to the credit pool if not requested not to */ |
| if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT, |
| &elem->cmd_data.vlan_mac.vlan_mac_flags) || |
| o->put_credit(o))) { |
| BNX2X_ERR("Failed to return a credit\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * bnx2x_validate_vlan_mac_move - check if the MOVE command can be executed |
| * |
| * @bp: device handle |
| * @qo: quable object to check (source) |
| * @elem: element that needs to be moved |
| * |
| * Checks that the requested configuration can be moved. If yes and if |
| * requested, returns a CAM credit. |
| * |
| * The 'validate' is run after the 'optimize'. |
| */ |
| static inline int bnx2x_validate_vlan_mac_move(struct bnx2x *bp, |
| union bnx2x_qable_obj *qo, |
| struct bnx2x_exeq_elem *elem) |
| { |
| struct bnx2x_vlan_mac_obj *src_o = &qo->vlan_mac; |
| struct bnx2x_vlan_mac_obj *dest_o = elem->cmd_data.vlan_mac.target_obj; |
| struct bnx2x_exeq_elem query_elem; |
| struct bnx2x_exe_queue_obj *src_exeq = &src_o->exe_queue; |
| struct bnx2x_exe_queue_obj *dest_exeq = &dest_o->exe_queue; |
| |
| /* |
| * Check if we can perform this operation based on the current registry |
| * state. |
| */ |
| if (!src_o->check_move(src_o, dest_o, &elem->cmd_data.vlan_mac.u)) { |
| DP(BNX2X_MSG_SP, "MOVE command is not allowed considering " |
| "current registry state\n"); |
| return -EINVAL; |
| } |
| |
| /* |
| * Check if there is an already pending DEL or MOVE command for the |
| * source object or ADD command for a destination object. Return an |
| * error if so. |
| */ |
| memcpy(&query_elem, elem, sizeof(query_elem)); |
| |
| /* Check DEL on source */ |
| query_elem.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_DEL; |
| if (src_exeq->get(src_exeq, &query_elem)) { |
| BNX2X_ERR("There is a pending DEL command on the source " |
| "queue already\n"); |
| return -EINVAL; |
| } |
| |
| /* Check MOVE on source */ |
| if (src_exeq->get(src_exeq, elem)) { |
| DP(BNX2X_MSG_SP, "There is a pending MOVE command already\n"); |
| return -EEXIST; |
| } |
| |
| /* Check ADD on destination */ |
| query_elem.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_ADD; |
| if (dest_exeq->get(dest_exeq, &query_elem)) { |
| BNX2X_ERR("There is a pending ADD command on the " |
| "destination queue already\n"); |
| return -EINVAL; |
| } |
| |
| /* Consume the credit if not requested not to */ |
| if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT_DEST, |
| &elem->cmd_data.vlan_mac.vlan_mac_flags) || |
| dest_o->get_credit(dest_o))) |
| return -EINVAL; |
| |
| if (!(test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT, |
| &elem->cmd_data.vlan_mac.vlan_mac_flags) || |
| src_o->put_credit(src_o))) { |
| /* return the credit taken from dest... */ |
| dest_o->put_credit(dest_o); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int bnx2x_validate_vlan_mac(struct bnx2x *bp, |
| union bnx2x_qable_obj *qo, |
| struct bnx2x_exeq_elem *elem) |
| { |
| switch (elem->cmd_data.vlan_mac.cmd) { |
| case BNX2X_VLAN_MAC_ADD: |
| return bnx2x_validate_vlan_mac_add(bp, qo, elem); |
| case BNX2X_VLAN_MAC_DEL: |
| return bnx2x_validate_vlan_mac_del(bp, qo, elem); |
| case BNX2X_VLAN_MAC_MOVE: |
| return bnx2x_validate_vlan_mac_move(bp, qo, elem); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int bnx2x_remove_vlan_mac(struct bnx2x *bp, |
| union bnx2x_qable_obj *qo, |
| struct bnx2x_exeq_elem *elem) |
| { |
| int rc = 0; |
| |
| /* If consumption wasn't required, nothing to do */ |
| if (test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT, |
| &elem->cmd_data.vlan_mac.vlan_mac_flags)) |
| return 0; |
| |
| switch (elem->cmd_data.vlan_mac.cmd) { |
| case BNX2X_VLAN_MAC_ADD: |
| case BNX2X_VLAN_MAC_MOVE: |
| rc = qo->vlan_mac.put_credit(&qo->vlan_mac); |
| break; |
| case BNX2X_VLAN_MAC_DEL: |
| rc = qo->vlan_mac.get_credit(&qo->vlan_mac); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| if (rc != true) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| /** |
| * bnx2x_wait_vlan_mac - passivly wait for 5 seconds until all work completes. |
| * |
| * @bp: device handle |
| * @o: bnx2x_vlan_mac_obj |
| * |
| */ |
| static int bnx2x_wait_vlan_mac(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o) |
| { |
| int cnt = 5000, rc; |
| struct bnx2x_exe_queue_obj *exeq = &o->exe_queue; |
| struct bnx2x_raw_obj *raw = &o->raw; |
| |
| while (cnt--) { |
| /* Wait for the current command to complete */ |
| rc = raw->wait_comp(bp, raw); |
| if (rc) |
| return rc; |
| |
| /* Wait until there are no pending commands */ |
| if (!bnx2x_exe_queue_empty(exeq)) |
| usleep_range(1000, 1000); |
| else |
| return 0; |
| } |
| |
| return -EBUSY; |
| } |
| |
| /** |
| * bnx2x_complete_vlan_mac - complete one VLAN-MAC ramrod |
| * |
| * @bp: device handle |
| * @o: bnx2x_vlan_mac_obj |
| * @cqe: |
| * @cont: if true schedule next execution chunk |
| * |
| */ |
| static int bnx2x_complete_vlan_mac(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| union event_ring_elem *cqe, |
| unsigned long *ramrod_flags) |
| { |
| struct bnx2x_raw_obj *r = &o->raw; |
| int rc; |
| |
| /* Reset pending list */ |
| bnx2x_exe_queue_reset_pending(bp, &o->exe_queue); |
| |
| /* Clear pending */ |
| r->clear_pending(r); |
| |
| /* If ramrod failed this is most likely a SW bug */ |
| if (cqe->message.error) |
| return -EINVAL; |
| |
| /* Run the next bulk of pending commands if requeted */ |
| if (test_bit(RAMROD_CONT, ramrod_flags)) { |
| rc = bnx2x_exe_queue_step(bp, &o->exe_queue, ramrod_flags); |
| if (rc < 0) |
| return rc; |
| } |
| |
| /* If there is more work to do return PENDING */ |
| if (!bnx2x_exe_queue_empty(&o->exe_queue)) |
| return 1; |
| |
| return 0; |
| } |
| |
| /** |
| * bnx2x_optimize_vlan_mac - optimize ADD and DEL commands. |
| * |
| * @bp: device handle |
| * @o: bnx2x_qable_obj |
| * @elem: bnx2x_exeq_elem |
| */ |
| static int bnx2x_optimize_vlan_mac(struct bnx2x *bp, |
| union bnx2x_qable_obj *qo, |
| struct bnx2x_exeq_elem *elem) |
| { |
| struct bnx2x_exeq_elem query, *pos; |
| struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac; |
| struct bnx2x_exe_queue_obj *exeq = &o->exe_queue; |
| |
| memcpy(&query, elem, sizeof(query)); |
| |
| switch (elem->cmd_data.vlan_mac.cmd) { |
| case BNX2X_VLAN_MAC_ADD: |
| query.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_DEL; |
| break; |
| case BNX2X_VLAN_MAC_DEL: |
| query.cmd_data.vlan_mac.cmd = BNX2X_VLAN_MAC_ADD; |
| break; |
| default: |
| /* Don't handle anything other than ADD or DEL */ |
| return 0; |
| } |
| |
| /* If we found the appropriate element - delete it */ |
| pos = exeq->get(exeq, &query); |
| if (pos) { |
| |
| /* Return the credit of the optimized command */ |
| if (!test_bit(BNX2X_DONT_CONSUME_CAM_CREDIT, |
| &pos->cmd_data.vlan_mac.vlan_mac_flags)) { |
| if ((query.cmd_data.vlan_mac.cmd == |
| BNX2X_VLAN_MAC_ADD) && !o->put_credit(o)) { |
| BNX2X_ERR("Failed to return the credit for the " |
| "optimized ADD command\n"); |
| return -EINVAL; |
| } else if (!o->get_credit(o)) { /* VLAN_MAC_DEL */ |
| BNX2X_ERR("Failed to recover the credit from " |
| "the optimized DEL command\n"); |
| return -EINVAL; |
| } |
| } |
| |
| DP(BNX2X_MSG_SP, "Optimizing %s command\n", |
| (elem->cmd_data.vlan_mac.cmd == BNX2X_VLAN_MAC_ADD) ? |
| "ADD" : "DEL"); |
| |
| list_del(&pos->link); |
| bnx2x_exe_queue_free_elem(bp, pos); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * bnx2x_vlan_mac_get_registry_elem - prepare a registry element |
| * |
| * @bp: device handle |
| * @o: |
| * @elem: |
| * @restore: |
| * @re: |
| * |
| * prepare a registry element according to the current command request. |
| */ |
| static inline int bnx2x_vlan_mac_get_registry_elem( |
| struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| struct bnx2x_exeq_elem *elem, |
| bool restore, |
| struct bnx2x_vlan_mac_registry_elem **re) |
| { |
| int cmd = elem->cmd_data.vlan_mac.cmd; |
| struct bnx2x_vlan_mac_registry_elem *reg_elem; |
| |
| /* Allocate a new registry element if needed. */ |
| if (!restore && |
| ((cmd == BNX2X_VLAN_MAC_ADD) || (cmd == BNX2X_VLAN_MAC_MOVE))) { |
| reg_elem = kzalloc(sizeof(*reg_elem), GFP_ATOMIC); |
| if (!reg_elem) |
| return -ENOMEM; |
| |
| /* Get a new CAM offset */ |
| if (!o->get_cam_offset(o, ®_elem->cam_offset)) { |
| /* |
| * This shell never happen, because we have checked the |
| * CAM availiability in the 'validate'. |
| */ |
| WARN_ON(1); |
| kfree(reg_elem); |
| return -EINVAL; |
| } |
| |
| DP(BNX2X_MSG_SP, "Got cam offset %d\n", reg_elem->cam_offset); |
| |
| /* Set a VLAN-MAC data */ |
| memcpy(®_elem->u, &elem->cmd_data.vlan_mac.u, |
| sizeof(reg_elem->u)); |
| |
| /* Copy the flags (needed for DEL and RESTORE flows) */ |
| reg_elem->vlan_mac_flags = |
| elem->cmd_data.vlan_mac.vlan_mac_flags; |
| } else /* DEL, RESTORE */ |
| reg_elem = o->check_del(o, &elem->cmd_data.vlan_mac.u); |
| |
| *re = reg_elem; |
| return 0; |
| } |
| |
| /** |
| * bnx2x_execute_vlan_mac - execute vlan mac command |
| * |
| * @bp: device handle |
| * @qo: |
| * @exe_chunk: |
| * @ramrod_flags: |
| * |
| * go and send a ramrod! |
| */ |
| static int bnx2x_execute_vlan_mac(struct bnx2x *bp, |
| union bnx2x_qable_obj *qo, |
| struct list_head *exe_chunk, |
| unsigned long *ramrod_flags) |
| { |
| struct bnx2x_exeq_elem *elem; |
| struct bnx2x_vlan_mac_obj *o = &qo->vlan_mac, *cam_obj; |
| struct bnx2x_raw_obj *r = &o->raw; |
| int rc, idx = 0; |
| bool restore = test_bit(RAMROD_RESTORE, ramrod_flags); |
| bool drv_only = test_bit(RAMROD_DRV_CLR_ONLY, ramrod_flags); |
| struct bnx2x_vlan_mac_registry_elem *reg_elem; |
| int cmd; |
| |
| /* |
| * If DRIVER_ONLY execution is requested, cleanup a registry |
| * and exit. Otherwise send a ramrod to FW. |
| */ |
| if (!drv_only) { |
| WARN_ON(r->check_pending(r)); |
| |
| /* Set pending */ |
| r->set_pending(r); |
| |
| /* Fill tha ramrod data */ |
| list_for_each_entry(elem, exe_chunk, link) { |
| cmd = elem->cmd_data.vlan_mac.cmd; |
| /* |
| * We will add to the target object in MOVE command, so |
| * change the object for a CAM search. |
| */ |
| if (cmd == BNX2X_VLAN_MAC_MOVE) |
| cam_obj = elem->cmd_data.vlan_mac.target_obj; |
| else |
| cam_obj = o; |
| |
| rc = bnx2x_vlan_mac_get_registry_elem(bp, cam_obj, |
| elem, restore, |
| ®_elem); |
| if (rc) |
| goto error_exit; |
| |
| WARN_ON(!reg_elem); |
| |
| /* Push a new entry into the registry */ |
| if (!restore && |
| ((cmd == BNX2X_VLAN_MAC_ADD) || |
| (cmd == BNX2X_VLAN_MAC_MOVE))) |
| list_add(®_elem->link, &cam_obj->head); |
| |
| /* Configure a single command in a ramrod data buffer */ |
| o->set_one_rule(bp, o, elem, idx, |
| reg_elem->cam_offset); |
| |
| /* MOVE command consumes 2 entries in the ramrod data */ |
| if (cmd == BNX2X_VLAN_MAC_MOVE) |
| idx += 2; |
| else |
| idx++; |
| } |
| |
| /* |
| * No need for an explicit memory barrier here as long we would |
| * need to ensure the ordering of writing to the SPQ element |
| * and updating of the SPQ producer which involves a memory |
| * read and we will have to put a full memory barrier there |
| * (inside bnx2x_sp_post()). |
| */ |
| |
| rc = bnx2x_sp_post(bp, o->ramrod_cmd, r->cid, |
| U64_HI(r->rdata_mapping), |
| U64_LO(r->rdata_mapping), |
| ETH_CONNECTION_TYPE); |
| if (rc) |
| goto error_exit; |
| } |
| |
| /* Now, when we are done with the ramrod - clean up the registry */ |
| list_for_each_entry(elem, exe_chunk, link) { |
| cmd = elem->cmd_data.vlan_mac.cmd; |
| if ((cmd == BNX2X_VLAN_MAC_DEL) || |
| (cmd == BNX2X_VLAN_MAC_MOVE)) { |
| reg_elem = o->check_del(o, &elem->cmd_data.vlan_mac.u); |
| |
| WARN_ON(!reg_elem); |
| |
| o->put_cam_offset(o, reg_elem->cam_offset); |
| list_del(®_elem->link); |
| kfree(reg_elem); |
| } |
| } |
| |
| if (!drv_only) |
| return 1; |
| else |
| return 0; |
| |
| error_exit: |
| r->clear_pending(r); |
| |
| /* Cleanup a registry in case of a failure */ |
| list_for_each_entry(elem, exe_chunk, link) { |
| cmd = elem->cmd_data.vlan_mac.cmd; |
| |
| if (cmd == BNX2X_VLAN_MAC_MOVE) |
| cam_obj = elem->cmd_data.vlan_mac.target_obj; |
| else |
| cam_obj = o; |
| |
| /* Delete all newly added above entries */ |
| if (!restore && |
| ((cmd == BNX2X_VLAN_MAC_ADD) || |
| (cmd == BNX2X_VLAN_MAC_MOVE))) { |
| reg_elem = o->check_del(cam_obj, |
| &elem->cmd_data.vlan_mac.u); |
| if (reg_elem) { |
| list_del(®_elem->link); |
| kfree(reg_elem); |
| } |
| } |
| } |
| |
| return rc; |
| } |
| |
| static inline int bnx2x_vlan_mac_push_new_cmd( |
| struct bnx2x *bp, |
| struct bnx2x_vlan_mac_ramrod_params *p) |
| { |
| struct bnx2x_exeq_elem *elem; |
| struct bnx2x_vlan_mac_obj *o = p->vlan_mac_obj; |
| bool restore = test_bit(RAMROD_RESTORE, &p->ramrod_flags); |
| |
| /* Allocate the execution queue element */ |
| elem = bnx2x_exe_queue_alloc_elem(bp); |
| if (!elem) |
| return -ENOMEM; |
| |
| /* Set the command 'length' */ |
| switch (p->user_req.cmd) { |
| case BNX2X_VLAN_MAC_MOVE: |
| elem->cmd_len = 2; |
| break; |
| default: |
| elem->cmd_len = 1; |
| } |
| |
| /* Fill the object specific info */ |
| memcpy(&elem->cmd_data.vlan_mac, &p->user_req, sizeof(p->user_req)); |
| |
| /* Try to add a new command to the pending list */ |
| return bnx2x_exe_queue_add(bp, &o->exe_queue, elem, restore); |
| } |
| |
| /** |
| * bnx2x_config_vlan_mac - configure VLAN/MAC/VLAN_MAC filtering rules. |
| * |
| * @bp: device handle |
| * @p: |
| * |
| */ |
| int bnx2x_config_vlan_mac( |
| struct bnx2x *bp, |
| struct bnx2x_vlan_mac_ramrod_params *p) |
| { |
| int rc = 0; |
| struct bnx2x_vlan_mac_obj *o = p->vlan_mac_obj; |
| unsigned long *ramrod_flags = &p->ramrod_flags; |
| bool cont = test_bit(RAMROD_CONT, ramrod_flags); |
| struct bnx2x_raw_obj *raw = &o->raw; |
| |
| /* |
| * Add new elements to the execution list for commands that require it. |
| */ |
| if (!cont) { |
| rc = bnx2x_vlan_mac_push_new_cmd(bp, p); |
| if (rc) |
| return rc; |
| } |
| |
| /* |
| * If nothing will be executed further in this iteration we want to |
| * return PENDING if there are pending commands |
| */ |
| if (!bnx2x_exe_queue_empty(&o->exe_queue)) |
| rc = 1; |
| |
| if (test_bit(RAMROD_DRV_CLR_ONLY, ramrod_flags)) { |
| DP(BNX2X_MSG_SP, "RAMROD_DRV_CLR_ONLY requested: " |
| "clearing a pending bit.\n"); |
| raw->clear_pending(raw); |
| } |
| |
| /* Execute commands if required */ |
| if (cont || test_bit(RAMROD_EXEC, ramrod_flags) || |
| test_bit(RAMROD_COMP_WAIT, ramrod_flags)) { |
| rc = bnx2x_exe_queue_step(bp, &o->exe_queue, ramrod_flags); |
| if (rc < 0) |
| return rc; |
| } |
| |
| /* |
| * RAMROD_COMP_WAIT is a superset of RAMROD_EXEC. If it was set |
| * then user want to wait until the last command is done. |
| */ |
| if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags)) { |
| /* |
| * Wait maximum for the current exe_queue length iterations plus |
| * one (for the current pending command). |
| */ |
| int max_iterations = bnx2x_exe_queue_length(&o->exe_queue) + 1; |
| |
| while (!bnx2x_exe_queue_empty(&o->exe_queue) && |
| max_iterations--) { |
| |
| /* Wait for the current command to complete */ |
| rc = raw->wait_comp(bp, raw); |
| if (rc) |
| return rc; |
| |
| /* Make a next step */ |
| rc = bnx2x_exe_queue_step(bp, &o->exe_queue, |
| ramrod_flags); |
| if (rc < 0) |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| return rc; |
| } |
| |
| |
| |
| /** |
| * bnx2x_vlan_mac_del_all - delete elements with given vlan_mac_flags spec |
| * |
| * @bp: device handle |
| * @o: |
| * @vlan_mac_flags: |
| * @ramrod_flags: execution flags to be used for this deletion |
| * |
| * if the last operation has completed successfully and there are no |
| * moreelements left, positive value if the last operation has completed |
| * successfully and there are more previously configured elements, negative |
| * value is current operation has failed. |
| */ |
| static int bnx2x_vlan_mac_del_all(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *o, |
| unsigned long *vlan_mac_flags, |
| unsigned long *ramrod_flags) |
| { |
| struct bnx2x_vlan_mac_registry_elem *pos = NULL; |
| int rc = 0; |
| struct bnx2x_vlan_mac_ramrod_params p; |
| struct bnx2x_exe_queue_obj *exeq = &o->exe_queue; |
| struct bnx2x_exeq_elem *exeq_pos, *exeq_pos_n; |
| |
| /* Clear pending commands first */ |
| |
| spin_lock_bh(&exeq->lock); |
| |
| list_for_each_entry_safe(exeq_pos, exeq_pos_n, &exeq->exe_queue, link) { |
| if (exeq_pos->cmd_data.vlan_mac.vlan_mac_flags == |
| *vlan_mac_flags) { |
| rc = exeq->remove(bp, exeq->owner, exeq_pos); |
| if (rc) { |
| BNX2X_ERR("Failed to remove command\n"); |
| return rc; |
| } |
| list_del(&exeq_pos->link); |
| } |
| } |
| |
| spin_unlock_bh(&exeq->lock); |
| |
| /* Prepare a command request */ |
| memset(&p, 0, sizeof(p)); |
| p.vlan_mac_obj = o; |
| p.ramrod_flags = *ramrod_flags; |
| p.user_req.cmd = BNX2X_VLAN_MAC_DEL; |
| |
| /* |
| * Add all but the last VLAN-MAC to the execution queue without actually |
| * execution anything. |
| */ |
| __clear_bit(RAMROD_COMP_WAIT, &p.ramrod_flags); |
| __clear_bit(RAMROD_EXEC, &p.ramrod_flags); |
| __clear_bit(RAMROD_CONT, &p.ramrod_flags); |
| |
| list_for_each_entry(pos, &o->head, link) { |
| if (pos->vlan_mac_flags == *vlan_mac_flags) { |
| p.user_req.vlan_mac_flags = pos->vlan_mac_flags; |
| memcpy(&p.user_req.u, &pos->u, sizeof(pos->u)); |
| rc = bnx2x_config_vlan_mac(bp, &p); |
| if (rc < 0) { |
| BNX2X_ERR("Failed to add a new DEL command\n"); |
| return rc; |
| } |
| } |
| } |
| |
| p.ramrod_flags = *ramrod_flags; |
| __set_bit(RAMROD_CONT, &p.ramrod_flags); |
| |
| return bnx2x_config_vlan_mac(bp, &p); |
| } |
| |
| static inline void bnx2x_init_raw_obj(struct bnx2x_raw_obj *raw, u8 cl_id, |
| u32 cid, u8 func_id, void *rdata, dma_addr_t rdata_mapping, int state, |
| unsigned long *pstate, bnx2x_obj_type type) |
| { |
| raw->func_id = func_id; |
| raw->cid = cid; |
| raw->cl_id = cl_id; |
| raw->rdata = rdata; |
| raw->rdata_mapping = rdata_mapping; |
| raw->state = state; |
| raw->pstate = pstate; |
| raw->obj_type = type; |
| raw->check_pending = bnx2x_raw_check_pending; |
| raw->clear_pending = bnx2x_raw_clear_pending; |
| raw->set_pending = bnx2x_raw_set_pending; |
| raw->wait_comp = bnx2x_raw_wait; |
| } |
| |
| static inline void bnx2x_init_vlan_mac_common(struct bnx2x_vlan_mac_obj *o, |
| u8 cl_id, u32 cid, u8 func_id, void *rdata, dma_addr_t rdata_mapping, |
| int state, unsigned long *pstate, bnx2x_obj_type type, |
| struct bnx2x_credit_pool_obj *macs_pool, |
| struct bnx2x_credit_pool_obj *vlans_pool) |
| { |
| INIT_LIST_HEAD(&o->head); |
| |
| o->macs_pool = macs_pool; |
| o->vlans_pool = vlans_pool; |
| |
| o->delete_all = bnx2x_vlan_mac_del_all; |
| o->restore = bnx2x_vlan_mac_restore; |
| o->complete = bnx2x_complete_vlan_mac; |
| o->wait = bnx2x_wait_vlan_mac; |
| |
| bnx2x_init_raw_obj(&o->raw, cl_id, cid, func_id, rdata, rdata_mapping, |
| state, pstate, type); |
| } |
| |
| |
| void bnx2x_init_mac_obj(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *mac_obj, |
| u8 cl_id, u32 cid, u8 func_id, void *rdata, |
| dma_addr_t rdata_mapping, int state, |
| unsigned long *pstate, bnx2x_obj_type type, |
| struct bnx2x_credit_pool_obj *macs_pool) |
| { |
| union bnx2x_qable_obj *qable_obj = (union bnx2x_qable_obj *)mac_obj; |
| |
| bnx2x_init_vlan_mac_common(mac_obj, cl_id, cid, func_id, rdata, |
| rdata_mapping, state, pstate, type, |
| macs_pool, NULL); |
| |
| /* CAM credit pool handling */ |
| mac_obj->get_credit = bnx2x_get_credit_mac; |
| mac_obj->put_credit = bnx2x_put_credit_mac; |
| mac_obj->get_cam_offset = bnx2x_get_cam_offset_mac; |
| mac_obj->put_cam_offset = bnx2x_put_cam_offset_mac; |
| |
| if (CHIP_IS_E1x(bp)) { |
| mac_obj->set_one_rule = bnx2x_set_one_mac_e1x; |
| mac_obj->check_del = bnx2x_check_mac_del; |
| mac_obj->check_add = bnx2x_check_mac_add; |
| mac_obj->check_move = bnx2x_check_move_always_err; |
| mac_obj->ramrod_cmd = RAMROD_CMD_ID_ETH_SET_MAC; |
| |
| /* Exe Queue */ |
| bnx2x_exe_queue_init(bp, |
| &mac_obj->exe_queue, 1, qable_obj, |
| bnx2x_validate_vlan_mac, |
| bnx2x_remove_vlan_mac, |
| bnx2x_optimize_vlan_mac, |
| bnx2x_execute_vlan_mac, |
| bnx2x_exeq_get_mac); |
| } else { |
| mac_obj->set_one_rule = bnx2x_set_one_mac_e2; |
| mac_obj->check_del = bnx2x_check_mac_del; |
| mac_obj->check_add = bnx2x_check_mac_add; |
| mac_obj->check_move = bnx2x_check_move; |
| mac_obj->ramrod_cmd = |
| RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES; |
| mac_obj->get_n_elements = bnx2x_get_n_elements; |
| |
| /* Exe Queue */ |
| bnx2x_exe_queue_init(bp, |
| &mac_obj->exe_queue, CLASSIFY_RULES_COUNT, |
| qable_obj, bnx2x_validate_vlan_mac, |
| bnx2x_remove_vlan_mac, |
| bnx2x_optimize_vlan_mac, |
| bnx2x_execute_vlan_mac, |
| bnx2x_exeq_get_mac); |
| } |
| } |
| |
| void bnx2x_init_vlan_obj(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *vlan_obj, |
| u8 cl_id, u32 cid, u8 func_id, void *rdata, |
| dma_addr_t rdata_mapping, int state, |
| unsigned long *pstate, bnx2x_obj_type type, |
| struct bnx2x_credit_pool_obj *vlans_pool) |
| { |
| union bnx2x_qable_obj *qable_obj = (union bnx2x_qable_obj *)vlan_obj; |
| |
| bnx2x_init_vlan_mac_common(vlan_obj, cl_id, cid, func_id, rdata, |
| rdata_mapping, state, pstate, type, NULL, |
| vlans_pool); |
| |
| vlan_obj->get_credit = bnx2x_get_credit_vlan; |
| vlan_obj->put_credit = bnx2x_put_credit_vlan; |
| vlan_obj->get_cam_offset = bnx2x_get_cam_offset_vlan; |
| vlan_obj->put_cam_offset = bnx2x_put_cam_offset_vlan; |
| |
| if (CHIP_IS_E1x(bp)) { |
| BNX2X_ERR("Do not support chips others than E2 and newer\n"); |
| BUG(); |
| } else { |
| vlan_obj->set_one_rule = bnx2x_set_one_vlan_e2; |
| vlan_obj->check_del = bnx2x_check_vlan_del; |
| vlan_obj->check_add = bnx2x_check_vlan_add; |
| vlan_obj->check_move = bnx2x_check_move; |
| vlan_obj->ramrod_cmd = |
| RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES; |
| |
| /* Exe Queue */ |
| bnx2x_exe_queue_init(bp, |
| &vlan_obj->exe_queue, CLASSIFY_RULES_COUNT, |
| qable_obj, bnx2x_validate_vlan_mac, |
| bnx2x_remove_vlan_mac, |
| bnx2x_optimize_vlan_mac, |
| bnx2x_execute_vlan_mac, |
| bnx2x_exeq_get_vlan); |
| } |
| } |
| |
| void bnx2x_init_vlan_mac_obj(struct bnx2x *bp, |
| struct bnx2x_vlan_mac_obj *vlan_mac_obj, |
| u8 cl_id, u32 cid, u8 func_id, void *rdata, |
| dma_addr_t rdata_mapping, int state, |
| unsigned long *pstate, bnx2x_obj_type type, |
| struct bnx2x_credit_pool_obj *macs_pool, |
| struct bnx2x_credit_pool_obj *vlans_pool) |
| { |
| union bnx2x_qable_obj *qable_obj = |
| (union bnx2x_qable_obj *)vlan_mac_obj; |
| |
| bnx2x_init_vlan_mac_common(vlan_mac_obj, cl_id, cid, func_id, rdata, |
| rdata_mapping, state, pstate, type, |
| macs_pool, vlans_pool); |
| |
| /* CAM pool handling */ |
| vlan_mac_obj->get_credit = bnx2x_get_credit_vlan_mac; |
| vlan_mac_obj->put_credit = bnx2x_put_credit_vlan_mac; |
| /* |
| * CAM offset is relevant for 57710 and 57711 chips only which have a |
| * single CAM for both MACs and VLAN-MAC pairs. So the offset |
| * will be taken from MACs' pool object only. |
| */ |
| vlan_mac_obj->get_cam_offset = bnx2x_get_cam_offset_mac; |
| vlan_mac_obj->put_cam_offset = bnx2x_put_cam_offset_mac; |
| |
| if (CHIP_IS_E1(bp)) { |
| BNX2X_ERR("Do not support chips others than E2\n"); |
| BUG(); |
| } else if (CHIP_IS_E1H(bp)) { |
| vlan_mac_obj->set_one_rule = bnx2x_set_one_vlan_mac_e1h; |
| vlan_mac_obj->check_del = bnx2x_check_vlan_mac_del; |
| vlan_mac_obj->check_add = bnx2x_check_vlan_mac_add; |
| vlan_mac_obj->check_move = bnx2x_check_move_always_err; |
| vlan_mac_obj->ramrod_cmd = RAMROD_CMD_ID_ETH_SET_MAC; |
| |
| /* Exe Queue */ |
| bnx2x_exe_queue_init(bp, |
| &vlan_mac_obj->exe_queue, 1, qable_obj, |
| bnx2x_validate_vlan_mac, |
| bnx2x_remove_vlan_mac, |
| bnx2x_optimize_vlan_mac, |
| bnx2x_execute_vlan_mac, |
| bnx2x_exeq_get_vlan_mac); |
| } else { |
| vlan_mac_obj->set_one_rule = bnx2x_set_one_vlan_mac_e2; |
| vlan_mac_obj->check_del = bnx2x_check_vlan_mac_del; |
| vlan_mac_obj->check_add = bnx2x_check_vlan_mac_add; |
| vlan_mac_obj->check_move = bnx2x_check_move; |
| vlan_mac_obj->ramrod_cmd = |
| RAMROD_CMD_ID_ETH_CLASSIFICATION_RULES; |
| |
| /* Exe Queue */ |
| bnx2x_exe_queue_init(bp, |
| &vlan_mac_obj->exe_queue, |
| CLASSIFY_RULES_COUNT, |
| qable_obj, bnx2x_validate_vlan_mac, |
| bnx2x_remove_vlan_mac, |
| bnx2x_optimize_vlan_mac, |
| bnx2x_execute_vlan_mac, |
| bnx2x_exeq_get_vlan_mac); |
| } |
| |
| } |
| |
| /* RX_MODE verbs: DROP_ALL/ACCEPT_ALL/ACCEPT_ALL_MULTI/ACCEPT_ALL_VLAN/NORMAL */ |
| static inline void __storm_memset_mac_filters(struct bnx2x *bp, |
| struct tstorm_eth_mac_filter_config *mac_filters, |
| u16 pf_id) |
| { |
| size_t size = sizeof(struct tstorm_eth_mac_filter_config); |
| |
| u32 addr = BAR_TSTRORM_INTMEM + |
| TSTORM_MAC_FILTER_CONFIG_OFFSET(pf_id); |
| |
| __storm_memset_struct(bp, addr, size, (u32 *)mac_filters); |
| } |
| |
| static int bnx2x_set_rx_mode_e1x(struct bnx2x *bp, |
| struct bnx2x_rx_mode_ramrod_params *p) |
| { |
| /* update the bp MAC filter structure */ |
| u32 mask = (1 << p->cl_id); |
| |
| struct tstorm_eth_mac_filter_config *mac_filters = |
| (struct tstorm_eth_mac_filter_config *)p->rdata; |
| |
| /* initial seeting is drop-all */ |
| u8 drop_all_ucast = 1, drop_all_mcast = 1; |
| u8 accp_all_ucast = 0, accp_all_bcast = 0, accp_all_mcast = 0; |
| u8 unmatched_unicast = 0; |
| |
| /* In e1x there we only take into account rx acceot flag since tx switching |
| * isn't enabled. */ |
| if (test_bit(BNX2X_ACCEPT_UNICAST, &p->rx_accept_flags)) |
| /* accept matched ucast */ |
| drop_all_ucast = 0; |
| |
| if (test_bit(BNX2X_ACCEPT_MULTICAST, &p->rx_accept_flags)) |
| /* accept matched mcast */ |
| drop_all_mcast = 0; |
| |
| if (test_bit(BNX2X_ACCEPT_ALL_UNICAST, &p->rx_accept_flags)) { |
| /* accept all mcast */ |
| drop_all_ucast = 0; |
| accp_all_ucast = 1; |
| } |
| if (test_bit(BNX2X_ACCEPT_ALL_MULTICAST, &p->rx_accept_flags)) { |
| /* accept all mcast */ |
| drop_all_mcast = 0; |
| accp_all_mcast = 1; |
| } |
| if (test_bit(BNX2X_ACCEPT_BROADCAST, &p->rx_accept_flags)) |
| /* accept (all) bcast */ |
| accp_all_bcast = 1; |
| if (test_bit(BNX2X_ACCEPT_UNMATCHED, &p->rx_accept_flags)) |
| /* accept unmatched unicasts */ |
| unmatched_unicast = 1; |
| |
| mac_filters->ucast_drop_all = drop_all_ucast ? |
| mac_filters->ucast_drop_all | mask : |
| mac_filters->ucast_drop_all & ~mask; |
| |
| mac_filters->mcast_drop_all = drop_all_mcast ? |
| mac_filters->mcast_drop_all | mask : |
| mac_filters->mcast_drop_all & ~mask; |
| |
| mac_filters->ucast_accept_all = accp_all_ucast ? |
| mac_filters->ucast_accept_all | mask : |
| mac_filters->ucast_accept_all & ~mask; |
| |
| mac_filters->mcast_accept_all = accp_all_mcast ? |
| mac_filters->mcast_accept_all | mask : |
| mac_filters->mcast_accept_all & ~mask; |
| |
| mac_filters->bcast_accept_all = accp_all_bcast ? |
| mac_filters->bcast_accept_all | mask : |
| mac_filters->bcast_accept_all & ~mask; |
| |
| mac_filters->unmatched_unicast = unmatched_unicast ? |
| mac_filters->unmatched_unicast | mask : |
| mac_filters->unmatched_unicast & ~mask; |
| |
| DP(BNX2X_MSG_SP, "drop_ucast 0x%x\ndrop_mcast 0x%x\n accp_ucast 0x%x\n" |
| "accp_mcast 0x%x\naccp_bcast 0x%x\n", |
| mac_filters->ucast_drop_all, |
| mac_filters->mcast_drop_all, |
| mac_filters->ucast_accept_all, |
| mac_filters->mcast_accept_all, |
| mac_filters->bcast_accept_all); |
| |
| /* write the MAC filter structure*/ |
| __storm_memset_mac_filters(bp, mac_filters, p->func_id); |
| |
| /* The operation is completed */ |
| clear_bit(p->state, p->pstate); |
| smp_mb__after_clear_bit(); |
| |
| return 0; |
| } |
| |
| /* Setup ramrod data */ |
| static inline void bnx2x_rx_mode_set_rdata_hdr_e2(u32 cid, |
| struct eth_classify_header *hdr, |
| u8 rule_cnt) |
| { |
| hdr->echo = cid; |
| hdr->rule_cnt = rule_cnt; |
| } |
| |
| static inline void bnx2x_rx_mode_set_cmd_state_e2(struct bnx2x *bp, |
| unsigned long accept_flags, |
| struct eth_filter_rules_cmd *cmd, |
| bool clear_accept_all) |
| { |
| u16 state; |
| |
| /* start with 'drop-all' */ |
| state = ETH_FILTER_RULES_CMD_UCAST_DROP_ALL | |
| ETH_FILTER_RULES_CMD_MCAST_DROP_ALL; |
| |
| if (accept_flags) { |
| if (test_bit(BNX2X_ACCEPT_UNICAST, &accept_flags)) |
| state &= ~ETH_FILTER_RULES_CMD_UCAST_DROP_ALL; |
| |
| if (test_bit(BNX2X_ACCEPT_MULTICAST, &accept_flags)) |
| state &= ~ETH_FILTER_RULES_CMD_MCAST_DROP_ALL; |
| |
| if (test_bit(BNX2X_ACCEPT_ALL_UNICAST, &accept_flags)) { |
| state &= ~ETH_FILTER_RULES_CMD_UCAST_DROP_ALL; |
| state |= ETH_FILTER_RULES_CMD_UCAST_ACCEPT_ALL; |
| } |
| |
| if (test_bit(BNX2X_ACCEPT_ALL_MULTICAST, &accept_flags)) { |
| state |= ETH_FILTER_RULES_CMD_MCAST_ACCEPT_ALL; |
| state &= ~ETH_FILTER_RULES_CMD_MCAST_DROP_ALL; |
| } |
| if (test_bit(BNX2X_ACCEPT_BROADCAST, &accept_flags)) |
| state |= ETH_FILTER_RULES_CMD_BCAST_ACCEPT_ALL; |
| |
| if (test_bit(BNX2X_ACCEPT_UNMATCHED, &accept_flags)) { |
| state &= ~ETH_FILTER_RULES_CMD_UCAST_DROP_ALL; |
| state |= ETH_FILTER_RULES_CMD_UCAST_ACCEPT_UNMATCHED; |
| } |
| if (test_bit(BNX2X_ACCEPT_ANY_VLAN, &accept_flags)) |
| state |= ETH_FILTER_RULES_CMD_ACCEPT_ANY_VLAN; |
| } |
| |
| /* Clear ACCEPT_ALL_XXX flags for FCoE L2 Queue */ |
| if (clear_accept_all) { |
| state &= ~ETH_FILTER_RULES_CMD_MCAST_ACCEPT_ALL; |
| state &= ~ETH_FILTER_RULES_CMD_BCAST_ACCEPT_ALL; |
| state &= ~ETH_FILTER_RULES_CMD_UCAST_ACCEPT_ALL; |
| state &= ~ETH_FILTER_RULES_CMD_UCAST_ACCEPT_UNMATCHED; |
| } |
| |
| cmd->state = cpu_to_le16(state); |
| |
| } |
| |
| static int bnx2x_set_rx_mode_e2(struct bnx2x *bp, |
| struct bnx2x_rx_mode_ramrod_params *p) |
| { |
| struct eth_filter_rules_ramrod_data *data = p->rdata; |
| int rc; |
| u8 rule_idx = 0; |
| |
| /* Reset the ramrod data buffer */ |
| memset(data, 0, sizeof(*data)); |
| |
| /* Setup ramrod data */ |
| |
| /* Tx (internal switching) */ |
| if (test_bit(RAMROD_TX, &p->ramrod_flags)) { |
| data->rules[rule_idx].client_id = p->cl_id; |
| data->rules[rule_idx].func_id = p->func_id; |
| |
| data->rules[rule_idx].cmd_general_data = |
| ETH_FILTER_RULES_CMD_TX_CMD; |
| |
| bnx2x_rx_mode_set_cmd_state_e2(bp, p->tx_accept_flags, |
| &(data->rules[rule_idx++]), false); |
| } |
| |
| /* Rx */ |
| if (test_bit(RAMROD_RX, &p->ramrod_flags)) { |
| data->rules[rule_idx].client_id = p->cl_id; |
| data->rules[rule_idx].func_id = p->func_id; |
| |
| data->rules[rule_idx].cmd_general_data = |
| ETH_FILTER_RULES_CMD_RX_CMD; |
| |
| bnx2x_rx_mode_set_cmd_state_e2(bp, p->rx_accept_flags, |
| &(data->rules[rule_idx++]), false); |
| } |
| |
| |
| /* |
| * If FCoE Queue configuration has been requested configure the Rx and |
| * internal switching modes for this queue in separate rules. |
| * |
| * FCoE queue shell never be set to ACCEPT_ALL packets of any sort: |
| * MCAST_ALL, UCAST_ALL, BCAST_ALL and UNMATCHED. |
| */ |
| if (test_bit(BNX2X_RX_MODE_FCOE_ETH, &p->rx_mode_flags)) { |
| /* Tx (internal switching) */ |
| if (test_bit(RAMROD_TX, &p->ramrod_flags)) { |
| data->rules[rule_idx].client_id = bnx2x_fcoe(bp, cl_id); |
| data->rules[rule_idx].func_id = p->func_id; |
| |
| data->rules[rule_idx].cmd_general_data = |
| ETH_FILTER_RULES_CMD_TX_CMD; |
| |
| bnx2x_rx_mode_set_cmd_state_e2(bp, p->tx_accept_flags, |
| &(data->rules[rule_idx++]), |
| true); |
| } |
| |
| /* Rx */ |
| if (test_bit(RAMROD_RX, &p->ramrod_flags)) { |
| data->rules[rule_idx].client_id = bnx2x_fcoe(bp, cl_id); |
| data->rules[rule_idx].func_id = p->func_id; |
| |
| data->rules[rule_idx].cmd_general_data = |
| ETH_FILTER_RULES_CMD_RX_CMD; |
| |
| bnx2x_rx_mode_set_cmd_state_e2(bp, p->rx_accept_flags, |
| &(data->rules[rule_idx++]), |
| true); |
| } |
| } |
| |
| /* |
| * Set the ramrod header (most importantly - number of rules to |
| * configure). |
| */ |
| bnx2x_rx_mode_set_rdata_hdr_e2(p->cid, &data->header, rule_idx); |
| |
| DP(BNX2X_MSG_SP, "About to configure %d rules, rx_accept_flags 0x%lx, " |
| "tx_accept_flags 0x%lx\n", |
| data->header.rule_cnt, p->rx_accept_flags, |
| p->tx_accept_flags); |
| |
| /* |
| * No need for an explicit memory barrier here as long we would |
| * need to ensure the ordering of writing to the SPQ element |
| * and updating of the SPQ producer which involves a memory |
| * read and we will have to put a full memory barrier there |
| * (inside bnx2x_sp_post()). |
| */ |
| |
| /* Send a ramrod */ |
| rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_FILTER_RULES, p->cid, |
| U64_HI(p->rdata_mapping), |
| U64_LO(p->rdata_mapping), |
| ETH_CONNECTION_TYPE); |
| if (rc) |
| return rc; |
| |
| /* Ramrod completion is pending */ |
| return 1; |
| } |
| |
| static int bnx2x_wait_rx_mode_comp_e2(struct bnx2x *bp, |
| struct bnx2x_rx_mode_ramrod_params *p) |
| { |
| return bnx2x_state_wait(bp, p->state, p->pstate); |
| } |
| |
| static int bnx2x_empty_rx_mode_wait(struct bnx2x *bp, |
| struct bnx2x_rx_mode_ramrod_params *p) |
| { |
| /* Do nothing */ |
| return 0; |
| } |
| |
| int bnx2x_config_rx_mode(struct bnx2x *bp, |
| struct bnx2x_rx_mode_ramrod_params *p) |
| { |
| int rc; |
| |
| /* Configure the new classification in the chip */ |
| rc = p->rx_mode_obj->config_rx_mode(bp, p); |
| if (rc < 0) |
| return rc; |
| |
| /* Wait for a ramrod completion if was requested */ |
| if (test_bit(RAMROD_COMP_WAIT, &p->ramrod_flags)) { |
| rc = p->rx_mode_obj->wait_comp(bp, p); |
| if (rc) |
| return rc; |
| } |
| |
| return rc; |
| } |
| |
| void bnx2x_init_rx_mode_obj(struct bnx2x *bp, |
| struct bnx2x_rx_mode_obj *o) |
| { |
| if (CHIP_IS_E1x(bp)) { |
| o->wait_comp = bnx2x_empty_rx_mode_wait; |
| o->config_rx_mode = bnx2x_set_rx_mode_e1x; |
| } else { |
| o->wait_comp = bnx2x_wait_rx_mode_comp_e2; |
| o->config_rx_mode = bnx2x_set_rx_mode_e2; |
| } |
| } |
| |
| /********************* Multicast verbs: SET, CLEAR ****************************/ |
| static inline u8 bnx2x_mcast_bin_from_mac(u8 *mac) |
| { |
| return (crc32c_le(0, mac, ETH_ALEN) >> 24) & 0xff; |
| } |
| |
| struct bnx2x_mcast_mac_elem { |
| struct list_head link; |
| u8 mac[ETH_ALEN]; |
| u8 pad[2]; /* For a natural alignment of the following buffer */ |
| }; |
| |
| struct bnx2x_pending_mcast_cmd { |
| struct list_head link; |
| int type; /* BNX2X_MCAST_CMD_X */ |
| union { |
| struct list_head macs_head; |
| u32 macs_num; /* Needed for DEL command */ |
| int next_bin; /* Needed for RESTORE flow with aprox match */ |
| } data; |
| |
| bool done; /* set to true, when the command has been handled, |
| * practically used in 57712 handling only, where one pending |
| * command may be handled in a few operations. As long as for |
| * other chips every operation handling is completed in a |
| * single ramrod, there is no need to utilize this field. |
| */ |
| }; |
| |
| static int bnx2x_mcast_wait(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o) |
| { |
| if (bnx2x_state_wait(bp, o->sched_state, o->raw.pstate) || |
| o->raw.wait_comp(bp, &o->raw)) |
| return -EBUSY; |
| |
| return 0; |
| } |
| |
| static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, |
| struct bnx2x_mcast_ramrod_params *p, |
| int cmd) |
| { |
| int total_sz; |
| struct bnx2x_pending_mcast_cmd *new_cmd; |
| struct bnx2x_mcast_mac_elem *cur_mac = NULL; |
| struct bnx2x_mcast_list_elem *pos; |
| int macs_list_len = ((cmd == BNX2X_MCAST_CMD_ADD) ? |
| p->mcast_list_len : 0); |
| |
| /* If the command is empty ("handle pending commands only"), break */ |
| if (!p->mcast_list_len) |
| return 0; |
| |
| total_sz = sizeof(*new_cmd) + |
| macs_list_len * sizeof(struct bnx2x_mcast_mac_elem); |
| |
| /* Add mcast is called under spin_lock, thus calling with GFP_ATOMIC */ |
| new_cmd = kzalloc(total_sz, GFP_ATOMIC); |
| |
| if (!new_cmd) |
| return -ENOMEM; |
| |
| DP(BNX2X_MSG_SP, "About to enqueue a new %d command. " |
| "macs_list_len=%d\n", cmd, macs_list_len); |
| |
| INIT_LIST_HEAD(&new_cmd->data.macs_head); |
| |
| new_cmd->type = cmd; |
| new_cmd->done = false; |
| |
| switch (cmd) { |
| case BNX2X_MCAST_CMD_ADD: |
| cur_mac = (struct bnx2x_mcast_mac_elem *) |
| ((u8 *)new_cmd + sizeof(*new_cmd)); |
| |
| /* Push the MACs of the current command into the pendig command |
| * MACs list: FIFO |
| */ |
| list_for_each_entry(pos, &p->mcast_list, link) { |
| memcpy(cur_mac->mac, pos->mac, ETH_ALEN); |
| list_add_tail(&cur_mac->link, &new_cmd->data.macs_head); |
| cur_mac++; |
| } |
| |
| break; |
| |
| case BNX2X_MCAST_CMD_DEL: |
| new_cmd->data.macs_num = p->mcast_list_len; |
| break; |
| |
| case BNX2X_MCAST_CMD_RESTORE: |
| new_cmd->data.next_bin = 0; |
| break; |
| |
| default: |
| BNX2X_ERR("Unknown command: %d\n", cmd); |
| return -EINVAL; |
| } |
| |
| /* Push the new pending command to the tail of the pending list: FIFO */ |
| list_add_tail(&new_cmd->link, &o->pending_cmds_head); |
| |
| o->set_sched(o); |
| |
| return 1; |
| } |
| |
| /** |
| * bnx2x_mcast_get_next_bin - get the next set bin (index) |
| * |
| * @o: |
| * @last: index to start looking from (including) |
| * |
| * returns the next found (set) bin or a negative value if none is found. |
| */ |
| static inline int bnx2x_mcast_get_next_bin(struct bnx2x_mcast_obj *o, int last) |
| { |
| int i, j, inner_start = last % BIT_VEC64_ELEM_SZ; |
| |
| for (i = last / BIT_VEC64_ELEM_SZ; i < BNX2X_MCAST_VEC_SZ; i++) { |
| if (o->registry.aprox_match.vec[i]) |
| for (j = inner_start; j < BIT_VEC64_ELEM_SZ; j++) { |
| int cur_bit = j + BIT_VEC64_ELEM_SZ * i; |
| if (BIT_VEC64_TEST_BIT(o->registry.aprox_match. |
| vec, cur_bit)) { |
| return cur_bit; |
| } |
| } |
| inner_start = 0; |
| } |
| |
| /* None found */ |
| return -1; |
| } |
| |
| /** |
| * bnx2x_mcast_clear_first_bin - find the first set bin and clear it |
| * |
| * @o: |
| * |
| * returns the index of the found bin or -1 if none is found |
| */ |
| static inline int bnx2x_mcast_clear_first_bin(struct bnx2x_mcast_obj *o) |
| { |
| int cur_bit = bnx2x_mcast_get_next_bin(o, 0); |
| |
| if (cur_bit >= 0) |
| BIT_VEC64_CLEAR_BIT(o->registry.aprox_match.vec, cur_bit); |
| |
| return cur_bit; |
| } |
| |
| static inline u8 bnx2x_mcast_get_rx_tx_flag(struct bnx2x_mcast_obj *o) |
| { |
| struct bnx2x_raw_obj *raw = &o->raw; |
| u8 rx_tx_flag = 0; |
| |
| if ((raw->obj_type == BNX2X_OBJ_TYPE_TX) || |
| (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX)) |
| rx_tx_flag |= ETH_MULTICAST_RULES_CMD_TX_CMD; |
| |
| if ((raw->obj_type == BNX2X_OBJ_TYPE_RX) || |
| (raw->obj_type == BNX2X_OBJ_TYPE_RX_TX)) |
| rx_tx_flag |= ETH_MULTICAST_RULES_CMD_RX_CMD; |
| |
| return rx_tx_flag; |
| } |
| |
| static void bnx2x_mcast_set_one_rule_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, int idx, |
| union bnx2x_mcast_config_data *cfg_data, |
| int cmd) |
| { |
| struct bnx2x_raw_obj *r = &o->raw; |
| struct eth_multicast_rules_ramrod_data *data = |
| (struct eth_multicast_rules_ramrod_data *)(r->rdata); |
| u8 func_id = r->func_id; |
| u8 rx_tx_add_flag = bnx2x_mcast_get_rx_tx_flag(o); |
| int bin; |
| |
| if ((cmd == BNX2X_MCAST_CMD_ADD) || (cmd == BNX2X_MCAST_CMD_RESTORE)) |
| rx_tx_add_flag |= ETH_MULTICAST_RULES_CMD_IS_ADD; |
| |
| data->rules[idx].cmd_general_data |= rx_tx_add_flag; |
| |
| /* Get a bin and update a bins' vector */ |
| switch (cmd) { |
| case BNX2X_MCAST_CMD_ADD: |
| bin = bnx2x_mcast_bin_from_mac(cfg_data->mac); |
| BIT_VEC64_SET_BIT(o->registry.aprox_match.vec, bin); |
| break; |
| |
| case BNX2X_MCAST_CMD_DEL: |
| /* If there were no more bins to clear |
| * (bnx2x_mcast_clear_first_bin() returns -1) then we would |
| * clear any (0xff) bin. |
| * See bnx2x_mcast_validate_e2() for explanation when it may |
| * happen. |
| */ |
| bin = bnx2x_mcast_clear_first_bin(o); |
| break; |
| |
| case BNX2X_MCAST_CMD_RESTORE: |
| bin = cfg_data->bin; |
| break; |
| |
| default: |
| BNX2X_ERR("Unknown command: %d\n", cmd); |
| return; |
| } |
| |
| DP(BNX2X_MSG_SP, "%s bin %d\n", |
| ((rx_tx_add_flag & ETH_MULTICAST_RULES_CMD_IS_ADD) ? |
| "Setting" : "Clearing"), bin); |
| |
| data->rules[idx].bin_id = (u8)bin; |
| data->rules[idx].func_id = func_id; |
| data->rules[idx].engine_id = o->engine_id; |
| } |
| |
| /** |
| * bnx2x_mcast_handle_restore_cmd_e2 - restore configuration from the registry |
| * |
| * @bp: device handle |
| * @o: |
| * @start_bin: index in the registry to start from (including) |
| * @rdata_idx: index in the ramrod data to start from |
| * |
| * returns last handled bin index or -1 if all bins have been handled |
| */ |
| static inline int bnx2x_mcast_handle_restore_cmd_e2( |
| struct bnx2x *bp, struct bnx2x_mcast_obj *o , int start_bin, |
| int *rdata_idx) |
| { |
| int cur_bin, cnt = *rdata_idx; |
| union bnx2x_mcast_config_data cfg_data = {0}; |
| |
| /* go through the registry and configure the bins from it */ |
| for (cur_bin = bnx2x_mcast_get_next_bin(o, start_bin); cur_bin >= 0; |
| cur_bin = bnx2x_mcast_get_next_bin(o, cur_bin + 1)) { |
| |
| cfg_data.bin = (u8)cur_bin; |
| o->set_one_rule(bp, o, cnt, &cfg_data, |
| BNX2X_MCAST_CMD_RESTORE); |
| |
| cnt++; |
| |
| DP(BNX2X_MSG_SP, "About to configure a bin %d\n", cur_bin); |
| |
| /* Break if we reached the maximum number |
| * of rules. |
| */ |
| if (cnt >= o->max_cmd_len) |
| break; |
| } |
| |
| *rdata_idx = cnt; |
| |
| return cur_bin; |
| } |
| |
| static inline void bnx2x_mcast_hdl_pending_add_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, struct bnx2x_pending_mcast_cmd *cmd_pos, |
| int *line_idx) |
| { |
| struct bnx2x_mcast_mac_elem *pmac_pos, *pmac_pos_n; |
| int cnt = *line_idx; |
| union bnx2x_mcast_config_data cfg_data = {0}; |
| |
| list_for_each_entry_safe(pmac_pos, pmac_pos_n, &cmd_pos->data.macs_head, |
| link) { |
| |
| cfg_data.mac = &pmac_pos->mac[0]; |
| o->set_one_rule(bp, o, cnt, &cfg_data, cmd_pos->type); |
| |
| cnt++; |
| |
| DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC\n", |
| pmac_pos->mac); |
| |
| list_del(&pmac_pos->link); |
| |
| /* Break if we reached the maximum number |
| * of rules. |
| */ |
| if (cnt >= o->max_cmd_len) |
| break; |
| } |
| |
| *line_idx = cnt; |
| |
| /* if no more MACs to configure - we are done */ |
| if (list_empty(&cmd_pos->data.macs_head)) |
| cmd_pos->done = true; |
| } |
| |
| static inline void bnx2x_mcast_hdl_pending_del_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, struct bnx2x_pending_mcast_cmd *cmd_pos, |
| int *line_idx) |
| { |
| int cnt = *line_idx; |
| |
| while (cmd_pos->data.macs_num) { |
| o->set_one_rule(bp, o, cnt, NULL, cmd_pos->type); |
| |
| cnt++; |
| |
| cmd_pos->data.macs_num--; |
| |
| DP(BNX2X_MSG_SP, "Deleting MAC. %d left,cnt is %d\n", |
| cmd_pos->data.macs_num, cnt); |
| |
| /* Break if we reached the maximum |
| * number of rules. |
| */ |
| if (cnt >= o->max_cmd_len) |
| break; |
| } |
| |
| *line_idx = cnt; |
| |
| /* If we cleared all bins - we are done */ |
| if (!cmd_pos->data.macs_num) |
| cmd_pos->done = true; |
| } |
| |
| static inline void bnx2x_mcast_hdl_pending_restore_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, struct bnx2x_pending_mcast_cmd *cmd_pos, |
| int *line_idx) |
| { |
| cmd_pos->data.next_bin = o->hdl_restore(bp, o, cmd_pos->data.next_bin, |
| line_idx); |
| |
| if (cmd_pos->data.next_bin < 0) |
| /* If o->set_restore returned -1 we are done */ |
| cmd_pos->done = true; |
| else |
| /* Start from the next bin next time */ |
| cmd_pos->data.next_bin++; |
| } |
| |
| static inline int bnx2x_mcast_handle_pending_cmds_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p) |
| { |
| struct bnx2x_pending_mcast_cmd *cmd_pos, *cmd_pos_n; |
| int cnt = 0; |
| struct bnx2x_mcast_obj *o = p->mcast_obj; |
| |
| list_for_each_entry_safe(cmd_pos, cmd_pos_n, &o->pending_cmds_head, |
| link) { |
| switch (cmd_pos->type) { |
| case BNX2X_MCAST_CMD_ADD: |
| bnx2x_mcast_hdl_pending_add_e2(bp, o, cmd_pos, &cnt); |
| break; |
| |
| case BNX2X_MCAST_CMD_DEL: |
| bnx2x_mcast_hdl_pending_del_e2(bp, o, cmd_pos, &cnt); |
| break; |
| |
| case BNX2X_MCAST_CMD_RESTORE: |
| bnx2x_mcast_hdl_pending_restore_e2(bp, o, cmd_pos, |
| &cnt); |
| break; |
| |
| default: |
| BNX2X_ERR("Unknown command: %d\n", cmd_pos->type); |
| return -EINVAL; |
| } |
| |
| /* If the command has been completed - remove it from the list |
| * and free the memory |
| */ |
| if (cmd_pos->done) { |
| list_del(&cmd_pos->link); |
| kfree(cmd_pos); |
| } |
| |
| /* Break if we reached the maximum number of rules */ |
| if (cnt >= o->max_cmd_len) |
| break; |
| } |
| |
| return cnt; |
| } |
| |
| static inline void bnx2x_mcast_hdl_add(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, struct bnx2x_mcast_ramrod_params *p, |
| int *line_idx) |
| { |
| struct bnx2x_mcast_list_elem *mlist_pos; |
| union bnx2x_mcast_config_data cfg_data = {0}; |
| int cnt = *line_idx; |
| |
| list_for_each_entry(mlist_pos, &p->mcast_list, link) { |
| cfg_data.mac = mlist_pos->mac; |
| o->set_one_rule(bp, o, cnt, &cfg_data, BNX2X_MCAST_CMD_ADD); |
| |
| cnt++; |
| |
| DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC\n", |
| mlist_pos->mac); |
| } |
| |
| *line_idx = cnt; |
| } |
| |
| static inline void bnx2x_mcast_hdl_del(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, struct bnx2x_mcast_ramrod_params *p, |
| int *line_idx) |
| { |
| int cnt = *line_idx, i; |
| |
| for (i = 0; i < p->mcast_list_len; i++) { |
| o->set_one_rule(bp, o, cnt, NULL, BNX2X_MCAST_CMD_DEL); |
| |
| cnt++; |
| |
| DP(BNX2X_MSG_SP, "Deleting MAC. %d left\n", |
| p->mcast_list_len - i - 1); |
| } |
| |
| *line_idx = cnt; |
| } |
| |
| /** |
| * bnx2x_mcast_handle_current_cmd - |
| * |
| * @bp: device handle |
| * @p: |
| * @cmd: |
| * @start_cnt: first line in the ramrod data that may be used |
| * |
| * This function is called iff there is enough place for the current command in |
| * the ramrod data. |
| * Returns number of lines filled in the ramrod data in total. |
| */ |
| static inline int bnx2x_mcast_handle_current_cmd(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, int cmd, |
| int start_cnt) |
| { |
| struct bnx2x_mcast_obj *o = p->mcast_obj; |
| int cnt = start_cnt; |
| |
| DP(BNX2X_MSG_SP, "p->mcast_list_len=%d\n", p->mcast_list_len); |
| |
| switch (cmd) { |
| case BNX2X_MCAST_CMD_ADD: |
| bnx2x_mcast_hdl_add(bp, o, p, &cnt); |
| break; |
| |
| case BNX2X_MCAST_CMD_DEL: |
| bnx2x_mcast_hdl_del(bp, o, p, &cnt); |
| break; |
| |
| case BNX2X_MCAST_CMD_RESTORE: |
| o->hdl_restore(bp, o, 0, &cnt); |
| break; |
| |
| default: |
| BNX2X_ERR("Unknown command: %d\n", cmd); |
| return -EINVAL; |
| } |
| |
| /* The current command has been handled */ |
| p->mcast_list_len = 0; |
| |
| return cnt; |
| } |
| |
| static int bnx2x_mcast_validate_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| int cmd) |
| { |
| struct bnx2x_mcast_obj *o = p->mcast_obj; |
| int reg_sz = o->get_registry_size(o); |
| |
| switch (cmd) { |
| /* DEL command deletes all currently configured MACs */ |
| case BNX2X_MCAST_CMD_DEL: |
| o->set_registry_size(o, 0); |
| /* Don't break */ |
| |
| /* RESTORE command will restore the entire multicast configuration */ |
| case BNX2X_MCAST_CMD_RESTORE: |
| /* Here we set the approximate amount of work to do, which in |
| * fact may be only less as some MACs in postponed ADD |
| * command(s) scheduled before this command may fall into |
| * the same bin and the actual number of bins set in the |
| * registry would be less than we estimated here. See |
| * bnx2x_mcast_set_one_rule_e2() for further details. |
| */ |
| p->mcast_list_len = reg_sz; |
| break; |
| |
| case BNX2X_MCAST_CMD_ADD: |
| case BNX2X_MCAST_CMD_CONT: |
| /* Here we assume that all new MACs will fall into new bins. |
| * However we will correct the real registry size after we |
| * handle all pending commands. |
| */ |
| o->set_registry_size(o, reg_sz + p->mcast_list_len); |
| break; |
| |
| default: |
| BNX2X_ERR("Unknown command: %d\n", cmd); |
| return -EINVAL; |
| |
| } |
| |
| /* Increase the total number of MACs pending to be configured */ |
| o->total_pending_num += p->mcast_list_len; |
| |
| return 0; |
| } |
| |
| static void bnx2x_mcast_revert_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| int old_num_bins) |
| { |
| struct bnx2x_mcast_obj *o = p->mcast_obj; |
| |
| o->set_registry_size(o, old_num_bins); |
| o->total_pending_num -= p->mcast_list_len; |
| } |
| |
| /** |
| * bnx2x_mcast_set_rdata_hdr_e2 - sets a header values |
| * |
| * @bp: device handle |
| * @p: |
| * @len: number of rules to handle |
| */ |
| static inline void bnx2x_mcast_set_rdata_hdr_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| u8 len) |
| { |
| struct bnx2x_raw_obj *r = &p->mcast_obj->raw; |
| struct eth_multicast_rules_ramrod_data *data = |
| (struct eth_multicast_rules_ramrod_data *)(r->rdata); |
| |
| data->header.echo = ((r->cid & BNX2X_SWCID_MASK) | |
| (BNX2X_FILTER_MCAST_PENDING << BNX2X_SWCID_SHIFT)); |
| data->header.rule_cnt = len; |
| } |
| |
| /** |
| * bnx2x_mcast_refresh_registry_e2 - recalculate the actual number of set bins |
| * |
| * @bp: device handle |
| * @o: |
| * |
| * Recalculate the actual number of set bins in the registry using Brian |
| * Kernighan's algorithm: it's execution complexity is as a number of set bins. |
| * |
| * returns 0 for the compliance with bnx2x_mcast_refresh_registry_e1(). |
| */ |
| static inline int bnx2x_mcast_refresh_registry_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o) |
| { |
| int i, cnt = 0; |
| u64 elem; |
| |
| for (i = 0; i < BNX2X_MCAST_VEC_SZ; i++) { |
| elem = o->registry.aprox_match.vec[i]; |
| for (; elem; cnt++) |
| elem &= elem - 1; |
| } |
| |
| o->set_registry_size(o, cnt); |
| |
| return 0; |
| } |
| |
| static int bnx2x_mcast_setup_e2(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| int cmd) |
| { |
| struct bnx2x_raw_obj *raw = &p->mcast_obj->raw; |
| struct bnx2x_mcast_obj *o = p->mcast_obj; |
| struct eth_multicast_rules_ramrod_data *data = |
| (struct eth_multicast_rules_ramrod_data *)(raw->rdata); |
| int cnt = 0, rc; |
| |
| /* Reset the ramrod data buffer */ |
| memset(data, 0, sizeof(*data)); |
| |
| cnt = bnx2x_mcast_handle_pending_cmds_e2(bp, p); |
| |
| /* If there are no more pending commands - clear SCHEDULED state */ |
| if (list_empty(&o->pending_cmds_head)) |
| o->clear_sched(o); |
| |
| /* The below may be true iff there was enough room in ramrod |
| * data for all pending commands and for the current |
| * command. Otherwise the current command would have been added |
| * to the pending commands and p->mcast_list_len would have been |
| * zeroed. |
| */ |
| if (p->mcast_list_len > 0) |
| cnt = bnx2x_mcast_handle_current_cmd(bp, p, cmd, cnt); |
| |
| /* We've pulled out some MACs - update the total number of |
| * outstanding. |
| */ |
| o->total_pending_num -= cnt; |
| |
| /* send a ramrod */ |
| WARN_ON(o->total_pending_num < 0); |
| WARN_ON(cnt > o->max_cmd_len); |
| |
| bnx2x_mcast_set_rdata_hdr_e2(bp, p, (u8)cnt); |
| |
| /* Update a registry size if there are no more pending operations. |
| * |
| * We don't want to change the value of the registry size if there are |
| * pending operations because we want it to always be equal to the |
| * exact or the approximate number (see bnx2x_mcast_validate_e2()) of |
| * set bins after the last requested operation in order to properly |
| * evaluate the size of the next DEL/RESTORE operation. |
| * |
| * Note that we update the registry itself during command(s) handling |
| * - see bnx2x_mcast_set_one_rule_e2(). That's because for 57712 we |
| * aggregate multiple commands (ADD/DEL/RESTORE) into one ramrod but |
| * with a limited amount of update commands (per MAC/bin) and we don't |
| * know in this scope what the actual state of bins configuration is |
| * going to be after this ramrod. |
| */ |
| if (!o->total_pending_num) |
| bnx2x_mcast_refresh_registry_e2(bp, o); |
| |
| /* |
| * If CLEAR_ONLY was requested - don't send a ramrod and clear |
| * RAMROD_PENDING status immediately. |
| */ |
| if (test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) { |
| raw->clear_pending(raw); |
| return 0; |
| } else { |
| /* |
| * No need for an explicit memory barrier here as long we would |
| * need to ensure the ordering of writing to the SPQ element |
| * and updating of the SPQ producer which involves a memory |
| * read and we will have to put a full memory barrier there |
| * (inside bnx2x_sp_post()). |
| */ |
| |
| /* Send a ramrod */ |
| rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_MULTICAST_RULES, |
| raw->cid, U64_HI(raw->rdata_mapping), |
| U64_LO(raw->rdata_mapping), |
| ETH_CONNECTION_TYPE); |
| if (rc) |
| return rc; |
| |
| /* Ramrod completion is pending */ |
| return 1; |
| } |
| } |
| |
| static int bnx2x_mcast_validate_e1h(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| int cmd) |
| { |
| /* Mark, that there is a work to do */ |
| if ((cmd == BNX2X_MCAST_CMD_DEL) || (cmd == BNX2X_MCAST_CMD_RESTORE)) |
| p->mcast_list_len = 1; |
| |
| return 0; |
| } |
| |
| static void bnx2x_mcast_revert_e1h(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| int old_num_bins) |
| { |
| /* Do nothing */ |
| } |
| |
| #define BNX2X_57711_SET_MC_FILTER(filter, bit) \ |
| do { \ |
| (filter)[(bit) >> 5] |= (1 << ((bit) & 0x1f)); \ |
| } while (0) |
| |
| static inline void bnx2x_mcast_hdl_add_e1h(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, |
| struct bnx2x_mcast_ramrod_params *p, |
| u32 *mc_filter) |
| { |
| struct bnx2x_mcast_list_elem *mlist_pos; |
| int bit; |
| |
| list_for_each_entry(mlist_pos, &p->mcast_list, link) { |
| bit = bnx2x_mcast_bin_from_mac(mlist_pos->mac); |
| BNX2X_57711_SET_MC_FILTER(mc_filter, bit); |
| |
| DP(BNX2X_MSG_SP, "About to configure %pM mcast MAC, bin %d\n", |
| mlist_pos->mac, bit); |
| |
| /* bookkeeping... */ |
| BIT_VEC64_SET_BIT(o->registry.aprox_match.vec, |
| bit); |
| } |
| } |
| |
| static inline void bnx2x_mcast_hdl_restore_e1h(struct bnx2x *bp, |
| struct bnx2x_mcast_obj *o, struct bnx2x_mcast_ramrod_params *p, |
| u32 *mc_filter) |
| { |
| int bit; |
| |
| for (bit = bnx2x_mcast_get_next_bin(o, 0); |
| bit >= 0; |
| bit = bnx2x_mcast_get_next_bin(o, bit + 1)) { |
| BNX2X_57711_SET_MC_FILTER(mc_filter, bit); |
| DP(BNX2X_MSG_SP, "About to set bin %d\n", bit); |
| } |
| } |
| |
| /* On 57711 we write the multicast MACs' aproximate match |
| * table by directly into the TSTORM's internal RAM. So we don't |
| * really need to handle any tricks to make it work. |
| */ |
| static int bnx2x_mcast_setup_e1h(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| int cmd) |
| { |
| int i; |
| struct bnx2x_mcast_obj *o = p->mcast_obj; |
| struct bnx2x_raw_obj *r = &o->raw; |
| |
| /* If CLEAR_ONLY has been requested - clear the registry |
| * and clear a pending bit. |
| */ |
| if (!test_bit(RAMROD_DRV_CLR_ONLY, &p->ramrod_flags)) { |
| u32 mc_filter[MC_HASH_SIZE] = {0}; |
| |
| /* Set the multicast filter bits before writing it into |
| * the internal memory. |
| */ |
| switch (cmd) { |
| case BNX2X_MCAST_CMD_ADD: |
| bnx2x_mcast_hdl_add_e1h(bp, o, p, mc_filter); |
| break; |
| |
| case BNX2X_MCAST_CMD_DEL: |
| DP(BNX2X_MSG_SP, |
| "Invalidating multicast MACs configuration\n"); |
| |
| /* clear the registry */ |
| memset(o->registry.aprox_match.vec, 0, |
| sizeof(o->registry.aprox_match.vec)); |
| break; |
| |
| case BNX2X_MCAST_CMD_RESTORE: |
| bnx2x_mcast_hdl_restore_e1h(bp, o, p, mc_filter); |
| break; |
| |
| default: |
| BNX2X_ERR("Unknown command: %d\n", cmd); |
| return -EINVAL; |
| } |
| |
| /* Set the mcast filter in the internal memory */ |
| for (i = 0; i < MC_HASH_SIZE; i++) |
| REG_WR(bp, MC_HASH_OFFSET(bp, i), mc_filter[i]); |
| } else |
| /* clear the registry */ |
| memset(o->registry.aprox_match.vec, 0, |
| sizeof(o->registry.aprox_match.vec)); |
| |
| /* We are done */ |
| r->clear_pending(r); |
| |
| return 0; |
| } |
| |
| static int bnx2x_mcast_validate_e1(struct bnx2x *bp, |
| struct bnx2x_mcast_ramrod_params *p, |
| int cmd) |
| { |
| struct bnx2x_mcast_obj *o = p->mcast_obj; |
| int reg_sz = o->get_registry_size(o); |
| |
| switch (cmd) { |
| /* DEL command deletes all currently configured MACs */ |
| case BNX2X_MCAST_CMD_DEL: |
| o->set_registry_size(o, 0); |
| /* Don't break */ |
| |
| /* RESTORE command will restore the entire multicast configuration */ |
| case BNX2X_MCAST_CMD_RESTORE: |
| p->mcast_list_len = reg_sz; |
| DP(BNX2X_MSG_SP, "Command %d, p->mcast_list_len=%d\n", |
| cmd, p->mcast_list_len); |
| break; |
| |
| case BNX2X_MCAST_CMD_ADD: |
| case BNX2X_MCAST_CMD_CONT: |
| /* Multicast MACs on 57710 are configured as unicast MACs and |
| * there is only a limited number of CAM entries for that |
| * matter. |
| */ |
| if (p->mcast_list_len > o->max_cmd_len) { |
| BNX2X_ERR("Can't configure more than %d multicast MACs" |
| "on 57710\n", o->max_cmd_l
|