Commit 9215310c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net

Pull networking fixes from David Miller:

 1) Various netfilter fixlets from Pablo and the netfilter team.

 2) Fix regression in IPVS caused by lack of PMTU exceptions on local
    routes in ipv6, from Julian Anastasov.

 3) Check pskb_trim_rcsum for failure in DSA, from Zhouyang Jia.

 4) Don't crash on poll in TLS, from Daniel Borkmann.

 5) Revert SO_REUSE{ADDR,PORT} change, it regresses various things
    including Avahi mDNS. From Bart Van Assche.

 6) Missing of_node_put in qcom/emac driver, from Yue Haibing.

 7) We lack checking of the TCP checking in one special case during SYN
    receive, from Frank van der Linden.

 8) Fix module init error paths of mac80211 hwsim, from Johannes Berg.

 9) Handle 802.1ad properly in stmmac driver, from Elad Nachman.

10) Must grab HW caps before doing quirk checks in stmmac driver, from
    Jose Abreu.

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (81 commits)
  net: stmmac: Run HWIF Quirks after getting HW caps
  neighbour: skip NTF_EXT_LEARNED entries during forced gc
  net: cxgb3: add error handling for sysfs_create_group
  tls: fix waitall behavior in tls_sw_recvmsg
  tls: fix use-after-free in tls_push_record
  l2tp: filter out non-PPP sessions in pppol2tp_tunnel_ioctl()
  l2tp: reject creation of non-PPP sessions on L2TPv2 tunnels
  mlxsw: spectrum_switchdev: Fix port_vlan refcounting
  mlxsw: spectrum_router: Align with new route replace logic
  mlxsw: spectrum_router: Allow appending to dev-only routes
  ipv6: Only emit append events for appended routes
  stmmac: added support for 802.1ad vlan stripping
  cfg80211: fix rcu in cfg80211_unregister_wdev
  mac80211: Move up init of TXQs
  mac80211_hwsim: fix module init error paths
  cfg80211: initialize sinfo in cfg80211_get_station
  nl80211: fix some kernel doc tag mistakes
  hv_netvsc: Fix the variable sizes in ipsecv2 and rsc offload
  rds: avoid unenecessary cong_update in loop transport
  l2tp: clean up stale tunnel or session in pppol2tp_connect's error path
  ...
parents de7f01c2 7cfde0af
...@@ -325,6 +325,8 @@ struct nicvf { ...@@ -325,6 +325,8 @@ struct nicvf {
struct tasklet_struct qs_err_task; struct tasklet_struct qs_err_task;
struct work_struct reset_task; struct work_struct reset_task;
struct nicvf_work rx_mode_work; struct nicvf_work rx_mode_work;
/* spinlock to protect workqueue arguments from concurrent access */
spinlock_t rx_mode_wq_lock;
/* PTP timestamp */ /* PTP timestamp */
struct cavium_ptp *ptp_clock; struct cavium_ptp *ptp_clock;
......
...@@ -1923,17 +1923,12 @@ static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd) ...@@ -1923,17 +1923,12 @@ static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
} }
} }
static void nicvf_set_rx_mode_task(struct work_struct *work_arg) static void __nicvf_set_rx_mode_task(u8 mode, struct xcast_addr_list *mc_addrs,
struct nicvf *nic)
{ {
struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
work.work);
struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
union nic_mbx mbx = {}; union nic_mbx mbx = {};
int idx; int idx;
if (!vf_work)
return;
/* From the inside of VM code flow we have only 128 bits memory /* From the inside of VM code flow we have only 128 bits memory
* available to send message to host's PF, so send all mc addrs * available to send message to host's PF, so send all mc addrs
* one by one, starting from flush command in case if kernel * one by one, starting from flush command in case if kernel
...@@ -1944,7 +1939,7 @@ static void nicvf_set_rx_mode_task(struct work_struct *work_arg) ...@@ -1944,7 +1939,7 @@ static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST; mbx.xcast.msg = NIC_MBOX_MSG_RESET_XCAST;
nicvf_send_msg_to_pf(nic, &mbx); nicvf_send_msg_to_pf(nic, &mbx);
if (vf_work->mode & BGX_XCAST_MCAST_FILTER) { if (mode & BGX_XCAST_MCAST_FILTER) {
/* once enabling filtering, we need to signal to PF to add /* once enabling filtering, we need to signal to PF to add
* its' own LMAC to the filter to accept packets for it. * its' own LMAC to the filter to accept packets for it.
*/ */
...@@ -1954,23 +1949,46 @@ static void nicvf_set_rx_mode_task(struct work_struct *work_arg) ...@@ -1954,23 +1949,46 @@ static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
} }
/* check if we have any specific MACs to be added to PF DMAC filter */ /* check if we have any specific MACs to be added to PF DMAC filter */
if (vf_work->mc) { if (mc_addrs) {
/* now go through kernel list of MACs and add them one by one */ /* now go through kernel list of MACs and add them one by one */
for (idx = 0; idx < vf_work->mc->count; idx++) { for (idx = 0; idx < mc_addrs->count; idx++) {
mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST; mbx.xcast.msg = NIC_MBOX_MSG_ADD_MCAST;
mbx.xcast.data.mac = vf_work->mc->mc[idx]; mbx.xcast.data.mac = mc_addrs->mc[idx];
nicvf_send_msg_to_pf(nic, &mbx); nicvf_send_msg_to_pf(nic, &mbx);
} }
kfree(vf_work->mc); kfree(mc_addrs);
} }
/* and finally set rx mode for PF accordingly */ /* and finally set rx mode for PF accordingly */
mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST; mbx.xcast.msg = NIC_MBOX_MSG_SET_XCAST;
mbx.xcast.data.mode = vf_work->mode; mbx.xcast.data.mode = mode;
nicvf_send_msg_to_pf(nic, &mbx); nicvf_send_msg_to_pf(nic, &mbx);
} }
static void nicvf_set_rx_mode_task(struct work_struct *work_arg)
{
struct nicvf_work *vf_work = container_of(work_arg, struct nicvf_work,
work.work);
struct nicvf *nic = container_of(vf_work, struct nicvf, rx_mode_work);
u8 mode;
struct xcast_addr_list *mc;
if (!vf_work)
return;
/* Save message data locally to prevent them from
* being overwritten by next ndo_set_rx_mode call().
*/
spin_lock(&nic->rx_mode_wq_lock);
mode = vf_work->mode;
mc = vf_work->mc;
vf_work->mc = NULL;
spin_unlock(&nic->rx_mode_wq_lock);
__nicvf_set_rx_mode_task(mode, mc, nic);
}
static void nicvf_set_rx_mode(struct net_device *netdev) static void nicvf_set_rx_mode(struct net_device *netdev)
{ {
struct nicvf *nic = netdev_priv(netdev); struct nicvf *nic = netdev_priv(netdev);
...@@ -2004,9 +2022,12 @@ static void nicvf_set_rx_mode(struct net_device *netdev) ...@@ -2004,9 +2022,12 @@ static void nicvf_set_rx_mode(struct net_device *netdev)
} }
} }
} }
spin_lock(&nic->rx_mode_wq_lock);
kfree(nic->rx_mode_work.mc);
nic->rx_mode_work.mc = mc_list; nic->rx_mode_work.mc = mc_list;
nic->rx_mode_work.mode = mode; nic->rx_mode_work.mode = mode;
queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 2 * HZ); queue_delayed_work(nicvf_rx_mode_wq, &nic->rx_mode_work.work, 0);
spin_unlock(&nic->rx_mode_wq_lock);
} }
static const struct net_device_ops nicvf_netdev_ops = { static const struct net_device_ops nicvf_netdev_ops = {
...@@ -2163,6 +2184,7 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -2163,6 +2184,7 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
INIT_WORK(&nic->reset_task, nicvf_reset_task); INIT_WORK(&nic->reset_task, nicvf_reset_task);
INIT_DELAYED_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task); INIT_DELAYED_WORK(&nic->rx_mode_work.work, nicvf_set_rx_mode_task);
spin_lock_init(&nic->rx_mode_wq_lock);
err = register_netdev(netdev); err = register_netdev(netdev);
if (err) { if (err) {
......
...@@ -3362,10 +3362,17 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -3362,10 +3362,17 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
err = sysfs_create_group(&adapter->port[0]->dev.kobj, err = sysfs_create_group(&adapter->port[0]->dev.kobj,
&cxgb3_attr_group); &cxgb3_attr_group);
if (err) {
dev_err(&pdev->dev, "cannot create sysfs group\n");
goto out_close_led;
}
print_port_info(adapter, ai); print_port_info(adapter, ai);
return 0; return 0;
out_close_led:
t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0);
out_free_dev: out_free_dev:
iounmap(adapter->regs); iounmap(adapter->regs);
for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i) for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i)
......
...@@ -760,9 +760,9 @@ struct ixgbe_adapter { ...@@ -760,9 +760,9 @@ struct ixgbe_adapter {
#define IXGBE_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */ #define IXGBE_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */
u32 *rss_key; u32 *rss_key;
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM_OFFLOAD
struct ixgbe_ipsec *ipsec; struct ixgbe_ipsec *ipsec;
#endif /* CONFIG_XFRM */ #endif /* CONFIG_XFRM_OFFLOAD */
}; };
static inline u8 ixgbe_max_rss_indices(struct ixgbe_adapter *adapter) static inline u8 ixgbe_max_rss_indices(struct ixgbe_adapter *adapter)
......
...@@ -158,7 +158,16 @@ static void ixgbe_ipsec_stop_data(struct ixgbe_adapter *adapter) ...@@ -158,7 +158,16 @@ static void ixgbe_ipsec_stop_data(struct ixgbe_adapter *adapter)
reg |= IXGBE_SECRXCTRL_RX_DIS; reg |= IXGBE_SECRXCTRL_RX_DIS;
IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, reg); IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, reg);
IXGBE_WRITE_FLUSH(hw); /* If both Tx and Rx are ready there are no packets
* that we need to flush so the loopback configuration
* below is not necessary.
*/
t_rdy = IXGBE_READ_REG(hw, IXGBE_SECTXSTAT) &
IXGBE_SECTXSTAT_SECTX_RDY;
r_rdy = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) &
IXGBE_SECRXSTAT_SECRX_RDY;
if (t_rdy && r_rdy)
return;
/* If the tx fifo doesn't have link, but still has data, /* If the tx fifo doesn't have link, but still has data,
* we can't clear the tx sec block. Set the MAC loopback * we can't clear the tx sec block. Set the MAC loopback
...@@ -185,7 +194,7 @@ static void ixgbe_ipsec_stop_data(struct ixgbe_adapter *adapter) ...@@ -185,7 +194,7 @@ static void ixgbe_ipsec_stop_data(struct ixgbe_adapter *adapter)
IXGBE_SECTXSTAT_SECTX_RDY; IXGBE_SECTXSTAT_SECTX_RDY;
r_rdy = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) & r_rdy = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) &
IXGBE_SECRXSTAT_SECRX_RDY; IXGBE_SECRXSTAT_SECRX_RDY;
} while (!t_rdy && !r_rdy && limit--); } while (!(t_rdy && r_rdy) && limit--);
/* undo loopback if we played with it earlier */ /* undo loopback if we played with it earlier */
if (!link) { if (!link) {
...@@ -966,10 +975,22 @@ void ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring, ...@@ -966,10 +975,22 @@ void ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring,
**/ **/
void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter) void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter)
{ {
struct ixgbe_hw *hw = &adapter->hw;
struct ixgbe_ipsec *ipsec; struct ixgbe_ipsec *ipsec;
u32 t_dis, r_dis;
size_t size; size_t size;
if (adapter->hw.mac.type == ixgbe_mac_82598EB) if (hw->mac.type == ixgbe_mac_82598EB)
return;
/* If there is no support for either Tx or Rx offload
* we should not be advertising support for IPsec.
*/
t_dis = IXGBE_READ_REG(hw, IXGBE_SECTXSTAT) &
IXGBE_SECTXSTAT_SECTX_OFF_DIS;
r_dis = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) &
IXGBE_SECRXSTAT_SECRX_OFF_DIS;
if (t_dis || r_dis)
return; return;
ipsec = kzalloc(sizeof(*ipsec), GFP_KERNEL); ipsec = kzalloc(sizeof(*ipsec), GFP_KERNEL);
...@@ -1001,13 +1022,6 @@ void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter) ...@@ -1001,13 +1022,6 @@ void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter)
adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops; adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops;
#define IXGBE_ESP_FEATURES (NETIF_F_HW_ESP | \
NETIF_F_HW_ESP_TX_CSUM | \
NETIF_F_GSO_ESP)
adapter->netdev->features |= IXGBE_ESP_FEATURES;
adapter->netdev->hw_enc_features |= IXGBE_ESP_FEATURES;
return; return;
err2: err2:
......
...@@ -593,6 +593,14 @@ static bool ixgbe_set_sriov_queues(struct ixgbe_adapter *adapter) ...@@ -593,6 +593,14 @@ static bool ixgbe_set_sriov_queues(struct ixgbe_adapter *adapter)
} }
#endif #endif
/* To support macvlan offload we have to use num_tc to
* restrict the queues that can be used by the device.
* By doing this we can avoid reporting a false number of
* queues.
*/
if (vmdq_i > 1)
netdev_set_num_tc(adapter->netdev, 1);
/* populate TC0 for use by pool 0 */ /* populate TC0 for use by pool 0 */
netdev_set_tc_queue(adapter->netdev, 0, netdev_set_tc_queue(adapter->netdev, 0,
adapter->num_rx_queues_per_pool, 0); adapter->num_rx_queues_per_pool, 0);
......
...@@ -6117,6 +6117,7 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter, ...@@ -6117,6 +6117,7 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter,
#ifdef CONFIG_IXGBE_DCB #ifdef CONFIG_IXGBE_DCB
ixgbe_init_dcb(adapter); ixgbe_init_dcb(adapter);
#endif #endif
ixgbe_init_ipsec_offload(adapter);
/* default flow control settings */ /* default flow control settings */
hw->fc.requested_mode = ixgbe_fc_full; hw->fc.requested_mode = ixgbe_fc_full;
...@@ -8822,14 +8823,6 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc) ...@@ -8822,14 +8823,6 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc)
} else { } else {
netdev_reset_tc(dev); netdev_reset_tc(dev);
/* To support macvlan offload we have to use num_tc to
* restrict the queues that can be used by the device.
* By doing this we can avoid reporting a false number of
* queues.
*/
if (!tc && adapter->num_rx_pools > 1)
netdev_set_num_tc(dev, 1);
if (adapter->hw.mac.type == ixgbe_mac_82598EB) if (adapter->hw.mac.type == ixgbe_mac_82598EB)
adapter->hw.fc.requested_mode = adapter->last_lfc_mode; adapter->hw.fc.requested_mode = adapter->last_lfc_mode;
...@@ -9904,7 +9897,7 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev, ...@@ -9904,7 +9897,7 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev,
* the TSO, so it's the exception. * the TSO, so it's the exception.
*/ */
if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) { if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) {
#ifdef CONFIG_XFRM #ifdef CONFIG_XFRM_OFFLOAD
if (!skb->sp) if (!skb->sp)
#endif #endif
features &= ~NETIF_F_TSO; features &= ~NETIF_F_TSO;
...@@ -10437,6 +10430,14 @@ skip_sriov: ...@@ -10437,6 +10430,14 @@ skip_sriov:
if (hw->mac.type >= ixgbe_mac_82599EB) if (hw->mac.type >= ixgbe_mac_82599EB)
netdev->features |= NETIF_F_SCTP_CRC; netdev->features |= NETIF_F_SCTP_CRC;
#ifdef CONFIG_XFRM_OFFLOAD
#define IXGBE_ESP_FEATURES (NETIF_F_HW_ESP | \
NETIF_F_HW_ESP_TX_CSUM | \
NETIF_F_GSO_ESP)
if (adapter->ipsec)
netdev->features |= IXGBE_ESP_FEATURES;
#endif
/* copy netdev features into list of user selectable features */ /* copy netdev features into list of user selectable features */
netdev->hw_features |= netdev->features | netdev->hw_features |= netdev->features |
NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_FILTER |
...@@ -10499,8 +10500,6 @@ skip_sriov: ...@@ -10499,8 +10500,6 @@ skip_sriov:
NETIF_F_FCOE_MTU; NETIF_F_FCOE_MTU;
} }
#endif /* IXGBE_FCOE */ #endif /* IXGBE_FCOE */
ixgbe_init_ipsec_offload(adapter);
if (adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE) if (adapter->flags2 & IXGBE_FLAG2_RSC_CAPABLE)
netdev->hw_features |= NETIF_F_LRO; netdev->hw_features |= NETIF_F_LRO;
if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)
......
...@@ -599,13 +599,15 @@ struct ixgbe_nvm_version { ...@@ -599,13 +599,15 @@ struct ixgbe_nvm_version {
#define IXGBE_SECTXCTRL_STORE_FORWARD 0x00000004 #define IXGBE_SECTXCTRL_STORE_FORWARD 0x00000004
#define IXGBE_SECTXSTAT_SECTX_RDY 0x00000001 #define IXGBE_SECTXSTAT_SECTX_RDY 0x00000001
#define IXGBE_SECTXSTAT_ECC_TXERR 0x00000002 #define IXGBE_SECTXSTAT_SECTX_OFF_DIS 0x00000002
#define IXGBE_SECTXSTAT_ECC_TXERR 0x00000004
#define IXGBE_SECRXCTRL_SECRX_DIS 0x00000001 #define IXGBE_SECRXCTRL_SECRX_DIS 0x00000001
#define IXGBE_SECRXCTRL_RX_DIS 0x00000002 #define IXGBE_SECRXCTRL_RX_DIS 0x00000002
#define IXGBE_SECRXSTAT_SECRX_RDY 0x00000001 #define IXGBE_SECRXSTAT_SECRX_RDY 0x00000001
#define IXGBE_SECRXSTAT_ECC_RXERR 0x00000002 #define IXGBE_SECRXSTAT_SECRX_OFF_DIS 0x00000002
#define IXGBE_SECRXSTAT_ECC_RXERR 0x00000004
/* LinkSec (MacSec) Registers */ /* LinkSec (MacSec) Registers */
#define IXGBE_LSECTXCAP 0x08A00 #define IXGBE_LSECTXCAP 0x08A00
......
...@@ -4756,12 +4756,6 @@ static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6) ...@@ -4756,12 +4756,6 @@ static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
kfree(mlxsw_sp_rt6); kfree(mlxsw_sp_rt6);
} }
static bool mlxsw_sp_fib6_rt_can_mp(const struct fib6_info *rt)
{
/* RTF_CACHE routes are ignored */
return (rt->fib6_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY;
}
static struct fib6_info * static struct fib6_info *
mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
{ {
...@@ -4771,11 +4765,11 @@ mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) ...@@ -4771,11 +4765,11 @@ mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry)
static struct mlxsw_sp_fib6_entry * static struct mlxsw_sp_fib6_entry *
mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node, mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
const struct fib6_info *nrt, bool replace) const struct fib6_info *nrt, bool append)
{ {
struct mlxsw_sp_fib6_entry *fib6_entry; struct mlxsw_sp_fib6_entry *fib6_entry;
if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace) if (!append)
return NULL; return NULL;
list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
...@@ -4790,8 +4784,7 @@ mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node, ...@@ -4790,8 +4784,7 @@ mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node,
break; break;
if (rt->fib6_metric < nrt->fib6_metric) if (rt->fib6_metric < nrt->fib6_metric)
continue; continue;
if (rt->fib6_metric == nrt->fib6_metric && if (rt->fib6_metric == nrt->fib6_metric)
mlxsw_sp_fib6_rt_can_mp(rt))
return fib6_entry; return fib6_entry;
if (rt->fib6_metric > nrt->fib6_metric) if (rt->fib6_metric > nrt->fib6_metric)
break; break;
...@@ -5170,7 +5163,7 @@ static struct mlxsw_sp_fib6_entry * ...@@ -5170,7 +5163,7 @@ static struct mlxsw_sp_fib6_entry *
mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
const struct fib6_info *nrt, bool replace) const struct fib6_info *nrt, bool replace)
{ {
struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL; struct mlxsw_sp_fib6_entry *fib6_entry;
list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) {
struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); struct fib6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
...@@ -5179,18 +5172,13 @@ mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, ...@@ -5179,18 +5172,13 @@ mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node,
continue; continue;
if (rt->fib6_table->tb6_id != nrt->fib6_table->tb6_id) if (rt->fib6_table->tb6_id != nrt->fib6_table->tb6_id)
break; break;
if (replace && rt->fib6_metric == nrt->fib6_metric) { if (replace && rt->fib6_metric == nrt->fib6_metric)
if (mlxsw_sp_fib6_rt_can_mp(rt) == return fib6_entry;
mlxsw_sp_fib6_rt_can_mp(nrt))
return fib6_entry;
if (mlxsw_sp_fib6_rt_can_mp(nrt))
fallback = fallback ?: fib6_entry;
}
if (rt->fib6_metric > nrt->fib6_metric) if (rt->fib6_metric > nrt->fib6_metric)
return fallback ?: fib6_entry; return fib6_entry;
} }
return fallback; return NULL;
} }
static int static int
...@@ -5316,7 +5304,8 @@ static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, ...@@ -5316,7 +5304,8 @@ static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp,
} }
static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
struct fib6_info *rt, bool replace) struct fib6_info *rt, bool replace,
bool append)
{ {
struct mlxsw_sp_fib6_entry *fib6_entry; struct mlxsw_sp_fib6_entry *fib6_entry;
struct mlxsw_sp_fib_node *fib_node; struct mlxsw_sp_fib_node *fib_node;
...@@ -5342,7 +5331,7 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, ...@@ -5342,7 +5331,7 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
/* Before creating a new entry, try to append route to an existing /* Before creating a new entry, try to append route to an existing
* multipath entry. * multipath entry.
*/ */
fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace); fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, append);
if (fib6_entry) { if (fib6_entry) {
err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt); err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt);
if (err) if (err)
...@@ -5350,6 +5339,14 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, ...@@ -5350,6 +5339,14 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
return 0; return 0;
} }
/* We received an append event, yet did not find any route to
* append to.
*/
if (WARN_ON(append)) {
err = -EINVAL;
goto err_fib6_entry_append;
}
fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt); fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt);
if (IS_ERR(fib6_entry)) { if (IS_ERR(fib6_entry)) {
err = PTR_ERR(fib6_entry); err = PTR_ERR(fib6_entry);
...@@ -5367,6 +5364,7 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, ...@@ -5367,6 +5364,7 @@ static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp,
err_fib6_node_entry_link: err_fib6_node_entry_link:
mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
err_fib6_entry_create: err_fib6_entry_create:
err_fib6_entry_append:
err_fib6_entry_nexthop_add: err_fib6_entry_nexthop_add:
mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
return err; return err;
...@@ -5717,7 +5715,7 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) ...@@ -5717,7 +5715,7 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
struct mlxsw_sp_fib_event_work *fib_work = struct mlxsw_sp_fib_event_work *fib_work =
container_of(work, struct mlxsw_sp_fib_event_work, work); container_of(work, struct mlxsw_sp_fib_event_work, work);
struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
bool replace; bool replace, append;
int err; int err;
rtnl_lock(); rtnl_lock();
...@@ -5728,8 +5726,10 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) ...@@ -5728,8 +5726,10 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
case FIB_EVENT_ENTRY_APPEND: /* fall through */ case FIB_EVENT_ENTRY_APPEND: /* fall through */
case FIB_EVENT_ENTRY_ADD: case FIB_EVENT_ENTRY_ADD:
replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE;
append = fib_work->event == FIB_EVENT_ENTRY_APPEND;
err = mlxsw_sp_router_fib6_add(mlxsw_sp, err = mlxsw_sp_router_fib6_add(mlxsw_sp,
fib_work->fen6_info.rt, replace); fib_work->fen6_info.rt, replace,
append);
if (err)