commit: 516de23c184688cd1071c1c815fbb2e9827612ad Author: Mike Pagano <mpagano <AT> gentoo <DOT> org> AuthorDate: Sat Oct 3 16:07:16 2015 +0000 Commit: Mike Pagano <mpagano <AT> gentoo <DOT> org> CommitDate: Sat Oct 3 16:07:16 2015 +0000 URL: https://gitweb.gentoo.org/proj/linux-patches.git/commit/?id=516de23c
Linux patch 4.1.10 0000_README | 4 + 1009_linux-4.1.10.patch | 1353 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1357 insertions(+) diff --git a/0000_README b/0000_README index 348e8f5..b9b941a 100644 --- a/0000_README +++ b/0000_README @@ -79,6 +79,10 @@ Patch: 1008_linux-4.1.9.patch From: http://www.kernel.org Desc: Linux 4.1.9 +Patch: 1009_linux-4.1.10.patch +From: http://www.kernel.org +Desc: Linux 4.1.10 + Patch: 1500_XATTR_USER_PREFIX.patch From: https://bugs.gentoo.org/show_bug.cgi?id=470644 Desc: Support for namespace user.pax.* on tmpfs. diff --git a/1009_linux-4.1.10.patch b/1009_linux-4.1.10.patch new file mode 100644 index 0000000..8d80808 --- /dev/null +++ b/1009_linux-4.1.10.patch @@ -0,0 +1,1353 @@ +diff --git a/Documentation/devicetree/bindings/net/ethernet.txt b/Documentation/devicetree/bindings/net/ethernet.txt +index 41b3f3f864e8..5d88f37480b6 100644 +--- a/Documentation/devicetree/bindings/net/ethernet.txt ++++ b/Documentation/devicetree/bindings/net/ethernet.txt +@@ -25,7 +25,11 @@ The following properties are common to the Ethernet controllers: + flow control thresholds. + - tx-fifo-depth: the size of the controller's transmit fifo in bytes. This + is used for components that can have configurable fifo sizes. ++- managed: string, specifies the PHY management type. Supported values are: ++ "auto", "in-band-status". "auto" is the default, it usess MDIO for ++ management if fixed-link is not specified. + + Child nodes of the Ethernet controller are typically the individual PHY devices + connected via the MDIO bus (sometimes the MDIO bus controller is separate). + They are described in the phy.txt file in this same directory. ++For non-MDIO PHY management see fixed-link.txt. +diff --git a/Makefile b/Makefile +index e071176b2ce6..d02f16b510dc 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 4 + PATCHLEVEL = 1 +-SUBLEVEL = 9 ++SUBLEVEL = 10 + EXTRAVERSION = + NAME = Series 4800 + +diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c +index f1ff39a3d1c1..54d946a9eee6 100644 +--- a/drivers/block/zram/zcomp.c ++++ b/drivers/block/zram/zcomp.c +@@ -325,12 +325,14 @@ void zcomp_destroy(struct zcomp *comp) + * allocate new zcomp and initialize it. return compressing + * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) + * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in +- * case of allocation error. ++ * case of allocation error, or any other error potentially ++ * returned by functions zcomp_strm_{multi,single}_create. + */ + struct zcomp *zcomp_create(const char *compress, int max_strm) + { + struct zcomp *comp; + struct zcomp_backend *backend; ++ int error; + + backend = find_backend(compress); + if (!backend) +@@ -342,12 +344,12 @@ struct zcomp *zcomp_create(const char *compress, int max_strm) + + comp->backend = backend; + if (max_strm > 1) +- zcomp_strm_multi_create(comp, max_strm); ++ error = zcomp_strm_multi_create(comp, max_strm); + else +- zcomp_strm_single_create(comp); +- if (!comp->stream) { ++ error = zcomp_strm_single_create(comp); ++ if (error) { + kfree(comp); +- return ERR_PTR(-ENOMEM); ++ return ERR_PTR(error); + } + return comp; + } +diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c +index cedb572bf25a..db9ebbc1a732 100644 +--- a/drivers/net/dsa/bcm_sf2.c ++++ b/drivers/net/dsa/bcm_sf2.c +@@ -417,7 +417,7 @@ static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port) + core_writel(priv, port, CORE_FAST_AGE_PORT); + + reg = core_readl(priv, CORE_FAST_AGE_CTRL); +- reg |= EN_AGE_PORT | FAST_AGE_STR_DONE; ++ reg |= EN_AGE_PORT | EN_AGE_DYNAMIC | FAST_AGE_STR_DONE; + core_writel(priv, reg, CORE_FAST_AGE_CTRL); + + do { +@@ -431,6 +431,8 @@ static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port) + if (!timeout) + return -ETIMEDOUT; + ++ core_writel(priv, 0, CORE_FAST_AGE_CTRL); ++ + return 0; + } + +@@ -506,7 +508,7 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, + u32 reg; + + reg = core_readl(priv, CORE_G_PCTL_PORT(port)); +- cur_hw_state = reg >> G_MISTP_STATE_SHIFT; ++ cur_hw_state = reg & (G_MISTP_STATE_MASK << G_MISTP_STATE_SHIFT); + + switch (state) { + case BR_STATE_DISABLED: +@@ -530,10 +532,12 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, + } + + /* Fast-age ARL entries if we are moving a port from Learning or +- * Forwarding state to Disabled, Blocking or Listening state ++ * Forwarding (cur_hw_state) state to Disabled, Blocking or Listening ++ * state (hw_state) + */ + if (cur_hw_state != hw_state) { +- if (cur_hw_state & 4 && !(hw_state & 4)) { ++ if (cur_hw_state >= G_MISTP_LEARN_STATE && ++ hw_state <= G_MISTP_LISTEN_STATE) { + ret = bcm_sf2_sw_fast_age_port(ds, port); + if (ret) { + pr_err("%s: fast-ageing failed\n", __func__); +@@ -889,15 +893,11 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, + struct fixed_phy_status *status) + { + struct bcm_sf2_priv *priv = ds_to_priv(ds); +- u32 duplex, pause, speed; ++ u32 duplex, pause; + u32 reg; + + duplex = core_readl(priv, CORE_DUPSTS); + pause = core_readl(priv, CORE_PAUSESTS); +- speed = core_readl(priv, CORE_SPDSTS); +- +- speed >>= (port * SPDSTS_SHIFT); +- speed &= SPDSTS_MASK; + + status->link = 0; + +@@ -925,18 +925,6 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, + reg &= ~LINK_STS; + core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); + +- switch (speed) { +- case SPDSTS_10: +- status->speed = SPEED_10; +- break; +- case SPDSTS_100: +- status->speed = SPEED_100; +- break; +- case SPDSTS_1000: +- status->speed = SPEED_1000; +- break; +- } +- + if ((pause & (1 << port)) && + (pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) { + status->asym_pause = 1; +diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h +index 22e2ebf31333..789d7b7737da 100644 +--- a/drivers/net/dsa/bcm_sf2.h ++++ b/drivers/net/dsa/bcm_sf2.h +@@ -112,8 +112,8 @@ static inline u64 name##_readq(struct bcm_sf2_priv *priv, u32 off) \ + spin_unlock(&priv->indir_lock); \ + return (u64)indir << 32 | dir; \ + } \ +-static inline void name##_writeq(struct bcm_sf2_priv *priv, u32 off, \ +- u64 val) \ ++static inline void name##_writeq(struct bcm_sf2_priv *priv, u64 val, \ ++ u32 off) \ + { \ + spin_lock(&priv->indir_lock); \ + reg_writel(priv, upper_32_bits(val), REG_DIR_DATA_WRITE); \ +diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c +index da48e66377b5..8207877d6237 100644 +--- a/drivers/net/ethernet/altera/altera_tse_main.c ++++ b/drivers/net/ethernet/altera/altera_tse_main.c +@@ -511,8 +511,7 @@ static int tse_poll(struct napi_struct *napi, int budget) + + if (rxcomplete < budget) { + +- napi_gro_flush(napi, false); +- __napi_complete(napi); ++ napi_complete(napi); + + netdev_dbg(priv->dev, + "NAPI Complete, did %d packets with budget %d\n", +diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c +index 66d47e448e4d..570390b5cd42 100644 +--- a/drivers/net/ethernet/freescale/fec_main.c ++++ b/drivers/net/ethernet/freescale/fec_main.c +@@ -1396,6 +1396,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + if ((status & BD_ENET_RX_LAST) == 0) + netdev_err(ndev, "rcv is not +last\n"); + ++ writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); + + /* Check for errors. */ + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 74d0389bf233..4d608f0117cd 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -3029,8 +3029,8 @@ static int mvneta_probe(struct platform_device *pdev) + const char *dt_mac_addr; + char hw_mac_addr[ETH_ALEN]; + const char *mac_from; ++ const char *managed; + int phy_mode; +- int fixed_phy = 0; + int err; + + /* Our multiqueue support is not complete, so for now, only +@@ -3064,7 +3064,6 @@ static int mvneta_probe(struct platform_device *pdev) + dev_err(&pdev->dev, "cannot register fixed PHY\n"); + goto err_free_irq; + } +- fixed_phy = 1; + + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. +@@ -3088,8 +3087,10 @@ static int mvneta_probe(struct platform_device *pdev) + pp = netdev_priv(dev); + pp->phy_node = phy_node; + pp->phy_interface = phy_mode; +- pp->use_inband_status = (phy_mode == PHY_INTERFACE_MODE_SGMII) && +- fixed_phy; ++ ++ err = of_property_read_string(dn, "managed", &managed); ++ pp->use_inband_status = (err == 0 && ++ strcmp(managed, "in-band-status") == 0); + + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { +diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c +index eab4e080ebd2..80aac20104de 100644 +--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c ++++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c +@@ -1256,8 +1256,6 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) + rss_context->hash_fn = MLX4_RSS_HASH_TOP; + memcpy(rss_context->rss_key, priv->rss_key, + MLX4_EN_RSS_KEY_SIZE); +- netdev_rss_key_fill(rss_context->rss_key, +- MLX4_EN_RSS_KEY_SIZE); + } else { + en_err(priv, "Unknown RSS hash function requested\n"); + err = -EINVAL; +diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c +index 8c350c5d54ad..58858c5589db 100644 +--- a/drivers/net/macvtap.c ++++ b/drivers/net/macvtap.c +@@ -1054,10 +1054,10 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, + return 0; + + case TUNSETSNDBUF: +- if (get_user(u, up)) ++ if (get_user(s, sp)) + return -EFAULT; + +- q->sk.sk_sndbuf = u; ++ q->sk.sk_sndbuf = s; + return 0; + + case TUNGETVNETHDRSZ: +diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c +index 1960b46add65..479b93f9581c 100644 +--- a/drivers/net/phy/fixed_phy.c ++++ b/drivers/net/phy/fixed_phy.c +@@ -52,6 +52,10 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) + u16 lpagb = 0; + u16 lpa = 0; + ++ if (!fp->status.link) ++ goto done; ++ bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; ++ + if (fp->status.duplex) { + bmcr |= BMCR_FULLDPLX; + +@@ -96,15 +100,13 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) + } + } + +- if (fp->status.link) +- bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; +- + if (fp->status.pause) + lpa |= LPA_PAUSE_CAP; + + if (fp->status.asym_pause) + lpa |= LPA_PAUSE_ASYM; + ++done: + fp->regs[MII_PHYSID1] = 0; + fp->regs[MII_PHYSID2] = 0; + +diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c +index 3c86b107275a..e0498571ae26 100644 +--- a/drivers/net/usb/usbnet.c ++++ b/drivers/net/usb/usbnet.c +@@ -778,7 +778,7 @@ int usbnet_stop (struct net_device *net) + { + struct usbnet *dev = netdev_priv(net); + struct driver_info *info = dev->driver_info; +- int retval, pm; ++ int retval, pm, mpn; + + clear_bit(EVENT_DEV_OPEN, &dev->flags); + netif_stop_queue (net); +@@ -809,6 +809,8 @@ int usbnet_stop (struct net_device *net) + + usbnet_purge_paused_rxq(dev); + ++ mpn = !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags); ++ + /* deferred work (task, timer, softirq) must also stop. + * can't flush_scheduled_work() until we drop rtnl (later), + * else workers could deadlock; so make workers a NOP. +@@ -819,8 +821,7 @@ int usbnet_stop (struct net_device *net) + if (!pm) + usb_autopm_put_interface(dev->intf); + +- if (info->manage_power && +- !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags)) ++ if (info->manage_power && mpn) + info->manage_power(dev, 0); + else + usb_autopm_put_interface(dev->intf); +diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c +index 21a0fbf1ed94..0085b8df83e2 100644 +--- a/drivers/net/vxlan.c ++++ b/drivers/net/vxlan.c +@@ -2212,6 +2212,8 @@ static int vxlan_open(struct net_device *dev) + + if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { + ret = vxlan_igmp_join(vxlan); ++ if (ret == -EADDRINUSE) ++ ret = 0; + if (ret) { + vxlan_sock_release(vs); + return ret; +diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c +index 0c064485d1c2..bec8ec2b31f6 100644 +--- a/drivers/of/of_mdio.c ++++ b/drivers/of/of_mdio.c +@@ -263,7 +263,8 @@ EXPORT_SYMBOL(of_phy_attach); + bool of_phy_is_fixed_link(struct device_node *np) + { + struct device_node *dn; +- int len; ++ int len, err; ++ const char *managed; + + /* New binding */ + dn = of_get_child_by_name(np, "fixed-link"); +@@ -272,6 +273,10 @@ bool of_phy_is_fixed_link(struct device_node *np) + return true; + } + ++ err = of_property_read_string(np, "managed", &managed); ++ if (err == 0 && strcmp(managed, "auto") != 0) ++ return true; ++ + /* Old binding */ + if (of_get_property(np, "fixed-link", &len) && + len == (5 * sizeof(__be32))) +@@ -286,8 +291,18 @@ int of_phy_register_fixed_link(struct device_node *np) + struct fixed_phy_status status = {}; + struct device_node *fixed_link_node; + const __be32 *fixed_link_prop; +- int len; ++ int len, err; + struct phy_device *phy; ++ const char *managed; ++ ++ err = of_property_read_string(np, "managed", &managed); ++ if (err == 0) { ++ if (strcmp(managed, "in-band-status") == 0) { ++ /* status is zeroed, namely its .link member */ ++ phy = fixed_phy_register(PHY_POLL, &status, np); ++ return IS_ERR(phy) ? PTR_ERR(phy) : 0; ++ } ++ } + + /* New binding */ + fixed_link_node = of_get_child_by_name(np, "fixed-link"); +diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c +index 06697315a088..fb4dd7b3ee71 100644 +--- a/drivers/platform/x86/hp-wmi.c ++++ b/drivers/platform/x86/hp-wmi.c +@@ -54,8 +54,9 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); + #define HPWMI_HARDWARE_QUERY 0x4 + #define HPWMI_WIRELESS_QUERY 0x5 + #define HPWMI_BIOS_QUERY 0x9 ++#define HPWMI_FEATURE_QUERY 0xb + #define HPWMI_HOTKEY_QUERY 0xc +-#define HPWMI_FEATURE_QUERY 0xd ++#define HPWMI_FEATURE2_QUERY 0xd + #define HPWMI_WIRELESS2_QUERY 0x1b + #define HPWMI_POSTCODEERROR_QUERY 0x2a + +@@ -295,25 +296,33 @@ static int hp_wmi_tablet_state(void) + return (state & 0x4) ? 1 : 0; + } + +-static int __init hp_wmi_bios_2009_later(void) ++static int __init hp_wmi_bios_2008_later(void) + { + int state = 0; + int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state, + sizeof(state), sizeof(state)); +- if (ret) +- return ret; ++ if (!ret) ++ return 1; + +- return (state & 0x10) ? 1 : 0; ++ return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; + } + +-static int hp_wmi_enable_hotkeys(void) ++static int __init hp_wmi_bios_2009_later(void) + { +- int ret; +- int query = 0x6e; ++ int state = 0; ++ int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, 0, &state, ++ sizeof(state), sizeof(state)); ++ if (!ret) ++ return 1; + +- ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query), +- 0); ++ return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; ++} + ++static int __init hp_wmi_enable_hotkeys(void) ++{ ++ int value = 0x6e; ++ int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &value, ++ sizeof(value), 0); + if (ret) + return -EINVAL; + return 0; +@@ -663,7 +672,7 @@ static int __init hp_wmi_input_setup(void) + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); + +- if (hp_wmi_bios_2009_later() == 4) ++ if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) + hp_wmi_enable_hotkeys(); + + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index ff667e18b2d6..9ba383f5b0c4 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -980,7 +980,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, + + ih = igmpv3_report_hdr(skb); + num = ntohs(ih->ngrec); +- len = sizeof(*ih); ++ len = skb_transport_offset(skb) + sizeof(*ih); + + for (i = 0; i < num; i++) { + len += sizeof(*grec); +@@ -1035,7 +1035,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, + + icmp6h = icmp6_hdr(skb); + num = ntohs(icmp6h->icmp6_dataun.un_data16[1]); +- len = sizeof(*icmp6h); ++ len = skb_transport_offset(skb) + sizeof(*icmp6h); + + for (i = 0; i < num; i++) { + __be16 *nsrcs, _nsrcs; +diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c +index 9a12668f7d62..0ad144fb0c79 100644 +--- a/net/core/fib_rules.c ++++ b/net/core/fib_rules.c +@@ -615,15 +615,17 @@ static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, + { + int idx = 0; + struct fib_rule *rule; ++ int err = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(rule, &ops->rules_list, list) { + if (idx < cb->args[1]) + goto skip; + +- if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).portid, +- cb->nlh->nlmsg_seq, RTM_NEWRULE, +- NLM_F_MULTI, ops) < 0) ++ err = fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).portid, ++ cb->nlh->nlmsg_seq, RTM_NEWRULE, ++ NLM_F_MULTI, ops); ++ if (err) + break; + skip: + idx++; +@@ -632,7 +634,7 @@ skip: + cb->args[1] = idx; + rules_ops_put(ops); + +- return skb->len; ++ return err; + } + + static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) +@@ -648,7 +650,9 @@ static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) + if (ops == NULL) + return -EAFNOSUPPORT; + +- return dump_rules(skb, cb, ops); ++ dump_rules(skb, cb, ops); ++ ++ return skb->len; + } + + rcu_read_lock(); +diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c +index 74dddf84adcd..556ecf96a385 100644 +--- a/net/core/sock_diag.c ++++ b/net/core/sock_diag.c +@@ -86,6 +86,9 @@ int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk, + goto out; + + fprog = filter->prog->orig_prog; ++ if (!fprog) ++ goto out; ++ + flen = bpf_classic_proglen(fprog); + + attr = nla_reserve(skb, attrtype, flen); +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index a369e8a70b2c..986440b24978 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -2893,6 +2893,7 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority) + skb_reserve(skb, MAX_TCP_HEADER); + tcp_init_nondata_skb(skb, tcp_acceptable_seq(sk), + TCPHDR_ACK | TCPHDR_RST); ++ skb_mstamp_get(&skb->skb_mstamp); + /* Send it off. */ + if (tcp_transmit_skb(sk, skb, 0, priority)) + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED); +diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c +index 447a7fbd1bb6..f5e2ba1c18bf 100644 +--- a/net/ipv6/exthdrs_offload.c ++++ b/net/ipv6/exthdrs_offload.c +@@ -36,6 +36,6 @@ out: + return ret; + + out_rt: +- inet_del_offload(&rthdr_offload, IPPROTO_ROUTING); ++ inet6_del_offload(&rthdr_offload, IPPROTO_ROUTING); + goto out; + } +diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c +index a38d3ac0f18f..69f4f689f06a 100644 +--- a/net/ipv6/ip6_gre.c ++++ b/net/ipv6/ip6_gre.c +@@ -361,6 +361,7 @@ static void ip6gre_tunnel_uninit(struct net_device *dev) + struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); + + ip6gre_tunnel_unlink(ign, t); ++ ip6_tnl_dst_reset(t); + dev_put(dev); + } + +diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c +index 74ceb73c1c9a..5f36266b1f5e 100644 +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -550,7 +550,7 @@ static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) + + if (it->cache == &mrt->mfc6_unres_queue) + spin_unlock_bh(&mfc_unres_lock); +- else if (it->cache == mrt->mfc6_cache_array) ++ else if (it->cache == &mrt->mfc6_cache_array[it->ct]) + read_unlock(&mrt_lock); + } + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index c73ae5039e46..f371fefa7fdc 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -1515,7 +1515,7 @@ static int ip6_convert_metrics(struct mx6_config *mxc, + return -EINVAL; + } + +-int ip6_route_add(struct fib6_config *cfg) ++int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret) + { + int err; + struct net *net = cfg->fc_nlinfo.nl_net; +@@ -1523,7 +1523,6 @@ int ip6_route_add(struct fib6_config *cfg) + struct net_device *dev = NULL; + struct inet6_dev *idev = NULL; + struct fib6_table *table; +- struct mx6_config mxc = { .mx = NULL, }; + int addr_type; + + if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) +@@ -1719,6 +1718,32 @@ install_route: + + cfg->fc_nlinfo.nl_net = dev_net(dev); + ++ *rt_ret = rt; ++ ++ return 0; ++out: ++ if (dev) ++ dev_put(dev); ++ if (idev) ++ in6_dev_put(idev); ++ if (rt) ++ dst_free(&rt->dst); ++ ++ *rt_ret = NULL; ++ ++ return err; ++} ++ ++int ip6_route_add(struct fib6_config *cfg) ++{ ++ struct mx6_config mxc = { .mx = NULL, }; ++ struct rt6_info *rt = NULL; ++ int err; ++ ++ err = ip6_route_info_create(cfg, &rt); ++ if (err) ++ goto out; ++ + err = ip6_convert_metrics(&mxc, cfg); + if (err) + goto out; +@@ -1726,14 +1751,12 @@ install_route: + err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); + + kfree(mxc.mx); ++ + return err; + out: +- if (dev) +- dev_put(dev); +- if (idev) +- in6_dev_put(idev); + if (rt) + dst_free(&rt->dst); ++ + return err; + } + +@@ -2496,19 +2519,78 @@ errout: + return err; + } + +-static int ip6_route_multipath(struct fib6_config *cfg, int add) ++struct rt6_nh { ++ struct rt6_info *rt6_info; ++ struct fib6_config r_cfg; ++ struct mx6_config mxc; ++ struct list_head next; ++}; ++ ++static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) ++{ ++ struct rt6_nh *nh; ++ ++ list_for_each_entry(nh, rt6_nh_list, next) { ++ pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n", ++ &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, ++ nh->r_cfg.fc_ifindex); ++ } ++} ++ ++static int ip6_route_info_append(struct list_head *rt6_nh_list, ++ struct rt6_info *rt, struct fib6_config *r_cfg) ++{ ++ struct rt6_nh *nh; ++ struct rt6_info *rtnh; ++ int err = -EEXIST; ++ ++ list_for_each_entry(nh, rt6_nh_list, next) { ++ /* check if rt6_info already exists */ ++ rtnh = nh->rt6_info; ++ ++ if (rtnh->dst.dev == rt->dst.dev && ++ rtnh->rt6i_idev == rt->rt6i_idev && ++ ipv6_addr_equal(&rtnh->rt6i_gateway, ++ &rt->rt6i_gateway)) ++ return err; ++ } ++ ++ nh = kzalloc(sizeof(*nh), GFP_KERNEL); ++ if (!nh) ++ return -ENOMEM; ++ nh->rt6_info = rt; ++ err = ip6_convert_metrics(&nh->mxc, r_cfg); ++ if (err) { ++ kfree(nh); ++ return err; ++ } ++ memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); ++ list_add_tail(&nh->next, rt6_nh_list); ++ ++ return 0; ++} ++ ++static int ip6_route_multipath_add(struct fib6_config *cfg) + { + struct fib6_config r_cfg; + struct rtnexthop *rtnh; ++ struct rt6_info *rt; ++ struct rt6_nh *err_nh; ++ struct rt6_nh *nh, *nh_safe; + int remaining; + int attrlen; +- int err = 0, last_err = 0; ++ int err = 1; ++ int nhn = 0; ++ int replace = (cfg->fc_nlinfo.nlh && ++ (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); ++ LIST_HEAD(rt6_nh_list); + + remaining = cfg->fc_mp_len; +-beginning: + rtnh = (struct rtnexthop *)cfg->fc_mp; + +- /* Parse a Multipath Entry */ ++ /* Parse a Multipath Entry and build a list (rt6_nh_list) of ++ * rt6_info structs per nexthop ++ */ + while (rtnh_ok(rtnh, remaining)) { + memcpy(&r_cfg, cfg, sizeof(*cfg)); + if (rtnh->rtnh_ifindex) +@@ -2524,22 +2606,32 @@ beginning: + r_cfg.fc_flags |= RTF_GATEWAY; + } + } +- err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); ++ ++ err = ip6_route_info_create(&r_cfg, &rt); ++ if (err) ++ goto cleanup; ++ ++ err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); + if (err) { +- last_err = err; +- /* If we are trying to remove a route, do not stop the +- * loop when ip6_route_del() fails (because next hop is +- * already gone), we should try to remove all next hops. +- */ +- if (add) { +- /* If add fails, we should try to delete all +- * next hops that have been already added. +- */ +- add = 0; +- remaining = cfg->fc_mp_len - remaining; +- goto beginning; +- } ++ dst_free(&rt->dst); ++ goto cleanup; ++ } ++ ++ rtnh = rtnh_next(rtnh, &remaining); ++ } ++ ++ err_nh = NULL; ++ list_for_each_entry(nh, &rt6_nh_list, next) { ++ err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc); ++ /* nh->rt6_info is used or freed at this point, reset to NULL*/ ++ nh->rt6_info = NULL; ++ if (err) { ++ if (replace && nhn) ++ ip6_print_replace_route_err(&rt6_nh_list); ++ err_nh = nh; ++ goto add_errout; + } ++ + /* Because each route is added like a single route we remove + * these flags after the first nexthop: if there is a collision, + * we have already failed to add the first nexthop: +@@ -2549,6 +2641,63 @@ beginning: + */ + cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | + NLM_F_REPLACE); ++ nhn++; ++ } ++ ++ goto cleanup; ++ ++add_errout: ++ /* Delete routes that were already added */ ++ list_for_each_entry(nh, &rt6_nh_list, next) { ++ if (err_nh == nh) ++ break; ++ ip6_route_del(&nh->r_cfg); ++ } ++ ++cleanup: ++ list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { ++ if (nh->rt6_info) ++ dst_free(&nh->rt6_info->dst); ++ if (nh->mxc.mx) ++ kfree(nh->mxc.mx); ++ list_del(&nh->next); ++ kfree(nh); ++ } ++ ++ return err; ++} ++ ++static int ip6_route_multipath_del(struct fib6_config *cfg) ++{ ++ struct fib6_config r_cfg; ++ struct rtnexthop *rtnh; ++ int remaining; ++ int attrlen; ++ int err = 1, last_err = 0; ++ ++ remaining = cfg->fc_mp_len; ++ rtnh = (struct rtnexthop *)cfg->fc_mp; ++ ++ /* Parse a Multipath Entry */ ++ while (rtnh_ok(rtnh, remaining)) { ++ memcpy(&r_cfg, cfg, sizeof(*cfg)); ++ if (rtnh->rtnh_ifindex) ++ r_cfg.fc_ifindex = rtnh->rtnh_ifindex; ++ ++ attrlen = rtnh_attrlen(rtnh); ++ if (attrlen > 0) { ++ struct nlattr *nla, *attrs = rtnh_attrs(rtnh); ++ ++ nla = nla_find(attrs, attrlen, RTA_GATEWAY); ++ if (nla) { ++ nla_memcpy(&r_cfg.fc_gateway, nla, 16); ++ r_cfg.fc_flags |= RTF_GATEWAY; ++ } ++ } ++ err = ip6_route_del(&r_cfg); ++ if (err) ++ last_err = err; ++ + rtnh = rtnh_next(rtnh, &remaining); + } + +@@ -2565,7 +2714,7 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) + return err; + + if (cfg.fc_mp) +- return ip6_route_multipath(&cfg, 0); ++ return ip6_route_multipath_del(&cfg); + else + return ip6_route_del(&cfg); + } +@@ -2580,7 +2729,7 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + return err; + + if (cfg.fc_mp) +- return ip6_route_multipath(&cfg, 1); ++ return ip6_route_multipath_add(&cfg); + else + return ip6_route_add(&cfg); + } +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 4856d975492d..980121e75d2e 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -123,6 +123,24 @@ static inline u32 netlink_group_mask(u32 group) + return group ? 1 << (group - 1) : 0; + } + ++static struct sk_buff *netlink_to_full_skb(const struct sk_buff *skb, ++ gfp_t gfp_mask) ++{ ++ unsigned int len = skb_end_offset(skb); ++ struct sk_buff *new; ++ ++ new = alloc_skb(len, gfp_mask); ++ if (new == NULL) ++ return NULL; ++ ++ NETLINK_CB(new).portid = NETLINK_CB(skb).portid; ++ NETLINK_CB(new).dst_group = NETLINK_CB(skb).dst_group; ++ NETLINK_CB(new).creds = NETLINK_CB(skb).creds; ++ ++ memcpy(skb_put(new, len), skb->data, len); ++ return new; ++} ++ + int netlink_add_tap(struct netlink_tap *nt) + { + if (unlikely(nt->dev->type != ARPHRD_NETLINK)) +@@ -204,7 +222,11 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb, + int ret = -ENOMEM; + + dev_hold(dev); +- nskb = skb_clone(skb, GFP_ATOMIC); ++ ++ if (netlink_skb_is_mmaped(skb) || is_vmalloc_addr(skb->head)) ++ nskb = netlink_to_full_skb(skb, GFP_ATOMIC); ++ else ++ nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + nskb->dev = dev; + nskb->protocol = htons((u16) sk->sk_protocol); +@@ -276,11 +298,6 @@ static void netlink_rcv_wake(struct sock *sk) + } + + #ifdef CONFIG_NETLINK_MMAP +-static bool netlink_skb_is_mmaped(const struct sk_buff *skb) +-{ +- return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; +-} +- + static bool netlink_rx_is_mmaped(struct sock *sk) + { + return nlk_sk(sk)->rx_ring.pg_vec != NULL; +@@ -832,7 +849,6 @@ static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb) + } + + #else /* CONFIG_NETLINK_MMAP */ +-#define netlink_skb_is_mmaped(skb) false + #define netlink_rx_is_mmaped(sk) false + #define netlink_tx_is_mmaped(sk) false + #define netlink_mmap sock_no_mmap +@@ -1080,8 +1096,8 @@ static int netlink_insert(struct sock *sk, u32 portid) + + lock_sock(sk); + +- err = -EBUSY; +- if (nlk_sk(sk)->portid) ++ err = nlk_sk(sk)->portid == portid ? 0 : -EBUSY; ++ if (nlk_sk(sk)->bound) + goto err; + + err = -ENOMEM; +@@ -1101,10 +1117,13 @@ static int netlink_insert(struct sock *sk, u32 portid) + err = -EOVERFLOW; + if (err == -EEXIST) + err = -EADDRINUSE; +- nlk_sk(sk)->portid = 0; + sock_put(sk); + } + ++ /* We need to ensure that the socket is hashed and visible. */ ++ smp_wmb(); ++ nlk_sk(sk)->bound = portid; ++ + err: + release_sock(sk); + return err; +@@ -1484,6 +1503,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, + struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; + int err; + long unsigned int groups = nladdr->nl_groups; ++ bool bound; + + if (addr_len < sizeof(struct sockaddr_nl)) + return -EINVAL; +@@ -1500,9 +1520,14 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, + return err; + } + +- if (nlk->portid) ++ bound = nlk->bound; ++ if (bound) { ++ /* Ensure nlk->portid is up-to-date. */ ++ smp_rmb(); ++ + if (nladdr->nl_pid != nlk->portid) + return -EINVAL; ++ } + + if (nlk->netlink_bind && groups) { + int group; +@@ -1518,7 +1543,10 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, + } + } + +- if (!nlk->portid) { ++ /* No need for barriers here as we return to user-space without ++ * using any of the bound attributes. ++ */ ++ if (!bound) { + err = nladdr->nl_pid ? + netlink_insert(sk, nladdr->nl_pid) : + netlink_autobind(sock); +@@ -1566,7 +1594,10 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, + !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND)) + return -EPERM; + +- if (!nlk->portid) ++ /* No need for barriers here as we return to user-space without ++ * using any of the bound attributes. ++ */ ++ if (!nlk->bound) + err = netlink_autobind(sock); + + if (err == 0) { +@@ -2323,10 +2354,13 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) + dst_group = nlk->dst_group; + } + +- if (!nlk->portid) { ++ if (!nlk->bound) { + err = netlink_autobind(sock); + if (err) + goto out; ++ } else { ++ /* Ensure nlk is hashed and visible. */ ++ smp_rmb(); + } + + /* It's a really convoluted way for userland to ask for mmaped +diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h +index 89008405d6b4..14437d9b1965 100644 +--- a/net/netlink/af_netlink.h ++++ b/net/netlink/af_netlink.h +@@ -35,6 +35,7 @@ struct netlink_sock { + unsigned long state; + size_t max_recvmsg_len; + wait_queue_head_t wait; ++ bool bound; + bool cb_running; + struct netlink_callback cb; + struct mutex *cb_mutex; +@@ -59,6 +60,15 @@ static inline struct netlink_sock *nlk_sk(struct sock *sk) + return container_of(sk, struct netlink_sock, sk); + } + ++static inline bool netlink_skb_is_mmaped(const struct sk_buff *skb) ++{ ++#ifdef CONFIG_NETLINK_MMAP ++ return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; ++#else ++ return false; ++#endif /* CONFIG_NETLINK_MMAP */ ++} ++ + struct netlink_table { + struct rhashtable hash; + struct hlist_head mc_list; +diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c +index 096c6276e6b9..27e14962b504 100644 +--- a/net/openvswitch/datapath.c ++++ b/net/openvswitch/datapath.c +@@ -906,7 +906,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) + if (error) + goto err_kfree_flow; + +- ovs_flow_mask_key(&new_flow->key, &key, &mask); ++ ovs_flow_mask_key(&new_flow->key, &key, true, &mask); + + /* Extract flow identifier. */ + error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], +@@ -1033,7 +1033,7 @@ static struct sw_flow_actions *get_flow_actions(const struct nlattr *a, + struct sw_flow_key masked_key; + int error; + +- ovs_flow_mask_key(&masked_key, key, mask); ++ ovs_flow_mask_key(&masked_key, key, true, mask); + error = ovs_nla_copy_actions(a, &masked_key, &acts, log); + if (error) { + OVS_NLERR(log, +diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c +index 4613df8c8290..aa349514e4cb 100644 +--- a/net/openvswitch/flow_table.c ++++ b/net/openvswitch/flow_table.c +@@ -56,20 +56,21 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range) + } + + void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, +- const struct sw_flow_mask *mask) ++ bool full, const struct sw_flow_mask *mask) + { +- const long *m = (const long *)((const u8 *)&mask->key + +- mask->range.start); +- const long *s = (const long *)((const u8 *)src + +- mask->range.start); +- long *d = (long *)((u8 *)dst + mask->range.start); ++ int start = full ? 0 : mask->range.start; ++ int len = full ? sizeof *dst : range_n_bytes(&mask->range); ++ const long *m = (const long *)((const u8 *)&mask->key + start); ++ const long *s = (const long *)((const u8 *)src + start); ++ long *d = (long *)((u8 *)dst + start); + int i; + +- /* The memory outside of the 'mask->range' are not set since +- * further operations on 'dst' only uses contents within +- * 'mask->range'. ++ /* If 'full' is true then all of 'dst' is fully initialized. Otherwise, ++ * if 'full' is false the memory outside of the 'mask->range' is left ++ * uninitialized. This can be used as an optimization when further ++ * operations on 'dst' only use contents within 'mask->range'. + */ +- for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long)) ++ for (i = 0; i < len; i += sizeof(long)) + *d++ = *s++ & *m++; + } + +@@ -473,7 +474,7 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti, + u32 hash; + struct sw_flow_key masked_key; + +- ovs_flow_mask_key(&masked_key, unmasked, mask); ++ ovs_flow_mask_key(&masked_key, unmasked, false, mask); + hash = flow_hash(&masked_key, &mask->range); + head = find_bucket(ti, hash); + hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) { +diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h +index 616eda10d955..2dd9900f533d 100644 +--- a/net/openvswitch/flow_table.h ++++ b/net/openvswitch/flow_table.h +@@ -86,5 +86,5 @@ struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *, + bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *); + + void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, +- const struct sw_flow_mask *mask); ++ bool full, const struct sw_flow_mask *mask); + #endif /* flow_table.h */ +diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c +index 715e01e5910a..f23a3b68bba6 100644 +--- a/net/sched/cls_fw.c ++++ b/net/sched/cls_fw.c +@@ -33,7 +33,6 @@ + + struct fw_head { + u32 mask; +- bool mask_set; + struct fw_filter __rcu *ht[HTSIZE]; + struct rcu_head rcu; + }; +@@ -84,7 +83,7 @@ static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, + } + } + } else { +- /* old method */ ++ /* Old method: classify the packet using its skb mark. */ + if (id && (TC_H_MAJ(id) == 0 || + !(TC_H_MAJ(id ^ tp->q->handle)))) { + res->classid = id; +@@ -114,14 +113,9 @@ static unsigned long fw_get(struct tcf_proto *tp, u32 handle) + + static int fw_init(struct tcf_proto *tp) + { +- struct fw_head *head; +- +- head = kzalloc(sizeof(struct fw_head), GFP_KERNEL); +- if (head == NULL) +- return -ENOBUFS; +- +- head->mask_set = false; +- rcu_assign_pointer(tp->root, head); ++ /* We don't allocate fw_head here, because in the old method ++ * we don't need it at all. ++ */ + return 0; + } + +@@ -252,7 +246,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, + int err; + + if (!opt) +- return handle ? -EINVAL : 0; ++ return handle ? -EINVAL : 0; /* Succeed if it is old method. */ + + err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy); + if (err < 0) +@@ -302,11 +296,17 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, + if (!handle) + return -EINVAL; + +- if (!head->mask_set) { +- head->mask = 0xFFFFFFFF; ++ if (!head) { ++ u32 mask = 0xFFFFFFFF; + if (tb[TCA_FW_MASK]) +- head->mask = nla_get_u32(tb[TCA_FW_MASK]); +- head->mask_set = true; ++ mask = nla_get_u32(tb[TCA_FW_MASK]); ++ ++ head = kzalloc(sizeof(*head), GFP_KERNEL); ++ if (!head) ++ return -ENOBUFS; ++ head->mask = mask; ++ ++ rcu_assign_pointer(tp->root, head); + } + + f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); +diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c +index cab9e9b43967..4fbb67430ce4 100644 +--- a/net/sched/cls_u32.c ++++ b/net/sched/cls_u32.c +@@ -490,6 +490,19 @@ static bool u32_destroy(struct tcf_proto *tp, bool force) + return false; + } + } ++ ++ if (tp_c->refcnt > 1) ++ return false; ++ ++ if (tp_c->refcnt == 1) { ++ struct tc_u_hnode *ht; ++ ++ for (ht = rtnl_dereference(tp_c->hlist); ++ ht; ++ ht = rtnl_dereference(ht->next)) ++ if (!ht_empty(ht)) ++ return false; ++ } + } + + if (root_ht && --root_ht->refcnt == 0) +diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c +index 53b7acde9aa3..e13c3c3ea4ac 100644 +--- a/net/sctp/protocol.c ++++ b/net/sctp/protocol.c +@@ -1166,7 +1166,7 @@ static void sctp_v4_del_protocol(void) + unregister_inetaddr_notifier(&sctp_inetaddr_notifier); + } + +-static int __net_init sctp_net_init(struct net *net) ++static int __net_init sctp_defaults_init(struct net *net) + { + int status; + +@@ -1259,12 +1259,6 @@ static int __net_init sctp_net_init(struct net *net) + + sctp_dbg_objcnt_init(net); + +- /* Initialize the control inode/socket for handling OOTB packets. */ +- if ((status = sctp_ctl_sock_init(net))) { +- pr_err("Failed to initialize the SCTP control sock\n"); +- goto err_ctl_sock_init; +- } +- + /* Initialize the local address list. */ + INIT_LIST_HEAD(&net->sctp.local_addr_list); + spin_lock_init(&net->sctp.local_addr_lock); +@@ -1280,9 +1274,6 @@ static int __net_init sctp_net_init(struct net *net) + + return 0; + +-err_ctl_sock_init: +- sctp_dbg_objcnt_exit(net); +- sctp_proc_exit(net); + err_init_proc: + cleanup_sctp_mibs(net); + err_init_mibs: +@@ -1291,15 +1282,12 @@ err_sysctl_register: + return status; + } + +-static void __net_exit sctp_net_exit(struct net *net) ++static void __net_exit sctp_defaults_exit(struct net *net) + { + /* Free the local address list */ + sctp_free_addr_wq(net); + sctp_free_local_addr_list(net); + +- /* Free the control endpoint. */ +- inet_ctl_sock_destroy(net->sctp.ctl_sock); +- + sctp_dbg_objcnt_exit(net); + + sctp_proc_exit(net); +@@ -1307,9 +1295,32 @@ static void __net_exit sctp_net_exit(struct net *net) + sctp_sysctl_net_unregister(net); + } + +-static struct pernet_operations sctp_net_ops = { +- .init = sctp_net_init, +- .exit = sctp_net_exit, ++static struct pernet_operations sctp_defaults_ops = { ++ .init = sctp_defaults_init, ++ .exit = sctp_defaults_exit, ++}; ++ ++static int __net_init sctp_ctrlsock_init(struct net *net) ++{ ++ int status; ++ ++ /* Initialize the control inode/socket for handling OOTB packets. */ ++ status = sctp_ctl_sock_init(net); ++ if (status) ++ pr_err("Failed to initialize the SCTP control sock\n"); ++ ++ return status; ++} ++ ++static void __net_init sctp_ctrlsock_exit(struct net *net) ++{ ++ /* Free the control endpoint. */ ++ inet_ctl_sock_destroy(net->sctp.ctl_sock); ++} ++ ++static struct pernet_operations sctp_ctrlsock_ops = { ++ .init = sctp_ctrlsock_init, ++ .exit = sctp_ctrlsock_exit, + }; + + /* Initialize the universe into something sensible. */ +@@ -1442,8 +1453,11 @@ static __init int sctp_init(void) + sctp_v4_pf_init(); + sctp_v6_pf_init(); + +- status = sctp_v4_protosw_init(); ++ status = register_pernet_subsys(&sctp_defaults_ops); ++ if (status) ++ goto err_register_defaults; + ++ status = sctp_v4_protosw_init(); + if (status) + goto err_protosw_init; + +@@ -1451,9 +1465,9 @@ static __init int sctp_init(void) + if (status) + goto err_v6_protosw_init; + +- status = register_pernet_subsys(&sctp_net_ops); ++ status = register_pernet_subsys(&sctp_ctrlsock_ops); + if (status) +- goto err_register_pernet_subsys; ++ goto err_register_ctrlsock; + + status = sctp_v4_add_protocol(); + if (status) +@@ -1469,12 +1483,14 @@ out: + err_v6_add_protocol: + sctp_v4_del_protocol(); + err_add_protocol: +- unregister_pernet_subsys(&sctp_net_ops); +-err_register_pernet_subsys: ++ unregister_pernet_subsys(&sctp_ctrlsock_ops); ++err_register_ctrlsock: + sctp_v6_protosw_exit(); + err_v6_protosw_init: + sctp_v4_protosw_exit(); + err_protosw_init: ++ unregister_pernet_subsys(&sctp_defaults_ops); ++err_register_defaults: + sctp_v4_pf_exit(); + sctp_v6_pf_exit(); + sctp_sysctl_unregister(); +@@ -1507,12 +1523,14 @@ static __exit void sctp_exit(void) + sctp_v6_del_protocol(); + sctp_v4_del_protocol(); + +- unregister_pernet_subsys(&sctp_net_ops); ++ unregister_pernet_subsys(&sctp_ctrlsock_ops); + + /* Free protosw registrations */ + sctp_v6_protosw_exit(); + sctp_v4_protosw_exit(); + ++ unregister_pernet_subsys(&sctp_defaults_ops); ++ + /* Unregister with socket layer. */ + sctp_v6_pf_exit(); + sctp_v4_pf_exit();
