From: Rajesh Borundia Date: Fri, 19 Apr 2013 07:01:12 +0000 (+0000) Subject: qlcnic: Support VLAN id config. X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=91b7282b613d4da65e8b4c87d521156cdc64c169;p=openwrt%2Fstaging%2Fblogic.git qlcnic: Support VLAN id config. o Add support for VLAN id configuration per VF using iproute2 tool. o VLAN id's 1-4094 are treated as PVID by the PF and Guest VLAN tagging is not allowed by default. o PVID is disabled when the VLAN id is set to 0 o Guest VLAN tagging is allowed when the VLAN id is set to 4095. o Only one Guest VLAN id is supported. o VLAN id can be changed only when the VF driver is not loaded. Signed-off-by: Manish Chopra Signed-off-by: Sucheta Chakraborty Signed-off-by: Rajesh Borundia Signed-off-by: David S. Miller --- diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 2b13bd0495ea..7dc7e02d00aa 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -979,7 +979,8 @@ struct qlcnic_adapter { u8 reset_ctx_cnt; u16 is_up; - u16 pvid; + u16 rx_pvid; + u16 tx_pvid; u32 irq; u32 heartbeat; @@ -1445,10 +1446,10 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, struct qlcnic_host_rds_ring *rds_ring, u8 ring_id); int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max); void qlcnic_set_multi(struct net_device *netdev); -void __qlcnic_set_multi(struct net_device *netdev); -int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *); +void __qlcnic_set_multi(struct net_device *, u16); +int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *, u16); int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *); -void qlcnic_free_mac_list(struct qlcnic_adapter *adapter); +void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter); int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu); int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *); @@ -1530,7 +1531,7 @@ int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); int qlcnic_read_mac_addr(struct qlcnic_adapter *); int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int); void qlcnic_sriov_vf_schedule_multi(struct net_device *); -void qlcnic_vf_add_mc_list(struct net_device *); +void qlcnic_vf_add_mc_list(struct net_device *, u16); /* * QLOGIC Board information @@ -1606,6 +1607,7 @@ struct qlcnic_hardware_ops { int (*config_promisc_mode) (struct qlcnic_adapter *, u32); void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16); int (*get_board_info) (struct qlcnic_adapter *); + void (*free_mac_list) (struct qlcnic_adapter *); }; extern struct qlcnic_nic_template qlcnic_vf_ops; @@ -1815,6 +1817,11 @@ static inline int qlcnic_get_board_info(struct qlcnic_adapter *adapter) return adapter->ahw->hw_ops->get_board_info(adapter); } +static inline void qlcnic_free_mac_list(struct qlcnic_adapter *adapter) +{ + return adapter->ahw->hw_ops->free_mac_list(adapter); +} + static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter, u32 key) { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 4035e82167a8..3a4b5723b94e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -172,6 +172,7 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = { .config_promisc_mode = qlcnic_83xx_nic_set_promisc, .change_l2_filter = qlcnic_83xx_change_l2_filter, .get_board_info = qlcnic_83xx_get_port_info, + .free_mac_list = qlcnic_82xx_free_mac_list, }; static struct qlcnic_nic_template qlcnic_83xx_ops = { @@ -1797,6 +1798,10 @@ int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr, if (err) return err; + if (vlan_id) + op = (op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ? + QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL; + cmd.req.arg[1] = op | (1 << 8); qlcnic_83xx_set_interface_id_macaddr(adapter, &temp); cmd.req.arg[1] |= temp; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index c3cbfaea97c3..6a6512ba9f38 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -468,7 +468,7 @@ int qlcnic_nic_del_mac(struct qlcnic_adapter *adapter, const u8 *addr) return err; } -int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr) +int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan) { struct list_head *head; struct qlcnic_mac_list_s *cur; @@ -487,7 +487,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr) memcpy(cur->mac_addr, addr, ETH_ALEN); if (qlcnic_sre_macaddr_change(adapter, - cur->mac_addr, 0, QLCNIC_MAC_ADD)) { + cur->mac_addr, vlan, QLCNIC_MAC_ADD)) { kfree(cur); return -EIO; } @@ -496,7 +496,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr) return 0; } -void __qlcnic_set_multi(struct net_device *netdev) +void __qlcnic_set_multi(struct net_device *netdev, u16 vlan) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct netdev_hw_addr *ha; @@ -509,8 +509,8 @@ void __qlcnic_set_multi(struct net_device *netdev) return; if (!qlcnic_sriov_vf_check(adapter)) - qlcnic_nic_add_mac(adapter, adapter->mac_addr); - qlcnic_nic_add_mac(adapter, bcast_addr); + qlcnic_nic_add_mac(adapter, adapter->mac_addr, vlan); + qlcnic_nic_add_mac(adapter, bcast_addr, vlan); if (netdev->flags & IFF_PROMISC) { if (!(adapter->flags & QLCNIC_PROMISC_DISABLED)) @@ -526,12 +526,12 @@ void __qlcnic_set_multi(struct net_device *netdev) if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) { netdev_for_each_mc_addr(ha, netdev) { - qlcnic_nic_add_mac(adapter, ha->addr); + qlcnic_nic_add_mac(adapter, ha->addr, vlan); } } if (qlcnic_sriov_vf_check(adapter)) - qlcnic_vf_add_mc_list(netdev); + qlcnic_vf_add_mc_list(netdev, vlan); send_fw_cmd: if (!qlcnic_sriov_vf_check(adapter)) { @@ -570,7 +570,7 @@ void qlcnic_set_multi(struct net_device *netdev) qlcnic_sriov_vf_schedule_multi(adapter->netdev); return; } - __qlcnic_set_multi(netdev); + __qlcnic_set_multi(netdev, 0); } int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) @@ -592,7 +592,7 @@ int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) (struct cmd_desc_type0 *)&req, 1); } -void qlcnic_free_mac_list(struct qlcnic_adapter *adapter) +void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter) { struct qlcnic_mac_list_s *cur; struct list_head *head = &adapter->mac_list; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index f3fe31e8f6bd..356859b9f21c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -344,14 +344,14 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter, flags = FLAGS_VLAN_OOB; vlan_tci = vlan_tx_tag_get(skb); } - if (unlikely(adapter->pvid)) { + if (unlikely(adapter->tx_pvid)) { if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED)) return -EIO; if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED)) goto set_flags; flags = FLAGS_VLAN_OOB; - vlan_tci = adapter->pvid; + vlan_tci = adapter->tx_pvid; } set_flags: qlcnic_set_tx_vlan_tci(first_desc, vlan_tci); @@ -980,10 +980,10 @@ static inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter, memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2); skb_pull(skb, VLAN_HLEN); } - if (!adapter->pvid) + if (!adapter->rx_pvid) return 0; - if (*vlan_tag == adapter->pvid) { + if (*vlan_tag == adapter->rx_pvid) { /* Outer vlan tag. Packet should follow non-vlan path */ *vlan_tag = 0xffff; return 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index b3ab7a3386e3..9890aa8abdcd 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -290,7 +290,7 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return err; if (is_unicast_ether_addr(addr)) - err = qlcnic_nic_add_mac(adapter, addr); + err = qlcnic_nic_add_mac(adapter, addr, 0); else if (is_multicast_ether_addr(addr)) err = dev_mc_add_excl(netdev, addr); else @@ -345,6 +345,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { .ndo_set_vf_mac = qlcnic_sriov_set_vf_mac, .ndo_set_vf_tx_rate = qlcnic_sriov_set_vf_tx_rate, .ndo_get_vf_config = qlcnic_sriov_get_vf_config, + .ndo_set_vf_vlan = qlcnic_sriov_set_vf_vlan, #endif }; @@ -404,6 +405,7 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = { .config_promisc_mode = qlcnic_82xx_nic_set_promisc, .change_l2_filter = qlcnic_82xx_change_filter, .get_board_info = qlcnic_82xx_get_board_info, + .free_mac_list = qlcnic_82xx_free_mac_list, }; int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix) @@ -900,16 +902,31 @@ void qlcnic_set_vlan_config(struct qlcnic_adapter *adapter, else adapter->flags |= QLCNIC_TAGGING_ENABLED; - if (esw_cfg->vlan_id) - adapter->pvid = esw_cfg->vlan_id; - else - adapter->pvid = 0; + if (esw_cfg->vlan_id) { + adapter->rx_pvid = esw_cfg->vlan_id; + adapter->tx_pvid = esw_cfg->vlan_id; + } else { + adapter->rx_pvid = 0; + adapter->tx_pvid = 0; + } } static int qlcnic_vlan_rx_add(struct net_device *netdev, __be16 proto, u16 vid) { struct qlcnic_adapter *adapter = netdev_priv(netdev); + int err; + + if (qlcnic_sriov_vf_check(adapter)) { + err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 1); + if (err) { + netdev_err(netdev, + "Cannot add VLAN filter for VLAN id %d, err=%d", + vid, err); + return err; + } + } + set_bit(vid, adapter->vlans); return 0; } @@ -918,6 +935,17 @@ static int qlcnic_vlan_rx_del(struct net_device *netdev, __be16 proto, u16 vid) { struct qlcnic_adapter *adapter = netdev_priv(netdev); + int err; + + if (qlcnic_sriov_vf_check(adapter)) { + err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 0); + if (err) { + netdev_err(netdev, + "Cannot delete VLAN filter for VLAN id %d, err=%d", + vid, err); + return err; + } + } qlcnic_restore_indev_addr(netdev, NETDEV_DOWN); clear_bit(vid, adapter->vlans); @@ -1736,6 +1764,9 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, if (qlcnic_vlan_tx_check(adapter)) netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX); + if (qlcnic_sriov_vf_check(adapter)) + netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO) netdev->features |= NETIF_F_LRO; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index 5f73d42d6689..d85fbb57c25b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h @@ -48,6 +48,8 @@ struct qlcnic_bc_hdr { enum qlcnic_bc_commands { QLCNIC_BC_CMD_CHANNEL_INIT = 0x0, QLCNIC_BC_CMD_CHANNEL_TERM = 0x1, + QLCNIC_BC_CMD_GET_ACL = 0x2, + QLCNIC_BC_CMD_CFG_GUEST_VLAN = 0x3, }; #define QLC_BC_CMD 1 @@ -95,6 +97,12 @@ enum qlcnic_vf_state { QLC_BC_VF_SOFT_FLR, }; +enum qlcnic_vlan_mode { + QLC_NO_VLAN_MODE = 0, + QLC_PVID_MODE, + QLC_GUEST_VLAN_MODE, +}; + struct qlcnic_resources { u16 num_tx_mac_filters; u16 num_rx_ucast_mac_filters; @@ -118,6 +126,9 @@ struct qlcnic_vport { u16 handle; u16 max_tx_bw; u16 min_tx_bw; + u8 vlan_mode; + u16 vlan; + u8 qos; u8 mac[6]; }; @@ -156,6 +167,11 @@ struct qlcnic_back_channel { struct qlcnic_sriov { u16 vp_handle; u8 num_vfs; + u8 any_vlan; + u8 vlan_mode; + u16 num_allowed_vlans; + u16 *allowed_vlans; + u16 vlan; struct qlcnic_resources ff_max; struct qlcnic_back_channel bc; struct qlcnic_vf_info *vf_info; @@ -177,6 +193,7 @@ int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *, struct qlcnic_bc_trans *); int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *, struct qlcnic_info *, u16); +int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *, u16, u8); static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) { @@ -207,6 +224,7 @@ int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *); int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int); int qlcnic_sriov_get_vf_config(struct net_device *, int , struct ifla_vf_info *); +int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8); #else static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index b00cba96a82a..d5b626dad16b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -28,6 +28,8 @@ #define QLC_83XX_VF_RESET_FAIL_THRESH 8 #define QLC_BC_CMD_MAX_RETRY_CNT 5 +static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *); +static int qlcnic_sriov_alloc_bc_mbx_args(struct qlcnic_cmd_args *, u32); static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *); static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *); static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *); @@ -64,6 +66,7 @@ static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { .config_promisc_mode = qlcnic_83xx_nic_set_promisc, .change_l2_filter = qlcnic_83xx_change_l2_filter, .get_board_info = qlcnic_83xx_get_port_info, + .free_mac_list = qlcnic_sriov_vf_free_mac_list, }; static struct qlcnic_nic_template qlcnic_sriov_vf_ops = { @@ -79,6 +82,8 @@ static struct qlcnic_nic_template qlcnic_sriov_vf_ops = { static const struct qlcnic_mailbox_metadata qlcnic_sriov_bc_mbx_tbl[] = { {QLCNIC_BC_CMD_CHANNEL_INIT, 2, 2}, {QLCNIC_BC_CMD_CHANNEL_TERM, 2, 2}, + {QLCNIC_BC_CMD_GET_ACL, 3, 14}, + {QLCNIC_BC_CMD_CFG_GUEST_VLAN, 2, 2}, }; static inline bool qlcnic_sriov_bc_msg_check(u32 val) @@ -446,6 +451,71 @@ int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *adapter, return err; } +static int qlcnic_sriov_set_pvid_mode(struct qlcnic_adapter *adapter, + struct qlcnic_cmd_args *cmd) +{ + adapter->rx_pvid = (cmd->rsp.arg[1] >> 16) & 0xffff; + adapter->flags &= ~QLCNIC_TAGGING_ENABLED; + return 0; +} + +static int qlcnic_sriov_set_guest_vlan_mode(struct qlcnic_adapter *adapter, + struct qlcnic_cmd_args *cmd) +{ + struct qlcnic_sriov *sriov = adapter->ahw->sriov; + int i, num_vlans; + u16 *vlans; + + if (sriov->allowed_vlans) + return 0; + + sriov->any_vlan = cmd->rsp.arg[2] & 0xf; + if (!sriov->any_vlan) + return 0; + + sriov->num_allowed_vlans = cmd->rsp.arg[2] >> 16; + num_vlans = sriov->num_allowed_vlans; + sriov->allowed_vlans = kzalloc(sizeof(u16) * num_vlans, GFP_KERNEL); + if (!sriov->allowed_vlans) + return -ENOMEM; + + vlans = (u16 *)&cmd->rsp.arg[3]; + for (i = 0; i < num_vlans; i++) + sriov->allowed_vlans[i] = vlans[i]; + + return 0; +} + +static int qlcnic_sriov_get_vf_acl(struct qlcnic_adapter *adapter) +{ + struct qlcnic_sriov *sriov = adapter->ahw->sriov; + struct qlcnic_cmd_args cmd; + int ret; + + ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd, QLCNIC_BC_CMD_GET_ACL); + if (ret) + return ret; + + ret = qlcnic_issue_cmd(adapter, &cmd); + if (ret) { + dev_err(&adapter->pdev->dev, "Failed to get ACL, err=%d\n", + ret); + } else { + sriov->vlan_mode = cmd.rsp.arg[1] & 0x3; + switch (sriov->vlan_mode) { + case QLC_GUEST_VLAN_MODE: + ret = qlcnic_sriov_set_guest_vlan_mode(adapter, &cmd); + break; + case QLC_PVID_MODE: + ret = qlcnic_sriov_set_pvid_mode(adapter, &cmd); + break; + } + } + + qlcnic_free_mbx_args(&cmd); + return ret; +} + static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter) { struct qlcnic_info nic_info; @@ -460,6 +530,10 @@ static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter) if (err) return -EIO; + err = qlcnic_sriov_get_vf_acl(adapter); + if (err) + return err; + if (qlcnic_83xx_get_port_info(adapter)) return -EIO; @@ -1411,7 +1485,7 @@ out: return ret; } -void qlcnic_vf_add_mc_list(struct net_device *netdev) +void qlcnic_vf_add_mc_list(struct net_device *netdev, u16 vlan) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_mac_list_s *cur; @@ -1431,7 +1505,7 @@ void qlcnic_vf_add_mc_list(struct net_device *netdev) while (!list_empty(&tmp_list)) { cur = list_entry((&tmp_list)->next, struct qlcnic_mac_list_s, list); - qlcnic_nic_add_mac(adapter, cur->mac_addr); + qlcnic_nic_add_mac(adapter, cur->mac_addr, vlan); list_del(&cur->list); kfree(cur); } @@ -1454,11 +1528,13 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc) static void qlcnic_sriov_vf_set_multi(struct net_device *netdev) { struct qlcnic_adapter *adapter = netdev_priv(netdev); + u16 vlan; if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) return; - __qlcnic_set_multi(netdev); + vlan = adapter->ahw->sriov->vlan; + __qlcnic_set_multi(netdev, vlan); } static void qlcnic_sriov_handle_async_multi(struct work_struct *work) @@ -1787,3 +1863,92 @@ static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *adapter) clear_bit(__QLCNIC_RESETTING, &adapter->state); cancel_delayed_work_sync(&adapter->fw_work); } + +static int qlcnic_sriov_validate_vlan_cfg(struct qlcnic_sriov *sriov, + u16 vid, u8 enable) +{ + u16 vlan = sriov->vlan; + u8 allowed = 0; + int i; + + if (sriov->vlan_mode != QLC_GUEST_VLAN_MODE) + return -EINVAL; + + if (enable) { + if (vlan) + return -EINVAL; + + if (sriov->any_vlan) { + for (i = 0; i < sriov->num_allowed_vlans; i++) { + if (sriov->allowed_vlans[i] == vid) + allowed = 1; + } + + if (!allowed) + return -EINVAL; + } + } else { + if (!vlan || vlan != vid) + return -EINVAL; + } + + return 0; +} + +int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter, + u16 vid, u8 enable) +{ + struct qlcnic_sriov *sriov = adapter->ahw->sriov; + struct qlcnic_cmd_args cmd; + int ret; + + if (vid == 0) + return 0; + + ret = qlcnic_sriov_validate_vlan_cfg(sriov, vid, enable); + if (ret) + return ret; + + ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd, + QLCNIC_BC_CMD_CFG_GUEST_VLAN); + if (ret) + return ret; + + cmd.req.arg[1] = (enable & 1) | vid << 16; + + qlcnic_sriov_cleanup_async_list(&sriov->bc); + ret = qlcnic_issue_cmd(adapter, &cmd); + if (ret) { + dev_err(&adapter->pdev->dev, + "Failed to configure guest VLAN, err=%d\n", ret); + } else { + qlcnic_free_mac_list(adapter); + + if (enable) + sriov->vlan = vid; + else + sriov->vlan = 0; + + qlcnic_sriov_vf_set_multi(adapter->netdev); + } + + qlcnic_free_mbx_args(&cmd); + return ret; +} + +static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *adapter) +{ + struct list_head *head = &adapter->mac_list; + struct qlcnic_mac_list_s *cur; + u16 vlan; + + vlan = adapter->ahw->sriov->vlan; + + while (!list_empty(head)) { + cur = list_entry(head->next, struct qlcnic_mac_list_s, list); + qlcnic_sre_macaddr_change(adapter, cur->mac_addr, + vlan, QLCNIC_MAC_DEL); + list_del(&cur->list); + kfree(cur); + } +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index 59d385b7ccd7..c81be2da119b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -286,6 +286,29 @@ out: return ret; } +static int qlcnic_sriov_pf_cfg_vlan_filtering(struct qlcnic_adapter *adapter, + u8 enable) +{ + struct qlcnic_cmd_args cmd; + int err; + + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); + if (err) + return err; + + cmd.req.arg[1] = 0x4; + if (enable) + cmd.req.arg[1] |= BIT_16; + + err = qlcnic_issue_cmd(adapter, &cmd); + if (err) + dev_err(&adapter->pdev->dev, + "Failed to configure VLAN filtering, err=%d\n", err); + + qlcnic_free_mbx_args(&cmd); + return err; +} + static int qlcnic_sriov_pf_cfg_eswitch(struct qlcnic_adapter *adapter, u8 func, u8 enable) { @@ -350,6 +373,7 @@ void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) qlcnic_sriov_cfg_bc_intr(adapter, 0); qlcnic_sriov_pf_config_vport(adapter, 0, func); qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); + qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 0); __qlcnic_sriov_cleanup(adapter); adapter->ahw->op_mode = QLCNIC_MGMT_FUNC; clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); @@ -402,10 +426,14 @@ static int qlcnic_sriov_pf_init(struct qlcnic_adapter *adapter) if (!qlcnic_sriov_enable_check(adapter)) return 0; - err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1); + err = qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 1); if (err) return err; + err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1); + if (err) + goto disable_vlan_filtering; + err = qlcnic_sriov_pf_config_vport(adapter, 1, func); if (err) goto disable_eswitch; @@ -439,6 +467,9 @@ delete_vport: disable_eswitch: qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); +disable_vlan_filtering: + qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 0); + return err; } @@ -544,6 +575,36 @@ int qlcnic_pci_sriov_configure(struct pci_dev *dev, int num_vfs) return err; } +static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func) +{ + struct qlcnic_cmd_args cmd; + struct qlcnic_vport *vp; + int err, id; + + id = qlcnic_sriov_func_to_index(adapter, func); + if (id < 0) + return id; + + vp = adapter->ahw->sriov->vf_info[id].vp; + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); + if (err) + return err; + + cmd.req.arg[1] = 0x3 | func << 16; + if (vp->vlan_mode == QLC_PVID_MODE) { + cmd.req.arg[2] |= BIT_6; + cmd.req.arg[3] |= vp->vlan << 8; + } + + err = qlcnic_issue_cmd(adapter, &cmd); + if (err) + dev_err(&adapter->pdev->dev, "Failed to set ACL, err=%d\n", + err); + + qlcnic_free_mbx_args(&cmd); + return err; +} + static int qlcnic_sriov_set_vf_vport_info(struct qlcnic_adapter *adapter, u16 func) { @@ -554,6 +615,10 @@ static int qlcnic_sriov_set_vf_vport_info(struct qlcnic_adapter *adapter, if (err) return -EIO; + err = qlcnic_sriov_set_vf_acl(adapter, func); + if (err) + return err; + return 0; } @@ -661,6 +726,7 @@ static int qlcnic_sriov_pf_create_rx_ctx_cmd(struct qlcnic_bc_trans *tran, struct qlcnic_adapter *adapter = vf->adapter; struct qlcnic_rcv_mbx_out *mbx_out; int err; + u16 vlan; err = qlcnic_sriov_validate_create_rx_ctx(cmd); if (err) { @@ -671,11 +737,12 @@ static int qlcnic_sriov_pf_create_rx_ctx_cmd(struct qlcnic_bc_trans *tran, cmd->req.arg[6] = vf->vp->handle; err = qlcnic_issue_cmd(adapter, cmd); + vlan = vf->vp->vlan; if (!err) { mbx_out = (struct qlcnic_rcv_mbx_out *)&cmd->rsp.arg[1]; vf->rx_ctx_id = mbx_out->ctx_id; qlcnic_sriov_cfg_vf_def_mac(adapter, vf->vp, vf->pci_func, - 0, QLCNIC_MAC_ADD); + vlan, QLCNIC_MAC_ADD); } else { vf->rx_ctx_id = 0; } @@ -759,6 +826,7 @@ static int qlcnic_sriov_pf_del_rx_ctx_cmd(struct qlcnic_bc_trans *trans, struct qlcnic_vf_info *vf = trans->vf; struct qlcnic_adapter *adapter = vf->adapter; int err; + u16 vlan; err = qlcnic_sriov_validate_del_rx_ctx(vf, cmd); if (err) { @@ -766,8 +834,9 @@ static int qlcnic_sriov_pf_del_rx_ctx_cmd(struct qlcnic_bc_trans *trans, return err; } + vlan = vf->vp->vlan; qlcnic_sriov_cfg_vf_def_mac(adapter, vf->vp, vf->pci_func, - 0, QLCNIC_MAC_DEL); + vlan, QLCNIC_MAC_DEL); cmd->req.arg[1] |= vf->vp->handle << 16; err = qlcnic_issue_cmd(adapter, cmd); @@ -1012,6 +1081,8 @@ static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *cmd) { struct qlcnic_macvlan_mbx *macvlan; + struct qlcnic_vport *vp = vf->vp; + u8 op, new_op; if (!(cmd->req.arg[1] & BIT_8)) return -EINVAL; @@ -1027,6 +1098,15 @@ static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter, return -EINVAL; } + if (vp->vlan_mode == QLC_PVID_MODE) { + op = cmd->req.arg[1] & 0x7; + cmd->req.arg[1] &= ~0x7; + new_op = (op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ? + QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL; + cmd->req.arg[3] |= vp->vlan << 16; + cmd->req.arg[1] |= new_op; + } + return 0; } @@ -1089,6 +1169,109 @@ static int qlcnic_sriov_pf_cfg_promisc_cmd(struct qlcnic_bc_trans *trans, return err; } +static int qlcnic_sriov_pf_get_acl_cmd(struct qlcnic_bc_trans *trans, + struct qlcnic_cmd_args *cmd) +{ + struct qlcnic_vf_info *vf = trans->vf; + struct qlcnic_vport *vp = vf->vp; + u8 cmd_op, mode = vp->vlan_mode; + + cmd_op = trans->req_hdr->cmd_op; + cmd->rsp.arg[0] = (cmd_op & 0xffff) | 14 << 16 | 1 << 25; + + switch (mode) { + case QLC_GUEST_VLAN_MODE: + cmd->rsp.arg[1] = mode | 1 << 8; + cmd->rsp.arg[2] = 1 << 16; + break; + case QLC_PVID_MODE: + cmd->rsp.arg[1] = mode | 1 << 8 | vp->vlan << 16; + break; + } + + return 0; +} + +static int qlcnic_sriov_pf_del_guest_vlan(struct qlcnic_adapter *adapter, + struct qlcnic_vf_info *vf) + +{ + struct qlcnic_vport *vp = vf->vp; + + if (!vp->vlan) + return -EINVAL; + + if (!vf->rx_ctx_id) { + vp->vlan = 0; + return 0; + } + + qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, + vp->vlan, QLCNIC_MAC_DEL); + vp->vlan = 0; + qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, + 0, QLCNIC_MAC_ADD); + return 0; +} + +static int qlcnic_sriov_pf_add_guest_vlan(struct qlcnic_adapter *adapter, + struct qlcnic_vf_info *vf, + struct qlcnic_cmd_args *cmd) +{ + struct qlcnic_vport *vp = vf->vp; + int err = -EIO; + + if (vp->vlan) + return err; + + if (!vf->rx_ctx_id) { + vp->vlan = cmd->req.arg[1] >> 16; + return 0; + } + + err = qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, + 0, QLCNIC_MAC_DEL); + if (err) + return err; + + vp->vlan = cmd->req.arg[1] >> 16; + err = qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, + vp->vlan, QLCNIC_MAC_ADD); + + if (err) { + qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, + 0, QLCNIC_MAC_ADD); + vp->vlan = 0; + } + + return err; +} + +static int qlcnic_sriov_pf_cfg_guest_vlan_cmd(struct qlcnic_bc_trans *tran, + struct qlcnic_cmd_args *cmd) +{ + struct qlcnic_vf_info *vf = tran->vf; + struct qlcnic_adapter *adapter = vf->adapter; + struct qlcnic_vport *vp = vf->vp; + int err = -EIO; + u8 op; + + if (vp->vlan_mode != QLC_GUEST_VLAN_MODE) { + cmd->rsp.arg[0] |= 2 << 25; + return err; + } + + op = cmd->req.arg[1] & 0xf; + + if (op) + err = qlcnic_sriov_pf_add_guest_vlan(adapter, vf, cmd); + else + err = qlcnic_sriov_pf_del_guest_vlan(adapter, vf); + + cmd->rsp.arg[0] |= err ? 2 << 25 : 1 << 25; + return err; +} + static const int qlcnic_pf_passthru_supp_cmds[] = { QLCNIC_CMD_GET_STATISTICS, QLCNIC_CMD_GET_PORT_CONFIG, @@ -1098,6 +1281,8 @@ static const int qlcnic_pf_passthru_supp_cmds[] = { static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = { [QLCNIC_BC_CMD_CHANNEL_INIT] = {&qlcnic_sriov_pf_channel_cfg_cmd}, [QLCNIC_BC_CMD_CHANNEL_TERM] = {&qlcnic_sriov_pf_channel_cfg_cmd}, + [QLCNIC_BC_CMD_GET_ACL] = {&qlcnic_sriov_pf_get_acl_cmd}, + [QLCNIC_BC_CMD_CFG_GUEST_VLAN] = {&qlcnic_sriov_pf_cfg_guest_vlan_cmd}, }; static const struct qlcnic_sriov_fw_cmd_handler qlcnic_pf_fw_cmd_hdlr[] = { @@ -1518,6 +1703,56 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate) return 0; } +int qlcnic_sriov_set_vf_vlan(struct net_device *netdev, int vf, + u16 vlan, u8 qos) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_sriov *sriov = adapter->ahw->sriov; + struct qlcnic_vf_info *vf_info; + struct qlcnic_vport *vp; + + if (!qlcnic_sriov_pf_check(adapter)) + return -EOPNOTSUPP; + + if (vf >= sriov->num_vfs || qos > 7) + return -EINVAL; + + if (vlan > MAX_VLAN_ID) { + netdev_err(netdev, + "Invalid VLAN ID, allowed range is [0 - %d]\n", + MAX_VLAN_ID); + return -EINVAL; + } + + vf_info = &sriov->vf_info[vf]; + vp = vf_info->vp; + if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { + netdev_err(netdev, + "VLAN change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n", + vf); + return -EOPNOTSUPP; + } + + switch (vlan) { + case 4095: + vp->vlan_mode = QLC_GUEST_VLAN_MODE; + break; + case 0: + vp->vlan_mode = QLC_NO_VLAN_MODE; + vp->vlan = 0; + vp->qos = 0; + break; + default: + vp->vlan_mode = QLC_PVID_MODE; + vp->vlan = vlan; + vp->qos = qos; + } + + netdev_info(netdev, "Setting VLAN %d, QoS %d, for VF %d\n", + vlan, qos, vf); + return 0; +} + int qlcnic_sriov_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivi) { @@ -1533,6 +1768,8 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev, vp = sriov->vf_info[vf].vp; memcpy(&ivi->mac, vp->mac, ETH_ALEN); + ivi->vlan = vp->vlan; + ivi->qos = vp->qos; if (vp->max_tx_bw == MAX_BW) ivi->tx_rate = 0; else