* [PATCH v7 net-next 1/5] net: core: page_pool: add user refcnt and reintroduce page_pool_destroy
From: Ivan Khoronzhuk @ 2019-07-04 23:14 UTC (permalink / raw)
To: grygorii.strashko, hawk, davem
Cc: ast, linux-kernel, linux-omap, xdp-newbies, ilias.apalodimas,
netdev, daniel, jakub.kicinski, john.fastabend, Ivan Khoronzhuk,
Jesper Dangaard Brouer
In-Reply-To: <20190704231406.27083-1-ivan.khoronzhuk@linaro.org>
Jesper recently removed page_pool_destroy() (from driver invocation)
and moved shutdown and free of page_pool into xdp_rxq_info_unreg(),
in-order to handle in-flight packets/pages. This created an asymmetry
in drivers create/destroy pairs.
This patch reintroduce page_pool_destroy and add page_pool user
refcnt. This serves the purpose to simplify drivers error handling as
driver now drivers always calls page_pool_destroy() and don't need to
track if xdp_rxq_info_reg_mem_model() was unsuccessful.
This could be used for a special cases where a single RX-queue (with a
single page_pool) provides packets for two net_device'es, and thus
needs to register the same page_pool twice with two xdp_rxq_info
structures.
This patch is primarily to ease API usage for drivers. The recently
merged netsec driver, actually have a bug in this area, which is
solved by this API change.
This patch is a modified version of Ivan Khoronzhu's original patch.
Link: https://lore.kernel.org/netdev/20190625175948.24771-2-ivan.khoronzhuk@linaro.org/
Fixes: 5c67bf0ec4d0 ("net: netsec: Use page_pool API")
Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
.../net/ethernet/mellanox/mlx5/core/en_main.c | 4 +--
drivers/net/ethernet/socionext/netsec.c | 8 ++----
include/net/page_pool.h | 25 +++++++++++++++++++
net/core/page_pool.c | 8 ++++++
net/core/xdp.c | 3 +++
5 files changed, 40 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 2f9093ba82aa..ac882b2341d0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -575,8 +575,6 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
}
err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq,
MEM_TYPE_PAGE_POOL, rq->page_pool);
- if (err)
- page_pool_free(rq->page_pool);
}
if (err)
goto err_free;
@@ -644,6 +642,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
if (rq->xdp_prog)
bpf_prog_put(rq->xdp_prog);
xdp_rxq_info_unreg(&rq->xdp_rxq);
+ page_pool_destroy(rq->page_pool);
mlx5_wq_destroy(&rq->wq_ctrl);
return err;
@@ -678,6 +677,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq)
}
xdp_rxq_info_unreg(&rq->xdp_rxq);
+ page_pool_destroy(rq->page_pool);
mlx5_wq_destroy(&rq->wq_ctrl);
}
diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c
index 5544a722543f..43ab0ce90704 100644
--- a/drivers/net/ethernet/socionext/netsec.c
+++ b/drivers/net/ethernet/socionext/netsec.c
@@ -1210,15 +1210,11 @@ static void netsec_uninit_pkt_dring(struct netsec_priv *priv, int id)
}
}
- /* Rx is currently using page_pool
- * since the pool is created during netsec_setup_rx_dring(), we need to
- * free the pool manually if the registration failed
- */
+ /* Rx is currently using page_pool */
if (id == NETSEC_RING_RX) {
if (xdp_rxq_info_is_reg(&dring->xdp_rxq))
xdp_rxq_info_unreg(&dring->xdp_rxq);
- else
- page_pool_free(dring->page_pool);
+ page_pool_destroy(dring->page_pool);
}
memset(dring->desc, 0, sizeof(struct netsec_desc) * DESC_NUM);
diff --git a/include/net/page_pool.h b/include/net/page_pool.h
index ee9c871d2043..2cbcdbdec254 100644
--- a/include/net/page_pool.h
+++ b/include/net/page_pool.h
@@ -101,6 +101,12 @@ struct page_pool {
struct ptr_ring ring;
atomic_t pages_state_release_cnt;
+
+ /* A page_pool is strictly tied to a single RX-queue being
+ * protected by NAPI, due to above pp_alloc_cache. This
+ * refcnt serves purpose is to simplify drivers error handling.
+ */
+ refcount_t user_cnt;
};
struct page *page_pool_alloc_pages(struct page_pool *pool, gfp_t gfp);
@@ -134,6 +140,15 @@ static inline void page_pool_free(struct page_pool *pool)
#endif
}
+/* Drivers use this instead of page_pool_free */
+static inline void page_pool_destroy(struct page_pool *pool)
+{
+ if (!pool)
+ return;
+
+ page_pool_free(pool);
+}
+
/* Never call this directly, use helpers below */
void __page_pool_put_page(struct page_pool *pool,
struct page *page, bool allow_direct);
@@ -201,4 +216,14 @@ static inline bool is_page_pool_compiled_in(void)
#endif
}
+static inline void page_pool_get(struct page_pool *pool)
+{
+ refcount_inc(&pool->user_cnt);
+}
+
+static inline bool page_pool_put(struct page_pool *pool)
+{
+ return refcount_dec_and_test(&pool->user_cnt);
+}
+
#endif /* _NET_PAGE_POOL_H */
diff --git a/net/core/page_pool.c b/net/core/page_pool.c
index b366f59885c1..3272dc7a8c81 100644
--- a/net/core/page_pool.c
+++ b/net/core/page_pool.c
@@ -49,6 +49,9 @@ static int page_pool_init(struct page_pool *pool,
atomic_set(&pool->pages_state_release_cnt, 0);
+ /* Driver calling page_pool_create() also call page_pool_destroy() */
+ refcount_set(&pool->user_cnt, 1);
+
if (pool->p.flags & PP_FLAG_DMA_MAP)
get_device(pool->p.dev);
@@ -70,6 +73,7 @@ struct page_pool *page_pool_create(const struct page_pool_params *params)
kfree(pool);
return ERR_PTR(err);
}
+
return pool;
}
EXPORT_SYMBOL(page_pool_create);
@@ -356,6 +360,10 @@ static void __warn_in_flight(struct page_pool *pool)
void __page_pool_free(struct page_pool *pool)
{
+ /* Only last user actually free/release resources */
+ if (!page_pool_put(pool))
+ return;
+
WARN(pool->alloc.count, "API usage violation");
WARN(!ptr_ring_empty(&pool->ring), "ptr_ring is not empty");
diff --git a/net/core/xdp.c b/net/core/xdp.c
index 829377cc83db..d7bf62ffbb5e 100644
--- a/net/core/xdp.c
+++ b/net/core/xdp.c
@@ -370,6 +370,9 @@ int xdp_rxq_info_reg_mem_model(struct xdp_rxq_info *xdp_rxq,
goto err;
}
+ if (type == MEM_TYPE_PAGE_POOL)
+ page_pool_get(xdp_alloc->page_pool);
+
mutex_unlock(&mem_id_lock);
trace_mem_connect(xdp_alloc, xdp_rxq);
--
2.17.1
^ permalink raw reply related
* [PATCH v7 net-next 0/5] net: ethernet: ti: cpsw: Add XDP support
From: Ivan Khoronzhuk @ 2019-07-04 23:14 UTC (permalink / raw)
To: grygorii.strashko, hawk, davem
Cc: ast, linux-kernel, linux-omap, xdp-newbies, ilias.apalodimas,
netdev, daniel, jakub.kicinski, john.fastabend, Ivan Khoronzhuk
This patchset adds XDP support for TI cpsw driver and base it on
page_pool allocator. It was verified on af_xdp socket drop,
af_xdp l2f, ebpf XDP_DROP, XDP_REDIRECT, XDP_PASS, XDP_TX.
It was verified with following configs enabled:
CONFIG_JIT=y
CONFIG_BPFILTER=y
CONFIG_BPF_SYSCALL=y
CONFIG_XDP_SOCKETS=y
CONFIG_BPF_EVENTS=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_JIT=y
CONFIG_CGROUP_BPF=y
Link on previous v6:
https://lkml.org/lkml/2019/7/3/243
Also regular tests with iperf2 were done in order to verify impact on
regular netstack performance, compared with base commit:
https://pastebin.com/JSMT0iZ4
v6..v7:
- rolled back to v4 solution but with small modification
- picked up patch:
https://www.spinics.net/lists/netdev/msg583145.html
- added changes related to netsec fix and cpsw
v5..v6:
- do changes that is rx_dev while redirect/flush cycle is kept the same
- dropped net: ethernet: ti: davinci_cpdma: return handler status
- other changes desc in patches
v4..v5:
- added two plreliminary patches:
net: ethernet: ti: davinci_cpdma: allow desc split while down
net: ethernet: ti: cpsw_ethtool: allow res split while down
- added xdp alocator refcnt on xdp level, avoiding page pool refcnt
- moved flush status as separate argument for cpdma_chan_process
- reworked cpsw code according to last changes to allocator
- added missed statistic counter
v3..v4:
- added page pool user counter
- use same pool for ndevs in dual mac
- restructured page pool create/destroy according to the last changes in API
v2..v3:
- each rxq and ndev has its own page pool
v1..v2:
- combined xdp_xmit functions
- used page allocation w/o refcnt juggle
- unmapped page for skb netstack
- moved rxq/page pool allocation to open/close pair
- added several preliminary patches:
net: page_pool: add helper function to retrieve dma addresses
net: page_pool: add helper function to unmap dma addresses
net: ethernet: ti: cpsw: use cpsw as drv data
net: ethernet: ti: cpsw_ethtool: simplify slave loops
Ivan Khoronzhuk (5):
net: core: page_pool: add user refcnt and reintroduce
page_pool_destroy
net: ethernet: ti: davinci_cpdma: add dma mapped submit
net: ethernet: ti: davinci_cpdma: allow desc split while down
net: ethernet: ti: cpsw_ethtool: allow res split while down
net: ethernet: ti: cpsw: add XDP support
.../net/ethernet/mellanox/mlx5/core/en_main.c | 4 +-
drivers/net/ethernet/socionext/netsec.c | 8 +-
drivers/net/ethernet/ti/Kconfig | 1 +
drivers/net/ethernet/ti/cpsw.c | 502 ++++++++++++++++--
drivers/net/ethernet/ti/cpsw_ethtool.c | 57 +-
drivers/net/ethernet/ti/cpsw_priv.h | 7 +
drivers/net/ethernet/ti/davinci_cpdma.c | 106 +++-
drivers/net/ethernet/ti/davinci_cpdma.h | 7 +-
include/net/page_pool.h | 25 +
net/core/page_pool.c | 8 +
net/core/xdp.c | 3 +
11 files changed, 640 insertions(+), 88 deletions(-)
--
2.17.1
^ permalink raw reply
* Re: bug: tpacket_snd can cause data corruption
From: Willem de Bruijn @ 2019-07-04 22:59 UTC (permalink / raw)
To: Frank de Brabander
Cc: David S . Miller, Willem de Bruijn, Network Development
In-Reply-To: <d32bc4b8-b547-1d78-e245-2ec51df19c77@gmail.com>
> > Can you reproduce the issue when running the modified test in a
> > network namespace (./in_netns.sh ./txring_overwrite)?
> But even when running the test with ./in_netns.sh it shows
> "wrong pattern", this time without length mismatches:
>
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
> wrong pattern: 0x62 != 0x61
>
> As already mentioned, it seems to trigger mainly (only ?) when
> an USB device is connected. The PC I'm testing this on has an
> USB hub with many ports and connected devices. When connecting
> this USB hub, the amount of "wrong pattern" errors that are
> shown seems to correlate to the amount of new devices
> that the kernel should detect. Connecting in a single USB device
> also triggers the error, but not on every attempt.
>
> Unfortunately have not found any other way to force the
> error to trigger. E.g. running stress-ng to generate CPU load or
> timer interrupts does not seem to have any impact.
Interesting, thanks for testing. No exact idea so far. The USB devices
are not necessarily network devices, I suppose? I don't immediately
have a setup to test the usb hotplug, so cannot yet reproduce the bug.
^ permalink raw reply
* [PATCH v3 0/4] net: dsa: Add Vitesse VSC73xx parallel mode
From: Pawel Dembicki @ 2019-07-04 22:29 UTC (permalink / raw)
Cc: Pawel Dembicki, linus.walleij, Andrew Lunn, Vivien Didelot,
Florian Fainelli, David S. Miller, Rob Herring, Mark Rutland,
netdev, devicetree, linux-kernel
Main goal of this patch series is to add support for CPU attached parallel
bus in Vitesse VSC73xx switches. Existing driver supports only SPI mode.
Second change is needed for devices in unmanaged state.
V3:
- fix commit messages and descriptions about memory-mapped I/O mode
V2:
- drop changes in compatible strings
- make changes less invasive
- drop mutex in platform part and move mutex from core to spi part
- fix indentation
- fix devm_ioremap_resource result check
- add cover letter
Pawel Dembicki (4):
net: dsa: Change DT bindings for Vitesse VSC73xx switches
net: dsa: vsc73xx: Split vsc73xx driver
net: dsa: vsc73xx: add support for parallel mode
net: dsa: vsc73xx: Assert reset if iCPU is enabled
.../bindings/net/dsa/vitesse,vsc73xx.txt | 58 ++++-
drivers/net/dsa/Kconfig | 20 +-
drivers/net/dsa/Makefile | 4 +-
...tesse-vsc73xx.c => vitesse-vsc73xx-core.c} | 206 +++---------------
drivers/net/dsa/vitesse-vsc73xx-platform.c | 164 ++++++++++++++
drivers/net/dsa/vitesse-vsc73xx-spi.c | 203 +++++++++++++++++
drivers/net/dsa/vitesse-vsc73xx.h | 29 +++
7 files changed, 499 insertions(+), 185 deletions(-)
rename drivers/net/dsa/{vitesse-vsc73xx.c => vitesse-vsc73xx-core.c} (90%)
create mode 100644 drivers/net/dsa/vitesse-vsc73xx-platform.c
create mode 100644 drivers/net/dsa/vitesse-vsc73xx-spi.c
create mode 100644 drivers/net/dsa/vitesse-vsc73xx.h
--
2.20.1
^ permalink raw reply
* [PATCH v3 1/4] net: dsa: Change DT bindings for Vitesse VSC73xx switches
From: Pawel Dembicki @ 2019-07-04 22:29 UTC (permalink / raw)
Cc: Pawel Dembicki, linus.walleij, Andrew Lunn, Vivien Didelot,
Florian Fainelli, David S. Miller, Rob Herring, Mark Rutland,
netdev, devicetree, linux-kernel
In-Reply-To: <20190704222907.2888-1-paweldembicki@gmail.com>
This commit introduce how to use vsc73xx platform driver.
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
.../bindings/net/dsa/vitesse,vsc73xx.txt | 58 +++++++++++++++++--
1 file changed, 54 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt b/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt
index ed4710c40641..bbf4a13f6d75 100644
--- a/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt
+++ b/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt
@@ -2,8 +2,8 @@ Vitesse VSC73xx Switches
========================
This defines device tree bindings for the Vitesse VSC73xx switch chips.
-The Vitesse company has been acquired by Microsemi and Microsemi in turn
-acquired by Microchip but retains this vendor branding.
+The Vitesse company has been acquired by Microsemi and Microsemi has
+been acquired Microchip but retains this vendor branding.
The currently supported switch chips are:
Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch
@@ -11,8 +11,14 @@ Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch
Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch
Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch
-The device tree node is an SPI device so it must reside inside a SPI bus
-device tree node, see spi/spi-bus.txt
+This switch could have two different management interface.
+
+If SPI interface is used, the device tree node is an SPI device so it must
+reside inside a SPI bus device tree node, see spi/spi-bus.txt
+
+When the chip is connected to a parallel memory bus and work in memory-mapped
+I/O mode, a platform device is used to represent the vsc73xx. In this case it
+must reside inside a platform bus device tree node.
Required properties:
@@ -38,6 +44,7 @@ and subnodes of DSA switches.
Examples:
+SPI:
switch@0 {
compatible = "vitesse,vsc7395";
reg = <0>;
@@ -79,3 +86,46 @@ switch@0 {
};
};
};
+
+Platform:
+switch@2,0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "vitesse,vsc7385";
+ reg = <0x2 0x0 0x20000>;
+ reset-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ label = "lan1";
+ };
+ port@1 {
+ reg = <1>;
+ label = "lan2";
+ };
+ port@2 {
+ reg = <2>;
+ label = "lan3";
+ };
+ port@3 {
+ reg = <3>;
+ label = "lan4";
+ };
+ vsc: port@6 {
+ reg = <6>;
+ label = "cpu";
+ ethernet = <&enet0>;
+ phy-mode = "rgmii";
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ pause;
+ };
+ };
+ };
+
+};
--
2.20.1
^ permalink raw reply related
* [PATCH v3 4/4] net: dsa: vsc73xx: Assert reset if iCPU is enabled
From: Pawel Dembicki @ 2019-07-04 22:29 UTC (permalink / raw)
Cc: Pawel Dembicki, linus.walleij, Andrew Lunn, Vivien Didelot,
Florian Fainelli, David S. Miller, Rob Herring, Mark Rutland,
netdev, devicetree, linux-kernel
In-Reply-To: <20190704222907.2888-1-paweldembicki@gmail.com>
Driver allow to use devices with disabled iCPU only.
Some devices have pre-initialised iCPU by bootloader.
That state make switch unmanaged. This patch force reset
if device is in unmanaged state. In the result chip lost
internal firmware from RAM and it can be managed.
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/vitesse-vsc73xx-core.c | 36 ++++++++++++--------------
1 file changed, 17 insertions(+), 19 deletions(-)
diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c
index 10063f31d9a3..614377ef7956 100644
--- a/drivers/net/dsa/vitesse-vsc73xx-core.c
+++ b/drivers/net/dsa/vitesse-vsc73xx-core.c
@@ -417,22 +417,8 @@ static int vsc73xx_detect(struct vsc73xx *vsc)
}
if (val == 0xffffffff) {
- dev_info(vsc->dev, "chip seems dead, assert reset\n");
- gpiod_set_value_cansleep(vsc->reset, 1);
- /* Reset pulse should be 20ns minimum, according to datasheet
- * table 245, so 10us should be fine
- */
- usleep_range(10, 100);
- gpiod_set_value_cansleep(vsc->reset, 0);
- /* Wait 20ms according to datasheet table 245 */
- msleep(20);
-
- ret = vsc73xx_read(vsc, VSC73XX_BLOCK_SYSTEM, 0,
- VSC73XX_ICPU_MBOX_VAL, &val);
- if (val == 0xffffffff) {
- dev_err(vsc->dev, "seems not to help, giving up\n");
- return -ENODEV;
- }
+ dev_info(vsc->dev, "chip seems dead.\n");
+ return -EAGAIN;
}
ret = vsc73xx_read(vsc, VSC73XX_BLOCK_SYSTEM, 0,
@@ -483,9 +469,8 @@ static int vsc73xx_detect(struct vsc73xx *vsc)
}
if (icpu_si_boot_en && !icpu_pi_en) {
dev_err(vsc->dev,
- "iCPU enabled boots from SI, no external memory\n");
- dev_err(vsc->dev, "no idea how to deal with this\n");
- return -ENODEV;
+ "iCPU enabled boots from PI/SI, no external memory\n");
+ return -EAGAIN;
}
if (!icpu_si_boot_en && icpu_pi_en) {
dev_err(vsc->dev,
@@ -1158,6 +1143,19 @@ int vsc73xx_probe(struct vsc73xx *vsc)
msleep(20);
ret = vsc73xx_detect(vsc);
+ if (ret == -EAGAIN) {
+ dev_err(vsc->dev,
+ "Chip seems to be out of control. Assert reset and try again.\n");
+ gpiod_set_value_cansleep(vsc->reset, 1);
+ /* Reset pulse should be 20ns minimum, according to datasheet
+ * table 245, so 10us should be fine
+ */
+ usleep_range(10, 100);
+ gpiod_set_value_cansleep(vsc->reset, 0);
+ /* Wait 20ms according to datasheet table 245 */
+ msleep(20);
+ ret = vsc73xx_detect(vsc);
+ }
if (ret) {
dev_err(dev, "no chip found (%d)\n", ret);
return -ENODEV;
--
2.20.1
^ permalink raw reply related
* [PATCH v3 3/4] net: dsa: vsc73xx: add support for parallel mode
From: Pawel Dembicki @ 2019-07-04 22:29 UTC (permalink / raw)
Cc: Pawel Dembicki, linus.walleij, Andrew Lunn, Vivien Didelot,
Florian Fainelli, David S. Miller, Rob Herring, Mark Rutland,
netdev, devicetree, linux-kernel
In-Reply-To: <20190704222907.2888-1-paweldembicki@gmail.com>
This patch add platform part of vsc73xx driver.
It allows to use chip connected to a parallel memory bus and work in
memory-mapped I/O mode. (aka PI bus in chip manual)
By default device is working in big endian mode.
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
drivers/net/dsa/Kconfig | 9 ++
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/vitesse-vsc73xx-platform.c | 164 +++++++++++++++++++++
3 files changed, 174 insertions(+)
create mode 100644 drivers/net/dsa/vitesse-vsc73xx-platform.c
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4ab2aa09e2e4..cf9dbd15dd2d 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -116,4 +116,13 @@ config NET_DSA_VITESSE_VSC73XX_SPI
---help---
This enables support for the Vitesse VSC7385, VSC7388, VSC7395
and VSC7398 SparX integrated ethernet switches in SPI managed mode.
+
+config NET_DSA_VITESSE_VSC73XX_PLATFORM
+ tristate "Vitesse VSC7385/7388/7395/7398 Platform mode support"
+ depends on HAS_IOMEM
+ select NET_DSA_VITESSE_VSC73XX
+ ---help---
+ This enables support for the Vitesse VSC7385, VSC7388, VSC7395
+ and VSC7398 SparX integrated ethernet switches, connected over
+ a CPU-attached address bus and work in memory-mapped I/O mode.
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 117bf78be211..d5e4c668ac03 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
+obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
obj-y += b53/
obj-y += microchip/
diff --git a/drivers/net/dsa/vitesse-vsc73xx-platform.c b/drivers/net/dsa/vitesse-vsc73xx-platform.c
new file mode 100644
index 000000000000..0541785f9fee
--- /dev/null
+++ b/drivers/net/dsa/vitesse-vsc73xx-platform.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0
+/* DSA driver for:
+ * Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch
+ * Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch
+ * Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch
+ * Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch
+ *
+ * This driver takes control of the switch chip connected over CPU-attached
+ * address bus and configures it to route packages around when connected to
+ * a CPU port.
+ *
+ * Copyright (C) 2019 Pawel Dembicki <paweldembicki@gmail.com>
+ * Based on vitesse-vsc-spi.c by:
+ * Copyright (C) 2018 Linus Wallej <linus.walleij@linaro.org>
+ * Includes portions of code from the firmware uploader by:
+ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "vitesse-vsc73xx.h"
+
+#define VSC73XX_CMD_PLATFORM_BLOCK_SHIFT 14
+#define VSC73XX_CMD_PLATFORM_BLOCK_MASK 0x7
+#define VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT 10
+#define VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK 0xf
+#define VSC73XX_CMD_PLATFORM_REGISTER_SHIFT 2
+
+/**
+ * struct vsc73xx_platform - VSC73xx Platform state container
+ */
+struct vsc73xx_platform {
+ struct platform_device *pdev;
+ void __iomem *base_addr;
+ struct vsc73xx vsc;
+};
+
+static const struct vsc73xx_ops vsc73xx_platform_ops;
+
+static u32 vsc73xx_make_addr(u8 block, u8 subblock, u8 reg)
+{
+ u32 ret;
+
+ ret = (block & VSC73XX_CMD_PLATFORM_BLOCK_MASK)
+ << VSC73XX_CMD_PLATFORM_BLOCK_SHIFT;
+ ret |= (subblock & VSC73XX_CMD_PLATFORM_SUBBLOCK_MASK)
+ << VSC73XX_CMD_PLATFORM_SUBBLOCK_SHIFT;
+ ret |= reg << VSC73XX_CMD_PLATFORM_REGISTER_SHIFT;
+
+ return ret;
+}
+
+static int vsc73xx_platform_read(struct vsc73xx *vsc, u8 block, u8 subblock,
+ u8 reg, u32 *val)
+{
+ struct vsc73xx_platform *vsc_platform = vsc->priv;
+ u32 offset;
+
+ if (!vsc73xx_is_addr_valid(block, subblock))
+ return -EINVAL;
+
+ offset = vsc73xx_make_addr(block, subblock, reg);
+ /* By default vsc73xx running in big-endian mode.
+ * (See "Register Addressing" section 5.5.3 in the VSC7385 manual.)
+ */
+ *val = ioread32be(vsc_platform->base_addr + offset);
+
+ return 0;
+}
+
+static int vsc73xx_platform_write(struct vsc73xx *vsc, u8 block, u8 subblock,
+ u8 reg, u32 val)
+{
+ struct vsc73xx_platform *vsc_platform = vsc->priv;
+ u32 offset;
+
+ if (!vsc73xx_is_addr_valid(block, subblock))
+ return -EINVAL;
+
+ offset = vsc73xx_make_addr(block, subblock, reg);
+ iowrite32be(val, vsc_platform->base_addr + offset);
+
+ return 0;
+}
+
+static int vsc73xx_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vsc73xx_platform *vsc_platform;
+ struct resource *res = NULL;
+ int ret;
+
+ vsc_platform = devm_kzalloc(dev, sizeof(*vsc_platform), GFP_KERNEL);
+ if (!vsc_platform)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vsc_platform);
+ vsc_platform->pdev = pdev;
+ vsc_platform->vsc.dev = dev;
+ vsc_platform->vsc.priv = vsc_platform;
+ vsc_platform->vsc.ops = &vsc73xx_platform_ops;
+
+ /* obtain I/O memory space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot obtain I/O memory space\n");
+ ret = -ENXIO;
+ return ret;
+ }
+
+ vsc_platform->base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vsc_platform->base_addr)) {
+ dev_err(&pdev->dev, "cannot request I/O memory space\n");
+ ret = -ENXIO;
+ return ret;
+ }
+
+ return vsc73xx_probe(&vsc_platform->vsc);
+}
+
+static int vsc73xx_platform_remove(struct platform_device *pdev)
+{
+ struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev);
+
+ return vsc73xx_remove(&vsc_platform->vsc);
+}
+
+static const struct vsc73xx_ops vsc73xx_platform_ops = {
+ .read = vsc73xx_platform_read,
+ .write = vsc73xx_platform_write,
+};
+
+static const struct of_device_id vsc73xx_of_match[] = {
+ {
+ .compatible = "vitesse,vsc7385",
+ },
+ {
+ .compatible = "vitesse,vsc7388",
+ },
+ {
+ .compatible = "vitesse,vsc7395",
+ },
+ {
+ .compatible = "vitesse,vsc7398",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, vsc73xx_of_match);
+
+static struct platform_driver vsc73xx_platform_driver = {
+ .probe = vsc73xx_platform_probe,
+ .remove = vsc73xx_platform_remove,
+ .driver = {
+ .name = "vsc73xx-platform",
+ .of_match_table = vsc73xx_of_match,
+ },
+};
+module_platform_driver(vsc73xx_platform_driver);
+
+MODULE_AUTHOR("Pawel Dembicki <paweldembicki@gmail.com>");
+MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 Platform driver");
+MODULE_LICENSE("GPL v2");
--
2.20.1
^ permalink raw reply related
* [PATCH v3 2/4] net: dsa: vsc73xx: Split vsc73xx driver
From: Pawel Dembicki @ 2019-07-04 22:29 UTC (permalink / raw)
Cc: Pawel Dembicki, linus.walleij, Andrew Lunn, Vivien Didelot,
Florian Fainelli, David S. Miller, Rob Herring, Mark Rutland,
netdev, devicetree, linux-kernel
In-Reply-To: <20190704222907.2888-1-paweldembicki@gmail.com>
This driver (currently) only takes control of the switch chip over
SPI and configures it to route packages around when connected to a
CPU port. But Vitesse chip support also parallel interface.
This patch split driver into two parts: core and spi. It is required
for add support to another managing interface.
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
---
drivers/net/dsa/Kconfig | 11 +-
drivers/net/dsa/Makefile | 3 +-
...tesse-vsc73xx.c => vitesse-vsc73xx-core.c} | 170 +--------------
drivers/net/dsa/vitesse-vsc73xx-spi.c | 203 ++++++++++++++++++
drivers/net/dsa/vitesse-vsc73xx.h | 29 +++
5 files changed, 254 insertions(+), 162 deletions(-)
rename drivers/net/dsa/{vitesse-vsc73xx.c => vitesse-vsc73xx-core.c} (91%)
create mode 100644 drivers/net/dsa/vitesse-vsc73xx-spi.c
create mode 100644 drivers/net/dsa/vitesse-vsc73xx.h
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index b91e78e3598f..4ab2aa09e2e4 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -99,8 +99,8 @@ config NET_DSA_SMSC_LAN9303_MDIO
for MDIO managed mode.
config NET_DSA_VITESSE_VSC73XX
- tristate "Vitesse VSC7385/7388/7395/7398 support"
- depends on OF && SPI
+ tristate
+ depends on OF
depends on NET_DSA
select FIXED_PHY
select VITESSE_PHY
@@ -109,4 +109,11 @@ config NET_DSA_VITESSE_VSC73XX
This enables support for the Vitesse VSC7385, VSC7388,
VSC7395 and VSC7398 SparX integrated ethernet switches.
+config NET_DSA_VITESSE_VSC73XX_SPI
+ tristate "Vitesse VSC7385/7388/7395/7398 SPI mode support"
+ depends on SPI
+ select NET_DSA_VITESSE_VSC73XX
+ ---help---
+ This enables support for the Vitesse VSC7385, VSC7388, VSC7395
+ and VSC7398 SparX integrated ethernet switches in SPI managed mode.
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index fefb6aaa82ba..117bf78be211 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -14,7 +14,8 @@ realtek-objs := realtek-smi.o rtl8366.o rtl8366rb.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
-obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx.o
+obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
+obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
obj-y += b53/
obj-y += microchip/
obj-y += mv88e6xxx/
diff --git a/drivers/net/dsa/vitesse-vsc73xx.c b/drivers/net/dsa/vitesse-vsc73xx-core.c
similarity index 91%
rename from drivers/net/dsa/vitesse-vsc73xx.c
rename to drivers/net/dsa/vitesse-vsc73xx-core.c
index d4780610ea8a..10063f31d9a3 100644
--- a/drivers/net/dsa/vitesse-vsc73xx.c
+++ b/drivers/net/dsa/vitesse-vsc73xx-core.c
@@ -10,10 +10,6 @@
* handling the switch in a memory-mapped manner by connecting to that external
* CPU's memory bus.
*
- * This driver (currently) only takes control of the switch chip over SPI and
- * configures it to route packages around when connected to a CPU port. The
- * chip has embedded PHYs and VLAN support so we model it using DSA.
- *
* Copyright (C) 2018 Linus Wallej <linus.walleij@linaro.org>
* Includes portions of code from the firmware uploader by:
* Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
@@ -24,8 +20,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
#include <linux/bitops.h>
#include <linux/if_bridge.h>
#include <linux/etherdevice.h>
@@ -34,6 +28,8 @@
#include <linux/random.h>
#include <net/dsa.h>
+#include "vitesse-vsc73xx.h"
+
#define VSC73XX_BLOCK_MAC 0x1 /* Subblocks 0-4, 6 (CPU port) */
#define VSC73XX_BLOCK_ANALYZER 0x2 /* Only subblock 0 */
#define VSC73XX_BLOCK_MII 0x3 /* Subblocks 0 and 1 */
@@ -255,13 +251,6 @@
#define VSC73XX_GLORESET_PHY_RESET BIT(1)
#define VSC73XX_GLORESET_MASTER_RESET BIT(0)
-#define VSC73XX_CMD_MODE_READ 0
-#define VSC73XX_CMD_MODE_WRITE 1
-#define VSC73XX_CMD_MODE_SHIFT 4
-#define VSC73XX_CMD_BLOCK_SHIFT 5
-#define VSC73XX_CMD_BLOCK_MASK 0x7
-#define VSC73XX_CMD_SUBBLOCK_MASK 0xf
-
#define VSC7385_CLOCK_DELAY ((3 << 4) | 3)
#define VSC7385_CLOCK_DELAY_MASK ((3 << 4) | 3)
@@ -274,20 +263,6 @@
VSC73XX_ICPU_CTRL_CLK_EN | \
VSC73XX_ICPU_CTRL_SRST)
-/**
- * struct vsc73xx - VSC73xx state container
- */
-struct vsc73xx {
- struct device *dev;
- struct gpio_desc *reset;
- struct spi_device *spi;
- struct dsa_switch *ds;
- struct gpio_chip gc;
- u16 chipid;
- u8 addr[ETH_ALEN];
- struct mutex lock; /* Protects SPI traffic */
-};
-
#define IS_7385(a) ((a)->chipid == VSC73XX_CHIPID_ID_7385)
#define IS_7388(a) ((a)->chipid == VSC73XX_CHIPID_ID_7388)
#define IS_7395(a) ((a)->chipid == VSC73XX_CHIPID_ID_7395)
@@ -365,7 +340,7 @@ static const struct vsc73xx_counter vsc73xx_tx_counters[] = {
{ 29, "TxQoSClass3" }, /* non-standard counter */
};
-static int vsc73xx_is_addr_valid(u8 block, u8 subblock)
+int vsc73xx_is_addr_valid(u8 block, u8 subblock)
{
switch (block) {
case VSC73XX_BLOCK_MAC:
@@ -396,96 +371,18 @@ static int vsc73xx_is_addr_valid(u8 block, u8 subblock)
return 0;
}
-
-static u8 vsc73xx_make_addr(u8 mode, u8 block, u8 subblock)
-{
- u8 ret;
-
- ret = (block & VSC73XX_CMD_BLOCK_MASK) << VSC73XX_CMD_BLOCK_SHIFT;
- ret |= (mode & 1) << VSC73XX_CMD_MODE_SHIFT;
- ret |= subblock & VSC73XX_CMD_SUBBLOCK_MASK;
-
- return ret;
-}
+EXPORT_SYMBOL(vsc73xx_is_addr_valid);
static int vsc73xx_read(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg,
u32 *val)
{
- struct spi_transfer t[2];
- struct spi_message m;
- u8 cmd[4];
- u8 buf[4];
- int ret;
-
- if (!vsc73xx_is_addr_valid(block, subblock))
- return -EINVAL;
-
- spi_message_init(&m);
-
- memset(&t, 0, sizeof(t));
-
- t[0].tx_buf = cmd;
- t[0].len = sizeof(cmd);
- spi_message_add_tail(&t[0], &m);
-
- t[1].rx_buf = buf;
- t[1].len = sizeof(buf);
- spi_message_add_tail(&t[1], &m);
-
- cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_MODE_READ, block, subblock);
- cmd[1] = reg;
- cmd[2] = 0;
- cmd[3] = 0;
-
- mutex_lock(&vsc->lock);
- ret = spi_sync(vsc->spi, &m);
- mutex_unlock(&vsc->lock);
-
- if (ret)
- return ret;
-
- *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
-
- return 0;
+ return vsc->ops->read(vsc, block, subblock, reg, val);
}
static int vsc73xx_write(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg,
u32 val)
{
- struct spi_transfer t[2];
- struct spi_message m;
- u8 cmd[2];
- u8 buf[4];
- int ret;
-
- if (!vsc73xx_is_addr_valid(block, subblock))
- return -EINVAL;
-
- spi_message_init(&m);
-
- memset(&t, 0, sizeof(t));
-
- t[0].tx_buf = cmd;
- t[0].len = sizeof(cmd);
- spi_message_add_tail(&t[0], &m);
-
- t[1].tx_buf = buf;
- t[1].len = sizeof(buf);
- spi_message_add_tail(&t[1], &m);
-
- cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_MODE_WRITE, block, subblock);
- cmd[1] = reg;
-
- buf[0] = (val >> 24) & 0xff;
- buf[1] = (val >> 16) & 0xff;
- buf[2] = (val >> 8) & 0xff;
- buf[3] = val & 0xff;
-
- mutex_lock(&vsc->lock);
- ret = spi_sync(vsc->spi, &m);
- mutex_unlock(&vsc->lock);
-
- return ret;
+ return vsc->ops->write(vsc, block, subblock, reg, val);
}
static int vsc73xx_update_bits(struct vsc73xx *vsc, u8 block, u8 subblock,
@@ -1245,21 +1142,11 @@ static int vsc73xx_gpio_probe(struct vsc73xx *vsc)
return 0;
}
-static int vsc73xx_probe(struct spi_device *spi)
+int vsc73xx_probe(struct vsc73xx *vsc)
{
- struct device *dev = &spi->dev;
- struct vsc73xx *vsc;
+ struct device *dev = vsc->dev;
int ret;
- vsc = devm_kzalloc(dev, sizeof(*vsc), GFP_KERNEL);
- if (!vsc)
- return -ENOMEM;
-
- spi_set_drvdata(spi, vsc);
- vsc->spi = spi_dev_get(spi);
- vsc->dev = dev;
- mutex_init(&vsc->lock);
-
/* Release reset, if any */
vsc->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(vsc->reset)) {
@@ -1270,14 +1157,6 @@ static int vsc73xx_probe(struct spi_device *spi)
/* Wait 20ms according to datasheet table 245 */
msleep(20);
- spi->mode = SPI_MODE_0;
- spi->bits_per_word = 8;
- ret = spi_setup(spi);
- if (ret < 0) {
- dev_err(dev, "spi setup failed.\n");
- return ret;
- }
-
ret = vsc73xx_detect(vsc);
if (ret) {
dev_err(dev, "no chip found (%d)\n", ret);
@@ -1321,43 +1200,16 @@ static int vsc73xx_probe(struct spi_device *spi)
return 0;
}
+EXPORT_SYMBOL(vsc73xx_probe);
-static int vsc73xx_remove(struct spi_device *spi)
+int vsc73xx_remove(struct vsc73xx *vsc)
{
- struct vsc73xx *vsc = spi_get_drvdata(spi);
-
dsa_unregister_switch(vsc->ds);
gpiod_set_value(vsc->reset, 1);
return 0;
}
-
-static const struct of_device_id vsc73xx_of_match[] = {
- {
- .compatible = "vitesse,vsc7385",
- },
- {
- .compatible = "vitesse,vsc7388",
- },
- {
- .compatible = "vitesse,vsc7395",
- },
- {
- .compatible = "vitesse,vsc7398",
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, vsc73xx_of_match);
-
-static struct spi_driver vsc73xx_driver = {
- .probe = vsc73xx_probe,
- .remove = vsc73xx_remove,
- .driver = {
- .name = "vsc73xx",
- .of_match_table = vsc73xx_of_match,
- },
-};
-module_spi_driver(vsc73xx_driver);
+EXPORT_SYMBOL(vsc73xx_remove);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 driver");
diff --git a/drivers/net/dsa/vitesse-vsc73xx-spi.c b/drivers/net/dsa/vitesse-vsc73xx-spi.c
new file mode 100644
index 000000000000..e73c8fcddc9f
--- /dev/null
+++ b/drivers/net/dsa/vitesse-vsc73xx-spi.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/* DSA driver for:
+ * Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch
+ * Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch
+ * Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch
+ * Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch
+ *
+ * This driver takes control of the switch chip over SPI and
+ * configures it to route packages around when connected to a CPU port.
+ *
+ * Copyright (C) 2018 Linus Wallej <linus.walleij@linaro.org>
+ * Includes portions of code from the firmware uploader by:
+ * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+
+#include "vitesse-vsc73xx.h"
+
+#define VSC73XX_CMD_SPI_MODE_READ 0
+#define VSC73XX_CMD_SPI_MODE_WRITE 1
+#define VSC73XX_CMD_SPI_MODE_SHIFT 4
+#define VSC73XX_CMD_SPI_BLOCK_SHIFT 5
+#define VSC73XX_CMD_SPI_BLOCK_MASK 0x7
+#define VSC73XX_CMD_SPI_SUBBLOCK_MASK 0xf
+
+/**
+ * struct vsc73xx_spi - VSC73xx SPI state container
+ */
+struct vsc73xx_spi {
+ struct spi_device *spi;
+ struct mutex lock; /* Protects SPI traffic */
+ struct vsc73xx vsc;
+};
+
+static const struct vsc73xx_ops vsc73xx_spi_ops;
+
+static u8 vsc73xx_make_addr(u8 mode, u8 block, u8 subblock)
+{
+ u8 ret;
+
+ ret =
+ (block & VSC73XX_CMD_SPI_BLOCK_MASK) << VSC73XX_CMD_SPI_BLOCK_SHIFT;
+ ret |= (mode & 1) << VSC73XX_CMD_SPI_MODE_SHIFT;
+ ret |= subblock & VSC73XX_CMD_SPI_SUBBLOCK_MASK;
+
+ return ret;
+}
+
+static int vsc73xx_spi_read(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg,
+ u32 *val)
+{
+ struct vsc73xx_spi *vsc_spi = vsc->priv;
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u8 cmd[4];
+ u8 buf[4];
+ int ret;
+
+ if (!vsc73xx_is_addr_valid(block, subblock))
+ return -EINVAL;
+
+ spi_message_init(&m);
+
+ memset(&t, 0, sizeof(t));
+
+ t[0].tx_buf = cmd;
+ t[0].len = sizeof(cmd);
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = buf;
+ t[1].len = sizeof(buf);
+ spi_message_add_tail(&t[1], &m);
+
+ cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_SPI_MODE_READ, block, subblock);
+ cmd[1] = reg;
+ cmd[2] = 0;
+ cmd[3] = 0;
+
+ mutex_lock(&vsc_spi->lock);
+ ret = spi_sync(vsc_spi->spi, &m);
+ mutex_unlock(&vsc_spi->lock);
+
+ if (ret)
+ return ret;
+
+ *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+
+ return 0;
+}
+
+static int vsc73xx_spi_write(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg,
+ u32 val)
+{
+ struct vsc73xx_spi *vsc_spi = vsc->priv;
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u8 cmd[2];
+ u8 buf[4];
+ int ret;
+
+ if (!vsc73xx_is_addr_valid(block, subblock))
+ return -EINVAL;
+
+ spi_message_init(&m);
+
+ memset(&t, 0, sizeof(t));
+
+ t[0].tx_buf = cmd;
+ t[0].len = sizeof(cmd);
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = buf;
+ t[1].len = sizeof(buf);
+ spi_message_add_tail(&t[1], &m);
+
+ cmd[0] = vsc73xx_make_addr(VSC73XX_CMD_SPI_MODE_WRITE, block, subblock);
+ cmd[1] = reg;
+
+ buf[0] = (val >> 24) & 0xff;
+ buf[1] = (val >> 16) & 0xff;
+ buf[2] = (val >> 8) & 0xff;
+ buf[3] = val & 0xff;
+
+ mutex_lock(&vsc_spi->lock);
+ ret = spi_sync(vsc_spi->spi, &m);
+ mutex_unlock(&vsc_spi->lock);
+
+ return ret;
+}
+
+static int vsc73xx_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct vsc73xx_spi *vsc_spi;
+ int ret;
+
+ vsc_spi = devm_kzalloc(dev, sizeof(*vsc_spi), GFP_KERNEL);
+ if (!vsc_spi)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, vsc_spi);
+ vsc_spi->spi = spi_dev_get(spi);
+ vsc_spi->vsc.dev = dev;
+ vsc_spi->vsc.priv = vsc_spi;
+ vsc_spi->vsc.ops = &vsc73xx_spi_ops;
+ mutex_init(&vsc_spi->lock);
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(dev, "spi setup failed.\n");
+ return ret;
+ }
+
+ return vsc73xx_probe(&vsc_spi->vsc);
+}
+
+static int vsc73xx_spi_remove(struct spi_device *spi)
+{
+ struct vsc73xx_spi *vsc_spi = spi_get_drvdata(spi);
+
+ return vsc73xx_remove(&vsc_spi->vsc);
+}
+
+static const struct vsc73xx_ops vsc73xx_spi_ops = {
+ .read = vsc73xx_spi_read,
+ .write = vsc73xx_spi_write,
+};
+
+static const struct of_device_id vsc73xx_of_match[] = {
+ {
+ .compatible = "vitesse,vsc7385",
+ },
+ {
+ .compatible = "vitesse,vsc7388",
+ },
+ {
+ .compatible = "vitesse,vsc7395",
+ },
+ {
+ .compatible = "vitesse,vsc7398",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, vsc73xx_of_match);
+
+static struct spi_driver vsc73xx_spi_driver = {
+ .probe = vsc73xx_spi_probe,
+ .remove = vsc73xx_spi_remove,
+ .driver = {
+ .name = "vsc73xx-spi",
+ .of_match_table = vsc73xx_of_match,
+ },
+};
+module_spi_driver(vsc73xx_spi_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/vitesse-vsc73xx.h b/drivers/net/dsa/vitesse-vsc73xx.h
new file mode 100644
index 000000000000..7478f8d4e0a9
--- /dev/null
+++ b/drivers/net/dsa/vitesse-vsc73xx.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/gpio/driver.h>
+
+/**
+ * struct vsc73xx - VSC73xx state container
+ */
+struct vsc73xx {
+ struct device *dev;
+ struct gpio_desc *reset;
+ struct dsa_switch *ds;
+ struct gpio_chip gc;
+ u16 chipid;
+ u8 addr[ETH_ALEN];
+ const struct vsc73xx_ops *ops;
+ void *priv;
+};
+
+struct vsc73xx_ops {
+ int (*read)(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg,
+ u32 *val);
+ int (*write)(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg,
+ u32 val);
+};
+
+int vsc73xx_is_addr_valid(u8 block, u8 subblock);
+int vsc73xx_probe(struct vsc73xx *vsc);
+int vsc73xx_remove(struct vsc73xx *vsc);
--
2.20.1
^ permalink raw reply related
* Re: [PATCH net-next 0/9] net: hns3: some cleanups & bugfixes
From: Jakub Kicinski @ 2019-07-04 22:13 UTC (permalink / raw)
To: Huazhong Tan
Cc: davem, netdev, linux-kernel, salil.mehta, yisen.zhuang, linuxarm
In-Reply-To: <1562249068-40176-1-git-send-email-tanhuazhong@huawei.com>
On Thu, 4 Jul 2019 22:04:19 +0800, Huazhong Tan wrote:
> This patch-set includes cleanups and bugfixes for
> the HNS3 ethernet controller driver.
>
> [patch 1/9] fixes VF's broadcast promisc mode not enabled after
> initializing.
>
> [patch 2/9] adds hints for fibre port not support flow control.
>
> [patch 3/9] fixes a port capbility updating issue.
>
> [patch 4/9 - 9/9] adds some cleanups for HNS3 driver.
For the unsigned "fixes" it seems it may have been easier to fix the
macroes than the users, but perhaps that's hard. Series looks
unobjectionable :)
^ permalink raw reply
* Re: [PATCH net-next v2 4/4] qed*: Add devlink support for configuration attributes.
From: Jakub Kicinski @ 2019-07-04 22:07 UTC (permalink / raw)
To: Sudarsana Reddy Kalluru; +Cc: davem, netdev, mkalderon, aelior, Jiri Pirko
In-Reply-To: <20190704132011.13600-5-skalluru@marvell.com>
On Thu, 4 Jul 2019 06:20:11 -0700, Sudarsana Reddy Kalluru wrote:
> This patch adds implementation for devlink callbacks for reading and
> configuring the device attributes.
>
> Signed-off-by: Sudarsana Reddy Kalluru <skalluru@marvell.com>
> Signed-off-by: Ariel Elior <aelior@marvell.com>
> ---
> Documentation/networking/devlink-params-qede.txt | 72 ++++++++
> drivers/net/ethernet/qlogic/qed/qed_main.c | 38 +++++
> drivers/net/ethernet/qlogic/qede/qede.h | 3 +
> drivers/net/ethernet/qlogic/qede/qede_devlink.c | 202 ++++++++++++++++++++++-
> drivers/net/ethernet/qlogic/qede/qede_devlink.h | 23 +++
> include/linux/qed/qed_if.h | 16 ++
> 6 files changed, 353 insertions(+), 1 deletion(-)
> create mode 100644 Documentation/networking/devlink-params-qede.txt
>
> diff --git a/Documentation/networking/devlink-params-qede.txt b/Documentation/networking/devlink-params-qede.txt
> new file mode 100644
> index 0000000..f78a993
> --- /dev/null
> +++ b/Documentation/networking/devlink-params-qede.txt
> @@ -0,0 +1,72 @@
> +enable_sriov [DEVICE, GENERIC]
> + Configuration mode: Permanent
> +
> +iwarp_cmt [DEVICE, DRIVER-SPECIFIC]
> + Enable iWARP support over 100G device (CMT mode).
> + Type: Boolean
> + Configuration mode: runtime
> +
> +entity_id [DEVICE, DRIVER-SPECIFIC]
> + Set the entity ID value to be used for this device
> + while reading/configuring the devlink attributes.
> + Type: u8
> + Configuration mode: runtime
Can you explain what this is?
> +device_capabilities [DEVICE, DRIVER-SPECIFIC]
> + Set the entity ID value to be used for this device
> + while reading/configuring the devlink attributes.
> + Type: u8
> + Configuration mode: runtime
Looks like you copied the previous text here.
> +mf_mode [DEVICE, DRIVER-SPECIFIC]
> + Configure Multi Function mode for the device.
> + Supported MF modes and the assoicated values are,
> + MF allowed(0), Default(1), SPIO4(2), NPAR1.0(3),
> + NPAR1.5(4), NPAR2.0(5), BD(6) and UFP(7)
NPAR should have a proper API in devlink port, what are the other modes?
> + Type: u8
> + Configuration mode: Permanent
> +
> +dcbx_mode [PORT, DRIVER-SPECIFIC]
> + Configure DCBX mode for the device.
> + Supported dcbx modes are,
> + Disabled(0), IEEE(1), CEE(2) and Dynamic(3)
> + Type: u8
> + Configuration mode: Permanent
Why is this a permanent parameter?
> +preboot_oprom [PORT, DRIVER-SPECIFIC]
> + Enable Preboot Option ROM.
> + Type: Boolean
> + Configuration mode: Permanent
This should definitely not be a driver specific toggle.
> +preboot_boot_protocol [PORT, DRIVER-SPECIFIC]
> + Configure preboot Boot protocol.
> + Possible values are,
> + PXE(0), iSCSI Boot(3), FCoE Boot(4) and NONE(7)
> + Type: u8
> + Configuration mode: Permanent
Ditto.
> +preboot_vlan [PORT, DRIVER-SPECIFIC]
> + Preboot VLAN.
> + Type: u16
> + Configuration mode: Permanent
> +
> +preboot_vlan_value [PORT, DRIVER-SPECIFIC]
> + Configure Preboot VLAN value.
> + Type: u16
> + Configuration mode: Permanent
And these.
> +mba_delay_time [PORT, DRIVER-SPECIFIC]
> + Configure MBA Delay Time. Supported range is [0-15].
> + Type: u8
> + Configuration mode: Permanent
> +
> +mba_setup_hot_key [PORT, DRIVER-SPECIFIC]
> + Configure MBA setup Hot Key. Possible values are,
> + Ctrl S(0) and Ctrl B(1).
> + Type: u8
> + Configuration mode: Permanent
> +
> +mba_hide_setup_prompt [PORT, DRIVER-SPECIFIC]
> + Configure MBA hide setup prompt.
> + Type: Boolean
> + Configuration mode: Permanent
^ permalink raw reply
* NEIGH: BUG, double timer add, state is 8
From: Marek Majkowski @ 2019-07-04 21:59 UTC (permalink / raw)
To: David Miller, dsahern; +Cc: netdev, kernel-team
Morning,
I found a way to hit an obscure BUG in the
net/core/neighbour.c:neigh_add_timer(), by piping two carefully
crafted messages into AF_NETLINK socket.
https://github.com/torvalds/linux/blob/v5.2-rc7/net/core/neighbour.c#L259
if (unlikely(mod_timer(&n->timer, when))) {
printk("NEIGH: BUG, double timer add, state is %x\n", n->nud_state);
dump_stack();
}
The repro is here:
https://gist.github.com/majek/d70297b9d72bc2e2b82145e122722a0c
wget https://gist.githubusercontent.com/majek/d70297b9d72bc2e2b82145e122722a0c/raw/9e140bcedecc28d722022f1da142a379a9b7a7b0/double_timer_add_bug.c
You need root for AF_NETLINK socket. I would lie if I said I
understand what these netlink messages actually do.
Tested under virtme, with 5.2-rc7 kernel.
Full stack trace:
4,147643,57161310899,-;NEIGH: BUG, double timer add, state is 8
4,147644,57161311114,-;CPU: 0 PID: 266 Comm: xxx Not tainted 5.2.0-rc7kvm+ #6
4,147645,57161311260,-;Hardware name: QEMU Standard PC (i440FX + PIIX,
1996), BIOS 1.10.2-1ubuntu1 04/01/2014
4,147646,57161311401,-;Call Trace:
4,147647,57161311616,-; dump_stack (linux/lib/dump_stack.c:115)
4,147648,57161311771,-; neigh_add_timer
(linux/net/core/neighbour.c:265 linux/net/core/neighbour.c:259)
4,147649,57161311878,-; __neigh_event_send (linux/net/core/neighbour.c:1143)
4,147650,57161312043,-; ? lockdep_hardirqs_on
(linux/kernel/locking/lockdep.c:3218
linux/kernel/locking/lockdep.c:3263)
4,147651,57161312205,-; ? __local_bh_enable_ip
(linux/arch/x86/include/asm/paravirt.h:777 linux/kernel/softirq.c:194)
4,147652,57161312311,-; ? neigh_lookup
(linux/include/linux/rcupdate.h:213 linux/include/linux/rcupdate.h:680
linux/net/core/neighbour.c:539)
4,147653,57161312454,-; ? trace_hardirqs_on
(linux/kernel/trace/trace_preemptirq.c:32)
4,147654,57161312579,-; ? neigh_lookup (linux/net/core/neighbour.c:1106)
4,147655,57161312700,-; ? __local_bh_enable_ip
(linux/arch/x86/include/asm/paravirt.h:777 linux/kernel/softirq.c:194)
4,147656,57161312804,-; ? neigh_lookup (linux/net/core/neighbour.c:541)
4,147657,57161312949,-; ? udp_gro_receive.cold.8 (linux/net/ipv4/arp.c:216)
4,147658,57161313087,-; neigh_add (linux/include/net/neighbour.h:445
linux/net/core/neighbour.c:1963)
4,147659,57161313216,-; ? neigh_xmit (linux/net/core/neighbour.c:1850)
4,147660,57161313346,-; ? __sanitizer_cov_trace_const_cmp8
(linux/kernel/kcov.c:198)
4,147661,57161313522,-; ? neigh_xmit (linux/net/core/neighbour.c:1850)
4,147662,57161313644,-; rtnetlink_rcv_msg (linux/net/core/rtnetlink.c:5214)
4,147663,57161313751,-; ? rtnetlink_put_metrics
(linux/net/core/rtnetlink.c:5117)
4,147664,57161313875,-; ? find_held_lock (linux/kernel/locking/lockdep.c:3898)
4,147665,57161314023,-; netlink_rcv_skb (linux/net/netlink/af_netlink.c:2483)
4,147666,57161314152,-; ? rtnetlink_put_metrics
(linux/net/core/rtnetlink.c:5117)
4,147667,57161314311,-; ? netlink_ack (linux/net/netlink/af_netlink.c:2459)
4,147668,57161314416,-; ? netlink_deliver_tap
(linux/net/netlink/af_netlink.c:333)
4,147669,57161314538,-; rtnetlink_rcv (linux/net/core/rtnetlink.c:5233)
4,147670,57161314701,-; netlink_unicast
(linux/net/netlink/af_netlink.c:1308
linux/net/netlink/af_netlink.c:1333)
4,147671,57161314811,-; ? netlink_attachskb
(linux/net/netlink/af_netlink.c:1318)
4,147672,57161315093,-; ? _copy_from_iter_full (linux/lib/iov_iter.c:780)
4,147673,57161315239,-; netlink_sendmsg (linux/net/netlink/af_netlink.c:1922)
4,147674,57161315349,-; ? netlink_unicast (linux/net/netlink/af_netlink.c:1848)
4,147675,57161315543,-; ? apparmor_socket_sendmsg
(linux/security/apparmor/lsm.c:937)
4,147676,57161315690,-; ? netlink_unicast (linux/net/netlink/af_netlink.c:1848)
4,147677,57161315838,-; sock_sendmsg (linux/net/socket.c:646
linux/net/socket.c:665)
4,147678,57161315983,-; ___sys_sendmsg (linux/net/socket.c:2286)
4,147679,57161316105,-; ? trace_hardirqs_on
(linux/kernel/trace/trace_preemptirq.c:32)
4,147680,57161316247,-; ? copy_msghdr_from_user (linux/net/socket.c:2214)
4,147681,57161316361,-; ? __wake_up_common_lock (linux/kernel/sched/wait.c:125)
4,147682,57161316470,-; ? __wake_up_common (linux/kernel/sched/wait.c:112)
4,147683,57161316584,-; ? _raw_write_unlock_irq
(linux/arch/x86/include/asm/paravirt.h:777
linux/include/linux/rwlock_api_smp.h:267
linux/kernel/locking/spinlock.c:343)
4,147684,57161316757,-; ? trace_hardirqs_on
(linux/kernel/trace/trace_preemptirq.c:32)
4,147685,57161316867,-; ? __wake_up (linux/kernel/sched/wait.c:147)
4,147686,57161316985,-; ? netlink_bind (linux/net/netlink/af_netlink.c:981)
4,147687,57161317096,-; ? netlink_setsockopt
(linux/net/netlink/af_netlink.c:981)
4,147688,57161317223,-; ? kasan_check_read (linux/mm/kasan/common.c:95)
4,147689,57161317346,-; ? __fget_light
(linux/include/linux/compiler.h:194
linux/arch/x86/include/asm/atomic.h:31
linux/include/asm-generic/atomic-instrumented.h:27
linux/fs/file.c:770)
4,147690,57161317454,-; ? __sanitizer_cov_trace_const_cmp8
(linux/kernel/kcov.c:198)
4,147691,57161317561,-; ? sockfd_lookup_light (linux/net/socket.c:505)
4,147692,57161317685,-; __sys_sendmsg (linux/net/socket.c:2326)
4,147693,57161317821,-; ? __ia32_sys_shutdown (linux/net/socket.c:2312)
4,147694,57161317927,-; ? __fd_install
(linux/arch/x86/include/asm/preempt.h:84
linux/include/linux/rcupdate.h:724 linux/fs/file.c:608)
4,147695,57161318050,-; ? fd_install (linux/fs/file.c:614)
4,147696,57161318162,-; ? entry_SYSCALL_64_after_hwframe
(linux/arch/x86/entry/entry_64.S:177)
4,147697,57161318270,-; ? lockdep_hardirqs_on
(linux/kernel/locking/lockdep.c:3218
linux/kernel/locking/lockdep.c:3263)
4,147698,57161318413,-; __x64_sys_sendmsg (linux/net/socket.c:2331)
4,147699,57161318532,-; do_syscall_64 (linux/arch/x86/entry/common.c:301)
4,147700,57161318640,-; entry_SYSCALL_64_after_hwframe
(linux/arch/x86/entry/entry_64.S:177)
4,147701,57161318768,-;RIP: 0033:0x7f7c28863d04
4,147702,57161318901,-;Code: 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb
b5 0f 1f 80 00 00 00 00 48 8d 05 01 dc 2c 00 8b 00 85 c0 75 13 b8 2e
00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 54 f3 c3 66 90 41 54 55 41 89 d4
53 48 89 f5
All code
========
0: 00 f7 add %dh,%bh
2: d8 64 89 02 fsubs 0x2(%rcx,%rcx,4)
6: 48 c7 c0 ff ff ff ff mov $0xffffffffffffffff,%rax
d: eb b5 jmp 0xffffffffffffffc4
f: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
16: 48 8d 05 01 dc 2c 00 lea 0x2cdc01(%rip),%rax # 0x2cdc1e
1d: 8b 00 mov (%rax),%eax
1f: 85 c0 test %eax,%eax
21: 75 13 jne 0x36
23: b8 2e 00 00 00 mov $0x2e,%eax
28: 0f 05 syscall
2a:* 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax <-- trapping instruction
30: 77 54 ja 0x86
32: f3 c3 repz retq
34: 66 90 xchg %ax,%ax
36: 41 54 push %r12
38: 55 push %rbp
39: 41 89 d4 mov %edx,%r12d
3c: 53 push %rbx
3d: 48 89 f5 mov %rsi,%rbp
Code starting with the faulting instruction
===========================================
0: 48 3d 00 f0 ff ff cmp $0xfffffffffffff000,%rax
6: 77 54 ja 0x5c
8: f3 c3 repz retq
a: 66 90 xchg %ax,%ax
c: 41 54 push %r12
e: 55 push %rbp
f: 41 89 d4 mov %edx,%r12d
12: 53 push %rbx
13: 48 89 f5 mov %rsi,%rbp
4,147703,57161319051,-;RSP: 002b:00007ffd5901b648 EFLAGS: 00000246
ORIG_RAX: 000000000000002e
4,147704,57161319210,-;RAX: ffffffffffffffda RBX: 000000000000003b
RCX: 00007f7c28863d04
4,147705,57161319369,-;RDX: 0000000000000000 RSI: 00007ffd5901b710
RDI: 0000000000000006
4,147706,57161319495,-;RBP: 0000000000000006 R08: 0000000000000010
R09: 0000000000000000
4,147707,57161319664,-;R10: 00007ffd5901b758 R11: 0000000000000246
R12: 00007f7c28cc3f70
4,147708,57161319771,-;R13: 00007ffd5901d790 R14: 0000000000000000
R15: 00007ffd5901b750
Cheers,
Marek
^ permalink raw reply
* Re: [PATCH v2 4/4] net: dsa: vsc73xx: Assert reset if iCPU is enabled
From: Paweł Dembicki @ 2019-07-04 21:57 UTC (permalink / raw)
To: Linus Walleij
Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, David S. Miller,
Rob Herring, Mark Rutland, netdev,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-kernel@vger.kernel.org
In-Reply-To: <CACRpkdYsA5437Sb8J539AJ=cYtnO2MiD7w7V_Emrmk8dNKbaEQ@mail.gmail.com>
On 4/7/19 09:22 Linus Walleij <linus.walleij@linaro.org> wrote:
>
> My devices do not have direct access to the reset line so I
> can't assert reset no matter how I try, if it works for you, the
> code is certainly better like this.
>
In P2020RDB, VSC7385 reset is connected to GPIO. U-boot put binary
file to iCPU and make VSC7385 unmanaged.
However reset flush internal memory and iCPU stop.
In this case bootlog looks like that:
[ 2.989047] vsc73xx-platform ffb00000.switch: VSC7385 (rev: 2) switch found
[ 2.996192] vsc73xx-platform ffb00000.switch: iCPU enabled boots
from PI/SI, no external memory
[ 3.005057] vsc73xx-platform ffb00000.switch: Chip seems to be out
of control. Assert reset and try again.
[ 3.045034] vsc73xx-platform ffb00000.switch: VSC7385 (rev: 2) switch found
[ 3.052171] vsc73xx-platform ffb00000.switch: iCPU disabled, no
external memory
Best Regards,
Pawel Dembicki
^ permalink raw reply
* Re: [PATCH net-next v3 1/4] net/sched: Introduce action ct
From: Jakub Kicinski @ 2019-07-04 21:55 UTC (permalink / raw)
To: Paul Blakey
Cc: Jiri Pirko, Roi Dayan, Yossi Kuperman, Oz Shlomo,
Marcelo Ricardo Leitner, netdev, David Miller, Aaron Conole,
Zhike Wang, Rony Efraim, nst-kernel, John Hurley, Simon Horman,
Justin Pettit
In-Reply-To: <1562241233-5176-2-git-send-email-paulb@mellanox.com>
On Thu, 4 Jul 2019 14:53:50 +0300, Paul Blakey wrote:
> +static const struct nla_policy ct_policy[TCA_CT_MAX + 1] = {
> + [TCA_CT_ACTION] = { .type = NLA_U16 },
Please use strict checking in all new policies.
attr 0 must have .strict_start_type set.
^ permalink raw reply
* [PATCH net 2/2] selftests/tls: add test for poll() with data in TLS ULP
From: Jakub Kicinski @ 2019-07-04 21:50 UTC (permalink / raw)
To: davem
Cc: netdev, oss-drivers, alexei.starovoitov, Jakub Kicinski,
Dirk van der Merwe
In-Reply-To: <20190704215037.6008-1-jakub.kicinski@netronome.com>
Add a test which checks if leftover record data in TLS
layer correctly wakes up poll().
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com>
---
tools/testing/selftests/net/tls.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index 278c86134556..090fff9dbc48 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -644,6 +644,32 @@ TEST_F(tls, poll_wait)
EXPECT_EQ(recv(self->cfd, recv_mem, send_len, MSG_WAITALL), send_len);
}
+TEST_F(tls, poll_wait_split)
+{
+ struct pollfd fd = { 0, 0, 0 };
+ char send_mem[20] = {};
+ char recv_mem[15];
+
+ fd.fd = self->cfd;
+ fd.events = POLLIN;
+ /* Send 20 bytes */
+ EXPECT_EQ(send(self->fd, send_mem, sizeof(send_mem), 0),
+ sizeof(send_mem));
+ /* Poll with inf. timeout */
+ EXPECT_EQ(poll(&fd, 1, -1), 1);
+ EXPECT_EQ(fd.revents & POLLIN, 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), MSG_WAITALL),
+ sizeof(recv_mem));
+
+ /* Now the remaining 5 bytes of record data are in TLS ULP */
+ fd.fd = self->cfd;
+ fd.events = POLLIN;
+ EXPECT_EQ(poll(&fd, 1, -1), 1);
+ EXPECT_EQ(fd.revents & POLLIN, 1);
+ EXPECT_EQ(recv(self->cfd, recv_mem, sizeof(recv_mem), 0),
+ sizeof(send_mem) - sizeof(recv_mem));
+}
+
TEST_F(tls, blocking)
{
size_t data = 100000;
--
2.21.0
^ permalink raw reply related
* [PATCH net 1/2] net/tls: fix poll ignoring partially copied records
From: Jakub Kicinski @ 2019-07-04 21:50 UTC (permalink / raw)
To: davem
Cc: netdev, oss-drivers, alexei.starovoitov, Jakub Kicinski,
David Beckett, Dirk van der Merwe
In-Reply-To: <20190704215037.6008-1-jakub.kicinski@netronome.com>
David reports that RPC applications which use epoll() occasionally
get stuck, and that TLS ULP causes the kernel to not wake applications,
even though read() will return data.
This is indeed true. The ctx->rx_list which holds partially copied
records is not consulted when deciding whether socket is readable.
Note that SO_RCVLOWAT with epoll() is and has always been broken for
kernel TLS. We'd need to parse all records from the TCP layer, instead
of just the first one.
Fixes: 692d7b5d1f91 ("tls: Fix recvmsg() to be able to peek across multiple records")
Reported-by: David Beckett <david.beckett@netronome.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com>
---
net/tls/tls_sw.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 455a782c7658..e2385183526e 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1958,7 +1958,8 @@ bool tls_sw_stream_read(const struct sock *sk)
ingress_empty = list_empty(&psock->ingress_msg);
rcu_read_unlock();
- return !ingress_empty || ctx->recv_pkt;
+ return !ingress_empty || ctx->recv_pkt ||
+ !skb_queue_empty(&ctx->rx_list);
}
static int tls_read_size(struct strparser *strp, struct sk_buff *skb)
--
2.21.0
^ permalink raw reply related
* [PATCH net 0/2] net/tls: fix poll() wake up
From: Jakub Kicinski @ 2019-07-04 21:50 UTC (permalink / raw)
To: davem; +Cc: netdev, oss-drivers, alexei.starovoitov, Jakub Kicinski
Hi!
This small fix + selftest series is very similar to the previous
commit 04b25a5411f9 ("net/tls: fix no wakeup on partial reads").
This time instead of recvmsg we're fixing poll wake up.
Jakub Kicinski (2):
net/tls: fix poll ignoring partially copied records
selftests/tls: add test for poll() with data in TLS ULP
net/tls/tls_sw.c | 3 ++-
tools/testing/selftests/net/tls.c | 26 ++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
--
2.21.0
^ permalink raw reply
* Re: [net-next 14/14] net/mlx5e: Add kTLS TX HW offload support
From: Jakub Kicinski @ 2019-07-04 21:35 UTC (permalink / raw)
To: Saeed Mahameed
Cc: David S. Miller, netdev@vger.kernel.org, Tariq Toukan,
Eran Ben Elisha, Boris Pismenny
In-Reply-To: <20190704181235.8966-15-saeedm@mellanox.com>
On Thu, 4 Jul 2019 18:16:15 +0000, Saeed Mahameed wrote:
> +struct sk_buff *mlx5e_ktls_handle_tx_skb(struct net_device *netdev,
> + struct mlx5e_txqsq *sq,
> + struct sk_buff *skb,
> + struct mlx5e_tx_wqe **wqe, u16 *pi)
> +{
> + struct mlx5e_ktls_offload_context_tx *priv_tx;
> + struct mlx5e_sq_stats *stats = sq->stats;
> + struct mlx5_wqe_ctrl_seg *cseg;
> + struct tls_context *tls_ctx;
> + int datalen;
> + u32 seq;
> +
> + if (!skb->sk || !tls_is_sk_tx_device_offloaded(skb->sk))
> + goto out;
> +
> + datalen = skb->len - (skb_transport_offset(skb) + tcp_hdrlen(skb));
> + if (!datalen)
> + goto out;
> +
> + tls_ctx = tls_get_ctx(skb->sk);
> + if (unlikely(tls_ctx->netdev != netdev))
> + goto out;
If a frame for a different device leaked through the stack, we need to
fix the stack. Also you should drop the frame in this case, the device
will no encrypt it.
> + priv_tx = mlx5e_get_ktls_tx_priv_ctx(tls_ctx);
> +
> + if (unlikely(mlx5e_ktls_tx_offload_test_and_clear_pending(priv_tx))) {
> + mlx5e_ktls_tx_post_param_wqes(sq, priv_tx, false, false);
> + *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi);
> + stats->ktls_ctx++;
> + }
> +
> + seq = ntohl(tcp_hdr(skb)->seq);
> + if (unlikely(priv_tx->expected_seq != seq)) {
> + skb = mlx5e_ktls_tx_handle_ooo(priv_tx, sq, skb, seq);
> + if (unlikely(!skb))
> + goto out;
> + *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi);
> + }
> +
> + priv_tx->expected_seq = seq + datalen;
> +
> + cseg = &(*wqe)->ctrl;
> + cseg->imm = cpu_to_be32(priv_tx->tisn);
> +
> + stats->ktls_enc_packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
> + stats->ktls_enc_bytes += datalen;
> +
> +out:
> + return skb;
> +}
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
> index 483d321d2151..6854f132d505 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
> @@ -50,6 +50,15 @@ static const struct counter_desc sw_stats_desc[] = {
> #ifdef CONFIG_MLX5_EN_TLS
> { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_ooo) },
> { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tls_resync_bytes) },
> +
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_ooo) },
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_ooo_drop_no_sync_data) },
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_ooo_drop_bypass_req) },
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_ooo_dump_bytes) },
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_ooo_dump_packets) },
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_enc_packets) },
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_enc_bytes) },
> + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_ktls_ctx) },
> #endif
>
> { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_lro_packets) },
> @@ -218,6 +227,16 @@ static void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv)
> #ifdef CONFIG_MLX5_EN_TLS
> s->tx_tls_ooo += sq_stats->tls_ooo;
> s->tx_tls_resync_bytes += sq_stats->tls_resync_bytes;
> +
> + s->tx_ktls_enc_packets += sq_stats->ktls_enc_packets;
> + s->tx_ktls_enc_bytes += sq_stats->ktls_enc_bytes;
> + s->tx_ktls_ooo += sq_stats->ktls_ooo;
> + s->tx_ktls_ooo_drop_no_sync_data +=
> + sq_stats->ktls_ooo_drop_no_sync_data;
> + s->tx_ktls_ooo_drop_bypass_req += sq_stats->ktls_ooo_drop_bypass_req;
> + s->tx_ktls_ooo_dump_bytes += sq_stats->ktls_ooo_dump_bytes;
> + s->tx_ktls_ooo_dump_packets += sq_stats->ktls_ooo_dump_packets;
> + s->tx_ktls_ctx += sq_stats->ktls_ctx;
> #endif
> s->tx_cqes += sq_stats->cqes;
> }
> @@ -1238,6 +1257,16 @@ static const struct counter_desc sq_stats_desc[] = {
> { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, csum_partial_inner) },
> { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, added_vlan_packets) },
> { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, nop) },
> +#ifdef CONFIG_MLX5_EN_TLS
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_ooo) },
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_ooo_drop_no_sync_data) },
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_ooo_drop_bypass_req) },
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_ooo_dump_bytes) },
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_ooo_dump_packets) },
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_enc_packets) },
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_enc_bytes) },
> + { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, ktls_ctx) },
> +#endif
> { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, csum_none) },
> { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, stopped) },
> { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, dropped) },
For the stats as discussed please use common names, and preferably
add yours to the doc too, so we have them documented. Unless the
stat seems very specific to your implementation, perhaps.
^ permalink raw reply
* [RFC bpf-next 5/8] bpf: migrate fixup_bpf_calls to list patching infra
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
This patch migrate fixup_bpf_calls to new list patching
infrastructure.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
kernel/bpf/verifier.c | 94 +++++++++++++++++++++++++++------------------------
1 file changed, 49 insertions(+), 45 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2d16e85..30ed28e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9033,16 +9033,19 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
{
struct bpf_prog *prog = env->prog;
struct bpf_insn *insn = prog->insnsi;
+ struct bpf_list_insn *list, *elem;
const struct bpf_func_proto *fn;
- const int insn_cnt = prog->len;
const struct bpf_map_ops *ops;
struct bpf_insn_aux_data *aux;
struct bpf_insn insn_buf[16];
- struct bpf_prog *new_prog;
struct bpf_map *map_ptr;
- int i, cnt, delta = 0;
+ int cnt, ret = 0;
- for (i = 0; i < insn_cnt; i++, insn++) {
+ list = bpf_create_list_insn(env->prog);
+ if (IS_ERR(list))
+ return PTR_ERR(list);
+ for (elem = list; elem; elem = elem->next) {
+ insn = &elem->insn;
if (insn->code == (BPF_ALU64 | BPF_MOD | BPF_X) ||
insn->code == (BPF_ALU64 | BPF_DIV | BPF_X) ||
insn->code == (BPF_ALU | BPF_MOD | BPF_X) ||
@@ -9073,13 +9076,11 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
cnt = ARRAY_SIZE(mask_and_mod) - (is64 ? 1 : 0);
}
- new_prog = bpf_patch_insn_data(env, i + delta, patchlet, cnt);
- if (!new_prog)
- return -ENOMEM;
-
- delta += cnt - 1;
- env->prog = prog = new_prog;
- insn = new_prog->insnsi + i + delta;
+ elem = bpf_patch_list_insn(elem, patchlet, cnt);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
continue;
}
@@ -9089,16 +9090,15 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
cnt = env->ops->gen_ld_abs(insn, insn_buf);
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
verbose(env, "bpf verifier is misconfigured\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_list_ret;
}
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
- if (!new_prog)
- return -ENOMEM;
-
- delta += cnt - 1;
- env->prog = prog = new_prog;
- insn = new_prog->insnsi + i + delta;
+ elem = bpf_patch_list_insn(elem, insn_buf, cnt);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
continue;
}
@@ -9111,7 +9111,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
bool issrc, isneg;
u32 off_reg;
- aux = &env->insn_aux_data[i + delta];
+ aux = &env->insn_aux_data[elem->orig_idx - 1];
if (!aux->alu_state ||
aux->alu_state == BPF_ALU_NON_POINTER)
continue;
@@ -9144,13 +9144,12 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);
cnt = patch - insn_buf;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
- if (!new_prog)
- return -ENOMEM;
+ elem = bpf_patch_list_insn(elem, insn_buf, cnt);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
- delta += cnt - 1;
- env->prog = prog = new_prog;
- insn = new_prog->insnsi + i + delta;
continue;
}
@@ -9183,7 +9182,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
insn->imm = 0;
insn->code = BPF_JMP | BPF_TAIL_CALL;
- aux = &env->insn_aux_data[i + delta];
+ aux = &env->insn_aux_data[elem->orig_idx - 1];
if (!bpf_map_ptr_unpriv(aux))
continue;
@@ -9195,7 +9194,8 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
*/
if (bpf_map_ptr_poisoned(aux)) {
verbose(env, "tail_call abusing map_ptr\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_list_ret;
}
map_ptr = BPF_MAP_PTR(aux->map_state);
@@ -9207,13 +9207,12 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
map)->index_mask);
insn_buf[2] = *insn;
cnt = 3;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
- if (!new_prog)
- return -ENOMEM;
+ elem = bpf_patch_list_insn(elem, insn_buf, cnt);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
- delta += cnt - 1;
- env->prog = prog = new_prog;
- insn = new_prog->insnsi + i + delta;
continue;
}
@@ -9228,7 +9227,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
insn->imm == BPF_FUNC_map_push_elem ||
insn->imm == BPF_FUNC_map_pop_elem ||
insn->imm == BPF_FUNC_map_peek_elem)) {
- aux = &env->insn_aux_data[i + delta];
+ aux = &env->insn_aux_data[elem->orig_idx - 1];
if (bpf_map_ptr_poisoned(aux))
goto patch_call_imm;
@@ -9239,17 +9238,16 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
cnt = ops->map_gen_lookup(map_ptr, insn_buf);
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
verbose(env, "bpf verifier is misconfigured\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_list_ret;
}
- new_prog = bpf_patch_insn_data(env, i + delta,
- insn_buf, cnt);
- if (!new_prog)
- return -ENOMEM;
+ elem = bpf_patch_list_insn(elem, insn_buf, cnt);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
- delta += cnt - 1;
- env->prog = prog = new_prog;
- insn = new_prog->insnsi + i + delta;
continue;
}
@@ -9307,12 +9305,18 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
verbose(env,
"kernel subsystem misconfigured func %s#%d\n",
func_id_name(insn->imm), insn->imm);
- return -EFAULT;
+ ret = -EFAULT;
+ goto free_list_ret;
}
insn->imm = fn->func - __bpf_call_base;
}
- return 0;
+ env = verifier_linearize_list_insn(env, list);
+ if (IS_ERR(env))
+ ret = PTR_ERR(env);
+free_list_ret:
+ bpf_destroy_list_insn(list);
+ return ret;
}
static void free_states(struct bpf_verifier_env *env)
--
2.7.4
^ permalink raw reply related
* [RFC bpf-next 8/8] bpf: delete all those code around old insn patching infrastructure
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
This patch delete all code around old insn patching infrastructure.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
include/linux/bpf_verifier.h | 1 -
include/linux/filter.h | 4 -
kernel/bpf/core.c | 169 ---------------------------------
kernel/bpf/verifier.c | 221 +------------------------------------------
4 files changed, 1 insertion(+), 394 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 5fe99f3..79c1733 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -305,7 +305,6 @@ struct bpf_insn_aux_data {
bool zext_dst; /* this insn zero extends dst reg */
u8 alu_state; /* used in combination with alu_limit */
bool prune_point;
- unsigned int orig_idx; /* original instruction index */
};
#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 1fea68c..fcfe0b0 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -838,10 +838,6 @@ static inline bool bpf_dump_raw_ok(void)
return kallsyms_show_value() == 1;
}
-struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
- const struct bpf_insn *patch, u32 len);
-int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt);
-
int bpf_jit_adj_imm_off(struct bpf_insn *insn, int old_idx, int new_idx,
int idx_map[]);
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index c3a5f84..716220b 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -333,175 +333,6 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
return 0;
}
-static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, s32 end_old,
- s32 end_new, s32 curr, const bool probe_pass)
-{
- const s64 imm_min = S32_MIN, imm_max = S32_MAX;
- s32 delta = end_new - end_old;
- s64 imm = insn->imm;
-
- if (curr < pos && curr + imm + 1 >= end_old)
- imm += delta;
- else if (curr >= end_new && curr + imm + 1 < end_new)
- imm -= delta;
- if (imm < imm_min || imm > imm_max)
- return -ERANGE;
- if (!probe_pass)
- insn->imm = imm;
- return 0;
-}
-
-static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, s32 end_old,
- s32 end_new, s32 curr, const bool probe_pass)
-{
- const s32 off_min = S16_MIN, off_max = S16_MAX;
- s32 delta = end_new - end_old;
- s32 off = insn->off;
-
- if (curr < pos && curr + off + 1 >= end_old)
- off += delta;
- else if (curr >= end_new && curr + off + 1 < end_new)
- off -= delta;
- if (off < off_min || off > off_max)
- return -ERANGE;
- if (!probe_pass)
- insn->off = off;
- return 0;
-}
-
-static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, s32 end_old,
- s32 end_new, const bool probe_pass)
-{
- u32 i, insn_cnt = prog->len + (probe_pass ? end_new - end_old : 0);
- struct bpf_insn *insn = prog->insnsi;
- int ret = 0;
-
- for (i = 0; i < insn_cnt; i++, insn++) {
- u8 code;
-
- /* In the probing pass we still operate on the original,
- * unpatched image in order to check overflows before we
- * do any other adjustments. Therefore skip the patchlet.
- */
- if (probe_pass && i == pos) {
- i = end_new;
- insn = prog->insnsi + end_old;
- }
- code = insn->code;
- if ((BPF_CLASS(code) != BPF_JMP &&
- BPF_CLASS(code) != BPF_JMP32) ||
- BPF_OP(code) == BPF_EXIT)
- continue;
- /* Adjust offset of jmps if we cross patch boundaries. */
- if (BPF_OP(code) == BPF_CALL) {
- if (insn->src_reg != BPF_PSEUDO_CALL)
- continue;
- ret = bpf_adj_delta_to_imm(insn, pos, end_old,
- end_new, i, probe_pass);
- } else {
- ret = bpf_adj_delta_to_off(insn, pos, end_old,
- end_new, i, probe_pass);
- }
- if (ret)
- break;
- }
-
- return ret;
-}
-
-static void bpf_adj_linfo(struct bpf_prog *prog, u32 off, u32 delta)
-{
- struct bpf_line_info *linfo;
- u32 i, nr_linfo;
-
- nr_linfo = prog->aux->nr_linfo;
- if (!nr_linfo || !delta)
- return;
-
- linfo = prog->aux->linfo;
-
- for (i = 0; i < nr_linfo; i++)
- if (off < linfo[i].insn_off)
- break;
-
- /* Push all off < linfo[i].insn_off by delta */
- for (; i < nr_linfo; i++)
- linfo[i].insn_off += delta;
-}
-
-struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
- const struct bpf_insn *patch, u32 len)
-{
- u32 insn_adj_cnt, insn_rest, insn_delta = len - 1;
- const u32 cnt_max = S16_MAX;
- struct bpf_prog *prog_adj;
- int err;
-
- /* Since our patchlet doesn't expand the image, we're done. */
- if (insn_delta == 0) {
- memcpy(prog->insnsi + off, patch, sizeof(*patch));
- return prog;
- }
-
- insn_adj_cnt = prog->len + insn_delta;
-
- /* Reject anything that would potentially let the insn->off
- * target overflow when we have excessive program expansions.
- * We need to probe here before we do any reallocation where
- * we afterwards may not fail anymore.
- */
- if (insn_adj_cnt > cnt_max &&
- (err = bpf_adj_branches(prog, off, off + 1, off + len, true)))
- return ERR_PTR(err);
-
- /* Several new instructions need to be inserted. Make room
- * for them. Likely, there's no need for a new allocation as
- * last page could have large enough tailroom.
- */
- prog_adj = bpf_prog_realloc(prog, bpf_prog_size(insn_adj_cnt),
- GFP_USER);
- if (!prog_adj)
- return ERR_PTR(-ENOMEM);
-
- prog_adj->len = insn_adj_cnt;
-
- /* Patching happens in 3 steps:
- *
- * 1) Move over tail of insnsi from next instruction onwards,
- * so we can patch the single target insn with one or more
- * new ones (patching is always from 1 to n insns, n > 0).
- * 2) Inject new instructions at the target location.
- * 3) Adjust branch offsets if necessary.
- */
- insn_rest = insn_adj_cnt - off - len;
-
- memmove(prog_adj->insnsi + off + len, prog_adj->insnsi + off + 1,
- sizeof(*patch) * insn_rest);
- memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len);
-
- /* We are guaranteed to not fail at this point, otherwise
- * the ship has sailed to reverse to the original state. An
- * overflow cannot happen at this point.
- */
- BUG_ON(bpf_adj_branches(prog_adj, off, off + 1, off + len, false));
-
- bpf_adj_linfo(prog_adj, off, insn_delta);
-
- return prog_adj;
-}
-
-int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt)
-{
- /* Branch offsets can't overflow when program is shrinking, no need
- * to call bpf_adj_branches(..., true) here
- */
- memmove(prog->insnsi + off, prog->insnsi + off + cnt,
- sizeof(struct bpf_insn) * (prog->len - off - cnt));
- prog->len -= cnt;
-
- return WARN_ON_ONCE(bpf_adj_branches(prog, off, off + cnt, off, false));
-}
-
int bpf_jit_adj_imm_off(struct bpf_insn *insn, int old_idx, int new_idx,
s32 idx_map[])
{
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index abe11fd..9e5618f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8067,223 +8067,6 @@ static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env)
insn->src_reg = 0;
}
-/* single env->prog->insni[off] instruction was replaced with the range
- * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying
- * [0, off) and [off, end) to new locations, so the patched range stays zero
- */
-static int adjust_insn_aux_data(struct bpf_verifier_env *env,
- struct bpf_prog *new_prog, u32 off, u32 cnt)
-{
- struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data;
- struct bpf_insn *insn = new_prog->insnsi;
- u32 prog_len;
- int i;
-
- /* aux info at OFF always needs adjustment, no matter fast path
- * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the
- * original insn at old prog.
- */
- old_data[off].zext_dst = insn_has_def32(env, insn + off + cnt - 1);
-
- if (cnt == 1)
- return 0;
- prog_len = new_prog->len;
- new_data = vzalloc(array_size(prog_len,
- sizeof(struct bpf_insn_aux_data)));
- if (!new_data)
- return -ENOMEM;
- memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off);
- memcpy(new_data + off + cnt - 1, old_data + off,
- sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1));
- for (i = off; i < off + cnt - 1; i++) {
- new_data[i].seen = true;
- new_data[i].zext_dst = insn_has_def32(env, insn + i);
- }
- env->insn_aux_data = new_data;
- vfree(old_data);
- return 0;
-}
-
-static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len)
-{
- int i;
-
- if (len == 1)
- return;
- /* NOTE: fake 'exit' subprog should be updated as well. */
- for (i = 0; i <= env->subprog_cnt; i++) {
- if (env->subprog_info[i].start <= off)
- continue;
- env->subprog_info[i].start += len - 1;
- }
-}
-
-static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
- const struct bpf_insn *patch, u32 len)
-{
- struct bpf_prog *new_prog;
-
- new_prog = bpf_patch_insn_single(env->prog, off, patch, len);
- if (IS_ERR(new_prog)) {
- if (PTR_ERR(new_prog) == -ERANGE)
- verbose(env,
- "insn %d cannot be patched due to 16-bit range\n",
- env->insn_aux_data[off].orig_idx);
- return NULL;
- }
- if (adjust_insn_aux_data(env, new_prog, off, len))
- return NULL;
- adjust_subprog_starts(env, off, len);
- return new_prog;
-}
-
-static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env,
- u32 off, u32 cnt)
-{
- int i, j;
-
- /* find first prog starting at or after off (first to remove) */
- for (i = 0; i < env->subprog_cnt; i++)
- if (env->subprog_info[i].start >= off)
- break;
- /* find first prog starting at or after off + cnt (first to stay) */
- for (j = i; j < env->subprog_cnt; j++)
- if (env->subprog_info[j].start >= off + cnt)
- break;
- /* if j doesn't start exactly at off + cnt, we are just removing
- * the front of previous prog
- */
- if (env->subprog_info[j].start != off + cnt)
- j--;
-
- if (j > i) {
- struct bpf_prog_aux *aux = env->prog->aux;
- int move;
-
- /* move fake 'exit' subprog as well */
- move = env->subprog_cnt + 1 - j;
-
- memmove(env->subprog_info + i,
- env->subprog_info + j,
- sizeof(*env->subprog_info) * move);
- env->subprog_cnt -= j - i;
-
- /* remove func_info */
- if (aux->func_info) {
- move = aux->func_info_cnt - j;
-
- memmove(aux->func_info + i,
- aux->func_info + j,
- sizeof(*aux->func_info) * move);
- aux->func_info_cnt -= j - i;
- /* func_info->insn_off is set after all code rewrites,
- * in adjust_btf_func() - no need to adjust
- */
- }
- } else {
- /* convert i from "first prog to remove" to "first to adjust" */
- if (env->subprog_info[i].start == off)
- i++;
- }
-
- /* update fake 'exit' subprog as well */
- for (; i <= env->subprog_cnt; i++)
- env->subprog_info[i].start -= cnt;
-
- return 0;
-}
-
-static int bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off,
- u32 cnt)
-{
- struct bpf_prog *prog = env->prog;
- u32 i, l_off, l_cnt, nr_linfo;
- struct bpf_line_info *linfo;
-
- nr_linfo = prog->aux->nr_linfo;
- if (!nr_linfo)
- return 0;
-
- linfo = prog->aux->linfo;
-
- /* find first line info to remove, count lines to be removed */
- for (i = 0; i < nr_linfo; i++)
- if (linfo[i].insn_off >= off)
- break;
-
- l_off = i;
- l_cnt = 0;
- for (; i < nr_linfo; i++)
- if (linfo[i].insn_off < off + cnt)
- l_cnt++;
- else
- break;
-
- /* First live insn doesn't match first live linfo, it needs to "inherit"
- * last removed linfo. prog is already modified, so prog->len == off
- * means no live instructions after (tail of the program was removed).
- */
- if (prog->len != off && l_cnt &&
- (i == nr_linfo || linfo[i].insn_off != off + cnt)) {
- l_cnt--;
- linfo[--i].insn_off = off + cnt;
- }
-
- /* remove the line info which refer to the removed instructions */
- if (l_cnt) {
- memmove(linfo + l_off, linfo + i,
- sizeof(*linfo) * (nr_linfo - i));
-
- prog->aux->nr_linfo -= l_cnt;
- nr_linfo = prog->aux->nr_linfo;
- }
-
- /* pull all linfo[i].insn_off >= off + cnt in by cnt */
- for (i = l_off; i < nr_linfo; i++)
- linfo[i].insn_off -= cnt;
-
- /* fix up all subprogs (incl. 'exit') which start >= off */
- for (i = 0; i <= env->subprog_cnt; i++)
- if (env->subprog_info[i].linfo_idx > l_off) {
- /* program may have started in the removed region but
- * may not be fully removed
- */
- if (env->subprog_info[i].linfo_idx >= l_off + l_cnt)
- env->subprog_info[i].linfo_idx -= l_cnt;
- else
- env->subprog_info[i].linfo_idx = l_off;
- }
-
- return 0;
-}
-
-static int verifier_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt)
-{
- struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
- unsigned int orig_prog_len = env->prog->len;
- int err;
-
- if (bpf_prog_is_dev_bound(env->prog->aux))
- bpf_prog_offload_remove_insns(env, off, cnt);
-
- err = bpf_remove_insns(env->prog, off, cnt);
- if (err)
- return err;
-
- err = adjust_subprog_starts_after_remove(env, off, cnt);
- if (err)
- return err;
-
- err = bpf_adj_linfo_after_remove(env, off, cnt);
- if (err)
- return err;
-
- memmove(aux_data + off, aux_data + off + cnt,
- sizeof(*aux_data) * (orig_prog_len - off - cnt));
-
- return 0;
-}
-
/* The verifier does more data flow analysis than llvm and will not
* explore branches that are dead at run time. Malicious programs can
* have dead code too. Therefore replace all dead at-run-time code
@@ -9365,7 +9148,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
u64 start_time = ktime_get_ns();
struct bpf_verifier_env *env;
struct bpf_verifier_log *log;
- int i, len, ret = -EINVAL;
+ int len, ret = -EINVAL;
bool is_priv;
/* no program is valid */
@@ -9386,8 +9169,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
ret = -ENOMEM;
if (!env->insn_aux_data)
goto err_free_env;
- for (i = 0; i < len; i++)
- env->insn_aux_data[i].orig_idx = i;
env->prog = *prog;
env->ops = bpf_verifier_ops[env->prog->type];
is_priv = capable(CAP_SYS_ADMIN);
--
2.7.4
^ permalink raw reply related
* [RFC bpf-next 6/8] bpf: migrate zero extension opt to list patching infra
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
This patch migrate 32-bit zero extension insertion to new list patching
infrastructure.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
kernel/bpf/verifier.c | 45 +++++++++++++++++++++++++--------------------
1 file changed, 25 insertions(+), 20 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 30ed28e..58d6bbe 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8549,10 +8549,9 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
const union bpf_attr *attr)
{
struct bpf_insn *patch, zext_patch[2], rnd_hi32_patch[4];
- struct bpf_insn_aux_data *aux = env->insn_aux_data;
- int i, patch_len, delta = 0, len = env->prog->len;
- struct bpf_insn *insns = env->prog->insnsi;
- struct bpf_prog *new_prog;
+ struct bpf_list_insn *list, *elem;
+ struct bpf_insn_aux_data *aux;
+ int patch_len, ret = 0;
bool rnd_hi32;
rnd_hi32 = attr->prog_flags & BPF_F_TEST_RND_HI32;
@@ -8560,12 +8559,16 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
rnd_hi32_patch[1] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, 0);
rnd_hi32_patch[2] = BPF_ALU64_IMM(BPF_LSH, BPF_REG_AX, 32);
rnd_hi32_patch[3] = BPF_ALU64_REG(BPF_OR, 0, BPF_REG_AX);
- for (i = 0; i < len; i++) {
- int adj_idx = i + delta;
- struct bpf_insn insn;
- insn = insns[adj_idx];
- if (!aux[adj_idx].zext_dst) {
+ list = bpf_create_list_insn(env->prog);
+ if (IS_ERR(list))
+ return PTR_ERR(list);
+
+ for (elem = list; elem; elem = elem->next) {
+ struct bpf_insn insn = elem->insn;
+
+ aux = &env->insn_aux_data[elem->orig_idx - 1];
+ if (!aux->zext_dst) {
u8 code, class;
u32 imm_rnd;
@@ -8584,13 +8587,13 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
if (is_reg64(env, &insn, insn.dst_reg, NULL, DST_OP)) {
if (class == BPF_LD &&
BPF_MODE(code) == BPF_IMM)
- i++;
+ elem = elem->next;
continue;
}
/* ctx load could be transformed into wider load. */
if (class == BPF_LDX &&
- aux[adj_idx].ptr_type == PTR_TO_CTX)
+ aux->ptr_type == PTR_TO_CTX)
continue;
imm_rnd = get_random_int();
@@ -8611,16 +8614,18 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
patch = zext_patch;
patch_len = 2;
apply_patch_buffer:
- new_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len);
- if (!new_prog)
- return -ENOMEM;
- env->prog = new_prog;
- insns = new_prog->insnsi;
- aux = env->insn_aux_data;
- delta += patch_len - 1;
+ elem = bpf_patch_list_insn(elem, patch, patch_len);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
}
-
- return 0;
+ env = verifier_linearize_list_insn(env, list);
+ if (IS_ERR(env))
+ ret = PTR_ERR(env);
+free_list_ret:
+ bpf_destroy_list_insn(list);
+ return ret;
}
/* convert load instructions that access fields of a context type into a
--
2.7.4
^ permalink raw reply related
* [RFC bpf-next 4/8] bpf: migrate convert_ctx_accesses to list patching infra
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
This patch migrate convert_ctx_accesses to new list patching
infrastructure. pre-patch is used for generating prologue, because what we
really want to do is insert the prog before prog start without touching
the first insn.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
kernel/bpf/verifier.c | 98 ++++++++++++++++++++++++++++++---------------------
1 file changed, 58 insertions(+), 40 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2026d64..2d16e85 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8631,41 +8631,59 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
static int convert_ctx_accesses(struct bpf_verifier_env *env)
{
const struct bpf_verifier_ops *ops = env->ops;
- int i, cnt, size, ctx_field_size, delta = 0;
- const int insn_cnt = env->prog->len;
struct bpf_insn insn_buf[16], *insn;
u32 target_size, size_default, off;
- struct bpf_prog *new_prog;
+ struct bpf_list_insn *list, *elem;
+ int cnt, size, ctx_field_size;
enum bpf_access_type type;
bool is_narrower_load;
+ int ret = 0;
+
+ list = bpf_create_list_insn(env->prog);
+ if (IS_ERR(list))
+ return PTR_ERR(list);
+ elem = list;
if (ops->gen_prologue || env->seen_direct_write) {
if (!ops->gen_prologue) {
verbose(env, "bpf verifier is misconfigured\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_list_ret;
}
cnt = ops->gen_prologue(insn_buf, env->seen_direct_write,
env->prog);
if (cnt >= ARRAY_SIZE(insn_buf)) {
verbose(env, "bpf verifier is misconfigured\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_list_ret;
} else if (cnt) {
- new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);
- if (!new_prog)
- return -ENOMEM;
+ struct bpf_list_insn *new_hdr;
- env->prog = new_prog;
- delta += cnt - 1;
+ /* "gen_prologue" generates patch buffer, we want to use
+ * pre-patch buffer because we don't want to touch the
+ * insn/aux at start offset.
+ */
+ new_hdr = bpf_prepatch_list_insn(list, insn_buf,
+ cnt - 1);
+ if (IS_ERR(new_hdr)) {
+ ret = -ENOMEM;
+ goto free_list_ret;
+ }
+ /* Update list head, so new pre-patched nodes could be
+ * freed by destroyer.
+ */
+ list = new_hdr;
}
}
if (bpf_prog_is_dev_bound(env->prog->aux))
- return 0;
+ goto linearize_list_ret;
- insn = env->prog->insnsi + delta;
-
- for (i = 0; i < insn_cnt; i++, insn++) {
+ for (; elem; elem = elem->next) {
bpf_convert_ctx_access_t convert_ctx_access;
+ struct bpf_insn_aux_data *aux;
+
+ insn = &elem->insn;
if (insn->code == (BPF_LDX | BPF_MEM | BPF_B) ||
insn->code == (BPF_LDX | BPF_MEM | BPF_H) ||
@@ -8680,8 +8698,8 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
else
continue;
- if (type == BPF_WRITE &&
- env->insn_aux_data[i + delta].sanitize_stack_off) {
+ aux = &env->insn_aux_data[elem->orig_idx - 1];
+ if (type == BPF_WRITE && aux->sanitize_stack_off) {
struct bpf_insn patch[] = {
/* Sanitize suspicious stack slot with zero.
* There are no memory dependencies for this store,
@@ -8689,8 +8707,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
* constant of zero
*/
BPF_ST_MEM(BPF_DW, BPF_REG_FP,
- env->insn_aux_data[i + delta].sanitize_stack_off,
- 0),
+ aux->sanitize_stack_off, 0),
/* the original STX instruction will immediately
* overwrite the same stack slot with appropriate value
*/
@@ -8698,17 +8715,15 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
};
cnt = ARRAY_SIZE(patch);
- new_prog = bpf_patch_insn_data(env, i + delta, patch, cnt);
- if (!new_prog)
- return -ENOMEM;
-
- delta += cnt - 1;
- env->prog = new_prog;
- insn = new_prog->insnsi + i + delta;
+ elem = bpf_patch_list_insn(elem, patch, cnt);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
continue;
}
- switch (env->insn_aux_data[i + delta].ptr_type) {
+ switch (aux->ptr_type) {
case PTR_TO_CTX:
if (!ops->convert_ctx_access)
continue;
@@ -8728,7 +8743,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
continue;
}
- ctx_field_size = env->insn_aux_data[i + delta].ctx_field_size;
+ ctx_field_size = aux->ctx_field_size;
size = BPF_LDST_BYTES(insn);
/* If the read access is a narrower load of the field,
@@ -8744,7 +8759,8 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
if (type == BPF_WRITE) {
verbose(env, "bpf verifier narrow ctx access misconfigured\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_list_ret;
}
size_code = BPF_H;
@@ -8763,7 +8779,8 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf) ||
(ctx_field_size && !target_size)) {
verbose(env, "bpf verifier is misconfigured\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_list_ret;
}
if (is_narrower_load && size < target_size) {
@@ -8786,18 +8803,19 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
}
}
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
- if (!new_prog)
- return -ENOMEM;
-
- delta += cnt - 1;
-
- /* keep walking new program and skip insns we just inserted */
- env->prog = new_prog;
- insn = new_prog->insnsi + i + delta;
+ elem = bpf_patch_list_insn(elem, insn_buf, cnt);
+ if (IS_ERR(elem)) {
+ ret = PTR_ERR(elem);
+ goto free_list_ret;
+ }
}
-
- return 0;
+linearize_list_ret:
+ env = verifier_linearize_list_insn(env, list);
+ if (IS_ERR(env))
+ ret = PTR_ERR(env);
+free_list_ret:
+ bpf_destroy_list_insn(list);
+ return ret;
}
static int jit_subprogs(struct bpf_verifier_env *env)
--
2.7.4
^ permalink raw reply related
* [RFC bpf-next 7/8] bpf: migrate insn remove to list patching infra
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
This patch migrate dead code remove pass to new list patching
infrastructure.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
kernel/bpf/verifier.c | 59 +++++++++++++++++----------------------------------
1 file changed, 19 insertions(+), 40 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 58d6bbe..abe11fd 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8500,49 +8500,30 @@ verifier_linearize_list_insn(struct bpf_verifier_env *env,
return ret_env;
}
-static int opt_remove_dead_code(struct bpf_verifier_env *env)
+static int opt_remove_useless_code(struct bpf_verifier_env *env)
{
- struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
- int insn_cnt = env->prog->len;
- int i, err;
-
- for (i = 0; i < insn_cnt; i++) {
- int j;
-
- j = 0;
- while (i + j < insn_cnt && !aux_data[i + j].seen)
- j++;
- if (!j)
- continue;
-
- err = verifier_remove_insns(env, i, j);
- if (err)
- return err;
- insn_cnt = env->prog->len;
- }
-
- return 0;
-}
-
-static int opt_remove_nops(struct bpf_verifier_env *env)
-{
- const struct bpf_insn ja = BPF_JMP_IMM(BPF_JA, 0, 0, 0);
- struct bpf_insn *insn = env->prog->insnsi;
- int insn_cnt = env->prog->len;
- int i, err;
+ struct bpf_insn_aux_data *auxs = env->insn_aux_data;
+ const struct bpf_insn nop =
+ BPF_JMP_IMM(BPF_JA, 0, 0, 0);
+ struct bpf_list_insn *list, *elem;
+ int ret = 0;
- for (i = 0; i < insn_cnt; i++) {
- if (memcmp(&insn[i], &ja, sizeof(ja)))
+ list = bpf_create_list_insn(env->prog);
+ if (IS_ERR(list))
+ return PTR_ERR(list);
+ for (elem = list; elem; elem = elem->next) {
+ if (auxs[elem->orig_idx - 1].seen &&
+ memcmp(&elem->insn, &nop, sizeof(nop)))
continue;
- err = verifier_remove_insns(env, i, 1);
- if (err)
- return err;
- insn_cnt--;
- i--;
+ elem->flag |= LIST_INSN_FLAG_REMOVED;
}
- return 0;
+ env = verifier_linearize_list_insn(env, list);
+ if (IS_ERR(env))
+ ret = PTR_ERR(env);
+ bpf_destroy_list_insn(list);
+ return ret;
}
static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
@@ -9488,9 +9469,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
if (ret == 0)
opt_hard_wire_dead_code_branches(env);
if (ret == 0)
- ret = opt_remove_dead_code(env);
- if (ret == 0)
- ret = opt_remove_nops(env);
+ ret = opt_remove_useless_code(env);
} else {
if (ret == 0)
sanitize_dead_code(env);
--
2.7.4
^ permalink raw reply related
* [RFC bpf-next 3/8] bpf: migrate jit blinding to list patching infra
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
List linerization function will figure out the new jump destination of
patched/blinded jumps. No need of destination adjustment inside
bpf_jit_blind_insn any more.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
kernel/bpf/core.c | 76 ++++++++++++++++++++++++++-----------------------------
1 file changed, 36 insertions(+), 40 deletions(-)
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index e60703e..c3a5f84 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1162,7 +1162,6 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from,
{
struct bpf_insn *to = to_buff;
u32 imm_rnd = get_random_int();
- s16 off;
BUILD_BUG_ON(BPF_REG_AX + 1 != MAX_BPF_JIT_REG);
BUILD_BUG_ON(MAX_BPF_REG + 1 != MAX_BPF_JIT_REG);
@@ -1234,13 +1233,10 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from,
case BPF_JMP | BPF_JSGE | BPF_K:
case BPF_JMP | BPF_JSLE | BPF_K:
case BPF_JMP | BPF_JSET | BPF_K:
- /* Accommodate for extra offset in case of a backjump. */
- off = from->off;
- if (off < 0)
- off -= 2;
*to++ = BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^ from->imm);
*to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd);
- *to++ = BPF_JMP_REG(from->code, from->dst_reg, BPF_REG_AX, off);
+ *to++ = BPF_JMP_REG(from->code, from->dst_reg, BPF_REG_AX,
+ from->off);
break;
case BPF_JMP32 | BPF_JEQ | BPF_K:
@@ -1254,14 +1250,10 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from,
case BPF_JMP32 | BPF_JSGE | BPF_K:
case BPF_JMP32 | BPF_JSLE | BPF_K:
case BPF_JMP32 | BPF_JSET | BPF_K:
- /* Accommodate for extra offset in case of a backjump. */
- off = from->off;
- if (off < 0)
- off -= 2;
*to++ = BPF_ALU32_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^ from->imm);
*to++ = BPF_ALU32_IMM(BPF_XOR, BPF_REG_AX, imm_rnd);
*to++ = BPF_JMP32_REG(from->code, from->dst_reg, BPF_REG_AX,
- off);
+ from->off);
break;
case BPF_LD | BPF_IMM | BPF_DW:
@@ -1332,10 +1324,9 @@ void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other)
struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog)
{
struct bpf_insn insn_buff[16], aux[2];
- struct bpf_prog *clone, *tmp;
- int insn_delta, insn_cnt;
- struct bpf_insn *insn;
- int i, rewritten;
+ struct bpf_list_insn *list, *elem;
+ struct bpf_prog *clone, *ret_prog;
+ int rewritten;
if (!bpf_jit_blinding_enabled(prog) || prog->blinded)
return prog;
@@ -1344,43 +1335,48 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog)
if (!clone)
return ERR_PTR(-ENOMEM);
- insn_cnt = clone->len;
- insn = clone->insnsi;
+ list = bpf_create_list_insn(clone);
+ if (IS_ERR(list))
+ return (struct bpf_prog *)list;
+
+ /* kill uninitialized warning on some gcc versions. */
+ memset(&aux, 0, sizeof(aux));
+
+ for (elem = list; elem; elem = elem->next) {
+ struct bpf_list_insn *next = elem->next;
+ struct bpf_insn insn = elem->insn;
- for (i = 0; i < insn_cnt; i++, insn++) {
/* We temporarily need to hold the original ld64 insn
* so that we can still access the first part in the
* second blinding run.
*/
- if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW) &&
- insn[1].code == 0)
- memcpy(aux, insn, sizeof(aux));
+ if (insn.code == (BPF_LD | BPF_IMM | BPF_DW)) {
+ struct bpf_insn next_insn = next->insn;
- rewritten = bpf_jit_blind_insn(insn, aux, insn_buff);
+ if (next_insn.code == 0) {
+ aux[0] = insn;
+ aux[1] = next_insn;
+ }
+ }
+
+ rewritten = bpf_jit_blind_insn(&insn, aux, insn_buff);
if (!rewritten)
continue;
- tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten);
- if (IS_ERR(tmp)) {
- /* Patching may have repointed aux->prog during
- * realloc from the original one, so we need to
- * fix it up here on error.
- */
- bpf_jit_prog_release_other(prog, clone);
- return tmp;
+ elem = bpf_patch_list_insn(elem, insn_buff, rewritten);
+ if (IS_ERR(elem)) {
+ ret_prog = (struct bpf_prog *)elem;
+ goto free_list_ret;
}
-
- clone = tmp;
- insn_delta = rewritten - 1;
-
- /* Walk new program and skip insns we just inserted. */
- insn = clone->insnsi + i + insn_delta;
- insn_cnt += insn_delta;
- i += insn_delta;
}
- clone->blinded = 1;
- return clone;
+ clone = bpf_linearize_list_insn(clone, list);
+ if (!IS_ERR(clone))
+ clone->blinded = 1;
+ ret_prog = clone;
+free_list_ret:
+ bpf_destroy_list_insn(list);
+ return ret_prog;
}
#endif /* CONFIG_BPF_JIT */
--
2.7.4
^ permalink raw reply related
* [RFC bpf-next 2/8] bpf: extend list based insn patching infra to verification layer
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
Verification layer also needs to handle auxiliar info as well as adjusting
subprog start.
At this layer, insns inside patch buffer could be jump, but they should
have been resolved, meaning they shouldn't jump to insn outside of the
patch buffer. Lineration function for this layer won't touch insns inside
patch buffer.
Adjusting subprog is finished along with adjusting jump target when the
input will cover bpf to bpf call insn, re-register subprog start is cheap.
But adjustment when there is insn deleteion is not considered yet.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
kernel/bpf/verifier.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 150 insertions(+)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a2e7637..2026d64 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -8350,6 +8350,156 @@ static void opt_hard_wire_dead_code_branches(struct bpf_verifier_env *env)
}
}
+/* Linearize bpf list insn to array (verifier layer). */
+static struct bpf_verifier_env *
+verifier_linearize_list_insn(struct bpf_verifier_env *env,
+ struct bpf_list_insn *list)
+{
+ u32 *idx_map, idx, orig_cnt, fini_cnt = 0;
+ struct bpf_subprog_info *new_subinfo;
+ struct bpf_insn_aux_data *new_data;
+ struct bpf_prog *prog = env->prog;
+ struct bpf_verifier_env *ret_env;
+ struct bpf_insn *insns, *insn;
+ struct bpf_list_insn *elem;
+ int ret;
+
+ /* Calculate final size. */
+ for (elem = list; elem; elem = elem->next)
+ if (!(elem->flag & LIST_INSN_FLAG_REMOVED))
+ fini_cnt++;
+
+ orig_cnt = prog->len;
+ insns = prog->insnsi;
+ /* If prog length remains same, nothing else to do. */
+ if (fini_cnt == orig_cnt) {
+ for (insn = insns, elem = list; elem; elem = elem->next, insn++)
+ *insn = elem->insn;
+ return env;
+ }
+ /* Realloc insn buffer when necessary. */
+ if (fini_cnt > orig_cnt)
+ prog = bpf_prog_realloc(prog, bpf_prog_size(fini_cnt),
+ GFP_USER);
+ if (!prog)
+ return ERR_PTR(-ENOMEM);
+ insns = prog->insnsi;
+ prog->len = fini_cnt;
+ ret_env = env;
+
+ /* idx_map[OLD_IDX] = NEW_IDX */
+ idx_map = kvmalloc(orig_cnt * sizeof(u32), GFP_KERNEL);
+ if (!idx_map)
+ return ERR_PTR(-ENOMEM);
+ memset(idx_map, 0xff, orig_cnt * sizeof(u32));
+
+ /* Use the same alloc method used when allocating env->insn_aux_data. */
+ new_data = vzalloc(array_size(sizeof(*new_data), fini_cnt));
+ if (!new_data) {
+ kvfree(idx_map);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Copy over insn + calculate idx_map. */
+ for (idx = 0, elem = list; elem; elem = elem->next) {
+ int orig_idx = elem->orig_idx - 1;
+
+ if (orig_idx >= 0) {
+ idx_map[orig_idx] = idx;
+
+ if (elem->flag & LIST_INSN_FLAG_REMOVED)
+ continue;
+
+ new_data[idx] = env->insn_aux_data[orig_idx];
+
+ if (elem->flag & LIST_INSN_FLAG_PATCHED)
+ new_data[idx].zext_dst =
+ insn_has_def32(env, &elem->insn);
+ } else {
+ new_data[idx].seen = true;
+ new_data[idx].zext_dst = insn_has_def32(env,
+ &elem->insn);
+ }
+ insns[idx++] = elem->insn;
+ }
+
+ new_subinfo = kvzalloc(sizeof(env->subprog_info), GFP_KERNEL);
+ if (!new_subinfo) {
+ kvfree(idx_map);
+ vfree(new_data);
+ return ERR_PTR(-ENOMEM);
+ }
+ memcpy(new_subinfo, env->subprog_info, sizeof(env->subprog_info));
+ memset(env->subprog_info, 0, sizeof(env->subprog_info));
+ env->subprog_cnt = 0;
+ env->prog = prog;
+ ret = add_subprog(env, 0);
+ if (ret < 0) {
+ ret_env = ERR_PTR(ret);
+ goto free_all_ret;
+ }
+ /* Relocate jumps using idx_map.
+ * old_dst = jmp_insn.old_target + old_pc + 1;
+ * new_dst = idx_map[old_dst] = jmp_insn.new_target + new_pc + 1;
+ * jmp_insn.new_target = new_dst - new_pc - 1;
+ */
+ for (idx = 0, elem = list; elem; elem = elem->next) {
+ int orig_idx = elem->orig_idx;
+
+ if (elem->flag & LIST_INSN_FLAG_REMOVED)
+ continue;
+ if ((elem->flag & LIST_INSN_FLAG_PATCHED) || !orig_idx) {
+ idx++;
+ continue;
+ }
+
+ ret = bpf_jit_adj_imm_off(&insns[idx], orig_idx - 1, idx,
+ idx_map);
+ if (ret < 0) {
+ ret_env = ERR_PTR(ret);
+ goto free_all_ret;
+ }
+ /* Recalculate subprog start as we are at bpf2bpf call insn. */
+ if (ret > 0) {
+ ret = add_subprog(env, idx + insns[idx].imm + 1);
+ if (ret < 0) {
+ ret_env = ERR_PTR(ret);
+ goto free_all_ret;
+ }
+ }
+ idx++;
+ }
+ if (ret < 0) {
+ ret_env = ERR_PTR(ret);
+ goto free_all_ret;
+ }
+
+ env->subprog_info[env->subprog_cnt].start = fini_cnt;
+ for (idx = 0; idx <= env->subprog_cnt; idx++)
+ new_subinfo[idx].start = env->subprog_info[idx].start;
+ memcpy(env->subprog_info, new_subinfo, sizeof(env->subprog_info));
+
+ /* Adjust linfo.
+ * FIXME: no support for insn removal at the moment.
+ */
+ if (prog->aux->nr_linfo) {
+ struct bpf_line_info *linfo = prog->aux->linfo;
+ u32 nr_linfo = prog->aux->nr_linfo;
+
+ for (idx = 0; idx < nr_linfo; idx++)
+ linfo[idx].insn_off = idx_map[linfo[idx].insn_off];
+ }
+ vfree(env->insn_aux_data);
+ env->insn_aux_data = new_data;
+ goto free_mem_list_ret;
+free_all_ret:
+ vfree(new_data);
+free_mem_list_ret:
+ kvfree(new_subinfo);
+ kvfree(idx_map);
+ return ret_env;
+}
+
static int opt_remove_dead_code(struct bpf_verifier_env *env)
{
struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
--
2.7.4
^ permalink raw reply related
* [RFC bpf-next 1/8] bpf: introducing list based insn patching infra to core layer
From: Jiong Wang @ 2019-07-04 21:26 UTC (permalink / raw)
To: alexei.starovoitov, daniel
Cc: ecree, naveen.n.rao, andriin, jakub.kicinski, bpf, netdev,
oss-drivers, Jiong Wang
In-Reply-To: <1562275611-31790-1-git-send-email-jiong.wang@netronome.com>
This patch introduces list based bpf insn patching infra to bpf core layer
which is lower than verification layer.
This layer has bpf insn sequence as the solo input, therefore the tasks
to be finished during list linerization is:
- copy insn
- relocate jumps
- relocation line info.
Suggested-by: Alexei Starovoitov <ast@kernel.org>
Suggested-by: Edward Cree <ecree@solarflare.com>
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
---
include/linux/filter.h | 25 +++++
kernel/bpf/core.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 293 insertions(+)
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 1fe53e7..1fea68c 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -842,6 +842,31 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
const struct bpf_insn *patch, u32 len);
int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt);
+int bpf_jit_adj_imm_off(struct bpf_insn *insn, int old_idx, int new_idx,
+ int idx_map[]);
+
+#define LIST_INSN_FLAG_PATCHED 0x1
+#define LIST_INSN_FLAG_REMOVED 0x2
+struct bpf_list_insn {
+ struct bpf_insn insn;
+ struct bpf_list_insn *next;
+ s32 orig_idx;
+ u32 flag;
+};
+
+struct bpf_list_insn *bpf_create_list_insn(struct bpf_prog *prog);
+void bpf_destroy_list_insn(struct bpf_list_insn *list);
+/* Replace LIST_INSN with new list insns generated from PATCH. */
+struct bpf_list_insn *bpf_patch_list_insn(struct bpf_list_insn *list_insn,
+ const struct bpf_insn *patch,
+ u32 len);
+/* Pre-patch list_insn with insns inside PATCH, meaning LIST_INSN is not
+ * touched. New list insns are inserted before it.
+ */
+struct bpf_list_insn *bpf_prepatch_list_insn(struct bpf_list_insn *list_insn,
+ const struct bpf_insn *patch,
+ u32 len);
+
void bpf_clear_redirect_map(struct bpf_map *map);
static inline bool xdp_return_frame_no_direct(void)
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index e2c1b43..e60703e 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -502,6 +502,274 @@ int bpf_remove_insns(struct bpf_prog *prog, u32 off, u32 cnt)
return WARN_ON_ONCE(bpf_adj_branches(prog, off, off + cnt, off, false));
}
+int bpf_jit_adj_imm_off(struct bpf_insn *insn, int old_idx, int new_idx,
+ s32 idx_map[])
+{
+ u8 code = insn->code;
+ s64 imm;
+ s32 off;
+
+ if (BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32)
+ return 0;
+
+ if (BPF_CLASS(code) == BPF_JMP &&
+ (BPF_OP(code) == BPF_EXIT ||
+ (BPF_OP(code) == BPF_CALL && insn->src_reg != BPF_PSEUDO_CALL)))
+ return 0;
+
+ /* BPF to BPF call. */
+ if (BPF_OP(code) == BPF_CALL) {
+ imm = idx_map[old_idx + insn->imm + 1] - new_idx - 1;
+ if (imm < S32_MIN || imm > S32_MAX)
+ return -ERANGE;
+ insn->imm = imm;
+ return 1;
+ }
+
+ /* Jump. */
+ off = idx_map[old_idx + insn->off + 1] - new_idx - 1;
+ if (off < S16_MIN || off > S16_MAX)
+ return -ERANGE;
+ insn->off = off;
+ return 0;
+}
+
+void bpf_destroy_list_insn(struct bpf_list_insn *list)
+{
+ struct bpf_list_insn *elem, *next;
+
+ for (elem = list; elem; elem = next) {
+ next = elem->next;
+ kvfree(elem);
+ }
+}
+
+struct bpf_list_insn *bpf_create_list_insn(struct bpf_prog *prog)
+{
+ unsigned int idx, len = prog->len;
+ struct bpf_list_insn *hdr, *prev;
+ struct bpf_insn *insns;
+
+ hdr = kvzalloc(sizeof(*hdr), GFP_KERNEL);
+ if (!hdr)
+ return ERR_PTR(-ENOMEM);
+
+ insns = prog->insnsi;
+ hdr->insn = insns[0];
+ hdr->orig_idx = 1;
+ prev = hdr;
+
+ for (idx = 1; idx < len; idx++) {
+ struct bpf_list_insn *node = kvzalloc(sizeof(*node),
+ GFP_KERNEL);
+
+ if (!node) {
+ /* Destroy what has been allocated. */
+ bpf_destroy_list_insn(hdr);
+ return ERR_PTR(-ENOMEM);
+ }
+ node->insn = insns[idx];
+ node->orig_idx = idx + 1;
+ prev->next = node;
+ prev = node;
+ }
+
+ return hdr;
+}
+
+/* Linearize bpf list insn to array. */
+static struct bpf_prog *bpf_linearize_list_insn(struct bpf_prog *prog,
+ struct bpf_list_insn *list)
+{
+ u32 *idx_map, idx, prev_idx, fini_cnt = 0, orig_cnt = prog->len;
+ struct bpf_insn *insns, *insn;
+ struct bpf_list_insn *elem;
+
+ /* Calculate final size. */
+ for (elem = list; elem; elem = elem->next)
+ if (!(elem->flag & LIST_INSN_FLAG_REMOVED))
+ fini_cnt++;
+
+ insns = prog->insnsi;
+ /* If prog length remains same, nothing else to do. */
+ if (fini_cnt == orig_cnt) {
+ for (insn = insns, elem = list; elem; elem = elem->next, insn++)
+ *insn = elem->insn;
+ return prog;
+ }
+ /* Realloc insn buffer when necessary. */
+ if (fini_cnt > orig_cnt)
+ prog = bpf_prog_realloc(prog, bpf_prog_size(fini_cnt),
+ GFP_USER);
+ if (!prog)
+ return ERR_PTR(-ENOMEM);
+ insns = prog->insnsi;
+ prog->len = fini_cnt;
+
+ /* idx_map[OLD_IDX] = NEW_IDX */
+ idx_map = kvmalloc(orig_cnt * sizeof(u32), GFP_KERNEL);
+ if (!idx_map)
+ return ERR_PTR(-ENOMEM);
+ memset(idx_map, 0xff, orig_cnt * sizeof(u32));
+
+ /* Copy over insn + calculate idx_map. */
+ for (idx = 0, elem = list; elem; elem = elem->next) {
+ int orig_idx = elem->orig_idx - 1;
+
+ if (orig_idx >= 0) {
+ idx_map[orig_idx] = idx;
+
+ if (elem->flag & LIST_INSN_FLAG_REMOVED)
+ continue;
+ }
+ insns[idx++] = elem->insn;
+ }
+
+ /* Relocate jumps using idx_map.
+ * old_dst = jmp_insn.old_target + old_pc + 1;
+ * new_dst = idx_map[old_dst] = jmp_insn.new_target + new_pc + 1;
+ * jmp_insn.new_target = new_dst - new_pc - 1;
+ */
+ for (idx = 0, prev_idx = 0, elem = list; elem; elem = elem->next) {
+ int ret, orig_idx;
+
+ /* A removed insn doesn't increase new_pc */
+ if (elem->flag & LIST_INSN_FLAG_REMOVED)
+ continue;
+
+ orig_idx = elem->orig_idx - 1;
+ ret = bpf_jit_adj_imm_off(&insns[idx],
+ orig_idx >= 0 ? orig_idx : prev_idx,
+ idx, idx_map);
+ idx++;
+ if (ret < 0) {
+ kvfree(idx_map);
+ return ERR_PTR(ret);
+ }
+ if (orig_idx >= 0)
+ /* Record prev_idx. it is used for relocating jump insn
+ * inside patch buffer. For example, when doing jit
+ * blinding, a jump could be moved to some other
+ * positions inside the patch buffer, and its old_dst
+ * could be calculated using prev_idx.
+ */
+ prev_idx = orig_idx;
+ }
+
+ /* Adjust linfo.
+ *
+ * NOTE: the prog reached core layer has been adjusted to contain insns
+ * for single function, however linfo contains information for
+ * whole program, so we need to make sure linfo beyond current
+ * function is handled properly.
+ */
+ if (prog->aux->nr_linfo) {
+ u32 linfo_idx, insn_start, insn_end, nr_linfo, idx, delta;
+ struct bpf_line_info *linfo;
+
+ linfo_idx = prog->aux->linfo_idx;
+ linfo = &prog->aux->linfo[linfo_idx];
+ insn_start = linfo[0].insn_off;
+ insn_end = insn_start + orig_cnt;
+ nr_linfo = prog->aux->nr_linfo - linfo_idx;
+ delta = fini_cnt - orig_cnt;
+ for (idx = 0; idx < nr_linfo; idx++) {
+ int adj_off;
+
+ if (linfo[idx].insn_off >= insn_end) {
+ linfo[idx].insn_off += delta;
+ continue;
+ }
+
+ adj_off = linfo[idx].insn_off - insn_start;
+ linfo[idx].insn_off = idx_map[adj_off] + insn_start;
+ }
+ }
+ kvfree(idx_map);
+
+ return prog;
+}
+
+struct bpf_list_insn *bpf_patch_list_insn(struct bpf_list_insn *list_insn,
+ const struct bpf_insn *patch,
+ u32 len)
+{
+ struct bpf_list_insn *prev, *next;
+ u32 insn_delta = len - 1;
+ u32 idx;
+
+ list_insn->insn = *patch;
+ list_insn->flag |= LIST_INSN_FLAG_PATCHED;
+
+ /* Since our patchlet doesn't expand the image, we're done. */
+ if (insn_delta == 0)
+ return list_insn;
+
+ len--;
+ patch++;
+
+ prev = list_insn;
+ next = list_insn->next;
+ for (idx = 0; idx < len; idx++) {
+ struct bpf_list_insn *node = kvzalloc(sizeof(*node),
+ GFP_KERNEL);
+
+ if (!node) {
+ /* Link what's allocated, so list destroyer could
+ * free them.
+ */
+ prev->next = next;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ node->insn = patch[idx];
+ prev->next = node;
+ prev = node;
+ }
+
+ prev->next = next;
+ return prev;
+}
+
+struct bpf_list_insn *bpf_prepatch_list_insn(struct bpf_list_insn *list_insn,
+ const struct bpf_insn *patch,
+ u32 len)
+{
+ struct bpf_list_insn *prev, *node, *begin_node;
+ u32 idx;
+
+ if (!len)
+ return list_insn;
+
+ node = kvzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+ node->insn = patch[0];
+ begin_node = node;
+ prev = node;
+
+ for (idx = 1; idx < len; idx++) {
+ node = kvzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ node = begin_node;
+ /* Release what's has been allocated. */
+ while (node) {
+ struct bpf_list_insn *next = node->next;
+
+ kvfree(node);
+ node = next;
+ }
+ return ERR_PTR(-ENOMEM);
+ }
+ node->insn = patch[idx];
+ prev->next = node;
+ prev = node;
+ }
+
+ prev->next = list_insn;
+ return begin_node;
+}
+
void bpf_prog_kallsyms_del_subprogs(struct bpf_prog *fp)
{
int i;
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox