mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-27 01:02:19 +00:00
1532 lines
46 KiB
Diff
1532 lines
46 KiB
Diff
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 3578b4426ecf..a6edbb11a69a 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,6 +1,6 @@
|
|
VERSION = 4
|
|
PATCHLEVEL = 2
|
|
-SUBLEVEL = 2
|
|
+SUBLEVEL = 3
|
|
EXTRAVERSION =
|
|
NAME = Hurr durr I'ma sheep
|
|
|
|
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
|
|
index 965d1afb0eaa..5cb13ca3a3ac 100644
|
|
--- a/drivers/block/zram/zcomp.c
|
|
+++ b/drivers/block/zram/zcomp.c
|
|
@@ -330,12 +330,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)
|
|
@@ -347,12 +349,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 079897b3a955..9d56515f4c4d 100644
|
|
--- a/drivers/net/dsa/bcm_sf2.c
|
|
+++ b/drivers/net/dsa/bcm_sf2.c
|
|
@@ -418,7 +418,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 {
|
|
@@ -432,6 +432,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;
|
|
}
|
|
|
|
@@ -507,7 +509,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:
|
|
@@ -531,10 +533,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__);
|
|
@@ -901,15 +905,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;
|
|
|
|
@@ -944,18 +944,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/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
|
|
index 561342466076..26ec2fbfaa89 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx.c
|
|
+++ b/drivers/net/dsa/mv88e6xxx.c
|
|
@@ -1387,6 +1387,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
|
|
reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
|
|
if (dsa_is_cpu_port(ds, port) ||
|
|
ds->dsa_port_mask & (1 << port)) {
|
|
+ reg &= ~PORT_PCS_CTRL_UNFORCED;
|
|
reg |= PORT_PCS_CTRL_FORCE_LINK |
|
|
PORT_PCS_CTRL_LINK_UP |
|
|
PORT_PCS_CTRL_DUPLEX_FULL |
|
|
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 b349e6f36ea7..de63266de16b 100644
|
|
--- a/drivers/net/ethernet/freescale/fec_main.c
|
|
+++ b/drivers/net/ethernet/freescale/fec_main.c
|
|
@@ -1402,6 +1402,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 62e48bc0cb23..09ec32e33076 100644
|
|
--- a/drivers/net/ethernet/marvell/mvneta.c
|
|
+++ b/drivers/net/ethernet/marvell/mvneta.c
|
|
@@ -1479,6 +1479,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
|
struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq);
|
|
struct sk_buff *skb;
|
|
unsigned char *data;
|
|
+ dma_addr_t phys_addr;
|
|
u32 rx_status;
|
|
int rx_bytes, err;
|
|
|
|
@@ -1486,6 +1487,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
|
rx_status = rx_desc->status;
|
|
rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE);
|
|
data = (unsigned char *)rx_desc->buf_cookie;
|
|
+ phys_addr = rx_desc->buf_phys_addr;
|
|
|
|
if (!mvneta_rxq_desc_is_first_last(rx_status) ||
|
|
(rx_status & MVNETA_RXD_ERR_SUMMARY)) {
|
|
@@ -1534,7 +1536,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
|
if (!skb)
|
|
goto err_drop_frame;
|
|
|
|
- dma_unmap_single(dev->dev.parent, rx_desc->buf_phys_addr,
|
|
+ dma_unmap_single(dev->dev.parent, phys_addr,
|
|
MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE);
|
|
|
|
rcvd_pkts++;
|
|
@@ -3027,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
|
|
@@ -3062,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.
|
|
@@ -3086,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 9c145dddd717..4f95fa7b594d 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
|
|
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
|
|
@@ -1250,8 +1250,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/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
|
|
index 29c2a017a450..a408977a531a 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
|
|
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
|
|
@@ -2654,9 +2654,14 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
|
|
|
|
if (msi_x) {
|
|
int nreq = dev->caps.num_ports * num_online_cpus() + 1;
|
|
+ bool shared_ports = false;
|
|
|
|
nreq = min_t(int, dev->caps.num_eqs - dev->caps.reserved_eqs,
|
|
nreq);
|
|
+ if (nreq > MAX_MSIX) {
|
|
+ nreq = MAX_MSIX;
|
|
+ shared_ports = true;
|
|
+ }
|
|
|
|
entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL);
|
|
if (!entries)
|
|
@@ -2679,6 +2684,9 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
|
|
bitmap_zero(priv->eq_table.eq[MLX4_EQ_ASYNC].actv_ports.ports,
|
|
dev->caps.num_ports);
|
|
|
|
+ if (MLX4_IS_LEGACY_EQ_MODE(dev->caps))
|
|
+ shared_ports = true;
|
|
+
|
|
for (i = 0; i < dev->caps.num_comp_vectors + 1; i++) {
|
|
if (i == MLX4_EQ_ASYNC)
|
|
continue;
|
|
@@ -2686,7 +2694,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
|
|
priv->eq_table.eq[i].irq =
|
|
entries[i + 1 - !!(i > MLX4_EQ_ASYNC)].vector;
|
|
|
|
- if (MLX4_IS_LEGACY_EQ_MODE(dev->caps)) {
|
|
+ if (shared_ports) {
|
|
bitmap_fill(priv->eq_table.eq[i].actv_ports.ports,
|
|
dev->caps.num_ports);
|
|
/* We don't set affinity hint when there
|
|
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
|
|
index edd77342773a..248478c6f6e4 100644
|
|
--- a/drivers/net/macvtap.c
|
|
+++ b/drivers/net/macvtap.c
|
|
@@ -1111,10 +1111,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 d7a65247f952..99d9bc19c94a 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/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
|
|
index 46a14cbb0215..02a4615b65f8 100644
|
|
--- a/drivers/net/phy/mdio_bus.c
|
|
+++ b/drivers/net/phy/mdio_bus.c
|
|
@@ -303,12 +303,12 @@ void mdiobus_unregister(struct mii_bus *bus)
|
|
BUG_ON(bus->state != MDIOBUS_REGISTERED);
|
|
bus->state = MDIOBUS_UNREGISTERED;
|
|
|
|
- device_del(&bus->dev);
|
|
for (i = 0; i < PHY_MAX_ADDR; i++) {
|
|
if (bus->phy_map[i])
|
|
device_unregister(&bus->phy_map[i]->dev);
|
|
bus->phy_map[i] = NULL;
|
|
}
|
|
+ device_del(&bus->dev);
|
|
}
|
|
EXPORT_SYMBOL(mdiobus_unregister);
|
|
|
|
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
|
|
index fa8f5046afe9..487be20b6b12 100644
|
|
--- a/drivers/net/ppp/ppp_generic.c
|
|
+++ b/drivers/net/ppp/ppp_generic.c
|
|
@@ -2742,6 +2742,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit,
|
|
*/
|
|
dev_net_set(dev, net);
|
|
|
|
+ rtnl_lock();
|
|
mutex_lock(&pn->all_ppp_mutex);
|
|
|
|
if (unit < 0) {
|
|
@@ -2772,7 +2773,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit,
|
|
ppp->file.index = unit;
|
|
sprintf(dev->name, "ppp%d", unit);
|
|
|
|
- ret = register_netdev(dev);
|
|
+ ret = register_netdevice(dev);
|
|
if (ret != 0) {
|
|
unit_put(&pn->units_idr, unit);
|
|
netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n",
|
|
@@ -2784,6 +2785,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit,
|
|
|
|
atomic_inc(&ppp_unit_count);
|
|
mutex_unlock(&pn->all_ppp_mutex);
|
|
+ rtnl_unlock();
|
|
|
|
*retp = 0;
|
|
return ppp;
|
|
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
|
|
index fdc60db60829..7c8c23cc6896 100644
|
|
--- a/drivers/of/of_mdio.c
|
|
+++ b/drivers/of/of_mdio.c
|
|
@@ -266,7 +266,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");
|
|
@@ -275,6 +276,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)))
|
|
@@ -289,8 +294,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 1285eaf5dc22..03cdb9e18d57 100644
|
|
--- a/net/bridge/br_multicast.c
|
|
+++ b/net/bridge/br_multicast.c
|
|
@@ -991,7 +991,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);
|
|
@@ -1052,7 +1052,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/rtnetlink.c b/net/core/rtnetlink.c
|
|
index dc004b1e1f85..0861018be708 100644
|
|
--- a/net/core/rtnetlink.c
|
|
+++ b/net/core/rtnetlink.c
|
|
@@ -3021,6 +3021,7 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
|
|
u32 portid = NETLINK_CB(cb->skb).portid;
|
|
u32 seq = cb->nlh->nlmsg_seq;
|
|
u32 filter_mask = 0;
|
|
+ int err;
|
|
|
|
if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) {
|
|
struct nlattr *extfilt;
|
|
@@ -3041,20 +3042,25 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
|
|
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
|
|
|
|
if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
|
|
- if (idx >= cb->args[0] &&
|
|
- br_dev->netdev_ops->ndo_bridge_getlink(
|
|
- skb, portid, seq, dev, filter_mask,
|
|
- NLM_F_MULTI) < 0)
|
|
- break;
|
|
+ if (idx >= cb->args[0]) {
|
|
+ err = br_dev->netdev_ops->ndo_bridge_getlink(
|
|
+ skb, portid, seq, dev,
|
|
+ filter_mask, NLM_F_MULTI);
|
|
+ if (err < 0 && err != -EOPNOTSUPP)
|
|
+ break;
|
|
+ }
|
|
idx++;
|
|
}
|
|
|
|
if (ops->ndo_bridge_getlink) {
|
|
- if (idx >= cb->args[0] &&
|
|
- ops->ndo_bridge_getlink(skb, portid, seq, dev,
|
|
- filter_mask,
|
|
- NLM_F_MULTI) < 0)
|
|
- break;
|
|
+ if (idx >= cb->args[0]) {
|
|
+ err = ops->ndo_bridge_getlink(skb, portid,
|
|
+ seq, dev,
|
|
+ filter_mask,
|
|
+ NLM_F_MULTI);
|
|
+ if (err < 0 && err != -EOPNOTSUPP)
|
|
+ break;
|
|
+ }
|
|
idx++;
|
|
}
|
|
}
|
|
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
|
|
index d79866c5f8bc..817622f3dbb7 100644
|
|
--- a/net/core/sock_diag.c
|
|
+++ b/net/core/sock_diag.c
|
|
@@ -90,6 +90,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 b1c218df2c85..b7dedd9d36d8 100644
|
|
--- a/net/ipv4/tcp_output.c
|
|
+++ b/net/ipv4/tcp_output.c
|
|
@@ -2898,6 +2898,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/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 d15586490cec..00b64d402a57 100644
|
|
--- a/net/ipv6/route.c
|
|
+++ b/net/ipv6/route.c
|
|
@@ -1727,7 +1727,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;
|
|
@@ -1735,7 +1735,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)
|
|
@@ -1941,6 +1940,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;
|
|
@@ -1948,14 +1973,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;
|
|
}
|
|
|
|
@@ -2727,19 +2750,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)
|
|
@@ -2755,22 +2837,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:
|
|
@@ -2780,6 +2872,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);
|
|
}
|
|
|
|
@@ -2796,7 +2945,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);
|
|
}
|
|
@@ -2811,7 +2960,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 a774985489e2..0857f7243797 100644
|
|
--- a/net/netlink/af_netlink.c
|
|
+++ b/net/netlink/af_netlink.c
|
|
@@ -124,6 +124,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))
|
|
@@ -205,7 +223,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);
|
|
@@ -278,11 +300,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;
|
|
@@ -834,7 +851,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
|
|
@@ -1082,8 +1098,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;
|
|
@@ -1103,10 +1119,14 @@ static int netlink_insert(struct sock *sk, u32 portid)
|
|
err = -EOVERFLOW;
|
|
if (err == -EEXIST)
|
|
err = -EADDRINUSE;
|
|
- nlk_sk(sk)->portid = 0;
|
|
sock_put(sk);
|
|
+ goto err;
|
|
}
|
|
|
|
+ /* We need to ensure that the socket is hashed and visible. */
|
|
+ smp_wmb();
|
|
+ nlk_sk(sk)->bound = portid;
|
|
+
|
|
err:
|
|
release_sock(sk);
|
|
return err;
|
|
@@ -1491,6 +1511,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;
|
|
@@ -1507,9 +1528,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;
|
|
@@ -1525,7 +1551,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);
|
|
@@ -1573,7 +1602,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) {
|
|
@@ -2391,10 +2423,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 ff8c4a4c1609..ff332d1b94bc 100644
|
|
--- a/net/openvswitch/datapath.c
|
|
+++ b/net/openvswitch/datapath.c
|
|
@@ -920,7 +920,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],
|
|
@@ -1047,7 +1047,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 65523948fb95..b5c3bba87fc8 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/packet/af_packet.c b/net/packet/af_packet.c
|
|
index ed458b315ef4..7851b1222a36 100644
|
|
--- a/net/packet/af_packet.c
|
|
+++ b/net/packet/af_packet.c
|
|
@@ -229,6 +229,8 @@ struct packet_skb_cb {
|
|
} sa;
|
|
};
|
|
|
|
+#define vio_le() virtio_legacy_is_little_endian()
|
|
+
|
|
#define PACKET_SKB_CB(__skb) ((struct packet_skb_cb *)((__skb)->cb))
|
|
|
|
#define GET_PBDQC_FROM_RB(x) ((struct tpacket_kbdq_core *)(&(x)->prb_bdqc))
|
|
@@ -2561,15 +2563,15 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
|
goto out_unlock;
|
|
|
|
if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
|
|
- (__virtio16_to_cpu(false, vnet_hdr.csum_start) +
|
|
- __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2 >
|
|
- __virtio16_to_cpu(false, vnet_hdr.hdr_len)))
|
|
- vnet_hdr.hdr_len = __cpu_to_virtio16(false,
|
|
- __virtio16_to_cpu(false, vnet_hdr.csum_start) +
|
|
- __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2);
|
|
+ (__virtio16_to_cpu(vio_le(), vnet_hdr.csum_start) +
|
|
+ __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset) + 2 >
|
|
+ __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len)))
|
|
+ vnet_hdr.hdr_len = __cpu_to_virtio16(vio_le(),
|
|
+ __virtio16_to_cpu(vio_le(), vnet_hdr.csum_start) +
|
|
+ __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset) + 2);
|
|
|
|
err = -EINVAL;
|
|
- if (__virtio16_to_cpu(false, vnet_hdr.hdr_len) > len)
|
|
+ if (__virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len) > len)
|
|
goto out_unlock;
|
|
|
|
if (vnet_hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
|
|
@@ -2612,7 +2614,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
|
hlen = LL_RESERVED_SPACE(dev);
|
|
tlen = dev->needed_tailroom;
|
|
skb = packet_alloc_skb(sk, hlen + tlen, hlen, len,
|
|
- __virtio16_to_cpu(false, vnet_hdr.hdr_len),
|
|
+ __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len),
|
|
msg->msg_flags & MSG_DONTWAIT, &err);
|
|
if (skb == NULL)
|
|
goto out_unlock;
|
|
@@ -2659,8 +2661,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
|
|
|
if (po->has_vnet_hdr) {
|
|
if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
|
|
- u16 s = __virtio16_to_cpu(false, vnet_hdr.csum_start);
|
|
- u16 o = __virtio16_to_cpu(false, vnet_hdr.csum_offset);
|
|
+ u16 s = __virtio16_to_cpu(vio_le(), vnet_hdr.csum_start);
|
|
+ u16 o = __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset);
|
|
if (!skb_partial_csum_set(skb, s, o)) {
|
|
err = -EINVAL;
|
|
goto out_free;
|
|
@@ -2668,7 +2670,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
|
}
|
|
|
|
skb_shinfo(skb)->gso_size =
|
|
- __virtio16_to_cpu(false, vnet_hdr.gso_size);
|
|
+ __virtio16_to_cpu(vio_le(), vnet_hdr.gso_size);
|
|
skb_shinfo(skb)->gso_type = gso_type;
|
|
|
|
/* Header must be checked, and gso_segs computed. */
|
|
@@ -3042,9 +3044,9 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
|
|
|
/* This is a hint as to how much should be linear. */
|
|
vnet_hdr.hdr_len =
|
|
- __cpu_to_virtio16(false, skb_headlen(skb));
|
|
+ __cpu_to_virtio16(vio_le(), skb_headlen(skb));
|
|
vnet_hdr.gso_size =
|
|
- __cpu_to_virtio16(false, sinfo->gso_size);
|
|
+ __cpu_to_virtio16(vio_le(), sinfo->gso_size);
|
|
if (sinfo->gso_type & SKB_GSO_TCPV4)
|
|
vnet_hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
|
|
else if (sinfo->gso_type & SKB_GSO_TCPV6)
|
|
@@ -3062,9 +3064,9 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
|
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
|
|
- vnet_hdr.csum_start = __cpu_to_virtio16(false,
|
|
+ vnet_hdr.csum_start = __cpu_to_virtio16(vio_le(),
|
|
skb_checksum_start_offset(skb));
|
|
- vnet_hdr.csum_offset = __cpu_to_virtio16(false,
|
|
+ vnet_hdr.csum_offset = __cpu_to_virtio16(vio_le(),
|
|
skb->csum_offset);
|
|
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
|
|
vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID;
|
|
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/sctp/protocol.c b/net/sctp/protocol.c
|
|
index 59e80356672b..3ac604f96da0 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();
|