Initial commit
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled

This commit is contained in:
domenico
2025-06-24 12:51:15 +02:00
commit 27c9d80f51
10493 changed files with 1885777 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 22 Oct 2020 22:00:03 +0200
Subject: [PATCH] compiler.h: only include asm/rwonce.h for kernel code
This header file is not in uapi, which makes any user space code that includes
linux/compiler.h to fail with the error 'asm/rwonce.h: No such file or directory'
Fixes: e506ea451254 ("compiler.h: Split {READ,WRITE}_ONCE definitions out into rwonce.h")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -203,6 +203,8 @@ void ftrace_likely_update(struct ftrace_
__v; \
})
+#include <asm/rwonce.h>
+
#endif /* __KERNEL__ */
/*
@@ -243,6 +245,4 @@ static inline void *offset_to_ptr(const
*/
#define prevent_tail_call_optimization() mb()
-#include <asm/rwonce.h>
-
#endif /* __LINUX_COMPILER_H */

View File

@@ -0,0 +1,57 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Wed, 18 Apr 2018 10:50:05 +0200
Subject: [PATCH] MIPS: only process negative stack offsets on stack traces
Fixes endless back traces in cases where the compiler emits a stack
pointer increase in a branch delay slot (probably for some form of
function return).
[ 3.475442] BUG: MAX_STACK_TRACE_ENTRIES too low!
[ 3.480070] turning off the locking correctness validator.
[ 3.485521] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.34 #0
[ 3.491475] Stack : 00000000 00000000 00000000 00000000 80e0fce2 00000034 00000000 00000000
[ 3.499764] 87c3838c 80696377 8061047c 00000000 00000001 00000001 87c2d850 6534689f
[ 3.508059] 00000000 00000000 80e10000 00000000 00000000 000000cf 0000000f 00000000
[ 3.516353] 00000000 806a0000 00076891 00000000 00000000 00000000 ffffffff 00000000
[ 3.524648] 806c0000 00000004 80e10000 806a0000 00000003 80690000 00000000 80700000
[ 3.532942] ...
[ 3.535362] Call Trace:
[ 3.537818] [<80010a48>] show_stack+0x58/0x100
[ 3.542207] [<804c2f78>] dump_stack+0xe8/0x170
[ 3.546613] [<80079f90>] save_trace+0xf0/0x110
[ 3.551010] [<8007b1ec>] mark_lock+0x33c/0x78c
[ 3.555413] [<8007bf48>] __lock_acquire+0x2ac/0x1a08
[ 3.560337] [<8007de60>] lock_acquire+0x64/0x8c
[ 3.564846] [<804e1570>] _raw_spin_lock_irqsave+0x54/0x78
[ 3.570186] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.574770] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.579257] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.583839] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.588329] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.592911] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.597401] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.601983] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.606473] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.611055] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.615545] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.620125] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.624619] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.629197] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.633691] [<801b618c>] kernfs_notify+0x94/0xac
[ 3.638269] [<801b7b10>] sysfs_notify+0x74/0xa0
[ 3.642763] [<801b618c>] kernfs_notify+0x94/0xac
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -395,6 +395,8 @@ static inline int is_sp_move_ins(union m
if (ip->i_format.opcode == addiu_op ||
ip->i_format.opcode == daddiu_op) {
+ if (ip->i_format.simmediate > 0)
+ return 0;
*frame_size = -ip->i_format.simmediate;
return 1;
}

View File

@@ -0,0 +1,21 @@
From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@nbd.name>
Date: Sun, 9 Jul 2017 00:26:53 +0200
Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
Makefile | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/Makefile
+++ b/Makefile
@@ -605,7 +605,7 @@ endif
# Allows the usage of unstable features in stable compilers.
export RUSTC_BOOTSTRAP := 1
-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG
+export ARCH SRCARCH SUBARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG
export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN CARGO
export HOSTRUSTC KBUILD_HOSTRUSTFLAGS
export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL

View File

@@ -0,0 +1,75 @@
From bd1b9f66d5134e518419f4c4dacf1884c1616983 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
Date: Thu, 28 Apr 2022 11:13:23 +0200
Subject: [PATCH] watchdog: max63xx_wdt: Add support for specifying WDI logic
via GPIO
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On some boards is WDI logic of max6370 chip connected via GPIO.
So extend max63xx_wdt driver to allow specifying WDI logic via GPIO.
Signed-off-by: Pali Rohár <pali@kernel.org>
---
drivers/watchdog/max63xx_wdt.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
--- a/drivers/watchdog/max63xx_wdt.c
+++ b/drivers/watchdog/max63xx_wdt.c
@@ -27,6 +27,7 @@
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/property.h>
+#include <linux/gpio/consumer.h>
#define DEFAULT_HEARTBEAT 60
#define MAX_HEARTBEAT 60
@@ -53,6 +54,9 @@ struct max63xx_wdt {
void __iomem *base;
spinlock_t lock;
+ /* GPIOs */
+ struct gpio_desc *gpio_wdi;
+
/* WDI and WSET bits write access routines */
void (*ping)(struct max63xx_wdt *wdt);
void (*set)(struct max63xx_wdt *wdt, u8 set);
@@ -158,6 +162,17 @@ static const struct watchdog_info max63x
.identity = "max63xx Watchdog",
};
+static void max63xx_gpio_ping(struct max63xx_wdt *wdt)
+{
+ spin_lock(&wdt->lock);
+
+ gpiod_set_value(wdt->gpio_wdi, 1);
+ udelay(1);
+ gpiod_set_value(wdt->gpio_wdi, 0);
+
+ spin_unlock(&wdt->lock);
+}
+
static void max63xx_mmap_ping(struct max63xx_wdt *wdt)
{
u8 val;
@@ -225,10 +240,19 @@ static int max63xx_wdt_probe(struct plat
return -EINVAL;
}
+ wdt->gpio_wdi = devm_gpiod_get(dev, NULL, GPIOD_FLAGS_BIT_DIR_OUT);
+ if (IS_ERR(wdt->gpio_wdi) && PTR_ERR(wdt->gpio_wdi) != -ENOENT)
+ return dev_err_probe(dev, PTR_ERR(wdt->gpio_wdi),
+ "unable to request gpio: %ld\n",
+ PTR_ERR(wdt->gpio_wdi));
+
err = max63xx_mmap_init(pdev, wdt);
if (err)
return err;
+ if (!IS_ERR(wdt->gpio_wdi))
+ wdt->ping = max63xx_gpio_ping;
+
platform_set_drvdata(pdev, &wdt->wdd);
watchdog_set_drvdata(&wdt->wdd, wdt);

View File

@@ -0,0 +1,82 @@
From: Tobias Wolf <dev-NTEO@vplace.de>
Subject: mm: Fix alloc_node_mem_map with ARCH_PFN_OFFSET calculation
An rt288x (ralink) based router (Belkin F5D8235 v1) does not boot with any
kernel beyond version 4.3 resulting in:
BUG: Bad page state in process swapper pfn:086ac
bisect resulted in:
a1c34a3bf00af2cede839879502e12dc68491ad5 is the first bad commit
commit a1c34a3bf00af2cede839879502e12dc68491ad5
Author: Laura Abbott <laura@labbott.name>
Date: Thu Nov 5 18:48:46 2015 -0800
mm: Don't offset memmap for flatmem
Srinivas Kandagatla reported bad page messages when trying to remove the
bottom 2MB on an ARM based IFC6410 board
BUG: Bad page state in process swapper pfn:fffa8
page:ef7fb500 count:0 mapcount:0 mapping: (null) index:0x0
flags: 0x96640253(locked|error|dirty|active|arch_1|reclaim|mlocked)
page dumped because: PAGE_FLAGS_CHECK_AT_FREE flag(s) set
bad because of flags:
flags: 0x200041(locked|active|mlocked)
Modules linked in:
CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00007-g412f9ba-dirty
#816
Hardware name: Qualcomm (Flattened Device Tree)
unwind_backtrace
show_stack
dump_stack
bad_page
free_pages_prepare
free_hot_cold_page
__free_pages
free_highmem_page
mem_init
start_kernel
Disabling lock debugging due to kernel taint
[...]
:040000 040000 2de013c372345fd471cd58f0553c9b38b0ef1cc4
0a8156f848733dfa21e16c196dfb6c0a76290709 M mm
This fix for ARM does not account ARCH_PFN_OFFSET for mem_map as later used by
page_to_pfn anymore.
The following output was generated with two hacked in printk statements:
printk("before %p vs. %p or %p\n", mem_map, mem_map - offset, mem_map -
(pgdat->node_start_pfn - ARCH_PFN_OFFSET));
if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
printk("after %p\n", mem_map);
Output:
[ 0.000000] before 8861b280 vs. 8861b280 or 8851b280
[ 0.000000] after 8851b280
As seen in the first line mem_map with subtraction of offset does not equal the
mem_map after subtraction of ARCH_PFN_OFFSET.
After adding the offset of ARCH_PFN_OFFSET as well to mem_map as the
previously calculated offset is zero for the named platform it is able to boot
4.4 and 4.9-rc7 again.
Signed-off-by: Tobias Wolf <dev-NTEO@vplace.de>
---
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -7903,7 +7903,7 @@ static void __init alloc_node_mem_map(st
if (pgdat == NODE_DATA(0)) {
mem_map = NODE_DATA(0)->node_mem_map;
if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
- mem_map -= offset;
+ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
}
#endif
}

View File

@@ -0,0 +1,81 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: jffs2: use .rename2 and add RENAME_WHITEOUT support
It is required for renames on overlayfs
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -614,8 +614,8 @@ static int jffs2_rmdir (struct inode *di
return ret;
}
-static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i,
- struct dentry *dentry, umode_t mode, dev_t rdev)
+static int __jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i,
+ struct dentry *dentry, umode_t mode, dev_t rdev, bool whiteout)
{
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
@@ -754,7 +754,11 @@ static int jffs2_mknod (struct user_name
mutex_unlock(&dir_f->sem);
jffs2_complete_reservation(c);
- d_instantiate_new(dentry, inode);
+ if (!whiteout)
+ d_instantiate_new(dentry, inode);
+ else
+ unlock_new_inode(inode);
+
return 0;
fail:
@@ -762,6 +766,19 @@ static int jffs2_mknod (struct user_name
return ret;
}
+static int jffs2_mknod (struct user_namespace *mnt_userns, struct inode *dir_i,
+ struct dentry *dentry, umode_t mode, dev_t rdev)
+{
+ return __jffs2_mknod(mnt_userns, dir_i, dentry, mode, rdev, false);
+}
+
+static int jffs2_whiteout (struct user_namespace *mnt_userns, struct inode *old_dir,
+ struct dentry *old_dentry)
+{
+ return __jffs2_mknod(mnt_userns, old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE,
+ WHITEOUT_DEV, true);
+}
+
static int jffs2_rename (struct user_namespace *mnt_userns,
struct inode *old_dir_i, struct dentry *old_dentry,
struct inode *new_dir_i, struct dentry *new_dentry,
@@ -773,7 +790,7 @@ static int jffs2_rename (struct user_nam
uint8_t type;
uint32_t now;
- if (flags & ~RENAME_NOREPLACE)
+ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT))
return -EINVAL;
/* The VFS will check for us and prevent trying to rename a
@@ -839,9 +856,14 @@ static int jffs2_rename (struct user_nam
if (d_is_dir(old_dentry) && !victim_f)
inc_nlink(new_dir_i);
- /* Unlink the original */
- ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
- old_dentry->d_name.name, old_dentry->d_name.len, NULL, now);
+ if (flags & RENAME_WHITEOUT)
+ /* Replace with whiteout */
+ ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry);
+ else
+ /* Unlink the original */
+ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
+ old_dentry->d_name.name,
+ old_dentry->d_name.len, NULL, now);
/* We don't touch inode->i_nlink */

View File

@@ -0,0 +1,73 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: jffs2: add RENAME_EXCHANGE support
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -787,18 +787,31 @@ static int jffs2_rename (struct user_nam
int ret;
struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
struct jffs2_inode_info *victim_f = NULL;
+ struct inode *fst_inode = d_inode(old_dentry);
+ struct inode *snd_inode = d_inode(new_dentry);
uint8_t type;
uint32_t now;
- if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT))
+ if (flags & ~(RENAME_NOREPLACE|RENAME_WHITEOUT|RENAME_EXCHANGE))
return -EINVAL;
+ if ((flags & RENAME_EXCHANGE) && (old_dir_i != new_dir_i)) {
+ if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) {
+ inc_nlink(new_dir_i);
+ drop_nlink(old_dir_i);
+ }
+ else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) {
+ drop_nlink(new_dir_i);
+ inc_nlink(old_dir_i);
+ }
+ }
+
/* The VFS will check for us and prevent trying to rename a
* file over a directory and vice versa, but if it's a directory,
* the VFS can't check whether the victim is empty. The filesystem
* needs to do that for itself.
*/
- if (d_really_is_positive(new_dentry)) {
+ if (d_really_is_positive(new_dentry) && !(flags & RENAME_EXCHANGE)) {
victim_f = JFFS2_INODE_INFO(d_inode(new_dentry));
if (d_is_dir(new_dentry)) {
struct jffs2_full_dirent *fd;
@@ -833,7 +846,7 @@ static int jffs2_rename (struct user_nam
if (ret)
return ret;
- if (victim_f) {
+ if (victim_f && !(flags & RENAME_EXCHANGE)) {
/* There was a victim. Kill it off nicely */
if (d_is_dir(new_dentry))
clear_nlink(d_inode(new_dentry));
@@ -859,6 +872,12 @@ static int jffs2_rename (struct user_nam
if (flags & RENAME_WHITEOUT)
/* Replace with whiteout */
ret = jffs2_whiteout(mnt_userns, old_dir_i, old_dentry);
+ else if (flags & RENAME_EXCHANGE)
+ /* Replace the original */
+ ret = jffs2_do_link(c, JFFS2_INODE_INFO(old_dir_i),
+ d_inode(new_dentry)->i_ino, type,
+ old_dentry->d_name.name, old_dentry->d_name.len,
+ now);
else
/* Unlink the original */
ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
@@ -890,7 +909,7 @@ static int jffs2_rename (struct user_nam
return ret;
}
- if (d_is_dir(old_dentry))
+ if (d_is_dir(old_dentry) && !(flags & RENAME_EXCHANGE))
drop_nlink(old_dir_i);
new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now);

View File

@@ -0,0 +1,20 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: jffs2: add splice ops
Add splice_read using generic_file_splice_read.
Add splice_write using iter_file_splice_write
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -53,6 +53,8 @@ const struct file_operations jffs2_file_
.open = generic_file_open,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
+ .splice_read = generic_file_splice_read,
+ .splice_write = iter_file_splice_write,
.unlocked_ioctl=jffs2_ioctl,
.mmap = generic_file_readonly_mmap,
.fsync = jffs2_fsync,

View File

@@ -0,0 +1,45 @@
From: Stephen Hemminger <stephen@networkplumber.org>
Subject: bridge: allow receiption on disabled port
When an ethernet device is enslaved to a bridge, and the bridge STP
detects loss of carrier (or operational state down), then normally
packet receiption is blocked.
This breaks control applications like WPA which maybe expecting to
receive packets to negotiate to bring link up. The bridge needs to
block forwarding packets from these disabled ports, but there is no
hard requirement to not allow local packet delivery.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -227,6 +227,9 @@ static void __br_handle_local_finish(str
/* note: already called with rcu_read_lock */
static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
+ struct net_bridge_port *p = br_port_get_rcu(skb->dev);
+
+ if (p->state != BR_STATE_DISABLED)
__br_handle_local_finish(skb);
/* return 1 to signal the okfn() was called so it's ok to use the skb */
@@ -397,6 +400,17 @@ forward:
goto defer_stp_filtering;
switch (p->state) {
+ case BR_STATE_DISABLED:
+ if (ether_addr_equal(p->br->dev->dev_addr, dest))
+ skb->pkt_type = PACKET_HOST;
+
+ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
+ dev_net(skb->dev), NULL, skb, skb->dev, NULL,
+ br_handle_local_finish) == 1) {
+ return RX_HANDLER_PASS;
+ }
+ break;
+
case BR_STATE_FORWARDING:
case BR_STATE_LEARNING:
defer_stp_filtering:

View File

@@ -0,0 +1,37 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 4 Jan 2024 15:21:21 +0100
Subject: [PATCH] net: bridge: do not send arp replies if src and target hw
addr is the same
There are broken devices in the wild that handle duplicate IP address
detection by sending out ARP requests for the IP that they received from a
DHCP server and refuse the address if they get a reply.
When proxyarp is enabled, they would go into a loop of requesting an address
and then NAKing it again.
Link: https://github.com/openwrt/openwrt/issues/14309
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -204,7 +204,10 @@ void br_do_proxy_suppress_arp(struct sk_
if ((p && (p->flags & BR_PROXYARP)) ||
(f->dst && (f->dst->flags & (BR_PROXYARP_WIFI |
BR_NEIGH_SUPPRESS)))) {
- if (!vid)
+ replied = true;
+ if (!memcmp(n->ha, sha, dev->addr_len))
+ replied = false;
+ else if (!vid)
br_arp_send(br, p, skb->dev, sip, tip,
sha, n->ha, sha, 0, 0);
else
@@ -212,7 +215,6 @@ void br_do_proxy_suppress_arp(struct sk_
sha, n->ha, sha,
skb->vlan_proto,
skb_vlan_tag_get(skb));
- replied = true;
}
/* If we have replied or as long as we know the

View File

@@ -0,0 +1,94 @@
From: Daniel González Cabanelas <dgcbueu@gmail.com>
Subject: [PATCH 1/2] rtc: rs5c372: support alarms up to 1 week
The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week
alarms.
Read the "wday" alarm register and convert it to a date to support up 1
week in our driver.
Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
---
drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 6 deletions(-)
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -399,7 +399,9 @@ static int rs5c_read_alarm(struct device
{
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
- int status;
+ int status, wday_offs;
+ struct rtc_time rtc;
+ unsigned long alarm_secs;
status = rs5c_get_regs(rs5c);
if (status < 0)
@@ -409,6 +411,30 @@ static int rs5c_read_alarm(struct device
t->time.tm_sec = 0;
t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);
t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]);
+ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1;
+
+ /* determine the day, month and year based on alarm wday, taking as a
+ * reference the current time from the rtc
+ */
+ status = rs5c372_rtc_read_time(dev, &rtc);
+ if (status < 0)
+ return status;
+
+ wday_offs = t->time.tm_wday - rtc.tm_wday;
+ alarm_secs = mktime64(rtc.tm_year + 1900,
+ rtc.tm_mon + 1,
+ rtc.tm_mday + wday_offs,
+ t->time.tm_hour,
+ t->time.tm_min,
+ t->time.tm_sec);
+
+ if (wday_offs < 0 || (wday_offs == 0 &&
+ (t->time.tm_hour < rtc.tm_hour ||
+ (t->time.tm_hour == rtc.tm_hour &&
+ t->time.tm_min <= rtc.tm_min))))
+ alarm_secs += 7 * 86400;
+
+ rtc_time64_to_tm(alarm_secs, &t->time);
/* ... and status */
t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE);
@@ -423,12 +449,20 @@ static int rs5c_set_alarm(struct device
struct rs5c372 *rs5c = i2c_get_clientdata(client);
int status, addr, i;
unsigned char buf[3];
+ struct rtc_time rtc_tm;
+ unsigned long rtc_secs, alarm_secs;
- /* only handle up to 24 hours in the future, like RTC_ALM_SET */
- if (t->time.tm_mday != -1
- || t->time.tm_mon != -1
- || t->time.tm_year != -1)
+ /* chip only can handle alarms up to one week in the future*/
+ status = rs5c372_rtc_read_time(dev, &rtc_tm);
+ if (status)
+ return status;
+ rtc_secs = rtc_tm_to_time64(&rtc_tm);
+ alarm_secs = rtc_tm_to_time64(&t->time);
+ if (alarm_secs >= rtc_secs + 7 * 86400) {
+ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n",
+ __func__, status);
return -EINVAL;
+ }
/* REVISIT: round up tm_sec */
@@ -449,7 +483,9 @@ static int rs5c_set_alarm(struct device
/* set alarm */
buf[0] = bin2bcd(t->time.tm_min);
buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
- buf[2] = 0x7f; /* any/all days */
+ /* each bit is the day of the week, 0x7f means all days */
+ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ?
+ BIT(t->time.tm_wday) : 0x7f;
for (i = 0; i < sizeof(buf); i++) {
addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);

View File

@@ -0,0 +1,71 @@
From: Daniel González Cabanelas <dgcbueu@gmail.com>
Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source
Currently there is no use for the interrupts on the rs5c372 RTC and the
wakealarm isn't enabled. There are some devices like NASes which use this
RTC to wake up from the power off state when the INTR pin is activated by
the alarm clock.
Enable the alarm and let to be used as a wakeup source.
Tested on a Buffalo LS421DE NAS.
Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
---
drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -833,6 +833,7 @@ static int rs5c372_probe(struct i2c_clie
int err = 0;
int smbus_mode = 0;
struct rs5c372 *rs5c372;
+ bool rs5c372_can_wakeup_device = false;
dev_dbg(&client->dev, "%s\n", __func__);
@@ -868,6 +869,12 @@ static int rs5c372_probe(struct i2c_clie
else
rs5c372->type = id->driver_data;
+#ifdef CONFIG_OF
+ if(of_property_read_bool(client->dev.of_node,
+ "wakeup-source"))
+ rs5c372_can_wakeup_device = true;
+#endif
+
/* we read registers 0x0f then 0x00-0x0f; skip the first one */
rs5c372->regs = &rs5c372->buf[1];
rs5c372->smbus = smbus_mode;
@@ -901,6 +908,8 @@ static int rs5c372_probe(struct i2c_clie
goto exit;
}
+ rs5c372->has_irq = 1;
+
/* if the oscillator lost power and no other software (like
* the bootloader) set it up, do it here.
*
@@ -927,6 +936,10 @@ static int rs5c372_probe(struct i2c_clie
);
/* REVISIT use client->irq to register alarm irq ... */
+ if (rs5c372_can_wakeup_device) {
+ device_init_wakeup(&client->dev, true);
+ }
+
rs5c372->rtc = devm_rtc_device_register(&client->dev,
rs5c372_driver.driver.name,
&rs5c372_rtc_ops, THIS_MODULE);
@@ -940,6 +953,10 @@ static int rs5c372_probe(struct i2c_clie
if (err)
goto exit;
+ /* the rs5c372 alarm only supports a minute accuracy */
+ set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rs5c372->rtc->features);
+ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rs5c372->rtc->features);
+
return 0;
exit:

View File

@@ -0,0 +1,118 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: kernel: add a config option for keeping the kallsyms table uncompressed, saving ~9kb kernel size after lzma on ar71xx
[john@phrozen.org: added to my upstream queue 30.12.2016]
lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
init/Kconfig | 11 +++++++++++
kernel/kallsyms.c | 8 ++++++++
scripts/kallsyms.c | 12 ++++++++++++
scripts/link-vmlinux.sh | 4 ++++
4 files changed, 35 insertions(+)
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1491,6 +1491,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW
the unaligned access emulation.
see arch/parisc/kernel/unaligned.c for reference
+config KALLSYMS_UNCOMPRESSED
+ bool "Keep kallsyms uncompressed"
+ depends on KALLSYMS
+ help
+ Normally kallsyms contains compressed symbols (using a token table),
+ reducing the uncompressed kernel image size. Keeping the symbol table
+ uncompressed significantly improves the size of this part in compressed
+ kernel images.
+
+ Say N unless you need compressed kernel images to be small.
+
config HAVE_PCSPKR_PLATFORM
bool
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -69,6 +69,11 @@ static unsigned int kallsyms_expand_symb
* For every byte on the compressed symbol data, copy the table
* entry for that byte.
*/
+#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
+ memcpy(result, data + 1, len - 1);
+ result += len - 1;
+ len = 0;
+#endif
while (len) {
tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
data++;
@@ -101,6 +106,9 @@ tail:
*/
static char kallsyms_get_symbol_type(unsigned int off)
{
+#ifdef CONFIG_KALLSYMS_UNCOMPRESSED
+ return kallsyms_names[off + 1];
+#endif
/*
* Get just the first code, look it up in the token table,
* and return the first char from this token.
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -77,6 +77,7 @@ static struct addr_range percpu_range =
static struct sym_entry **table;
static unsigned int table_size, table_cnt;
static int all_symbols;
+static int uncompressed;
static int absolute_percpu;
static int base_relative;
static int lto_clang;
@@ -608,6 +609,9 @@ static void write_src(void)
(unsigned char)(table[i]->seq >> 0));
printf("\n");
+ if (uncompressed)
+ return;
+
output_label("kallsyms_token_table");
off = 0;
for (i = 0; i < 256; i++) {
@@ -659,6 +663,9 @@ static unsigned char *find_token(unsigne
{
int i;
+ if (uncompressed)
+ return NULL;
+
for (i = 0; i < len - 1; i++) {
if (str[i] == token[0] && str[i+1] == token[1])
return &str[i];
@@ -731,6 +738,9 @@ static void optimize_result(void)
{
int i, best;
+ if (uncompressed)
+ return;
+
/* using the '\0' symbol last allows compress_symbols to use standard
* fast string functions */
for (i = 255; i >= 0; i--) {
@@ -892,6 +902,7 @@ int main(int argc, char **argv)
{"absolute-percpu", no_argument, &absolute_percpu, 1},
{"base-relative", no_argument, &base_relative, 1},
{"lto-clang", no_argument, &lto_clang, 1},
+ {"uncompressed", no_argument, &uncompressed, 1},
{},
};
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -165,6 +165,10 @@ kallsyms()
kallsymopt="${kallsymopt} --lto-clang"
fi
+ if is_enabled CONFIG_KALLSYMS_UNCOMPRESSED; then
+ kallsymopt="${kallsymopt} --uncompressed"
+ fi
+
info KSYMS ${2}
scripts/kallsyms ${kallsymopt} ${1} > ${2}
}

View File

@@ -0,0 +1,41 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: kernel: when KALLSYMS is disabled, print module address + size for matching backtrace entries
[john@phrozen.org: felix will add this to his upstream queue]
lede-commit 53827cdc824556cda910b23ce5030c363b8f1461
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
lib/vsprintf.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -985,8 +985,10 @@ char *symbol_string(char *buf, char *end
struct printf_spec spec, const char *fmt)
{
unsigned long value;
-#ifdef CONFIG_KALLSYMS
char sym[KSYM_SYMBOL_LEN];
+#ifndef CONFIG_KALLSYMS
+ struct module *mod;
+ int len;
#endif
if (fmt[1] == 'R')
@@ -1007,8 +1009,14 @@ char *symbol_string(char *buf, char *end
return string_nocheck(buf, end, sym, spec);
#else
- return special_hex_number(buf, end, value, sizeof(void *));
+ len = snprintf(sym, sizeof(sym), "0x%lx", value);
+ mod = __module_address(value);
+ if (mod)
+ snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]",
+ mod->name, mod->core_layout.base,
+ mod->core_layout.size);
#endif
+ return string(buf, end, sym, spec);
}
static const struct printf_spec default_str_spec = {

View File

@@ -0,0 +1,30 @@
From: Gabor Juhos <juhosg@openwrt.org>
Subject: usr: sanitize deps_initramfs list
If any filename in the intramfs dependency
list contains a colon, that causes a kernel
build error like this:
/devel/openwrt/build_dir/linux-ar71xx_generic/linux-3.6.6/usr/Makefile:58: *** multiple target patterns. Stop.
make[5]: *** [usr] Error 2
Fix it by removing such filenames from the
deps_initramfs list.
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
usr/Makefile | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -56,6 +56,8 @@ hostprogs := gen_init_cpio
# The dependency list is generated by gen_initramfs.sh -l
-include $(obj)/.initramfs_data.cpio.d
+deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v)))
+
# do not try to update files included in initramfs
$(deps_initramfs): ;

View File

@@ -0,0 +1,20 @@
From: Imre Kaloz <kaloz@openwrt.org>
Subject: [PATCH] hack: net: wireless: make the wl12xx glue code available with
compat-wireless, too
Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
---
drivers/net/wireless/ti/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/wireless/ti/Kconfig
+++ b/drivers/net/wireless/ti/Kconfig
@@ -20,7 +20,7 @@ source "drivers/net/wireless/ti/wlcore/K
config WILINK_PLATFORM_DATA
bool "TI WiLink platform data"
- depends on WLCORE_SDIO || WL1251_SDIO
+ depends on WLCORE_SDIO || WL1251_SDIO || ARCH_OMAP2PLUS
default y
help
Small platform data bit needed to pass data to the sdio modules.

View File

@@ -0,0 +1,31 @@
From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
Date: Sat, 28 Mar 2020 12:11:50 +0100
Subject: [PATCH] generic: platform/mikrotik build bits (5.4)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch adds platform/mikrotik kernel build bits
Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
---
drivers/platform/Kconfig | 2 ++
drivers/platform/Makefile | 1 +
2 files changed, 3 insertions(+)
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -16,3 +16,5 @@ source "drivers/platform/olpc/Kconfig"
source "drivers/platform/surface/Kconfig"
source "drivers/platform/x86/Kconfig"
+
+source "drivers/platform/mikrotik/Kconfig"
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
+obj-$(CONFIG_MIKROTIK) += mikrotik/

View File

@@ -0,0 +1,40 @@
From: Mark Miller <mark@mirell.org>
Subject: mips: expose CONFIG_BOOT_RAW
This exposes the CONFIG_BOOT_RAW symbol in Kconfig. This is needed on
certain Broadcom chipsets running CFE in order to load the kernel.
Signed-off-by: Mark Miller <mark@mirell.org>
Acked-by: Rob Landley <rob@landley.net>
---
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1035,9 +1035,6 @@ config FW_ARC
config ARCH_MAY_HAVE_PC_FDC
bool
-config BOOT_RAW
- bool
-
config CEVT_BCM1480
bool
@@ -3093,6 +3090,18 @@ choice
bool "Extend builtin kernel arguments with bootloader arguments"
endchoice
+config BOOT_RAW
+ bool "Enable the kernel to be executed from the load address"
+ default n
+ help
+ Allow the kernel to be executed from the load address for
+ bootloaders which cannot read the ELF format. This places
+ a jump to start_kernel at the load address.
+
+ If unsure, say N.
+
+
+
endmenu
config LOCKDEP_SUPPORT

View File

@@ -0,0 +1,71 @@
From e6e6ef4275978823ec3a84133fc91f4ffbef5c84 Mon Sep 17 00:00:00 2001
From: Paul Burton <paul.burton@imgtec.com>
Date: Mon, 22 Feb 2016 18:09:44 +0000
Subject: [PATCH] MIPS: Add barriers between dcache & icache flushes
Index-based cache operations may be arbitrarily reordered by out of
order CPUs. Thus code which writes back the dcache & then invalidates
the icache using indexed cache ops must include a barrier between
operating on the 2 caches in order to prevent the scenario in which:
- icache invalidation occurs.
- icache fetch occurs, due to speculation.
- dcache writeback occurs.
If the above were allowed to happen then the icache would contain stale
data. Forcing the dcache writeback to complete before the icache
invalidation avoids this.
Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: James Hogan <james.hogan@imgtec.com>
---
arch/mips/mm/c-r4k.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -514,6 +514,7 @@ static inline void local_r4k___flush_cac
default:
r4k_blast_dcache();
+ mb(); /* cache instructions may be reordered */
r4k_blast_icache();
break;
}
@@ -594,8 +595,10 @@ static inline void local_r4k_flush_cache
if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc))
r4k_blast_dcache();
/* If executable, blast stale lines from icache */
- if (exec)
+ if (exec) {
+ mb(); /* cache instructions may be reordered */
r4k_blast_icache();
+ }
}
static void r4k_flush_cache_range(struct vm_area_struct *vma,
@@ -696,8 +699,13 @@ static inline void local_r4k_flush_cache
if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) {
vaddr ? r4k_blast_dcache_page(addr) :
r4k_blast_dcache_user_page(addr);
- if (exec && !cpu_icache_snoops_remote_store)
+ if (exec)
+ mb(); /* cache instructions may be reordered */
+
+ if (exec && !cpu_icache_snoops_remote_store) {
r4k_blast_scache_page(addr);
+ mb(); /* cache instructions may be reordered */
+ }
}
if (exec) {
if (vaddr && cpu_has_vtag_icache && mm == current->active_mm) {
@@ -764,6 +772,7 @@ static inline void __local_r4k_flush_ica
else
blast_dcache_range(start, end);
}
+ mb(); /* cache instructions may be reordered */
}
if (type == R4K_INDEX ||

View File

@@ -0,0 +1,22 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: mips: use -mno-branch-likely for kernel and userspace
saves ~11k kernel size after lzma and ~12k squashfs size in the
lede-commit: 41a039f46450ffae9483d6216422098669da2900
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
arch/mips/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -94,7 +94,7 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
# machines may also. Since BFD is incredibly buggy with respect to
# crossformat linking we rely on the elf2ecoff tool for format conversion.
#
-cflags-y += -G 0 -mno-abicalls -fno-pic -pipe
+cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
cflags-y += -msoft-float
LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
KBUILD_AFLAGS_MODULE += -mlong-calls

View File

@@ -0,0 +1,370 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: mips: replace -mlong-calls with -mno-long-calls to make function calls faster in kernel modules to achieve this, try to
lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
arch/mips/Makefile | 5 +
arch/mips/include/asm/module.h | 5 +
arch/mips/kernel/module.c | 279 ++++++++++++++++++++++++++++++++++++++++-
3 files changed, 284 insertions(+), 5 deletions(-)
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -97,8 +97,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
cflags-y += -msoft-float
LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
+ifdef CONFIG_64BIT
KBUILD_AFLAGS_MODULE += -mlong-calls
KBUILD_CFLAGS_MODULE += -mlong-calls
+else
+ ifdef CONFIG_DYNAMIC_FTRACE
+ KBUILD_AFLAGS_MODULE += -mlong-calls
+ KBUILD_CFLAGS_MODULE += -mlong-calls
+ else
+ KBUILD_AFLAGS_MODULE += -mno-long-calls
+ KBUILD_CFLAGS_MODULE += -mno-long-calls
+ endif
+endif
ifeq ($(CONFIG_RELOCATABLE),y)
LDFLAGS_vmlinux += --emit-relocs
--- a/arch/mips/include/asm/module.h
+++ b/arch/mips/include/asm/module.h
@@ -12,6 +12,11 @@ struct mod_arch_specific {
const struct exception_table_entry *dbe_start;
const struct exception_table_entry *dbe_end;
struct mips_hi16 *r_mips_hi16_list;
+
+ void *phys_plt_tbl;
+ void *virt_plt_tbl;
+ unsigned int phys_plt_offset;
+ unsigned int virt_plt_offset;
};
typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */
--- a/arch/mips/kernel/module.c
+++ b/arch/mips/kernel/module.c
@@ -32,23 +32,261 @@ struct mips_hi16 {
static LIST_HEAD(dbe_list);
static DEFINE_SPINLOCK(dbe_lock);
-#ifdef MODULE_START
+/*
+ * Get the potential max trampolines size required of the init and
+ * non-init sections. Only used if we cannot find enough contiguous
+ * physically mapped memory to put the module into.
+ */
+static unsigned int
+get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ const char *secstrings, unsigned int symindex, bool is_init)
+{
+ unsigned long ret = 0;
+ unsigned int i, j;
+ Elf_Sym *syms;
+
+ /* Everything marked ALLOC (this includes the exported symbols) */
+ for (i = 1; i < hdr->e_shnum; ++i) {
+ unsigned int info = sechdrs[i].sh_info;
+
+ if (sechdrs[i].sh_type != SHT_REL
+ && sechdrs[i].sh_type != SHT_RELA)
+ continue;
+
+ /* Not a valid relocation section? */
+ if (info >= hdr->e_shnum)
+ continue;
+
+ /* Don't bother with non-allocated sections */
+ if (!(sechdrs[info].sh_flags & SHF_ALLOC))
+ continue;
+
+ /* If it's called *.init*, and we're not init, we're
+ not interested */
+ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0)
+ != is_init)
+ continue;
+
+ syms = (Elf_Sym *) sechdrs[symindex].sh_addr;
+ if (sechdrs[i].sh_type == SHT_REL) {
+ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr;
+ unsigned int size = sechdrs[i].sh_size / sizeof(*rel);
+
+ for (j = 0; j < size; ++j) {
+ Elf_Sym *sym;
+
+ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26)
+ continue;
+
+ sym = syms + ELF_MIPS_R_SYM(rel[j]);
+ if (!is_init && sym->st_shndx != SHN_UNDEF)
+ continue;
+
+ ret += 4 * sizeof(int);
+ }
+ } else {
+ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr;
+ unsigned int size = sechdrs[i].sh_size / sizeof(*rela);
+
+ for (j = 0; j < size; ++j) {
+ Elf_Sym *sym;
+
+ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26)
+ continue;
+
+ sym = syms + ELF_MIPS_R_SYM(rela[j]);
+ if (!is_init && sym->st_shndx != SHN_UNDEF)
+ continue;
+
+ ret += 4 * sizeof(int);
+ }
+ }
+ }
+
+ return ret;
+}
+
+#ifndef MODULE_START
+static void *alloc_phys(unsigned long size)
+{
+ unsigned order;
+ struct page *page;
+ struct page *p;
+
+ size = PAGE_ALIGN(size);
+ order = get_order(size);
+
+ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN |
+ __GFP_THISNODE, order);
+ if (!page)
+ return NULL;
+
+ split_page(page, order);
+
+ /* mark all pages except for the last one */
+ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p)
+ set_bit(PG_owner_priv_1, &p->flags);
+
+ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p)
+ __free_page(p);
+
+ return page_address(page);
+}
+#endif
+
+static void free_phys(void *ptr)
+{
+ struct page *page;
+ bool free;
+
+ page = virt_to_page(ptr);
+ do {
+ free = test_and_clear_bit(PG_owner_priv_1, &page->flags);
+ __free_page(page);
+ page++;
+ } while (free);
+}
+
+
void *module_alloc(unsigned long size)
{
+#ifdef MODULE_START
return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END,
GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE,
__builtin_return_address(0));
+#else
+ void *ptr;
+
+ if (size == 0)
+ return NULL;
+
+ ptr = alloc_phys(size);
+
+ /* If we failed to allocate physically contiguous memory,
+ * fall back to regular vmalloc. The module loader code will
+ * create jump tables to handle long jumps */
+ if (!ptr)
+ return vmalloc(size);
+
+ return ptr;
+#endif
}
+
+static inline bool is_phys_addr(void *ptr)
+{
+#ifdef CONFIG_64BIT
+ return (KSEGX((unsigned long)ptr) == CKSEG0);
+#else
+ return (KSEGX(ptr) == KSEG0);
#endif
+}
+
+/* Free memory returned from module_alloc */
+void module_memfree(void *module_region)
+{
+ if (is_phys_addr(module_region))
+ free_phys(module_region);
+ else
+ vfree(module_region);
+}
+
+static void *__module_alloc(int size, bool phys)
+{
+ void *ptr;
+
+ if (phys)
+ ptr = kmalloc(size, GFP_KERNEL);
+ else
+ ptr = vmalloc(size);
+ return ptr;
+}
+
+static void __module_free(void *ptr)
+{
+ if (is_phys_addr(ptr))
+ kfree(ptr);
+ else
+ vfree(ptr);
+}
+
+int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+ char *secstrings, struct module *mod)
+{
+ unsigned int symindex = 0;
+ unsigned int core_size, init_size;
+ int i;
+
+ mod->arch.phys_plt_offset = 0;
+ mod->arch.virt_plt_offset = 0;
+ mod->arch.phys_plt_tbl = NULL;
+ mod->arch.virt_plt_tbl = NULL;
+
+ if (IS_ENABLED(CONFIG_64BIT))
+ return 0;
+
+ for (i = 1; i < hdr->e_shnum; i++)
+ if (sechdrs[i].sh_type == SHT_SYMTAB)
+ symindex = i;
+
+ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false);
+ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true);
+
+ if ((core_size + init_size) == 0)
+ return 0;
+
+ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1);
+ if (!mod->arch.phys_plt_tbl)
+ return -ENOMEM;
+
+ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0);
+ if (!mod->arch.virt_plt_tbl) {
+ __module_free(mod->arch.phys_plt_tbl);
+ mod->arch.phys_plt_tbl = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
static void apply_r_mips_32(u32 *location, u32 base, Elf_Addr v)
{
*location = base + v;
}
+static Elf_Addr add_plt_entry_to(unsigned *plt_offset,
+ void *start, Elf_Addr v)
+{
+ unsigned *tramp = start + *plt_offset;
+ *plt_offset += 4 * sizeof(int);
+
+ /* adjust carry for addiu */
+ if (v & 0x00008000)
+ v += 0x10000;
+
+ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */
+ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */
+ tramp[2] = 0x03200008; /* jr t9 */
+ tramp[3] = 0x00000000; /* nop */
+
+ return (Elf_Addr) tramp;
+}
+
+static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v)
+{
+ if (is_phys_addr(location))
+ return add_plt_entry_to(&me->arch.phys_plt_offset,
+ me->arch.phys_plt_tbl, v);
+ else
+ return add_plt_entry_to(&me->arch.virt_plt_offset,
+ me->arch.virt_plt_tbl, v);
+
+}
+
static int apply_r_mips_26(struct module *me, u32 *location, u32 base,
Elf_Addr v)
{
+ u32 ofs = base & 0x03ffffff;
+
if (v % 4) {
pr_err("module %s: dangerous R_MIPS_26 relocation\n",
me->name);
@@ -56,13 +294,17 @@ static int apply_r_mips_26(struct module
}
if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
- pr_err("module %s: relocation overflow\n",
- me->name);
- return -ENOEXEC;
+ v = add_plt_entry(me, location, v + (ofs << 2));
+ if (!v) {
+ pr_err("module %s: relocation overflow\n",
+ me->name);
+ return -ENOEXEC;
+ }
+ ofs = 0;
}
*location = (*location & ~0x03ffffff) |
- ((base + (v >> 2)) & 0x03ffffff);
+ ((ofs + (v >> 2)) & 0x03ffffff);
return 0;
}
@@ -442,9 +684,36 @@ int module_finalize(const Elf_Ehdr *hdr,
list_add(&me->arch.dbe_list, &dbe_list);
spin_unlock_irq(&dbe_lock);
}
+
+ /* Get rid of the fixup trampoline if we're running the module
+ * from physically mapped address space */
+ if (me->arch.phys_plt_offset == 0) {
+ __module_free(me->arch.phys_plt_tbl);
+ me->arch.phys_plt_tbl = NULL;
+ }
+ if (me->arch.virt_plt_offset == 0) {
+ __module_free(me->arch.virt_plt_tbl);
+ me->arch.virt_plt_tbl = NULL;
+ }
+
return 0;
}
+void module_arch_freeing_init(struct module *mod)
+{
+ if (mod->state == MODULE_STATE_LIVE)
+ return;
+
+ if (mod->arch.phys_plt_tbl) {
+ __module_free(mod->arch.phys_plt_tbl);
+ mod->arch.phys_plt_tbl = NULL;
+ }
+ if (mod->arch.virt_plt_tbl) {
+ __module_free(mod->arch.virt_plt_tbl);
+ mod->arch.virt_plt_tbl = NULL;
+ }
+}
+
void module_arch_cleanup(struct module *mod)
{
spin_lock_irq(&dbe_lock);

View File

@@ -0,0 +1,22 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: kernel: add -mtune=34kc to MIPS CFLAGS when building for mips32r2
This provides a good tradeoff across at least 24Kc-74Kc, while also
producing smaller code.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
arch/mips/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -172,7 +172,7 @@ cflags-$(CONFIG_CPU_R4300) += -march=r43
cflags-$(CONFIG_CPU_R4X00) += -march=r4600 -Wa,--trap
cflags-$(CONFIG_CPU_TX49XX) += -march=r4600 -Wa,--trap
cflags-$(CONFIG_CPU_MIPS32_R1) += -march=mips32 -Wa,--trap
-cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -Wa,--trap
+cflags-$(CONFIG_CPU_MIPS32_R2) += -march=mips32r2 -mtune=34kc -Wa,--trap
cflags-$(CONFIG_CPU_MIPS32_R5) += -march=mips32r5 -Wa,--trap -modd-spreg
cflags-$(CONFIG_CPU_MIPS32_R6) += -march=mips32r6 -Wa,--trap -modd-spreg
cflags-$(CONFIG_CPU_MIPS64_R1) += -march=mips64 -Wa,--trap

View File

@@ -0,0 +1,22 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: fix errors in unresolved weak symbols on arm
lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
arch/arm/kernel/module.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -146,6 +146,10 @@ apply_relocate(Elf32_Shdr *sechdrs, cons
return -ENOEXEC;
}
+ if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) &&
+ ELF_ST_BIND(sym->st_info) == STB_WEAK)
+ continue;
+
loc = dstsec->sh_addr + rel->r_offset;
switch (ELF32_R_TYPE(rel->r_info)) {

View File

@@ -0,0 +1,282 @@
From: Yousong Zhou <yszhou4tech@gmail.com>
Subject: MIPS: kexec: Accept command line parameters from userspace.
Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
---
arch/mips/kernel/machine_kexec.c | 153 +++++++++++++++++++++++++++++++-----
arch/mips/kernel/machine_kexec.h | 20 +++++
arch/mips/kernel/relocate_kernel.S | 21 +++--
3 files changed, 167 insertions(+), 27 deletions(-)
create mode 100644 arch/mips/kernel/machine_kexec.h
--- a/arch/mips/kernel/machine_kexec.c
+++ b/arch/mips/kernel/machine_kexec.c
@@ -9,14 +9,11 @@
#include <linux/delay.h>
#include <linux/libfdt.h>
+#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/page.h>
-
-extern const unsigned char relocate_new_kernel[];
-extern const size_t relocate_new_kernel_size;
-
-extern unsigned long kexec_start_address;
-extern unsigned long kexec_indirection_page;
+#include <linux/uaccess.h>
+#include "machine_kexec.h"
static unsigned long reboot_code_buffer;
@@ -30,6 +27,101 @@ void (*_crash_smp_send_stop)(void) = NUL
void (*_machine_kexec_shutdown)(void) = NULL;
void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
+static void machine_kexec_print_args(void)
+{
+ unsigned long argc = (int)kexec_args[0];
+ int i;
+
+ pr_info("kexec_args[0] (argc): %lu\n", argc);
+ pr_info("kexec_args[1] (argv): %p\n", (void *)kexec_args[1]);
+ pr_info("kexec_args[2] (env ): %p\n", (void *)kexec_args[2]);
+ pr_info("kexec_args[3] (desc): %p\n", (void *)kexec_args[3]);
+
+ for (i = 0; i < argc; i++) {
+ pr_info("kexec_argv[%d] = %p, %s\n",
+ i, kexec_argv[i], kexec_argv[i]);
+ }
+}
+
+static void machine_kexec_init_argv(struct kimage *image)
+{
+ void __user *buf = NULL;
+ size_t bufsz;
+ size_t size;
+ int i;
+
+ bufsz = 0;
+ for (i = 0; i < image->nr_segments; i++) {
+ struct kexec_segment *seg;
+
+ seg = &image->segment[i];
+ if (seg->bufsz < 6)
+ continue;
+
+ if (strncmp((char *) seg->buf, "kexec ", 6))
+ continue;
+
+ buf = seg->buf;
+ bufsz = seg->bufsz;
+ break;
+ }
+
+ if (!buf)
+ return;
+
+ size = KEXEC_COMMAND_LINE_SIZE;
+ size = min(size, bufsz);
+ if (size < bufsz)
+ pr_warn("kexec command line truncated to %zd bytes\n", size);
+
+ /* Copy to kernel space */
+ if (copy_from_user(kexec_argv_buf, buf, size))
+ pr_warn("kexec command line copy to kernel space failed\n");
+
+ kexec_argv_buf[size - 1] = 0;
+}
+
+static void machine_kexec_parse_argv(struct kimage *image)
+{
+ char *reboot_code_buffer;
+ int reloc_delta;
+ char *ptr;
+ int argc;
+ int i;
+
+ ptr = kexec_argv_buf;
+ argc = 0;
+
+ /*
+ * convert command line string to array of parameters
+ * (as bootloader does).
+ */
+ while (ptr && *ptr && (KEXEC_MAX_ARGC > argc)) {
+ if (*ptr == ' ') {
+ *ptr++ = '\0';
+ continue;
+ }
+
+ kexec_argv[argc++] = ptr;
+ ptr = strchr(ptr, ' ');
+ }
+
+ if (!argc)
+ return;
+
+ kexec_args[0] = argc;
+ kexec_args[1] = (unsigned long)kexec_argv;
+ kexec_args[2] = 0;
+ kexec_args[3] = 0;
+
+ reboot_code_buffer = page_address(image->control_code_page);
+ reloc_delta = reboot_code_buffer - (char *)kexec_relocate_new_kernel;
+
+ kexec_args[1] += reloc_delta;
+ for (i = 0; i < argc; i++)
+ kexec_argv[i] += reloc_delta;
+}
+
static void kexec_image_info(const struct kimage *kimage)
{
unsigned long i;
@@ -99,6 +191,18 @@ machine_kexec_prepare(struct kimage *kim
#endif
kexec_image_info(kimage);
+ /*
+ * Whenever arguments passed from kexec-tools, Init the arguments as
+ * the original ones to try avoiding booting failure.
+ */
+
+ kexec_args[0] = fw_arg0;
+ kexec_args[1] = fw_arg1;
+ kexec_args[2] = fw_arg2;
+ kexec_args[3] = fw_arg3;
+
+ machine_kexec_init_argv(kimage);
+ machine_kexec_parse_argv(kimage);
if (_machine_kexec_prepare)
return _machine_kexec_prepare(kimage);
@@ -161,7 +265,7 @@ machine_crash_shutdown(struct pt_regs *r
void kexec_nonboot_cpu_jump(void)
{
local_flush_icache_range((unsigned long)relocated_kexec_smp_wait,
- reboot_code_buffer + relocate_new_kernel_size);
+ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
relocated_kexec_smp_wait(NULL);
}
@@ -199,7 +303,7 @@ void kexec_reboot(void)
* machine_kexec() CPU.
*/
local_flush_icache_range(reboot_code_buffer,
- reboot_code_buffer + relocate_new_kernel_size);
+ reboot_code_buffer + KEXEC_RELOCATE_NEW_KERNEL_SIZE);
do_kexec = (void *)reboot_code_buffer;
do_kexec();
@@ -212,10 +316,12 @@ machine_kexec(struct kimage *image)
unsigned long *ptr;
reboot_code_buffer =
- (unsigned long)page_address(image->control_code_page);
+ (unsigned long)page_address(image->control_code_page);
+ pr_info("reboot_code_buffer = %p\n", (void *)reboot_code_buffer);
kexec_start_address =
(unsigned long) phys_to_virt(image->start);
+ pr_info("kexec_start_address = %p\n", (void *)kexec_start_address);
if (image->type == KEXEC_TYPE_DEFAULT) {
kexec_indirection_page =
@@ -223,9 +329,19 @@ machine_kexec(struct kimage *image)
} else {
kexec_indirection_page = (unsigned long)&image->head;
}
+ pr_info("kexec_indirection_page = %p\n", (void *)kexec_indirection_page);
- memcpy((void*)reboot_code_buffer, relocate_new_kernel,
- relocate_new_kernel_size);
+ pr_info("Where is memcpy: %p\n", memcpy);
+ pr_info("kexec_relocate_new_kernel = %p, kexec_relocate_new_kernel_end = %p\n",
+ (void *)kexec_relocate_new_kernel, &kexec_relocate_new_kernel_end);
+ pr_info("Copy %lu bytes from %p to %p\n", KEXEC_RELOCATE_NEW_KERNEL_SIZE,
+ (void *)kexec_relocate_new_kernel, (void *)reboot_code_buffer);
+ memcpy((void*)reboot_code_buffer, kexec_relocate_new_kernel,
+ KEXEC_RELOCATE_NEW_KERNEL_SIZE);
+
+ pr_info("Before _print_args().\n");
+ machine_kexec_print_args();
+ pr_info("Before eval loop.\n");
/*
* The generic kexec code builds a page list with physical
@@ -256,7 +372,7 @@ machine_kexec(struct kimage *image)
#ifdef CONFIG_SMP
/* All secondary cpus now may jump to kexec_wait cycle */
relocated_kexec_smp_wait = reboot_code_buffer +
- (void *)(kexec_smp_wait - relocate_new_kernel);
+ (void *)(kexec_smp_wait - kexec_relocate_new_kernel);
smp_wmb();
atomic_set(&kexec_ready_to_reboot, 1);
#endif
--- /dev/null
+++ b/arch/mips/kernel/machine_kexec.h
@@ -0,0 +1,20 @@
+#ifndef _MACHINE_KEXEC_H
+#define _MACHINE_KEXEC_H
+
+#ifndef __ASSEMBLY__
+extern const unsigned char kexec_relocate_new_kernel[];
+extern unsigned long kexec_relocate_new_kernel_end;
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_indirection_page;
+
+extern char kexec_argv_buf[];
+extern char *kexec_argv[];
+
+#define KEXEC_RELOCATE_NEW_KERNEL_SIZE ((unsigned long)&kexec_relocate_new_kernel_end - (unsigned long)kexec_relocate_new_kernel)
+#endif /* !__ASSEMBLY__ */
+
+#define KEXEC_COMMAND_LINE_SIZE 256
+#define KEXEC_ARGV_SIZE (KEXEC_COMMAND_LINE_SIZE / 16)
+#define KEXEC_MAX_ARGC (KEXEC_ARGV_SIZE / sizeof(long))
+
+#endif
--- a/arch/mips/kernel/relocate_kernel.S
+++ b/arch/mips/kernel/relocate_kernel.S
@@ -10,10 +10,11 @@
#include <asm/mipsregs.h>
#include <asm/stackframe.h>
#include <asm/addrspace.h>
+#include "machine_kexec.h"
#include <kernel-entry-init.h>
-LEAF(relocate_new_kernel)
+LEAF(kexec_relocate_new_kernel)
PTR_L a0, arg0
PTR_L a1, arg1
PTR_L a2, arg2
@@ -98,7 +99,7 @@ done:
#endif
/* jump to kexec_start_address */
j s1
- END(relocate_new_kernel)
+ END(kexec_relocate_new_kernel)
#ifdef CONFIG_SMP
/*
@@ -177,8 +178,15 @@ EXPORT(kexec_indirection_page)
PTR_WD 0
.size kexec_indirection_page, PTRSIZE
-relocate_new_kernel_end:
+kexec_argv_buf:
+ EXPORT(kexec_argv_buf)
+ .skip KEXEC_COMMAND_LINE_SIZE
+ .size kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE
+
+kexec_argv:
+ EXPORT(kexec_argv)
+ .skip KEXEC_ARGV_SIZE
+ .size kexec_argv, KEXEC_ARGV_SIZE
-EXPORT(relocate_new_kernel_size)
- PTR_WD relocate_new_kernel_end - relocate_new_kernel
- .size relocate_new_kernel_size, PTRSIZE
+kexec_relocate_new_kernel_end:
+ EXPORT(kexec_relocate_new_kernel_end)

View File

@@ -0,0 +1,84 @@
From bb0c3b0175240bf152fd7c644821a0cf9f77c37c Mon Sep 17 00:00:00 2001
From: Evgeniy Didin <Evgeniy.Didin@synopsys.com>
Date: Fri, 15 Mar 2019 18:53:38 +0300
Subject: [PATCH] arc add OWRTDTB section
This change allows OpenWRT to patch resulting kernel binary with
external .dtb.
That allows us to re-use exactky the same vmlinux on different boards
given its ARC core configurations match (at least cache line sizes etc).
""patch-dtb" searches for ASCII "OWRTDTB:" strign and copies external
.dtb right after it, keeping the string in place.
Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
Signed-off-by: Evgeniy Didin <Evgeniy.Didin@synopsys.com>
---
arch/arc/kernel/head.S | 10 ++++++++++
arch/arc/kernel/setup.c | 4 +++-
arch/arc/kernel/vmlinux.lds.S | 13 +++++++++++++
3 files changed, 26 insertions(+), 1 deletion(-)
--- a/arch/arc/kernel/head.S
+++ b/arch/arc/kernel/head.S
@@ -88,6 +88,16 @@
DSP_EARLY_INIT
.endm
+ ; Here "patch-dtb" will embed external .dtb
+ ; Note "patch-dtb" searches for ASCII "OWRTDTB:" string
+ ; and pastes .dtb right after it, hense the string precedes
+ ; __image_dtb symbol.
+ .section .owrt, "aw",@progbits
+ .ascii "OWRTDTB:"
+ENTRY(__image_dtb)
+ .fill 0x4000
+END(__image_dtb)
+
.section .init.text, "ax",@progbits
;----------------------------------------------------------------
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -495,6 +495,8 @@ static inline bool uboot_arg_invalid(uns
/* We always pass 0 as magic from U-boot */
#define UBOOT_MAGIC_VALUE 0
+extern struct boot_param_header __image_dtb;
+
void __init handle_uboot_args(void)
{
bool use_embedded_dtb = true;
@@ -533,7 +535,7 @@ void __init handle_uboot_args(void)
ignore_uboot_args:
if (use_embedded_dtb) {
- machine_desc = setup_machine_fdt(__dtb_start);
+ machine_desc = setup_machine_fdt(&__image_dtb);
if (!machine_desc)
panic("Embedded DT invalid\n");
}
--- a/arch/arc/kernel/vmlinux.lds.S
+++ b/arch/arc/kernel/vmlinux.lds.S
@@ -27,6 +27,19 @@ SECTIONS
. = CONFIG_LINUX_LINK_BASE;
+ /*
+ * In OpenWRT we want to patch built binary embedding .dtb of choice.
+ * This is implemented with "patch-dtb" utility which searches for
+ * "OWRTDTB:" string in first 16k of image and if it is found
+ * copies .dtb right after mentioned string.
+ *
+ * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it.
+ */
+ .owrt : {
+ *(.owrt)
+ . = ALIGN(PAGE_SIZE);
+ }
+
_int_vec_base_lds = .;
.vector : {
*(.vector)

View File

@@ -0,0 +1,24 @@
From: Alexey Brodkin <abrodkin@synopsys.com>
Subject: arc: enable unaligned access in kernel mode
This enables misaligned access handling even in kernel mode.
Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses
here and there and to cope with that without fixing stuff in the drivers
we're just gracefully handling it on ARC.
Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
---
arch/arc/kernel/unaligned.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/arch/arc/kernel/unaligned.c
+++ b/arch/arc/kernel/unaligned.c
@@ -202,7 +202,7 @@ int misaligned_fixup(unsigned long addre
char buf[TASK_COMM_LEN];
/* handle user mode only and only if enabled by sysadmin */
- if (!user_mode(regs) || !unaligned_enabled)
+ if (!unaligned_enabled)
return 1;
if (no_unaligned_warning) {

View File

@@ -0,0 +1,25 @@
From 66770a004afe10df11d3902e16eaa0c2c39436bb Mon Sep 17 00:00:00 2001
From: Pawel Dembicki <paweldembicki@gmail.com>
Date: Fri, 24 May 2019 17:56:19 +0200
Subject: [PATCH] powerpc: Enable kernel XZ compression option on PPC_85xx
Enable kernel XZ compression option on PPC_85xx. Tested with
simpleImage on TP-Link TL-WDR4900 (Freescale P1014 processor).
Suggested-by: Christian Lamparter <chunkeey@gmail.com>
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
---
arch/powerpc/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -229,7 +229,7 @@ config PPC
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZMA if DEFAULT_UIMAGE
select HAVE_KERNEL_LZO if DEFAULT_UIMAGE
- select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x
+ select HAVE_KERNEL_XZ if PPC_BOOK3S || 44x || PPC_85xx
select HAVE_KPROBES
select HAVE_KPROBES_ON_FTRACE
select HAVE_KRETPROBES

View File

@@ -0,0 +1,74 @@
From: Shiji Yang <yangshiji66@outlook.com>
Date: Wed, 13 Mar 2024 20:28:37 +0800
Subject: [PATCH] mips: kernel: fix detect_memory_region() function
1. Do not use memcmp() on unallocated memory, as the new introduced
fortify dynamic object size check[1] will report unexpected result.
2. Use a fixed pattern instead of a random function pointer as the
magic value.
3. Flip magic value and double check it.
4. Enable this feature only for 32-bit CPUs. Currently, only ath79 and
ralink CPUs are using it.
[1] 439a1bcac648 ("fortify: Use __builtin_dynamic_object_size() when available")
Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
---
arch/mips/include/asm/bootinfo.h | 2 ++
arch/mips/kernel/setup.c | 17 ++++++++++++-----
2 files changed, 14 insertions(+), 5 deletions(-)
--- a/arch/mips/include/asm/bootinfo.h
+++ b/arch/mips/include/asm/bootinfo.h
@@ -93,7 +93,9 @@ const char *get_system_type(void);
extern unsigned long mips_machtype;
+#ifndef CONFIG_64BIT
extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max);
+#endif
extern void prom_init(void);
extern void prom_free_prom_memory(void);
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -90,21 +90,27 @@ static struct resource bss_resource = {
unsigned long __kaslr_offset __ro_after_init;
EXPORT_SYMBOL(__kaslr_offset);
-static void *detect_magic __initdata = detect_memory_region;
-
#ifdef CONFIG_MIPS_AUTO_PFN_OFFSET
unsigned long ARCH_PFN_OFFSET;
EXPORT_SYMBOL(ARCH_PFN_OFFSET);
#endif
+#ifndef CONFIG_64BIT
+static u32 detect_magic __initdata;
+#define MIPS_MEM_TEST_PATTERN 0xaa5555aa
+
void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max)
{
- void *dm = &detect_magic;
+ void *dm = (void *)KSEG1ADDR(&detect_magic);
phys_addr_t size;
for (size = sz_min; size < sz_max; size <<= 1) {
- if (!memcmp(dm, dm + size, sizeof(detect_magic)))
- break;
+ __raw_writel(MIPS_MEM_TEST_PATTERN, dm);
+ if (__raw_readl(dm) == __raw_readl(dm + size)) {
+ __raw_writel(~MIPS_MEM_TEST_PATTERN, dm);
+ if (__raw_readl(dm) == __raw_readl(dm + size))
+ break;
+ }
}
pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n",
@@ -115,6 +121,7 @@ void __init detect_memory_region(phys_ad
memblock_add(start, size);
}
+#endif /* CONFIG_64BIT */
/*
* Manage initrd

View File

@@ -0,0 +1,113 @@
From patchwork Thu Mar 16 19:28:33 2023
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?=
<noltari@gmail.com>
X-Patchwork-Id: 13178238
Return-Path: <linux-mips-owner@vger.kernel.org>
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
aws-us-west-2-korg-lkml-1.web.codeaurora.org
Received: from vger.kernel.org (vger.kernel.org [23.128.96.18])
by smtp.lore.kernel.org (Postfix) with ESMTP id 5EF2AC6FD19
for <linux-mips@archiver.kernel.org>; Thu, 16 Mar 2023 19:28:43 +0000 (UTC)
Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand
id S230076AbjCPT2l (ORCPT <rfc822;linux-mips@archiver.kernel.org>);
Thu, 16 Mar 2023 15:28:41 -0400
Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56412 "EHLO
lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org
with ESMTP id S230039AbjCPT2k (ORCPT
<rfc822;linux-mips@vger.kernel.org>); Thu, 16 Mar 2023 15:28:40 -0400
Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com
[IPv6:2a00:1450:4864:20::42f])
by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7259B7D9F;
Thu, 16 Mar 2023 12:28:38 -0700 (PDT)
Received: by mail-wr1-x42f.google.com with SMTP id y14so2539231wrq.4;
Thu, 16 Mar 2023 12:28:38 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=20210112; t=1678994917;
h=content-transfer-encoding:mime-version:references:in-reply-to
:message-id:date:subject:cc:to:from:from:to:cc:subject:date
:message-id:reply-to;
bh=j8afldfRZftLeVmekmQfoh01jVdumsVP7nkKoPaU3Q0=;
b=FzMRr5ekh/fDiJqTlezNj6nLjzvn5z92FtYeB8MquVSMB8PuvarccnyqAzsXiccf+v
uwRFIomnTWNLGVjzc1xrB2hGiCKD3jBo5n1u8p/yEV6rpolbxVjfM7eTHXyAHXGXz7ZJ
TPeVbWfAlxiSD6+BPtXr/efehcdI64fIoL6G/U1WHNMo01Tzr/Obf3y5tug17N0fGcXg
CH6E5a2HguZUtwrm26LcK9IOV/7xEx5eIE1cOvTLMxPbGWaZwEjjP16HylJr06xRLhaf
RpiYBT3mXwwuOx0jLOhqavY/2kZ9GVbZRWMMwZrZv9xNO13SBwc1VUVgD4k3FntnSk7Z
AaOQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20210112; t=1678994917;
h=content-transfer-encoding:mime-version:references:in-reply-to
:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc
:subject:date:message-id:reply-to;
bh=j8afldfRZftLeVmekmQfoh01jVdumsVP7nkKoPaU3Q0=;
b=OaA5DMgqalrfqO5iOtmmxFPsH90MkN7l4EJpyVnzuiO1Wd6rSCpqPOR7xpxZno8OPP
tdfm4vzn9Ie4AUDbFKDTUlPG+tgkmIruo3K9C0VnY9DD2PRZMEYBbWaJKU1otqKt0NKu
IAAHNvxvQvCESKzbXFLYwWbRKFScOSMGmGBTDfgThz51A18Ff1hJy/BmnuZk7M2TLgHO
wQpy9t7oeB/Hkxl41y46emLc/nESsvwvAG/fx/zPzCe9UiaQLrdZq+BKeOwSBedktzK5
U/ZTfgzU2UGSI67aGRqqGnI0uXq+MAJMK18qzM0VByxj6W+AXJ6BJr5P0quljeQ8upSg
bEUg==
X-Gm-Message-State: AO0yUKWnqTlccBDnqwCSRdqOBGc2FyfiLy1Tg7EjPENlISpzXuDYwW/R
lJSI06rrfq+Vel/SigfpGJI=
X-Google-Smtp-Source:
AK7set/jYfYl9ttVzIXJO+ZQVfa6cE/yOsP8fx4teiTmGNNWyVlIJRzMAlF3IUGqRAXAmY3hAabIuQ==
X-Received: by 2002:a5d:40ce:0:b0:2cd:ceab:df1a with SMTP id
b14-20020a5d40ce000000b002cdceabdf1amr381006wrq.32.1678994916642;
Thu, 16 Mar 2023 12:28:36 -0700 (PDT)
Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net.
[79.146.124.255])
by smtp.gmail.com with ESMTPSA id
l10-20020a5d4bca000000b002cfea3c49d5sm180041wrt.52.2023.03.16.12.28.35
(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
Thu, 16 Mar 2023 12:28:35 -0700 (PDT)
From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
To: f.fainelli@gmail.com, jonas.gorski@gmail.com,
bcm-kernel-feedback-list@broadcom.com, tglx@linutronix.de,
maz@kernel.org, linux-mips@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= <noltari@gmail.com>
Subject: [PATCH v2] irqchip/bcm-6345-l1: request memory region
Date: Thu, 16 Mar 2023 20:28:33 +0100
Message-Id: <20230316192833.1603149-1-noltari@gmail.com>
X-Mailer: git-send-email 2.30.2
In-Reply-To: <20230316180701.783785-1-noltari@gmail.com>
References: <20230316180701.783785-1-noltari@gmail.com>
MIME-Version: 1.0
Precedence: bulk
List-ID: <linux-mips.vger.kernel.org>
X-Mailing-List: linux-mips@vger.kernel.org
Request memory region in order to display it in /proc/iomem.
Also stop printing the MMIO address since it just displays (ptrval).
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
---
v2: request memory region and stop displaying MMIO address.
drivers/irqchip/irq-bcm6345-l1.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
--- a/drivers/irqchip/irq-bcm6345-l1.c
+++ b/drivers/irqchip/irq-bcm6345-l1.c
@@ -253,6 +253,9 @@ static int __init bcm6345_l1_init_one(st
if (!cpu->map_base)
return -ENOMEM;
+ if (!request_mem_region(res.start, sz, res.name))
+ pr_err("failed to request intc memory");
+
for (i = 0; i < n_words; i++) {
cpu->enable_cache[i] = 0;
__raw_writel(0, cpu->map_base + reg_enable(intc, i));
@@ -331,8 +334,7 @@ static int __init bcm6345_l1_of_init(str
for_each_cpu(idx, &intc->cpumask) {
struct bcm6345_l1_cpu *cpu = intc->cpus[idx];
- pr_info(" CPU%u at MMIO 0x%p (irq = %d)\n", idx,
- cpu->map_base, cpu->parent_irq);
+ pr_info(" CPU%u (irq = %d)\n", idx, cpu->parent_irq);
}
return 0;

View File

@@ -0,0 +1,328 @@
From 39717277d5c87bdb183cf2f258957b44ba99b4df Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Wed, 13 Jul 2022 11:47:35 +0200
Subject: [PATCH] mtd: mtdsplit support
---
drivers/mtd/Kconfig | 19 ++++
drivers/mtd/Makefile | 2 +
drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++-----
include/linux/mtd/mtd.h | 25 +++++
include/linux/mtd/partitions.h | 7 ++
5 files changed, 197 insertions(+), 25 deletions(-)
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -12,6 +12,25 @@ menuconfig MTD
if MTD
+menu "OpenWrt specific MTD options"
+
+config MTD_ROOTFS_ROOT_DEV
+ bool "Automatically set 'rootfs' partition to be root filesystem"
+ default y
+
+config MTD_SPLIT_FIRMWARE
+ bool "Automatically split firmware partition for kernel+rootfs"
+ default y
+
+config MTD_SPLIT_FIRMWARE_NAME
+ string "Firmware partition name"
+ depends on MTD_SPLIT_FIRMWARE
+ default "firmware"
+
+source "drivers/mtd/mtdsplit/Kconfig"
+
+endmenu
+
config MTD_TESTS
tristate "MTD tests support (DANGEROUS)"
depends on m
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconc
obj-y += parsers/
+obj-$(CONFIG_MTD_SPLIT) += mtdsplit/
+
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -15,11 +15,13 @@
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/magic.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include "mtdcore.h"
+#include "mtdsplit/mtdsplit.h"
/*
* MTD methods which simply translate the effective address and pass through
@@ -236,6 +238,147 @@ static int mtd_add_partition_attrs(struc
return ret;
}
+static DEFINE_SPINLOCK(part_parser_lock);
+static LIST_HEAD(part_parsers);
+
+static struct mtd_part_parser *mtd_part_parser_get(const char *name)
+{
+ struct mtd_part_parser *p, *ret = NULL;
+
+ spin_lock(&part_parser_lock);
+
+ list_for_each_entry(p, &part_parsers, list)
+ if (!strcmp(p->name, name) && try_module_get(p->owner)) {
+ ret = p;
+ break;
+ }
+
+ spin_unlock(&part_parser_lock);
+
+ return ret;
+}
+
+static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
+{
+ module_put(p->owner);
+}
+
+static struct mtd_part_parser *
+get_partition_parser_by_type(enum mtd_parser_type type,
+ struct mtd_part_parser *start)
+{
+ struct mtd_part_parser *p, *ret = NULL;
+
+ spin_lock(&part_parser_lock);
+
+ p = list_prepare_entry(start, &part_parsers, list);
+ if (start)
+ mtd_part_parser_put(start);
+
+ list_for_each_entry_continue(p, &part_parsers, list) {
+ if (p->type == type && try_module_get(p->owner)) {
+ ret = p;
+ break;
+ }
+ }
+
+ spin_unlock(&part_parser_lock);
+
+ return ret;
+}
+
+static int parse_mtd_partitions_by_type(struct mtd_info *master,
+ enum mtd_parser_type type,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ struct mtd_part_parser *prev = NULL;
+ int ret = 0;
+
+ while (1) {
+ struct mtd_part_parser *parser;
+
+ parser = get_partition_parser_by_type(type, prev);
+ if (!parser)
+ break;
+
+ ret = (*parser->parse_fn)(master, pparts, data);
+
+ if (ret > 0) {
+ mtd_part_parser_put(parser);
+ printk(KERN_NOTICE
+ "%d %s partitions found on MTD device %s\n",
+ ret, parser->name, master->name);
+ break;
+ }
+
+ prev = parser;
+ }
+
+ return ret;
+}
+
+static int
+run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type)
+{
+ struct mtd_partition *parts;
+ int nr_parts;
+ int i;
+
+ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts,
+ NULL);
+ if (nr_parts <= 0)
+ return nr_parts;
+
+ if (WARN_ON(!parts))
+ return 0;
+
+ for (i = 0; i < nr_parts; i++) {
+ /* adjust partition offsets */
+ parts[i].offset += child->part.offset;
+
+ mtd_add_partition(child->parent,
+ parts[i].name,
+ parts[i].offset,
+ parts[i].size);
+ }
+
+ kfree(parts);
+
+ return nr_parts;
+}
+
+#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME
+#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME
+#else
+#define SPLIT_FIRMWARE_NAME "unused"
+#endif
+
+static void split_firmware(struct mtd_info *master, struct mtd_info *part)
+{
+ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE);
+}
+
+static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part)
+{
+ static int rootfs_found = 0;
+
+ if (rootfs_found)
+ return;
+
+ if (of_find_property(mtd_get_of_node(part), "linux,rootfs", NULL) ||
+ !strcmp(part->name, "rootfs")) {
+ run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS);
+
+ rootfs_found = 1;
+ }
+
+ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) &&
+ !strcmp(part->name, SPLIT_FIRMWARE_NAME) &&
+ !of_find_property(mtd_get_of_node(part), "compatible", NULL))
+ split_firmware(master, part);
+}
+
int mtd_add_partition(struct mtd_info *parent, const char *name,
long long offset, long long length)
{
@@ -274,6 +417,7 @@ int mtd_add_partition(struct mtd_info *p
if (ret)
goto err_remove_part;
+ mtd_partition_split(parent, child);
mtd_add_partition_attrs(child);
return 0;
@@ -422,6 +566,7 @@ int add_mtd_partitions(struct mtd_info *
goto err_del_partitions;
}
+ mtd_partition_split(master, child);
mtd_add_partition_attrs(child);
/* Look for subpartitions */
@@ -438,31 +583,6 @@ err_del_partitions:
return ret;
}
-static DEFINE_SPINLOCK(part_parser_lock);
-static LIST_HEAD(part_parsers);
-
-static struct mtd_part_parser *mtd_part_parser_get(const char *name)
-{
- struct mtd_part_parser *p, *ret = NULL;
-
- spin_lock(&part_parser_lock);
-
- list_for_each_entry(p, &part_parsers, list)
- if (!strcmp(p->name, name) && try_module_get(p->owner)) {
- ret = p;
- break;
- }
-
- spin_unlock(&part_parser_lock);
-
- return ret;
-}
-
-static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
-{
- module_put(p->owner);
-}
-
/*
* Many partition parsers just expected the core to kfree() all their data in
* one chunk. Do that by default.
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -615,6 +615,24 @@ static inline void mtd_align_erase_req(s
req->len += mtd->erasesize - mod;
}
+static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd)
+{
+ if (mtd_mod_by_eb(sz, mtd) == 0)
+ return sz;
+
+ /* Round up to next erase block */
+ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize;
+}
+
+static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd)
+{
+ if (mtd_mod_by_eb(sz, mtd) == 0)
+ return sz;
+
+ /* Round down to the start of the current erase block */
+ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize;
+}
+
static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd)
{
if (mtd->writesize_shift)
@@ -688,6 +706,13 @@ extern struct mtd_info *of_get_mtd_devic
extern struct mtd_info *get_mtd_device_nm(const char *name);
extern void put_mtd_device(struct mtd_info *mtd);
+static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd)
+{
+ if (!mtd_is_partition(mtd))
+ return 0;
+
+ return mtd->part.offset;
+}
struct mtd_notifier {
void (*add)(struct mtd_info *mtd);
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -75,6 +75,12 @@ struct mtd_part_parser_data {
* Functions dealing with the various ways of partitioning the space
*/
+enum mtd_parser_type {
+ MTD_PARSER_TYPE_DEVICE = 0,
+ MTD_PARSER_TYPE_ROOTFS,
+ MTD_PARSER_TYPE_FIRMWARE,
+};
+
struct mtd_part_parser {
struct list_head list;
struct module *owner;
@@ -83,6 +89,7 @@ struct mtd_part_parser {
int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
struct mtd_part_parser_data *);
void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
+ enum mtd_parser_type type;
};
/* Container for passing around a set of parsed partitions */

View File

@@ -0,0 +1,48 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
Date: Tue, 31 Oct 2023 15:51:01 +0100
Subject: [PATCH] mtd: don't register NVMEM devices for partitions with custom
drivers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This fixes issue exposed by upstream commit f4cf4e5db331 ("Revert
"nvmem: add new config option"").
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
---
drivers/mtd/mtdcore.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -519,6 +519,29 @@ static int mtd_nvmem_add(struct mtd_info
struct device_node *node = mtd_get_of_node(mtd);
struct nvmem_config config = {};
+ /*
+ * Do NOT register NVMEM device for any partition that is meant to be
+ * handled by a U-Boot env driver. That would result in associating two
+ * different NVMEM devices with the same OF node.
+ *
+ * An example of unwanted behaviour of above (forwardtrace):
+ * of_get_mac_addr_nvmem()
+ * of_nvmem_cell_get()
+ * __nvmem_device_get()
+ *
+ * We can't have __nvmem_device_get() return "mtdX" NVMEM device instead
+ * of U-Boot env NVMEM device. That would result in failing to find
+ * NVMEM cell.
+ *
+ * This issue seems to affect U-Boot env case only and will go away with
+ * switch to NVMEM layouts.
+ */
+ if (of_device_is_compatible(node, "u-boot,env") ||
+ of_device_is_compatible(node, "u-boot,env-redundant-bool") ||
+ of_device_is_compatible(node, "u-boot,env-redundant-count") ||
+ of_device_is_compatible(node, "brcm,env"))
+ return 0;
+
config.id = -1;
config.dev = &mtd->dev;
config.name = dev_name(&mtd->dev);

View File

@@ -0,0 +1,245 @@
From acacdac272927ae1d96e0bca51eb82899671eaea Mon Sep 17 00:00:00 2001
From: John Thomson <git@johnthomson.fastmail.com.au>
Date: Fri, 25 Dec 2020 18:50:08 +1000
Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Do not prevent writing to mtd partitions where a partition boundary sits
on a minor erasesize boundary.
This addresses a FIXME that has been present since the start of the
linux git history:
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
Allow a uniform erase region spi-nor device to be configured
to use the non-uniform erase regions code path for an erase with:
CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
On supporting hardware (SECT_4K: majority of current SPI-NOR device)
provide the facility for an erase to use the least number
of SPI-NOR operations, as well as access to 4K erase without
requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
Introduce erasesize_minor to the mtd struct,
the smallest erasesize supported by the device
On existing devices, this is useful where write support is wanted
for data on a 4K partition, such as some u-boot-env partitions,
or RouterBoot soft_config, while still netting the performance
benefits of using 64K sectors
Performance:
time mtd erase firmware
OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length
Without this patch
MTD_SPI_NOR_USE_4K_SECTORS=y |n
real 2m 11.66s |0m 50.86s
user 0m 0.00s |0m 0.00s
sys 1m 56.20s |0m 50.80s
With this patch
MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y
real 0m 51.68s |0m 50.85s |2m 12.89s
user 0m 0.00s |0m 0.00s |0m 0.01s
sys 0m 46.94s |0m 50.38s |2m 12.46s
Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au>
Signed-off-by: Thibaut VARÈNE <hacks+kernel@slashdirt.org>
---
checkpatch does not like the printk(KERN_WARNING
these should be changed separately beforehand?
Changes v1 -> v2:
Added mtdcore sysfs for erasesize_minor
Removed finding minor erasesize for variable erase regions device,
as untested and no responses regarding it.
Moved IF_ENABLED for SPINOR variable erase to guard setting
erasesize_minor in spi-nor/core.c
Removed setting erasesize to minor where partition boundaries require
minor erase to be writable
Simplified minor boundary check by relying on minor being a factor of
major
Changes RFC -> v1:
Fix uninitialized variable smatch warning
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
---
drivers/mtd/mtdcore.c | 10 ++++++++++
drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++----------
drivers/mtd/spi-nor/Kconfig | 10 ++++++++++
drivers/mtd/spi-nor/core.c | 11 +++++++++--
include/linux/mtd/mtd.h | 2 ++
5 files changed, 56 insertions(+), 12 deletions(-)
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -169,6 +169,15 @@ static ssize_t mtd_erasesize_show(struct
}
MTD_DEVICE_ATTR_RO(erasesize);
+static ssize_t mtd_erasesize_minor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor);
+}
+MTD_DEVICE_ATTR_RO(erasesize_minor);
+
static ssize_t mtd_writesize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -314,6 +323,7 @@ static struct attribute *mtd_attrs[] = {
&dev_attr_flags.attr,
&dev_attr_size.attr,
&dev_attr_erasesize.attr,
+ &dev_attr_erasesize_minor.attr,
&dev_attr_writesize.attr,
&dev_attr_subpagesize.attr,
&dev_attr_oobsize.attr,
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -41,6 +41,7 @@ static struct mtd_info *allocate_partiti
struct mtd_info *master = mtd_get_master(parent);
int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
master->writesize : master->erasesize;
+ int wr_alignment_minor = 0;
u64 parent_size = mtd_is_partition(parent) ?
parent->part.size : parent->size;
struct mtd_info *child;
@@ -165,6 +166,7 @@ static struct mtd_info *allocate_partiti
} else {
/* Single erase size */
child->erasesize = master->erasesize;
+ child->erasesize_minor = master->erasesize_minor;
}
/*
@@ -172,26 +174,39 @@ static struct mtd_info *allocate_partiti
* exposes several regions with different erasesize. Adjust
* wr_alignment accordingly.
*/
- if (!(child->flags & MTD_NO_ERASE))
+ if (!(child->flags & MTD_NO_ERASE)) {
wr_alignment = child->erasesize;
+ wr_alignment_minor = child->erasesize_minor;
+ }
tmp = mtd_get_master_ofs(child, 0);
remainder = do_div(tmp, wr_alignment);
if ((child->flags & MTD_WRITEABLE) && remainder) {
- /* Doesn't start on a boundary of major erase size */
- /* FIXME: Let it be writable if it is on a boundary of
- * _minor_ erase size though */
- child->flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
- part->name);
+ if (wr_alignment_minor) {
+ /* rely on minor being a factor of major erasesize */
+ tmp = remainder;
+ remainder = do_div(tmp, wr_alignment_minor);
+ }
+ if (remainder) {
+ child->flags &= ~MTD_WRITEABLE;
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+ part->name);
+ }
}
tmp = mtd_get_master_ofs(child, 0) + child->part.size;
remainder = do_div(tmp, wr_alignment);
if ((child->flags & MTD_WRITEABLE) && remainder) {
- child->flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
- part->name);
+ if (wr_alignment_minor) {
+ tmp = remainder;
+ remainder = do_div(tmp, wr_alignment_minor);
+ }
+
+ if (remainder) {
+ child->flags &= ~MTD_WRITEABLE;
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+ part->name);
+ }
}
child->size = child->part.size;
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR
if MTD_SPI_NOR
+config MTD_SPI_NOR_USE_VARIABLE_ERASE
+ bool "Disable uniform_erase to allow use of all hardware supported erasesizes"
+ depends on !MTD_SPI_NOR_USE_4K_SECTORS
+ default n
+ help
+ Allow mixed use of all hardware supported erasesizes,
+ by forcing spi_nor to use the multiple eraseregions code path.
+ For example: A 68K erase will use one 64K erase, and one 4K erase
+ on supporting hardware.
+
config MTD_SPI_NOR_USE_4K_SECTORS
bool "Use small 4096 B erase sectors"
default y
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1050,6 +1050,8 @@ static u8 spi_nor_convert_3to4_erase(u8
static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
+ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE))
+ return false;
return !!nor->params->erase_map.uniform_erase_type;
}
@@ -2180,6 +2182,7 @@ static int spi_nor_select_erase(struct s
{
struct spi_nor_erase_map *map = &nor->params->erase_map;
const struct spi_nor_erase_type *erase = NULL;
+ const struct spi_nor_erase_type *erase_minor = NULL;
struct mtd_info *mtd = &nor->mtd;
u32 wanted_size = nor->info->sector_size;
int i;
@@ -2212,8 +2215,9 @@ static int spi_nor_select_erase(struct s
*/
for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
if (map->erase_type[i].size) {
- erase = &map->erase_type[i];
- break;
+ if (!erase)
+ erase = &map->erase_type[i];
+ erase_minor = &map->erase_type[i];
}
}
@@ -2221,6 +2225,9 @@ static int spi_nor_select_erase(struct s
return -EINVAL;
mtd->erasesize = erase->size;
+ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) &&
+ erase_minor && erase_minor->size < erase->size)
+ mtd->erasesize_minor = erase_minor->size;
return 0;
}
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -245,6 +245,8 @@ struct mtd_info {
* information below if they desire
*/
uint32_t erasesize;
+ /* "Minor" (smallest) erase size supported by the whole device */
+ uint32_t erasesize_minor;
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR

View File

@@ -0,0 +1,41 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: add patch for including unpartitioned space in the rootfs partition for redboot devices (if applicable)
[john@phrozen.org: used by ixp and others]
lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/mtd/redboot.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
--- a/drivers/mtd/parsers/redboot.c
+++ b/drivers/mtd/parsers/redboot.c
@@ -278,14 +278,21 @@ nogood:
#endif
names += strlen(names) + 1;
-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
- i++;
- parts[i].offset = parts[i - 1].size + parts[i - 1].offset;
- parts[i].size = fl->next->img->flash_base - parts[i].offset;
- parts[i].name = nullname;
- }
+ if (!strcmp(parts[i].name, "rootfs")) {
+ parts[i].size = fl->next->img->flash_base;
+ parts[i].size &= ~(master->erasesize - 1);
+ parts[i].size -= parts[i].offset;
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+ nrparts--;
+ } else {
+ i++;
+ parts[i].offset = parts[i-1].size + parts[i-1].offset;
+ parts[i].size = fl->next->img->flash_base - parts[i].offset;
+ parts[i].name = nullname;
#endif
+ }
+ }
tmp_fl = fl;
fl = fl->next;
kfree(tmp_fl);

View File

@@ -0,0 +1,229 @@
From: Florian Fainelli <f.fainelli@gmail.com>
Subject: Add myloader partition table parser
[john@phozen.org: shoud be upstreamable]
lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
[adjust for kernel 5.4, add myloader.c to patch]
Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
--- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig
@@ -67,6 +67,22 @@ config MTD_CMDLINE_PARTS
If unsure, say 'N'.
+config MTD_MYLOADER_PARTS
+ tristate "MyLoader partition parsing"
+ depends on ADM5120 || ATH79
+ help
+ MyLoader is a bootloader which allows the user to define partitions
+ in flash devices, by putting a table in the second erase block
+ on the device, similar to a partition table. This table gives the
+ offsets and lengths of the user defined partitions.
+
+ If you need code which can detect and parse these tables, and
+ register MTD 'partitions' corresponding to each image detected,
+ enable this option.
+
+ You will still need the parsing functions to be called by the driver
+ for your particular device. It won't happen automatically.
+
config MTD_OF_PARTS
tristate "OpenFirmware (device tree) partitioning parser"
default y
--- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm4
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
obj-$(CONFIG_MTD_BRCM_U_BOOT) += brcm_u-boot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
+obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o
obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
ofpart-y += ofpart_core.o
ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o
--- /dev/null
+++ b/drivers/mtd/parsers/myloader.c
@@ -0,0 +1,181 @@
+/*
+ * Parse MyLoader-style flash partition tables and produce a Linux partition
+ * array to match.
+ *
+ * Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This file was based on drivers/mtd/redboot.c
+ * Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+#include <linux/myloader.h>
+
+#define BLOCK_LEN_MIN 0x10000
+#define PART_NAME_LEN 32
+
+struct part_data {
+ struct mylo_partition_table tab;
+ char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
+};
+
+static int myloader_parse_partitions(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+{
+ struct part_data *buf;
+ struct mylo_partition_table *tab;
+ struct mylo_partition *part;
+ struct mtd_partition *mtd_parts;
+ struct mtd_partition *mtd_part;
+ int num_parts;
+ int ret, i;
+ size_t retlen;
+ char *names;
+ unsigned long offset;
+ unsigned long blocklen;
+
+ buf = vmalloc(sizeof(*buf));
+ if (!buf) {
+ return -ENOMEM;
+ goto out;
+ }
+ tab = &buf->tab;
+
+ blocklen = master->erasesize;
+ if (blocklen < BLOCK_LEN_MIN)
+ blocklen = BLOCK_LEN_MIN;
+
+ offset = blocklen;
+
+ /* Find the partition table */
+ for (i = 0; i < 4; i++, offset += blocklen) {
+ printk(KERN_DEBUG "%s: searching for MyLoader partition table"
+ " at offset 0x%lx\n", master->name, offset);
+
+ ret = mtd_read(master, offset, sizeof(*buf), &retlen,
+ (void *)buf);
+ if (ret)
+ goto out_free_buf;
+
+ if (retlen != sizeof(*buf)) {
+ ret = -EIO;
+ goto out_free_buf;
+ }
+
+ /* Check for Partition Table magic number */
+ if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
+ break;
+
+ }
+
+ if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
+ printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
+ master->name);
+ ret = 0;
+ goto out_free_buf;
+ }
+
+ /* The MyLoader and the Partition Table is always present */
+ num_parts = 2;
+
+ /* Detect number of used partitions */
+ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+ part = &tab->partitions[i];
+
+ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+ continue;
+
+ num_parts++;
+ }
+
+ mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
+ num_parts * PART_NAME_LEN), GFP_KERNEL);
+
+ if (!mtd_parts) {
+ ret = -ENOMEM;
+ goto out_free_buf;
+ }
+
+ mtd_part = mtd_parts;
+ names = (char *)&mtd_parts[num_parts];
+
+ strncpy(names, "myloader", PART_NAME_LEN);
+ mtd_part->name = names;
+ mtd_part->offset = 0;
+ mtd_part->size = offset;
+ mtd_part->mask_flags = MTD_WRITEABLE;
+ mtd_part++;
+ names += PART_NAME_LEN;
+
+ strncpy(names, "partition_table", PART_NAME_LEN);
+ mtd_part->name = names;
+ mtd_part->offset = offset;
+ mtd_part->size = blocklen;
+ mtd_part->mask_flags = MTD_WRITEABLE;
+ mtd_part++;
+ names += PART_NAME_LEN;
+
+ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+ part = &tab->partitions[i];
+
+ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+ continue;
+
+ if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
+ strncpy(names, buf->names[i], PART_NAME_LEN);
+ else
+ snprintf(names, PART_NAME_LEN, "partition%d", i);
+
+ mtd_part->offset = le32_to_cpu(part->addr);
+ mtd_part->size = le32_to_cpu(part->size);
+ mtd_part->name = names;
+ mtd_part++;
+ names += PART_NAME_LEN;
+ }
+
+ *pparts = mtd_parts;
+ ret = num_parts;
+
+ out_free_buf:
+ vfree(buf);
+ out:
+ return ret;
+}
+
+static struct mtd_part_parser myloader_mtd_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = myloader_parse_partitions,
+ .name = "MyLoader",
+};
+
+static int __init myloader_mtd_parser_init(void)
+{
+ register_mtd_parser(&myloader_mtd_parser);
+
+ return 0;
+}
+
+static void __exit myloader_mtd_parser_exit(void)
+{
+ deregister_mtd_parser(&myloader_mtd_parser);
+}
+
+module_init(myloader_mtd_parser_init);
+module_exit(myloader_mtd_parser_exit);
+
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
+MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,68 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Subject: [PATCH] mtd: bcm47xxpart: check for bad blocks when calculating offsets
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
--- a/drivers/mtd/parsers/parser_trx.c
+++ b/drivers/mtd/parsers/parser_trx.c
@@ -25,6 +25,33 @@ struct trx_header {
uint32_t offset[3];
} __packed;
+/*
+ * Calculate real end offset (address) for a given amount of data. It checks
+ * all blocks skipping bad ones.
+ */
+static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes)
+{
+ size_t real_offset = 0;
+
+ if (mtd_block_isbad(mtd, real_offset))
+ pr_warn("Base offset shouldn't be at bad block");
+
+ while (bytes >= mtd->erasesize) {
+ bytes -= mtd->erasesize;
+ real_offset += mtd->erasesize;
+ while (mtd_block_isbad(mtd, real_offset)) {
+ real_offset += mtd->erasesize;
+
+ if (real_offset >= mtd->size)
+ return real_offset - mtd->erasesize;
+ }
+ }
+
+ real_offset += bytes;
+
+ return real_offset;
+}
+
static const char *parser_trx_data_part_name(struct mtd_info *master,
size_t offset)
{
@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_i
if (trx.offset[2]) {
part = &parts[curr_part++];
part->name = "loader";
- part->offset = trx.offset[i];
+ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
i++;
}
if (trx.offset[i]) {
part = &parts[curr_part++];
part->name = "linux";
- part->offset = trx.offset[i];
+ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
i++;
}
if (trx.offset[i]) {
part = &parts[curr_part++];
- part->name = parser_trx_data_part_name(mtd, trx.offset[i]);
- part->offset = trx.offset[i];
+ part->offset = parser_trx_real_offset(mtd, trx.offset[i]);
+ part->name = parser_trx_data_part_name(mtd, part->offset);
i++;
}

View File

@@ -0,0 +1,37 @@
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Subject: mtd: bcm47xxpart: detect T_Meter partition
It can be found on many Netgear devices. It consists of many 0x30 blocks
starting with 4D 54.
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
drivers/mtd/bcm47xxpart.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/drivers/mtd/parsers/bcm47xxpart.c
+++ b/drivers/mtd/parsers/bcm47xxpart.c
@@ -35,6 +35,7 @@
#define NVRAM_HEADER 0x48534C46 /* FLSH */
#define POT_MAGIC1 0x54544f50 /* POTT */
#define POT_MAGIC2 0x504f /* OP */
+#define T_METER_MAGIC 0x4D540000 /* MT */
#define ML_MAGIC1 0x39685a42
#define ML_MAGIC2 0x26594131
#define TRX_MAGIC 0x30524448
@@ -178,6 +179,15 @@ static int bcm47xxpart_parse(struct mtd_
MTD_WRITEABLE);
continue;
}
+
+ /* T_Meter */
+ if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
+ (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC &&
+ (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) {
+ bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset,
+ MTD_WRITEABLE);
+ continue;
+ }
/* TRX */
if (buf[0x000 / 4] == TRX_MAGIC) {

View File

@@ -0,0 +1,38 @@
From 4437e01fb6bca63fccdba5d6c44888b0935885c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
Date: Tue, 24 Mar 2020 11:45:07 +0100
Subject: [PATCH] generic: routerboot partition build bits (5.4)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch adds routerbootpart kernel build bits
Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
---
drivers/mtd/parsers/Kconfig | 9 +++++++++
drivers/mtd/parsers/Makefile | 1 +
2 files changed, 10 insertions(+)
--- a/drivers/mtd/parsers/Kconfig
+++ b/drivers/mtd/parsers/Kconfig
@@ -236,3 +236,12 @@ config MTD_SERCOMM_PARTS
partition map. This partition table contains real partition
offsets, which may differ from device to device depending on the
number and location of bad blocks on NAND.
+
+config MTD_ROUTERBOOT_PARTS
+ tristate "RouterBoot flash partition parser"
+ depends on MTD && OF
+ help
+ MikroTik RouterBoot is implemented as a multi segment system on the
+ flash, some of which are fixed and some of which are located at
+ variable offsets. This parser handles both cases via properly
+ formatted DTS.
--- a/drivers/mtd/parsers/Makefile
+++ b/drivers/mtd/parsers/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpa
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o
+obj-$(CONFIG_MTD_ROUTERBOOT_PARTS) += routerbootpart.o

View File

@@ -0,0 +1,121 @@
From ffbbe7d66872ff8957dad2136133e28a1fd5d437 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 7 Aug 2023 22:51:05 +0100
Subject: [PATCH 01/15] dt-bindings: mtd: add basic bindings for UBI
Add basic bindings for UBI devices and volumes.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
.../bindings/mtd/partitions/linux,ubi.yaml | 65 +++++++++++++++++++
.../bindings/mtd/partitions/ubi-volume.yaml | 35 ++++++++++
2 files changed, 100 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
create mode 100644 Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/partitions/linux,ubi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Unsorted Block Images
+
+description: |
+ UBI ("Unsorted Block Images") is a volume management system for raw
+ flash devices which manages multiple logical volumes on a single
+ physical flash device and spreads the I/O load (i.e wear-leveling)
+ across the whole flash chip.
+
+maintainers:
+ - Daniel Golle <daniel@makrotopia.org>
+
+allOf:
+ - $ref: partition.yaml#
+
+properties:
+ compatible:
+ const: linux,ubi
+
+ volumes:
+ type: object
+ description: UBI Volumes
+
+ patternProperties:
+ "^ubi-volume-.*$":
+ $ref: /schemas/mtd/partitions/ubi-volume.yaml#
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ reg = <0x0 0x100000>;
+ label = "bootloader";
+ read-only;
+ };
+
+ partition@100000 {
+ reg = <0x100000 0x1ff00000>;
+ label = "ubi";
+ compatible = "linux,ubi";
+
+ volumes {
+ ubi-volume-caldata {
+ volid = <2>;
+ volname = "rf";
+ };
+ };
+ };
+ };
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/partitions/ubi-volume.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: UBI volume
+
+description: |
+ This binding describes a single UBI volume. Volumes can be matches either
+ by their ID or their name, or both.
+
+maintainers:
+ - Daniel Golle <daniel@makrotopia.org>
+
+properties:
+ volid:
+ $ref: "/schemas/types.yaml#/definitions/uint32"
+ description:
+ Match UBI volume ID
+
+ volname:
+ $ref: "/schemas/types.yaml#/definitions/string"
+ description:
+ Match UBI volume ID
+
+anyOf:
+ - required:
+ - volid
+
+ - required:
+ - volname
+
+# This is a generic file other binding inherit from and extend
+additionalProperties: true

View File

@@ -0,0 +1,48 @@
From e4dad3aa5c3ab9c553555dd23c0b85f725f2eb51 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 7 Aug 2023 22:53:01 +0100
Subject: [PATCH 02/15] dt-bindings: mtd: ubi-volume: allow UBI volumes to
provide NVMEM
UBI volumes may be used to contain NVMEM bits, typically device MAC
addresses or wireless radio calibration data.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
.../devicetree/bindings/mtd/partitions/linux,ubi.yaml | 10 ++++++++++
.../devicetree/bindings/mtd/partitions/ubi-volume.yaml | 5 +++++
2 files changed, 15 insertions(+)
--- a/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
+++ b/Documentation/devicetree/bindings/mtd/partitions/linux,ubi.yaml
@@ -59,6 +59,16 @@ examples:
ubi-volume-caldata {
volid = <2>;
volname = "rf";
+
+ nvmem-layout {
+ compatible = "fixed-layout";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ eeprom@0 {
+ reg = <0x0 0x1000>;
+ };
+ };
};
};
};
--- a/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
+++ b/Documentation/devicetree/bindings/mtd/partitions/ubi-volume.yaml
@@ -24,6 +24,11 @@ properties:
description:
Match UBI volume ID
+ nvmem-layout:
+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
+ description:
+ This container may reference an NVMEM layout parser.
+
anyOf:
- required:
- volid

View File

@@ -0,0 +1,225 @@
From e5cf19bd8204925f3bd2067df9e867313eac388b Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 1 May 2023 11:57:51 +0100
Subject: [PATCH 03/15] mtd: ubi: block: use notifier to create ubiblock from
parameter
Use UBI_VOLUME_ADDED notification to create ubiblock device specified
on kernel cmdline or module parameter.
This makes thing more simple and has the advantage that ubiblock devices
on volumes which are not present at the time the ubi module is probed
will still be created.
Suggested-by: Zhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/block.c | 154 ++++++++++++++++++++++------------------
1 file changed, 85 insertions(+), 69 deletions(-)
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -33,6 +33,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/mtd/ubi.h>
#include <linux/workqueue.h>
@@ -67,10 +68,10 @@ struct ubiblock_pdu {
};
/* Numbers of elements set in the @ubiblock_param array */
-static int ubiblock_devs __initdata;
+static int ubiblock_devs;
/* MTD devices specification parameters */
-static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES] __initdata;
+static struct ubiblock_param ubiblock_param[UBIBLOCK_MAX_DEVICES];
struct ubiblock {
struct ubi_volume_desc *desc;
@@ -504,7 +505,7 @@ int ubiblock_remove(struct ubi_volume_in
}
/* Found a device, let's lock it so we can check if it's busy */
- mutex_lock(&dev->dev_mutex);
+ mutex_lock_nested(&dev->dev_mutex, SINGLE_DEPTH_NESTING);
if (dev->refcnt > 0) {
ret = -EBUSY;
goto out_unlock_dev;
@@ -567,6 +568,85 @@ static int ubiblock_resize(struct ubi_vo
return 0;
}
+static bool
+match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id)
+{
+ int err, len;
+ struct path path;
+ struct kstat stat;
+
+ if (ubi_num == -1) {
+ /* No ubi num, name must be a vol device path */
+ err = kern_path(name, LOOKUP_FOLLOW, &path);
+ if (err)
+ return false;
+
+ err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
+ path_put(&path);
+ if (err)
+ return false;
+
+ if (!S_ISCHR(stat.mode))
+ return false;
+
+ if (vi->ubi_num != ubi_major2num(MAJOR(stat.rdev)))
+ return false;
+
+ if (vi->vol_id != MINOR(stat.rdev) - 1)
+ return false;
+
+ return true;
+ }
+
+ if (vol_id == -1) {
+ if (vi->ubi_num != ubi_num)
+ return false;
+
+ len = strnlen(name, UBI_VOL_NAME_MAX + 1);
+ if (len < 1 || vi->name_len != len)
+ return false;
+
+ if (strcmp(name, vi->name))
+ return false;
+
+ return true;
+ }
+
+ if (vi->ubi_num != ubi_num)
+ return false;
+
+ if (vi->vol_id != vol_id)
+ return false;
+
+ return true;
+}
+
+static void
+ubiblock_create_from_param(struct ubi_volume_info *vi)
+{
+ int i, ret = 0;
+ struct ubiblock_param *p;
+
+ /*
+ * Iterate over ubiblock cmdline parameters. If a parameter matches the
+ * newly added volume create the ubiblock device for it.
+ */
+ for (i = 0; i < ubiblock_devs; i++) {
+ p = &ubiblock_param[i];
+
+ if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
+ continue;
+
+ ret = ubiblock_create(vi);
+ if (ret) {
+ pr_err(
+ "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
+ vi->name, p->ubi_num, p->vol_id, ret);
+ }
+ break;
+ }
+}
+
static int ubiblock_notify(struct notifier_block *nb,
unsigned long notification_type, void *ns_ptr)
{
@@ -574,10 +654,7 @@ static int ubiblock_notify(struct notifi
switch (notification_type) {
case UBI_VOLUME_ADDED:
- /*
- * We want to enforce explicit block device creation for
- * volumes, so when a volume is added we do nothing.
- */
+ ubiblock_create_from_param(&nt->vi);
break;
case UBI_VOLUME_REMOVED:
ubiblock_remove(&nt->vi);
@@ -603,56 +680,6 @@ static struct notifier_block ubiblock_no
.notifier_call = ubiblock_notify,
};
-static struct ubi_volume_desc * __init
-open_volume_desc(const char *name, int ubi_num, int vol_id)
-{
- if (ubi_num == -1)
- /* No ubi num, name must be a vol device path */
- return ubi_open_volume_path(name, UBI_READONLY);
- else if (vol_id == -1)
- /* No vol_id, must be vol_name */
- return ubi_open_volume_nm(ubi_num, name, UBI_READONLY);
- else
- return ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
-}
-
-static void __init ubiblock_create_from_param(void)
-{
- int i, ret = 0;
- struct ubiblock_param *p;
- struct ubi_volume_desc *desc;
- struct ubi_volume_info vi;
-
- /*
- * If there is an error creating one of the ubiblocks, continue on to
- * create the following ubiblocks. This helps in a circumstance where
- * the kernel command-line specifies multiple block devices and some
- * may be broken, but we still want the working ones to come up.
- */
- for (i = 0; i < ubiblock_devs; i++) {
- p = &ubiblock_param[i];
-
- desc = open_volume_desc(p->name, p->ubi_num, p->vol_id);
- if (IS_ERR(desc)) {
- pr_err(
- "UBI: block: can't open volume on ubi%d_%d, err=%ld\n",
- p->ubi_num, p->vol_id, PTR_ERR(desc));
- continue;
- }
-
- ubi_get_volume_info(desc, &vi);
- ubi_close_volume(desc);
-
- ret = ubiblock_create(&vi);
- if (ret) {
- pr_err(
- "UBI: block: can't add '%s' volume on ubi%d_%d, err=%d\n",
- vi.name, p->ubi_num, p->vol_id, ret);
- continue;
- }
- }
-}
-
static void ubiblock_remove_all(void)
{
struct ubiblock *next;
@@ -678,18 +705,7 @@ int __init ubiblock_init(void)
if (ubiblock_major < 0)
return ubiblock_major;
- /*
- * Attach block devices from 'block=' module param.
- * Even if one block device in the param list fails to come up,
- * still allow the module to load and leave any others up.
- */
- ubiblock_create_from_param();
-
- /*
- * Block devices are only created upon user requests, so we ignore
- * existing volumes.
- */
- ret = ubi_register_volume_notifier(&ubiblock_notifier, 1);
+ ret = ubi_register_volume_notifier(&ubiblock_notifier, 0);
if (ret)
goto err_unreg;
return 0;

View File

@@ -0,0 +1,264 @@
From 471a17d8d1b838092d1a76e48cdce8b5b67ff809 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 27 Nov 2023 01:54:28 +0000
Subject: [PATCH 04/15] mtd: ubi: attach from device tree
Introduce device tree compatible 'linux,ubi' and attach compatible MTD
devices using the MTD add notifier. This is needed for a UBI device to
be available early at boot (and not only after late_initcall), so
volumes on them can be used eg. as NVMEM providers for other drivers.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/build.c | 146 ++++++++++++++++++++++++++++------------
drivers/mtd/ubi/cdev.c | 2 +-
drivers/mtd/ubi/ubi.h | 2 +-
3 files changed, 106 insertions(+), 44 deletions(-)
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -27,6 +27,7 @@
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/kernel.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <linux/major.h>
#include "ubi.h"
@@ -1071,6 +1072,7 @@ out_free:
* ubi_detach_mtd_dev - detach an MTD device.
* @ubi_num: UBI device number to detach from
* @anyway: detach MTD even if device reference count is not zero
+ * @have_lock: called by MTD notifier holding mtd_table_mutex
*
* This function destroys an UBI device number @ubi_num and detaches the
* underlying MTD device. Returns zero in case of success and %-EBUSY if the
@@ -1080,7 +1082,7 @@ out_free:
* Note, the invocations of this function has to be serialized by the
* @ubi_devices_mutex.
*/
-int ubi_detach_mtd_dev(int ubi_num, int anyway)
+int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock)
{
struct ubi_device *ubi;
@@ -1136,7 +1138,11 @@ int ubi_detach_mtd_dev(int ubi_num, int
vfree(ubi->peb_buf);
vfree(ubi->fm_buf);
ubi_msg(ubi, "mtd%d is detached", ubi->mtd->index);
- put_mtd_device(ubi->mtd);
+ if (have_lock)
+ __put_mtd_device(ubi->mtd);
+ else
+ put_mtd_device(ubi->mtd);
+
put_device(&ubi->dev);
return 0;
}
@@ -1213,43 +1219,43 @@ static struct mtd_info * __init open_mtd
return mtd;
}
-static int __init ubi_init(void)
+static void ubi_notify_add(struct mtd_info *mtd)
{
- int err, i, k;
+ struct device_node *np = mtd_get_of_node(mtd);
+ int err;
- /* Ensure that EC and VID headers have correct size */
- BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
- BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
+ if (!of_device_is_compatible(np, "linux,ubi"))
+ return;
- if (mtd_devs > UBI_MAX_DEVICES) {
- pr_err("UBI error: too many MTD devices, maximum is %d\n",
- UBI_MAX_DEVICES);
- return -EINVAL;
- }
+ /*
+ * we are already holding &mtd_table_mutex, but still need
+ * to bump refcount
+ */
+ err = __get_mtd_device(mtd);
+ if (err)
+ return;
- /* Create base sysfs directory and sysfs files */
- err = class_register(&ubi_class);
+ /* called while holding mtd_table_mutex */
+ mutex_lock_nested(&ubi_devices_mutex, SINGLE_DEPTH_NESTING);
+ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false);
+ mutex_unlock(&ubi_devices_mutex);
if (err < 0)
- return err;
-
- err = misc_register(&ubi_ctrl_cdev);
- if (err) {
- pr_err("UBI error: cannot register device\n");
- goto out;
- }
+ __put_mtd_device(mtd);
+}
- ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
- sizeof(struct ubi_wl_entry),
- 0, 0, NULL);
- if (!ubi_wl_entry_slab) {
- err = -ENOMEM;
- goto out_dev_unreg;
- }
+static void ubi_notify_remove(struct mtd_info *mtd)
+{
+ WARN(1, "mtd%d removed despite UBI still being attached", mtd->index);
+}
- err = ubi_debugfs_init();
- if (err)
- goto out_slab;
+static struct mtd_notifier ubi_mtd_notifier = {
+ .add = ubi_notify_add,
+ .remove = ubi_notify_remove,
+};
+static int __init ubi_init_attach(void)
+{
+ int err, i, k;
/* Attach MTD devices */
for (i = 0; i < mtd_devs; i++) {
@@ -1297,25 +1303,79 @@ static int __init ubi_init(void)
}
}
+ return 0;
+
+out_detach:
+ for (k = 0; k < i; k++)
+ if (ubi_devices[k]) {
+ mutex_lock(&ubi_devices_mutex);
+ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1, false);
+ mutex_unlock(&ubi_devices_mutex);
+ }
+ return err;
+}
+#ifndef CONFIG_MTD_UBI_MODULE
+late_initcall(ubi_init_attach);
+#endif
+
+static int __init ubi_init(void)
+{
+ int err;
+
+ /* Ensure that EC and VID headers have correct size */
+ BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
+ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
+
+ if (mtd_devs > UBI_MAX_DEVICES) {
+ pr_err("UBI error: too many MTD devices, maximum is %d\n",
+ UBI_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+ /* Create base sysfs directory and sysfs files */
+ err = class_register(&ubi_class);
+ if (err < 0)
+ return err;
+
+ err = misc_register(&ubi_ctrl_cdev);
+ if (err) {
+ pr_err("UBI error: cannot register device\n");
+ goto out;
+ }
+
+ ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
+ sizeof(struct ubi_wl_entry),
+ 0, 0, NULL);
+ if (!ubi_wl_entry_slab) {
+ err = -ENOMEM;
+ goto out_dev_unreg;
+ }
+
+ err = ubi_debugfs_init();
+ if (err)
+ goto out_slab;
+
err = ubiblock_init();
if (err) {
pr_err("UBI error: block: cannot initialize, error %d\n", err);
/* See comment above re-ubi_is_module(). */
if (ubi_is_module())
- goto out_detach;
+ goto out_slab;
+ }
+
+ register_mtd_user(&ubi_mtd_notifier);
+
+ if (ubi_is_module()) {
+ err = ubi_init_attach();
+ if (err)
+ goto out_mtd_notifier;
}
return 0;
-out_detach:
- for (k = 0; k < i; k++)
- if (ubi_devices[k]) {
- mutex_lock(&ubi_devices_mutex);
- ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
- mutex_unlock(&ubi_devices_mutex);
- }
- ubi_debugfs_exit();
+out_mtd_notifier:
+ unregister_mtd_user(&ubi_mtd_notifier);
out_slab:
kmem_cache_destroy(ubi_wl_entry_slab);
out_dev_unreg:
@@ -1325,18 +1385,20 @@ out:
pr_err("UBI error: cannot initialize UBI, error %d\n", err);
return err;
}
-late_initcall(ubi_init);
+device_initcall(ubi_init);
+
static void __exit ubi_exit(void)
{
int i;
ubiblock_exit();
+ unregister_mtd_user(&ubi_mtd_notifier);
for (i = 0; i < UBI_MAX_DEVICES; i++)
if (ubi_devices[i]) {
mutex_lock(&ubi_devices_mutex);
- ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1);
+ ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1, false);
mutex_unlock(&ubi_devices_mutex);
}
ubi_debugfs_exit();
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -1065,7 +1065,7 @@ static long ctrl_cdev_ioctl(struct file
}
mutex_lock(&ubi_devices_mutex);
- err = ubi_detach_mtd_dev(ubi_num, 0);
+ err = ubi_detach_mtd_dev(ubi_num, 0, false);
mutex_unlock(&ubi_devices_mutex);
break;
}
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -939,7 +939,7 @@ int ubi_io_write_vid_hdr(struct ubi_devi
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
int vid_hdr_offset, int max_beb_per1024,
bool disable_fm);
-int ubi_detach_mtd_dev(int ubi_num, int anyway);
+int ubi_detach_mtd_dev(int ubi_num, int anyway, bool have_lock);
struct ubi_device *ubi_get_device(int ubi_num);
void ubi_put_device(struct ubi_device *ubi);
struct ubi_device *ubi_get_by_major(int major);

View File

@@ -0,0 +1,226 @@
From 2d664266cfdd114cc7a1fa28dd64275e99222455 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 8 Jun 2023 17:18:09 +0100
Subject: [PATCH 05/15] mtd: ubi: introduce pre-removal notification for UBI
volumes
Introduce a new notification type UBI_VOLUME_SHUTDOWN to inform users
that a volume is just about to be removed.
This is needed because users (such as the NVMEM subsystem) expect that
at the time their removal function is called, the parenting device is
still available (for removal of sysfs nodes, for example, in case of
NVMEM which otherwise WARNs on volume removal).
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/block.c | 26 ++++++++++++++++++++++++++
drivers/mtd/ubi/build.c | 20 +++++++++++++++-----
drivers/mtd/ubi/kapi.c | 2 +-
drivers/mtd/ubi/ubi.h | 2 ++
drivers/mtd/ubi/vmt.c | 17 +++++++++++++++--
include/linux/mtd/ubi.h | 2 ++
6 files changed, 61 insertions(+), 8 deletions(-)
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -568,6 +568,29 @@ static int ubiblock_resize(struct ubi_vo
return 0;
}
+static int ubiblock_shutdown(struct ubi_volume_info *vi)
+{
+ struct ubiblock *dev;
+ struct gendisk *disk;
+ int ret = 0;
+
+ mutex_lock(&devices_mutex);
+ dev = find_dev_nolock(vi->ubi_num, vi->vol_id);
+ if (!dev) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+ disk = dev->gd;
+
+out_unlock:
+ mutex_unlock(&devices_mutex);
+
+ if (!ret)
+ blk_mark_disk_dead(disk);
+
+ return ret;
+};
+
static bool
match_volume_desc(struct ubi_volume_info *vi, const char *name, int ubi_num, int vol_id)
{
@@ -659,6 +682,9 @@ static int ubiblock_notify(struct notifi
case UBI_VOLUME_REMOVED:
ubiblock_remove(&nt->vi);
break;
+ case UBI_VOLUME_SHUTDOWN:
+ ubiblock_shutdown(&nt->vi);
+ break;
case UBI_VOLUME_RESIZED:
ubiblock_resize(&nt->vi);
break;
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -89,7 +89,7 @@ static struct ubi_device *ubi_devices[UB
/* Serializes UBI devices creations and removals */
DEFINE_MUTEX(ubi_devices_mutex);
-/* Protects @ubi_devices and @ubi->ref_count */
+/* Protects @ubi_devices, @ubi->ref_count and @ubi->is_dead */
static DEFINE_SPINLOCK(ubi_devices_lock);
/* "Show" method for files in '/<sysfs>/class/ubi/' */
@@ -258,6 +258,9 @@ struct ubi_device *ubi_get_device(int ub
spin_lock(&ubi_devices_lock);
ubi = ubi_devices[ubi_num];
+ if (ubi && ubi->is_dead)
+ ubi = NULL;
+
if (ubi) {
ubi_assert(ubi->ref_count >= 0);
ubi->ref_count += 1;
@@ -295,7 +298,7 @@ struct ubi_device *ubi_get_by_major(int
spin_lock(&ubi_devices_lock);
for (i = 0; i < UBI_MAX_DEVICES; i++) {
ubi = ubi_devices[i];
- if (ubi && MAJOR(ubi->cdev.dev) == major) {
+ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
ubi_assert(ubi->ref_count >= 0);
ubi->ref_count += 1;
get_device(&ubi->dev);
@@ -324,7 +327,7 @@ int ubi_major2num(int major)
for (i = 0; i < UBI_MAX_DEVICES; i++) {
struct ubi_device *ubi = ubi_devices[i];
- if (ubi && MAJOR(ubi->cdev.dev) == major) {
+ if (ubi && !ubi->is_dead && MAJOR(ubi->cdev.dev) == major) {
ubi_num = ubi->ubi_num;
break;
}
@@ -511,7 +514,7 @@ static void ubi_free_volumes_from(struct
int i;
for (i = from; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
- if (!ubi->volumes[i])
+ if (!ubi->volumes[i] || ubi->volumes[i]->is_dead)
continue;
ubi_eba_replace_table(ubi->volumes[i], NULL);
ubi_fastmap_destroy_checkmap(ubi->volumes[i]);
@@ -1094,10 +1097,10 @@ int ubi_detach_mtd_dev(int ubi_num, int
return -EINVAL;
spin_lock(&ubi_devices_lock);
- put_device(&ubi->dev);
ubi->ref_count -= 1;
if (ubi->ref_count) {
if (!anyway) {
+ ubi->ref_count += 1;
spin_unlock(&ubi_devices_lock);
return -EBUSY;
}
@@ -1105,6 +1108,13 @@ int ubi_detach_mtd_dev(int ubi_num, int
ubi_err(ubi, "%s reference count %d, destroy anyway",
ubi->ubi_name, ubi->ref_count);
}
+ ubi->is_dead = true;
+ spin_unlock(&ubi_devices_lock);
+
+ ubi_notify_all(ubi, UBI_VOLUME_SHUTDOWN, NULL);
+
+ spin_lock(&ubi_devices_lock);
+ put_device(&ubi->dev);
ubi_devices[ubi_num] = NULL;
spin_unlock(&ubi_devices_lock);
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -152,7 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(
spin_lock(&ubi->volumes_lock);
vol = ubi->volumes[vol_id];
- if (!vol)
+ if (!vol || vol->is_dead)
goto out_unlock;
err = -EBUSY;
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -345,6 +345,7 @@ struct ubi_volume {
int writers;
int exclusive;
int metaonly;
+ bool is_dead;
int reserved_pebs;
int vol_type;
@@ -564,6 +565,7 @@ struct ubi_device {
spinlock_t volumes_lock;
int ref_count;
int image_seq;
+ bool is_dead;
int rsvd_pebs;
int avail_pebs;
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -59,7 +59,7 @@ static ssize_t vol_attribute_show(struct
struct ubi_device *ubi = vol->ubi;
spin_lock(&ubi->volumes_lock);
- if (!ubi->volumes[vol->vol_id]) {
+ if (!ubi->volumes[vol->vol_id] || ubi->volumes[vol->vol_id]->is_dead) {
spin_unlock(&ubi->volumes_lock);
return -ENODEV;
}
@@ -189,7 +189,7 @@ int ubi_create_volume(struct ubi_device
/* Ensure that the name is unique */
for (i = 0; i < ubi->vtbl_slots; i++)
- if (ubi->volumes[i] &&
+ if (ubi->volumes[i] && !ubi->volumes[i]->is_dead &&
ubi->volumes[i]->name_len == req->name_len &&
!strcmp(ubi->volumes[i]->name, req->name)) {
ubi_err(ubi, "volume \"%s\" exists (ID %d)",
@@ -352,6 +352,19 @@ int ubi_remove_volume(struct ubi_volume_
err = -EBUSY;
goto out_unlock;
}
+
+ /*
+ * Mark volume as dead at this point to prevent that anyone
+ * can take a reference to the volume from now on.
+ * This is necessary as we have to release the spinlock before
+ * calling ubi_volume_notify.
+ */
+ vol->is_dead = true;
+ spin_unlock(&ubi->volumes_lock);
+
+ ubi_volume_notify(ubi, vol, UBI_VOLUME_SHUTDOWN);
+
+ spin_lock(&ubi->volumes_lock);
ubi->volumes[vol_id] = NULL;
spin_unlock(&ubi->volumes_lock);
--- a/include/linux/mtd/ubi.h
+++ b/include/linux/mtd/ubi.h
@@ -192,6 +192,7 @@ struct ubi_device_info {
* or a volume was removed)
* @UBI_VOLUME_RESIZED: a volume has been re-sized
* @UBI_VOLUME_RENAMED: a volume has been re-named
+ * @UBI_VOLUME_SHUTDOWN: a volume is going to removed, shutdown users
* @UBI_VOLUME_UPDATED: data has been written to a volume
*
* These constants define which type of event has happened when a volume
@@ -202,6 +203,7 @@ enum {
UBI_VOLUME_REMOVED,
UBI_VOLUME_RESIZED,
UBI_VOLUME_RENAMED,
+ UBI_VOLUME_SHUTDOWN,
UBI_VOLUME_UPDATED,
};

View File

@@ -0,0 +1,65 @@
From 3a041ee543cdf2e707a1dd72946cd6a583509b28 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Fri, 21 Jul 2023 19:26:37 +0100
Subject: [PATCH 06/15] mtd: ubi: populate ubi volume fwnode
Look for the 'volumes' subnode of an MTD partition attached to a UBI
device and attach matching child nodes to UBI volumes.
This allows UBI volumes to be referenced in device tree, e.g. for use
as NVMEM providers.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/vmt.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -124,6 +124,31 @@ static void vol_release(struct device *d
kfree(vol);
}
+static struct fwnode_handle *find_volume_fwnode(struct ubi_volume *vol)
+{
+ struct fwnode_handle *fw_vols, *fw_vol;
+ const char *volname;
+ u32 volid;
+
+ fw_vols = device_get_named_child_node(vol->dev.parent->parent, "volumes");
+ if (!fw_vols)
+ return NULL;
+
+ fwnode_for_each_child_node(fw_vols, fw_vol) {
+ if (!fwnode_property_read_string(fw_vol, "volname", &volname) &&
+ strncmp(volname, vol->name, vol->name_len))
+ continue;
+
+ if (!fwnode_property_read_u32(fw_vol, "volid", &volid) &&
+ vol->vol_id != volid)
+ continue;
+
+ return fw_vol;
+ }
+
+ return NULL;
+}
+
/**
* ubi_create_volume - create volume.
* @ubi: UBI device description object
@@ -223,6 +248,7 @@ int ubi_create_volume(struct ubi_device
vol->name_len = req->name_len;
memcpy(vol->name, req->name, vol->name_len);
vol->ubi = ubi;
+ device_set_node(&vol->dev, find_volume_fwnode(vol));
/*
* Finish all pending erases because there may be some LEBs belonging
@@ -605,6 +631,7 @@ int ubi_add_volume(struct ubi_device *ub
vol->dev.class = &ubi_class;
vol->dev.groups = volume_dev_groups;
dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id);
+ device_set_node(&vol->dev, find_volume_fwnode(vol));
err = device_register(&vol->dev);
if (err) {
cdev_del(&vol->cdev);

View File

@@ -0,0 +1,246 @@
From 7eb6666348f3f2d1f7308c712fa5903cbe189401 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 8 Jun 2023 17:22:04 +0100
Subject: [PATCH 07/15] mtd: ubi: provide NVMEM layer over UBI volumes
In an ideal world we would like UBI to be used where ever possible on a
NAND chip. And with UBI support in ARM Trusted Firmware and U-Boot it
is possible to achieve an (almost-)all-UBI flash layout. Hence the need
for a way to also use UBI volumes to store board-level constants, such
as MAC addresses and calibration data of wireless interfaces.
Add UBI volume NVMEM driver module exposing UBI volumes as NVMEM
providers. Allow UBI devices to have a "volumes" firmware subnode with
volumes which may be compatible with "nvmem-cells".
Access to UBI volumes via the NVMEM interface at this point is
read-only, and it is slow, opening and closing the UBI volume for each
access due to limitations of the NVMEM provider API.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/Kconfig | 12 +++
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/nvmem.c | 188 +++++++++++++++++++++++++++++++++++++++
3 files changed, 201 insertions(+)
create mode 100644 drivers/mtd/ubi/nvmem.c
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -104,4 +104,16 @@ config MTD_UBI_BLOCK
If in doubt, say "N".
+config MTD_UBI_NVMEM
+ tristate "UBI virtual NVMEM"
+ default n
+ depends on NVMEM
+ help
+ This option enabled an additional driver exposing UBI volumes as NVMEM
+ providers, intended for platforms where UBI is part of the firmware
+ specification and used to store also e.g. MAC addresses or board-
+ specific Wi-Fi calibration data.
+
+ If in doubt, say "N".
+
endif # MTD_UBI
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -7,3 +7,4 @@ ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap
ubi-$(CONFIG_MTD_UBI_BLOCK) += block.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_NVMEM) += nvmem.o
--- /dev/null
+++ b/drivers/mtd/ubi/nvmem.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org>
+ */
+
+/* UBI NVMEM provider */
+#include "ubi.h"
+#include <linux/nvmem-provider.h>
+#include <asm/div64.h>
+
+/* List of all NVMEM devices */
+static LIST_HEAD(nvmem_devices);
+static DEFINE_MUTEX(devices_mutex);
+
+struct ubi_nvmem {
+ struct nvmem_device *nvmem;
+ int ubi_num;
+ int vol_id;
+ int usable_leb_size;
+ struct list_head list;
+};
+
+static int ubi_nvmem_reg_read(void *priv, unsigned int from,
+ void *val, size_t bytes)
+{
+ uint32_t offs, to_read, bytes_left;
+ struct ubi_nvmem *unv = priv;
+ struct ubi_volume_desc *desc;
+ uint64_t lnum = from;
+ int err = 0;
+
+ desc = ubi_open_volume(unv->ubi_num, unv->vol_id, UBI_READONLY);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ bytes_left = bytes;
+ offs = do_div(lnum, unv->usable_leb_size);
+ while (bytes_left) {
+ to_read = unv->usable_leb_size - offs;
+
+ if (to_read > bytes_left)
+ to_read = bytes_left;
+
+ err = ubi_read(desc, lnum, val, offs, to_read);
+ if (err)
+ break;
+
+ lnum += 1;
+ offs = 0;
+ bytes_left -= to_read;
+ val += to_read;
+ }
+ ubi_close_volume(desc);
+
+ if (err)
+ return err;
+
+ return bytes_left == 0 ? 0 : -EIO;
+}
+
+static int ubi_nvmem_add(struct ubi_volume_info *vi)
+{
+ struct device_node *np = dev_of_node(vi->dev);
+ struct nvmem_config config = {};
+ struct ubi_nvmem *unv;
+ int ret;
+
+ if (!np)
+ return 0;
+
+ if (!of_get_child_by_name(np, "nvmem-layout"))
+ return 0;
+
+ if (WARN_ON_ONCE(vi->usable_leb_size <= 0) ||
+ WARN_ON_ONCE(vi->size <= 0))
+ return -EINVAL;
+
+ unv = kzalloc(sizeof(struct ubi_nvmem), GFP_KERNEL);
+ if (!unv)
+ return -ENOMEM;
+
+ config.id = NVMEM_DEVID_NONE;
+ config.dev = vi->dev;
+ config.name = dev_name(vi->dev);
+ config.owner = THIS_MODULE;
+ config.priv = unv;
+ config.reg_read = ubi_nvmem_reg_read;
+ config.size = vi->usable_leb_size * vi->size;
+ config.word_size = 1;
+ config.stride = 1;
+ config.read_only = true;
+ config.root_only = true;
+ config.ignore_wp = true;
+ config.of_node = np;
+
+ unv->ubi_num = vi->ubi_num;
+ unv->vol_id = vi->vol_id;
+ unv->usable_leb_size = vi->usable_leb_size;
+ unv->nvmem = nvmem_register(&config);
+ if (IS_ERR(unv->nvmem)) {
+ ret = dev_err_probe(vi->dev, PTR_ERR(unv->nvmem),
+ "Failed to register NVMEM device\n");
+ kfree(unv);
+ return ret;
+ }
+
+ mutex_lock(&devices_mutex);
+ list_add_tail(&unv->list, &nvmem_devices);
+ mutex_unlock(&devices_mutex);
+
+ return 0;
+}
+
+static void ubi_nvmem_remove(struct ubi_volume_info *vi)
+{
+ struct ubi_nvmem *unv_c, *unv = NULL;
+
+ mutex_lock(&devices_mutex);
+ list_for_each_entry(unv_c, &nvmem_devices, list)
+ if (unv_c->ubi_num == vi->ubi_num && unv_c->vol_id == vi->vol_id) {
+ unv = unv_c;
+ break;
+ }
+
+ if (!unv) {
+ mutex_unlock(&devices_mutex);
+ return;
+ }
+
+ list_del(&unv->list);
+ mutex_unlock(&devices_mutex);
+ nvmem_unregister(unv->nvmem);
+ kfree(unv);
+}
+
+/**
+ * nvmem_notify - UBI notification handler.
+ * @nb: registered notifier block
+ * @l: notification type
+ * @ns_ptr: pointer to the &struct ubi_notification object
+ */
+static int nvmem_notify(struct notifier_block *nb, unsigned long l,
+ void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (l) {
+ case UBI_VOLUME_RESIZED:
+ ubi_nvmem_remove(&nt->vi);
+ fallthrough;
+ case UBI_VOLUME_ADDED:
+ ubi_nvmem_add(&nt->vi);
+ break;
+ case UBI_VOLUME_SHUTDOWN:
+ ubi_nvmem_remove(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block nvmem_notifier = {
+ .notifier_call = nvmem_notify,
+};
+
+static int __init ubi_nvmem_init(void)
+{
+ return ubi_register_volume_notifier(&nvmem_notifier, 0);
+}
+
+static void __exit ubi_nvmem_exit(void)
+{
+ struct ubi_nvmem *unv, *tmp;
+
+ mutex_lock(&devices_mutex);
+ list_for_each_entry_safe(unv, tmp, &nvmem_devices, list) {
+ nvmem_unregister(unv->nvmem);
+ list_del(&unv->list);
+ kfree(unv);
+ }
+ mutex_unlock(&devices_mutex);
+
+ ubi_unregister_volume_notifier(&nvmem_notifier);
+}
+
+module_init(ubi_nvmem_init);
+module_exit(ubi_nvmem_exit);
+MODULE_DESCRIPTION("NVMEM layer over UBI volumes");
+MODULE_AUTHOR("Daniel Golle");
+MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,120 @@
From 9ffc1d7d73609a89eb264d6066340f8b7b3b0ebe Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 7 Aug 2023 21:19:45 +0100
Subject: [PATCH 08/15] dt-bindings: block: add basic bindings for block
devices
Add bindings for block devices which are used to allow referencing
nvmem bits on them.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
.../bindings/block/block-device.yaml | 22 ++++++++
.../devicetree/bindings/block/partition.yaml | 50 +++++++++++++++++++
.../devicetree/bindings/block/partitions.yaml | 20 ++++++++
3 files changed, 92 insertions(+)
create mode 100644 Documentation/devicetree/bindings/block/block-device.yaml
create mode 100644 Documentation/devicetree/bindings/block/partition.yaml
create mode 100644 Documentation/devicetree/bindings/block/partitions.yaml
--- /dev/null
+++ b/Documentation/devicetree/bindings/block/block-device.yaml
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/block/block-device.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: block storage device
+
+description: |
+ This binding is generic and describes a block-oriented storage device.
+
+maintainers:
+ - Daniel Golle <daniel@makrotopia.org>
+
+properties:
+ partitions:
+ $ref: /schemas/block/partitions.yaml
+
+ nvmem-layout:
+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
+
+unevaluatedProperties: false
--- /dev/null
+++ b/Documentation/devicetree/bindings/block/partition.yaml
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/block/partition.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Partition on a block device
+
+description: |
+ This binding describes a partition on a block device.
+ Partitions may be matched by a combination of partition number, name,
+ and UUID.
+
+maintainers:
+ - Daniel Golle <daniel@makrotopia.org>
+
+properties:
+ $nodename:
+ pattern: '^block-partition-.+$'
+
+ partnum:
+ description:
+ Matches partition by number if present.
+
+ partname:
+ "$ref": "/schemas/types.yaml#/definitions/string"
+ description:
+ Matches partition by PARTNAME if present.
+
+ uuid:
+ "$ref": "/schemas/types.yaml#/definitions/string"
+ description:
+ Matches partition by PARTUUID if present.
+
+ nvmem-layout:
+ $ref: /schemas/nvmem/layouts/nvmem-layout.yaml#
+ description:
+ This container may reference an NVMEM layout parser.
+
+anyOf:
+ - required:
+ - partnum
+
+ - required:
+ - partname
+
+ - required:
+ - uuid
+
+unevaluatedProperties: false
--- /dev/null
+++ b/Documentation/devicetree/bindings/block/partitions.yaml
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/block/partitions.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Partitions on block devices
+
+description: |
+ This binding is generic and describes the content of the partitions container
+ node.
+
+maintainers:
+ - Daniel Golle <daniel@makrotopia.org>
+
+patternProperties:
+ "^block-partition-.+$":
+ $ref: partition.yaml
+
+unevaluatedProperties: false

View File

@@ -0,0 +1,77 @@
From 614f4f6fdda09e30ecf7ef6c8091579db15018cb Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Fri, 21 Jul 2023 17:51:03 +0100
Subject: [PATCH 09/15] block: partitions: populate fwnode
Let block partitions to be represented by a firmware node and hence
allow them to being referenced e.g. for use with blk-nvmem.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
block/partitions/core.c | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
--- a/block/partitions/core.c
+++ b/block/partitions/core.c
@@ -10,6 +10,8 @@
#include <linux/ctype.h>
#include <linux/vmalloc.h>
#include <linux/raid/detect.h>
+#include <linux/property.h>
+
#include "check.h"
static int (*check_part[])(struct parsed_partitions *) = {
@@ -298,6 +300,43 @@ static ssize_t whole_disk_show(struct de
}
static DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
+static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev)
+{
+ struct fwnode_handle *fw_parts, *fw_part;
+ struct device *ddev = disk_to_dev(bdev->bd_disk);
+ const char *partname, *uuid;
+ u32 partno;
+
+ fw_parts = device_get_named_child_node(ddev, "partitions");
+ if (!fw_parts)
+ fw_parts = device_get_named_child_node(ddev->parent, "partitions");
+
+ if (!fw_parts)
+ return NULL;
+
+ fwnode_for_each_child_node(fw_parts, fw_part) {
+ if (!fwnode_property_read_string(fw_part, "uuid", &uuid) &&
+ (!bdev->bd_meta_info || strncmp(uuid,
+ bdev->bd_meta_info->uuid,
+ PARTITION_META_INFO_UUIDLTH)))
+ continue;
+
+ if (!fwnode_property_read_string(fw_part, "partname", &partname) &&
+ (!bdev->bd_meta_info || strncmp(partname,
+ bdev->bd_meta_info->volname,
+ PARTITION_META_INFO_VOLNAMELTH)))
+ continue;
+
+ if (!fwnode_property_read_u32(fw_part, "partno", &partno) &&
+ bdev->bd_partno != partno)
+ continue;
+
+ return fw_part;
+ }
+
+ return NULL;
+}
+
/*
* Must be called either with open_mutex held, before a disk can be opened or
* after all disk users are gone.
@@ -380,6 +419,8 @@ static struct block_device *add_partitio
goto out_put;
}
+ device_set_node(pdev, find_partition_fwnode(bdev));
+
/* delay uevent until 'holders' subdir is created */
dev_set_uevent_suppress(pdev, 1);
err = device_add(pdev);

View File

@@ -0,0 +1,29 @@
From 65f3ff9672ccd5ee78937047e7a2fc696eee1c8f Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 13 Jul 2023 04:07:16 +0100
Subject: [PATCH 10/15] block: add new genhd flag GENHD_FL_NVMEM
Add new flag to destinguish block devices which may act as an NVMEM
provider.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
include/linux/blkdev.h | 2 ++
1 file changed, 2 insertions(+)
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -87,11 +87,13 @@ struct partition_meta_info {
* ``GENHD_FL_NO_PART``: partition support is disabled. The kernel will not
* scan for partitions from add_disk, and users can't add partitions manually.
*
+ * ``GENHD_FL_NVMEM``: the block device should be considered as NVMEM provider.
*/
enum {
GENHD_FL_REMOVABLE = 1 << 0,
GENHD_FL_HIDDEN = 1 << 1,
GENHD_FL_NO_PART = 1 << 2,
+ GENHD_FL_NVMEM = 1 << 3,
};
enum {

View File

@@ -0,0 +1,235 @@
From b9936aa8a3775c2027f655d91a206d0e6e1c7ec0 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 11 Jul 2023 00:17:31 +0100
Subject: [PATCH 11/15] block: implement NVMEM provider
On embedded devices using an eMMC it is common that one or more partitions
on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
data. Allow referencing the partition in device tree for the kernel and
Wi-Fi drivers accessing it via the NVMEM layer.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
block/Kconfig | 9 +++
block/Makefile | 1 +
block/blk-nvmem.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 196 insertions(+)
create mode 100644 block/blk-nvmem.c
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -203,6 +203,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
by falling back to the kernel crypto API when inline
encryption hardware is not present.
+config BLK_NVMEM
+ bool "Block device NVMEM provider"
+ depends on OF
+ depends on NVMEM
+ help
+ Allow block devices (or partitions) to act as NVMEM prodivers,
+ typically used with eMMC to store MAC addresses or Wi-Fi
+ calibration data on embedded devices.
+
source "block/partitions/Kconfig"
config BLOCK_COMPAT
--- a/block/Makefile
+++ b/block/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_ZONED) += blk-zoned
obj-$(CONFIG_BLK_WBT) += blk-wbt.o
obj-$(CONFIG_BLK_DEBUG_FS) += blk-mq-debugfs.o
obj-$(CONFIG_BLK_DEBUG_FS_ZONED)+= blk-mq-debugfs-zoned.o
+obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
obj-$(CONFIG_BLK_SED_OPAL) += sed-opal.o
obj-$(CONFIG_BLK_PM) += blk-pm.o
obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
--- /dev/null
+++ b/block/blk-nvmem.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * block device NVMEM provider
+ *
+ * Copyright (c) 2023 Daniel Golle <daniel@makrotopia.org>
+ *
+ * Useful on devices using a partition on an eMMC for MAC addresses or
+ * Wi-Fi calibration EEPROM data.
+ */
+
+#include "blk.h"
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <linux/pagemap.h>
+#include <linux/property.h>
+
+/* List of all NVMEM devices */
+static LIST_HEAD(nvmem_devices);
+static DEFINE_MUTEX(devices_mutex);
+
+struct blk_nvmem {
+ struct nvmem_device *nvmem;
+ struct block_device *bdev;
+ struct list_head list;
+};
+
+static int blk_nvmem_reg_read(void *priv, unsigned int from,
+ void *val, size_t bytes)
+{
+ unsigned long offs = from & ~PAGE_MASK, to_read;
+ pgoff_t f_index = from >> PAGE_SHIFT;
+ struct address_space *mapping;
+ struct blk_nvmem *bnv = priv;
+ size_t bytes_left = bytes;
+ struct folio *folio;
+ void *p;
+ int ret;
+
+ if (!bnv->bdev)
+ return -ENODEV;
+
+ if (!bnv->bdev->bd_disk)
+ return -EINVAL;
+
+ if (!bnv->bdev->bd_disk->fops)
+ return -EIO;
+
+ if (!bnv->bdev->bd_disk->fops->open)
+ return -EIO;
+
+ ret = bnv->bdev->bd_disk->fops->open(bnv->bdev, FMODE_READ);
+ if (ret)
+ return ret;
+
+ mapping = bnv->bdev->bd_inode->i_mapping;
+
+ while (bytes_left) {
+ folio = read_mapping_folio(mapping, f_index++, NULL);
+ if (IS_ERR(folio)) {
+ ret = PTR_ERR(folio);
+ goto err_release_bdev;
+ }
+ to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs);
+ p = folio_address(folio) + offset_in_folio(folio, offs);
+ memcpy(val, p, to_read);
+ offs = 0;
+ bytes_left -= to_read;
+ val += to_read;
+ folio_put(folio);
+ }
+
+err_release_bdev:
+ bnv->bdev->bd_disk->fops->release(bnv->bdev->bd_disk, FMODE_READ);
+
+ return ret;
+}
+
+static int blk_nvmem_register(struct device *dev, struct class_interface *iface)
+{
+ struct device_node *np = dev_of_node(dev);
+ struct block_device *bdev = dev_to_bdev(dev);
+ struct nvmem_config config = {};
+ struct blk_nvmem *bnv;
+
+ /* skip devices which do not have a device tree node */
+ if (!np)
+ return 0;
+
+ /* skip devices without an nvmem layout defined */
+ if (!of_get_child_by_name(np, "nvmem-layout"))
+ return 0;
+
+ /*
+ * skip devices which don't have GENHD_FL_NVMEM set
+ *
+ * This flag is used for mtdblock and ubiblock devices because
+ * both, MTD and UBI already implement their own NVMEM provider.
+ * To avoid registering multiple NVMEM providers for the same
+ * device node, don't register the block NVMEM provider for them.
+ */
+ if (!(bdev->bd_disk->flags & GENHD_FL_NVMEM))
+ return 0;
+
+ /*
+ * skip block device too large to be represented as NVMEM devices
+ * which are using an 'int' as address
+ */
+ if (bdev_nr_bytes(bdev) > INT_MAX)
+ return -EFBIG;
+
+ bnv = kzalloc(sizeof(struct blk_nvmem), GFP_KERNEL);
+ if (!bnv)
+ return -ENOMEM;
+
+ config.id = NVMEM_DEVID_NONE;
+ config.dev = &bdev->bd_device;
+ config.name = dev_name(&bdev->bd_device);
+ config.owner = THIS_MODULE;
+ config.priv = bnv;
+ config.reg_read = blk_nvmem_reg_read;
+ config.size = bdev_nr_bytes(bdev);
+ config.word_size = 1;
+ config.stride = 1;
+ config.read_only = true;
+ config.root_only = true;
+ config.ignore_wp = true;
+ config.of_node = to_of_node(dev->fwnode);
+
+ bnv->bdev = bdev;
+ bnv->nvmem = nvmem_register(&config);
+ if (IS_ERR(bnv->nvmem)) {
+ dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem),
+ "Failed to register NVMEM device\n");
+
+ kfree(bnv);
+ return PTR_ERR(bnv->nvmem);
+ }
+
+ mutex_lock(&devices_mutex);
+ list_add_tail(&bnv->list, &nvmem_devices);
+ mutex_unlock(&devices_mutex);
+
+ return 0;
+}
+
+static void blk_nvmem_unregister(struct device *dev, struct class_interface *iface)
+{
+ struct block_device *bdev = dev_to_bdev(dev);
+ struct blk_nvmem *bnv_c, *bnv = NULL;
+
+ mutex_lock(&devices_mutex);
+ list_for_each_entry(bnv_c, &nvmem_devices, list) {
+ if (bnv_c->bdev == bdev) {
+ bnv = bnv_c;
+ break;
+ }
+ }
+
+ if (!bnv) {
+ mutex_unlock(&devices_mutex);
+ return;
+ }
+
+ list_del(&bnv->list);
+ mutex_unlock(&devices_mutex);
+ nvmem_unregister(bnv->nvmem);
+ kfree(bnv);
+}
+
+static struct class_interface blk_nvmem_bus_interface __refdata = {
+ .class = &block_class,
+ .add_dev = &blk_nvmem_register,
+ .remove_dev = &blk_nvmem_unregister,
+};
+
+static int __init blk_nvmem_init(void)
+{
+ int ret;
+
+ ret = class_interface_register(&blk_nvmem_bus_interface);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+device_initcall(blk_nvmem_init);

View File

@@ -0,0 +1,74 @@
From 86864bf8f40e84dc881c197ef470a88668329dbf Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 7 Aug 2023 21:21:45 +0100
Subject: [PATCH 12/15] dt-bindings: mmc: mmc-card: add block device nodes
Add nodes representing the block devices exposed by an MMC device
including an example involving nvmem-cells.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
.../devicetree/bindings/mmc/mmc-card.yaml | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
--- a/Documentation/devicetree/bindings/mmc/mmc-card.yaml
+++ b/Documentation/devicetree/bindings/mmc/mmc-card.yaml
@@ -26,6 +26,18 @@ properties:
Use this to indicate that the mmc-card has a broken hpi
implementation, and that hpi should not be used.
+ block:
+ $ref: /schemas/block/block-device.yaml#
+ description:
+ Represents the block storage provided by an SD card or the
+ main hardware partition of an eMMC.
+
+patternProperties:
+ '^boot[0-9]+':
+ $ref: /schemas/block/block-device.yaml#
+ description:
+ Represents a boot hardware partition on an eMMC.
+
required:
- compatible
- reg
@@ -42,6 +54,39 @@ examples:
compatible = "mmc-card";
reg = <0>;
broken-hpi;
+
+ block {
+ partitions {
+ cal_data: block-partition-rf {
+ partnum = <3>;
+ partname = "rf";
+
+ nvmem-layout {
+ compatible = "fixed-layout";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ eeprom@0 {
+ reg = <0x0 0x1000>;
+ };
+ };
+ };
+ };
+ };
+
+ boot1 {
+ nvmem-layout {
+ compatible = "fixed-layout";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ macaddr: macaddr@a {
+ compatible = "mac-base";
+ reg = <0xa 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+ };
+ };
};
};

View File

@@ -0,0 +1,23 @@
From 644942a31719de674e2aa68f83d66bd8ae7e4fb7 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 13 Jul 2023 04:12:21 +0100
Subject: [PATCH 13/15] mmc: core: set card fwnode_handle
Set fwnode in case it isn't set yet and of_node is present.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mmc/core/bus.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -363,6 +363,8 @@ int mmc_add_card(struct mmc_card *card)
mmc_add_card_debugfs(card);
#endif
card->dev.of_node = mmc_of_find_child_device(card->host, 0);
+ if (card->dev.of_node && !card->dev.fwnode)
+ card->dev.fwnode = &card->dev.of_node->fwnode;
device_enable_async_suspend(&card->dev);

View File

@@ -0,0 +1,39 @@
From d9143f86330dd038fc48878558dd287ceee5d3d4 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 13 Jul 2023 04:13:04 +0100
Subject: [PATCH 14/15] mmc: block: set fwnode of disk devices
Set fwnode of disk devices to 'block', 'boot0' and 'boot1' subnodes of
the mmc-card. This is done in preparation for having the eMMC act as
NVMEM provider.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mmc/core/block.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2486,6 +2486,8 @@ static struct mmc_blk_data *mmc_blk_allo
int area_type,
unsigned int part_type)
{
+ struct fwnode_handle *fwnode;
+ struct device *ddev;
struct mmc_blk_data *md;
int devidx, ret;
char cap_str[10];
@@ -2582,6 +2584,13 @@ static struct mmc_blk_data *mmc_blk_allo
blk_queue_write_cache(md->queue.queue, cache_enabled, fua_enabled);
+ ddev = disk_to_dev(md->disk);
+ fwnode = device_get_named_child_node(subname ? md->parent->parent :
+ md->parent,
+ subname ? subname : "block");
+ if (fwnode)
+ device_set_node(ddev, fwnode);
+
string_get_size((u64)size, 512, STRING_UNITS_2,
cap_str, sizeof(cap_str));
pr_info("%s: %s %s %s %s\n",

View File

@@ -0,0 +1,22 @@
From 322035ab2b0113d98b6c0ea788d971e0df2952a4 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 20 Jul 2023 17:36:44 +0100
Subject: [PATCH 15/15] mmc: block: set GENHD_FL_NVMEM
Set flag to consider MMC block devices as NVMEM providers.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mmc/core/block.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -2540,6 +2540,7 @@ static struct mmc_blk_data *mmc_blk_allo
md->disk->major = MMC_BLOCK_MAJOR;
md->disk->minors = perdev_minors;
md->disk->first_minor = devidx * perdev_minors;
+ md->disk->flags = GENHD_FL_NVMEM;
md->disk->fops = &mmc_bdops;
md->disk->private_data = md;
md->parent = parent;

View File

@@ -0,0 +1,25 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: kernel: disable cfi cmdset 0002 erase suspend
on some platforms, erase suspend leads to data corruption and lockups when write
ops collide with erase ops. this has been observed on the buffalo wzr-hp-g300nh.
rather than play whack-a-mole with a hard to reproduce issue on a variety of devices,
simply disable erase suspend, as it will usually not produce any useful gain on
the small filesystems used on embedded hardware.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/mtd/chips/cfi_cmdset_0002.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -908,7 +908,7 @@ static int get_chip(struct map_info *map
return 0;
case FL_ERASING:
- if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
+ if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
!(mode == FL_READY || mode == FL_POINT ||
(mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
goto sleep;

View File

@@ -0,0 +1,17 @@
From: George Kashperko <george@znau.edu.ua>
Subject: Issue map read after Write Buffer Load command to ensure chip is ready to receive data.
Signed-off-by: George Kashperko <george@znau.edu.ua>
---
drivers/mtd/chips/cfi_cmdset_0002.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -2052,6 +2052,7 @@ static int __xipram do_write_buffer(stru
/* Write Buffer Load */
map_write(map, CMD(0x25), cmd_adr);
+ (void) map_read(map, cmd_adr);
chip->state = FL_WRITING_TO_BUFFER;

View File

@@ -0,0 +1,18 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: Disable software protection bits for Macronix flashes.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/mtd/spi-nor/spi-nor.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -106,6 +106,7 @@ static void macronix_nor_default_init(st
{
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
+ nor->flags |= SNOR_F_HAS_LOCK;
}
static const struct spi_nor_fixups macronix_nor_fixups = {

View File

@@ -0,0 +1,19 @@
From: Piotr Dymacz <pepe2k@gmail.com>
Subject: kernel/mtd: add support for EON EN25Q128
Signed-off-by: Piotr Dymacz <pepe2k@gmail.com>
---
drivers/mtd/spi-nor/spi-nor.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/spi-nor/eon.c
+++ b/drivers/mtd/spi-nor/eon.c
@@ -17,6 +17,8 @@ static const struct flash_info eon_nor_p
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128) },
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128)
NO_SFDP_FLAGS(SECT_4K) },
+ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256)
+ NO_SFDP_FLAGS(SECT_4K) },
{ "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
{ "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32)

View File

@@ -0,0 +1,21 @@
From: Christian Marangi <ansuelsmth@gmail.com>
Subject: kernel/mtd: add support for EON EN25QX128A
Add support for EON EN25QX128A with no flags as it does
support SFDP parsing.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/mtd/spi-nor/spi-nor.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/spi-nor/eon.c
+++ b/drivers/mtd/spi-nor/eon.c
@@ -19,6 +19,7 @@ static const struct flash_info eon_nor_p
NO_SFDP_FLAGS(SECT_4K) },
{ "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K) },
+ { "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256) },
{ "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
{ "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32)

View File

@@ -0,0 +1,81 @@
From patchwork Thu Feb 6 17:19:41 2020
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
X-Patchwork-Id: 1234465
Date: Thu, 6 Feb 2020 19:19:41 +0200
From: Daniel Golle <daniel@makrotopia.org>
To: linux-mtd@lists.infradead.org
Subject: [PATCH v2] mtd: spi-nor: Add support for xt25f128b chip
Message-ID: <20200206171941.GA2398@makrotopia.org>
MIME-Version: 1.0
Content-Disposition: inline
List-Subscribe: <http://lists.infradead.org/mailman/listinfo/linux-mtd>,
<mailto:linux-mtd-request@lists.infradead.org?subject=subscribe>
Cc: Eitan Cohen <eitan@neot-semadar.com>, Piotr Dymacz <pepe2k@gmail.com>,
Tudor Ambarus <tudor.ambarus@microchip.com>
Sender: "linux-mtd" <linux-mtd-bounces@lists.infradead.org>
Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org
Add XT25F128B made by XTX Technology (Shenzhen) Limited.
This chip supports dual and quad read and uniform 4K-byte erase.
Verified on Teltonika RUT955 which comes with XT25F128B in recent
versions of the device.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/mtd/spi-nor/spi-nor.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -17,6 +17,7 @@ spi-nor-objs += sst.o
spi-nor-objs += winbond.o
spi-nor-objs += xilinx.o
spi-nor-objs += xmc.o
+spi-nor-objs += xtx.o
spi-nor-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
--- /dev/null
+++ b/drivers/mtd/spi-nor/xtx.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/mtd/spi-nor.h>
+
+#include "core.h"
+
+static const struct flash_info xtx_parts[] = {
+ /* XTX Technology (Shenzhen) Limited */
+ { "xt25f128b", INFO(0x0B4018, 0, 64 * 1024, 256)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
+};
+
+const struct spi_nor_manufacturer spi_nor_xtx = {
+ .name = "xtx",
+ .parts = xtx_parts,
+ .nparts = ARRAY_SIZE(xtx_parts),
+};
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1636,6 +1636,7 @@ static const struct spi_nor_manufacturer
&spi_nor_winbond,
&spi_nor_xilinx,
&spi_nor_xmc,
+ &spi_nor_xtx,
};
static const struct flash_info spi_nor_generic_flash = {
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -633,6 +633,7 @@ extern const struct spi_nor_manufacturer
extern const struct spi_nor_manufacturer spi_nor_winbond;
extern const struct spi_nor_manufacturer spi_nor_xilinx;
extern const struct spi_nor_manufacturer spi_nor_xmc;
+extern const struct spi_nor_manufacturer spi_nor_xtx;
extern const struct attribute_group *spi_nor_sysfs_groups[];

View File

@@ -0,0 +1,23 @@
From d68b4aa22e8c625685bfad642dd7337948dc0ad1 Mon Sep 17 00:00:00 2001
From: Koen Vandeputte <koen.vandeputte@ncentric.com>
Date: Mon, 6 Jan 2020 13:07:56 +0100
Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05
Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
---
drivers/mtd/spi-nor/spi-nor.c | 5 +++++
1 file changed, 5 insertions(+)
--- a/drivers/mtd/spi-nor/gigadevice.c
+++ b/drivers/mtd/spi-nor/gigadevice.c
@@ -34,6 +34,10 @@ static const struct spi_nor_fixups gd25q
};
static const struct flash_info gigadevice_nor_parts[] = {
+ { "gd25q05", INFO(0xc84010, 0, 64 * 1024, 1)
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
{ "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |

View File

@@ -0,0 +1,23 @@
From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Thu, 14 Jul 2022 08:38:07 +0200
Subject: [PATCH] spi-nor/gigadevic: add gd25q512
---
drivers/mtd/spi-nor/gigadevice.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/mtd/spi-nor/gigadevice.c
+++ b/drivers/mtd/spi-nor/gigadevice.c
@@ -71,6 +71,11 @@ static const struct flash_info gigadevic
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6)
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
.fixups = &gd25q256_fixups },
+ { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024)
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
};
const struct spi_nor_manufacturer spi_nor_gigadevice = {

View File

@@ -0,0 +1,24 @@
From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Wed, 13 Jul 2022 12:17:21 +0200
Subject: [PATCH] spi-nor/esmt.c: add esmt f25l16pa
This fixes support for Dongwon T&I DW02-412H which uses F25L16PA(2S)
flash.
---
drivers/mtd/spi-nor/esmt.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/mtd/spi-nor/esmt.c
+++ b/drivers/mtd/spi-nor/esmt.c
@@ -10,6 +10,9 @@
static const struct flash_info esmt_nor_parts[] = {
/* ESMT */
+ { "f25l16pa-2s", INFO(0x8c2115, 0, 64 * 1024, 32)
+ FLAGS(SPI_NOR_HAS_LOCK)
+ NO_SFDP_FLAGS(SECT_4K) },
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K) },

View File

@@ -0,0 +1,25 @@
From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Wed, 13 Jul 2022 12:19:01 +0200
Subject: [PATCH] spi-nor/xmc.c: add xm25qh128c
The XMC XM25QH128C is a 16MB SPI NOR chip. The patch is verified on
Ruijie RG-EW3200GX PRO.
Datasheet available at https://www.xmcwh.com/uploads/435/XM25QH128C.pdf
---
drivers/mtd/spi-nor/xmc.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/mtd/spi-nor/xmc.c
+++ b/drivers/mtd/spi-nor/xmc.c
@@ -16,6 +16,9 @@ static const struct flash_info xmc_nor_p
{ "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_QUAD_READ) },
+ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
};
const struct spi_nor_manufacturer spi_nor_xmc = {

View File

@@ -0,0 +1,143 @@
From a43b844cb40bf1b783055fdc81b7f991e21e7e76 Mon Sep 17 00:00:00 2001
From: Chuanhong Guo <gch981213@gmail.com>
Date: Wed, 13 Apr 2022 11:58:17 +0800
Subject: [PATCH] mtd: spinand: add support for ESMT F50x1G41LB
This patch adds support for ESMT F50L1G41LB and F50D1G41LB.
It seems that ESMT likes to use random JEDEC ID from other vendors.
Their 1G chips uses 0xc8 from GigaDevice and 2G/4G chips uses 0x2c from
Micron. For this reason, the ESMT entry is named esmt_c8 with explicit
JEDEC ID in variable name.
Datasheets:
https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50L1G41LB(2M).pdf
https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50D1G41LB(2M).pdf
Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
---
drivers/mtd/nand/spi/Makefile | 2 +-
drivers/mtd/nand/spi/core.c | 1 +
drivers/mtd/nand/spi/esmt.c | 89 +++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 1 +
4 files changed, 92 insertions(+), 1 deletion(-)
create mode 100644 drivers/mtd/nand/spi/esmt.c
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
-spinand-objs := core.o ato.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
+spinand-objs := core.o ato.o esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -938,6 +938,7 @@ static const struct nand_ops spinand_ops
static const struct spinand_manufacturer *spinand_manufacturers[] = {
&ato_spinand_manufacturer,
+ &esmt_c8_spinand_manufacturer,
&gigadevice_spinand_manufacturer,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
--- /dev/null
+++ b/drivers/mtd/nand/spi/esmt.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author:
+ * Chuanhong Guo <gch981213@gmail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
+#define SPINAND_MFR_ESMT_C8 0xc8
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = 16 * section + 8;
+ region->length = 8;
+
+ return 0;
+}
+
+static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = 16 * section + 2;
+ region->length = 6;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
+ .ecc = f50l1g41lb_ooblayout_ecc,
+ .free = f50l1g41lb_ooblayout_free,
+};
+
+static const struct spinand_info esmt_c8_spinand_table[] = {
+ SPINAND_INFO("F50L1G41LB",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
+ SPINAND_INFO("F50D1G41LB",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
+};
+
+static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
+ .id = SPINAND_MFR_ESMT_C8,
+ .name = "ESMT",
+ .chips = esmt_c8_spinand_table,
+ .nchips = ARRAY_SIZE(esmt_c8_spinand_table),
+ .ops = &esmt_spinand_manuf_ops,
+};
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -261,6 +261,7 @@ struct spinand_manufacturer {
/* SPI NAND manufacturers */
extern const struct spinand_manufacturer ato_spinand_manufacturer;
+extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;

View File

@@ -0,0 +1,168 @@
From f32085fc0b87049491b07e198d924d738a1a2834 Mon Sep 17 00:00:00 2001
From: Daniel Danzberger <daniel@dd-wrt.com>
Date: Wed, 3 Aug 2022 17:31:03 +0200
Subject: [PATCH] mtd: spinand: Add support for Etron EM73D044VCx
Airoha is a new ARM platform based on Cortex-A53 which has recently been
merged into linux-next.
Due to BootROM limitations on this platform, the Cortex-A53 can't run in
Aarch64 mode and code must be compiled for 32-Bit ARM.
This support is based mostly on those linux-next commits backported
for kernel 5.15.
Patches:
1 - platform support = linux-next
2 - clock driver = linux-next
3 - gpio driver = linux-next
4 - linux,usable-memory-range dts support = linux-next
5 - mtd spinand driver
6 - spi driver
7 - pci driver (kconfig only, uses mediatek PCI) = linux-next
Still missing:
- Ethernet driver
- Sysupgrade support
A.t.m there exists one subtarget EN7523 with only one evaluation
board.
The initramfs can be run with the following commands from u-boot:
-
u-boot> setenv bootfile \
openwrt-airoha-airoha_en7523-evb-initramfs-kernel.bin
u-boot> tftpboot
u-boot> bootm 0x81800000
-
Submitted-by: Daniel Danzberger <daniel@dd-wrt.com>
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
-spinand-objs := core.o ato.o esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
+spinand-objs := core.o ato.o esmt.o etron.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -939,6 +939,7 @@ static const struct nand_ops spinand_ops
static const struct spinand_manufacturer *spinand_manufacturers[] = {
&ato_spinand_manufacturer,
&esmt_c8_spinand_manufacturer,
+ &etron_spinand_manufacturer,
&gigadevice_spinand_manufacturer,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
--- /dev/null
+++ b/drivers/mtd/nand/spi/etron.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_ETRON 0xd5
+
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int etron_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 72;
+ oobregion->length = 56;
+
+ return 0;
+}
+
+static int etron_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 1;
+ oobregion->length = 71;
+
+ return 0;
+}
+
+static int etron_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ /* Between 1-7 bitflips were corrected */
+ return 7;
+
+ case STATUS_ECC_MASK:
+ /* Maximum bitflips were corrected */
+ return 8;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+ }
+
+ return -EINVAL;
+}
+
+static const struct mtd_ooblayout_ops etron_ooblayout = {
+ .ecc = etron_ooblayout_ecc,
+ .free = etron_ooblayout_free,
+};
+
+static const struct spinand_info etron_spinand_table[] = {
+ SPINAND_INFO("EM73D044VCx",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x1f),
+ // bpc, pagesize, oobsize, pagesperblock, bperlun, maxbadplun, ppl, lpt, #t
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
+};
+
+static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer etron_spinand_manufacturer = {
+ .id = SPINAND_MFR_ETRON,
+ .name = "Etron",
+ .chips = etron_spinand_table,
+ .nchips = ARRAY_SIZE(etron_spinand_table),
+ .ops = &etron_spinand_manuf_ops,
+};
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -262,6 +262,7 @@ struct spinand_manufacturer {
/* SPI NAND manufacturers */
extern const struct spinand_manufacturer ato_spinand_manufacturer;
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
+extern const struct spinand_manufacturer etron_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;

View File

@@ -0,0 +1,23 @@
From: Joe Mullally <jwmullally@gmail.com>
Subject: mtd/spi-nor/xmc: add support for XMC XM25QH64C
The XMC XM25QH64C is a 8MB SPI NOR chip. The patch is verified on TL-WPA8631P v3.
Datasheet available at https://www.xmcwh.com/uploads/442/XM25QH64C.pdf
Signed-off-by: Joe Mullally <jwmullally@gmail.com>
---
drivers/mtd/spi-nor/xmc.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/mtd/spi-nor/xmc.c
+++ b/drivers/mtd/spi-nor/xmc.c
@@ -13,6 +13,9 @@ static const struct flash_info xmc_nor_p
{ "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_QUAD_READ) },
+ { "XM25QH64C", INFO(0x204017, 0, 64 * 1024, 128)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
{ "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_QUAD_READ) },

View File

@@ -0,0 +1,98 @@
From: Daniel Golle <daniel@makrotopia.org>
Subject: ubi: auto-attach mtd device named "ubi" or "data" on boot
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/build.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1263,6 +1263,74 @@ static struct mtd_notifier ubi_mtd_notif
.remove = ubi_notify_remove,
};
+
+/*
+ * This function tries attaching mtd partitions named either "ubi" or "data"
+ * during boot.
+ */
+static void __init ubi_auto_attach(void)
+{
+ int err;
+ struct mtd_info *mtd;
+ loff_t offset = 0;
+ size_t len;
+ char magic[4];
+
+ /* try attaching mtd device named "ubi" or "data" */
+ mtd = open_mtd_device("ubi");
+ if (IS_ERR(mtd))
+ mtd = open_mtd_device("data");
+
+ if (IS_ERR(mtd))
+ return;
+
+ /* get the first not bad block */
+ if (mtd_can_have_bb(mtd))
+ while (mtd_block_isbad(mtd, offset)) {
+ offset += mtd->erasesize;
+
+ if (offset > mtd->size) {
+ pr_err("UBI error: Failed to find a non-bad "
+ "block on mtd%d\n", mtd->index);
+ goto cleanup;
+ }
+ }
+
+ /* check if the read from flash was successful */
+ err = mtd_read(mtd, offset, 4, &len, (void *) magic);
+ if ((err && !mtd_is_bitflip(err)) || len != 4) {
+ pr_err("UBI error: unable to read from mtd%d\n", mtd->index);
+ goto cleanup;
+ }
+
+ /* check for a valid ubi magic */
+ if (strncmp(magic, "UBI#", 4)) {
+ pr_err("UBI error: no valid UBI magic found inside mtd%d\n", mtd->index);
+ goto cleanup;
+ }
+
+ /* don't auto-add media types where UBI doesn't makes sense */
+ if (mtd->type != MTD_NANDFLASH &&
+ mtd->type != MTD_NORFLASH &&
+ mtd->type != MTD_DATAFLASH &&
+ mtd->type != MTD_MLCNANDFLASH)
+ goto cleanup;
+
+ mutex_lock(&ubi_devices_mutex);
+ pr_notice("UBI: auto-attach mtd%d\n", mtd->index);
+ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, 0, 0, false);
+ mutex_unlock(&ubi_devices_mutex);
+ if (err < 0) {
+ pr_err("UBI error: cannot attach mtd%d\n", mtd->index);
+ goto cleanup;
+ }
+
+ return;
+
+cleanup:
+ put_mtd_device(mtd);
+}
+
static int __init ubi_init_attach(void)
{
int err, i, k;
@@ -1313,6 +1381,12 @@ static int __init ubi_init_attach(void)
}
}
+ /* auto-attach mtd devices only if built-in to the kernel and no ubi.mtd
+ * parameter was given */
+ if (IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
+ !ubi_is_module() && !mtd_devs)
+ ubi_auto_attach();
+
return 0;
out_detach:

View File

@@ -0,0 +1,77 @@
From: Daniel Golle <daniel@makrotopia.org>
Subject: ubi: auto-create ubiblock device for rootfs
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/block.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -644,10 +644,47 @@ match_volume_desc(struct ubi_volume_info
return true;
}
+#define UBIFS_NODE_MAGIC 0x06101831
+static inline int ubi_vol_is_ubifs(struct ubi_volume_desc *desc)
+{
+ int ret;
+ uint32_t magic_of, magic;
+ ret = ubi_read(desc, 0, (char *)&magic_of, 0, 4);
+ if (ret)
+ return 0;
+ magic = le32_to_cpu(magic_of);
+ return magic == UBIFS_NODE_MAGIC;
+}
+
+static void __init ubiblock_create_auto_rootfs(struct ubi_volume_info *vi)
+{
+ int ret, is_ubifs;
+ struct ubi_volume_desc *desc;
+
+ if (strcmp(vi->name, "rootfs") &&
+ strcmp(vi->name, "fit"))
+ return;
+
+ desc = ubi_open_volume(vi->ubi_num, vi->vol_id, UBI_READONLY);
+ if (IS_ERR(desc))
+ return;
+
+ is_ubifs = ubi_vol_is_ubifs(desc);
+ ubi_close_volume(desc);
+ if (is_ubifs)
+ return;
+
+ ret = ubiblock_create(vi);
+ if (ret)
+ pr_err("UBI error: block: can't add '%s' volume, err=%d\n",
+ vi->name, ret);
+}
+
static void
ubiblock_create_from_param(struct ubi_volume_info *vi)
{
int i, ret = 0;
+ bool got_param = false;
struct ubiblock_param *p;
/*
@@ -660,6 +697,7 @@ ubiblock_create_from_param(struct ubi_vo
if (!match_volume_desc(vi, p->name, p->ubi_num, p->vol_id))
continue;
+ got_param = true;
ret = ubiblock_create(vi);
if (ret) {
pr_err(
@@ -668,6 +706,10 @@ ubiblock_create_from_param(struct ubi_vo
}
break;
}
+
+ /* auto-attach "rootfs" volume if existing and non-ubifs */
+ if (!got_param && IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV))
+ ubiblock_create_auto_rootfs(vi);
}
static int ubiblock_notify(struct notifier_block *nb,

View File

@@ -0,0 +1,53 @@
From: Daniel Golle <daniel@makrotopia.org>
Subject: try auto-mounting ubi0:rootfs in init/do_mounts.c
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
init/do_mounts.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -446,7 +446,30 @@ retry:
out:
put_page(page);
}
-
+
+#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
+static int __init mount_ubi_rootfs(void)
+{
+ int flags = MS_SILENT;
+ int err, tried = 0;
+
+ while (tried < 2) {
+ err = do_mount_root("ubi0:rootfs", "ubifs", flags, \
+ root_mount_data);
+ switch (err) {
+ case -EACCES:
+ flags |= MS_RDONLY;
+ tried++;
+ break;
+ default:
+ return err;
+ }
+ }
+
+ return -EINVAL;
+}
+#endif
+
#ifdef CONFIG_ROOT_NFS
#define NFSROOT_TIMEOUT_MIN 5
@@ -579,6 +602,10 @@ void __init mount_root(void)
return;
}
#endif
+#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
+ if (!mount_ubi_rootfs())
+ return;
+#endif
if (ROOT_DEV == 0 && root_device_name && root_fs_names) {
if (mount_nodev_root() == 0)
return;

View File

@@ -0,0 +1,34 @@
From: Daniel Golle <daniel@makrotopia.org>
Subject: ubi: set ROOT_DEV to ubiblock "rootfs" if unset
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/mtd/ubi/block.c | 10 ++++++++++
1 file changed, 10 insertions(+)
--- a/drivers/mtd/ubi/block.c
+++ b/drivers/mtd/ubi/block.c
@@ -43,6 +43,7 @@
#include <linux/scatterlist.h>
#include <linux/idr.h>
#include <asm/div64.h>
+#include <linux/root_dev.h>
#include "ubi-media.h"
#include "ubi.h"
@@ -460,6 +461,15 @@ int ubiblock_create(struct ubi_volume_in
dev_info(disk_to_dev(dev->gd), "created from ubi%d:%d(%s)",
dev->ubi_num, dev->vol_id, vi->name);
mutex_unlock(&devices_mutex);
+
+ if (!strcmp(vi->name, "rootfs") &&
+ IS_ENABLED(CONFIG_MTD_ROOTFS_ROOT_DEV) &&
+ ROOT_DEV == 0) {
+ pr_notice("ubiblock: device ubiblock%d_%d (%s) set to be root filesystem\n",
+ dev->ubi_num, dev->vol_id, vi->name);
+ ROOT_DEV = MKDEV(gd->major, gd->first_minor);
+ }
+
return 0;
out_destroy_wq:

View File

@@ -0,0 +1,60 @@
From: Gabor Juhos <juhosg@openwrt.org>
Subject: mtd: add EOF marker support to the UBI layer
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
---
drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++---
drivers/mtd/ubi/ubi.h | 1 +
2 files changed, 23 insertions(+), 3 deletions(-)
--- a/drivers/mtd/ubi/attach.c
+++ b/drivers/mtd/ubi/attach.c
@@ -926,6 +926,13 @@ static bool vol_ignored(int vol_id)
#endif
}
+static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech)
+{
+ return ech->padding1[0] == 'E' &&
+ ech->padding1[1] == 'O' &&
+ ech->padding1[2] == 'F';
+}
+
/**
* scan_peb - scan and process UBI headers of a PEB.
* @ubi: UBI device description object
@@ -958,9 +965,21 @@ static int scan_peb(struct ubi_device *u
return 0;
}
- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
- if (err < 0)
- return err;
+ if (!ai->eof_found) {
+ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+ if (err < 0)
+ return err;
+
+ if (ec_hdr_has_eof(ech)) {
+ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n",
+ pnum);
+ ai->eof_found = true;
+ }
+ }
+
+ if (ai->eof_found)
+ err = UBI_IO_FF_BITFLIPS;
+
switch (err) {
case 0:
break;
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -780,6 +780,7 @@ struct ubi_attach_info {
int mean_ec;
uint64_t ec_sum;
int ec_count;
+ bool eof_found;
struct kmem_cache *aeb_slab_cache;
struct ubi_ec_hdr *ech;
struct ubi_vid_io_buf *vidb;

View File

@@ -0,0 +1,52 @@
From 5734c6669fba7ddb5ef491ccff7159d15dba0b59 Mon Sep 17 00:00:00 2001
From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
Date: Wed, 5 Sep 2018 01:32:51 +0200
Subject: [PATCH 496/497] dt-bindings: add bindings for mtd-concat devices
Document virtual mtd-concat device bindings.
Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
---
.../devicetree/bindings/mtd/mtd-concat.txt | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt
@@ -0,0 +1,36 @@
+Virtual MTD concat device
+
+Requires properties:
+- devices: list of phandles to mtd nodes that should be concatenated
+
+Example:
+
+&spi {
+ flash0: flash@0 {
+ ...
+ };
+ flash1: flash@1 {
+ ...
+ };
+};
+
+flash {
+ compatible = "mtd-concat";
+
+ devices = <&flash0 &flash1>;
+
+ partitions {
+ compatible = "fixed-partitions";
+
+ partition@0 {
+ label = "boot";
+ reg = <0x0000000 0x0040000>;
+ read-only;
+ };
+
+ partition@40000 {
+ label = "firmware";
+ reg = <0x0040000 0x1fc0000>;
+ };
+ }
+}

View File

@@ -0,0 +1,216 @@
From e53f712d8eac71f54399b61038ccf87d2cee99d7 Mon Sep 17 00:00:00 2001
From: Bernhard Frauendienst <kernel@nospam.obeliks.de>
Date: Sat, 25 Aug 2018 12:35:22 +0200
Subject: [PATCH 497/497] mtd: mtdconcat: add dt driver for concat devices
Some mtd drivers like physmap variants have support for concatenating
multiple mtd devices, but there is no generic way to define such a
concat device from within the device tree.
This is useful for some SoC boards that use multiple flash chips as
memory banks of a single mtd device, with partitions spanning chip
borders.
This commit adds a driver for creating virtual mtd-concat devices. They
must have a compatible = "mtd-concat" line, and define a list of devices
to concat in the 'devices' property, for example:
flash {
compatible = "mtd-concat";
devices = <&flash0 &flash1>;
partitions {
...
};
};
The driver is added to the very end of the mtd Makefile to increase the
likelyhood of all child devices already being loaded at the time of
probing, preventing unnecessary deferred probes.
Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de>
---
drivers/mtd/Kconfig | 2 +
drivers/mtd/Makefile | 3 +
drivers/mtd/composite/Kconfig | 12 +++
drivers/mtd/composite/Makefile | 6 ++
drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++
5 files changed, 151 insertions(+)
create mode 100644 drivers/mtd/composite/Kconfig
create mode 100644 drivers/mtd/composite/Makefile
create mode 100644 drivers/mtd/composite/virt_concat.c
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -241,4 +241,6 @@ source "drivers/mtd/ubi/Kconfig"
source "drivers/mtd/hyperbus/Kconfig"
+source "drivers/mtd/composite/Kconfig"
+
endif # MTD
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -33,3 +33,6 @@ obj-y += chips/ lpddr/ maps/ devices/ n
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/
obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/
+
+# Composite drivers must be loaded last
+obj-y += composite/
--- /dev/null
+++ b/drivers/mtd/composite/Kconfig
@@ -0,0 +1,12 @@
+menu "Composite MTD device drivers"
+ depends on MTD!=n
+
+config MTD_VIRT_CONCAT
+ tristate "Virtual concat MTD device"
+ help
+ This driver allows creation of a virtual MTD concat device, which
+ concatenates multiple underlying MTD devices to a single device.
+ This is required by some SoC boards where multiple memory banks are
+ used as one device with partitions spanning across device boundaries.
+
+endmenu
--- /dev/null
+++ b/drivers/mtd/composite/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# linux/drivers/mtd/composite/Makefile
+#
+
+obj-$(CONFIG_MTD_VIRT_CONCAT) += virt_concat.o
--- /dev/null
+++ b/drivers/mtd/composite/virt_concat.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Virtual concat MTD device driver
+ *
+ * Copyright (C) 2018 Bernhard Frauendienst
+ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mtd/concat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+/*
+ * struct of_virt_concat - platform device driver data.
+ * @cmtd the final mtd_concat device
+ * @num_devices the number of devices in @devices
+ * @devices points to an array of devices already loaded
+ */
+struct of_virt_concat {
+ struct mtd_info *cmtd;
+ int num_devices;
+ struct mtd_info **devices;
+};
+
+static int virt_concat_remove(struct platform_device *pdev)
+{
+ struct of_virt_concat *info;
+ int i;
+
+ info = platform_get_drvdata(pdev);
+ if (!info)
+ return 0;
+
+ // unset data for when this is called after a probe error
+ platform_set_drvdata(pdev, NULL);
+
+ if (info->cmtd) {
+ mtd_device_unregister(info->cmtd);
+ mtd_concat_destroy(info->cmtd);
+ }
+
+ if (info->devices) {
+ for (i = 0; i < info->num_devices; i++)
+ put_mtd_device(info->devices[i]);
+ }
+
+ return 0;
+}
+
+static int virt_concat_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct of_phandle_iterator it;
+ struct of_virt_concat *info;
+ struct mtd_info *mtd;
+ int err = 0, count;
+
+ count = of_count_phandle_with_args(node, "devices", NULL);
+ if (count <= 0)
+ return -EINVAL;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->devices = devm_kcalloc(&pdev->dev, count,
+ sizeof(*(info->devices)), GFP_KERNEL);
+ if (!info->devices) {
+ err = -ENOMEM;
+ goto err_remove;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ of_for_each_phandle(&it, err, node, "devices", NULL, 0) {
+ mtd = of_get_mtd_device_by_node(it.node);
+ if (IS_ERR(mtd)) {
+ of_node_put(it.node);
+ err = -EPROBE_DEFER;
+ goto err_remove;
+ }
+
+ info->devices[info->num_devices++] = mtd;
+ }
+
+ info->cmtd = mtd_concat_create(info->devices, info->num_devices,
+ dev_name(&pdev->dev));
+ if (!info->cmtd) {
+ err = -ENXIO;
+ goto err_remove;
+ }
+
+ info->cmtd->dev.parent = &pdev->dev;
+ mtd_set_of_node(info->cmtd, node);
+ mtd_device_register(info->cmtd, NULL, 0);
+
+ return 0;
+
+err_remove:
+ virt_concat_remove(pdev);
+
+ return err;
+}
+
+static const struct of_device_id virt_concat_of_match[] = {
+ { .compatible = "mtd-concat", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, virt_concat_of_match);
+
+static struct platform_driver virt_concat_driver = {
+ .probe = virt_concat_probe,
+ .remove = virt_concat_remove,
+ .driver = {
+ .name = "virt-mtdconcat",
+ .of_match_table = virt_concat_of_match,
+ },
+};
+
+module_platform_driver(virt_concat_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bernhard Frauendienst <kernel@nospam.obeliks.de>");
+MODULE_DESCRIPTION("Virtual concat MTD device driver");

View File

@@ -0,0 +1,32 @@
From 8bf2ce6ea4ee840b70f55a27f80e1cd308051b13 Mon Sep 17 00:00:00 2001
From: Nick Hainke <vincent@systemli.org>
Date: Mon, 27 Dec 2021 00:38:13 +0100
Subject: [PATCH 1/2] mtd: spi-nor: locking support for MX25L6405D
Macronix MX25L6405D supports locking with four block-protection bits.
Currently, the driver only sets three bits. If the bootloader does not
sustain the flash chip in an unlocked state, the flash might be
non-writeable. Add the corresponding flag to enable locking support with
four bits in the status register.
Tested on Nanostation M2 XM.
Similar to commit 7ea40b54e83b ("mtd: spi-nor: enable locking support for
MX25L12805D")
Signed-off-by: David Bauer <mail@david-bauer.net>
Signed-off-by: Nick Hainke <vincent@systemli.org>
---
drivers/mtd/spi-nor/macronix.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -48,6 +48,7 @@ static const struct flash_info macronix_
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64)
NO_SFDP_FLAGS(SECT_4K) },
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128)
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP)
NO_SFDP_FLAGS(SECT_4K) },
{ "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4)
NO_SFDP_FLAGS(SECT_4K) },

View File

@@ -0,0 +1,30 @@
From 245224608b5368c10407da07557e546743d3c489 Mon Sep 17 00:00:00 2001
From: Nick Hainke <vincent@systemli.org>
Date: Mon, 27 Dec 2021 09:33:13 +0100
Subject: [PATCH 2/2] mtd: spi-nor: disable 16-bit-sr for macronix
Macronix flash chips seem to consist of only one status register.
These chips will not work with the "16-bit Write Status (01h) Command".
Disable SNOR_F_HAS_16BIT_SR for all Macronix chips.
Tested with MX25L6405D.
Fixes: 39d1e3340c73 ("mtd: spi-nor: Fix clearing of QE bit on
lock()/unlock()")
Signed-off-by: David Bauer <mail@david-bauer.net>
Signed-off-by: Nick Hainke <vincent@systemli.org>
---
drivers/mtd/spi-nor/macronix.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -107,6 +107,7 @@ static void macronix_nor_default_init(st
{
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode;
+ nor->flags &= ~SNOR_F_HAS_16BIT_SR;
nor->flags |= SNOR_F_HAS_LOCK;
}

View File

@@ -0,0 +1,52 @@
From af7b91bcecce0eae24e90acd35d96ecee73e1407 Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Wed, 13 Jul 2022 12:21:15 +0200
Subject: [PATCH] fs: add cdrom dependency
---
fs/hfs/Kconfig | 1 +
fs/hfsplus/Kconfig | 1 +
fs/isofs/Kconfig | 1 +
fs/udf/Kconfig | 1 +
4 files changed, 4 insertions(+)
--- a/fs/hfs/Kconfig
+++ b/fs/hfs/Kconfig
@@ -2,6 +2,7 @@
config HFS_FS
tristate "Apple Macintosh file system support"
depends on BLOCK
+ select CDROM
select NLS
help
If you say Y here, you will be able to mount Macintosh-formatted
--- a/fs/hfsplus/Kconfig
+++ b/fs/hfsplus/Kconfig
@@ -2,6 +2,7 @@
config HFSPLUS_FS
tristate "Apple Extended HFS file system support"
depends on BLOCK
+ select CDROM
select NLS
select NLS_UTF8
help
--- a/fs/isofs/Kconfig
+++ b/fs/isofs/Kconfig
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config ISO9660_FS
tristate "ISO 9660 CDROM file system support"
+ select CDROM
help
This is the standard file system used on CD-ROMs. It was previously
known as "High Sierra File System" and is called "hsfs" on other
--- a/fs/udf/Kconfig
+++ b/fs/udf/Kconfig
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config UDF_FS
tristate "UDF file system support"
+ select CDROM
select CRC_ITU_T
select NLS
help

View File

@@ -0,0 +1,733 @@
From 6173a065cb395d4a9528c4e49810af127db68141 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 16 Nov 2022 12:49:52 +0000
Subject: [PATCH 1/2] block: add uImage.FIT subimage block driver
Add a small block driver which exposes filesystem sub-images contained
in U-Boot uImage.FIT images as block devices.
The uImage.FIT image has to be stored directly on a block device or
partition, MTD device or partition, or UBI volume.
The driver is intended for systems using the U-Boot bootloader and
uses the root device hint left by the bootloader (or the user) in
the 'chosen' section of the device-tree.
Example:
/dts-v1/;
/ {
chosen {
rootdisk = <&mmc0_part3>;
};
};
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
MAINTAINERS | 6 +
drivers/block/Kconfig | 12 +
drivers/block/Makefile | 2 +
drivers/block/fitblk.c | 658 ++++++++++++++++++++++++++++++++++++
drivers/block/open | 4 +
include/uapi/linux/fitblk.h | 10 +
6 files changed, 692 insertions(+)
create mode 100644 drivers/block/fitblk.c
create mode 100644 drivers/block/open
create mode 100644 include/uapi/linux/fitblk.h
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21059,6 +21059,12 @@ F: Documentation/filesystems/ubifs-authe
F: Documentation/filesystems/ubifs.rst
F: fs/ubifs/
+U-BOOT UIMAGE.FIT PARSER
+M: Daniel Golle <daniel@makrotopia.org>
+L: linux-block@vger.kernel.org
+S: Maintained
+F: drivers/block/fitblk.c
+
UBLK USERSPACE BLOCK DRIVER
M: Ming Lei <ming.lei@redhat.com>
L: linux-block@vger.kernel.org
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -383,6 +383,18 @@ config VIRTIO_BLK
This is the virtual block driver for virtio. It can be used with
QEMU based VMMs (like KVM or Xen). Say Y or M.
+config UIMAGE_FIT_BLK
+ bool "uImage.FIT block driver"
+ help
+ This driver allows using filesystems contained in uImage.FIT images
+ by mapping them as block devices.
+
+ It can currently not be built as a module due to libfdt symbols not
+ being exported.
+
+ Say Y if you want to mount filesystems sub-images of a uImage.FIT
+ stored in a block device partition, mtdblock or ubiblock device.
+
config BLK_DEV_RBD
tristate "Rados block device (RBD)"
depends on INET && BLOCK
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -39,4 +39,6 @@ obj-$(CONFIG_BLK_DEV_NULL_BLK) += null_b
obj-$(CONFIG_BLK_DEV_UBLK) += ublk_drv.o
+obj-$(CONFIG_UIMAGE_FIT_BLK) += fitblk.o
+
swim_mod-y := swim.o swim_asm.o
--- /dev/null
+++ b/drivers/block/fitblk.c
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * uImage.FIT virtual block device driver.
+ *
+ * Copyright (C) 2023 Daniel Golle
+ * Copyright (C) 2007 Nick Piggin
+ * Copyright (C) 2007 Novell Inc.
+ *
+ * Initially derived from drivers/block/brd.c which is in parts derived from
+ * drivers/block/rd.c, and drivers/block/loop.c, copyright of their respective
+ * owners.
+ *
+ * uImage.FIT headers extracted from Das U-Boot
+ * (C) Copyright 2008 Semihalf
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ */
+
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/blkpg.h>
+#include <linux/blk-mq.h>
+#include <linux/ctype.h>
+#include <linux/hdreg.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_fdt.h>
+#include <linux/pagemap.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/refcount.h>
+#include <linux/task_work.h>
+#include <linux/types.h>
+#include <linux/libfdt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/root_dev.h>
+#include <uapi/linux/fitblk.h>
+
+#define FIT_DEVICE_PREFIX "fit"
+
+/* maximum number of pages used for the uImage.FIT index structure */
+#define FIT_MAX_PAGES 1024
+
+/* minimum free sectors to map as read-write "remainder" volume */
+#define MIN_FREE_SECT 16
+
+/* maximum number of mapped loadables */
+#define MAX_FIT_LOADABLES 16
+
+/* constants for uImage.FIT structrure traversal */
+#define FIT_IMAGES_PATH "/images"
+#define FIT_CONFS_PATH "/configurations"
+
+/* hash/signature/key node */
+#define FIT_HASH_NODENAME "hash"
+#define FIT_ALGO_PROP "algo"
+#define FIT_VALUE_PROP "value"
+#define FIT_IGNORE_PROP "uboot-ignore"
+#define FIT_SIG_NODENAME "signature"
+#define FIT_KEY_REQUIRED "required"
+#define FIT_KEY_HINT "key-name-hint"
+
+/* cipher node */
+#define FIT_CIPHER_NODENAME "cipher"
+#define FIT_ALGO_PROP "algo"
+
+/* image node */
+#define FIT_DATA_PROP "data"
+#define FIT_DATA_POSITION_PROP "data-position"
+#define FIT_DATA_OFFSET_PROP "data-offset"
+#define FIT_DATA_SIZE_PROP "data-size"
+#define FIT_TIMESTAMP_PROP "timestamp"
+#define FIT_DESC_PROP "description"
+#define FIT_ARCH_PROP "arch"
+#define FIT_TYPE_PROP "type"
+#define FIT_OS_PROP "os"
+#define FIT_COMP_PROP "compression"
+#define FIT_ENTRY_PROP "entry"
+#define FIT_LOAD_PROP "load"
+
+/* configuration node */
+#define FIT_KERNEL_PROP "kernel"
+#define FIT_FILESYSTEM_PROP "filesystem"
+#define FIT_RAMDISK_PROP "ramdisk"
+#define FIT_FDT_PROP "fdt"
+#define FIT_LOADABLE_PROP "loadables"
+#define FIT_DEFAULT_PROP "default"
+#define FIT_SETUP_PROP "setup"
+#define FIT_FPGA_PROP "fpga"
+#define FIT_FIRMWARE_PROP "firmware"
+#define FIT_STANDALONE_PROP "standalone"
+
+/* fitblk driver data */
+static const char *_fitblk_claim_ptr = "I belong to fitblk";
+static const char *ubootver;
+struct device_node *rootdisk;
+static struct platform_device *pdev;
+static LIST_HEAD(fitblk_devices);
+static DEFINE_MUTEX(devices_mutex);
+refcount_t num_devs;
+
+struct fitblk {
+ struct platform_device *pdev;
+ struct block_device *lower_bdev;
+ sector_t start_sect;
+ struct gendisk *disk;
+ struct work_struct remove_work;
+ struct list_head list;
+ bool dead;
+};
+
+static int fitblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct fitblk *fitblk = bdev->bd_disk->private_data;
+
+ if (fitblk->dead)
+ return -ENOENT;
+
+ return 0;
+}
+
+static void fitblk_release(struct gendisk *disk, fmode_t mode)
+{
+ return;
+}
+
+static void fitblk_submit_bio(struct bio *orig_bio)
+{
+ struct bio *bio = orig_bio;
+ struct fitblk *fitblk = bio->bi_bdev->bd_disk->private_data;
+
+ if (fitblk->dead)
+ return;
+
+ /* mangle bio and re-submit */
+ while (bio) {
+ bio->bi_iter.bi_sector += fitblk->start_sect;
+ bio->bi_bdev = fitblk->lower_bdev;
+ bio = bio->bi_next;
+ }
+ submit_bio(orig_bio);
+}
+
+static void fitblk_remove(struct fitblk *fitblk)
+{
+ blk_mark_disk_dead(fitblk->disk);
+ mutex_lock(&devices_mutex);
+ fitblk->dead = true;
+ list_del(&fitblk->list);
+ mutex_unlock(&devices_mutex);
+
+ schedule_work(&fitblk->remove_work);
+}
+
+static int fitblk_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ struct fitblk *fitblk = bdev->bd_disk->private_data;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (fitblk->dead)
+ return -ENOENT;
+
+ switch (cmd) {
+ case FITBLK_RELEASE:
+ fitblk_remove(fitblk);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct block_device_operations fitblk_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = fitblk_ioctl,
+ .open = fitblk_open,
+ .release = fitblk_release,
+ .submit_bio = fitblk_submit_bio,
+};
+
+static void fitblk_purge(struct work_struct *work)
+{
+ struct fitblk *fitblk = container_of(work, struct fitblk, remove_work);
+
+ //del_gendisk(fitblk->disk); // causes crash, not doing it doesn't matter
+ refcount_dec(&num_devs);
+ platform_device_del(fitblk->pdev);
+ platform_device_put(fitblk->pdev);
+
+ if (refcount_dec_if_one(&num_devs)) {
+ sysfs_remove_link(&pdev->dev.kobj, "lower_dev");
+ blkdev_put(fitblk->lower_bdev, FMODE_READ | FMODE_EXCL);
+ }
+
+ kfree(fitblk);
+}
+
+static int add_fit_subimage_device(struct block_device *lower_bdev,
+ unsigned int slot, sector_t start_sect,
+ sector_t nr_sect, bool readonly)
+{
+ struct fitblk *fitblk;
+ struct gendisk *disk;
+ int err;
+
+ mutex_lock(&devices_mutex);
+ if (!refcount_inc_not_zero(&num_devs))
+ return -EBADF;
+
+ fitblk = kzalloc(sizeof(struct fitblk), GFP_KERNEL);
+ if (!fitblk) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ fitblk->lower_bdev = lower_bdev;
+ fitblk->start_sect = start_sect;
+ INIT_WORK(&fitblk->remove_work, fitblk_purge);
+
+ disk = blk_alloc_disk(NUMA_NO_NODE);
+ if (!disk) {
+ err = -ENOMEM;
+ goto out_free_fitblk;
+ }
+
+ disk->first_minor = 0;
+ disk->flags = lower_bdev->bd_disk->flags | GENHD_FL_NO_PART;
+ disk->fops = &fitblk_fops;
+ disk->private_data = fitblk;
+ if (readonly) {
+ set_disk_ro(disk, 1);
+ snprintf(disk->disk_name, sizeof(disk->disk_name), FIT_DEVICE_PREFIX "%u", slot);
+ } else {
+ strcpy(disk->disk_name, FIT_DEVICE_PREFIX "rw");
+ }
+
+ set_capacity(disk, nr_sect);
+
+ disk->queue->queue_flags = lower_bdev->bd_disk->queue->queue_flags;
+ memcpy(&disk->queue->limits, &lower_bdev->bd_disk->queue->limits,
+ sizeof(struct queue_limits));
+
+ fitblk->disk = disk;
+ fitblk->pdev = platform_device_alloc(disk->disk_name, PLATFORM_DEVID_NONE);
+ if (!fitblk->pdev) {
+ err = -ENOMEM;
+ goto out_cleanup_disk;
+ }
+
+ fitblk->pdev->dev.parent = &pdev->dev;
+ err = platform_device_add(fitblk->pdev);
+ if (err)
+ goto out_put_pdev;
+
+ err = device_add_disk(&fitblk->pdev->dev, disk, NULL);
+ if (err)
+ goto out_del_pdev;
+
+ if (!ROOT_DEV)
+ ROOT_DEV = disk->part0->bd_dev;
+
+ list_add_tail(&fitblk->list, &fitblk_devices);
+
+ mutex_unlock(&devices_mutex);
+
+ return 0;
+
+out_del_pdev:
+ platform_device_del(fitblk->pdev);
+out_put_pdev:
+ platform_device_put(fitblk->pdev);
+out_cleanup_disk:
+ put_disk(disk);
+out_free_fitblk:
+ kfree(fitblk);
+out_unlock:
+ refcount_dec(&num_devs);
+ mutex_unlock(&devices_mutex);
+ return err;
+}
+
+static int parse_fit_on_dev(struct device *dev)
+{
+ struct block_device *bdev;
+ struct address_space *mapping;
+ struct folio *folio;
+ pgoff_t f_index = 0;
+ size_t bytes_left, bytes_to_copy;
+ void *pre_fit, *fit, *fit_c;
+ u64 dsize, dsectors, imgmaxsect = 0;
+ u32 size, image_pos, image_len;
+ const __be32 *image_offset_be, *image_len_be, *image_pos_be;
+ int ret = 0, node, images, config;
+ const char *image_name, *image_type, *image_description,
+ *config_default, *config_description, *config_loadables;
+ u32 image_name_len, image_type_len, image_description_len,
+ bootconf_len, config_default_len, config_description_len,
+ config_loadables_len;
+ sector_t start_sect, nr_sects;
+ struct device_node *np = NULL;
+ const char *bootconf_c;
+ const char *loadable;
+ char *bootconf = NULL, *bootconf_term;
+ bool found;
+ int loadables_rem_len, loadable_len;
+ u16 loadcnt;
+ unsigned int slot = 0;
+
+ /* Exclusive open the block device to receive holder notifications */
+ bdev = blkdev_get_by_dev(dev->devt, FMODE_READ | FMODE_EXCL, &_fitblk_claim_ptr);
+ if (!bdev)
+ return -ENODEV;
+
+ if (IS_ERR(bdev))
+ return PTR_ERR(bdev);
+
+ mapping = bdev->bd_inode->i_mapping;
+
+ /* map first page */
+ folio = read_mapping_folio(mapping, f_index++, NULL);
+ if (IS_ERR(folio)) {
+ ret = PTR_ERR(folio);
+ goto out_blkdev;
+ }
+ pre_fit = folio_address(folio) + offset_in_folio(folio, 0);
+
+ /* uImage.FIT is based on flattened device tree structure */
+ if (fdt_check_header(pre_fit)) {
+ ret = -EINVAL;
+ folio_put(folio);
+ goto out_blkdev;
+ }
+
+ size = fdt_totalsize(pre_fit);
+
+ if (size > PAGE_SIZE * FIT_MAX_PAGES) {
+ ret = -EOPNOTSUPP;
+ folio_put(folio);
+ goto out_blkdev;
+ }
+
+ /* acquire disk size */
+ dsectors = bdev_nr_sectors(bdev);
+ dsize = dsectors << SECTOR_SHIFT;
+
+ /* abort if FIT structure is larger than disk or partition size */
+ if (size >= dsize) {
+ ret = -EFBIG;
+ folio_put(folio);
+ goto out_blkdev;
+ }
+
+ fit = kmalloc(size, GFP_KERNEL);
+ if (!fit) {
+ ret = -ENOMEM;
+ folio_put(folio);
+ goto out_blkdev;
+ }
+
+ bytes_left = size;
+ fit_c = fit;
+ while (bytes_left > 0) {
+ bytes_to_copy = min_t(size_t, bytes_left,
+ folio_size(folio) - offset_in_folio(folio, 0));
+ memcpy(fit_c, pre_fit, bytes_to_copy);
+ fit_c += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ if (bytes_left) {
+ folio_put(folio);
+ folio = read_mapping_folio(mapping, f_index++, NULL);
+ if (IS_ERR(folio)) {
+ ret = PTR_ERR(folio);
+ goto out_blkdev;
+ };
+ pre_fit = folio_address(folio) + offset_in_folio(folio, 0);
+ }
+ }
+ folio_put(folio);
+
+ /* set boot config node name U-Boot may have added to the device tree */
+ np = of_find_node_by_path("/chosen");
+ if (np) {
+ bootconf_c = of_get_property(np, "u-boot,bootconf", &bootconf_len);
+ if (bootconf_c && bootconf_len)
+ bootconf = kmemdup_nul(bootconf_c, bootconf_len, GFP_KERNEL);
+ }
+
+ if (bootconf) {
+ bootconf_term = strchr(bootconf, '#');
+ if (bootconf_term)
+ *bootconf_term = '\0';
+ }
+
+ /* find configuration path in uImage.FIT */
+ config = fdt_path_offset(fit, FIT_CONFS_PATH);
+ if (config < 0) {
+ pr_err("FIT: Cannot find %s node: %d\n",
+ FIT_CONFS_PATH, config);
+ ret = -ENOENT;
+ goto out_bootconf;
+ }
+
+ /* get default configuration node name */
+ config_default =
+ fdt_getprop(fit, config, FIT_DEFAULT_PROP, &config_default_len);
+
+ /* make sure we got either default or selected boot config node name */
+ if (!config_default && !bootconf) {
+ pr_err("FIT: Cannot find default configuration\n");
+ ret = -ENOENT;
+ goto out_bootconf;
+ }
+
+ /* find selected boot config node, fallback on default config node */
+ node = fdt_subnode_offset(fit, config, bootconf ?: config_default);
+ if (node < 0) {
+ pr_err("FIT: Cannot find %s node: %d\n",
+ bootconf ?: config_default, node);
+ ret = -ENOENT;
+ goto out_bootconf;
+ }
+
+ pr_info("FIT: Detected U-Boot %s\n", ubootver);
+
+ /* get selected configuration data */
+ config_description =
+ fdt_getprop(fit, node, FIT_DESC_PROP, &config_description_len);
+ config_loadables = fdt_getprop(fit, node, FIT_LOADABLE_PROP,
+ &config_loadables_len);
+
+ pr_info("FIT: %s configuration: \"%.*s\"%s%.*s%s\n",
+ bootconf ? "Selected" : "Default",
+ bootconf ? bootconf_len : config_default_len,
+ bootconf ?: config_default,
+ config_description ? " (" : "",
+ config_description ? config_description_len : 0,
+ config_description ?: "",
+ config_description ? ")" : "");
+
+ if (!config_loadables || !config_loadables_len) {
+ pr_err("FIT: No loadables configured in \"%s\"\n",
+ bootconf ?: config_default);
+ ret = -ENOENT;
+ goto out_bootconf;
+ }
+
+ /* get images path in uImage.FIT */
+ images = fdt_path_offset(fit, FIT_IMAGES_PATH);
+ if (images < 0) {
+ pr_err("FIT: Cannot find %s node: %d\n", FIT_IMAGES_PATH, images);
+ ret = -EINVAL;
+ goto out_bootconf;
+ }
+
+ /* iterate over images in uImage.FIT */
+ fdt_for_each_subnode(node, fit, images) {
+ image_name = fdt_get_name(fit, node, &image_name_len);
+ image_type = fdt_getprop(fit, node, FIT_TYPE_PROP, &image_type_len);
+ image_offset_be = fdt_getprop(fit, node, FIT_DATA_OFFSET_PROP, NULL);
+ image_pos_be = fdt_getprop(fit, node, FIT_DATA_POSITION_PROP, NULL);
+ image_len_be = fdt_getprop(fit, node, FIT_DATA_SIZE_PROP, NULL);
+
+ if (!image_name || !image_type || !image_len_be ||
+ !image_name_len || !image_type_len)
+ continue;
+
+ image_len = be32_to_cpu(*image_len_be);
+ if (!image_len)
+ continue;
+
+ if (image_offset_be)
+ image_pos = be32_to_cpu(*image_offset_be) + size;
+ else if (image_pos_be)
+ image_pos = be32_to_cpu(*image_pos_be);
+ else
+ continue;
+
+ image_description = fdt_getprop(fit, node, FIT_DESC_PROP,
+ &image_description_len);
+
+ pr_info("FIT: %16s sub-image 0x%08x..0x%08x \"%.*s\"%s%.*s%s\n",
+ image_type, image_pos, image_pos + image_len - 1,
+ image_name_len, image_name, image_description ? " (" : "",
+ image_description ? image_description_len : 0,
+ image_description ?: "", image_description ? ") " : "");
+
+ /* only 'filesystem' images should be mapped as partitions */
+ if (strncmp(image_type, FIT_FILESYSTEM_PROP, image_type_len))
+ continue;
+
+ /* check if sub-image is part of configured loadables */
+ found = false;
+ loadable = config_loadables;
+ loadables_rem_len = config_loadables_len;
+ for (loadcnt = 0; loadables_rem_len > 1 &&
+ loadcnt < MAX_FIT_LOADABLES; ++loadcnt) {
+ loadable_len =
+ strnlen(loadable, loadables_rem_len - 1) + 1;
+ loadables_rem_len -= loadable_len;
+ if (!strncmp(image_name, loadable, loadable_len)) {
+ found = true;
+ break;
+ }
+ loadable += loadable_len;
+ }
+ if (!found)
+ continue;
+
+ if (image_pos % (1 << PAGE_SHIFT)) {
+ dev_err(dev, "FIT: image %.*s start not aligned to page boundaries, skipping\n",
+ image_name_len, image_name);
+ continue;
+ }
+
+ if (image_len % (1 << PAGE_SHIFT)) {
+ dev_err(dev, "FIT: sub-image %.*s end not aligned to page boundaries, skipping\n",
+ image_name_len, image_name);
+ continue;
+ }
+
+ start_sect = image_pos >> SECTOR_SHIFT;
+ nr_sects = image_len >> SECTOR_SHIFT;
+ imgmaxsect = max_t(sector_t, imgmaxsect, start_sect + nr_sects);
+
+ if (start_sect + nr_sects > dsectors) {
+ dev_err(dev, "FIT: sub-image %.*s disk access beyond EOD\n",
+ image_name_len, image_name);
+ continue;
+ }
+
+ if (!slot) {
+ ret = sysfs_create_link_nowarn(&pdev->dev.kobj, bdev_kobj(bdev), "lower_dev");
+ if (ret && ret != -EEXIST)
+ goto out_bootconf;
+
+ ret = 0;
+ }
+
+ add_fit_subimage_device(bdev, slot++, start_sect, nr_sects, true);
+ }
+
+ if (!found || !slot)
+ goto out_bootconf;
+
+ dev_info(dev, "mapped %u uImage.FIT filesystem sub-image%s as /dev/fit%s%u%s\n",
+ slot, (slot > 1)?"s":"", (slot > 1)?"[0...":"", slot - 1,
+ (slot > 1)?"]":"");
+
+ /* in case uImage.FIT is stored in a partition, map the remaining space */
+ if (!bdev->bd_read_only && bdev_is_partition(bdev) &&
+ (imgmaxsect + MIN_FREE_SECT) < dsectors) {
+ add_fit_subimage_device(bdev, slot++, imgmaxsect,
+ dsectors - imgmaxsect, false);
+ dev_info(dev, "mapped remaining space as /dev/fitrw\n");
+ }
+
+out_bootconf:
+ kfree(bootconf);
+ kfree(fit);
+out_blkdev:
+ if (!found || ret)
+ blkdev_put(bdev, FMODE_READ | FMODE_EXCL);
+
+ return ret;
+}
+
+static int fitblk_match_of_node(struct device *dev, const void *np)
+{
+ int ret;
+
+ ret = device_match_of_node(dev, np);
+ if (ret)
+ return ret;
+
+ /*
+ * To match ubiblock and mtdblock devices by their parent ubi
+ * or mtd device, also consider block device parent
+ */
+ if (!dev->parent)
+ return 0;
+
+ return device_match_of_node(dev->parent, np);
+}
+
+static int fitblk_probe(struct platform_device *pdev)
+{
+ struct device *dev;
+
+ dev = class_find_device(&block_class, NULL, rootdisk, fitblk_match_of_node);
+ if (!dev)
+ return -EPROBE_DEFER;
+
+ return parse_fit_on_dev(dev);
+}
+
+static struct platform_driver fitblk_driver = {
+ .probe = fitblk_probe,
+ .driver = {
+ .name = "fitblk",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init fitblk_init(void)
+{
+ /* detect U-Boot firmware */
+ ubootver = of_get_property(of_chosen, "u-boot,version", NULL);
+ if (!ubootver)
+ return 0;
+
+ /* parse 'rootdisk' property phandle */
+ rootdisk = of_parse_phandle(of_chosen, "rootdisk", 0);
+ if (!rootdisk)
+ return 0;
+
+ if (platform_driver_register(&fitblk_driver))
+ return -ENODEV;
+
+ refcount_set(&num_devs, 1);
+ pdev = platform_device_register_simple("fitblk", -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ return 0;
+}
+device_initcall(fitblk_init);
--- /dev/null
+++ b/include/uapi/linux/fitblk.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_FITBLK_H
+#define _UAPI_LINUX_FITBLK_H
+
+/*
+ * IOCTL commands --- we will commandeer 0x46 ('F')
+ */
+#define FITBLK_RELEASE 0x4600
+
+#endif /* _UAPI_LINUX_FITBLK_H */

View File

@@ -0,0 +1,25 @@
From 5ede3f8aed9a1a579bf7304142600d1f3500add9 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Mon, 12 Jun 2023 03:58:42 +0100
Subject: [PATCH 2/2] init: bypass device lookup for /dev/fit* rootfs
Allow 'rootwait' as /dev/fit* can show up late if the underlaying
device is probed late.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
init/do_mounts.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -645,7 +645,8 @@ void __init prepare_namespace(void)
if (saved_root_name[0]) {
root_device_name = saved_root_name;
- if (!strncmp(root_device_name, "mtd", 3) ||
+ if (!strncmp(root_device_name, "fit", 3) ||
+ !strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: fs: jffs2: EOF marker
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
fs/jffs2/build.c | 10 ++++++++++
fs/jffs2/scan.c | 21 +++++++++++++++++++--
2 files changed, 29 insertions(+), 2 deletions(-)
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct
dbg_fsbuild("scanned flash completely\n");
jffs2_dbg_dump_block_lists_nolock(c);
+ if (c->flags & (1 << 7)) {
+ printk("%s(): unlocking the mtd device... ", __func__);
+ mtd_unlock(c->mtd, 0, c->mtd->size);
+ printk("done.\n");
+
+ printk("%s(): erasing all blocks after the end marker... ", __func__);
+ jffs2_erase_pending_blocks(c, -1);
+ printk("done.\n");
+ }
+
dbg_fsbuild("pass 1 starting\n");
c->flags |= JFFS2_SB_FLAG_BUILDING;
/* Now scan the directory tree, increasing nlink according to every dirent found. */
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_in
/* reset summary info for next eraseblock scan */
jffs2_sum_reset_collected(s);
- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
- buf_size, s);
+ if (c->flags & (1 << 7)) {
+ if (mtd_block_isbad(c->mtd, jeb->offset))
+ ret = BLK_STATE_BADBLOCK;
+ else
+ ret = BLK_STATE_ALLFF;
+ } else
+ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
+ buf_size, s);
if (ret < 0)
goto out;
@@ -567,6 +573,17 @@ full_scan:
return err;
}
+ if ((buf[0] == 0xde) &&
+ (buf[1] == 0xad) &&
+ (buf[2] == 0xc0) &&
+ (buf[3] == 0xde)) {
+ /* end of filesystem. erase everything after this point */
+ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset);
+ c->flags |= (1 << 7);
+
+ return BLK_STATE_ALLFF;
+ }
+
/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
ofs = 0;
max_ofs = EMPTY_SCAN_SIZE(c->sector_size);

View File

@@ -0,0 +1,90 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: netfilter: add support for flushing conntrack via /proc
lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
net/netfilter/nf_conntrack_standalone.c | 59 ++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -9,6 +9,7 @@
#include <linux/percpu.h>
#include <linux/netdevice.h>
#include <linux/security.h>
+#include <linux/inet.h>
#include <net/net_namespace.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
@@ -465,6 +466,58 @@ static int ct_cpu_seq_show(struct seq_fi
return 0;
}
+struct kill_request {
+ u16 family;
+ union nf_inet_addr addr;
+};
+
+static int kill_matching(struct nf_conn *i, void *data)
+{
+ struct kill_request *kr = data;
+ struct nf_conntrack_tuple *t1 = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
+ struct nf_conntrack_tuple *t2 = &i->tuplehash[IP_CT_DIR_REPLY].tuple;
+
+ if (!kr->family)
+ return 1;
+
+ if (t1->src.l3num != kr->family)
+ return 0;
+
+ return (nf_inet_addr_cmp(&kr->addr, &t1->src.u3) ||
+ nf_inet_addr_cmp(&kr->addr, &t1->dst.u3) ||
+ nf_inet_addr_cmp(&kr->addr, &t2->src.u3) ||
+ nf_inet_addr_cmp(&kr->addr, &t2->dst.u3));
+}
+
+static int ct_file_write(struct file *file, char *buf, size_t count)
+{
+ struct seq_file *seq = file->private_data;
+ struct nf_ct_iter_data iter_data;
+ struct kill_request kr = { };
+
+ if (count == 0)
+ return 0;
+
+ if (count >= INET6_ADDRSTRLEN)
+ count = INET6_ADDRSTRLEN - 1;
+
+ if (strnchr(buf, count, ':')) {
+ kr.family = AF_INET6;
+ if (!in6_pton(buf, count, (void *)&kr.addr, '\n', NULL))
+ return -EINVAL;
+ } else if (strnchr(buf, count, '.')) {
+ kr.family = AF_INET;
+ if (!in4_pton(buf, count, (void *)&kr.addr, '\n', NULL))
+ return -EINVAL;
+ }
+
+ iter_data.net = seq_file_net(seq);
+ iter_data.data = &kr;
+ nf_ct_iterate_cleanup_net(kill_matching, &iter_data);
+
+ return 0;
+}
+
static const struct seq_operations ct_cpu_seq_ops = {
.start = ct_cpu_seq_start,
.next = ct_cpu_seq_next,
@@ -478,8 +531,9 @@ static int nf_conntrack_standalone_init_
kuid_t root_uid;
kgid_t root_gid;
- pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops,
- sizeof(struct ct_iter_state));
+ pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net,
+ &ct_seq_ops, &ct_file_write,
+ sizeof(struct ct_iter_state), NULL);
if (!pde)
goto out_nf_conntrack;

View File

@@ -0,0 +1,110 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: kernel: add a new version of my netfilter speedup patches for linux 2.6.39 and 3.0
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
include/uapi/linux/netfilter_ipv4/ip_tables.h | 1 +
net/ipv4/netfilter/ip_tables.c | 37 +++++++++++++++++++++++++++
2 files changed, 38 insertions(+)
--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h
+++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h
@@ -89,6 +89,7 @@ struct ipt_ip {
#define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */
#define IPT_F_GOTO 0x02 /* Set if jump is a goto */
#define IPT_F_MASK 0x03 /* All possible flag bits mask. */
+#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */
/* Values for "inv" field in struct ipt_ip. */
#define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -50,6 +50,9 @@ ip_packet_match(const struct iphdr *ip,
{
unsigned long ret;
+ if (ipinfo->flags & IPT_F_NO_DEF_MATCH)
+ return true;
+
if (NF_INVF(ipinfo, IPT_INV_SRCIP,
(ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
NF_INVF(ipinfo, IPT_INV_DSTIP,
@@ -80,6 +83,29 @@ ip_packet_match(const struct iphdr *ip,
return true;
}
+static void
+ip_checkdefault(struct ipt_ip *ip)
+{
+ static const char iface_mask[IFNAMSIZ] = {};
+
+ if (ip->invflags || ip->flags & IPT_F_FRAG)
+ return;
+
+ if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0)
+ return;
+
+ if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0)
+ return;
+
+ if (ip->smsk.s_addr || ip->dmsk.s_addr)
+ return;
+
+ if (ip->proto)
+ return;
+
+ ip->flags |= IPT_F_NO_DEF_MATCH;
+}
+
static bool
ip_checkentry(const struct ipt_ip *ip)
{
@@ -525,6 +551,8 @@ find_check_entry(struct ipt_entry *e, st
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;
+ ip_checkdefault(&e->ip);
+
if (!xt_percpu_counter_alloc(alloc_state, &e->counters))
return -ENOMEM;
@@ -819,6 +847,7 @@ copy_entries_to_user(unsigned int total_
const struct xt_table_info *private = table->private;
int ret = 0;
const void *loc_cpu_entry;
+ u8 flags;
counters = alloc_counters(table);
if (IS_ERR(counters))
@@ -846,6 +875,14 @@ copy_entries_to_user(unsigned int total_
goto free_counters;
}
+ flags = e->ip.flags & IPT_F_MASK;
+ if (copy_to_user(userptr + off
+ + offsetof(struct ipt_entry, ip.flags),
+ &flags, sizeof(flags)) != 0) {
+ ret = -EFAULT;
+ goto free_counters;
+ }
+
for (i = sizeof(struct ipt_entry);
i < e->target_offset;
i += m->u.match_size) {
@@ -1227,12 +1264,15 @@ compat_copy_entry_to_user(struct ipt_ent
compat_uint_t origsize;
const struct xt_entry_match *ematch;
int ret = 0;
+ u8 flags = e->ip.flags & IPT_F_MASK;
origsize = *size;
ce = *dstptr;
if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 ||
copy_to_user(&ce->counters, &counters[i],
- sizeof(counters[i])) != 0)
+ sizeof(counters[i])) != 0 ||
+ copy_to_user(&ce->ip.flags, &flags,
+ sizeof(flags)) != 0)
return -EFAULT;
*dstptr += sizeof(struct compat_ipt_entry);

View File

@@ -0,0 +1,106 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: netfilter: match bypass default table
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
net/ipv4/netfilter/ip_tables.c | 79 +++++++++++++++++++++++++++++++-----------
1 file changed, 58 insertions(+), 21 deletions(-)
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -246,6 +246,33 @@ struct ipt_entry *ipt_next_entry(const s
return (void *)entry + entry->next_offset;
}
+static bool
+ipt_handle_default_rule(struct ipt_entry *e, unsigned int *verdict)
+{
+ struct xt_entry_target *t;
+ struct xt_standard_target *st;
+
+ if (e->target_offset != sizeof(struct ipt_entry))
+ return false;
+
+ if (!(e->ip.flags & IPT_F_NO_DEF_MATCH))
+ return false;
+
+ t = ipt_get_target(e);
+ if (t->u.kernel.target->target)
+ return false;
+
+ st = (struct xt_standard_target *) t;
+ if (st->verdict == XT_RETURN)
+ return false;
+
+ if (st->verdict >= 0)
+ return false;
+
+ *verdict = (unsigned)(-st->verdict) - 1;
+ return true;
+}
+
/* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int
ipt_do_table(void *priv,
@@ -267,27 +294,28 @@ ipt_do_table(void *priv,
unsigned int addend;
/* Initialization */
+ WARN_ON(!(table->valid_hooks & (1 << hook)));
+ local_bh_disable();
+ private = READ_ONCE(table->private); /* Address dependency. */
+ cpu = smp_processor_id();
+ table_base = private->entries;
+
+ e = get_entry(table_base, private->hook_entry[hook]);
+ if (ipt_handle_default_rule(e, &verdict)) {
+ struct xt_counters *counter;
+
+ counter = xt_get_this_cpu_counter(&e->counters);
+ ADD_COUNTER(*counter, skb->len, 1);
+ local_bh_enable();
+ return verdict;
+ }
+
stackidx = 0;
ip = ip_hdr(skb);
indev = state->in ? state->in->name : nulldevname;
outdev = state->out ? state->out->name : nulldevname;
- /* We handle fragments by dealing with the first fragment as
- * if it was a normal packet. All other fragments are treated
- * normally, except that they will NEVER match rules that ask
- * things we don't know, ie. tcp syn flag or ports). If the
- * rule is also a fragment-specific rule, non-fragments won't
- * match it. */
- acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
- acpar.thoff = ip_hdrlen(skb);
- acpar.hotdrop = false;
- acpar.state = state;
- WARN_ON(!(table->valid_hooks & (1 << hook)));
- local_bh_disable();
addend = xt_write_recseq_begin();
- private = READ_ONCE(table->private); /* Address dependency. */
- cpu = smp_processor_id();
- table_base = private->entries;
jumpstack = (struct ipt_entry **)private->jumpstack[cpu];
/* Switch to alternate jumpstack if we're being invoked via TEE.
@@ -300,7 +328,16 @@ ipt_do_table(void *priv,
if (static_key_false(&xt_tee_enabled))
jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated);
- e = get_entry(table_base, private->hook_entry[hook]);
+ /* We handle fragments by dealing with the first fragment as
+ * if it was a normal packet. All other fragments are treated
+ * normally, except that they will NEVER match rules that ask
+ * things we don't know, ie. tcp syn flag or ports). If the
+ * rule is also a fragment-specific rule, non-fragments won't
+ * match it. */
+ acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
+ acpar.thoff = ip_hdrlen(skb);
+ acpar.hotdrop = false;
+ acpar.state = state;
do {
const struct xt_entry_target *t;

View File

@@ -0,0 +1,22 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: netfilter: reduce match memory access
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
net/ipv4/netfilter/ip_tables.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -53,9 +53,9 @@ ip_packet_match(const struct iphdr *ip,
if (ipinfo->flags & IPT_F_NO_DEF_MATCH)
return true;
- if (NF_INVF(ipinfo, IPT_INV_SRCIP,
+ if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr &&
(ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) ||
- NF_INVF(ipinfo, IPT_INV_DSTIP,
+ NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr &&
(ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr))
return false;

View File

@@ -0,0 +1,86 @@
From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Date: Mon, 21 Aug 2017 11:14:14 +0300
Subject: [PATCH] net_sched/codel: do not defer queue length update
When codel wants to drop last packet in ->dequeue() it cannot call
qdisc_tree_reduce_backlog() right away - it will notify parent qdisc
about zero qlen and HTB/HFSC will deactivate class. The same class will
be deactivated second time by caller of ->dequeue(). Currently codel and
fq_codel defer update. This triggers warning in HFSC when it's qlen != 0
but there is no active classes.
This patch update parent queue length immediately: just temporary increase
qlen around qdisc_tree_reduce_backlog() to prevent first class deactivation
if we have skb to return.
This might open another problem in HFSC - now operation peek could fail and
deactivate parent class.
Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581
---
--- a/net/sched/sch_codel.c
+++ b/net/sched/sch_codel.c
@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_deque
&q->stats, qdisc_pkt_len, codel_get_enqueue_time,
drop_func, dequeue_func);
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
- * or HTB crashes. Defer it for next round.
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
+ * parent class, dequeue in parent qdisc will do the same if we
+ * return skb. Temporary increment qlen if we have skb.
*/
- if (q->stats.drop_count && sch->q.qlen) {
- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
+ if (q->stats.drop_count) {
+ if (skb)
+ sch->q.qlen++;
+ qdisc_tree_reduce_backlog(sch, q->stats.drop_count,
+ q->stats.drop_len);
+ if (skb)
+ sch->q.qlen--;
q->stats.drop_count = 0;
q->stats.drop_len = 0;
}
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -304,6 +304,21 @@ begin:
&flow->cvars, &q->cstats, qdisc_pkt_len,
codel_get_enqueue_time, drop_func, dequeue_func);
+ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate
+ * parent class, dequeue in parent qdisc will do the same if we
+ * return skb. Temporary increment qlen if we have skb.
+ */
+ if (q->cstats.drop_count) {
+ if (skb)
+ sch->q.qlen++;
+ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
+ q->cstats.drop_len);
+ if (skb)
+ sch->q.qlen--;
+ q->cstats.drop_count = 0;
+ q->cstats.drop_len = 0;
+ }
+
if (!skb) {
/* force a pass through old_flows to prevent starvation */
if ((head == &q->new_flows) && !list_empty(&q->old_flows))
@@ -314,15 +329,6 @@ begin:
}
qdisc_bstats_update(sch, skb);
flow->deficit -= qdisc_pkt_len(skb);
- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
- * or HTB crashes. Defer it for next round.
- */
- if (q->cstats.drop_count && sch->q.qlen) {
- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
- q->cstats.drop_len);
- q->cstats.drop_count = 0;
- q->cstats.drop_len = 0;
- }
return skb;
}

View File

@@ -0,0 +1,138 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: net: add an optimization for dealing with raw sockets
lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
include/uapi/linux/if_packet.h | 3 +++
net/packet/af_packet.c | 34 +++++++++++++++++++++++++++-------
net/packet/internal.h | 1 +
3 files changed, 31 insertions(+), 7 deletions(-)
--- a/include/uapi/linux/if_packet.h
+++ b/include/uapi/linux/if_packet.h
@@ -33,6 +33,8 @@ struct sockaddr_ll {
#define PACKET_KERNEL 7 /* To kernel space */
/* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */
#define PACKET_FASTROUTE 6 /* Fastrouted frame */
+#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */
+
/* Packet socket options */
@@ -59,6 +61,7 @@ struct sockaddr_ll {
#define PACKET_ROLLOVER_STATS 21
#define PACKET_FANOUT_DATA 22
#define PACKET_IGNORE_OUTGOING 23
+#define PACKET_RECV_TYPE 24
#define PACKET_FANOUT_HASH 0
#define PACKET_FANOUT_LB 1
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1866,6 +1866,7 @@ static int packet_rcv_spkt(struct sk_buf
{
struct sock *sk;
struct sockaddr_pkt *spkt;
+ struct packet_sock *po;
/*
* When we registered the protocol we saved the socket in the data
@@ -1873,6 +1874,7 @@ static int packet_rcv_spkt(struct sk_buf
*/
sk = pt->af_packet_priv;
+ po = pkt_sk(sk);
/*
* Yank back the headers [hope the device set this
@@ -1885,7 +1887,7 @@ static int packet_rcv_spkt(struct sk_buf
* so that this procedure is noop.
*/
- if (skb->pkt_type == PACKET_LOOPBACK)
+ if (!(po->pkt_type & (1 << skb->pkt_type)))
goto out;
if (!net_eq(dev_net(dev), sock_net(sk)))
@@ -2131,12 +2133,12 @@ static int packet_rcv(struct sk_buff *sk
unsigned int snaplen, res;
bool is_drop_n_account = false;
- if (skb->pkt_type == PACKET_LOOPBACK)
- goto drop;
-
sk = pt->af_packet_priv;
po = pkt_sk(sk);
+ if (!(po->pkt_type & (1 << skb->pkt_type)))
+ goto drop;
+
if (!net_eq(dev_net(dev), sock_net(sk)))
goto drop;
@@ -2263,12 +2265,12 @@ static int tpacket_rcv(struct sk_buff *s
BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32);
BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48);
- if (skb->pkt_type == PACKET_LOOPBACK)
- goto drop;
-
sk = pt->af_packet_priv;
po = pkt_sk(sk);
+ if (!(po->pkt_type & (1 << skb->pkt_type)))
+ goto drop;
+
if (!net_eq(dev_net(dev), sock_net(sk)))
goto drop;
@@ -3377,6 +3379,7 @@ static int packet_create(struct net *net
mutex_init(&po->pg_vec_lock);
po->rollover = NULL;
po->prot_hook.func = packet_rcv;
+ po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK);
if (sock->type == SOCK_PACKET)
po->prot_hook.func = packet_rcv_spkt;
@@ -4014,6 +4017,16 @@ packet_setsockopt(struct socket *sock, i
WRITE_ONCE(po->xmit, val ? packet_direct_xmit : dev_queue_xmit);
return 0;
}
+ case PACKET_RECV_TYPE:
+ {
+ unsigned int val;
+ if (optlen != sizeof(val))
+ return -EINVAL;
+ if (copy_from_sockptr(&val, optval, sizeof(val)))
+ return -EFAULT;
+ po->pkt_type = val & ~BIT(PACKET_LOOPBACK);
+ return 0;
+ }
default:
return -ENOPROTOOPT;
}
@@ -4070,6 +4083,13 @@ static int packet_getsockopt(struct sock
case PACKET_VNET_HDR:
val = po->has_vnet_hdr;
break;
+ case PACKET_RECV_TYPE:
+ if (len > sizeof(unsigned int))
+ len = sizeof(unsigned int);
+ val = po->pkt_type;
+
+ data = &val;
+ break;
case PACKET_VERSION:
val = po->tp_version;
break;
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -136,6 +136,7 @@ struct packet_sock {
int (*xmit)(struct sk_buff *skb);
struct packet_type prot_hook ____cacheline_aligned_in_smp;
atomic_t tp_drops ____cacheline_aligned_in_smp;
+ unsigned int pkt_type;
};
static inline struct packet_sock *pkt_sk(struct sock *sk)

View File

@@ -0,0 +1,20 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: kernel: add a few patches for avoiding unnecessary skb reallocations - significantly improves ethernet<->wireless performance
lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
include/linux/skbuff.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3038,7 +3038,7 @@ static inline int pskb_network_may_pull(
* NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8)
*/
#ifndef NET_SKB_PAD
-#define NET_SKB_PAD max(32, L1_CACHE_BYTES)
+#define NET_SKB_PAD max(64, L1_CACHE_BYTES)
#endif
int ___pskb_trim(struct sk_buff *skb, unsigned int len);

View File

@@ -0,0 +1,511 @@
From: Steven Barth <steven@midlink.org>
Subject: Add support for MAP-E FMRs (mesh mode)
MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication
between MAP CEs (mesh mode) without the need to forward such data to a
border relay. This is similar to how 6rd works but for IPv4 over IPv6.
Signed-off-by: Steven Barth <cyrus@openwrt.org>
---
include/net/ip6_tunnel.h | 13 ++
include/uapi/linux/if_tunnel.h | 13 ++
net/ipv6/ip6_tunnel.c | 276 +++++++++++++++++++++++++++++++++++++++--
3 files changed, 291 insertions(+), 11 deletions(-)
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -18,6 +18,18 @@
/* determine capability on a per-packet basis */
#define IP6_TNL_F_CAP_PER_PACKET 0x40000
+/* IPv6 tunnel FMR */
+struct __ip6_tnl_fmr {
+ struct __ip6_tnl_fmr *next; /* next fmr in list */
+ struct in6_addr ip6_prefix;
+ struct in_addr ip4_prefix;
+
+ __u8 ip6_prefix_len;
+ __u8 ip4_prefix_len;
+ __u8 ea_len;
+ __u8 offset;
+};
+
struct __ip6_tnl_parm {
char name[IFNAMSIZ]; /* name of tunnel device */
int link; /* ifindex of underlying L2 interface */
@@ -29,6 +41,7 @@ struct __ip6_tnl_parm {
__u32 flags; /* tunnel flags */
struct in6_addr laddr; /* local tunnel end-point address */
struct in6_addr raddr; /* remote tunnel end-point address */
+ struct __ip6_tnl_fmr *fmrs; /* FMRs */
__be16 i_flags;
__be16 o_flags;
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -77,10 +77,23 @@ enum {
IFLA_IPTUN_ENCAP_DPORT,
IFLA_IPTUN_COLLECT_METADATA,
IFLA_IPTUN_FWMARK,
+ IFLA_IPTUN_FMRS,
__IFLA_IPTUN_MAX,
};
#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
+enum {
+ IFLA_IPTUN_FMR_UNSPEC,
+ IFLA_IPTUN_FMR_IP6_PREFIX,
+ IFLA_IPTUN_FMR_IP4_PREFIX,
+ IFLA_IPTUN_FMR_IP6_PREFIX_LEN,
+ IFLA_IPTUN_FMR_IP4_PREFIX_LEN,
+ IFLA_IPTUN_FMR_EA_LEN,
+ IFLA_IPTUN_FMR_OFFSET,
+ __IFLA_IPTUN_FMR_MAX,
+};
+#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1)
+
enum tunnel_encap_types {
TUNNEL_ENCAP_NONE,
TUNNEL_ENCAP_FOU,
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -11,6 +11,9 @@
* linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c
*
* RFC 2473
+ *
+ * Changes:
+ * Steven Barth <cyrus@openwrt.org>: MAP-E FMR support
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -67,9 +70,9 @@ static bool log_ecn_error = true;
module_param(log_ecn_error, bool, 0644);
MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
-static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
+static u32 HASH(const struct in6_addr *addr)
{
- u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
+ u32 hash = ipv6_addr_hash(addr);
return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT);
}
@@ -114,17 +117,33 @@ static struct ip6_tnl *
ip6_tnl_lookup(struct net *net, int link,
const struct in6_addr *remote, const struct in6_addr *local)
{
- unsigned int hash = HASH(remote, local);
+ unsigned int hash = HASH(local);
struct ip6_tnl *t, *cand = NULL;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
struct in6_addr any;
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (!ipv6_addr_equal(local, &t->parms.laddr) ||
- !ipv6_addr_equal(remote, &t->parms.raddr) ||
!(t->dev->flags & IFF_UP))
continue;
+ if (!ipv6_addr_equal(remote, &t->parms.raddr)) {
+ struct __ip6_tnl_fmr *fmr;
+ bool found = false;
+
+ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) {
+ if (!ipv6_prefix_equal(remote, &fmr->ip6_prefix,
+ fmr->ip6_prefix_len))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ if (!found)
+ continue;
+ }
+
if (link == t->parms.link)
return t;
else
@@ -132,7 +151,7 @@ ip6_tnl_lookup(struct net *net, int link
}
memset(&any, 0, sizeof(any));
- hash = HASH(&any, local);
+ hash = HASH(local);
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (!ipv6_addr_equal(local, &t->parms.laddr) ||
!ipv6_addr_any(&t->parms.raddr) ||
@@ -145,7 +164,7 @@ ip6_tnl_lookup(struct net *net, int link
cand = t;
}
- hash = HASH(remote, &any);
+ hash = HASH(&any);
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
!ipv6_addr_any(&t->parms.laddr) ||
@@ -194,7 +213,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n,
if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
prio = 1;
- h = HASH(remote, local);
+ h = HASH(local);
}
return &ip6n->tnls[prio][h];
}
@@ -376,6 +395,12 @@ ip6_tnl_dev_uninit(struct net_device *de
struct net *net = t->net;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ while (t->parms.fmrs) {
+ struct __ip6_tnl_fmr *next = t->parms.fmrs->next;
+ kfree(t->parms.fmrs);
+ t->parms.fmrs = next;
+ }
+
if (dev == ip6n->fb_tnl_dev)
RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL);
else
@@ -788,6 +813,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
}
EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl);
+/**
+ * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR
+ * @dest: destination IPv6 address buffer
+ * @skb: received socket buffer
+ * @fmr: MAP FMR
+ * @xmit: Calculate for xmit or rcv
+ **/
+static void ip4ip6_fmr_calc(struct in6_addr *dest,
+ const struct iphdr *iph, const uint8_t *end,
+ const struct __ip6_tnl_fmr *fmr, bool xmit)
+{
+ int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len);
+ u8 *portp = NULL;
+ bool use_dest_addr;
+ const struct iphdr *dsth = iph;
+
+ if ((u8*)dsth >= end)
+ return;
+
+ /* find significant IP header */
+ if (iph->protocol == IPPROTO_ICMP) {
+ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4);
+ if (ih && ((u8*)&ih[1]) <= end && (
+ ih->type == ICMP_DEST_UNREACH ||
+ ih->type == ICMP_SOURCE_QUENCH ||
+ ih->type == ICMP_TIME_EXCEEDED ||
+ ih->type == ICMP_PARAMETERPROB ||
+ ih->type == ICMP_REDIRECT))
+ dsth = (const struct iphdr*)&ih[1];
+ }
+
+ /* in xmit-path use dest port by default and source port only if
+ this is an ICMP reply to something else; vice versa in rcv-path */
+ use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph);
+
+ /* get dst port */
+ if (((u8*)&dsth[1]) <= end && (
+ dsth->protocol == IPPROTO_UDP ||
+ dsth->protocol == IPPROTO_TCP ||
+ dsth->protocol == IPPROTO_SCTP ||
+ dsth->protocol == IPPROTO_DCCP)) {
+ /* for UDP, TCP, SCTP and DCCP source and dest port
+ follow IPv4 header directly */
+ portp = ((u8*)dsth) + dsth->ihl * 4;
+
+ if (use_dest_addr)
+ portp += sizeof(u16);
+ } else if (iph->protocol == IPPROTO_ICMP) {
+ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4);
+
+ /* use icmp identifier as port */
+ if (((u8*)&ih) <= end && (
+ (use_dest_addr && (
+ ih->type == ICMP_ECHOREPLY ||
+ ih->type == ICMP_TIMESTAMPREPLY ||
+ ih->type == ICMP_INFO_REPLY ||
+ ih->type == ICMP_ADDRESSREPLY)) ||
+ (!use_dest_addr && (
+ ih->type == ICMP_ECHO ||
+ ih->type == ICMP_TIMESTAMP ||
+ ih->type == ICMP_INFO_REQUEST ||
+ ih->type == ICMP_ADDRESS)
+ )))
+ portp = (u8*)&ih->un.echo.id;
+ }
+
+ if ((portp && &portp[2] <= end) || psidlen == 0) {
+ int frombyte = fmr->ip6_prefix_len / 8;
+ int fromrem = fmr->ip6_prefix_len % 8;
+ int bytes = sizeof(struct in6_addr) - frombyte;
+ const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr;
+ u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len);
+ u64 t = 0;
+
+ /* extract PSID from port and add it to eabits */
+ u16 psidbits = 0;
+ if (psidlen > 0) {
+ psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]);
+ psidbits >>= 16 - psidlen - fmr->offset;
+ psidbits = (u16)(psidbits << (16 - psidlen));
+ eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen));
+ }
+
+ /* rewrite destination address */
+ *dest = fmr->ip6_prefix;
+ memcpy(&dest->s6_addr[10], addr, sizeof(*addr));
+ dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen));
+
+ if (bytes > sizeof(u64))
+ bytes = sizeof(u64);
+
+ /* insert eabits */
+ memcpy(&t, &dest->s6_addr[frombyte], bytes);
+ t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1)
+ << (64 - fmr->ea_len - fromrem));
+ t = cpu_to_be64(t | (eabits >> fromrem));
+ memcpy(&dest->s6_addr[frombyte], &t, bytes);
+ }
+}
+
+
static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
const struct tnl_ptk_info *tpi,
struct metadata_dst *tun_dst,
@@ -855,6 +981,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl
memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+ if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs &&
+ !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) {
+ /* Packet didn't come from BR, so lookup FMR */
+ struct __ip6_tnl_fmr *fmr;
+ struct in6_addr expected = tunnel->parms.raddr;
+ for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next)
+ if (ipv6_prefix_equal(&ipv6h->saddr,
+ &fmr->ip6_prefix, fmr->ip6_prefix_len))
+ break;
+
+ /* Check that IPv6 matches IPv4 source to prevent spoofing */
+ if (fmr)
+ ip4ip6_fmr_calc(&expected, ip_hdr(skb),
+ skb_tail_pointer(skb), fmr, false);
+
+ if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) {
+ rcu_read_unlock();
+ goto drop;
+ }
+ }
+
__skb_tunnel_rx(skb, tunnel->dev, tunnel->net);
err = dscp_ecn_decapsulate(tunnel, ipv6h, skb);
@@ -1002,6 +1149,7 @@ static void init_tel_txopt(struct ipv6_t
opt->ops.opt_nflen = 8;
}
+
/**
* ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
* @t: the outgoing tunnel device
@@ -1293,6 +1441,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str
u8 protocol)
{
struct ip6_tnl *t = netdev_priv(dev);
+ struct __ip6_tnl_fmr *fmr;
struct ipv6hdr *ipv6h;
const struct iphdr *iph;
int encap_limit = -1;
@@ -1392,6 +1541,18 @@ ipxip6_tnl_xmit(struct sk_buff *skb, str
fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield);
+ /* try to find matching FMR */
+ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) {
+ unsigned mshift = 32 - fmr->ip4_prefix_len;
+ if (ntohl(fmr->ip4_prefix.s_addr) >> mshift ==
+ ntohl(ip_hdr(skb)->daddr) >> mshift)
+ break;
+ }
+
+ /* change dstaddr according to FMR */
+ if (fmr)
+ ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true);
+
if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6))
return -1;
@@ -1545,6 +1706,14 @@ ip6_tnl_change(struct ip6_tnl *t, const
t->parms.link = p->link;
t->parms.proto = p->proto;
t->parms.fwmark = p->fwmark;
+
+ while (t->parms.fmrs) {
+ struct __ip6_tnl_fmr *next = t->parms.fmrs->next;
+ kfree(t->parms.fmrs);
+ t->parms.fmrs = next;
+ }
+ t->parms.fmrs = p->fmrs;
+
dst_cache_reset(&t->dst_cache);
ip6_tnl_link_config(t);
}
@@ -1579,6 +1748,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_
p->flowinfo = u->flowinfo;
p->link = u->link;
p->proto = u->proto;
+ p->fmrs = NULL;
memcpy(p->name, u->name, sizeof(u->name));
}
@@ -1965,6 +2135,15 @@ static int ip6_tnl_validate(struct nlatt
return 0;
}
+static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = {
+ [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) },
+ [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 },
+ [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 },
+ [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 },
+ [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 }
+};
+
static void ip6_tnl_netlink_parms(struct nlattr *data[],
struct __ip6_tnl_parm *parms)
{
@@ -2002,6 +2181,46 @@ static void ip6_tnl_netlink_parms(struct
if (data[IFLA_IPTUN_FWMARK])
parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]);
+
+ if (data[IFLA_IPTUN_FMRS]) {
+ unsigned rem;
+ struct nlattr *fmr;
+ nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) {
+ struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c;
+ struct __ip6_tnl_fmr *nfmr;
+
+ nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX,
+ fmr, ip6_tnl_fmr_policy, NULL);
+
+ if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL)))
+ continue;
+
+ nfmr->offset = 6;
+
+ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX]))
+ nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX],
+ sizeof(nfmr->ip6_prefix));
+
+ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX]))
+ nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX],
+ sizeof(nfmr->ip4_prefix));
+
+ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN]))
+ nfmr->ip6_prefix_len = nla_get_u8(c);
+
+ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN]))
+ nfmr->ip4_prefix_len = nla_get_u8(c);
+
+ if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN]))
+ nfmr->ea_len = nla_get_u8(c);
+
+ if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET]))
+ nfmr->offset = nla_get_u8(c);
+
+ nfmr->next = parms->fmrs;
+ parms->fmrs = nfmr;
+ }
+ }
}
static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev,
@@ -2085,6 +2304,12 @@ static void ip6_tnl_dellink(struct net_d
static size_t ip6_tnl_get_size(const struct net_device *dev)
{
+ const struct ip6_tnl *t = netdev_priv(dev);
+ struct __ip6_tnl_fmr *c;
+ int fmrs = 0;
+ for (c = t->parms.fmrs; c; c = c->next)
+ ++fmrs;
+
return
/* IFLA_IPTUN_LINK */
nla_total_size(4) +
@@ -2114,6 +2339,24 @@ static size_t ip6_tnl_get_size(const str
nla_total_size(0) +
/* IFLA_IPTUN_FWMARK */
nla_total_size(4) +
+ /* IFLA_IPTUN_FMRS */
+ nla_total_size(0) +
+ (
+ /* nest */
+ nla_total_size(0) +
+ /* IFLA_IPTUN_FMR_IP6_PREFIX */
+ nla_total_size(sizeof(struct in6_addr)) +
+ /* IFLA_IPTUN_FMR_IP4_PREFIX */
+ nla_total_size(sizeof(struct in_addr)) +
+ /* IFLA_IPTUN_FMR_EA_LEN */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_FMR_OFFSET */
+ nla_total_size(1)
+ ) * fmrs +
0;
}
@@ -2121,6 +2364,9 @@ static int ip6_tnl_fill_info(struct sk_b
{
struct ip6_tnl *tunnel = netdev_priv(dev);
struct __ip6_tnl_parm *parm = &tunnel->parms;
+ struct __ip6_tnl_fmr *c;
+ int fmrcnt = 0;
+ struct nlattr *fmrs;
if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) ||
@@ -2130,9 +2376,27 @@ static int ip6_tnl_fill_info(struct sk_b
nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) ||
nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) ||
nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) ||
- nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark))
+ nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark) ||
+ !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS)))
goto nla_put_failure;
+ for (c = parm->fmrs; c; c = c->next) {
+ struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt);
+ if (!fmr ||
+ nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX,
+ sizeof(c->ip6_prefix), &c->ip6_prefix) ||
+ nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX,
+ sizeof(c->ip4_prefix), &c->ip4_prefix) ||
+ nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) ||
+ nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) ||
+ nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) ||
+ nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, fmr);
+ }
+ nla_nest_end(skb, fmrs);
+
if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) ||
nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) ||
nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) ||
@@ -2172,6 +2436,7 @@ static const struct nla_policy ip6_tnl_p
[IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 },
[IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG },
[IFLA_IPTUN_FWMARK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED },
};
static struct rtnl_link_ops ip6_link_ops __read_mostly = {

View File

@@ -0,0 +1,263 @@
From: Jonas Gorski <jogo@openwrt.org>
Subject: ipv6: allow rejecting with "source address failed policy"
RFC6204 L-14 requires rejecting traffic from invalid addresses with
ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/
egress policy) on the LAN side, so add an appropriate rule for that.
Signed-off-by: Jonas Gorski <jogo@openwrt.org>
---
include/net/netns/ipv6.h | 1 +
include/uapi/linux/fib_rules.h | 4 +++
include/uapi/linux/rtnetlink.h | 1 +
net/ipv4/fib_semantics.c | 4 +++
net/ipv4/fib_trie.c | 1 +
net/ipv4/ipmr.c | 1 +
net/ipv6/fib6_rules.c | 4 +++
net/ipv6/ip6mr.c | 2 ++
net/ipv6/route.c | 58 +++++++++++++++++++++++++++++++++++++++++-
9 files changed, 75 insertions(+), 1 deletion(-)
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -85,6 +85,7 @@ struct netns_ipv6 {
unsigned int fib6_routes_require_src;
#endif
struct rt6_info *ip6_prohibit_entry;
+ struct rt6_info *ip6_policy_failed_entry;
struct rt6_info *ip6_blk_hole_entry;
struct fib6_table *fib6_local_tbl;
struct fib_rules_ops *fib6_rules_ops;
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -82,6 +82,10 @@ enum {
FR_ACT_BLACKHOLE, /* Drop without notification */
FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */
FR_ACT_PROHIBIT, /* Drop with EACCES */
+ FR_ACT_RES9,
+ FR_ACT_RES10,
+ FR_ACT_RES11,
+ FR_ACT_POLICY_FAILED, /* Drop with EACCES */
__FR_ACT_MAX,
};
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -265,6 +265,7 @@ enum {
RTN_THROW, /* Not in this table */
RTN_NAT, /* Translate this address */
RTN_XRESOLVE, /* Use external resolver */
+ RTN_POLICY_FAILED, /* Failed ingress/egress policy */
__RTN_MAX
};
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -145,6 +145,10 @@ const struct fib_prop fib_props[RTN_MAX
.error = -EINVAL,
.scope = RT_SCOPE_NOWHERE,
},
+ [RTN_POLICY_FAILED] = {
+ .error = -EACCES,
+ .scope = RT_SCOPE_UNIVERSE,
+ },
};
static void rt_fibinfo_free(struct rtable __rcu **rtp)
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -2783,6 +2783,7 @@ static const char *const rtn_type_names[
[RTN_THROW] = "THROW",
[RTN_NAT] = "NAT",
[RTN_XRESOLVE] = "XRESOLVE",
+ [RTN_POLICY_FAILED] = "POLICY_FAILED",
};
static inline const char *rtn_type(char *buf, size_t len, unsigned int t)
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -180,6 +180,7 @@ static int ipmr_rule_action(struct fib_r
case FR_ACT_UNREACHABLE:
return -ENETUNREACH;
case FR_ACT_PROHIBIT:
+ case FR_ACT_POLICY_FAILED:
return -EACCES;
case FR_ACT_BLACKHOLE:
default:
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -221,6 +221,10 @@ static int __fib6_rule_action(struct fib
err = -EACCES;
rt = net->ipv6.ip6_prohibit_entry;
goto discard_pkt;
+ case FR_ACT_POLICY_FAILED:
+ err = -EACCES;
+ rt = net->ipv6.ip6_policy_failed_entry;
+ goto discard_pkt;
}
tb_id = fib_rule_get_table(rule, arg);
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -170,6 +170,8 @@ static int ip6mr_rule_action(struct fib_
return -ENETUNREACH;
case FR_ACT_PROHIBIT:
return -EACCES;
+ case FR_ACT_POLICY_FAILED:
+ return -EACCES;
case FR_ACT_BLACKHOLE:
default:
return -EINVAL;
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -98,6 +98,8 @@ static int ip6_pkt_discard(struct sk_bu
static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
static int ip6_pkt_prohibit(struct sk_buff *skb);
static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
+static int ip6_pkt_policy_failed(struct sk_buff *skb);
+static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb);
static void ip6_link_failure(struct sk_buff *skb);
static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb, u32 mtu,
@@ -318,6 +320,18 @@ static const struct rt6_info ip6_prohibi
.rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
};
+static const struct rt6_info ip6_policy_failed_entry_template = {
+ .dst = {
+ .__refcnt = ATOMIC_INIT(1),
+ .__use = 1,
+ .obsolete = DST_OBSOLETE_FORCE_CHK,
+ .error = -EACCES,
+ .input = ip6_pkt_policy_failed,
+ .output = ip6_pkt_policy_failed_out,
+ },
+ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
+};
+
static const struct rt6_info ip6_blk_hole_entry_template = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
@@ -1042,6 +1056,7 @@ static const int fib6_prop[RTN_MAX + 1]
[RTN_BLACKHOLE] = -EINVAL,
[RTN_UNREACHABLE] = -EHOSTUNREACH,
[RTN_PROHIBIT] = -EACCES,
+ [RTN_POLICY_FAILED] = -EACCES,
[RTN_THROW] = -EAGAIN,
[RTN_NAT] = -EINVAL,
[RTN_XRESOLVE] = -EINVAL,
@@ -1077,6 +1092,10 @@ static void ip6_rt_init_dst_reject(struc
rt->dst.output = ip6_pkt_prohibit_out;
rt->dst.input = ip6_pkt_prohibit;
break;
+ case RTN_POLICY_FAILED:
+ rt->dst.output = ip6_pkt_policy_failed_out;
+ rt->dst.input = ip6_pkt_policy_failed;
+ break;
case RTN_THROW:
case RTN_UNREACHABLE:
default:
@@ -4547,6 +4566,17 @@ static int ip6_pkt_prohibit_out(struct n
return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
}
+static int ip6_pkt_policy_failed(struct sk_buff *skb)
+{
+ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES);
+}
+
+static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+ skb->dev = skb_dst(skb)->dev;
+ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES);
+}
+
/*
* Allocate a dst for local (unicast / anycast) address.
*/
@@ -5040,7 +5070,8 @@ static int rtm_to_fib6_config(struct sk_
if (rtm->rtm_type == RTN_UNREACHABLE ||
rtm->rtm_type == RTN_BLACKHOLE ||
rtm->rtm_type == RTN_PROHIBIT ||
- rtm->rtm_type == RTN_THROW)
+ rtm->rtm_type == RTN_THROW ||
+ rtm->rtm_type == RTN_POLICY_FAILED)
cfg->fc_flags |= RTF_REJECT;
if (rtm->rtm_type == RTN_LOCAL)
@@ -6287,6 +6318,8 @@ static int ip6_route_dev_notify(struct n
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
net->ipv6.ip6_prohibit_entry->dst.dev = dev;
net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
+ net->ipv6.ip6_policy_failed_entry->dst.dev = dev;
+ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev);
net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
#endif
@@ -6298,6 +6331,7 @@ static int ip6_route_dev_notify(struct n
in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
+ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev);
in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
#endif
}
@@ -6489,6 +6523,8 @@ static int __net_init ip6_route_net_init
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
net->ipv6.fib6_has_custom_rules = false;
+
+
net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
sizeof(*net->ipv6.ip6_prohibit_entry),
GFP_KERNEL);
@@ -6499,11 +6535,21 @@ static int __net_init ip6_route_net_init
ip6_template_metrics, true);
INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached);
+ net->ipv6.ip6_policy_failed_entry =
+ kmemdup(&ip6_policy_failed_entry_template,
+ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL);
+ if (!net->ipv6.ip6_policy_failed_entry)
+ goto out_ip6_prohibit_entry;
+ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops;
+ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst,
+ ip6_template_metrics, true);
+ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached);
+
net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
sizeof(*net->ipv6.ip6_blk_hole_entry),
GFP_KERNEL);
if (!net->ipv6.ip6_blk_hole_entry)
- goto out_ip6_prohibit_entry;
+ goto out_ip6_policy_failed_entry;
net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
ip6_template_metrics, true);
@@ -6530,6 +6576,8 @@ out:
return ret;
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+out_ip6_policy_failed_entry:
+ kfree(net->ipv6.ip6_policy_failed_entry);
out_ip6_prohibit_entry:
kfree(net->ipv6.ip6_prohibit_entry);
out_ip6_null_entry:
@@ -6549,6 +6597,7 @@ static void __net_exit ip6_route_net_exi
kfree(net->ipv6.ip6_null_entry);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
kfree(net->ipv6.ip6_prohibit_entry);
+ kfree(net->ipv6.ip6_policy_failed_entry);
kfree(net->ipv6.ip6_blk_hole_entry);
#endif
dst_entries_destroy(&net->ipv6.ip6_dst_ops);
@@ -6632,6 +6681,9 @@ void __init ip6_route_init_special_entri
init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
+ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev;
+ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev =
+ in6_dev_get(init_net.loopback_dev);
#endif
}

View File

@@ -0,0 +1,50 @@
From: Jonas Gorski <jogo@openwrt.org>
Subject: net: provide defines for _POLICY_FAILED until all code is updated
Upstream introduced ICMPV6_POLICY_FAIL for code 5 of destination
unreachable, conflicting with our name.
Add appropriate defines to allow our code to build with the new
name until we have updated our local patches for older kernels
and userspace packages.
Signed-off-by: Jonas Gorski <jogo@openwrt.org>
---
include/uapi/linux/fib_rules.h | 2 ++
include/uapi/linux/icmpv6.h | 2 ++
include/uapi/linux/rtnetlink.h | 2 ++
3 files changed, 6 insertions(+)
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -89,6 +89,8 @@ enum {
__FR_ACT_MAX,
};
+#define FR_ACT_FAILED_POLICY FR_ACT_POLICY_FAILED
+
#define FR_ACT_MAX (__FR_ACT_MAX - 1)
#endif
--- a/include/uapi/linux/icmpv6.h
+++ b/include/uapi/linux/icmpv6.h
@@ -126,6 +126,8 @@ struct icmp6hdr {
#define ICMPV6_POLICY_FAIL 5
#define ICMPV6_REJECT_ROUTE 6
+#define ICMPV6_FAILED_POLICY ICMPV6_POLICY_FAIL
+
/*
* Codes for Time Exceeded
*/
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -269,6 +269,8 @@ enum {
__RTN_MAX
};
+#define RTN_FAILED_POLICY RTN_POLICY_FAILED
+
#define RTN_MAX (__RTN_MAX - 1)

View File

@@ -0,0 +1,627 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Tue, 23 Apr 2024 11:23:03 +0200
Subject: [PATCH] net: add TCP fraglist GRO support
When forwarding TCP after GRO, software segmentation is very expensive,
especially when the checksum needs to be recalculated.
One case where that's currently unavoidable is when routing packets over
PPPoE. Performance improves significantly when using fraglist GRO
implemented in the same way as for UDP.
Here's a measurement of running 2 TCP streams through a MediaTek MT7622
device (2-core Cortex-A53), which runs NAT with flow offload enabled from
one ethernet port to PPPoE on another ethernet port + cake qdisc set to
1Gbps.
rx-gro-list off: 630 Mbit/s, CPU 35% idle
rx-gro-list on: 770 Mbit/s, CPU 40% idle
Signe-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/net/gro.h
+++ b/include/net/gro.h
@@ -424,6 +424,7 @@ static inline __wsum ip6_gro_compute_pse
}
int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);
+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb);
/* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
static inline void gro_normal_list(struct napi_struct *napi)
@@ -446,5 +447,48 @@ static inline void gro_normal_one(struct
gro_normal_list(napi);
}
+/* This function is the alternative of 'inet_iif' and 'inet_sdif'
+ * functions in case we can not rely on fields of IPCB.
+ *
+ * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized.
+ * The caller must hold the RCU read lock.
+ */
+static inline void inet_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif)
+{
+ *iif = inet_iif(skb) ?: skb->dev->ifindex;
+ *sdif = 0;
+
+#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
+ if (netif_is_l3_slave(skb->dev)) {
+ struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev);
+
+ *sdif = *iif;
+ *iif = master ? master->ifindex : 0;
+ }
+#endif
+}
+
+/* This function is the alternative of 'inet6_iif' and 'inet6_sdif'
+ * functions in case we can not rely on fields of IP6CB.
+ *
+ * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized.
+ * The caller must hold the RCU read lock.
+ */
+static inline void inet6_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif)
+{
+ /* using skb->dev->ifindex because skb_dst(skb) is not initialized */
+ *iif = skb->dev->ifindex;
+ *sdif = 0;
+
+#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
+ if (netif_is_l3_slave(skb->dev)) {
+ struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev);
+
+ *sdif = *iif;
+ *iif = master ? master->ifindex : 0;
+ }
+#endif
+}
+
#endif /* _NET_IPV6_GRO_H */
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2057,7 +2057,10 @@ void tcp_v4_destroy_sock(struct sock *sk
struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
netdev_features_t features);
-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb);
+struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb);
+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th);
+struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th);
INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff));
INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb));
INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff));
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -290,6 +290,33 @@ done:
return 0;
}
+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
+{
+ if (unlikely(p->len + skb->len >= 65536))
+ return -E2BIG;
+
+ if (NAPI_GRO_CB(p)->last == p)
+ skb_shinfo(p)->frag_list = skb;
+ else
+ NAPI_GRO_CB(p)->last->next = skb;
+
+ skb_pull(skb, skb_gro_offset(skb));
+
+ NAPI_GRO_CB(p)->last = skb;
+ NAPI_GRO_CB(p)->count++;
+ p->data_len += skb->len;
+
+ /* sk ownership - if any - completely transferred to the aggregated packet */
+ skb->destructor = NULL;
+ skb->sk = NULL;
+ p->truesize += skb->truesize;
+ p->len += skb->len;
+
+ NAPI_GRO_CB(skb)->same_flow = 1;
+
+ return 0;
+}
+
static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb)
{
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -27,6 +27,70 @@ static void tcp_gso_tstamp(struct sk_buf
}
}
+static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
+ __be32 *oldip, __be32 newip,
+ __be16 *oldport, __be16 newport)
+{
+ struct tcphdr *th;
+ struct iphdr *iph;
+
+ if (*oldip == newip && *oldport == newport)
+ return;
+
+ th = tcp_hdr(seg);
+ iph = ip_hdr(seg);
+
+ inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
+ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
+ *oldport = newport;
+
+ csum_replace4(&iph->check, *oldip, newip);
+ *oldip = newip;
+}
+
+static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
+{
+ const struct tcphdr *th;
+ const struct iphdr *iph;
+ struct sk_buff *seg;
+ struct tcphdr *th2;
+ struct iphdr *iph2;
+
+ seg = segs;
+ th = tcp_hdr(seg);
+ iph = ip_hdr(seg);
+ th2 = tcp_hdr(seg->next);
+ iph2 = ip_hdr(seg->next);
+
+ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
+ iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
+ return segs;
+
+ while ((seg = seg->next)) {
+ th2 = tcp_hdr(seg);
+ iph2 = ip_hdr(seg);
+
+ __tcpv4_gso_segment_csum(seg,
+ &iph2->saddr, iph->saddr,
+ &th2->source, th->source);
+ __tcpv4_gso_segment_csum(seg,
+ &iph2->daddr, iph->daddr,
+ &th2->dest, th->dest);
+ }
+
+ return segs;
+}
+
+static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
+ if (IS_ERR(skb))
+ return skb;
+
+ return __tcpv4_gso_segment_list_csum(skb);
+}
+
static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
@@ -36,6 +100,9 @@ static struct sk_buff *tcp4_gso_segment(
if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
return ERR_PTR(-EINVAL);
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
+ return __tcp4_gso_segment_list(skb, features);
+
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
const struct iphdr *iph = ip_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
@@ -177,61 +244,76 @@ out:
return segs;
}
-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb)
+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th)
{
- struct sk_buff *pp = NULL;
+ struct tcphdr *th2;
struct sk_buff *p;
+
+ list_for_each_entry(p, head, list) {
+ if (!NAPI_GRO_CB(p)->same_flow)
+ continue;
+
+ th2 = tcp_hdr(p);
+ if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+
+ return p;
+ }
+
+ return NULL;
+}
+
+struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb)
+{
+ unsigned int thlen, hlen, off;
struct tcphdr *th;
- struct tcphdr *th2;
- unsigned int len;
- unsigned int thlen;
- __be32 flags;
- unsigned int mss = 1;
- unsigned int hlen;
- unsigned int off;
- int flush = 1;
- int i;
off = skb_gro_offset(skb);
hlen = off + sizeof(*th);
th = skb_gro_header(skb, hlen, off);
if (unlikely(!th))
- goto out;
+ return NULL;
thlen = th->doff * 4;
if (thlen < sizeof(*th))
- goto out;
+ return NULL;
hlen = off + thlen;
if (skb_gro_header_hard(skb, hlen)) {
th = skb_gro_header_slow(skb, hlen, off);
if (unlikely(!th))
- goto out;
+ return NULL;
}
skb_gro_pull(skb, thlen);
- len = skb_gro_len(skb);
- flags = tcp_flag_word(th);
-
- list_for_each_entry(p, head, list) {
- if (!NAPI_GRO_CB(p)->same_flow)
- continue;
+ return th;
+}
- th2 = tcp_hdr(p);
+struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th)
+{
+ unsigned int thlen = th->doff * 4;
+ struct sk_buff *pp = NULL;
+ struct sk_buff *p;
+ struct tcphdr *th2;
+ unsigned int len;
+ __be32 flags;
+ unsigned int mss = 1;
+ int flush = 1;
+ int i;
- if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
- NAPI_GRO_CB(p)->same_flow = 0;
- continue;
- }
+ len = skb_gro_len(skb);
+ flags = tcp_flag_word(th);
- goto found;
- }
- p = NULL;
- goto out_check_final;
+ p = tcp_gro_lookup(head, th);
+ if (!p)
+ goto out_check_final;
-found:
/* Include the IP ID check below from the inner most IP hdr */
+ th2 = tcp_hdr(p);
flush = NAPI_GRO_CB(p)->flush;
flush |= (__force int)(flags & TCP_FLAG_CWR);
flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
@@ -268,6 +350,19 @@ found:
flush |= p->decrypted ^ skb->decrypted;
#endif
+ if (unlikely(NAPI_GRO_CB(p)->is_flist)) {
+ flush |= (__force int)(flags ^ tcp_flag_word(th2));
+ flush |= skb->ip_summed != p->ip_summed;
+ flush |= skb->csum_level != p->csum_level;
+ flush |= !pskb_may_pull(skb, skb_gro_offset(skb));
+ flush |= NAPI_GRO_CB(p)->count >= 64;
+
+ if (flush || skb_gro_receive_list(p, skb))
+ mss = 1;
+
+ goto out_check_final;
+ }
+
if (flush || skb_gro_receive(p, skb)) {
mss = 1;
goto out_check_final;
@@ -289,7 +384,6 @@ out_check_final:
if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
pp = p;
-out:
NAPI_GRO_CB(skb)->flush |= (flush != 0);
return pp;
@@ -315,18 +409,58 @@ int tcp_gro_complete(struct sk_buff *skb
}
EXPORT_SYMBOL(tcp_gro_complete);
+static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th)
+{
+ const struct iphdr *iph;
+ struct sk_buff *p;
+ struct sock *sk;
+ struct net *net;
+ int iif, sdif;
+
+ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
+ return;
+
+ p = tcp_gro_lookup(head, th);
+ if (p) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ inet_get_iif_sdif(skb, &iif, &sdif);
+ iph = skb_gro_network_header(skb);
+ net = dev_net(skb->dev);
+ sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
+ iph->saddr, th->source,
+ iph->daddr, ntohs(th->dest),
+ iif, sdif);
+ NAPI_GRO_CB(skb)->is_flist = !sk;
+ if (sk)
+ sock_put(sk);
+}
+
INDIRECT_CALLABLE_SCOPE
struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
{
+ struct tcphdr *th;
+
/* Don't bother verifying checksum if we're going to flush anyway. */
if (!NAPI_GRO_CB(skb)->flush &&
skb_gro_checksum_validate(skb, IPPROTO_TCP,
- inet_gro_compute_pseudo)) {
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
- }
+ inet_gro_compute_pseudo))
+ goto flush;
+
+ th = tcp_gro_pull_header(skb);
+ if (!th)
+ goto flush;
- return tcp_gro_receive(head, skb);
+ tcp4_check_fraglist_gro(head, skb, th);
+
+ return tcp_gro_receive(head, skb, th);
+
+flush:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
}
INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff)
@@ -334,6 +468,15 @@ INDIRECT_CALLABLE_SCOPE int tcp4_gro_com
const struct iphdr *iph = ip_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
+ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
+ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV4;
+ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
+
+ __skb_incr_checksum_unnecessary(skb);
+
+ return 0;
+ }
+
th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr,
iph->daddr, 0);
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -425,33 +425,6 @@ out:
return segs;
}
-static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
-{
- if (unlikely(p->len + skb->len >= 65536))
- return -E2BIG;
-
- if (NAPI_GRO_CB(p)->last == p)
- skb_shinfo(p)->frag_list = skb;
- else
- NAPI_GRO_CB(p)->last->next = skb;
-
- skb_pull(skb, skb_gro_offset(skb));
-
- NAPI_GRO_CB(p)->last = skb;
- NAPI_GRO_CB(p)->count++;
- p->data_len += skb->len;
-
- /* sk ownership - if any - completely transferred to the aggregated packet */
- skb->destructor = NULL;
- skb->sk = NULL;
- p->truesize += skb->truesize;
- p->len += skb->len;
-
- NAPI_GRO_CB(skb)->same_flow = 1;
-
- return 0;
-}
-
#define UDP_GRO_CNT_MAX 64
static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -7,24 +7,67 @@
*/
#include <linux/indirect_call_wrapper.h>
#include <linux/skbuff.h>
+#include <net/inet6_hashtables.h>
#include <net/gro.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/ip6_checksum.h>
#include "ip6_offload.h"
+static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+ const struct ipv6hdr *hdr;
+ struct sk_buff *p;
+ struct sock *sk;
+ struct net *net;
+ int iif, sdif;
+
+ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
+ return;
+
+ p = tcp_gro_lookup(head, th);
+ if (p) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ inet6_get_iif_sdif(skb, &iif, &sdif);
+ hdr = skb_gro_network_header(skb);
+ net = dev_net(skb->dev);
+ sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
+ &hdr->saddr, th->source,
+ &hdr->daddr, ntohs(th->dest),
+ iif, sdif);
+ NAPI_GRO_CB(skb)->is_flist = !sk;
+ if (sk)
+ sock_put(sk);
+#endif /* IS_ENABLED(CONFIG_IPV6) */
+}
+
INDIRECT_CALLABLE_SCOPE
struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
{
+ struct tcphdr *th;
+
/* Don't bother verifying checksum if we're going to flush anyway. */
if (!NAPI_GRO_CB(skb)->flush &&
skb_gro_checksum_validate(skb, IPPROTO_TCP,
- ip6_gro_compute_pseudo)) {
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
- }
+ ip6_gro_compute_pseudo))
+ goto flush;
- return tcp_gro_receive(head, skb);
+ th = tcp_gro_pull_header(skb);
+ if (!th)
+ goto flush;
+
+ tcp6_check_fraglist_gro(head, skb, th);
+
+ return tcp_gro_receive(head, skb, th);
+
+flush:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
}
INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
@@ -32,6 +75,15 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
const struct ipv6hdr *iph = ipv6_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
+ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
+ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV6;
+ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
+
+ __skb_incr_checksum_unnecessary(skb);
+
+ return 0;
+ }
+
th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
&iph->daddr, 0);
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
@@ -39,6 +91,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
return tcp_gro_complete(skb);
}
+static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
+ __be16 *oldport, __be16 newport)
+{
+ struct tcphdr *th;
+
+ if (*oldport == newport)
+ return;
+
+ th = tcp_hdr(seg);
+ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
+ *oldport = newport;
+}
+
+static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
+{
+ const struct tcphdr *th;
+ const struct ipv6hdr *iph;
+ struct sk_buff *seg;
+ struct tcphdr *th2;
+ struct ipv6hdr *iph2;
+
+ seg = segs;
+ th = tcp_hdr(seg);
+ iph = ipv6_hdr(seg);
+ th2 = tcp_hdr(seg->next);
+ iph2 = ipv6_hdr(seg->next);
+
+ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
+ ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
+ ipv6_addr_equal(&iph->daddr, &iph2->daddr))
+ return segs;
+
+ while ((seg = seg->next)) {
+ th2 = tcp_hdr(seg);
+ iph2 = ipv6_hdr(seg);
+
+ iph2->saddr = iph->saddr;
+ iph2->daddr = iph->daddr;
+ __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
+ __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
+ }
+
+ return segs;
+}
+
+static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
+ if (IS_ERR(skb))
+ return skb;
+
+ return __tcpv6_gso_segment_list_csum(skb);
+}
+
static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
@@ -50,6 +157,9 @@ static struct sk_buff *tcp6_gso_segment(
if (!pskb_may_pull(skb, sizeof(*th)))
return ERR_PTR(-EINVAL);
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
+ return __tcp6_gso_segment_list(skb, features);
+
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);

View File

@@ -0,0 +1,75 @@
From 8585756342caa6d27008d1ad0c18023e4211a40a Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Wed, 13 Jul 2022 12:22:48 +0200
Subject: [PATCH] of/of_net: write back netdev MAC-address to device-tree
The label-mac logic relies on the mac-address property of a netdev
devices of-node. However, the mac address can also be stored as a
different property or read from e.g. an mtd device.
Create this node when reading a mac-address from OF if it does not
already exist and copy the mac-address used for the device to this
property. This way, the MAC address can be accessed using procfs.
---
net/core/of_net.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
--- a/net/core/of_net.c
+++ b/net/core/of_net.c
@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct
return 0;
}
+static int of_add_mac_address(struct device_node *np, u8* addr)
+{
+ struct property *prop;
+
+ prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+ if (!prop)
+ return -ENOMEM;
+
+ prop->name = "mac-address";
+ prop->length = ETH_ALEN;
+ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL);
+ if (!prop->value || of_update_property(np, prop))
+ goto free;
+
+ return 0;
+free:
+ kfree(prop->value);
+ kfree(prop);
+ return -ENOMEM;
+}
+
/**
* of_get_mac_address()
* @np: Caller's Device Node
@@ -130,17 +151,23 @@ int of_get_mac_address(struct device_nod
ret = of_get_mac_addr(np, "mac-address", addr);
if (!ret)
- return 0;
+ goto found;
ret = of_get_mac_addr(np, "local-mac-address", addr);
if (!ret)
- return 0;
+ goto found;
ret = of_get_mac_addr(np, "address", addr);
if (!ret)
- return 0;
+ goto found;
- return of_get_mac_addr_nvmem(np, addr);
+ ret = of_get_mac_addr_nvmem(np, addr);
+ if (ret)
+ return ret;
+
+found:
+ ret = of_add_mac_address(np, addr);
+ return ret;
}
EXPORT_SYMBOL(of_get_mac_address);

View File

@@ -0,0 +1,110 @@
From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu, 25 Jan 2018 12:58:55 +0100
Subject: [PATCH] netfilter: nft_flow_offload: handle netdevice events from
nf_flow_table
Move the code that deals with device events to the core.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -659,6 +659,23 @@ static struct pernet_operations nf_flow_
.exit_batch = nf_flow_table_pernet_exit,
};
+static int nf_flow_table_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+ if (event != NETDEV_DOWN)
+ return NOTIFY_DONE;
+
+ nf_flow_table_cleanup(dev);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block flow_offload_netdev_notifier = {
+ .notifier_call = nf_flow_table_netdev_event,
+};
+
static int __init nf_flow_table_module_init(void)
{
int ret;
@@ -671,8 +688,14 @@ static int __init nf_flow_table_module_i
if (ret)
goto out_offload;
+ ret = register_netdevice_notifier(&flow_offload_netdev_notifier);
+ if (ret)
+ goto out_offload_init;
+
return 0;
+out_offload_init:
+ nf_flow_table_offload_exit();
out_offload:
unregister_pernet_subsys(&nf_flow_table_net_ops);
return ret;
@@ -680,6 +703,7 @@ out_offload:
static void __exit nf_flow_table_module_exit(void)
{
+ unregister_netdevice_notifier(&flow_offload_netdev_notifier);
nf_flow_table_offload_exit();
unregister_pernet_subsys(&nf_flow_table_net_ops);
}
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -479,47 +479,14 @@ static struct nft_expr_type nft_flow_off
.owner = THIS_MODULE,
};
-static int flow_offload_netdev_event(struct notifier_block *this,
- unsigned long event, void *ptr)
-{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-
- if (event != NETDEV_DOWN)
- return NOTIFY_DONE;
-
- nf_flow_table_cleanup(dev);
-
- return NOTIFY_DONE;
-}
-
-static struct notifier_block flow_offload_netdev_notifier = {
- .notifier_call = flow_offload_netdev_event,
-};
-
static int __init nft_flow_offload_module_init(void)
{
- int err;
-
- err = register_netdevice_notifier(&flow_offload_netdev_notifier);
- if (err)
- goto err;
-
- err = nft_register_expr(&nft_flow_offload_type);
- if (err < 0)
- goto register_expr;
-
- return 0;
-
-register_expr:
- unregister_netdevice_notifier(&flow_offload_netdev_notifier);
-err:
- return err;
+ return nft_register_expr(&nft_flow_offload_type);
}
static void __exit nft_flow_offload_module_exit(void)
{
nft_unregister_expr(&nft_flow_offload_type);
- unregister_netdevice_notifier(&flow_offload_netdev_notifier);
}
module_init(nft_flow_offload_module_init);

View File

@@ -0,0 +1,29 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Thu, 31 Aug 2023 21:48:38 +0200
Subject: [PATCH] netfilter: nf_tables: ignore -EOPNOTSUPP on flowtable device
offload setup
On many embedded devices, it is common to configure flowtable offloading for
a mix of different devices, some of which have hardware offload support and
some of which don't.
The current code limits the ability of user space to properly set up such a
configuration by only allowing adding devices with hardware offload support to
a offload-enabled flowtable.
Given that offload-enabled flowtables also imply fallback to pure software
offloading, this limitation makes little sense.
Fix it by not bailing out when the offload setup returns -EOPNOTSUPP
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -7958,7 +7958,7 @@ static int nft_register_flowtable_net_ho
err = flowtable->data.type->setup(&flowtable->data,
hook->ops.dev,
FLOW_BLOCK_BIND);
- if (err < 0)
+ if (err < 0 && err != -EOPNOTSUPP)
goto err_unregister_net_hooks;
err = nf_register_net_hook(net, &hook->ops);

View File

@@ -0,0 +1,21 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 21 Mar 2022 20:39:59 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI
This can improve performance under load by ensuring that NAPI processing is
not pinned on CPU 0.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -4940,6 +4940,8 @@ static int mtk_probe(struct platform_dev
* for NAPI to work
*/
init_dummy_netdev(&eth->dummy_dev);
+ eth->dummy_dev.threaded = 1;
+ strcpy(eth->dummy_dev.name, "mtk_eth");
netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);

View File

@@ -0,0 +1,38 @@
From: Gabor Juhos <juhosg@openwrt.org>
Subject: generic: add detach callback to struct phy_driver
lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
---
drivers/net/phy/phy_device.c | 3 +++
include/linux/phy.h | 6 ++++++
2 files changed, 9 insertions(+)
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1852,6 +1852,9 @@ void phy_detach(struct phy_device *phyde
struct module *ndev_owner = NULL;
struct mii_bus *bus;
+ if (phydev->drv && phydev->drv->detach)
+ phydev->drv->detach(phydev);
+
if (phydev->sysfs_links) {
if (dev)
sysfs_remove_link(&dev->dev.kobj, "phydev");
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -903,6 +903,12 @@ struct phy_driver {
/** @handle_interrupt: Override default interrupt handling */
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
+ /*
+ * Called before an ethernet device is detached
+ * from the PHY.
+ */
+ void (*detach)(struct phy_device *phydev);
+
/** @remove: Clears up any memory if needed */
void (*remove)(struct phy_device *phydev);

View File

@@ -0,0 +1,24 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Wed, 14 Feb 2024 15:24:41 +0100
Subject: [PATCH] netfilter: nf_tables: fix bidirectional offload regression
Commit 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
made unidirectional flow offload possible, while completely ignoring (and
breaking) bidirectional flow offload for nftables.
Add the missing flag that was left out as an exercise for the reader :)
Cc: Vlad Buslov <vladbu@nvidia.com>
Fixes: 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -361,6 +361,7 @@ static void nft_flow_offload_eval(const
ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
}
+ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
ret = flow_offload_add(flowtable, flow);
if (ret < 0)
goto err_flow_add;

View File

@@ -0,0 +1,28 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Fri, 6 May 2022 21:38:42 +0200
Subject: [PATCH] net: dsa: tag_mtk: add padding for tx packets
Padding for transmitted packets needs to account for the special tag.
With not enough padding, garbage bytes are inserted by the switch at the
end of small packets.
Fixes: 5cd8985a1909 ("net-next: dsa: add Mediatek tag RX/TX handler")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/dsa/tag_mtk.c
+++ b/net/dsa/tag_mtk.c
@@ -27,6 +27,13 @@ static struct sk_buff *mtk_tag_xmit(stru
skb_set_queue_mapping(skb, dp->index);
+ /* The Ethernet switch we are interfaced with needs packets to be at
+ * least 64 bytes (including FCS) otherwise their padding might be
+ * corrupted. With tags enabled, we need to make sure that packets are
+ * at least 68 bytes (including FCS and tag).
+ */
+ eth_skb_pad(skb);
+
/* Build the special tag after the MAC Source Address. If VLAN header
* is present, it's required that VLAN header and special tag is
* being combined. Only in this way we can allow the switch can parse

View File

@@ -0,0 +1,174 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Fri, 27 Aug 2021 12:22:32 +0200
Subject: [PATCH] bridge: add knob for filtering rx/tx BPDU packets on a port
Some devices (e.g. wireless APs) can't have devices behind them be part of
a bridge topology with redundant links, due to address limitations.
Additionally, broadcast traffic on these devices is somewhat expensive, due to
the low data rate and wakeups of clients in powersave mode.
This knob can be used to ensure that BPDU packets are never sent or forwarded
to/from these devices
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -59,6 +59,7 @@ struct br_ip_list {
#define BR_MRP_LOST_IN_CONT BIT(19)
#define BR_TX_FWD_OFFLOAD BIT(20)
#define BR_PORT_LOCKED BIT(21)
+#define BR_BPDU_FILTER BIT(22)
#define BR_DEFAULT_AGEING_TIME (300 * HZ)
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -199,6 +199,7 @@ out:
void br_flood(struct net_bridge *br, struct sk_buff *skb,
enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
{
+ const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *prev = NULL;
struct net_bridge_port *p;
@@ -214,6 +215,10 @@ void br_flood(struct net_bridge *br, str
case BR_PKT_MULTICAST:
if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
continue;
+ if ((p->flags & BR_BPDU_FILTER) &&
+ unlikely(is_link_local_ether_addr(dest) &&
+ dest[5] == 0))
+ continue;
break;
case BR_PKT_BROADCAST:
if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -349,6 +349,8 @@ static rx_handler_result_t br_handle_fra
fwd_mask |= p->group_fwd_mask;
switch (dest[5]) {
case 0x00: /* Bridge Group Address */
+ if (p->flags & BR_BPDU_FILTER)
+ goto drop;
/* If STP is turned off,
then must forward to keep loop detection */
if (p->br->stp_enabled == BR_NO_STP ||
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -240,6 +240,7 @@ BRPORT_ATTR_FLAG(multicast_flood, BR_MCA
BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD);
BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS);
BRPORT_ATTR_FLAG(isolated, BR_ISOLATED);
+BRPORT_ATTR_FLAG(bpdu_filter, BR_BPDU_FILTER);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -292,6 +293,7 @@ static const struct brport_attribute *br
&brport_attr_group_fwd_mask,
&brport_attr_neigh_suppress,
&brport_attr_isolated,
+ &brport_attr_bpdu_filter,
&brport_attr_backup_port,
NULL
};
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -80,7 +80,8 @@ void br_send_config_bpdu(struct net_brid
{
unsigned char buf[35];
- if (p->br->stp_enabled != BR_KERNEL_STP)
+ if (p->br->stp_enabled != BR_KERNEL_STP ||
+ (p->flags & BR_BPDU_FILTER))
return;
buf[0] = 0;
@@ -127,7 +128,8 @@ void br_send_tcn_bpdu(struct net_bridge_
{
unsigned char buf[4];
- if (p->br->stp_enabled != BR_KERNEL_STP)
+ if (p->br->stp_enabled != BR_KERNEL_STP ||
+ (p->flags & BR_BPDU_FILTER))
return;
buf[0] = 0;
@@ -172,6 +174,9 @@ void br_stp_rcv(const struct stp_proto *
if (!(br->dev->flags & IFF_UP))
goto out;
+ if (p->flags & BR_BPDU_FILTER)
+ goto out;
+
if (p->state == BR_STATE_DISABLED)
goto out;
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -561,6 +561,7 @@ enum {
IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
IFLA_BRPORT_LOCKED,
+ IFLA_BRPORT_BPDU_FILTER,
__IFLA_BRPORT_MAX
};
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -188,6 +188,7 @@ static inline size_t br_port_info_size(v
+ nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */
+ nla_total_size(1) /* IFLA_BRPORT_ISOLATED */
+ nla_total_size(1) /* IFLA_BRPORT_LOCKED */
+ + nla_total_size(1) /* IFLA_BRPORT_BPDU_FILTER */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */
+ nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */
+ nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */
@@ -274,7 +275,8 @@ static int br_port_fill_attrs(struct sk_
nla_put_u8(skb, IFLA_BRPORT_MRP_IN_OPEN,
!!(p->flags & BR_MRP_LOST_IN_CONT)) ||
nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)) ||
- nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)))
+ nla_put_u8(skb, IFLA_BRPORT_LOCKED, !!(p->flags & BR_PORT_LOCKED)) ||
+ nla_put_u8(skb, IFLA_BRPORT_BPDU_FILTER, !!(p->flags & BR_BPDU_FILTER)))
return -EMSGSIZE;
timerval = br_timer_value(&p->message_age_timer);
@@ -879,6 +881,7 @@ static const struct nla_policy br_port_p
[IFLA_BRPORT_LOCKED] = { .type = NLA_U8 },
[IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 },
[IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 },
+ [IFLA_BRPORT_BPDU_FILTER] = { .type = NLA_U8 },
};
/* Change the state of the port and notify spanning tree */
@@ -944,6 +947,7 @@ static int br_setport(struct net_bridge_
br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS);
br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED);
br_set_port_flag(p, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED);
+ br_set_port_flag(p, tb, IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER);
changed_mask = old_flags ^ p->flags;
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -57,7 +57,7 @@
#include "dev.h"
#define RTNL_MAX_TYPE 50
-#define RTNL_SLAVE_MAX_TYPE 40
+#define RTNL_SLAVE_MAX_TYPE 41
struct rtnl_link {
rtnl_doit_func doit;
@@ -4840,7 +4840,9 @@ int ndo_dflt_bridge_getlink(struct sk_bu
brport_nla_put_flag(skb, flags, mask,
IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) ||
brport_nla_put_flag(skb, flags, mask,
- IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) {
+ IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_BPDU_FILTER, BR_BPDU_FILTER)) {
nla_nest_cancel(skb, protinfo);
goto nla_put_failure;
}

View File

@@ -0,0 +1,86 @@
From 3b4329230db8750bea7a56ef07f07cbbf5fc6c5a Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Tue, 4 Jul 2023 22:50:12 +0200
Subject: [PATCH 19/20] net: dsa: qca8k: implement lag_fdb_add/del ops
Implement lag_fdb_add/del ops to correctly support using LAG interface.
Qca8k switch supports declaring fdb entry for link aggregation by simply
setting the DES_PORT bits to all the LAG member.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 2 ++
drivers/net/dsa/qca/qca8k-common.c | 48 ++++++++++++++++++++++++++++++
drivers/net/dsa/qca/qca8k.h | 6 ++++
3 files changed, 56 insertions(+)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -2015,6 +2015,8 @@ static const struct dsa_switch_ops qca8k
.port_fdb_add = qca8k_port_fdb_add,
.port_fdb_del = qca8k_port_fdb_del,
.port_fdb_dump = qca8k_port_fdb_dump,
+ .lag_fdb_add = qca8k_lag_fdb_add,
+ .lag_fdb_del = qca8k_lag_fdb_del,
.port_mdb_add = qca8k_port_mdb_add,
.port_mdb_del = qca8k_port_mdb_del,
.port_mirror_add = qca8k_port_mirror_add,
--- a/drivers/net/dsa/qca/qca8k-common.c
+++ b/drivers/net/dsa/qca/qca8k-common.c
@@ -1215,6 +1215,42 @@ int qca8k_port_lag_leave(struct dsa_swit
return qca8k_lag_refresh_portmap(ds, port, lag, true);
}
+int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db)
+{
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ struct dsa_port *dp;
+ u16 port_mask = 0;
+
+ /* Set the vid to the port vlan id if no vid is set */
+ if (!vid)
+ vid = QCA8K_PORT_VID_DEF;
+
+ dsa_lag_foreach_port(dp, ds->dst, &lag)
+ port_mask |= BIT(dp->index);
+
+ return qca8k_port_fdb_insert(priv, addr, port_mask, vid);
+}
+
+int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db)
+{
+ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
+ struct dsa_port *dp;
+ u16 port_mask = 0;
+
+ /* Set the vid to the port vlan id if no vid is set */
+ if (!vid)
+ vid = QCA8K_PORT_VID_DEF;
+
+ dsa_lag_foreach_port(dp, ds->dst, &lag)
+ port_mask |= BIT(dp->index);
+
+ return qca8k_fdb_del(priv, addr, port_mask, vid);
+}
+
int qca8k_read_switch_id(struct qca8k_priv *priv)
{
u32 val;
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -590,5 +590,11 @@ int qca8k_port_lag_join(struct dsa_switc
struct netlink_ext_ack *extack);
int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
struct dsa_lag lag);
+int qca8k_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db);
+int qca8k_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db);
#endif /* __QCA8K_H */

View File

@@ -0,0 +1,37 @@
From b954d61d9ecfa64450fc178586719dc2a95b92a7 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Tue, 20 Jun 2023 21:48:24 +0200
Subject: [PATCH 3/4] net: dsa: qca8k: enable flooding to both CPU port
To permit a multi-CPU setup, flood all unknown frames to all CPU ports.
Each CPU port should have correct LOOKUP MEMBER configuration to
prevent receiving duplicate packets from user ports.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1904,15 +1904,12 @@ qca8k_setup(struct dsa_switch *ds)
}
}
- /* Forward all unknown frames to CPU port for Linux processing
- * Notice that in multi-cpu config only one port should be set
- * for igmp, unknown, multicast and broadcast packet
- */
+ /* Forward all unknown frames to CPU port for Linux processing */
ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1,
- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) |
- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) |
- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) |
- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));
+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, dsa_cpu_ports(ds)) |
+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, dsa_cpu_ports(ds)) |
+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, dsa_cpu_ports(ds)) |
+ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, dsa_cpu_ports(ds)));
if (ret)
return ret;

Some files were not shown because too many files have changed in this diff Show More