6.18.14-rc1 review
Subsystems (5)
Greg Kroah-Hartman
maintainerPATCH 259/641 netfilter: nft_compat: add more restrictions on netlink attributes
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit cda26c645946b08f070f20c166d4736767e4a805 ]
As far as I can see nothing bad can happen when NFTA_TARGET/MATCH_NAME are too large because this calls x_tables helpers which check for the length, but it seems better to already reject it during netlink parsing.
Rest of the changes avoid silent u8/u16 truncations.
For _TYPE, its expected to be only 1 or 0. In x_tables world, this variable is set by kernel, for IPT_SO_GET_REVISION_TARGET its 1, for all others its set to 0.
As older versions of nf_tables permitted any value except 1 to mean 'match', keep this as-is but sanitize the value for consistency.
--- net/netfilter/nft_compat.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/netfilter/nft_compat.c +10 -3
@@ -134,7 +134,8 @@ static void nft_target_eval_bridge(const struct nft_expr *expr, }static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX +1 ] = { - [NFTA_TARGET_NAME ] = { .type =NLA_NUL_STRING }, + [NFTA_TARGET_NAME ] = { .type =NLA_NUL_STRING , + .len =XT_EXTENSION_MAXNAMELEN , }, [NFTA_TARGET_REV ] =NLA_POLICY_MAX (NLA_BE32 ,255 ), [NFTA_TARGET_INFO ] = { .type =NLA_BINARY }, };
@@ -434,7 +435,8 @@ static void nft_match_eval(const struct nft_expr *expr, }static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX +1 ] = { - [NFTA_MATCH_NAME ] = { .type =NLA_NUL_STRING }, + [NFTA_MATCH_NAME ] = { .type =NLA_NUL_STRING , + .len =XT_EXTENSION_MAXNAMELEN }, [NFTA_MATCH_REV ] =NLA_POLICY_MAX (NLA_BE32 ,255 ), [NFTA_MATCH_INFO ] = { .type =NLA_BINARY }, };
@@ -693,7 +695,12 @@ static int nfnl_compat_get_rcu(struct sk_buff *skb, name =nla_data (tb[NFTA_COMPAT_NAME ]); rev =ntohl (nla_get_be32 (tb[NFTA_COMPAT_REV ])); - target =ntohl (nla_get_be32 (tb[NFTA_COMPAT_TYPE ])); +/* x_tables api checks for 'target == 1' to mean target, +* everything else means 'match'. +* In x_tables world, the number is set by kernel, not +* userspace. +*/ + target =nla_get_be32 (tb[NFTA_COMPAT_TYPE ]) ==htonl (1 );switch (family) {case AF_INET :
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 260/641 netfilter: nf_conncount: fix tracking of connections from localhost
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fernando Fernandez Mancera <[email protected]>
[ Upstream commit de8a70cefcb26cdceaafdc5ac144712681419c29 ]
Since commit be102eb6a0e7 ("netfilter: nf_conncount: rework API to use sk_buff directly"), we skip the adding and trigger a GC when the ct is confirmed. For connections originated from local to local it doesn't work because the connection is confirmed on POSTROUTING, therefore tracking on the INPUT hook is always skipped.
In order to fix this, we check whether skb input ifindex is set to loopback ifindex. If it is then we fallback on a GC plus track operation skipping the optimization. This fallback is necessary to avoid duplicated tracking of a packet train e.g 10 UDP datagrams sent on a burst when initiating the connection.
Tested with xt_connlimit/nft_connlimit and OVS limit and with a HTTP server and iperf3 on UDP mode.
--- net/netfilter/nf_conncount.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
net/netfilter/nf_conncount.c +13 -2
@@ -179,14 +179,25 @@ static int __nf_conncount_add(struct net *net,return -ENOENT ;if (ct &&nf_ct_is_confirmed (ct)) { - err = -EEXIST ; - goto out_put; +/* local connections are confirmed in postrouting so confirmation +* might have happened before hitting connlimit +*/ +if (skb->skb_iif !=LOOPBACK_IFINDEX ) { + err = -EEXIST ; + goto out_put; + } + +/* this is likely a local connection, skip optimization to avoid +* adding duplicates from a 'packet train' +*/ + goto check_connections; }if ((u32 )jiffies == list->last_gc && (list->count - list->last_gc_count) <CONNCOUNT_GC_MAX_COLLECT ) goto add_new_node; +check_connections:/* check the saved connections */ list_for_each_entry_safe (conn, conn_n, &list->head, node) {if (collect >CONNCOUNT_GC_MAX_COLLECT )
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 261/641 kallsyms/bpf: rename __bpf_address_lookup() to bpf_address_lookup()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Mladek <[email protected]>
[ Upstream commit cd6735896d0343942cf3dafb48ce32eb79341990 ]
bpf_address_lookup() has been used only in kallsyms_lookup_buildid(). It was supposed to set @modname and @modbuildid when the symbol was in a module.
But it always just cleared @modname because BPF symbols were never in a module. And it did not clear @modbuildid because the pointer was not passed.
The wrapper is no longer needed. Both @modname and @modbuildid are now always initialized to NULL in kallsyms_lookup_buildid().
Remove the wrapper and rename __bpf_address_lookup() to bpf_address_lookup() because this variant is used everywhere.
--- arch/arm64/net/bpf_jit_comp.c | 2 +- arch/loongarch/net/bpf_jit.c | 2 +- arch/powerpc/net/bpf_jit_comp.c | 2 +- include/linux/filter.h | 26 ++++---------------------- kernel/bpf/core.c | 4 ++-- kernel/kallsyms.c | 5 ++--- 6 files changed, 11 insertions(+), 30 deletions(-)
arch/arm64/net/bpf_jit_comp.c +1 -1
@@ -2939,7 +2939,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,u64 plt_target =0ULL ;bool poking_bpf_entry; -if (!__bpf_address_lookup ((unsigned long )ip, &size, &offset, namebuf)) +if (!bpf_address_lookup ((unsigned long )ip, &size, &offset, namebuf))/* Only poking bpf text is supported. Since kernel function * entry is set up by ftrace, we reply on ftrace to poke kernel * functions.
arch/loongarch/net/bpf_jit.c +1 -1
@@ -1318,7 +1318,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,/* Only poking bpf text is supported. Since kernel function entry * is set up by ftrace, we rely on ftrace to poke kernel functions. */ -if (!__bpf_address_lookup ((unsigned long )ip, &size, &offset, namebuf)) +if (!bpf_address_lookup ((unsigned long )ip, &size, &offset, namebuf))return -ENOTSUPP ; image = ip - offset;
arch/powerpc/net/bpf_jit_comp.c +1 -1
@@ -1122,7 +1122,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type, branch_flags = poke_type ==BPF_MOD_CALL ?BRANCH_SET_LINK :0 ;/* We currently only support poking bpf programs */ -if (!__bpf_address_lookup (bpf_func, &size, &offset, name)) { +if (!bpf_address_lookup (bpf_func, &size, &offset, name)) {pr_err ("%s (0x%lx): kernel/modules are not supported\n" , __func__, bpf_func);return -EOPNOTSUPP ; }
include/linux/filter.h +4 -22
@@ -1375,24 +1375,13 @@ static inline bool bpf_jit_kallsyms_enabled(void)return false; } -int __bpf_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char *sym); +int bpf_address_lookup (unsigned long addr,unsigned long *size, +unsigned long *off,char *sym);bool is_bpf_text_address (unsigned long addr);int bpf_get_kallsym (unsigned int symnum,unsigned long *value,char *type,char *sym);struct bpf_prog *bpf_prog_ksym_find (unsigned long addr); -static inline int -bpf_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char **modname,char *sym) -{ -int ret =__bpf_address_lookup (addr, size, off, sym); - -if (ret && modname) - *modname =NULL ; -return ret; -} -void bpf_prog_kallsyms_add (struct bpf_prog *fp);void bpf_prog_kallsyms_del (struct bpf_prog *fp);
@@ -1431,8 +1420,8 @@ static inline bool bpf_jit_kallsyms_enabled(void) }static inline int -__bpf_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char *sym) +bpf_address_lookup (unsigned long addr,unsigned long *size, +unsigned long *off,char *sym) {return 0 ; }
@@ -1453,13 +1442,6 @@ static inline struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)return NULL ; } -static inline int -bpf_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char **modname,char *sym) -{ -return 0 ; -} -static inline void bpf_prog_kallsyms_add (struct bpf_prog *fp) { }
kernel/bpf/core.c +2 -2
@@ -713,8 +713,8 @@ static struct bpf_ksym *bpf_ksym_find(unsigned long addr) return n ? container_of(n, struct bpf_ksym, tnode) : NULL; } -int __bpf_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char *sym) +int bpf_address_lookup (unsigned long addr,unsigned long *size, +unsigned long *off,char *sym) {struct bpf_ksym *ksym;int ret =0 ;
kernel/kallsyms.c +2 -3
@@ -345,7 +345,7 @@ int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize,return 1 ; }return !!module_address_lookup (addr, symbolsize, offset,NULL ,NULL , namebuf) || - !!__bpf_address_lookup (addr, symbolsize, offset, namebuf); + !!bpf_address_lookup (addr, symbolsize, offset, namebuf); } static int kallsyms_lookup_buildid(unsigned long addr,
@@ -377,8 +377,7 @@ static int kallsyms_lookup_buildid(unsigned long addr, ret =module_address_lookup (addr, symbolsize, offset, modname, modbuildid, namebuf);if (!ret) - ret =bpf_address_lookup (addr, symbolsize, - offset, modname, namebuf); + ret =bpf_address_lookup (addr, symbolsize, offset, namebuf);if (!ret) ret = ftrace_mod_address_lookup(addr, symbolsize,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 262/641 module: add helper function for reading module_buildid()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Mladek <[email protected]>
[ Upstream commit acfdbb4ab2910ff6f03becb569c23ac7b2223913 ]
Add a helper function for reading the optional "build_id" member of struct module. It is going to be used also in ftrace_mod_address_lookup().
Use "#ifdef" instead of "#if IS_ENABLED()" to match the declaration of the optional field in struct module.
Stable-dep-of: e8a1e7eaa19d ("kallsyms/ftrace: set module buildid in ftrace_mod_address_lookup()")
--- include/linux/module.h | 9 +++++++++ kernel/module/kallsyms.c | 9 ++------- 2 files changed, 11 insertions(+), 7 deletions(-)
include/linux/module.h +9 -0
@@ -747,6 +747,15 @@ static inline void __module_get(struct module *module) __mod ? __mod->name :"kernel" ; \ }) +static inline const unsigned char *module_buildid (struct module *mod) +{ +#ifdef CONFIG_STACKTRACE_BUILD_ID +return mod->build_id; +#else +return NULL ; +#endif +} +/* Dereference module function descriptor */ void *dereference_module_function_descriptor (struct module *mod,void *ptr);
kernel/module/kallsyms.c +2 -7
@@ -334,13 +334,8 @@ int module_address_lookup(unsigned long addr,if (mod) {if (modname) *modname = mod->name; -if (modbuildid) { -#if IS_ENABLED (CONFIG_STACKTRACE_BUILD_ID ) - *modbuildid = mod->build_id; -#else - *modbuildid =NULL ; -#endif - } +if (modbuildid) + *modbuildid =module_buildid (mod); sym =find_kallsyms_symbol (mod, addr, size, offset);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 263/641 kallsyms/ftrace: set module buildid in ftrace_mod_address_lookup()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Mladek <[email protected]>
[ Upstream commit e8a1e7eaa19d0b757b06a2f913e3eeb4b1c002c6 ]
__sprint_symbol() might access an invalid pointer when kallsyms_lookup_buildid() returns a symbol found by ftrace_mod_address_lookup().
The ftrace lookup function must set both @modname and @modbuildid the same way as module_address_lookup().
--- include/linux/ftrace.h | 6 ++++-- kernel/kallsyms.c | 4 ++-- kernel/trace/ftrace.c | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-)
include/linux/ftrace.h +4 -2
@@ -87,11 +87,13 @@ struct ftrace_hash;defined (CONFIG_DYNAMIC_FTRACE) intftrace_mod_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char **modname,char *sym); +unsigned long *off,char **modname, +const unsigned char **modbuildid,char *sym);#else static inline int ftrace_mod_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char **modname,char *sym) +unsigned long *off,char **modname, +const unsigned char **modbuildid,char *sym) {return 0 ; }
kernel/kallsyms.c +2 -2
@@ -380,8 +380,8 @@ static int kallsyms_lookup_buildid(unsigned long addr, ret =bpf_address_lookup (addr, symbolsize, offset, namebuf);if (!ret) - ret =ftrace_mod_address_lookup (addr, symbolsize, - offset, modname, namebuf); + ret =ftrace_mod_address_lookup (addr, symbolsize, offset, + modname, modbuildid, namebuf);return ret; }
kernel/trace/ftrace.c +4 -1
@@ -7709,7 +7709,8 @@ ftrace_func_address_lookup(struct ftrace_mod_map *mod_map,int ftrace_mod_address_lookup (unsigned long addr,unsigned long *size, -unsigned long *off,char **modname,char *sym) +unsigned long *off,char **modname, +const unsigned char **modbuildid,char *sym) {struct ftrace_mod_map *mod_map;int ret =0 ;
@@ -7721,6 +7722,8 @@ ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,if (ret) {if (modname) *modname = mod_map->mod->name; +if (modbuildid) + *modbuildid =module_buildid (mod_map->mod);break ; } }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 264/641 PCI: Mark 3ware-9650SA Root Port Extended Tags as broken
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jörg Wedekind <[email protected]>
[ Upstream commit 959ac08a2c2811305be8c2779779e8b0932e5a99 ]
Per PCIe r7.0, sec 2.2.6.2.1 and 7.5.3.4, a Requester may not use 8-bit Tags unless its Extended Tag Field Enable is set, but all Receivers/Completers must handle 8-bit Tags correctly regardless of their Extended Tag Field Enable.
Some devices do not handle 8-bit Tags as Completers, so add a quirk for them. If we find such a device, we disable Extended Tags for the entire hierarchy to make peer-to-peer DMA possible.
The 3ware 9650SA seems to have issues with handling 8-bit tags. Mark it as broken.
This fixes PCI Parity Errors like :
3w-9xxx: scsi0: ERROR: (0x06:0x000C): PCI Parity Error: clearing. 3w-9xxx: scsi0: ERROR: (0x06:0x000D): PCI Abort: clearing. 3w-9xxx: scsi0: ERROR: (0x06:0x000E): Controller Queue Error: clearing. 3w-9xxx: scsi0: ERROR: (0x06:0x0010): Microcontroller Error: clearing.
--- drivers/pci/quirks.c | 1 + 1 file changed, 1 insertion(+)
drivers/pci/quirks.c +1 -0
@@ -5581,6 +5581,7 @@ static void quirk_no_ext_tags(struct pci_dev *pdev)pci_walk_bus (bridge->bus, pci_configure_extended_tags,NULL ); }DECLARE_PCI_FIXUP_EARLY (PCI_VENDOR_ID_3WARE ,0x1004 , quirk_no_ext_tags); +DECLARE_PCI_FIXUP_EARLY (PCI_VENDOR_ID_3WARE ,0x1005 , quirk_no_ext_tags);DECLARE_PCI_FIXUP_EARLY (PCI_VENDOR_ID_SERVERWORKS ,0x0132 , quirk_no_ext_tags);DECLARE_PCI_FIXUP_EARLY (PCI_VENDOR_ID_SERVERWORKS ,0x0140 , quirk_no_ext_tags);DECLARE_PCI_FIXUP_EARLY (PCI_VENDOR_ID_SERVERWORKS ,0x0141 , quirk_no_ext_tags);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 265/641 wifi: rtw89: debug: Fix memory leak in __print_txpwr_map()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zilin Guan <[email protected]>
[ Upstream commit 6070a44051b1c35714fa130de7726cfe91ca5559 ]
In __print_txpwr_map(), memory is allocated to bufp via vzalloc(). If max_valid_addr is 0, the function returns -EOPNOTSUPP immediately without freeing bufp, leading to a memory leak.
Since the validation of max_valid_addr does not depend on the allocated memory, fix this by moving the vzalloc() call after the check.
Compile tested only. Issue found using a prototype static analysis tool and code review.
--- drivers/net/wireless/realtek/rtw89/debug.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/net/wireless/realtek/rtw89/debug.c +4 -4
@@ -824,10 +824,6 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char *buf, size_t bufs8 *bufp, tmp;int ret; - bufp =vzalloc (map->addr_to - map->addr_from +4 ); -if (!bufp) -return -ENOMEM ; -if (path_num ==1 ) max_valid_addr = map->addr_to_1ss;else
@@ -836,6 +832,10 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char *buf, size_t bufif (max_valid_addr ==0 )return -EOPNOTSUPP ; + bufp =vzalloc (map->addr_to - map->addr_from +4 ); +if (!bufp) +return -ENOMEM ; +for (addr = map->addr_from; addr <= max_valid_addr; addr +=4 ) { ret =rtw89_mac_txpwr_read32 (rtwdev,RTW89_PHY_0 , addr, &val);if (ret)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 266/641 iommu/vt-d: Flush cache for PASID table before using it
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmytro Maluka <[email protected]>
[ Upstream commit 22d169bdd2849fe6bd18c2643742e1c02be6451c ]
When writing the address of a freshly allocated zero-initialized PASID table to a PASID directory entry, do that after the CPU cache flush for this PASID table, not before it, to avoid the time window when this PASID table may be already used by non-coherent IOMMU hardware while its contents in RAM is still some random old data, not zero-initialized.
--- drivers/iommu/intel/pasid.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/iommu/intel/pasid.c +4 -3
@@ -153,6 +153,9 @@ static struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid)if (!entries)return NULL ; +if (!ecap_coherent (info->iommu->ecap)) +clflush_cache_range (entries,VTD_PAGE_SIZE ); +/* * The pasid directory table entry won't be freed after * allocation. No worry about the race with free and
@@ -165,10 +168,8 @@ static struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid)iommu_free_pages (entries); goto retry; } -if (!ecap_coherent (info->iommu->ecap)) { -clflush_cache_range (entries,VTD_PAGE_SIZE ); +if (!ecap_coherent (info->iommu->ecap))clflush_cache_range (&dir[dir_index].val,sizeof (*dir)); - } }return &entries[index];
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 267/641 iommu/vt-d: Clear Present bit before tearing down PASID entry
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Baolu <[email protected]>
[ Upstream commit 75ed00055c059dedc47b5daaaa2f8a7a019138ff ]
The Intel VT-d Scalable Mode PASID table entry consists of 512 bits (64 bytes). When tearing down an entry, the current implementation zeros the entire 64-byte structure immediately using multiple 64-bit writes.
Since the IOMMU hardware may fetch these 64 bytes using multiple internal transactions (e.g., four 128-bit bursts), updating or zeroing the entire entry while it is active (P=1) risks a "torn" read. If a hardware fetch occurs simultaneously with the CPU zeroing the entry, the hardware could observe an inconsistent state, leading to unpredictable behavior or spurious faults.
Follow the "Guidance to Software for Invalidations" in the VT-d spec (Section 6.5.3.3) by implementing the recommended ownership handshake:
1. Clear only the 'Present' (P) bit of the PASID entry.
2. Use a dma_wmb() to ensure the cleared bit is visible to hardware before proceeding.
3. Execute the required invalidation sequence (PASID cache, IOTLB, and Device-TLB flush) to ensure the hardware has released all cached references.
4. Only after the flushes are complete, zero out the remaining fields of the PASID entry.
Also, add a dma_wmb() in pasid_set_present() to ensure that all other fields of the PASID entry are visible to the hardware before the Present bit is set.
--- drivers/iommu/intel/pasid.c | 6 +++++- drivers/iommu/intel/pasid.h | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-)
drivers/iommu/intel/pasid.c +5 -1
@@ -273,7 +273,7 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev, did =pasid_get_domain_id (pte); pgtt =pasid_pte_get_pgtt (pte); -intel_pasid_clear_entry (dev, pasid, fault_ignore); +pasid_clear_present (pte);spin_unlock (&iommu->lock);if (!ecap_coherent (iommu->ecap))
@@ -287,6 +287,10 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev, iommu->flush.flush_iotlb (iommu, did,0 ,0 ,DMA_TLB_DSI_FLUSH );devtlb_invalidation_with_pasid (iommu, dev, pasid); +intel_pasid_clear_entry (dev, pasid, fault_ignore); +if (!ecap_coherent (iommu->ecap)) +clflush_cache_range (pte,sizeof (*pte)); +if (!fault_ignore)intel_iommu_drain_pasid_prq (dev, pasid); }
drivers/iommu/intel/pasid.h +14 -0
@@ -233,9 +233,23 @@ static inline void pasid_set_wpe(struct pasid_entry *pe)*/ static inline void pasid_set_present (struct pasid_entry *pe) { +dma_wmb ();pasid_set_bits (&pe->val[0 ],1 <<0 ,1 ); } +/* +* Clear the Present (P) bit (bit 0) of a scalable-mode PASID table entry. +* This initiates the transition of the entry's ownership from hardware +* to software. The caller is responsible for fulfilling the invalidation +* handshake recommended by the VT-d spec, Section 6.5.3.3 (Guidance to +* Software for Invalidations). +*/ +static inline void pasid_clear_present (struct pasid_entry *pe) +{ +pasid_set_bits (&pe->val[0 ],1 <<0 ,0 ); +dma_wmb (); +} +/* * Setup Page Walk Snoop bit (Bit 87) of a scalable mode PASID * entry.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 268/641 iommu/vt-d: Clear Present bit before tearing down context entry
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Baolu <[email protected]>
[ Upstream commit c1e4f1dccbe9d7656d1c6872ebeadb5992d0aaa2 ]
When tearing down a context entry, the current implementation zeros the entire 128-bit entry using multiple 64-bit writes. This creates a window where the hardware can fetch a "torn" entry — where some fields are already zeroed while the 'Present' bit is still set — leading to unpredictable behavior or spurious faults.
While x86 provides strong write ordering, the compiler may reorder writes to the two 64-bit halves of the context entry. Even without compiler reordering, the hardware fetch is not guaranteed to be atomic with respect to multiple CPU writes.
Align with the "Guidance to Software for Invalidations" in the VT-d spec (Section 6.5.3.3) by implementing the recommended ownership handshake:
1. Clear only the 'Present' (P) bit of the context entry first to signal the transition of ownership from hardware to software.
2. Use dma_wmb() to ensure the cleared bit is visible to the IOMMU.
3. Perform the required cache and context-cache invalidation to ensure hardware no longer has cached references to the entry.
4. Fully zero out the entry only after the invalidation is complete.
Also, add a dma_wmb() to context_set_present() to ensure the entry is fully initialized before the 'Present' bit becomes visible.
--- drivers/iommu/intel/iommu.c | 4 +++- drivers/iommu/intel/iommu.h | 21 ++++++++++++++++++++- drivers/iommu/intel/pasid.c | 5 ++++- 3 files changed, 27 insertions(+), 3 deletions(-)
drivers/iommu/intel/iommu.c +3 -1
@@ -1722,10 +1722,12 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8 } did =context_domain_id (context); -context_clear_entry (context); +context_clear_present (context);__iommu_flush_cache (iommu, context,sizeof (*context));spin_unlock (&iommu->lock);intel_context_flush_no_pasid (info, context, did); +context_clear_entry (context); +__iommu_flush_cache (iommu, context,sizeof (*context)); } int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
drivers/iommu/intel/iommu.h +20 -1
@@ -969,7 +969,26 @@ static inline unsigned long lvl_to_nr_pages(unsigned int lvl)static inline void context_set_present (struct context_entry *context) { - context->lo |=1 ; +u64 val; + +dma_wmb (); + val =READ_ONCE (context->lo) |1 ; +WRITE_ONCE (context->lo, val); +} + +/* +* Clear the Present (P) bit (bit 0) of a context table entry. This initiates +* the transition of the entry's ownership from hardware to software. The +* caller is responsible for fulfilling the invalidation handshake recommended +* by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations). +*/ +static inline void context_clear_present (struct context_entry *context) +{ +u64 val; + + val =READ_ONCE (context->lo) &GENMASK_ULL (63 ,1 ); +WRITE_ONCE (context->lo, val); +dma_wmb (); }static inline void context_set_fault_enable (struct context_entry *context)
drivers/iommu/intel/pasid.c +4 -1
@@ -1028,7 +1028,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn) }if (context_copied (iommu, bus, devfn)) { -context_clear_entry (context); +context_clear_present (context);__iommu_flush_cache (iommu, context,sizeof (*context));/*
@@ -1048,6 +1048,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn) iommu->flush.flush_iotlb (iommu,0 ,0 ,0 ,DMA_TLB_GLOBAL_FLUSH );devtlb_invalidation_with_pasid (iommu, dev,IOMMU_NO_PASID ); +context_clear_entry (context); +__iommu_flush_cache (iommu, context,sizeof (*context)); +/* * At this point, the device is supposed to finish reset at * its driver probe stage, so no in-flight DMA will exist,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 269/641 dm: use bio_clone_blkg_association
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikulas Patocka <[email protected]>
[ Upstream commit 2df8b310bcfe76827fd71092f58a2493ee6590b0 ]
The origin bio carries blk-cgroup information which could be set from foreground(task_css(css) - wbc->wb->blkcg_css), so the blkcg won't control buffer io since commit ca522482e3eaf ("dm: pass NULL bdev to bio_alloc_clone"). The synchronous io is still under control by blkcg, because 'bio->bi_blkg' is set by io submitting task which has been added into 'cgroup.procs'.
Fix it by using bio_clone_blkg_association when submitting a cloned bio.
--- drivers/md/dm.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/md/dm.c +2 -0
@@ -1363,6 +1363,8 @@ void dm_submit_bio_remap(struct bio *clone, struct bio *tgt_clone)if (!tgt_clone) tgt_clone = clone; +bio_clone_blkg_association (tgt_clone, io->orig_bio); +/* * Account io->origin_bio to DM dev on behalf of target * that took ownership of IO with DM_MAPIO_SUBMITTED.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 270/641 xdrgen: Fix struct prefix for typedef types in program wrappers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit bf0fe9ad3d597d8e1378dc9953ca96dfc3addb2b ]
The program templates for decoder/argument.j2 and encoder/result.j2 unconditionally add 'struct' prefix to all types. This is incorrect when an RPC protocol specification lists a typedef'd basic type or an enum as a procedure argument or result (e.g., NFSv2's fhandle or stat), resulting in compiler errors when building generated C code.
--- tools/net/sunrpc/xdrgen/generators/__init__.py | 3 ++- .../sunrpc/xdrgen/templates/C/program/decoder/argument.j2 | 4 ++++ .../net/sunrpc/xdrgen/templates/C/program/encoder/result.j2 | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-)
tools/net/sunrpc/xdrgen/generators/__init__.py +2 -1
@@ -6,7 +6,7 @@ import sys from jinja2 import Environment, FileSystemLoader, Template from xdr_ast import _XdrAst, Specification, _RpcProgram, _XdrTypeSpecifier -from xdr_ast import public_apis, pass_by_reference, get_header_name +from xdr_ast import public_apis, pass_by_reference, structs, get_header_name from xdr_parse import get_xdr_annotate
@@ -22,6 +22,7 @@ def create_jinja2_environment(language: str, xdr_type: str) -> Environment: environment.globals["annotate"] = get_xdr_annotate() environment.globals["public_apis"] = public_apis environment.globals["pass_by_reference"] = pass_by_reference + environment.globals["structs"] = structs return environment case _: raise NotImplementedError("Language not supported")
tools/net/sunrpc/xdrgen/templates/C/program/decoder/argument.j2 +4 -0
@@ -14,7 +14,11 @@ bool {{ program }}_svc_decode_{{ argument }}(struct svc_rqst *rqstp, struct xdr_ {% if argument == 'void' %} return xdrgen_decode_void(xdr); {% else %} +{% if argument in structs %} struct {{ argument }} *argp = rqstp->rq_argp; +{% else %} + {{ argument }} *argp = rqstp->rq_argp; +{% endif %} return xdrgen_decode_{{ argument }}(xdr, argp); {% endif %}
tools/net/sunrpc/xdrgen/templates/C/program/encoder/result.j2 +6 -0
@@ -14,8 +14,14 @@ bool {{ program }}_svc_encode_{{ result }}(struct svc_rqst *rqstp, struct xdr_st {% if result == 'void' %} return xdrgen_encode_void(xdr); {% else %} +{% if result in structs %} struct {{ result }} *resp = rqstp->rq_resp; return xdrgen_encode_{{ result }}(xdr, resp); +{% else %} + {{ result }} *resp = rqstp->rq_resp; + + return xdrgen_encode_{{ result }}(xdr, *resp); +{% endif %} {% endif %} }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 271/641 NFS: NFSERR_INVAL is not defined by NFSv2
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 0ac903d1bfdce8ff40657c2b7d996947b72b6645 ]
A documenting comment in include/uapi/linux/nfs.h claims incorrectly that NFSv2 defines NFSERR_INVAL. There is no such definition in either RFC 1094 or https://pubs.opengroup.org/onlinepubs/9629799/chap7.htm
NFS3ERR_INVAL is introduced in RFC 1813.
NFSD returns NFSERR_INVAL for PROC_GETACL, which has no specification (yet).
However, nfsd_map_status() maps nfserr_symlink and nfserr_wrong_type to nfserr_inval, which does not align with RFC 1094. This logic was introduced only recently by commit 438f81e0e92a ("nfsd: move error choice for incorrect object types to version-specific code."). Given that we have no INVAL or SERVERFAULT status in NFSv2, probably the only choice is NFSERR_IO.
--- fs/nfsd/nfs2acl.c | 2 +- fs/nfsd/nfsproc.c | 2 +- include/uapi/linux/nfs.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
fs/nfsd/nfs2acl.c +1 -1
@@ -45,7 +45,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp) inode =d_inode (fh->fh_dentry);if (argp->mask & ~NFS_ACL_MASK ) { - resp->status = nfserr_inval; + resp->status = nfserr_io; goto out; } resp->mask = argp->mask;
fs/nfsd/nfsproc.c +1 -1
@@ -33,7 +33,7 @@ static __be32 nfsd_map_status(__be32 status)break ;case nfserr_symlink:case nfserr_wrong_type: - status = nfserr_inval; + status = nfserr_io;break ; }return status;
include/uapi/linux/nfs.h +1 -1
@@ -55,7 +55,7 @@NFSERR_NODEV =19 ,/* v2 v3 v4 */ NFSERR_NOTDIR =20 ,/* v2 v3 v4 */ NFSERR_ISDIR =21 ,/* v2 v3 v4 */ -NFSERR_INVAL =22 ,/* v2 v3 v4 */ +NFSERR_INVAL =22 ,/* v3 v4 */ NFSERR_FBIG =27 ,/* v2 v3 v4 */ NFSERR_NOSPC =28 ,/* v2 v3 v4 */ NFSERR_ROFS = 30, /* v2 v3 v4 */
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 272/641 xdrgen: Initialize data pointer for zero-length items
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit 27b0fcae8f535fb882b1876227a935dcfdf576aa ]
The xdrgen decoders for strings and opaque data had an optimization that skipped calling xdr_inline_decode() when the item length was zero. This left the data pointer uninitialized, which could lead to unpredictable behavior when callers access it.
Remove the zero-length check and always call xdr_inline_decode(). When passed a length of zero, xdr_inline_decode() returns the current buffer position, which is valid and matches the behavior of hand-coded XDR decoders throughout the kernel.
--- include/linux/sunrpc/xdrgen/_builtins.h | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-)
include/linux/sunrpc/xdrgen/_builtins.h +8 -12
@@ -188,12 +188,10 @@ xdrgen_decode_string(struct xdr_stream *xdr, string *ptr, u32 maxlen)return false;if (unlikely (maxlen && len > maxlen))return false; -if (len !=0 ) { - p =xdr_inline_decode (xdr, len); -if (unlikely (!p)) -return false; - ptr->data = (unsigned char *)p; - } + p =xdr_inline_decode (xdr, len); +if (unlikely (!p)) +return false; + ptr->data = (unsigned char *)p; ptr->len = len;return true; }
@@ -219,12 +217,10 @@ xdrgen_decode_opaque(struct xdr_stream *xdr, opaque *ptr, u32 maxlen)return false;if (unlikely (maxlen && len > maxlen))return false; -if (len !=0 ) { - p =xdr_inline_decode (xdr, len); -if (unlikely (!p)) -return false; - ptr->data = (u8 *)p; - } + p =xdr_inline_decode (xdr, len); +if (unlikely (!p)) +return false; + ptr->data = (u8 *)p; ptr->len = len;return true; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 273/641 xdrgen: Remove inclusion of nlm4.h header
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
[ Upstream commit eb1f3b55ac6202a013daf14ed508066947cdafa8 ]
The client-side source code template mistakenly includes the nlm4.h header file, which is specific to the NLM protocol and should not be present in the generic template that generates client stubs for all XDR-based protocols.
--- tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 | 1 - 1 file changed, 1 deletion(-)
tools/net/sunrpc/xdrgen/templates/C/source_top/client.j2 +0 -1
@@ -8,6 +8,5 @@ #include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdrgen/_defs.h> #include <linux/sunrpc/xdrgen/_builtins.h> -#include <linux/sunrpc/xdrgen/nlm4.h> #include <linux/sunrpc/clnt.h>
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 274/641 nfsd: never defer requests during idmap lookup
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anthony Iliopoulos <[email protected]>
[ Upstream commit f9c206cdc4266caad6a9a7f46341420a10f03ccb ]
During v4 request compound arg decoding, some ops (e.g. SETATTR) can trigger idmap lookup upcalls. When those upcall responses get delayed beyond the allowed time limit, cache_check() will mark the request for deferral and cause it to be dropped.
This prevents nfs4svc_encode_compoundres from being executed, and thus the session slot flag NFSD4_SLOT_INUSE never gets cleared. Subsequent client requests will fail with NFSERR_JUKEBOX, given that the slot will be marked as in-use, making the SEQUENCE op fail.
Fix this by making sure that the RQ_USEDEFERRAL flag is always clear during nfs4svc_decode_compoundargs(), since no v4 request should ever be deferred.
--- fs/nfsd/nfs4idmap.c | 48 +++++++++++++++++++++++++++++++++++++++------ fs/nfsd/nfs4proc.c | 2 -- fs/nfsd/nfs4xdr.c | 16 +++++++++++++++ 3 files changed, 58 insertions(+), 8 deletions(-)
fs/nfsd/nfs4idmap.c +42 -6
@@ -643,13 +643,31 @@ static __be32 encode_name_from_id(struct xdr_stream *xdr,return idmap_id_to_name (xdr, rqstp, type, id); } -__be32 -nfsd_map_name_to_uid (struct svc_rqst *rqstp,const char *name,size_t namelen, -kuid_t *uid) +/** +* nfsd_map_name_to_uid - Map user@domain to local UID +* @rqstp: RPC execution context +* @name: user@domain name to be mapped +* @namelen: length of name, in bytes +* @uid: OUT: mapped local UID value +* +* Returns nfs_ok on success or an NFSv4 status code on failure. +*/ +__be32 nfsd_map_name_to_uid (struct svc_rqst *rqstp,const char *name, +size_t namelen,kuid_t *uid) {__be32 status;u32 id =-1 ; +/* +* The idmap lookup below triggers an upcall that invokes +* cache_check(). RQ_USEDEFERRAL must be clear to prevent +* cache_check() from setting RQ_DROPME via svc_defer(). +* NFSv4 servers are not permitted to drop requests. Also +* RQ_DROPME will force NFSv4.1 session slot processing to +* be skipped. +*/ +WARN_ON_ONCE (test_bit (RQ_USEDEFERRAL , &rqstp->rq_flags)); +if (name ==NULL || namelen ==0 )return nfserr_inval;
@@ -660,13 +678,31 @@ nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen,return status; } -__be32 -nfsd_map_name_to_gid (struct svc_rqst *rqstp,const char *name,size_t namelen, -kgid_t *gid) +/** +* nfsd_map_name_to_gid - Map user@domain to local GID +* @rqstp: RPC execution context +* @name: user@domain name to be mapped +* @namelen: length of name, in bytes +* @gid: OUT: mapped local GID value +* +* Returns nfs_ok on success or an NFSv4 status code on failure. +*/ +__be32 nfsd_map_name_to_gid (struct svc_rqst *rqstp,const char *name, +size_t namelen,kgid_t *gid) {__be32 status;u32 id =-1 ; +/* +* The idmap lookup below triggers an upcall that invokes +* cache_check(). RQ_USEDEFERRAL must be clear to prevent +* cache_check() from setting RQ_DROPME via svc_defer(). +* NFSv4 servers are not permitted to drop requests. Also +* RQ_DROPME will force NFSv4.1 session slot processing to +* be skipped. +*/ +WARN_ON_ONCE (test_bit (RQ_USEDEFERRAL , &rqstp->rq_flags)); +if (name ==NULL || namelen ==0 )return nfserr_inval;
fs/nfsd/nfs4proc.c +0 -2
@@ -2995,8 +2995,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)BUG_ON (cstate->replay_owner); out: cstate->status = status; -/* Reset deferral mechanism for RPC deferrals */ -set_bit (RQ_USEDEFERRAL , &rqstp->rq_flags);return rpc_success; }
fs/nfsd/nfs4xdr.c +16 -0
@@ -6001,6 +6001,22 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) args->ops = args->iops; args->rqstp = rqstp; +/* +* NFSv4 operation decoders can invoke svc cache lookups +* that trigger svc_defer() when RQ_USEDEFERRAL is set, +* setting RQ_DROPME. This creates two problems: +* +* 1. Non-idempotency: Compounds make it too hard to avoid +* problems if a request is deferred and replayed. +* +* 2. Session slot leakage (NFSv4.1+): If RQ_DROPME is set +* during decode but SEQUENCE executes successfully, the +* session slot will be marked INUSE. The request is then +* dropped before encoding, so the slot is never released, +* rendering it permanently unusable by the client. +*/ +clear_bit (RQ_USEDEFERRAL , &rqstp->rq_flags); +return nfsd4_decode_compound (args); }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 275/641 lib/kstrtox: fix kstrtobool() docstring to mention enabled/disabled
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chaitanya Mishra <[email protected]>
[ Upstream commit 1921044eebf1d6861a6de1a76e3f63729a45e712 ]
Commit ae5b3500856f ("kstrtox: add support for enabled and disabled in kstrtobool()") added support for 'e'/'E' (enabled) and 'd'/'D' (disabled) inputs, but did not update the docstring accordingly.
Update the docstring to include 'Ee' (for true) and 'Dd' (for false) in the list of accepted first characters.
--- lib/kstrtox.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
lib/kstrtox.c +2 -2
@@ -340,8 +340,8 @@ EXPORT_SYMBOL(kstrtos8);* @s: input string * @res: result * -* This routine returns 0 iff the first character is one of 'YyTt1NnFf0', or -* [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value +* This routine returns 0 iff the first character is one of 'EeYyTt1DdNnFf0', +* or [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value * pointed to by res is updated upon finding a match. */ noinline
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 276/641 rust: task: restrict Task::group_leader() to current
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alice Ryhl <[email protected]>
[ Upstream commit 105ddfb2d2b3acec7a7d9695463df48733d91e6c ]
The Task::group_leader() method currently allows you to access the group_leader() of any task, for example one you hold a refcount to. But this is not safe in general since the group leader could change when a task exits. See for example commit a15f37a40145c ("kernel/sys.c: fix the racy usage of task_lock(tsk->group_leader) in sys_prlimit64() paths").
All existing users of Task::group_leader() call this method on current, which is guaranteed running, so there's not an actual issue in Rust code today. But to prevent code in the future from making this mistake, restrict Task::group_leader() so that it can only be called on current.
There are some other cases where accessing task->group_leader is okay. For example it can be safe if you hold tasklist_lock or rcu_read_lock(). However, only supporting current->group_leader is sufficient for all in-tree Rust users of group_leader right now. Safe Rust functionality for accessing it under rcu or while holding tasklist_lock may be added in the future if required by any future Rust module.
This patch is a bugfix in that it prevents users of this API from writing incorrect code. It doesn't change behavior of correct code.
--- rust/kernel/task.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
rust/kernel/task.rs +12 -12
@@ -204,18 +204,6 @@ pub fn as_ptr(&self) -> *mut bindings::task_struct { self.0 .get () } -/// Returns the group leader of the given task. -pub fn group_leader (&self) -> &Task { -// SAFETY: The group leader of a task never changes after initialization, so reading this -// field is not a data race. -let ptr =unsafe { *ptr::addr_of!((*self.as_ptr ()).group_leader) }; - -// SAFETY: The lifetime of the returned task reference is tied to the lifetime of `self`, -// and given that a task has a reference to its group leader, we know it must be valid for -// the lifetime of the returned task reference. -unsafe { &*ptr.cast () } - } -/// Returns the PID of the given task. pub fn pid (&self) ->Pid {// SAFETY: The pid of a task never changes after initialization, so reading this field is
@@ -345,6 +333,18 @@ pub fn active_pid_ns(&self) -> Option<&PidNamespace> {// `release_task()` call. Some (unsafe {PidNamespace ::from_ptr (active_ns) }) } + +/// Returns the group leader of the current task. +pub fn group_leader (&self) -> &Task { +// SAFETY: The group leader of a task never changes while the task is running, and `self` +// is the current task, which is guaranteed running. +let ptr =unsafe { (*self.as_ptr ()).group_leader }; + +// SAFETY: `current->group_leader` stays valid for at least the duration in which `current` +// is running, and the signature of this function ensures that the returned `&Task` can +// only be used while `current` is still valid, thus still running. +unsafe { &*ptr.cast () } + } }// SAFETY: The type invariants guarantee that `Task` is always refcounted.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 277/641 fat: avoid parent link count underflow in rmdir
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiyu Zhang <[email protected]>
[ Upstream commit 8cafcb881364af5ef3a8b9fed4db254054033d8a ]
Corrupted FAT images can leave a directory inode with an incorrect i_nlink (e.g. 2 even though subdirectories exist). rmdir then unconditionally calls drop_nlink(dir) and can drive i_nlink to 0, triggering the WARN_ON in drop_nlink().
Add a sanity check in vfat_rmdir() and msdos_rmdir(): only drop the parent link count when it is at least 3, otherwise report a filesystem error.
--- fs/fat/namei_msdos.c | 7 ++++++- fs/fat/namei_vfat.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-)
fs/fat/namei_msdos.c +6 -1
@@ -325,7 +325,12 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry) err =fat_remove_entries (dir, &sinfo);/* and releases bh */ if (err) goto out; -drop_nlink (dir); +if (dir->i_nlink >=3 ) +drop_nlink (dir); +else { +fat_fs_error (sb,"parent dir link count too low (%u)" , + dir->i_nlink); + }clear_nlink (inode);fat_truncate_time (inode,NULL ,S_CTIME );
fs/fat/namei_vfat.c +6 -1
@@ -803,7 +803,12 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry) err =fat_remove_entries (dir, &sinfo);/* and releases bh */ if (err) goto out; -drop_nlink (dir); +if (dir->i_nlink >=3 ) +drop_nlink (dir); +else { +fat_fs_error (sb,"parent dir link count too low (%u)" , + dir->i_nlink); + }clear_nlink (inode);fat_truncate_time (inode,NULL ,S_ATIME |S_MTIME );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 278/641 PCI: Rewrite bridge window head alignment function
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit bc75c8e5071120e919beb39e69f0979cccfdf219 ]
The calculation of bridge window head alignment is done by calculate_mem_align() [*]. With the default bridge window alignment, it is used for both head and tail alignment.
The selected head alignment does not always result in tight-fitting resources (gap at d4f00000-d4ffffff):
d4800000-dbffffff : PCI Bus 0000:06
d4800000-d48fffff : PCI Bus 0000:07
d4800000-d4803fff : 0000:07:00.0
d4800000-d4803fff : nvme
d4900000-d49fffff : PCI Bus 0000:0a
d4900000-d490ffff : 0000:0a:00.0
d4900000-d490ffff : r8169
d4910000-d4913fff : 0000:0a:00.0
d4a00000-d4cfffff : PCI Bus 0000:0b
d4a00000-d4bfffff : 0000:0b:00.0
d4a00000-d4bfffff : 0000:0b:00.0
d4c00000-d4c07fff : 0000:0b:00.0
d4d00000-d4dfffff : PCI Bus 0000:15
d4d00000-d4d07fff : 0000:15:00.0
d4d00000-d4d07fff : xhci-hcd
d4e00000-d4efffff : PCI Bus 0000:16
d4e00000-d4e7ffff : 0000:16:00.0
d4e80000-d4e803ff : 0000:16:00.0
d4e80000-d4e803ff : ahci
d5000000-dbffffff : PCI Bus 0000:0cThis has not caused problems (for years) with the default bridge window tail alignment that grossly over-estimates the required tail alignment leaving more tail room than necessary. With the introduction of relaxed tail alignment that leaves no extra tail room whatsoever, any gaps will immediately turn into assignment failures.
Introduce head alignment calculation that ensures no gaps are left and apply the new approach when using relaxed alignment. We may want to consider using it for the normal alignment eventually, but as the first step, solve only the problem with the relaxed tail alignment.
([*] I don't understand the algorithm in calculate_mem_align().)
Stable-dep-of: f909e3ee3ed1 ("PCI: Remove old_size limit from bridge window sizing")
--- drivers/pci/setup-bus.c | 53 ++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 9 deletions(-)
drivers/pci/setup-bus.c +44 -9
@@ -1223,6 +1223,45 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns,return min_align; } +/* +* Calculate bridge window head alignment that leaves no gaps in between +* resources. +*/ +static resource_size_t calculate_head_align (resource_size_t *aligns, +int max_order) +{ +resource_size_t head_align =1 ; +resource_size_t remainder =0 ; +int order; + +/* Take the largest alignment as the starting point. */ + head_align <<= max_order +__ffs (SZ_1M ); + +for (order = max_order -1 ; order >=0 ; order--) { +resource_size_t align1 =1 ; + + align1 <<= order +__ffs (SZ_1M ); + +/* +* Account smaller resources with alignment < max_order that +* could be used to fill head room if alignment less than +* max_order is used. +*/ + remainder += aligns[order]; + +/* +* Test if head fill is enough to satisfy the alignment of +* the larger resources after reducing the alignment. +*/ +while ((head_align > align1) && (remainder >= head_align /2 )) { + head_align /=2 ; + remainder -= head_align; + } + } + +return head_align; +} +/** * pbus_upstream_space_available - Check no upstream resource limits allocation * @bus: The bus
@@ -1310,13 +1349,13 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, {struct pci_dev *dev;resource_size_t min_align, win_align, align, size, size0, size1 =0 ; -resource_size_t aligns[28 ];/* Alignments from 1MB to 128TB */ +resource_size_t aligns[28 ] = {};/* Alignments from 1MB to 128TB */ +resource_size_t aligns2[28 ] = {};/* Alignments from 1MB to 128TB */ int order, max_order;struct resource *b_res =pbus_select_window_for_type (bus, type);resource_size_t children_add_size =0 ;resource_size_t children_add_align =0 ;resource_size_t add_align =0 ; -resource_size_t relaxed_align;resource_size_t old_size;if (!b_res)
@@ -1326,7 +1365,6 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,if (b_res->parent)return ; -memset (aligns,0 ,sizeof (aligns)); max_order =0 ; size =0 ;
@@ -1377,6 +1415,7 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,*/ if (r_size <= align) aligns[order] += align; + aligns2[order] += align;if (order > max_order) max_order = order;
@@ -1401,9 +1440,7 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,if (bus->self && size0 && !pbus_upstream_space_available (bus, b_res, size0, min_align)) { - relaxed_align =1ULL << (max_order +__ffs (SZ_1M )); - relaxed_align =max (relaxed_align, win_align); - min_align =min (min_align, relaxed_align); + min_align =calculate_head_align (aligns2, max_order); size0 =calculate_memsize (size, min_size,0 ,0 , old_size, win_align);resource_set_range (b_res, min_align, size0); pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n",
@@ -1417,9 +1454,7 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,if (bus->self && size1 && !pbus_upstream_space_available (bus, b_res, size1, add_align)) { - relaxed_align =1ULL << (max_order +__ffs (SZ_1M )); - relaxed_align =max (relaxed_align, win_align); - min_align =min (min_align, relaxed_align); + min_align =calculate_head_align (aligns2, max_order); size1 =calculate_memsize (size, min_size, add_size, children_add_size, old_size, win_align); pci_info(bus->self,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 279/641 PCI: Stop over-estimating bridge window size
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit 3958bf16e2fe1b1c95467e58694102122c951a31 ]
New way to calculate the bridge window head alignment produces tight-fit, that is, it does not leave any gaps between the resources. Similarly, relaxed tail alignment does not leave extra tail room.
Start to use bridge window calculation that does not over-estimate the size of the required window.
pbus_upstream_space_available() can be removed.
Stable-dep-of: f909e3ee3ed1 ("PCI: Remove old_size limit from bridge window sizing")
--- drivers/pci/setup-bus.c | 97 +++-------------------------------------- 1 file changed, 5 insertions(+), 92 deletions(-)
drivers/pci/setup-bus.c +5 -92
@@ -1262,68 +1262,6 @@ static resource_size_t calculate_head_align(resource_size_t *aligns,return head_align; } -/** -* pbus_upstream_space_available - Check no upstream resource limits allocation -* @bus: The bus -* @res: The resource to help select the correct bridge window -* @size: The size required from the bridge window -* @align: Required alignment for the resource -* -* Check that @size can fit inside the upstream bridge resources that are -* already assigned. Select the upstream bridge window based on the type of -* @res. -* -* Return: %true if enough space is available on all assigned upstream -* resources. -*/ -static bool pbus_upstream_space_available (struct pci_bus *bus, -struct resource *res, -resource_size_t size, -resource_size_t align) -{ -struct resource_constraint constraint = { - .max =RESOURCE_SIZE_MAX , - .align = align, - }; -struct pci_bus *downstream = bus; - -while ((bus = bus->parent)) { -if (pci_is_root_bus (bus)) -break ; - - res =pbus_select_window (bus, res); -if (!res) -return false; -if (!res->parent) -continue ; - -if (resource_size (res) >= size) { -struct resource gap = {}; - -if (find_resource_space (res, &gap, size, &constraint) ==0 ) { - gap.flags = res->flags; -pci_dbg (bus->self, -"Assigned bridge window %pR to %pR free space at %pR\n" , - res, &bus->busn_res, &gap); -return true; - } - } - -if (bus->self) { -pci_info (bus->self, -"Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n" , - res, &bus->busn_res, - (unsigned long long )size, -pci_name (downstream->self), - &downstream->busn_res); - } - -return false; - } - -return true; -} -/** * pbus_size_mem() - Size the memory window of a given bus *
@@ -1350,7 +1288,6 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,struct pci_dev *dev;resource_size_t min_align, win_align, align, size, size0, size1 =0 ;resource_size_t aligns[28 ] = {};/* Alignments from 1MB to 128TB */ -resource_size_t aligns2[28 ] = {};/* Alignments from 1MB to 128TB */ int order, max_order;struct resource *b_res =pbus_select_window_for_type (bus, type);resource_size_t children_add_size =0 ;
@@ -1409,13 +1346,8 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,continue ; } size +=max (r_size, align); -/* -* Exclude ranges with size > align from calculation of -* the alignment. -*/ -if (r_size <= align) - aligns[order] += align; - aligns2[order] += align; + + aligns[order] += align;if (order > max_order) max_order = order;
@@ -1429,38 +1361,19 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, old_size =resource_size (b_res); win_align =window_alignment (bus, b_res->flags); - min_align =calculate_mem_align (aligns, max_order); + min_align =calculate_head_align (aligns, max_order); min_align =max (min_align, win_align); - size0 =calculate_memsize (size, min_size,0 ,0 , old_size, min_align); + size0 =calculate_memsize (size, min_size,0 ,0 , old_size, win_align);if (size0) {resource_set_range (b_res, min_align, size0); b_res->flags &= ~IORESOURCE_DISABLED ; } -if (bus->self && size0 && - !pbus_upstream_space_available (bus, b_res, size0, min_align)) { - min_align =calculate_head_align (aligns2, max_order); - size0 =calculate_memsize (size, min_size,0 ,0 , old_size, win_align); -resource_set_range (b_res, min_align, size0); -pci_info (bus->self,"bridge window %pR to %pR requires relaxed alignment rules\n" , - b_res, &bus->busn_res); - } -if (realloc_head && (add_size >0 || children_add_size >0 )) { add_align =max (min_align, add_align); size1 =calculate_memsize (size, min_size, add_size, children_add_size, - old_size, add_align); - -if (bus->self && size1 && - !pbus_upstream_space_available (bus, b_res, size1, add_align)) { - min_align =calculate_head_align (aligns2, max_order); - size1 =calculate_memsize (size, min_size, add_size, children_add_size, - old_size, win_align); -pci_info (bus->self, -"bridge window %pR to %pR requires relaxed alignment rules\n" , - b_res, &bus->busn_res); - } + old_size, win_align); }if (!size0 && !size1) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 280/641 PCI: Remove old_size limit from bridge window sizing
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit f909e3ee3ed1a44202f09ac7e637a0f9ec372225 ]
calculate_memsize() applies lower bound to the resource size before aligning the resource size making it impossible to shrink bridge window resources. I've not found any justification for this lower bound and nothing indicated it was to work around some HW issue.
Prior to the commit 3baeae36039a ("PCI: Use pci_release_resource() instead of release_resource()"), releasing a bridge window during BAR resize resulted in clearing start and end address of the resource. Clearing addresses destroys the resource size as a side-effect, therefore nullifying the effect of the old size lower bound.
After the commit 3baeae36039a ("PCI: Use pci_release_resource() instead of release_resource()"), BAR resize uses the aligned old size, which results in exceeding what fits into the parent window in some cases:
xe 0030:03:00.0: [drm] Attempting to resize bar from 256MiB -> 16384MiB
xe 0030:03:00.0: BAR 0 [mem 0x620c000000000-0x620c000ffffff 64bit]: releasing
xe 0030:03:00.0: BAR 2 [mem 0x6200000000000-0x620000fffffff 64bit pref]: releasing
pci 0030:02:01.0: bridge window [mem 0x6200000000000-0x620001fffffff 64bit pref]: releasing
pci 0030:01:00.0: bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref]: releasing
pci 0030:00:00.0: bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref]: was not released (still contains assigned resources)
pci 0030:00:00.0: Assigned bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref] to [bus 01-04] free space at [mem 0x6200400000000-0x62007ffffffff 64bit pref] pci 0030:00:00.0: Assigned bridge window [mem 0x6200000000000-0x6203fbff0ffff 64bit pref] to [bus 01-04] cannot fit 0x4000000000 required for 0030:01:00.0 bridging to [bus 02-04]
The old size of 0x6200000000000-0x6203fbff0ffff resource was used as the lower bound which results in 0x4000000000 size request due to alignment. That exceeds what can fit into the parent window.
Since the lower bound never even was enforced fully because the resource addresses were cleared when the bridge window is released, remove the old_size lower bound entirely and trust the calculated bridge window size is enough.
This same problem may occur on io window side but seems less likely to cause issues due to general difference in alignment. Removing the lower bound may have other unforeseen consequences in case of io window so it's better to leave it as -next material if no problem is reported related to io window sizing (BAR resize shouldn't touch io windows anyway).
--- drivers/pci/setup-bus.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-)
drivers/pci/setup-bus.c +3 -8
@@ -1066,16 +1066,13 @@ static resource_size_t calculate_memsize(resource_size_t size,resource_size_t min_size, resource_size_t add_size, resource_size_t children_add_size, - resource_size_t old_size, resource_size_t align) { if (size < min_size) size = min_size; -if (old_size ==1 ) - old_size =0 ; size =max (size, add_size) + children_add_size; -return ALIGN (max (size, old_size), align); +return ALIGN (size, align); } resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
@@ -1293,7 +1290,6 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,resource_size_t children_add_size =0 ;resource_size_t children_add_align =0 ;resource_size_t add_align =0 ; -resource_size_t old_size;if (!b_res)return ;
@@ -1359,11 +1355,10 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, } } - old_size =resource_size (b_res); win_align =window_alignment (bus, b_res->flags); min_align =calculate_head_align (aligns, max_order); min_align =max (min_align, win_align); - size0 =calculate_memsize (size, min_size,0 ,0 , old_size, win_align); + size0 =calculate_memsize (size, min_size,0 ,0 , win_align);if (size0) {resource_set_range (b_res, min_align, size0);
@@ -1373,7 +1368,7 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,if (realloc_head && (add_size >0 || children_add_size >0 )) { add_align =max (min_align, add_align); size1 =calculate_memsize (size, min_size, add_size, children_add_size, - old_size, win_align); + win_align); }if (!size0 && !size1) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 281/641 tcp: tcp_tx_timestamp() must look at the rtx queue
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 838eb9687691d29915797a885b861fd09353386e ]
tcp_tx_timestamp() is only called at the end of tcp_sendmsg_locked() before the final tcp_push().
By the time it is called, it is possible all the copied data has been sent already (transmit queue is empty).
If this is the case, use the last skb in the rtx queue.
--- net/ipv4/tcp.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv4/tcp.c +3 -0
@@ -503,6 +503,9 @@ static void tcp_tx_timestamp(struct sock *sk, struct sockcm_cookie *sockc)struct sk_buff *skb =tcp_write_queue_tail (sk);u32 tsflags = sockc->tsflags; +if (unlikely (!skb)) + skb =skb_rb_last (&sk->tcp_rtx_queue); +if (tsflags && skb) {struct skb_shared_info *shinfo =skb_shinfo (skb);struct tcp_skb_cb *tcb =TCP_SKB_CB (skb);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 282/641 PCI: Check parent for NULL in of_pci_bus_release_domain_nr()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Shtylyov <[email protected]>
[ Upstream commit f7245901de8978d829f80b3d8e36ed9a8fd18049 ]
of_pci_bus_find_domain_nr() allows its parent parameter to be NULL but of_pci_bus_release_domain_nr() (that undoes its effect) doesn't -- that means it's going to blow up while calling of_get_pci_domain_nr() if the parent parameter indeed happens to be NULL. Add the missing NULL check.
Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool.
--- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/pci.c +1 -1
@@ -6717,7 +6717,7 @@ static void of_pci_bus_release_domain_nr(struct device *parent, int domain_nr)return ;/* Release domain from IDA where it was allocated. */ -if (of_get_pci_domain_nr (parent->of_node) == domain_nr) +if (parent &&of_get_pci_domain_nr (parent->of_node) == domain_nr)ida_free (&pci_domain_nr_static_ida, domain_nr);else ida_free (&pci_domain_nr_dynamic_ida, domain_nr);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 283/641 wifi: ath10k: sdio: add missing lock protection in ath10k_sdio_fw_crashed_dump()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ziyi Guo <[email protected]>
[ Upstream commit e55ac348089e579fc224569c7bd90340bf2439f9 ]
ath10k_sdio_fw_crashed_dump() calls ath10k_coredump_new() which requires ar->dump_mutex to be held, as indicated by lockdep_assert_held() in that function. However, the SDIO implementation does not acquire this lock, unlike the PCI and SNOC implementations which properly hold the mutex.
Additionally, ar->stats.fw_crash_counter is documented as protected by ar->data_lock in core.h, but the SDIO implementation modifies it without holding this spinlock.
Add the missing mutex_lock()/mutex_unlock() around the coredump operations, and add spin_lock_bh()/spin_unlock_bh() around the fw_crash_counter increment, following the pattern used in ath10k_pci_fw_dump_work() and ath10k_snoc_fw_crashed_dump().
--- drivers/net/wireless/ath/ath10k/sdio.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/net/wireless/ath/ath10k/sdio.c +6 -0
@@ -2487,7 +2487,11 @@ void ath10k_sdio_fw_crashed_dump(struct ath10k *ar)if (fast_dump)ath10k_bmi_start (ar); +mutex_lock (&ar->dump_mutex); + +spin_lock_bh (&ar->data_lock); ar->stats.fw_crash_counter++; +spin_unlock_bh (&ar->data_lock);ath10k_sdio_disable_intrs (ar);
@@ -2505,6 +2509,8 @@ void ath10k_sdio_fw_crashed_dump(struct ath10k *ar)ath10k_sdio_enable_intrs (ar); +mutex_unlock (&ar->dump_mutex); +ath10k_core_start_recovery (ar); }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 284/641 wifi: ath11k: add usecase firmware handling based on device compatible
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miaoqing Pan <[email protected]>
[ Upstream commit c386a2b1068910538e87ef1cf2fc938ebf7e218f ]
For M.2 WLAN chips, there is no suitable DTS node to specify the firmware-name property. In addition, assigning firmware for the M.2 PCIe interface causes chips that do not use usecase specific firmware to fail. Therefore, abandoning the approach of specifying firmware in DTS. As an alternative, propose a static lookup table mapping device compatible to firmware names. Currently, only WCN6855 HW2.1 requires this.
However, support for the firmware-name property is retained to keep the ABI backwards compatible.
For details on usecase specific firmware, see:
https://lore.kernel.org/all/[email protected]/.
Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1
--- drivers/net/wireless/ath/ath11k/core.c | 27 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath11k/core.h | 4 ++++ 2 files changed, 31 insertions(+)
drivers/net/wireless/ath/ath11k/core.c +27 -0
@@ -997,6 +997,33 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = { {} }; +static const struct __ath11k_core_usecase_firmware_table { +u32 hw_rev; +const char *compatible; +const char *firmware_name; +} ath11k_core_usecase_firmware_table[] = { + {ATH11K_HW_WCN6855_HW21 ,"qcom,lemans-evk" ,"nfa765" }, + {ATH11K_HW_WCN6855_HW21 ,"qcom,monaco-evk" ,"nfa765" }, + {ATH11K_HW_WCN6855_HW21 ,"qcom,hamoa-iot-evk" ,"nfa765" }, + {/* Sentinel */ } +}; + +const char *ath11k_core_get_usecase_firmware (struct ath11k_base *ab) +{ +const struct __ath11k_core_usecase_firmware_table *entry =NULL ; + + entry = ath11k_core_usecase_firmware_table; +while (entry->compatible) { +if (ab->hw_rev == entry->hw_rev && +of_machine_is_compatible (entry->compatible)) +return entry->firmware_name; + entry++; + } + +return NULL ; +} +EXPORT_SYMBOL (ath11k_core_get_usecase_firmware); +void ath11k_fw_stats_pdevs_free (struct list_head *head) {struct ath11k_fw_stats_pdev *i, *tmp;
drivers/net/wireless/ath/ath11k/core.h +4 -0
@@ -1275,6 +1275,7 @@ bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab);const struct firmware *ath11k_core_firmware_request (struct ath11k_base *ab,const char *filename); +const char *ath11k_core_get_usecase_firmware (struct ath11k_base *ab);static inline const char *ath11k_scan_state_str (enum ath11k_scan_state state) {
@@ -1329,6 +1330,9 @@ static inline void ath11k_core_create_firmware_path(struct ath11k_base *ab,of_property_read_string (ab->dev->of_node,"firmware-name" , &fw_name); +if (!fw_name) + fw_name =ath11k_core_get_usecase_firmware (ab); +if (fw_name &&strncmp (filename,"board" ,5 ))snprintf (buf, buf_len,"%s/%s/%s/%s" ,ATH11K_FW_DIR , ab->hw_params.fw.dir, fw_name, filename);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 285/641 wifi: ath12k: Fix index decrement when array_len is zero
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaradhana Sahu <[email protected]>
[ Upstream commit e4763898bb1325dbb3792961b6d607b5c6452d64 ]
Currently, print_array_to_buf_index() decrements index unconditionally. This may lead to invalid buffer access when array_len is zero.
Fix this by decrementing index only when array_len is non-zero.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
--- drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +5 -3
@@ -1,7 +1,7 @@// SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. -* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. +* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include <linux/vmalloc.h>
@@ -29,8 +29,10 @@ print_array_to_buf_index(u8 *buf, u32 offset, const char *header, u32 stats_inde " %u:%u,", stats_index++, le32_to_cpu(array[i])); }/* To overwrite the last trailing comma */ - index--; - *(buf + offset + index) ='\0' ; +if (array_len >0 ) { + index--; + *(buf + offset + index) ='\0' ; + }if (footer) { index += scnprintf(buf + offset + index,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 286/641 wifi: ath12k: clear stale link mapping of ahvif->links_map
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaradhana Sahu <[email protected]>
[ Upstream commit 2c1ba9c2adf0fda96eaaebd8799268a7506a8fc9 ]
When an arvif is initialized in non-AP STA mode but MLO connection preparation fails before the arvif is created (arvif->is_created remains false), the error path attempts to delete all links. However, link deletion only executes when arvif->is_created is true. As a result, ahvif retains a stale entry of arvif that is initialized but not created.
When a new arvif is initialized with the same link id, this stale mapping triggers the following WARN_ON.
WARNING: drivers/net/wireless/ath/ath12k/mac.c:4271 at ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k], CPU#3: wpa_supplicant/275 Call trace: ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k] (P) drv_change_vif_links+0xbc/0x1a4 [mac80211] ieee80211_vif_update_links+0x54c/0x6a0 [mac80211] ieee80211_vif_set_links+0x40/0x70 [mac80211] ieee80211_prep_connection+0x84/0x450 [mac80211] ieee80211_mgd_auth+0x200/0x480 [mac80211] ieee80211_auth+0x14/0x20 [mac80211] cfg80211_mlme_auth+0x90/0xf0 [cfg80211] nl80211_authenticate+0x32c/0x380 [cfg80211] genl_family_rcv_msg_doit+0xc8/0x134
Fix this issue by unassigning the link vif and clearing ahvif->links_map if arvif is only initialized but not created.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
--- drivers/net/wireless/ath/ath12k/mac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/wireless/ath/ath12k/mac.c +3 -1
@@ -4004,8 +4004,10 @@ ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw,if (WARN_ON (!arvif))return -EINVAL ; -if (!arvif->is_created) +if (!arvif->is_created) { +ath12k_mac_unassign_link_vif (arvif);continue ; + }if (WARN_ON (!arvif->ar))return -EINVAL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 287/641 PCI: Initialize RCB from pci_configure_device()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Håkon Bugge <[email protected]>
[ Upstream commit 1a6845aaa6de81f95959b380b45de8f10d6a8502 ]
Commit e42010d8207f ("PCI: Set Read Completion Boundary to 128 iff Root Port supports it (_HPX)") worked around a bogus _HPX type 2 record, which caused program_hpx_type2() to set the RCB in an endpoint even though the Root Port did not have the RCB bit set.
e42010d8207f fixed that by setting the RCB in the endpoint only when it was set in the Root Port.
In retrospect, program_hpx_type2() is intended for AER-related settings, and the RCB should be configured elsewhere so it doesn't depend on the presence or contents of an _HPX record.
Explicitly program the RCB from pci_configure_device() so it matches the Root Port's RCB. The Root Port may not be visible to virtualized guests; in that case, leave RCB alone.
--- drivers/pci/probe.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
drivers/pci/probe.c +32 -0
@@ -2381,6 +2381,37 @@ static void pci_configure_serr(struct pci_dev *dev) } } +static void pci_configure_rcb (struct pci_dev *dev) +{ +struct pci_dev *rp; +u16 rp_lnkctl; + +/* +* Per PCIe r7.0, sec 7.5.3.7, RCB is only meaningful in Root Ports +* (where it is read-only), Endpoints, and Bridges. It may only be +* set for Endpoints and Bridges if it is set in the Root Port. For +* Endpoints, it is 'RsvdP' for Virtual Functions. +*/ +if (!pci_is_pcie (dev) || +pci_pcie_type (dev) ==PCI_EXP_TYPE_ROOT_PORT || +pci_pcie_type (dev) ==PCI_EXP_TYPE_UPSTREAM || +pci_pcie_type (dev) ==PCI_EXP_TYPE_DOWNSTREAM || +pci_pcie_type (dev) ==PCI_EXP_TYPE_RC_EC || + dev->is_virtfn) +return ; + +/* Root Port often not visible to virtualized guests */ + rp =pcie_find_root_port (dev); +if (!rp) +return ; + +pcie_capability_read_word (rp,PCI_EXP_LNKCTL , &rp_lnkctl); +pcie_capability_clear_and_set_word (dev,PCI_EXP_LNKCTL , +PCI_EXP_LNKCTL_RCB , + (rp_lnkctl &PCI_EXP_LNKCTL_RCB ) ? +PCI_EXP_LNKCTL_RCB :0 ); +} +static void pci_configure_device (struct pci_dev *dev) {pci_configure_mps (dev);
@@ -2390,6 +2421,7 @@ static void pci_configure_device(struct pci_dev *dev)pci_configure_aspm_l1ss (dev);pci_configure_eetlp_prefix (dev);pci_configure_serr (dev); +pci_configure_rcb (dev);pci_acpi_program_hp_params (dev); }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 288/641 PCI/ACPI: Restrict program_hpx_type2() to AER bits
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Håkon Bugge <[email protected]>
[ Upstream commit 9abf79c8d7b40db0e5a34aa8c744ea60ff9a3fcf ]
Previously program_hpx_type2() applied PCIe settings unconditionally, which could incorrectly change bits like Extended Tag Field Enable and Enable Relaxed Ordering.
When _HPX was added to ACPI r3.0, the intent of the PCIe Setting Record (Type 2) in sec 6.2.7.3 was to configure AER registers when the OS does not own the AER Capability:
The PCI Express setting record contains ... [the AER] Uncorrectable Error Mask, Uncorrectable Error Severity, Correctable Error Mask ... to be used when configuring registers in the Advanced Error Reporting Extended Capability Structure ...
OSPM [1] will only evaluate _HPX with Setting Record – Type 2 if OSPM is not controlling the PCI Express Advanced Error Reporting capability.
ACPI r3.0b, sec 6.2.7.3, added more AER registers, including registers in the PCIe Capability with AER-related bits, and the restriction that the OS use this only when it owns PCIe native hotplug:
... when configuring PCI Express registers in the Advanced Error Reporting Extended Capability Structure *or PCI Express Capability Structure* ...
An OS that has assumed ownership of native hot plug but does not ... have ownership of the AER register set must use ... the Type 2 record to program the AER registers ...
However, since the Type 2 record also includes register bits that have functions other than AER, the OS must ignore values ... that are not applicable.
Restrict program_hpx_type2() to only the intended purpose:
- Apply settings only when OS owns PCIe native hotplug but not AER,
- Only touch the AER-related bits (Error Reporting Enables) in Device Control
- Don't touch Link Control at all, since nothing there seems AER-related, but log _HPX settings for debugging purposes
Note that Read Completion Boundary is now configured elsewhere, since it is unrelated to _HPX.
[1] Operating System-directed configuration and Power Management
--- drivers/pci/pci-acpi.c | 59 +++++++++++++++++------------------------- drivers/pci/pci.h | 3 +++ drivers/pci/pcie/aer.c | 3 --- 3 files changed, 27 insertions(+), 38 deletions(-)
drivers/pci/pci-acpi.c +24 -35
@@ -271,21 +271,6 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,return AE_OK ; } -static bool pcie_root_rcb_set (struct pci_dev *dev) -{ -struct pci_dev *rp =pcie_find_root_port (dev); -u16 lnkctl; - -if (!rp) -return false; - -pcie_capability_read_word (rp,PCI_EXP_LNKCTL , &lnkctl); -if (lnkctl &PCI_EXP_LNKCTL_RCB ) -return true; - -return false; -} -/* _HPX PCI Express Setting Record (Type 2) */ struct hpx_type2 {u32 revision;
@@ -311,6 +296,7 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) {int pos;u32 reg32; +const struct pci_host_bridge *host;if (!hpx)return ;
@@ -318,6 +304,15 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)if (!pci_is_pcie (dev))return ; + host =pci_find_host_bridge (dev->bus); + +/* +* Only do the _HPX Type 2 programming if OS owns PCIe native +* hotplug but not AER. +*/ +if (!host->native_pcie_hotplug || host->native_aer) +return ; +if (hpx->revision >1 ) {pci_warn (dev,"PCIe settings rev %d not supported\n" , hpx->revision);
@@ -325,33 +320,27 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) }/* -* Don't allow _HPX to change MPS or MRRS settings. We manage -* those to make sure they're consistent with the rest of the -* platform. +* We only allow _HPX to program DEVCTL bits related to AER, namely +* PCI_EXP_DEVCTL_CERE, PCI_EXP_DEVCTL_NFERE, PCI_EXP_DEVCTL_FERE, +* and PCI_EXP_DEVCTL_URRE. +* +* The rest of DEVCTL is managed by the OS to make sure it's +* consistent with the rest of the platform. */ - hpx->pci_exp_devctl_and |=PCI_EXP_DEVCTL_PAYLOAD | -PCI_EXP_DEVCTL_READRQ ; - hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD | -PCI_EXP_DEVCTL_READRQ ); + hpx->pci_exp_devctl_and |= ~PCI_EXP_AER_FLAGS ; + hpx->pci_exp_devctl_or &=PCI_EXP_AER_FLAGS ;/* Initialize Device Control Register */ pcie_capability_clear_and_set_word (dev,PCI_EXP_DEVCTL , ~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or); -/* Initialize Link Control Register */ +/* Log if _HPX attempts to modify Link Control Register */ if (pcie_cap_has_lnkctl (dev)) { - -/* -* If the Root Port supports Read Completion Boundary of -* 128, set RCB to 128. Otherwise, clear it. -*/ - hpx->pci_exp_lnkctl_and |=PCI_EXP_LNKCTL_RCB ; - hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB ; -if (pcie_root_rcb_set (dev)) - hpx->pci_exp_lnkctl_or |=PCI_EXP_LNKCTL_RCB ; - -pcie_capability_clear_and_set_word (dev,PCI_EXP_LNKCTL , - ~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or); +if (hpx->pci_exp_lnkctl_and !=0xffff || + hpx->pci_exp_lnkctl_or !=0 ) +pci_info (dev,"_HPX attempts Link Control setting (AND %#06x OR %#06x)\n" , + hpx->pci_exp_lnkctl_and, + hpx->pci_exp_lnkctl_or); }/* Find Advanced Error Reporting Enhanced Capability */
drivers/pci/pci.h +3 -0
@@ -88,6 +88,9 @@ struct pcie_tlp_log;#define PCI_BUS_BRIDGE_MEM_WINDOW 1#define PCI_BUS_BRIDGE_PREF_MEM_WINDOW 2 +#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) +extern const unsigned char pcie_link_speed[];extern bool pci_early_dump;
drivers/pci/pcie/aer.c +0 -3
@@ -238,9 +238,6 @@ void pcie_ecrc_get_policy(char *str) }#endif /* CONFIG_PCIE_ECRC */ -#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) -int pcie_aer_is_native (struct pci_dev *dev) {struct pci_host_bridge *host =pci_find_host_bridge (dev->bus);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 289/641 selftests/mm: fix usage of FORCE_READ() in cow tests
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Brodsky <[email protected]>
[ Upstream commit bce1dabd310e87fefe0645fec9ba98b84d37e418 ]
Commit 5bbc2b785e63 ("selftests/mm: fix FORCE_READ to read input value correctly") modified FORCE_READ() to take a value instead of a pointer. It also changed most of the call sites accordingly, but missed many of them in cow.c. In those cases, we ended up with the pointer itself being read, not the memory it points to.
No failure occurred as a result, so it looks like the tests work just fine without faulting in. However, the huge_zeropage tests explicitly check that pages are populated, so those became skipped.
Convert all the remaining FORCE_READ() to fault in the mapped page, as was originally intended. This allows the huge_zeropage tests to run again (3 tests in total).
--- tools/testing/selftests/mm/cow.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
tools/testing/selftests/mm/cow.c +8 -8
@@ -1612,8 +1612,8 @@ static void run_with_huge_zeropage(non_anon_test_fn fn, const char *desc)* the first sub-page and test if we get another sub-page populated * automatically. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);if (!pagemap_is_populated (pagemap_fd, mem + pagesize) || !pagemap_is_populated (pagemap_fd, smem + pagesize)) {ksft_test_result_skip ("Did not get THPs populated\n" );
@@ -1663,8 +1663,8 @@ static void run_with_memfd(non_anon_test_fn fn, const char *desc) }/* Fault the page in. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);fn (mem, smem, pagesize); munmap:
@@ -1719,8 +1719,8 @@ static void run_with_tmpfile(non_anon_test_fn fn, const char *desc) }/* Fault the page in. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);fn (mem, smem, pagesize); munmap:
@@ -1773,8 +1773,8 @@ static void run_with_memfd_hugetlb(non_anon_test_fn fn, const char *desc, }/* Fault the page in. */ -FORCE_READ (mem); -FORCE_READ (smem); +FORCE_READ (*mem); +FORCE_READ (*smem);fn (mem, smem, hugetlbsize); munmap:
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 290/641 selftests/mm: fix faulting-in code in pagemap_ioctl test
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Brodsky <[email protected]>
[ Upstream commit 7e938f00b00319510ae097e20b7487dfa578d53f ]
One of the pagemap_ioctl tests attempts to fault in pages by memcpy()'ing them to an unused buffer. This probably worked originally, but since commit 46036188ea1f ("selftests/mm: build with -O2") the compiler is free to optimise away that unused buffer and the memcpy() with it. As a result there might not be any resident page in the mapping and the test may fail.
We don't need to copy all that memory anyway. Just fault in every page.
While at it also make sure to compute the number of pages once using simple integer arithmetic instead of ceilf() and implicit conversions.
--- tools/testing/selftests/mm/pagemap_ioctl.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
tools/testing/selftests/mm/pagemap_ioctl.c +4 -5
@@ -1052,11 +1052,10 @@ static void test_simple(void)int sanity_tests (void ) {unsigned long long mem_size, vec_size; -long ret, fd, i, buf_size; +long ret, fd, i, buf_size, nr_pages;struct page_region *vec;char *mem, *fmem;struct stat sbuf; -char *tmp_buf;/* 1. wrong operation */ mem_size =10 * page_size;
@@ -1167,14 +1166,14 @@ int sanity_tests(void)if (fmem ==MAP_FAILED )ksft_exit_fail_msg ("error nomem %d %s\n" , errno,strerror (errno)); - tmp_buf =malloc (sbuf.st_size); -memcpy (tmp_buf, fmem, sbuf.st_size); + nr_pages = (sbuf.st_size + page_size -1 ) / page_size; +force_read_pages (fmem, nr_pages, page_size); ret =pagemap_ioctl (fmem, sbuf.st_size, vec, vec_size,0 ,0 ,0 ,PAGEMAP_NON_WRITTEN_BITS ,0 ,PAGEMAP_NON_WRITTEN_BITS );ksft_test_result (ret >=0 && vec[0 ].start == (uintptr_t )fmem && -LEN (vec[0 ]) ==ceilf ((float )sbuf.st_size/page_size) && +LEN (vec[0 ]) == nr_pages && (vec[0 ].categories &PAGE_IS_FILE ),"%s Memory mapped file\n" , __func__);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 291/641 ipc: dont audit capability check in ipc_permissions()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ondrej Mosnacek <[email protected]>
[ Upstream commit 071588136007482d70fd2667b827036bc60b1f8f ]
The IPC sysctls implement the ctl_table_root::permissions hook and they override the file access mode based on the CAP_CHECKPOINT_RESTORE capability, which is being checked regardless of whether any access is actually denied or not, so if an LSM denies the capability, an audit record may be logged even when access is in fact granted.
It wouldn't be viable to restructure the sysctl permission logic to only check the capability when the access would be actually denied if it's not granted. Thus, do the same as in net_ctl_permissions() (net/sysctl_net.c) - switch from ns_capable() to ns_capable_noaudit(), so that the check never emits an audit record.
Stable-dep-of: 8924336531e2 ("ipc: don't audit capability check in ipc_permissions()")
--- include/linux/capability.h | 6 ++++++ ipc/ipc_sysctl.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-)
include/linux/capability.h +6 -0
@@ -203,6 +203,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)ns_capable (ns,CAP_SYS_ADMIN ); } +static inline bool checkpoint_restore_ns_capable_noaudit (struct user_namespace *ns) +{ +return ns_capable_noaudit (ns,CAP_CHECKPOINT_RESTORE ) || +ns_capable_noaudit (ns,CAP_SYS_ADMIN ); +} +/* audit system wants to get cap info from files as well */ int get_vfs_caps_from_disk(struct mnt_idmap *idmap, const struct dentry *dentry,
ipc/ipc_sysctl.c +1 -1
@@ -214,7 +214,7 @@ static int ipc_permissions(struct ctl_table_header *head, const struct ctl_tableif (((table->data == &ns->ids[IPC_SEM_IDS ].next_id) || (table->data == &ns->ids[IPC_MSG_IDS ].next_id) || (table->data == &ns->ids[IPC_SHM_IDS ].next_id)) && -checkpoint_restore_ns_capable (ns->user_ns)) +checkpoint_restore_ns_capable_noaudit (ns->user_ns)) mode =0666 ; else#endif
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 292/641 ucount: check for CAP_SYS_RESOURCE using ns_capable_noaudit()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ondrej Mosnacek <[email protected]>
[ Upstream commit 0895a000e4fff9e950a7894210db45973e485c35 ]
The user.* sysctls implement the ctl_table_root::permissions hook and they override the file access mode based on the CAP_SYS_RESOURCE capability (at most rwx if capable, at most r-- if not). The capability is being checked unconditionally, so if an LSM denies the capability, an audit record may be logged even when access is in fact granted.
Given the logic in the set_permissions() function in kernel/ucount.c and the unfortunate way the permission checking is implemented, it doesn't seem viable to avoid false positive denials by deferring the capability check. Thus, do the same as in net_ctl_permissions() (net/sysctl_net.c) - switch from ns_capable() to ns_capable_noaudit(), so that the check never logs an audit record.
--- kernel/ucount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/ucount.c +1 -1
@@ -47,7 +47,7 @@ static int set_permissions(struct ctl_table_header *head,int mode;/* Allow users with CAP_SYS_RESOURCE unrestrained access */ -if (ns_capable (user_ns,CAP_SYS_RESOURCE )) +if (ns_capable_noaudit (user_ns,CAP_SYS_RESOURCE )) mode = (table->mode &S_IRWXU ) >>6 ;else /* Allow all others at most read-only access */
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 293/641 jfs: avoid -Wtautological-constant-out-of-range-compare warning
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnd Bergmann <[email protected]>
[ Upstream commit 7833570dae833028337bb53b7f389825b910c100 ]
A recent change for the range check started triggering a clang warning:
fs/jfs/jfs_dtree.c:2906:31: error: result of comparison of constant 128 with expression of type 's8' (aka 'signed char') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
2906 | if (stbl[i] < 0 || stbl[i] >= DTPAGEMAXSLOT) {
| ~~~~~~~ ^ ~~~~~~~~~~~~~
fs/jfs/jfs_dtree.c:3111:30: error: result of comparison of constant 128 with expression of type 's8' (aka 'signed char') is always false [-Werror,-Wtautological-constant-out-of-range-compare]
3111 | if (stbl[0] < 0 || stbl[0] >= DTPAGEMAXSLOT) {
| ~~~~~~~ ^ ~~~~~~~~~~~~~Both the old and the new check were useless, but the previous version apparently did not lead to the warning.
Remove the extraneous range check for simplicity.
--- fs/jfs/jfs_dtree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/jfs/jfs_dtree.c +2 -2
@@ -2903,7 +2903,7 @@ int jfs_readdir(struct file *file, struct dir_context *ctx) stbl =DT_GETSTBL (p);for (i = index; i < p->header.nextindex; i++) { -if (stbl[i] <0 || stbl[i] >=DTPAGEMAXSLOT ) { +if (stbl[i] <0 ) {jfs_err ("JFS: Invalid stbl[%d] = %d for inode %ld, block = %lld" , i, stbl[i], (long )ip->i_ino, (long long )bn);free_page (dirent_buf);
@@ -3108,7 +3108,7 @@ static int dtReadFirst(struct inode *ip, struct btstack * btstack)/* get the leftmost entry */ stbl =DT_GETSTBL (p); -if (stbl[0 ] <0 || stbl[0 ] >=DTPAGEMAXSLOT ) { +if (stbl[0 ] <0 ) {DT_PUTPAGE (mp);jfs_error (ip->i_sb,"stbl[0] out of bound\n" );return -EIO ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 294/641 tcp: ECT_1_NEGOTIATION and NEEDS_ACCECN identifiers
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Yu Chang <[email protected]>
[ Upstream commit 100f946b8d44b64bc0b8a8c30d283105031c0a77 ]
Two flags for congestion control (CC) module are added in this patch related to AccECN negotiation. First, a new flag (TCP_CONG_NEEDS_ACCECN) defines that the CC expects to negotiate AccECN functionality using the ECE, CWR and AE flags in the TCP header.
Second, during ECN negotiation, ECT(0) in the IP header is used. This patch enables CC to control whether ECT(0) or ECT(1) should be used on a per-segment basis. A new flag (TCP_CONG_ECT_1_NEGOTIATION) defines the expected ECT value in the IP header by the CA when not-yet initialized for the connection.
The detailed AccECN negotiaotn can be found in IETF RFC9768.
Stable-dep-of: c5ff6b837159 ("tcp: accecn: handle unexpected AccECN negotiation feedback")
--- include/net/inet_ecn.h | 20 +++++++++++++++++--- include/net/tcp.h | 21 ++++++++++++++++++++- include/net/tcp_ecn.h | 13 ++++++++++--- net/ipv4/tcp_cong.c | 5 +++-- net/ipv4/tcp_input.c | 3 ++- 5 files changed, 52 insertions(+), 10 deletions(-)
include/net/inet_ecn.h +17 -3
@@ -51,11 +51,25 @@ static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)return outer; } +/* Apply either ECT(0) or ECT(1) */ +static inline void __INET_ECN_xmit (struct sock *sk,bool use_ect_1) +{ +__u8 ect = use_ect_1 ?INET_ECN_ECT_1 :INET_ECN_ECT_0 ; + +/* Mask the complete byte in case the connection alternates between +* ECT(0) and ECT(1). +*/ +inet_sk (sk)->tos &= ~INET_ECN_MASK ; +inet_sk (sk)->tos |= ect; +if (inet6_sk (sk)) { +inet6_sk (sk)->tclass &= ~INET_ECN_MASK ; +inet6_sk (sk)->tclass |= ect; + } +} +static inline void INET_ECN_xmit (struct sock *sk) { -inet_sk (sk)->tos |=INET_ECN_ECT_0 ; -if (inet6_sk (sk) !=NULL ) -inet6_sk (sk)->tclass |=INET_ECN_ECT_0 ; +__INET_ECN_xmit (sk, false); }static inline void INET_ECN_dontxmit (struct sock *sk)
include/net/tcp.h +20 -1
@@ -1190,7 +1190,12 @@ enum tcp_ca_ack_event_flags {#define TCP_CONG_NON_RESTRICTED BIT(0)/* Requires ECN/ECT set on all packets */ #define TCP_CONG_NEEDS_ECN BIT(1) -#define TCP_CONG_MASK (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN) +/* Require successfully negotiated AccECN capability */ +#define TCP_CONG_NEEDS_ACCECN BIT(2) +/* Use ECT(1) instead of ECT(0) while the CA is uninitialized */ +#define TCP_CONG_ECT_1_NEGOTIATION BIT(3) +#define TCP_CONG_MASK (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN | \ + TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION)union tcp_cc_info ;
@@ -1322,6 +1327,20 @@ static inline bool tcp_ca_needs_ecn(const struct sock *sk)return icsk->icsk_ca_ops->flags &TCP_CONG_NEEDS_ECN ; } +static inline bool tcp_ca_needs_accecn (const struct sock *sk) +{ +const struct inet_connection_sock *icsk =inet_csk (sk); + +return icsk->icsk_ca_ops->flags &TCP_CONG_NEEDS_ACCECN ; +} + +static inline bool tcp_ca_ect_1_negotiation (const struct sock *sk) +{ +const struct inet_connection_sock *icsk =inet_csk (sk); + +return icsk->icsk_ca_ops->flags &TCP_CONG_ECT_1_NEGOTIATION ; +} +static inline void tcp_ca_event (struct sock *sk,const enum tcp_ca_event event) {const struct inet_connection_sock *icsk =inet_csk (sk);
include/net/tcp_ecn.h +10 -3
@@ -31,6 +31,12 @@ enum tcp_accecn_option { TCP_ACCECN_OPTION_FULL = 2, }; +/* Apply either ECT(0) or ECT(1) based on TCP_CONG_ECT_1_NEGOTIATION flag */ +static inline void INET_ECN_xmit_ect_1_negotiation (struct sock *sk) +{ +__INET_ECN_xmit (sk,tcp_ca_ect_1_negotiation (sk)); +} +static inline void tcp_ecn_queue_cwr (struct tcp_sock *tp) {/* Do not set CWR if in AccECN mode! */
@@ -561,7 +567,7 @@ static inline void tcp_ecn_send_synack(struct sock *sk, struct sk_buff *skb)TCP_SKB_CB (skb)->tcp_flags &= ~TCPHDR_ECE ; else if (tcp_ca_needs_ecn(sk) || tcp_bpf_ca_needs_ecn(sk)) - INET_ECN_xmit(sk); + INET_ECN_xmit_ect_1_negotiation(sk);if (tp->ecn_flags &TCP_ECN_MODE_ACCECN ) {TCP_SKB_CB (skb)->tcp_flags &= ~TCPHDR_ACE ;
@@ -579,7 +585,8 @@ static inline void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)bool use_ecn, use_accecn;u8 tcp_ecn =READ_ONCE (sock_net (sk)->ipv4.sysctl_tcp_ecn); - use_accecn = tcp_ecn ==TCP_ECN_IN_ACCECN_OUT_ACCECN ; + use_accecn = tcp_ecn ==TCP_ECN_IN_ACCECN_OUT_ACCECN || +tcp_ca_needs_accecn (sk); use_ecn = tcp_ecn ==TCP_ECN_IN_ECN_OUT_ECN || tcp_ecn ==TCP_ECN_IN_ACCECN_OUT_ECN ||tcp_ca_needs_ecn (sk) || bpf_needs_ecn || use_accecn;
@@ -595,7 +602,7 @@ static inline void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)if (use_ecn) {if (tcp_ca_needs_ecn (sk) || bpf_needs_ecn) -INET_ECN_xmit (sk); +INET_ECN_xmit_ect_1_negotiation (sk);TCP_SKB_CB (skb)->tcp_flags |=TCPHDR_ECE |TCPHDR_CWR ;if (use_accecn) {
net/ipv4/tcp_cong.c +3 -2
@@ -16,6 +16,7 @@#include <linux/gfp.h> #include <linux/jhash.h> #include <net/tcp.h> +#include <net/tcp_ecn.h> #include <trace/events/tcp.h> static DEFINE_SPINLOCK (tcp_cong_list_lock);
@@ -227,7 +228,7 @@ void tcp_assign_congestion_control(struct sock *sk)memset (icsk->icsk_ca_priv,0 ,sizeof (icsk->icsk_ca_priv));if (ca->flags &TCP_CONG_NEEDS_ECN ) -INET_ECN_xmit (sk); +INET_ECN_xmit_ect_1_negotiation (sk);else INET_ECN_dontxmit (sk); }
@@ -257,7 +258,7 @@ static void tcp_reinit_congestion_control(struct sock *sk,memset (icsk->icsk_ca_priv,0 ,sizeof (icsk->icsk_ca_priv));if (ca->flags &TCP_CONG_NEEDS_ECN ) -INET_ECN_xmit (sk); +INET_ECN_xmit_ect_1_negotiation (sk);else INET_ECN_dontxmit (sk);
net/ipv4/tcp_input.c +2 -1
@@ -7222,7 +7222,8 @@ static void tcp_ecn_create_request(struct request_sock *req,u32 ecn_ok_dst;if (tcp_accecn_syn_requested (th) && -READ_ONCE (net->ipv4.sysctl_tcp_ecn) >=3 ) { + (READ_ONCE (net->ipv4.sysctl_tcp_ecn) >=3 || +tcp_ca_needs_accecn (listen_sk))) {inet_rsk (req)->ecn_ok =1 ;tcp_rsk (req)->accecn_ok =1 ; tcp_rsk(req)->syn_ect_rcv = TCP_SKB_CB(skb)->ip_dsfield &
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 295/641 tcp: disable RFC3168 fallback identifier for CC modules
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Yu Chang <[email protected]>
[ Upstream commit e68c28f22f46ecfdec3656ae785dd8ccbb4d557d ]
When AccECN is not successfully negociated for a TCP flow, it defaults fallback to classic ECN (RFC3168). However, L4S service will fallback to non-ECN.
This patch enables congestion control module to control whether it should not fallback to classic ECN after unsuccessful AccECN negotiation. A new CA module flag (TCP_CONG_NO_FALLBACK_RFC3168) identifies this behavior expected by the CA.
Stable-dep-of: c5ff6b837159 ("tcp: accecn: handle unexpected AccECN negotiation feedback")
--- include/net/tcp.h | 12 +++++++++++- include/net/tcp_ecn.h | 11 ++++++++--- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_minisocks.c | 7 ++++--- 4 files changed, 24 insertions(+), 8 deletions(-)
include/net/tcp.h +11 -1
@@ -1194,8 +1194,11 @@ enum tcp_ca_ack_event_flags {#define TCP_CONG_NEEDS_ACCECN BIT(2)/* Use ECT(1) instead of ECT(0) while the CA is uninitialized */ #define TCP_CONG_ECT_1_NEGOTIATION BIT(3) +/* Cannot fallback to RFC3168 during AccECN negotiation */ +#define TCP_CONG_NO_FALLBACK_RFC3168 BIT(4)#define TCP_CONG_MASK (TCP_CONG_NON_RESTRICTED | TCP_CONG_NEEDS_ECN | \ - TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION) + TCP_CONG_NEEDS_ACCECN | TCP_CONG_ECT_1_NEGOTIATION | \ + TCP_CONG_NO_FALLBACK_RFC3168)union tcp_cc_info ;
@@ -1341,6 +1344,13 @@ static inline bool tcp_ca_ect_1_negotiation(const struct sock *sk)return icsk->icsk_ca_ops->flags &TCP_CONG_ECT_1_NEGOTIATION ; } +static inline bool tcp_ca_no_fallback_rfc3168 (const struct sock *sk) +{ +const struct inet_connection_sock *icsk =inet_csk (sk); + +return icsk->icsk_ca_ops->flags &TCP_CONG_NO_FALLBACK_RFC3168 ; +} +static inline void tcp_ca_event (struct sock *sk,const enum tcp_ca_event event) {const struct inet_connection_sock *icsk =inet_csk (sk);
include/net/tcp_ecn.h +8 -3
@@ -507,7 +507,9 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb* | ECN | AccECN | 0 0 1 | Classic ECN | * +========+========+============+=============+ */ -if (tcp_ecn_mode_pending (tp)) +if (tcp_ca_no_fallback_rfc3168 (sk)) +tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); +else if (tcp_ecn_mode_pending (tp))/* Downgrade from AccECN, or requested initially */ tcp_ecn_mode_set (tp,TCP_ECN_MODE_RFC3168 );break ;
@@ -531,9 +533,11 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb } } -static inline void tcp_ecn_rcv_syn (struct tcp_sock *tp,const struct tcphdr *th, +static inline void tcp_ecn_rcv_syn (struct sock *sk,const struct tcphdr *th,const struct sk_buff *skb) { +struct tcp_sock *tp =tcp_sk (sk); +if (tcp_ecn_mode_pending (tp)) {if (!tcp_accecn_syn_requested (th)) {/* Downgrade to classic ECN feedback */
@@ -545,7 +549,8 @@ static inline void tcp_ecn_rcv_syn(struct tcp_sock *tp, const struct tcphdr *th,tcp_ecn_mode_set (tp,TCP_ECN_MODE_ACCECN ); } } -if (tcp_ecn_mode_rfc3168 (tp) && (!th->ece || !th->cwr)) +if (tcp_ecn_mode_rfc3168 (tp) && + (!th->ece || !th->cwr ||tcp_ca_no_fallback_rfc3168 (sk)))tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); }
net/ipv4/tcp_input.c +1 -1
@@ -6817,7 +6817,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, tp->snd_wl1 =TCP_SKB_CB (skb)->seq; tp->max_window = tp->snd_wnd; -tcp_ecn_rcv_syn (tp, th, skb); +tcp_ecn_rcv_syn (sk, th, skb);tcp_mtup_init (sk);tcp_sync_mss (sk, icsk->icsk_pmtu_cookie);
net/ipv4/tcp_minisocks.c +4 -3
@@ -488,9 +488,10 @@ static void tcp_ecn_openreq_child(struct sock *sk, tp->accecn_opt_demand =1 ;tcp_ecn_received_counters_payload (sk, skb); }else { -tcp_ecn_mode_set (tp,inet_rsk (req)->ecn_ok ? -TCP_ECN_MODE_RFC3168 : -TCP_ECN_DISABLED ); +if (inet_rsk (req)->ecn_ok && !tcp_ca_no_fallback_rfc3168 (sk)) +tcp_ecn_mode_set (tp,TCP_ECN_MODE_RFC3168 ); +else +tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); } }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 296/641 tcp: accecn: handle unexpected AccECN negotiation feedback
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chia-Yu Chang <[email protected]>
[ Upstream commit c5ff6b83715919767f181f13e992b5055812a194 ]
According to Sections 3.1.2 and 3.1.3 of AccECN spec (RFC9768).
In Section 3.1.2, it says an AccECN implementation has no need to recognize or support the Server response labelled 'Nonce' or ECN-nonce feedback more generally, as RFC 3540 has been reclassified as Historic. AccECN is compatible with alternative ECN feedback integrity approaches to the nonce. The SYN/ACK labelled 'Nonce' with (AE,CWR,ECE) = (1,0,1) is reserved for future use. A TCP Client (A) that receives such a SYN/ACK follows the procedure for forward compatibility given in Section 3.1.3.
Then in Section 3.1.3, it says if a TCP Client has sent a SYN requesting AccECN feedback with (AE,CWR,ECE) = (1,1,1) then receives a SYN/ACK with the currently reserved combination (AE,CWR,ECE) = (1,0,1) but it does not have logic specific to such a combination, the Client MUST enable AccECN mode as if the SYN/ACK onfirmed that the Server supported AccECN and as if it fed back that the IP-ECN field on the SYN had arrived unchanged.
--- include/net/tcp_ecn.h | 44 ++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-)
include/net/tcp_ecn.h +31 -13
@@ -473,6 +473,26 @@ static inline u8 tcp_accecn_option_init(const struct sk_buff *skb,return TCP_ACCECN_OPT_COUNTER_SEEN ; } +static inline void tcp_ecn_rcv_synack_accecn (struct sock *sk, +const struct sk_buff *skb,u8 dsf) +{ +struct tcp_sock *tp =tcp_sk (sk); + +tcp_ecn_mode_set (tp,TCP_ECN_MODE_ACCECN ); + tp->syn_ect_rcv = dsf &INET_ECN_MASK ; +/* Demand Accurate ECN option in response to the SYN on the SYN/ACK +* and the TCP server will try to send one more packet with an AccECN +* Option at a later point during the connection. +*/ +if (tp->rx_opt.accecn && + tp->saw_accecn_opt <TCP_ACCECN_OPT_COUNTER_SEEN ) { +u8 saw_opt =tcp_accecn_option_init (skb, tp->rx_opt.accecn); + +tcp_accecn_saw_opt_fail_recv (tp, saw_opt); + tp->accecn_opt_demand =2 ; + } +} +/* See Table 2 of the AccECN draft */ static inline void tcp_ecn_rcv_synack (struct sock *sk,const struct sk_buff *skb,const struct tcphdr *th,u8 ip_dsfield)
@@ -495,13 +515,11 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skbtcp_ecn_mode_set (tp,TCP_ECN_DISABLED );break ;case 0x1 : -case 0x5 :/* +========+========+============+=============+ * | A | B | SYN/ACK | Feedback | * | | | B->A | Mode of A | * | | | AE CWR ECE | | * +========+========+============+=============+ -* | AccECN | Nonce | 1 0 1 | (Reserved) | * | AccECN | ECN | 0 0 1 | Classic ECN | * | Nonce | AccECN | 0 0 1 | Classic ECN | * | ECN | AccECN | 0 0 1 | Classic ECN |
@@ -509,20 +527,20 @@ static inline void tcp_ecn_rcv_synack(struct sock *sk, const struct sk_buff *skb*/ if (tcp_ca_no_fallback_rfc3168 (sk))tcp_ecn_mode_set (tp,TCP_ECN_DISABLED ); -else if (tcp_ecn_mode_pending (tp)) -/* Downgrade from AccECN, or requested initially */ +else tcp_ecn_mode_set (tp,TCP_ECN_MODE_RFC3168 );break ; -default : -tcp_ecn_mode_set (tp,TCP_ECN_MODE_ACCECN ); - tp->syn_ect_rcv = ip_dsfield &INET_ECN_MASK ; -if (tp->rx_opt.accecn && - tp->saw_accecn_opt <TCP_ACCECN_OPT_COUNTER_SEEN ) { -u8 saw_opt =tcp_accecn_option_init (skb, tp->rx_opt.accecn); - -tcp_accecn_saw_opt_fail_recv (tp, saw_opt); - tp->accecn_opt_demand =2 ; +case 0x5 : +if (tcp_ecn_mode_pending (tp)) { +tcp_ecn_rcv_synack_accecn (sk, skb, ip_dsfield); +if (INET_ECN_is_ce (ip_dsfield)) { + tp->received_ce++; + tp->received_ce_pending++; + } } +break ; +default : +tcp_ecn_rcv_synack_accecn (sk, skb, ip_dsfield);if (INET_ECN_is_ce (ip_dsfield) &&tcp_accecn_validate_syn_feedback (sk, ace, tp->syn_ect_snt)) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 297/641 of: unittest: fix possible null-pointer dereferences in of_unittest_property_copy()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tuo Li <[email protected]>
[ Upstream commit d289cb7fcefe41a54d8f9c6d0e0947f5f82b15c6 ]
This function first duplicates p1 and p2 into new, and then checks whether the duplication succeeds. However, if the duplication fails (e.g., kzalloc() returns NULL in __of_prop_dup()), new will be NULL but is still dereferenced in __of_prop_free(). To ensure that the unit test continues to run even when duplication fails, add a NULL check before calling __of_prop_free().
--- drivers/of/unittest.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/of/unittest.c +4 -2
@@ -804,11 +804,13 @@ static void __init of_unittest_property_copy(void) new =__of_prop_dup (&p1,GFP_KERNEL );unittest (new &&propcmp (&p1, new),"empty property didn't copy correctly\n" ); -__of_prop_free (new); +if (new) +__of_prop_free (new); new =__of_prop_dup (&p2,GFP_KERNEL );unittest (new &&propcmp (&p2, new),"non-empty property didn't copy correctly\n" ); -__of_prop_free (new); +if (new) +__of_prop_free (new);#endif }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 298/641 mptcp: do not account for OoO in mptcp_rcvbuf_grow()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Abeni <[email protected]>
[ Upstream commit 6b329393502e5857662b851a13f947209c588587 ]
MPTCP-level OoOs are physiological when multiple subflows are active concurrently and will not cause retransmissions nor are caused by drops.
Accounting for them in mptcp_rcvbuf_grow() causes the rcvbuf slowly drifting towards tcp_rmem[2].
Remove such accounting. Note that subflows will still account for TCP-level OoO when the MPTCP-level rcvbuf is propagated.
This also closes a subtle and very unlikely race condition with rcvspace init; active sockets with user-space holding the msk-level socket lock, could complete such initialization in the receive callback, after that the first OoO data reaches the rcvbuf and potentially triggering a divide by zero Oops.
--- net/mptcp/protocol.c | 6 ------ 1 file changed, 6 deletions(-)
net/mptcp/protocol.c +0 -6
@@ -224,9 +224,6 @@ static bool mptcp_rcvbuf_grow(struct sock *sk, u32 newval)do_div (grow, oldval); rcvwin += grow <<1 ; -if (!RB_EMPTY_ROOT (&msk->out_of_order_queue)) - rcvwin +=MPTCP_SKB_CB (msk->ooo_last_skb)->end_seq - msk->ack_seq; - cap =READ_ONCE (net->ipv4.sysctl_tcp_rmem[2 ]); rcvbuf =min_t (u32,mptcp_space_from_win (sk, rcvwin), cap);
@@ -350,9 +347,6 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb) end:skb_condense (skb);skb_set_owner_r (skb, sk); -/* do not grow rcvbuf for not-yet-accepted or orphaned sockets. */ -if (sk->sk_socket) -mptcp_rcvbuf_grow (sk, msk->rcvq_space.space); } static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 299/641 mptcp: fix receive space timestamp initialization
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paolo Abeni <[email protected]>
[ Upstream commit 70274765fef555af92a1532d5bd5450c691fca9d ]
MPTCP initialize the receive buffer stamp in mptcp_rcv_space_init(), using the provided subflow stamp. Such helper is invoked in several places; for passive sockets, space init happened at clone time.
In such scenario, MPTCP ends-up accesses the subflow stamp before its initialization, leading to quite randomic timing for the first receive buffer auto-tune event, as the timestamp for newly created subflow is not refreshed there.
Fix the issue moving the stamp initialization out of the mentioned helper, at the data transfer start, and always using a fresh timestamp.
--- net/mptcp/protocol.c | 8 ++++---- net/mptcp/protocol.h | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-)
net/mptcp/protocol.c +4 -4
@@ -2056,8 +2056,8 @@ static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied) msk->rcvq_space.copied += copied; - mstamp =div_u64 (tcp_clock_ns (),NSEC_PER_USEC ); - time =tcp_stamp_us_delta (mstamp, msk->rcvq_space.time); + mstamp =mptcp_stamp (); + time =tcp_stamp_us_delta (mstamp,READ_ONCE (msk->rcvq_space.time)); rtt_us = msk->rcvq_space.rtt_us;if (rtt_us && time < (rtt_us >>3 ))
@@ -3421,6 +3421,7 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk,__mptcp_propagate_sndbuf (nsk, ssk);mptcp_rcv_space_init (msk, ssk); + msk->rcvq_space.time =mptcp_stamp ();if (mp_opt->suboptions &OPTION_MPTCP_MPC_ACK )__mptcp_subflow_fully_established (msk, subflow, mp_opt);
@@ -3438,8 +3439,6 @@ void mptcp_rcv_space_init(struct mptcp_sock *msk, const struct sock *ssk) msk->rcvq_space.copied =0 ; msk->rcvq_space.rtt_us =0 ; - msk->rcvq_space.time = tp->tcp_mstamp; -/* initial rcv_space offering made to peer */ msk->rcvq_space.space =min_t (u32, tp->rcv_wnd,TCP_INIT_CWND * tp->advmss);
@@ -3652,6 +3651,7 @@ void mptcp_finish_connect(struct sock *ssk)* accessing the field below */ WRITE_ONCE (msk->local_key, subflow->local_key); +WRITE_ONCE (msk->rcvq_space.time,mptcp_stamp ());mptcp_pm_new_connection (msk, ssk,0 ); }
net/mptcp/protocol.h +5 -0
@@ -870,6 +870,11 @@ static inline bool mptcp_is_fully_established(struct sock *sk)READ_ONCE (mptcp_sk (sk)->fully_established); } +static inline u64 mptcp_stamp (void ) +{ +return div_u64 (tcp_clock_ns (),NSEC_PER_USEC ); +} +void mptcp_rcv_space_init (struct mptcp_sock *msk,const struct sock *ssk);void mptcp_data_ready (struct sock *sk,struct sock *ssk);bool mptcp_finish_join (struct sock *sk);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 300/641 octeontx2-af: Fix PF driver crash with kexec kernel booting
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anshumali Gaur <[email protected]>
[ Upstream commit 2d2d574309e3ae84ee794869a5da8b4c38753a94 ]
During a kexec reboot the hardware is not power-cycled, so AF state from the old kernel can persist into the new kernel. When AF and PF drivers are built as modules, the PF driver may probe before AF reinitializes the hardware.
The PF driver treats the RVUM block revision as an indication that AF initialization is complete. If this value is left uncleared at shutdown, PF may incorrectly assume AF is ready and access stale hardware state, leading to a crash.
Clear the RVUM block revision during AF shutdown to avoid PF mis-detecting AF readiness after kexec.
--- drivers/net/ethernet/marvell/octeontx2/af/rvu.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/net/ethernet/marvell/octeontx2/af/rvu.c +11 -0
@@ -3632,11 +3632,22 @@ static void rvu_remove(struct pci_dev *pdev)devm_kfree (&pdev->dev, rvu); } +static void rvu_shutdown (struct pci_dev *pdev) +{ +struct rvu *rvu =pci_get_drvdata (pdev); + +if (!rvu) +return ; + +rvu_clear_rvum_blk_revid (rvu); +} +static struct pci_driver rvu_driver = { .name =DRV_NAME , .id_table = rvu_id_table, .probe = rvu_probe, .remove = rvu_remove, + .shutdown = rvu_shutdown, };static int __init rvu_init_module (void )
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 301/641 bonding: only set speed/duplex to unknown, if getting speed failed
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Bogendoerfer <[email protected]>
[ Upstream commit 48dec8d88af96039a4a17b8c2f148f2a4066e195 ]
bond_update_speed_duplex() first set speed/duplex to unknown and then asks slave driver for current speed/duplex. Since getting speed/duplex might take longer there is a race, where this false state is visible by /proc/net/bonding. With commit 691b2bf14946 ("bonding:
update port speed when getting bond speed") this race gets more visible,
if user space is calling ethtool on a regular base.
Fix this by only setting speed/duplex to unknown, if link speed is really unknown/unusable.
--- drivers/net/bonding/bond_main.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/net/bonding/bond_main.c +9 -6
@@ -791,26 +791,29 @@ static int bond_update_speed_duplex(struct slave *slave)struct ethtool_link_ksettings ecmd;int res; - slave->speed =SPEED_UNKNOWN ; - slave->duplex =DUPLEX_UNKNOWN ; - res =__ethtool_get_link_ksettings (slave_dev, &ecmd);if (res <0 ) -return 1 ; + goto speed_duplex_unknown;if (ecmd.base.speed ==0 || ecmd.base.speed == ((__u32)-1 )) -return 1 ; + goto speed_duplex_unknown;switch (ecmd.base.duplex) {case DUPLEX_FULL :case DUPLEX_HALF :break ;default : -return 1 ; + goto speed_duplex_unknown; } slave->speed = ecmd.base.speed; slave->duplex = ecmd.base.duplex;return 0 ; + +speed_duplex_unknown: + slave->speed =SPEED_UNKNOWN ; + slave->duplex =DUPLEX_UNKNOWN ; + +return 1 ; }const char *bond_slave_link_status (s8 link)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 302/641 inet: RAW sockets using IPPROTO_RAW MUST drop incoming ICMP
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit c89477ad79446867394360b29bb801010fc3ff22 ]
Yizhou Zhao reported that simply having one RAW socket on protocol IPPROTO_RAW (255) was dangerous.
socket(AF_INET, SOCK_RAW, 255);
A malicious incoming ICMP packet can set the protocol field to 255 and match this socket, leading to FNHE cache changes.
inner = IP(src="192.168.2.1", dst="8.8.8.8", proto=255)/Raw("TEST")
pkt = IP(src="192.168.1.1", dst="192.168.2.1")/ICMP(type=3, code=4, nexthopmtu=576)/inner
"man 7 raw" states:
A protocol of IPPROTO_RAW implies enabled IP_HDRINCL and is able to send any IP protocol that is specified in the passed header. Receiving of all IP protocols via IPPROTO_RAW is not possible using raw sockets.
Make sure we drop these malicious packets.
--- net/ipv4/icmp.c | 14 ++++++++++---- net/ipv6/icmp.c | 6 ++++++ 2 files changed, 16 insertions(+), 4 deletions(-)
net/ipv4/icmp.c +10 -4
@@ -843,16 +843,22 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)/* Checkin full IP header plus 8 bytes of protocol to * avoid additional coding at protocol handlers. */ -if (!pskb_may_pull (skb, iph->ihl *4 +8 )) { -__ICMP_INC_STATS (dev_net_rcu (skb->dev),ICMP_MIB_INERRORS ); -return ; - } +if (!pskb_may_pull (skb, iph->ihl *4 +8 )) + goto out; + +/* IPPROTO_RAW sockets are not supposed to receive anything. */ +if (protocol ==IPPROTO_RAW ) + goto out;raw_icmp_error (skb, protocol, info); ipprot =rcu_dereference (inet_protos[protocol]);if (ipprot && ipprot->err_handler) ipprot->err_handler (skb, info); +return ; + +out: +__ICMP_INC_STATS (dev_net_rcu (skb->dev),ICMP_MIB_INERRORS ); }static bool icmp_tag_validation (int proto)
net/ipv6/icmp.c +6 -0
@@ -870,6 +870,12 @@ enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,if (reason !=SKB_NOT_DROPPED_YET ) goto out; +if (nexthdr ==IPPROTO_RAW ) { +/* Add a more specific reason later ? */ + reason =SKB_DROP_REASON_NOT_SPECIFIED ; + goto out; + } +/* BUGGG_FUTURE: we should try to parse exthdrs in this packet. Without this we will not able f.e. to make source routed pmtu discovery.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 303/641 nfc: hci: shdlc: Stop timers and work before freeing context
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Votokina Victoria <[email protected]>
[ Upstream commit c9efde1e537baed7648a94022b43836a348a074f ]
llc_shdlc_deinit() purges SHDLC skb queues and frees the llc_shdlc structure while its timers and state machine work may still be active.
Timer callbacks can schedule sm_work, and sm_work accesses SHDLC state and the skb queues. If teardown happens in parallel with a queued/running work item, it can lead to UAF and other shutdown races.
Stop all SHDLC timers and cancel sm_work synchronously before purging the queues and freeing the context.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
--- net/nfc/hci/llc_shdlc.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/nfc/hci/llc_shdlc.c +8 -0
@@ -762,6 +762,14 @@ static void llc_shdlc_deinit(struct nfc_llc *llc) {struct llc_shdlc *shdlc =nfc_llc_get_data (llc); +timer_shutdown_sync (&shdlc->connect_timer); +timer_shutdown_sync (&shdlc->t1_timer); +timer_shutdown_sync (&shdlc->t2_timer); + shdlc->t1_active = false; + shdlc->t2_active = false; + +cancel_work_sync (&shdlc->sm_work); +skb_queue_purge (&shdlc->rcv_q);skb_queue_purge (&shdlc->send_q);skb_queue_purge (&shdlc->ack_pending_q);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 304/641 netfilter: nfnetlink_queue: optimize verdict lookup with hash table
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Scott Mitchell <[email protected]>
[ Upstream commit e19079adcd26a25d7d3e586b1837493361fdf8b6 ]
The current implementation uses a linear list to find queued packets by ID when processing verdicts from userspace. With large queue depths and out-of-order verdicting, this O(n) lookup becomes a significant bottleneck, causing userspace verdict processing to dominate CPU time.
Replace the linear search with a hash table for O(1) average-case packet lookup by ID. A global rhashtable spanning all network namespaces attributes hash bucket memory to kernel but is subject to fixed upper bound.
Stable-dep-of: 207b3ebacb61 ("netfilter: nfnetlink_queue: do shared-unconfirmed check before segmentation")
--- include/net/netfilter/nf_queue.h | 3 + net/netfilter/nfnetlink_queue.c | 146 ++++++++++++++++++++++++------- 2 files changed, 119 insertions(+), 30 deletions(-)
include/net/netfilter/nf_queue.h +3 -0
@@ -6,11 +6,13 @@#include <linux/ipv6.h> #include <linux/jhash.h> #include <linux/netfilter.h> +#include <linux/rhashtable-types.h> #include <linux/skbuff.h> /* Each queued (to userspace) skbuff has one of these. */ struct nf_queue_entry {struct list_head list; +struct rhash_head hash_node;struct sk_buff *skb;unsigned int id;unsigned int hook_index;/* index in hook_entries->hook[] */
@@ -20,6 +22,7 @@ struct nf_queue_entry {#endif struct nf_hook_state state;u16 size;/* sizeof(entry) + saved route keys */ +u16 queue_num;/* extra space to store route keys */ };
net/netfilter/nfnetlink_queue.c +116 -30
245 lines not on this page · raw message
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 305/641 netfilter: nfnetlink_queue: do shared-unconfirmed check before segmentation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 207b3ebacb6113acaaec0d171d5307032c690004 ]
Ulrich reports a regression with nfqueue:
If an application did not set the 'F_GSO' capability flag and a gso packet with an unconfirmed nf_conn entry is received all packets are now dropped instead of queued, because the check happens after skb_gso_segment(). In that case, we did have exclusive ownership of the skb and its associated conntrack entry. The elevated use count is due to skb_clone happening via skb_gso_segment().
Move the check so that its peformed vs. the aggregated packet.
Then, annotate the individual segments except the first one so we can do a 2nd check at reinject time.
For the normal case, where userspace does in-order reinjects, this avoids packet drops: first reinjected segment continues traversal and confirms entry, remaining segments observe the confirmed entry.
While at it, simplify nf_ct_drop_unconfirmed(): We only care about unconfirmed entries with a refcnt > 1, there is no need to special-case dying entries.
This only happens with UDP. With TCP, the only unconfirmed packet will be the TCP SYN, those aren't aggregated by GRO.
Next patch adds a udpgro test case to cover this scenario.
--- include/net/netfilter/nf_queue.h | 1 + net/netfilter/nfnetlink_queue.c | 123 +++++++++++++++++++------------ 2 files changed, 75 insertions(+), 49 deletions(-)
include/net/netfilter/nf_queue.h +1 -0
@@ -21,6 +21,7 @@ struct nf_queue_entry {struct net_device *physout;#endif struct nf_hook_state state; +bool nf_ct_is_unconfirmed;u16 size;/* sizeof(entry) + saved route keys */ u16 queue_num;
net/netfilter/nfnetlink_queue.c +74 -49
173 lines not on this page · raw message
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 306/641 netfilter: nft_set_hash: fix get operation on big endian
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 2f635adbe2642d398a0be3ab245accd2987be0c3 ]
tests/shell/testcases/packetpath/set_match_nomatch_hash_fast fails on big endian with:
Error: Could not process rule: No such file or directory reset element ip test s { 244.147.90.126 } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Fatal: Cannot fetch element "244.147.90.126"
... because the wrong bucket is searched, jhash() and jhash1_word are not interchangeable on big endian.
--- net/netfilter/nft_set_hash.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
net/netfilter/nft_set_hash.c +7 -2
@@ -619,15 +619,20 @@ static struct nft_elem_priv * nft_hash_get(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem, unsigned int flags) { + const u32 *key = (const u32 *)&elem->key.val;struct nft_hash *priv =nft_set_priv (set);u8 genmask =nft_genmask_cur (net);struct nft_hash_elem *he;u32 hash; - hash =jhash (elem->key.val.data, set->klen, priv->seed); +if (set->klen ==4 ) + hash =jhash_1word (*key, priv->seed); +else + hash =jhash (key, set->klen, priv->seed); + hash =reciprocal_scale (hash, priv->buckets);hlist_for_each_entry_rcu (he, &priv->table[hash], node) { -if (!memcmp (nft_set_ext_key (&he->ext), elem->key.val.data, set->klen) && +if (!memcmp (nft_set_ext_key (&he->ext), key, set->klen) &&nft_set_elem_active (&he->ext, genmask))return &he->priv; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 307/641 netfilter: nft_counter: fix reset of counters on 32bit archs
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anders Grahn <[email protected]>
[ Upstream commit 1e13f27e0675552161ab1778be9a23a636dde8a7 ]
nft_counter_reset() calls u64_stats_add() with a negative value to reset the counter. This will work on 64bit archs, hence the negative value added will wrap as a 64bit value which then can wrap the stat counter as well.
On 32bit archs, the added negative value will wrap as a 32bit value and _not_ wrapping the stat counter properly. In most cases, this would just lead to a very large 32bit value being added to the stat counter.
Fix by introducing u64_stats_sub().
--- include/linux/u64_stats_sync.h | 10 ++++++++++ net/netfilter/nft_counter.c | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-)
include/linux/u64_stats_sync.h +10 -0
@@ -89,6 +89,11 @@ static inline void u64_stats_add(u64_stats_t *p, unsigned long val)local64_add (val, &p->v); } +static inline void u64_stats_sub (u64_stats_t *p,s64 val) +{ +local64_sub (val, &p->v); +} +static inline void u64_stats_inc (u64_stats_t *p) {local64_inc (&p->v);
@@ -130,6 +135,11 @@ static inline void u64_stats_add(u64_stats_t *p, unsigned long val) p->v += val; } +static inline void u64_stats_sub (u64_stats_t *p,s64 val) +{ + p->v -= val; +} +static inline void u64_stats_inc (u64_stats_t *p) { p->v++;
net/netfilter/nft_counter.c +2 -2
@@ -117,8 +117,8 @@ static void nft_counter_reset(struct nft_counter_percpu_priv *priv, nft_sync =this_cpu_ptr (&nft_counter_sync);u64_stats_update_begin (nft_sync); -u64_stats_add (&this_cpu->packets, -total->packets); -u64_stats_add (&this_cpu->bytes, -total->bytes); +u64_stats_sub (&this_cpu->packets, total->packets); +u64_stats_sub (&this_cpu->bytes, total->bytes);u64_stats_update_end (nft_sync);local_bh_enable ();
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 308/641 netfilter: nft_set_rbtree: fix bogus EEXIST with NLM_F_CREATE with null interval
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 7f9203f41aae8eea74fba6a3370da41332eabcda ]
Userspace adds a non-matching null element to the kernel for historical reasons. This null element is added when the set is populated with elements. Inclusion of this element is conditional, therefore, userspace needs to dump the set content to check for its presence.
If the NLM_F_CREATE flag is turned on, this becomes an issue because kernel bogusly reports EEXIST.
Add special case to ignore NLM_F_CREATE in this case, therefore, re-adding the nul-element never fails.
--- net/netfilter/nf_tables_api.c | 5 +++++ net/netfilter/nft_set_rbtree.c | 13 +++++++++++++ 2 files changed, 18 insertions(+)
net/netfilter/nf_tables_api.c +5 -0
@@ -7637,6 +7637,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,* and an existing one. */ err = -EEXIST ; + }else if (err == -ECANCELED ) { +/* ECANCELED reports an existing nul-element in +* interval sets. +*/ + err =0 ; } goto err_element_clash; }
net/netfilter/nft_set_rbtree.c +13 -0
@@ -39,6 +39,13 @@ static bool nft_rbtree_interval_start(const struct nft_rbtree_elem *rbe)return !nft_rbtree_interval_end (rbe); } +static bool nft_rbtree_interval_null (const struct nft_set *set, +const struct nft_rbtree_elem *rbe) +{ +return (!memchr_inv (nft_set_ext_key (&rbe->ext),0 , set->klen) && +nft_rbtree_interval_end (rbe)); +} +static int nft_rbtree_cmp (const struct nft_set *set,const struct nft_rbtree_elem *e1,const struct nft_rbtree_elem *e2)
@@ -431,6 +438,12 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,*/ if (rbe_le && !nft_rbtree_cmp (set, new, rbe_le) &&nft_rbtree_interval_end (rbe_le) ==nft_rbtree_interval_end (new)) { +/* - ignore null interval, otherwise NLM_F_CREATE bogusly +* reports EEXIST. +*/ +if (nft_rbtree_interval_null (set, new)) +return -ECANCELED ; + *elem_priv = &rbe_le->priv;return -EEXIST ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 309/641 netfilter: nft_set_rbtree: check for partial overlaps in anonymous sets
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 4780ec142cbb24b794129d3080eee5cac2943ffc ]
Userspace provides an optimized representation in case intervals are adjacent, where the end element is omitted.
The existing partial overlap detection logic skips anonymous set checks on start elements for this reason.
However, it is possible to add intervals that overlap to this anonymous where two start elements with the same, eg. A-B, A-C where C < B.
start end
A B
start end
A C
Restore the check on overlapping start elements to report an overlap.
--- net/netfilter/nft_set_rbtree.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-)
net/netfilter/nft_set_rbtree.c +25 -5
@@ -309,11 +309,23 @@ static bool nft_rbtree_update_first(const struct nft_set *set,return false; } +/* Only for anonymous sets which do not allow updates, all element are active. */ +static struct nft_rbtree_elem *nft_rbtree_prev_active (struct nft_rbtree_elem *rbe) +{ +struct rb_node *node; + + node =rb_prev (&rbe->node); +if (!node) +return NULL ; + + return rb_entry(node, struct nft_rbtree_elem, node); +} +static int __nft_rbtree_insert (const struct net *net,const struct nft_set *set,struct nft_rbtree_elem *new,struct nft_elem_priv **elem_priv) { -struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL ; +struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL , *rbe_prev;struct rb_node *node, *next, *parent, **p, *first =NULL ;struct nft_rbtree *priv =nft_set_priv (set);u8 cur_genmask =nft_genmask_cur (net);
@@ -451,11 +463,19 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,/* - new start element with existing closest, less or equal key value * being a start element: partial overlap, reported as -ENOTEMPTY. * Anonymous sets allow for two consecutive start element since they -* are constant, skip them to avoid bogus overlap reports. +* are constant, but validate that this new start element does not +* sit in between an existing start and end elements: partial overlap, +* reported as -ENOTEMPTY. */ -if (!nft_set_is_anonymous (set) && rbe_le && -nft_rbtree_interval_start (rbe_le) &&nft_rbtree_interval_start (new)) -return -ENOTEMPTY ; +if (rbe_le && +nft_rbtree_interval_start (rbe_le) &&nft_rbtree_interval_start (new)) { +if (!nft_set_is_anonymous (set)) +return -ENOTEMPTY ; + + rbe_prev =nft_rbtree_prev_active (rbe_le); +if (rbe_prev &&nft_rbtree_interval_end (rbe_prev)) +return -ENOTEMPTY ; + }/* - new end element with existing closest, less or equal key value * being a end element: partial overlap, reported as -ENOTEMPTY.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 310/641 netfilter: nft_set_rbtree: translate rbtree to array for binary search
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 7e43e0a1141deec651a60109dab3690854107298 ]
The rbtree can temporarily store overlapping inactive elements during the transaction processing, leading to false negative lookups.
To address this issue, this patch adds a .commit function that walks the the rbtree to build a array of intervals of ordered elements. This conversion compacts the two singleton elements that represent the start and the end of the interval into a single interval object for space efficient.
Binary search is O(log n), similar to rbtree lookup time, therefore, performance number should be similar, and there is an implementation available under lib/bsearch.c and include/linux/bsearch.h that is used for this purpose.
This slightly increases memory consumption for this new array that stores pointers to the start and the end of the interval.
With this patch:
# time nft -f 100k-intervals-set.nft
real 0m4.218s user 0m3.544s sys 0m0.400s
Without this patch:
# time nft -f 100k-intervals-set.nft
real 0m3.920s user 0m3.547s sys 0m0.276s
With this patch, with IPv4 intervals:
baseline rbtree (match on first field only): 15254954pps
Without this patch:
baseline rbtree (match on first field only): 10256119pps
This provides a ~50% improvement in matching intervals from packet path.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 341 +++++++++++++++++++++++++-------- 1 file changed, 257 insertions(+), 84 deletions(-)
net/netfilter/nft_set_rbtree.c +257 -84
@@ -10,14 +10,29 @@#include <linux/module.h> #include <linux/list.h> #include <linux/rbtree.h> +#include <linux/bsearch.h> #include <linux/netlink.h> #include <linux/netfilter.h> #include <linux/netfilter/nf_tables.h> #include <net/netfilter/nf_tables_core.h> +struct nft_array_interval { +struct nft_set_ext *from; +struct nft_set_ext *to; +}; + +struct nft_array { +u32 max_intervals; +u32 num_intervals; +struct nft_array_interval *intervals; +struct rcu_head rcu_head; +}; +struct nft_rbtree {struct rb_root root;rwlock_t lock; +struct nft_array __rcu *array; +struct nft_array *array_next;seqcount_rwlock_t count;unsigned long last_gc; };
@@ -54,90 +69,6 @@ static int nft_rbtree_cmp(const struct nft_set *set, set->klen); } -static bool nft_rbtree_elem_expired (const struct nft_rbtree_elem *rbe) -{ -return nft_set_elem_expired (&rbe->ext); -} - -static const struct nft_set_ext * -__nft_rbtree_lookup (const struct net *net,const struct nft_set *set, -const u32 *key,unsigned int seq) -{ -struct nft_rbtree *priv =nft_set_priv (set); -const struct nft_rbtree_elem *rbe, *interval =NULL ; -u8 genmask =nft_genmask_cur (net); -const struct rb_node *parent; -int d; - - parent =rcu_dereference_raw (priv->root.rb_node); -while (parent !=NULL ) { -if (read_seqcount_retry (&priv->count, seq)) -return NULL ; - - rbe = rb_entry(parent, struct nft_rbtree_elem, node); - - d =memcmp (nft_set_ext_key (&rbe->ext), key, set->klen); -if (d <0 ) { - parent =rcu_dereference_raw (parent->rb_left); -if (interval && - !nft_rbtree_cmp (set, rbe, interval) && -nft_rbtree_interval_end (rbe) && -nft_rbtree_interval_start (interval)) -continue ; -if (nft_set_elem_active (&rbe->ext, genmask) && - !nft_rbtree_elem_expired (rbe)) - interval = rbe; - }else if (d >0 ) - parent =rcu_dereference_raw (parent->rb_right); -else { -if (!nft_set_elem_active (&rbe->ext, genmask)) { - parent =rcu_dereference_raw (parent->rb_left); -continue ; - } - -if (nft_rbtree_elem_expired (rbe)) -return NULL ; - -if (nft_rbtree_interval_end (rbe)) { -if (nft_set_is_anonymous (set)) -return NULL ; - parent =rcu_dereference_raw (parent->rb_left); - interval =NULL ; -continue ; - } - -return &rbe->ext; - } - } - -if (set->flags &NFT_SET_INTERVAL && interval !=NULL && -nft_rbtree_interval_start (interval)) -return &interval->ext; - -return NULL ; -} - -INDIRECT_CALLABLE_SCOPE -const struct nft_set_ext * -nft_rbtree_lookup (const struct net *net,const struct nft_set *set, -const u32 *key) -{ -struct nft_rbtree *priv =nft_set_priv (set); -unsigned int seq =read_seqcount_begin (&priv->count); -const struct nft_set_ext *ext; - - ext =__nft_rbtree_lookup (net, set, key, seq); -if (ext || !read_seqcount_retry (&priv->count, seq)) -return ext; - -read_lock_bh (&priv->lock); - seq =read_seqcount_begin (&priv->count); - ext =__nft_rbtree_lookup (net, set, key, seq); -read_unlock_bh (&priv->lock); - -return ext; -} -static bool __nft_rbtree_get (const struct net *net,const struct nft_set *set,const u32 *key,struct nft_rbtree_elem **elem,unsigned int seq,unsigned int flags,u8 genmask)
@@ -228,6 +159,60 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set,return &rbe->priv; } +struct nft_array_lookup_ctx { +const u32 *key; +u32 klen; +}; + +static int nft_array_lookup_cmp (const void *pkey,const void *entry) +{ +const struct nft_array_interval *interval = entry; +const struct nft_array_lookup_ctx *ctx = pkey; +int a, b; + +if (!interval->from) +return 1 ; + + a =memcmp (ctx->key,nft_set_ext_key (interval->from), ctx->klen); +if (!interval->to) + b =-1 ; +else + b =memcmp (ctx->key,nft_set_ext_key (interval->to), ctx->klen); + +if (a >=0 && b <0 ) +return 0 ; + +if (a <0 ) +return -1 ; + +return 1 ; +} + +INDIRECT_CALLABLE_SCOPE +const struct nft_set_ext * +nft_rbtree_lookup (const struct net *net,const struct nft_set *set, +const u32 *key) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array =rcu_dereference (priv->array); +const struct nft_array_interval *interval; +struct nft_array_lookup_ctx ctx = { + .key = key, + .klen = set->klen, + }; + +if (!array) +return NULL ; + + interval =bsearch (&ctx, array->intervals, array->num_intervals, +sizeof (struct nft_array_interval ), + nft_array_lookup_cmp); +if (!interval ||nft_set_elem_expired (interval->from)) +return NULL ; + +return interval->from; +} +static void nft_rbtree_gc_elem_remove (struct net *net,struct nft_set *set,struct nft_rbtree *priv,struct nft_rbtree_elem *rbe)
@@ -514,6 +499,87 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,return 0 ; } +static int nft_array_intervals_alloc (struct nft_array *array,u32 max_intervals) +{ +struct nft_array_interval *intervals; + + intervals =kvcalloc (max_intervals,sizeof (struct nft_array_interval ), +GFP_KERNEL_ACCOUNT ); +if (!intervals) +return -ENOMEM ; + +if (array->intervals) +kvfree (array->intervals); + + array->intervals = intervals; + array->max_intervals = max_intervals; + +return 0 ; +} + +static struct nft_array *nft_array_alloc (u32 max_intervals) +{ +struct nft_array *array; + + array =kzalloc (sizeof (*array),GFP_KERNEL_ACCOUNT ); +if (!array) +return NULL ; + +if (nft_array_intervals_alloc (array, max_intervals) <0 ) { +kfree (array); +return NULL ; + } + +return array; +} + +#define NFT_ARRAY_EXTRA_SIZE 10240 + +/* Similar to nft_rbtree_{u,k}size to hide details to userspace, but consider +* packed representation coming from userspace for anonymous sets too. +*/ +static u32 nft_array_elems (const struct nft_set *set) +{ +u32 nelems =atomic_read (&set->nelems); + +/* Adjacent intervals are represented with a single start element in +* anonymous sets, use the current element counter as is. +*/ +if (nft_set_is_anonymous (set)) +return nelems; + +/* Add extra room for never matching interval at the beginning and open +* interval at the end which only use a single element to represent it. +* The conversion to array will compact intervals, this allows reduce +* memory consumption. +*/ +return (nelems /2 ) +2 ; +} + +static int nft_array_may_resize (const struct nft_set *set) +{ +u32 nelems =nft_array_elems (set), new_max_intervals; +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array; + +if (!priv->array_next) { + array =nft_array_alloc (nelems +NFT_ARRAY_EXTRA_SIZE ); +if (!array) +return -ENOMEM ; + + priv->array_next = array; + } + +if (nelems < priv->array_next->max_intervals) +return 0 ; + + new_max_intervals = priv->array_next->max_intervals +NFT_ARRAY_EXTRA_SIZE ; +if (nft_array_intervals_alloc (priv->array_next, new_max_intervals) <0 ) +return -ENOMEM ; + +return 0 ; +} +static int nft_rbtree_insert (const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem,struct nft_elem_priv **elem_priv)
@@ -522,6 +588,9 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,struct nft_rbtree *priv =nft_set_priv (set);int err; +if (nft_array_may_resize (set) <0 ) +return -ENOMEM ; + do { if (fatal_signal_pending(current)) return -EINTR;
@@ -586,6 +655,9 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,u64 tstamp =nft_net_tstamp (net);int d; +if (nft_array_may_resize (set) <0 ) +return NULL ; +while (parent !=NULL ) { rbe = rb_entry(parent, struct nft_rbtree_elem, node);
@@ -648,6 +720,11 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,switch (iter->type) {case NFT_ITER_UPDATE :lockdep_assert_held (&nft_pernet (ctx->net)->commit_mutex); + +if (nft_array_may_resize (set) <0 ) { + iter->err = -ENOMEM ; +break ; + }nft_rbtree_do_walk (ctx, set, iter);break ;case NFT_ITER_READ :
@@ -750,14 +827,24 @@ static int nft_rbtree_init(const struct nft_set *set,seqcount_rwlock_init (&priv->count, &priv->lock); priv->root =RB_ROOT ; + priv->array =NULL ; + priv->array_next =NULL ; +return 0 ; } +static void __nft_array_free (struct nft_array *array) +{ +kvfree (array->intervals); +kfree (array); +} +static void nft_rbtree_destroy (const struct nft_ctx *ctx,const struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set);struct nft_rbtree_elem *rbe; +struct nft_array *array;struct rb_node *node;while ((node = priv->root.rb_node) !=NULL ) {
@@ -765,6 +852,12 @@ static void nft_rbtree_destroy(const struct nft_ctx *ctx, rbe = rb_entry(node, struct nft_rbtree_elem, node);nf_tables_set_elem_destroy (ctx, set, &rbe->priv); } + + array =rcu_dereference_protected (priv->array, true); +if (array) +__nft_array_free (array); +if (priv->array_next) +__nft_array_free (priv->array_next); } static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
@@ -785,12 +878,91 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,return true; } +static void nft_array_free_rcu (struct rcu_head *rcu_head) +{ + struct nft_array *array = container_of(rcu_head, struct nft_array, rcu_head); + +__nft_array_free (array); +} +static void nft_rbtree_commit (struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set); +struct nft_rbtree_elem *rbe, *prev_rbe; +struct nft_array *old; +u32 num_intervals =0 ; +struct rb_node *node;if (time_after_eq (jiffies, priv->last_gc +nft_set_gc_interval (set)))nft_rbtree_gc (set); + +/* No changes, skip, eg. elements updates only. */ +if (!priv->array_next) +return ; + +/* Reverse walk to create an array from smaller to largest interval. */ + node =rb_last (&priv->root); +if (node) + prev_rbe = rb_entry(node, struct nft_rbtree_elem, node); +else + prev_rbe =NULL ; + +while (prev_rbe) { + rbe = prev_rbe; + +if (nft_rbtree_interval_start (rbe)) + priv->array_next->intervals[num_intervals].from = &rbe->ext; +else if (nft_rbtree_interval_end (rbe)) + priv->array_next->intervals[num_intervals++].to = &rbe->ext; + +if (num_intervals >= priv->array_next->max_intervals) { +pr_warn_once ("malformed interval set from userspace?" ); + goto err_out; + } + + node =rb_prev (node); +if (!node) +break ; + + prev_rbe = rb_entry(node, struct nft_rbtree_elem, node); + +/* For anonymous sets, when adjacent ranges are found, +* the end element is not added to the set to pack the set +* representation. Use next start element to complete this +* interval. +*/ +if (nft_rbtree_interval_start (rbe) && +nft_rbtree_interval_start (prev_rbe) && + priv->array_next->intervals[num_intervals].from) + priv->array_next->intervals[num_intervals++].to = &prev_rbe->ext; + +if (num_intervals >= priv->array_next->max_intervals) { +pr_warn_once ("malformed interval set from userspace?" ); + goto err_out; + } + } + +if (priv->array_next->intervals[num_intervals].from) + num_intervals++; +err_out: + priv->array_next->num_intervals = num_intervals; + old =rcu_replace_pointer (priv->array, priv->array_next, true); + priv->array_next =NULL ; +if (old) +call_rcu (&old->rcu_head, nft_array_free_rcu); +} + +static void nft_rbtree_abort (const struct nft_set *set) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array_next; + +if (!priv->array_next) +return ; + + array_next = priv->array_next; + priv->array_next =NULL ; +__nft_array_free (array_next); }static void nft_rbtree_gc_init (const struct nft_set *set)
@@ -854,6 +1026,7 @@ const struct nft_set_type nft_set_rbtree_type = { .flush = nft_rbtree_flush, .activate = nft_rbtree_activate, .commit = nft_rbtree_commit, + .abort = nft_rbtree_abort, .gc_init = nft_rbtree_gc_init, .lookup = nft_rbtree_lookup, .walk = nft_rbtree_walk,
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 311/641 netfilter: nft_set_rbtree: use binary search array in get command
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 2aa34191f06fc5af4f70241518a8554370d86054 ]
Rework .get interface to use the binary search array, this needs a specific lookup function to match on end intervals (<=). Packet path lookup is slight different because match is on lesser value, not equal (ie. <).
After this patch, seqcount can be removed in a follow up patch.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 154 ++++++++++++++------------------- 1 file changed, 64 insertions(+), 90 deletions(-)
net/netfilter/nft_set_rbtree.c +64 -90
@@ -69,96 +69,6 @@ static int nft_rbtree_cmp(const struct nft_set *set, set->klen); } -static bool __nft_rbtree_get (const struct net *net,const struct nft_set *set, -const u32 *key,struct nft_rbtree_elem **elem, -unsigned int seq,unsigned int flags,u8 genmask) -{ -struct nft_rbtree_elem *rbe, *interval =NULL ; -struct nft_rbtree *priv =nft_set_priv (set); -const struct rb_node *parent; -const void *this; -int d; - - parent =rcu_dereference_raw (priv->root.rb_node); -while (parent !=NULL ) { -if (read_seqcount_retry (&priv->count, seq)) -return false; - - rbe = rb_entry(parent, struct nft_rbtree_elem, node); - - this =nft_set_ext_key (&rbe->ext); - d =memcmp (this, key, set->klen); -if (d <0 ) { - parent =rcu_dereference_raw (parent->rb_left); -if (!(flags &NFT_SET_ELEM_INTERVAL_END )) - interval = rbe; - }else if (d >0 ) { - parent =rcu_dereference_raw (parent->rb_right); -if (flags &NFT_SET_ELEM_INTERVAL_END ) - interval = rbe; - }else { -if (!nft_set_elem_active (&rbe->ext, genmask)) { - parent =rcu_dereference_raw (parent->rb_left); -continue ; - } - -if (nft_set_elem_expired (&rbe->ext)) -return false; - -if (!nft_set_ext_exists (&rbe->ext,NFT_SET_EXT_FLAGS ) || - (*nft_set_ext_flags (&rbe->ext) &NFT_SET_ELEM_INTERVAL_END ) == - (flags &NFT_SET_ELEM_INTERVAL_END )) { - *elem = rbe; -return true; - } - -if (nft_rbtree_interval_end (rbe)) - interval =NULL ; - - parent =rcu_dereference_raw (parent->rb_left); - } - } - -if (set->flags &NFT_SET_INTERVAL && interval !=NULL && -nft_set_elem_active (&interval->ext, genmask) && - !nft_set_elem_expired (&interval->ext) && - ((!nft_rbtree_interval_end (interval) && - !(flags &NFT_SET_ELEM_INTERVAL_END )) || - (nft_rbtree_interval_end (interval) && - (flags &NFT_SET_ELEM_INTERVAL_END )))) { - *elem = interval; -return true; - } - -return false; -} - -static struct nft_elem_priv * -nft_rbtree_get (const struct net *net,const struct nft_set *set, -const struct nft_set_elem *elem,unsigned int flags) -{ -struct nft_rbtree *priv =nft_set_priv (set); -unsigned int seq =read_seqcount_begin (&priv->count); -struct nft_rbtree_elem *rbe =ERR_PTR (-ENOENT ); -const u32 *key = (const u32 *)&elem->key.val; -u8 genmask =nft_genmask_cur (net); -bool ret; - - ret =__nft_rbtree_get (net, set, key, &rbe, seq, flags, genmask); -if (ret || !read_seqcount_retry (&priv->count, seq)) -return &rbe->priv; - -read_lock_bh (&priv->lock); - seq =read_seqcount_begin (&priv->count); - ret =__nft_rbtree_get (net, set, key, &rbe, seq, flags, genmask); -read_unlock_bh (&priv->lock); - -if (!ret) -return ERR_PTR (-ENOENT ); - -return &rbe->priv; -} -struct nft_array_lookup_ctx {const u32 *key;u32 klen;
@@ -213,6 +123,70 @@ nft_rbtree_lookup(const struct net *net, const struct nft_set *set,return interval->from; } +struct nft_array_get_ctx { +const u32 *key; +unsigned int flags; +u32 klen; +}; + +static int nft_array_get_cmp (const void *pkey,const void *entry) +{ +const struct nft_array_interval *interval = entry; +const struct nft_array_get_ctx *ctx = pkey; +int a, b; + +if (!interval->from) +return 1 ; + + a =memcmp (ctx->key,nft_set_ext_key (interval->from), ctx->klen); +if (!interval->to) + b =-1 ; +else + b =memcmp (ctx->key,nft_set_ext_key (interval->to), ctx->klen); + +if (a >=0 ) { +if (ctx->flags &NFT_SET_ELEM_INTERVAL_END && b <=0 ) +return 0 ; +else if (b <0 ) +return 0 ; + } + +if (a <0 ) +return -1 ; + +return 1 ; +} + +static struct nft_elem_priv * +nft_rbtree_get (const struct net *net,const struct nft_set *set, +const struct nft_set_elem *elem,unsigned int flags) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_array *array =rcu_dereference (priv->array); +const struct nft_array_interval *interval; +struct nft_array_get_ctx ctx = { + .key = (const u32 *)&elem->key.val, + .flags = flags, + .klen = set->klen, + }; +struct nft_rbtree_elem *rbe; + +if (!array) +return ERR_PTR (-ENOENT ); + + interval =bsearch (&ctx, array->intervals, array->num_intervals, +sizeof (struct nft_array_interval ), nft_array_get_cmp); +if (!interval ||nft_set_elem_expired (interval->from)) +return ERR_PTR (-ENOENT ); + +if (flags &NFT_SET_ELEM_INTERVAL_END ) + rbe = container_of(interval->to, struct nft_rbtree_elem, ext); +else + rbe = container_of(interval->from, struct nft_rbtree_elem, ext); + +return &rbe->priv; +} +static void nft_rbtree_gc_elem_remove (struct net *net,struct nft_set *set,struct nft_rbtree *priv,struct nft_rbtree_elem *rbe)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 312/641 netfilter: nft_set_rbtree: remove seqcount_rwlock_t
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 5599fa810b503eafc2bd8cd15bd45f35fc8ff6b9 ]
After the conversion to binary search array, this is not required anymore. Remove it.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 6 ------ 1 file changed, 6 deletions(-)
net/netfilter/nft_set_rbtree.c +0 -6
@@ -33,7 +33,6 @@ struct nft_rbtree {rwlock_t lock;struct nft_array __rcu *array;struct nft_array *array_next; -seqcount_rwlock_t count;unsigned long last_gc; };
@@ -572,9 +571,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,cond_resched ();write_lock_bh (&priv->lock); -write_seqcount_begin (&priv->count); err =__nft_rbtree_insert (net, set, rbe, elem_priv); -write_seqcount_end (&priv->count);write_unlock_bh (&priv->lock); }while (err == -EAGAIN );
@@ -584,9 +581,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,static void nft_rbtree_erase (struct nft_rbtree *priv,struct nft_rbtree_elem *rbe) {write_lock_bh (&priv->lock); -write_seqcount_begin (&priv->count);rb_erase (&rbe->node, &priv->root); -write_seqcount_end (&priv->count);write_unlock_bh (&priv->lock); }
@@ -798,7 +793,6 @@ static int nft_rbtree_init(const struct nft_set *set,BUILD_BUG_ON (offsetof(struct nft_rbtree_elem , priv) !=0 );rwlock_init (&priv->lock); -seqcount_rwlock_init (&priv->count, &priv->lock); priv->root =RB_ROOT ; priv->array =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 313/641 netfilter: nft_set_rbtree: dont gc elements on insert
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 35f83a75529a829b0939708b003652f7b4f3df9a ]
During insertion we can queue up expired elements for garbage collection.
In case of later abort, the commit hook will never be called. Packet path and 'get' requests will find free'd elements in the binary search blob:
nft_set_ext_key include/net/netfilter/nf_tables.h:800 [inline] nft_array_get_cmp+0x1f6/0x2a0 net/netfilter/nft_set_rbtree.c:133 __inline_bsearch include/linux/bsearch.h:15 [inline] bsearch+0x50/0xc0 lib/bsearch.c:33 nft_rbtree_get+0x16b/0x400 net/netfilter/nft_set_rbtree.c:169 nft_setelem_get net/netfilter/nf_tables_api.c:6495 [inline] nft_get_set_elem+0x420/0xaa0 net/netfilter/nf_tables_api.c:6543 nf_tables_getsetelem+0x448/0x5e0 net/netfilter/nf_tables_api.c:6632 nfnetlink_rcv_msg+0x8ae/0x12c0 net/netfilter/nfnetlink.c:290
Also, when we insert an element that triggers -EEXIST, and that insertion happens to also zap a timed-out entry, we end up with same issue:
Neither commit nor abort hook is called.
Fix this by removing gc api usage during insertion.
The blamed commit also removes concurrency of the rbtree with the packet path, so we can now safely rb_erase() the element and move it to a new expired list that can be reaped in the commit hook before building the next blob iteration.
This also avoids the need to rebuild the blob in the abort path:
Expired elements seen during insertion attempts are kept around until a transaction passes.
Stable-dep-of: 782f2688128e ("netfilter: nft_set_rbtree: validate element belonging to interval")
--- net/netfilter/nft_set_rbtree.c | 136 ++++++++++++++++----------------- 1 file changed, 68 insertions(+), 68 deletions(-)
net/netfilter/nft_set_rbtree.c +68 -68
@@ -34,11 +34,15 @@ struct nft_rbtree {struct nft_array __rcu *array;struct nft_array *array_next;unsigned long last_gc; +struct list_head expired; };struct nft_rbtree_elem {struct nft_elem_priv priv; -struct rb_node node; +union { +struct rb_node node; +struct list_head list; + };struct nft_set_ext ext; };
@@ -186,13 +190,16 @@ nft_rbtree_get(const struct net *net, const struct nft_set *set,return &rbe->priv; } -static void nft_rbtree_gc_elem_remove (struct net *net,struct nft_set *set, -struct nft_rbtree *priv, -struct nft_rbtree_elem *rbe) +static void nft_rbtree_gc_elem_move (struct net *net,struct nft_set *set, +struct nft_rbtree *priv, +struct nft_rbtree_elem *rbe) {lockdep_assert_held_write (&priv->lock);nft_setelem_data_deactivate (net, set, &rbe->priv);rb_erase (&rbe->node, &priv->root); + +/* collected later on in commit callback */ +list_add (&rbe->list, &priv->expired); } static const struct nft_rbtree_elem *
@@ -203,11 +210,6 @@ nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv,struct rb_node *prev =rb_prev (&rbe->node);struct net *net =read_pnet (&set->net);struct nft_rbtree_elem *rbe_prev; -struct nft_trans_gc *gc; - - gc =nft_trans_gc_alloc (set,0 ,GFP_ATOMIC ); -if (!gc) -return ERR_PTR (-ENOMEM );/* search for end interval coming before this element. * end intervals don't carry a timeout extension, they
@@ -225,28 +227,10 @@ nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv, rbe_prev =NULL ;if (prev) { rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node); -nft_rbtree_gc_elem_remove (net, set, priv, rbe_prev); - -/* There is always room in this trans gc for this element, -* memory allocation never actually happens, hence, the warning -* splat in such case. No need to set NFT_SET_ELEM_DEAD_BIT, -* this is synchronous gc which never fails. -*/ - gc =nft_trans_gc_queue_sync (gc,GFP_ATOMIC ); -if (WARN_ON_ONCE (!gc)) -return ERR_PTR (-ENOMEM ); - -nft_trans_gc_elem_add (gc, rbe_prev); +nft_rbtree_gc_elem_move (net, set, priv, rbe_prev); } -nft_rbtree_gc_elem_remove (net, set, priv, rbe); - gc =nft_trans_gc_queue_sync (gc,GFP_ATOMIC ); -if (WARN_ON_ONCE (!gc)) -return ERR_PTR (-ENOMEM ); - -nft_trans_gc_elem_add (gc, rbe); - -nft_trans_gc_queue_sync_done (gc); +nft_rbtree_gc_elem_move (net, set, priv, rbe);return rbe_prev; }
@@ -708,29 +692,13 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, } } -static void nft_rbtree_gc_remove (struct net *net,struct nft_set *set, -struct nft_rbtree *priv, -struct nft_rbtree_elem *rbe) -{ -nft_setelem_data_deactivate (net, set, &rbe->priv); -nft_rbtree_erase (priv, rbe); -} - -static void nft_rbtree_gc (struct nft_set *set) +static void nft_rbtree_gc_scan (struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set);struct nft_rbtree_elem *rbe, *rbe_end =NULL ;struct net *net =read_pnet (&set->net);u64 tstamp =nft_net_tstamp (net);struct rb_node *node, *next; -struct nft_trans_gc *gc; - - set =nft_set_container_of (priv); - net =read_pnet (&set->net); - - gc =nft_trans_gc_alloc (set,0 ,GFP_KERNEL ); -if (!gc) -return ;for (node =rb_first (&priv->root); node ; node = next) { next =rb_next (node);
@@ -748,34 +716,46 @@ static void nft_rbtree_gc(struct nft_set *set)if (!__nft_set_elem_expired (&rbe->ext, tstamp))continue ; - gc =nft_trans_gc_queue_sync (gc,GFP_KERNEL ); -if (!gc) - goto try_later; -/* end element needs to be removed first, it has * no timeout extension. */ +write_lock_bh (&priv->lock);if (rbe_end) { -nft_rbtree_gc_remove (net, set, priv, rbe_end); -nft_trans_gc_elem_add (gc, rbe_end); +nft_rbtree_gc_elem_move (net, set, priv, rbe_end); rbe_end =NULL ; } - gc =nft_trans_gc_queue_sync (gc,GFP_KERNEL ); -if (!gc) - goto try_later; - -nft_rbtree_gc_remove (net, set, priv, rbe); -nft_trans_gc_elem_add (gc, rbe); +nft_rbtree_gc_elem_move (net, set, priv, rbe); +write_unlock_bh (&priv->lock); } -try_later: + priv->last_gc = jiffies; +} + +static void nft_rbtree_gc_queue (struct nft_set *set) +{ +struct nft_rbtree *priv =nft_set_priv (set); +struct nft_rbtree_elem *rbe, *rbe_end; +struct nft_trans_gc *gc; + +if (list_empty (&priv->expired)) +return ; -if (gc) { - gc =nft_trans_gc_catchall_sync (gc); -nft_trans_gc_queue_sync_done (gc); - priv->last_gc = jiffies; + gc =nft_trans_gc_alloc (set,0 ,GFP_KERNEL ); +if (!gc) +return ; + +list_for_each_entry_safe (rbe, rbe_end, &priv->expired, list) { +list_del (&rbe->list); +nft_trans_gc_elem_add (gc, rbe); + + gc =nft_trans_gc_queue_sync (gc,GFP_KERNEL ); +if (!gc) +return ; } + + gc =nft_trans_gc_catchall_sync (gc); +nft_trans_gc_queue_sync_done (gc); } static u64 nft_rbtree_privsize(const struct nlattr * const nla[],
@@ -794,6 +774,7 @@ static int nft_rbtree_init(const struct nft_set *set,rwlock_init (&priv->lock); priv->root =RB_ROOT ; +INIT_LIST_HEAD (&priv->expired); priv->array =NULL ; priv->array_next =NULL ;
@@ -811,10 +792,15 @@ static void nft_rbtree_destroy(const struct nft_ctx *ctx, const struct nft_set *set) {struct nft_rbtree *priv =nft_set_priv (set); -struct nft_rbtree_elem *rbe; +struct nft_rbtree_elem *rbe, *next;struct nft_array *array;struct rb_node *node; +list_for_each_entry_safe (rbe, next, &priv->expired, list) { +list_del (&rbe->list); +nf_tables_set_elem_destroy (ctx, set, &rbe->priv); + } +while ((node = priv->root.rb_node) !=NULL ) {rb_erase (node, &priv->root); rbe = rb_entry(node, struct nft_rbtree_elem, node);
@@ -861,13 +847,21 @@ static void nft_rbtree_commit(struct nft_set *set)u32 num_intervals =0 ;struct rb_node *node; -if (time_after_eq (jiffies, priv->last_gc +nft_set_gc_interval (set))) -nft_rbtree_gc (set); -/* No changes, skip, eg. elements updates only. */ if (!priv->array_next)return ; +/* GC can be performed if the binary search blob is going +* to be rebuilt. It has to be done in two phases: first +* scan tree and move all expired elements to the expired +* list. +* +* Then, after blob has been re-built and published to other +* CPUs, queue collected entries for freeing. +*/ +if (time_after_eq (jiffies, priv->last_gc +nft_set_gc_interval (set))) +nft_rbtree_gc_scan (set); +/* Reverse walk to create an array from smaller to largest interval. */ node =rb_last (&priv->root);if (node)
@@ -914,10 +908,16 @@ static void nft_rbtree_commit(struct nft_set *set) num_intervals++; err_out: priv->array_next->num_intervals = num_intervals; - old =rcu_replace_pointer (priv->array, priv->array_next, true); + old =rcu_replace_pointer (priv->array, priv->array_next, +lockdep_is_held (&nft_pernet (read_pnet (&set->net))->commit_mutex)); priv->array_next =NULL ;if (old)call_rcu (&old->rcu_head, nft_array_free_rcu); + +/* New blob is public, queue collected entries for freeing. +* call_rcu ensures elements stay around until readers are done. +*/ +nft_rbtree_gc_queue (set); }static void nft_rbtree_abort (const struct nft_set *set)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 314/641 netfilter: nft_set_rbtree: validate element belonging to interval
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 782f2688128eca6d05a48be1c247f68d86afc168 ]
The existing partial overlap detection does not check if the elements belong to the interval, eg.
add element inet x y { 1.1.1.1-2.2.2.2, 4.4.4.4-5.5.5.5 } add element inet x y { 1.1.1.1-5.5.5.5 } => this should fail: ENOENT
Similar situation occurs with deletions:
add element inet x y { 1.1.1.1-2.2.2.2, 4.4.4.4-5.5.5.5} delete element inet x y { 1.1.1.1-5.5.5.5 } => this should fail: ENOENT
This currently works via mitigation by nft in userspace, which is performing the overlap detection before sending the elements to the kernel. This requires a previous netlink dump of the set content which slows down incremental updates on interval sets, because a netlink set content dump is needed.
This patch extends the existing overlap detection to track the most recent start element that already exists. The pointer to the existing start element is stored as a cookie (no pointer dereference is ever possible). If the end element is added and it already exists, then check that the existing end element is adjacent to the already existing start element. Similar logic applies to element deactivation.
This patch also annotates the timestamp to identify if start cookie comes from an older batch, in such case reset it. Otherwise, a failing create element command leaves the start cookie in place, resulting in bogus error reporting.
There is still a few more corner cases of overlap detection related to the open interval that are addressed in follow up patches.
This is address an early design mistake where an interval is expressed as two elements, using the NFT_SET_ELEM_INTERVAL_END flag, instead of the more recent NFTA_SET_ELEM_KEY_END attribute that pipapo already uses.
--- net/netfilter/nft_set_rbtree.c | 147 ++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 4 deletions(-)
net/netfilter/nft_set_rbtree.c +143 -4
@@ -33,8 +33,10 @@ struct nft_rbtree {rwlock_t lock;struct nft_array __rcu *array;struct nft_array *array_next; +unsigned long start_rbe_cookie;unsigned long last_gc;struct list_head expired; +u64 last_tstamp; };struct nft_rbtree_elem {
@@ -263,16 +265,85 @@ static struct nft_rbtree_elem *nft_rbtree_prev_active(struct nft_rbtree_elem *rb return rb_entry(node, struct nft_rbtree_elem, node); } +static struct nft_rbtree_elem * +__nft_rbtree_next_active (struct rb_node *node,u8 genmask) +{ +struct nft_rbtree_elem *next_rbe; + +while (node) { + next_rbe = rb_entry(node, struct nft_rbtree_elem, node); +if (!nft_set_elem_active (&next_rbe->ext, genmask)) { + node =rb_next (node); +continue ; + } + +return next_rbe; + } + +return NULL ; +} + +static struct nft_rbtree_elem * +nft_rbtree_next_active (struct nft_rbtree_elem *rbe,u8 genmask) +{ +return __nft_rbtree_next_active (rb_next (&rbe->node), genmask); +} + +static void nft_rbtree_maybe_reset_start_cookie (struct nft_rbtree *priv, +u64 tstamp) +{ +if (priv->last_tstamp != tstamp) { + priv->start_rbe_cookie =0 ; + priv->last_tstamp = tstamp; + } +} + +static void nft_rbtree_set_start_cookie (struct nft_rbtree *priv, +const struct nft_rbtree_elem *rbe) +{ + priv->start_rbe_cookie = (unsigned long )rbe; +} + +static bool nft_rbtree_cmp_start_cookie (struct nft_rbtree *priv, +const struct nft_rbtree_elem *rbe) +{ +return priv->start_rbe_cookie == (unsigned long )rbe; +} + +static bool nft_rbtree_insert_same_interval (const struct net *net, +struct nft_rbtree *priv, +struct nft_rbtree_elem *rbe) +{ +u8 genmask =nft_genmask_next (net); +struct nft_rbtree_elem *next_rbe; + +if (!priv->start_rbe_cookie) +return true; + + next_rbe =nft_rbtree_next_active (rbe, genmask); +if (next_rbe) { +/* Closest start element differs from last element added. */ +if (nft_rbtree_interval_start (next_rbe) && +nft_rbtree_cmp_start_cookie (priv, next_rbe)) { + priv->start_rbe_cookie =0 ; +return true; + } + } + + priv->start_rbe_cookie =0 ; + +return false; +} +static int __nft_rbtree_insert (const struct net *net,const struct nft_set *set,struct nft_rbtree_elem *new, -struct nft_elem_priv **elem_priv) +struct nft_elem_priv **elem_priv,u64 tstamp) {struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL , *rbe_prev;struct rb_node *node, *next, *parent, **p, *first =NULL ;struct nft_rbtree *priv =nft_set_priv (set);u8 cur_genmask =nft_genmask_cur (net);u8 genmask =nft_genmask_next (net); -u64 tstamp =nft_net_tstamp (net);int d;/* Descend the tree to search for an existing element greater than the
@@ -378,12 +449,18 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, } } +if (nft_rbtree_interval_null (set, new)) + priv->start_rbe_cookie =0 ; +else if (nft_rbtree_interval_start (new) && priv->start_rbe_cookie) + priv->start_rbe_cookie =0 ; +/* - new start element matching existing start element: full overlap * reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given. */ if (rbe_ge && !nft_rbtree_cmp (set, new, rbe_ge) &&nft_rbtree_interval_start (rbe_ge) ==nft_rbtree_interval_start (new)) { *elem_priv = &rbe_ge->priv; +nft_rbtree_set_start_cookie (priv, rbe_ge);return -EEXIST ; }
@@ -399,6 +476,11 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,return -ECANCELED ; *elem_priv = &rbe_le->priv; + +/* - start and end element belong to the same interval. */ +if (!nft_rbtree_insert_same_interval (net, priv, rbe_le)) +return -ENOTEMPTY ; +return -EEXIST ; }
@@ -543,8 +625,11 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set, {struct nft_rbtree_elem *rbe =nft_elem_priv_cast (elem->priv);struct nft_rbtree *priv =nft_set_priv (set); +u64 tstamp =nft_net_tstamp (net);int err; +nft_rbtree_maybe_reset_start_cookie (priv, tstamp); +if (nft_array_may_resize (set) <0 )return -ENOMEM ;
@@ -555,7 +640,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,cond_resched ();write_lock_bh (&priv->lock); - err =__nft_rbtree_insert (net, set, rbe, elem_priv); + err =__nft_rbtree_insert (net, set, rbe, elem_priv, tstamp);write_unlock_bh (&priv->lock); }while (err == -EAGAIN );
@@ -588,6 +673,48 @@ static void nft_rbtree_activate(const struct net *net,nft_clear (net, &rbe->ext); } +static struct nft_rbtree_elem * +nft_rbtree_next_inactive (struct nft_rbtree_elem *rbe,u8 genmask) +{ +struct nft_rbtree_elem *next_rbe; +struct rb_node *node; + + node =rb_next (&rbe->node); +if (node) { + next_rbe = rb_entry(node, struct nft_rbtree_elem, node); +if (nft_rbtree_interval_start (next_rbe) && + !nft_set_elem_active (&next_rbe->ext, genmask)) +return next_rbe; + } + +return NULL ; +} + +static bool nft_rbtree_deactivate_same_interval (const struct net *net, +struct nft_rbtree *priv, +struct nft_rbtree_elem *rbe) +{ +u8 genmask =nft_genmask_next (net); +struct nft_rbtree_elem *next_rbe; + +if (!priv->start_rbe_cookie) +return true; + + next_rbe =nft_rbtree_next_inactive (rbe, genmask); +if (next_rbe) { +/* Closest start element differs from last element added. */ +if (nft_rbtree_interval_start (next_rbe) && +nft_rbtree_cmp_start_cookie (priv, next_rbe)) { + priv->start_rbe_cookie =0 ; +return true; + } + } + + priv->start_rbe_cookie =0 ; + +return false; +} +static void nft_rbtree_flush (const struct net *net,const struct nft_set *set,struct nft_elem_priv *elem_priv)
@@ -602,12 +729,18 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem) {struct nft_rbtree_elem *rbe, *this =nft_elem_priv_cast (elem->priv); -const struct nft_rbtree *priv =nft_set_priv (set); +struct nft_rbtree *priv =nft_set_priv (set);const struct rb_node *parent = priv->root.rb_node;u8 genmask =nft_genmask_next (net);u64 tstamp =nft_net_tstamp (net);int d; +nft_rbtree_maybe_reset_start_cookie (priv, tstamp); + +if (nft_rbtree_interval_start (this) || +nft_rbtree_interval_null (set, this)) + priv->start_rbe_cookie =0 ; +if (nft_array_may_resize (set) <0 )return NULL ;
@@ -635,6 +768,12 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set, parent = parent->rb_left;continue ; } + +if (nft_rbtree_interval_start (rbe)) +nft_rbtree_set_start_cookie (priv, rbe); +else if (!nft_rbtree_deactivate_same_interval (net, priv, rbe)) +return NULL ; +nft_rbtree_flush (net, set, &rbe->priv);return &rbe->priv; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 315/641 netfilter: nft_set_rbtree: validate open interval overlap
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 648946966a08e4cb1a71619e3d1b12bd7642de7b ]
Open intervals do not have an end element, in particular an open interval at the end of the set is hard to validate because of it is lacking the end element, and interval validation relies on such end element to perform the checks.
This patch adds a new flag field to struct nft_set_elem, this is not an issue because this is a temporary object that is allocated in the stack from the insert/deactivate path. This flag field is used to specify that this is the last element in this add/delete command.
The last flag is used, in combination with the start element cookie, to check if there is a partial overlap, eg.
Already exists: 255.255.255.0-255.255.255.254
Add interval: 255.255.255.0-255.255.255.255
~~~~~~~~~~~~~
start element overlap
Basically, the idea is to check for an existing end element in the set if there is an overlap with an existing start element.
However, the last open interval can come in any position in the add command, the corner case can get a bit more complicated:
Already exists: 255.255.255.0-255.255.255.254
Add intervals: 255.255.255.0-255.255.255.255,255.255.255.0-255.255.255.254
~~~~~~~~~~~~~
start element overlap
To catch this overlap, annotate that the new start element is a possible overlap, then report the overlap if the next element is another start element that confirms that previous element in an open interval at the end of the set.
For deletions, do not update the start cookie when deleting an open interval, otherwise this can trigger spurious EEXIST when adding new elements.
Unfortunately, there is no NFT_SET_ELEM_INTERVAL_OPEN flag which would make easier to detect open interval overlaps.
--- include/net/netfilter/nf_tables.h | 4 ++ net/netfilter/nf_tables_api.c | 21 +++++++-- net/netfilter/nft_set_rbtree.c | 71 ++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 14 deletions(-)
include/net/netfilter/nf_tables.h +4 -0
@@ -278,6 +278,8 @@ struct nft_userdata {unsigned char data[]; }; +#define NFT_SET_ELEM_INTERNAL_LAST 0x1 +/* placeholder structure for opaque set element backend representation. */ struct nft_elem_priv { };
@@ -287,6 +289,7 @@ struct nft_elem_priv { };* @key: element key * @key_end: closing element key * @data: element data +* @flags: flags * @priv: element private data and extensions */ struct nft_set_elem {
@@ -302,6 +305,7 @@ struct nft_set_elem {u32 buf[NFT_DATA_VALUE_MAXLEN /sizeof (u32)];struct nft_data val; } data; +u32 flags;struct nft_elem_priv *priv; };
net/netfilter/nf_tables_api.c +17 -4
@@ -7271,7 +7271,8 @@ static u32 nft_set_maxsize(const struct nft_set *set) }static int nft_add_set_elem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr,u32 nlmsg_flags) +const struct nlattr *attr,u32 nlmsg_flags, +bool last) {struct nft_expr *expr_array[NFT_SET_EXPR_MAX ] = {};struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];
@@ -7557,6 +7558,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; +if (obj) *nft_set_ext_obj (ext) = obj;
@@ -7720,7 +7726,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb,nft_ctx_init (&ctx, net, skb, info->nlh, family, table,NULL , nla);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags); + err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags, +nla_is_last (attr, rem));if (err <0 ) {NL_SET_BAD_ATTR (extack, attr);return err;
@@ -7843,7 +7850,7 @@ static void nft_trans_elems_destroy_abort(const struct nft_ctx *ctx, }static int nft_del_setelem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr) +const struct nlattr *attr,bool last) {struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];struct nft_set_ext_tmpl tmpl;
@@ -7911,6 +7918,11 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; + trans =nft_trans_elem_alloc (ctx,NFT_MSG_DELSETELEM , set);if (trans ==NULL ) goto fail_trans;
@@ -8058,7 +8070,8 @@ static int nf_tables_delsetelem(struct sk_buff *skb,return nft_set_flush (&ctx, set, genmask);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_del_setelem (&ctx, set, attr); + err =nft_del_setelem (&ctx, set, attr, +nla_is_last (attr, rem));if (err == -ENOENT &&NFNL_MSG_TYPE (info->nlh->nlmsg_type) ==NFT_MSG_DESTROYSETELEM )continue ;
net/netfilter/nft_set_rbtree.c +61 -10
136 lines not on this page · raw message
Signature · 1 line
2.51.0
Re: [PATCH 6.18 315/641] netfilter: nft_set_rbtree: validate open interval overlap
Hi Greg,
Would it be possible to revert this patch in -stable 6.18?
There is a bug in userspace nftables 1.1.6 that gets amplified by this patch, resulting in rejecting large interval sets with -stable 6.18.
Thanks!
Quoted 309 lines · Greg Kroah-Hartman
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit
648946966a08e4cb1a71619e3d1b12bd7642de7b]Open intervals do not have an end element, in particular an open interval at the end of the set is hard to validate because of it is lacking the end element, and interval validation relies on such end element to perform the checks.
This patch adds a new flag field to struct
nft_set_elem, this is not an issue because this is a temporary object that is allocated in the stack from the insert/deactivate path. This flag field is used to specify that this is the last element in this add/delete command.The last flag is used, in combination with the start element cookie, to check if there is a partial overlap, eg.
Already exists: 255.255.255.0-255.255.255.254 Add interval: 255.255.255.0-255.255.255.255 ~~~~~~~~~~~~~ start element overlapBasically, the idea is to check for an existing end element in the set if there is an overlap with an existing start element.
However, the last open interval can come in any position in the add command, the corner case can get a bit more complicated:
Already exists: 255.255.255.0-255.255.255.254 Add intervals: 255.255.255.0-255.255.255.255,255.255.255.0-255.255.255.254 ~~~~~~~~~~~~~ start element overlapTo catch this overlap, annotate that the new start element is a possible overlap, then report the overlap if the next element is another start element that confirms that previous element in an open interval at the end of the set.
For deletions, do not update the start cookie when deleting an open interval, otherwise this can trigger spurious EEXIST when adding new elements.
Unfortunately, there is no
NFT_SET_ELEM_INTERVAL_OPENflag which would make easier to detect open interval overlaps.Fixes 7c84d41416d8 ("netfilter: nft_set_rbtree: Detect partial overlaps on insertion")Signed-off-by Pablo Neira Ayuso <[email protected]>Signed-off-by Florian Westphal <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- include/net/netfilter/nf_tables.h | 4 ++ net/netfilter/nf_tables_api.c | 21 +++++++-- net/netfilter/nft_set_rbtree.c | 71 ++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 14 deletions(-)include/net/netfilter/nf_tables.h +4
-0@@ -278,6 +278,8 @@ struct nft_userdata {unsigned char data[]; }; +#define NFT_SET_ELEM_INTERNAL_LAST 0x1 +/* placeholder structure for opaque set element backend representation. */ struct nft_elem_priv { };@@ -287,6 +289,7 @@ struct nft_elem_priv { };* @key: element key * @key_end: closing element key * @data: element data +* @flags: flags * @priv: element private data and extensions */ struct nft_set_elem {@@ -302,6 +305,7 @@ struct nft_set_elem {u32 buf[NFT_DATA_VALUE_MAXLEN /sizeof (u32)];struct nft_data val; } data; +u32 flags;struct nft_elem_priv *priv; };net/netfilter/nf_tables_api.c +17
-4@@ -7271,7 +7271,8 @@ static u32 nft_set_maxsize(const struct nft_set *set) }static int nft_add_set_elem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr,u32 nlmsg_flags) +const struct nlattr *attr,u32 nlmsg_flags, +bool last) {struct nft_expr *expr_array[NFT_SET_EXPR_MAX ] = {};struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];@@ -7557,6 +7558,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; +if (obj) *nft_set_ext_obj (ext) = obj;@@ -7720,7 +7726,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb,nft_ctx_init (&ctx, net, skb, info->nlh, family, table,NULL , nla);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags); + err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags, +nla_is_last (attr, rem));if (err <0 ) {NL_SET_BAD_ATTR (extack, attr);return err;@@ -7843,7 +7850,7 @@ static void nft_trans_elems_destroy_abort(const struct nft_ctx *ctx, }static int nft_del_setelem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr) +const struct nlattr *attr,bool last) {struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];struct nft_set_ext_tmpl tmpl;@@ -7911,6 +7918,11 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; + trans =nft_trans_elem_alloc (ctx,NFT_MSG_DELSETELEM , set);if (trans ==NULL ) goto fail_trans;@@ -8058,7 +8070,8 @@ static int nf_tables_delsetelem(struct sk_buff *skb,return nft_set_flush (&ctx, set, genmask);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_del_setelem (&ctx, set, attr); + err =nft_del_setelem (&ctx, set, attr, +nla_is_last (attr, rem));if (err == -ENOENT &&NFNL_MSG_TYPE (info->nlh->nlmsg_type) ==NFT_MSG_DESTROYSETELEM )continue ;net/netfilter/nft_set_rbtree.c +61
-10@@ -304,10 +304,19 @@ static void nft_rbtree_set_start_cookie(struct nft_rbtree *priv, priv->start_rbe_cookie = (unsigned long )rbe; } +static void nft_rbtree_set_start_cookie_open (struct nft_rbtree *priv, +const struct nft_rbtree_elem *rbe, +unsigned long open_interval) +{ + priv->start_rbe_cookie = (unsigned long )rbe | open_interval; +} + +#define NFT_RBTREE_OPEN_INTERVAL 1UL +static bool nft_rbtree_cmp_start_cookie (struct nft_rbtree *priv,const struct nft_rbtree_elem *rbe) { -return priv->start_rbe_cookie == (unsigned long )rbe; +return (priv->start_rbe_cookie & ~NFT_RBTREE_OPEN_INTERVAL ) == (unsigned long )rbe; } static bool nft_rbtree_insert_same_interval(const struct net *net,@@ -337,13 +346,14 @@ static bool nft_rbtree_insert_same_interval(const struct net *net,static int __nft_rbtree_insert (const struct net *net,const struct nft_set *set,struct nft_rbtree_elem *new, -struct nft_elem_priv **elem_priv,u64 tstamp) +struct nft_elem_priv **elem_priv,u64 tstamp,bool last) {struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL , *rbe_prev;struct rb_node *node, *next, *parent, **p, *first =NULL ;struct nft_rbtree *priv =nft_set_priv (set);u8 cur_genmask =nft_genmask_cur (net);u8 genmask =nft_genmask_next (net); +unsigned long open_interval =0 ;int d;/* Descend the tree to search for an existing element greater than the @@ -449,10 +459,18 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, } } -if (nft_rbtree_interval_null (set, new)) - priv->start_rbe_cookie =0 ; -else if (nft_rbtree_interval_start (new) && priv->start_rbe_cookie) +if (nft_rbtree_interval_null (set, new)) { priv->start_rbe_cookie =0 ; + }else if (nft_rbtree_interval_start (new) && priv->start_rbe_cookie) { +if (nft_set_is_anonymous (set)) { + priv->start_rbe_cookie =0 ; + }else if (priv->start_rbe_cookie &NFT_RBTREE_OPEN_INTERVAL ) { +/* Previous element is an open interval that partially +* overlaps with an existing non-open interval. +*/ +return -ENOTEMPTY ; + } + }/* - new start element matching existing start element: full overlap * reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given. @@ -460,7 +478,27 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,if (rbe_ge && !nft_rbtree_cmp (set, new, rbe_ge) &&nft_rbtree_interval_start (rbe_ge) ==nft_rbtree_interval_start (new)) { *elem_priv = &rbe_ge->priv; -nft_rbtree_set_start_cookie (priv, rbe_ge); + +/* - Corner case: new start element of open interval (which +* comes as last element in the batch) overlaps the start of +* an existing interval with an end element: partial overlap. +*/ + node =rb_first (&priv->root); + rbe =__nft_rbtree_next_active (node, genmask); +if (rbe &&nft_rbtree_interval_end (rbe)) { + rbe =nft_rbtree_next_active (rbe, genmask); +if (rbe && +nft_rbtree_interval_start (rbe) && + !nft_rbtree_cmp (set, new, rbe)) { +if (last) +return -ENOTEMPTY ; + +/* Maybe open interval? */ + open_interval =NFT_RBTREE_OPEN_INTERVAL ; + } + } +nft_rbtree_set_start_cookie_open (priv, rbe_ge, open_interval); +return -EEXIST ; }@@ -515,6 +553,12 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, nft_rbtree_interval_end(rbe_ge) && nft_rbtree_interval_end(new))return -ENOTEMPTY ; +/* - start element overlaps an open interval but end element is new: +* partial overlap, reported as -ENOEMPTY. +*/ +if (!rbe_ge && priv->start_rbe_cookie &&nft_rbtree_interval_end (new)) +return -ENOTEMPTY ; +/* Accepted element: pick insertion point depending on key value */ parent =NULL ; p = &priv->root.rb_node;@@ -624,6 +668,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_elem_priv **elem_priv) {struct nft_rbtree_elem *rbe =nft_elem_priv_cast (elem->priv); +bool last = !!(elem->flags &NFT_SET_ELEM_INTERNAL_LAST );struct nft_rbtree *priv =nft_set_priv (set);u64 tstamp =nft_net_tstamp (net);int err;@@ -640,8 +685,12 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,cond_resched ();write_lock_bh (&priv->lock); - err =__nft_rbtree_insert (net, set, rbe, elem_priv, tstamp); + err =__nft_rbtree_insert (net, set, rbe, elem_priv, tstamp, last);write_unlock_bh (&priv->lock); + +if (nft_rbtree_interval_end (rbe)) + priv->start_rbe_cookie =0 ; + }while (err == -EAGAIN );return err;@@ -729,6 +778,7 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem) {struct nft_rbtree_elem *rbe, *this =nft_elem_priv_cast (elem->priv); +bool last = !!(elem->flags &NFT_SET_ELEM_INTERNAL_LAST );struct nft_rbtree *priv =nft_set_priv (set);const struct rb_node *parent = priv->root.rb_node;u8 genmask =nft_genmask_next (net);@@ -769,9 +819,10 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,continue ; } -if (nft_rbtree_interval_start (rbe)) -nft_rbtree_set_start_cookie (priv, rbe); -else if (!nft_rbtree_deactivate_same_interval (net, priv, rbe)) +if (nft_rbtree_interval_start (rbe)) { +if (!last) +nft_rbtree_set_start_cookie (priv, rbe); + }else if (!nft_rbtree_deactivate_same_interval (net, priv, rbe))return NULL ;nft_rbtree_flush (net, set, &rbe->priv);Signature · 1 line
2.51.0
Re: [PATCH 6.18 315/641] netfilter: nft_set_rbtree: validate open interval overlap
Quoted 5 lines · Pablo Neira Ayuso
Would it be possible to revert this patch in -stable 6.18?
There is a bug in userspace nftables 1.1.6 that gets amplified by this patch, resulting in rejecting large interval sets with -stable 6.18.
Sasha, I wonder if it might be worth the risk squeezing this revert into 6.18.16 and 6.19.6, as there are various reports coming in about the regression that Pablo mentioned:
https://bugzilla.kernel.org/show_bug.cgi?id=221152
https://bugzilla.kernel.org/show_bug.cgi?id=221158
https://lore.kernel.org/all/[email protected]/
https://lore.kernel.org/all/[email protected]/
I think I saw a at least two more.
Ciao, Thorsten
Quoted 310 lines · Greg Kroah-Hartman
Quoted 309 lines
6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit
648946966a08e4cb1a71619e3d1b12bd7642de7b]Open intervals do not have an end element, in particular an open interval at the end of the set is hard to validate because of it is lacking the end element, and interval validation relies on such end element to perform the checks.
This patch adds a new flag field to struct
nft_set_elem, this is not an issue because this is a temporary object that is allocated in the stack from the insert/deactivate path. This flag field is used to specify that this is the last element in this add/delete command.The last flag is used, in combination with the start element cookie, to check if there is a partial overlap, eg.
Already exists: 255.255.255.0-255.255.255.254 Add interval: 255.255.255.0-255.255.255.255 ~~~~~~~~~~~~~ start element overlapBasically, the idea is to check for an existing end element in the set if there is an overlap with an existing start element.
However, the last open interval can come in any position in the add command, the corner case can get a bit more complicated:
Already exists: 255.255.255.0-255.255.255.254 Add intervals: 255.255.255.0-255.255.255.255,255.255.255.0-255.255.255.254 ~~~~~~~~~~~~~ start element overlapTo catch this overlap, annotate that the new start element is a possible overlap, then report the overlap if the next element is another start element that confirms that previous element in an open interval at the end of the set.
For deletions, do not update the start cookie when deleting an open interval, otherwise this can trigger spurious EEXIST when adding new elements.
Unfortunately, there is no
NFT_SET_ELEM_INTERVAL_OPENflag which would make easier to detect open interval overlaps.Fixes 7c84d41416d8 ("netfilter: nft_set_rbtree: Detect partial overlaps on insertion")Signed-off-by Pablo Neira Ayuso <[email protected]>Signed-off-by Florian Westphal <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- include/net/netfilter/nf_tables.h | 4 ++ net/netfilter/nf_tables_api.c | 21 +++++++-- net/netfilter/nft_set_rbtree.c | 71 ++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 14 deletions(-)include/net/netfilter/nf_tables.h +4
-0@@ -278,6 +278,8 @@ struct nft_userdata {unsigned char data[]; }; +#define NFT_SET_ELEM_INTERNAL_LAST 0x1 +/* placeholder structure for opaque set element backend representation. */ struct nft_elem_priv { };@@ -287,6 +289,7 @@ struct nft_elem_priv { };* @key: element key * @key_end: closing element key * @data: element data +* @flags: flags * @priv: element private data and extensions */ struct nft_set_elem {@@ -302,6 +305,7 @@ struct nft_set_elem {u32 buf[NFT_DATA_VALUE_MAXLEN /sizeof (u32)];struct nft_data val; } data; +u32 flags;struct nft_elem_priv *priv; };net/netfilter/nf_tables_api.c +17
-4@@ -7271,7 +7271,8 @@ static u32 nft_set_maxsize(const struct nft_set *set) }static int nft_add_set_elem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr,u32 nlmsg_flags) +const struct nlattr *attr,u32 nlmsg_flags, +bool last) {struct nft_expr *expr_array[NFT_SET_EXPR_MAX ] = {};struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];@@ -7557,6 +7558,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; +if (obj) *nft_set_ext_obj (ext) = obj;@@ -7720,7 +7726,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb,nft_ctx_init (&ctx, net, skb, info->nlh, family, table,NULL , nla);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags); + err =nft_add_set_elem (&ctx, set, attr, info->nlh->nlmsg_flags, +nla_is_last (attr, rem));if (err <0 ) {NL_SET_BAD_ATTR (extack, attr);return err;@@ -7843,7 +7850,7 @@ static void nft_trans_elems_destroy_abort(const struct nft_ctx *ctx, }static int nft_del_setelem (struct nft_ctx *ctx,struct nft_set *set, -const struct nlattr *attr) +const struct nlattr *attr,bool last) {struct nlattr *nla[NFTA_SET_ELEM_MAX +1 ];struct nft_set_ext_tmpl tmpl;@@ -7911,6 +7918,11 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,if (flags) *nft_set_ext_flags (ext) = flags; +if (last) + elem.flags =NFT_SET_ELEM_INTERNAL_LAST ; +else + elem.flags =0 ; + trans =nft_trans_elem_alloc (ctx,NFT_MSG_DELSETELEM , set);if (trans ==NULL ) goto fail_trans;@@ -8058,7 +8070,8 @@ static int nf_tables_delsetelem(struct sk_buff *skb,return nft_set_flush (&ctx, set, genmask);nla_for_each_nested (attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS ], rem) { - err =nft_del_setelem (&ctx, set, attr); + err =nft_del_setelem (&ctx, set, attr, +nla_is_last (attr, rem));if (err == -ENOENT &&NFNL_MSG_TYPE (info->nlh->nlmsg_type) ==NFT_MSG_DESTROYSETELEM )continue ;net/netfilter/nft_set_rbtree.c +61
-10@@ -304,10 +304,19 @@ static void nft_rbtree_set_start_cookie(struct nft_rbtree *priv, priv->start_rbe_cookie = (unsigned long )rbe; } +static void nft_rbtree_set_start_cookie_open (struct nft_rbtree *priv, +const struct nft_rbtree_elem *rbe, +unsigned long open_interval) +{ + priv->start_rbe_cookie = (unsigned long )rbe | open_interval; +} + +#define NFT_RBTREE_OPEN_INTERVAL 1UL +static bool nft_rbtree_cmp_start_cookie (struct nft_rbtree *priv,const struct nft_rbtree_elem *rbe) { -return priv->start_rbe_cookie == (unsigned long )rbe; +return (priv->start_rbe_cookie & ~NFT_RBTREE_OPEN_INTERVAL ) == (unsigned long )rbe; } static bool nft_rbtree_insert_same_interval(const struct net *net,@@ -337,13 +346,14 @@ static bool nft_rbtree_insert_same_interval(const struct net *net,static int __nft_rbtree_insert (const struct net *net,const struct nft_set *set,struct nft_rbtree_elem *new, -struct nft_elem_priv **elem_priv,u64 tstamp) +struct nft_elem_priv **elem_priv,u64 tstamp,bool last) {struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL , *rbe_prev;struct rb_node *node, *next, *parent, **p, *first =NULL ;struct nft_rbtree *priv =nft_set_priv (set);u8 cur_genmask =nft_genmask_cur (net);u8 genmask =nft_genmask_next (net); +unsigned long open_interval =0 ;int d;/* Descend the tree to search for an existing element greater than the @@ -449,10 +459,18 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, } } -if (nft_rbtree_interval_null (set, new)) - priv->start_rbe_cookie =0 ; -else if (nft_rbtree_interval_start (new) && priv->start_rbe_cookie) +if (nft_rbtree_interval_null (set, new)) { priv->start_rbe_cookie =0 ; + }else if (nft_rbtree_interval_start (new) && priv->start_rbe_cookie) { +if (nft_set_is_anonymous (set)) { + priv->start_rbe_cookie =0 ; + }else if (priv->start_rbe_cookie &NFT_RBTREE_OPEN_INTERVAL ) { +/* Previous element is an open interval that partially +* overlaps with an existing non-open interval. +*/ +return -ENOTEMPTY ; + } + }/* - new start element matching existing start element: full overlap * reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given. @@ -460,7 +478,27 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,if (rbe_ge && !nft_rbtree_cmp (set, new, rbe_ge) &&nft_rbtree_interval_start (rbe_ge) ==nft_rbtree_interval_start (new)) { *elem_priv = &rbe_ge->priv; -nft_rbtree_set_start_cookie (priv, rbe_ge); + +/* - Corner case: new start element of open interval (which +* comes as last element in the batch) overlaps the start of +* an existing interval with an end element: partial overlap. +*/ + node =rb_first (&priv->root); + rbe =__nft_rbtree_next_active (node, genmask); +if (rbe &&nft_rbtree_interval_end (rbe)) { + rbe =nft_rbtree_next_active (rbe, genmask); +if (rbe && +nft_rbtree_interval_start (rbe) && + !nft_rbtree_cmp (set, new, rbe)) { +if (last) +return -ENOTEMPTY ; + +/* Maybe open interval? */ + open_interval =NFT_RBTREE_OPEN_INTERVAL ; + } + } +nft_rbtree_set_start_cookie_open (priv, rbe_ge, open_interval); +return -EEXIST ; }@@ -515,6 +553,12 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, nft_rbtree_interval_end(rbe_ge) && nft_rbtree_interval_end(new))return -ENOTEMPTY ; +/* - start element overlaps an open interval but end element is new: +* partial overlap, reported as -ENOEMPTY. +*/ +if (!rbe_ge && priv->start_rbe_cookie &&nft_rbtree_interval_end (new)) +return -ENOTEMPTY ; +/* Accepted element: pick insertion point depending on key value */ parent =NULL ; p = &priv->root.rb_node;@@ -624,6 +668,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_elem_priv **elem_priv) {struct nft_rbtree_elem *rbe =nft_elem_priv_cast (elem->priv); +bool last = !!(elem->flags &NFT_SET_ELEM_INTERNAL_LAST );struct nft_rbtree *priv =nft_set_priv (set);u64 tstamp =nft_net_tstamp (net);int err;@@ -640,8 +685,12 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,cond_resched ();write_lock_bh (&priv->lock); - err =__nft_rbtree_insert (net, set, rbe, elem_priv, tstamp); + err =__nft_rbtree_insert (net, set, rbe, elem_priv, tstamp, last);write_unlock_bh (&priv->lock); + +if (nft_rbtree_interval_end (rbe)) + priv->start_rbe_cookie =0 ; + }while (err == -EAGAIN );return err;@@ -729,6 +778,7 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem) {struct nft_rbtree_elem *rbe, *this =nft_elem_priv_cast (elem->priv); +bool last = !!(elem->flags &NFT_SET_ELEM_INTERNAL_LAST );struct nft_rbtree *priv =nft_set_priv (set);const struct rb_node *parent = priv->root.rb_node;u8 genmask =nft_genmask_next (net);@@ -769,9 +819,10 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,continue ; } -if (nft_rbtree_interval_start (rbe)) -nft_rbtree_set_start_cookie (priv, rbe); -else if (!nft_rbtree_deactivate_same_interval (net, priv, rbe)) +if (nft_rbtree_interval_start (rbe)) { +if (!last) +nft_rbtree_set_start_cookie (priv, rbe); + }else if (!nft_rbtree_deactivate_same_interval (net, priv, rbe))return NULL ;nft_rbtree_flush (net, set, &rbe->priv);Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerRe: [PATCH 6.18 315/641] netfilter: nft_set_rbtree: validate open interval overlap
Quoted 11 lines · Thorsten Leemhuis
Quoted 5 lines · Pablo Neira Ayuso
Would it be possible to revert this patch in -stable 6.18?
There is a bug in userspace nftables 1.1.6 that gets amplified by this patch, resulting in rejecting large interval sets with -stable 6.18.
Sasha, I wonder if it might be worth the risk squeezing this revert into 6.18.16 and 6.19.6, as there are various reports coming in about the regression that Pablo mentioned:
Quoted 6 lines
https://bugzilla.kernel.org/show_bug.cgi?id=221152
https://bugzilla.kernel.org/show_bug.cgi?id=221158
https://lore.kernel.org/all/[email protected]/
https://lore.kernel.org/all/[email protected]/I think I saw a at least two more.
Let's wait for the next cycle, this one is "big enough" as is, I can do a "quick" release with just a revert here for this if needed in a few days.
thanks,
greg k-h
Greg Kroah-Hartman
maintainerRe: [PATCH 6.18 315/641] netfilter: nft_set_rbtree: validate open interval overlap
Quoted 17 lines · Greg Kroah-Hartman
Quoted 16 lines · Thorsten Leemhuis
Quoted 5 lines · Pablo Neira Ayuso
Would it be possible to revert this patch in -stable 6.18?
There is a bug in userspace nftables 1.1.6 that gets amplified by this patch, resulting in rejecting large interval sets with -stable 6.18.
Sasha, I wonder if it might be worth the risk squeezing this revert into 6.18.16 and 6.19.6, as there are various reports coming in about the regression that Pablo mentioned:
https://bugzilla.kernel.org/show_bug.cgi?id=221152
https://bugzilla.kernel.org/show_bug.cgi?id=221158
https://lore.kernel.org/all/[email protected]/
https://lore.kernel.org/all/[email protected]/
Quoted 5 lines
Quoted 1 line
I think I saw a at least two more.
Let's wait for the next cycle, this one is "big enough" as is, I can do a "quick" release with just a revert here for this if needed in a few days.
Sorry for the delay, now queued up.
greg k-h
Greg Kroah-Hartman
maintainerPATCH 316/641 PCI: Add ACS quirk for Pericom PI7C9X2G404 switches [12d8:b404]
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Cavallari <[email protected]>
[ Upstream commit 5907a90551e9f7968781f3a6ab8684458959beb3 ]
12d8:b404 is apparently another PCI ID for Pericom PI7C9X2G404 (as identified by the chip silkscreen and lspci).
It is also affected by the PI7C9X2G errata (e.g. a network card attached to it fails under load when P2P Redirect Request is enabled), so apply the same quirk to this PCI ID too.
PCI bridge [0604]: Pericom Semiconductor PI7C9X2G404 EV/SV PCIe2 4-Port/4-Lane Packet Switch [12d8:b404] (rev 01)
--- drivers/pci/quirks.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pci/quirks.c +4 -0
@@ -6189,6 +6189,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2303, pci_fixup_pericom_acs_store_forward);DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0x2303 , pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_ENABLE (PCI_VENDOR_ID_PERICOM ,0xb404 , + pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0xb404 , + pci_fixup_pericom_acs_store_forward);static void nvidia_ion_ahci_fixup (struct pci_dev *pdev) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 317/641 dpll: add phase-adjust-gran pin attribute
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 30176bf7c871681df506f3165ffe76ec462db991 ]
Phase-adjust values are currently limited by a min-max range. Some hardware requires, for certain pin types, that values be multiples of a specific granularity, as in the zl3073x driver.
Add a phase-adjust-gran pin attribute and an appropriate field in dpll_pin_properties. If set by the driver, use its value to validate user-provided phase-adjust values.
Stable-dep-of: 5d41f95f5d0b ("dpll: zl3073x: Fix output pin phase adjustment sign")
--- Documentation/driver-api/dpll.rst | 36 +++++++++++++++------------ Documentation/netlink/specs/dpll.yaml | 7 ++++++ drivers/dpll/dpll_netlink.c | 12 ++++++++- include/linux/dpll.h | 1 + include/uapi/linux/dpll.h | 1 + 5 files changed, 40 insertions(+), 17 deletions(-)
Documentation/driver-api/dpll.rst +20 -16
@@ -198,26 +198,28 @@ be requested with the same attribute with ``DPLL_CMD_DEVICE_SET`` command. ================================== ====================================== Device may also provide ability to adjust a signal phase on a pin. -If pin phase adjustment is supported, minimal and maximal values that pin -handle shall be provide to the user on ``DPLL_CMD_PIN_GET`` respond -with ``DPLL_A_PIN_PHASE_ADJUST_MIN`` and ``DPLL_A_PIN_PHASE_ADJUST_MAX`` +If pin phase adjustment is supported, minimal and maximal values and +granularity that pin handle shall be provided to the user on +``DPLL_CMD_PIN_GET`` respond with ``DPLL_A_PIN_PHASE_ADJUST_MIN``, +``DPLL_A_PIN_PHASE_ADJUST_MAX`` and ``DPLL_A_PIN_PHASE_ADJUST_GRAN`` attributes. Configured phase adjust value is provided with ``DPLL_A_PIN_PHASE_ADJUST`` attribute of a pin, and value change can be requested with the same attribute with ``DPLL_CMD_PIN_SET`` command. - =============================== ====================================== - ``DPLL_A_PIN_ID`` configured pin id - ``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase adjustment - ``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase adjustment - ``DPLL_A_PIN_PHASE_ADJUST`` attr configured value of phase - adjustment on parent dpll device - ``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting - configuration on given parent dpll - device - ``DPLL_A_PIN_PARENT_ID`` parent dpll device id - ``DPLL_A_PIN_PHASE_OFFSET`` attr measured phase difference - between a pin and parent dpll device - =============================== ====================================== + ================================ ========================================== + ``DPLL_A_PIN_ID`` configured pin id + ``DPLL_A_PIN_PHASE_ADJUST_GRAN`` attr granularity of phase adjustment value + ``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase adjustment + ``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase adjustment + ``DPLL_A_PIN_PHASE_ADJUST`` attr configured value of phase + adjustment on parent dpll device + ``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting + configuration on given parent dpll + device + ``DPLL_A_PIN_PARENT_ID`` parent dpll device id + ``DPLL_A_PIN_PHASE_OFFSET`` attr measured phase difference + between a pin and parent dpll device + ================================ ========================================== All phase related values are provided in pico seconds, which represents time difference between signals phase. The negative value means that
@@ -384,6 +386,8 @@ according to attribute purpose. frequencies ``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency ``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency + ``DPLL_A_PIN_PHASE_ADJUST_GRAN`` attr granularity of phase + adjustment value ``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase adjustment ``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase
Documentation/netlink/specs/dpll.yaml +7 -0
@@ -440,6 +440,12 @@ attribute-sets: doc: | Capable pin provides list of pins that can be bound to create a reference-sync pin pair. + - + name: phase-adjust-gran + type: u32 + doc: | + Granularity of phase adjustment, in picoseconds. The value of + phase adjustment must be a multiple of this granularity. - name: pin-parent-device
@@ -616,6 +622,7 @@ operations: - capabilities - parent-device - parent-pin + - phase-adjust-gran - phase-adjust-min - phase-adjust-max - phase-adjust
drivers/dpll/dpll_netlink.c +11 -1
@@ -637,6 +637,10 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin, ret =dpll_msg_add_pin_freq (msg, pin, ref, extack);if (ret)return ret; +if (prop->phase_gran && +nla_put_u32 (msg,DPLL_A_PIN_PHASE_ADJUST_GRAN , + prop->phase_gran)) +return -EMSGSIZE ;if (nla_put_s32 (msg,DPLL_A_PIN_PHASE_ADJUST_MIN , prop->phase_range.min))return -EMSGSIZE ;
@@ -1261,7 +1265,13 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,if (phase_adj > pin->prop.phase_range.max || phase_adj < pin->prop.phase_range.min) {NL_SET_ERR_MSG_ATTR (extack, phase_adj_attr, -"phase adjust value not supported" ); +"phase adjust value of out range" ); +return -EINVAL ; + } +if (pin->prop.phase_gran && phase_adj % (s32 )pin->prop.phase_gran) { +NL_SET_ERR_MSG_ATTR_FMT (extack, phase_adj_attr, +"phase adjust value not multiple of %u" , + pin->prop.phase_gran);return -EINVAL ; }
include/linux/dpll.h +1 -0
@@ -163,6 +163,7 @@ struct dpll_pin_properties {u32 freq_supported_num;struct dpll_pin_frequency *freq_supported;struct dpll_pin_phase_adjust_range phase_range; +u32 phase_gran; };#if IS_ENABLED (CONFIG_DPLL )
include/uapi/linux/dpll.h +1 -0
@@ -251,6 +251,7 @@ enum dpll_a_pin {DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED ,DPLL_A_PIN_ESYNC_PULSE ,DPLL_A_PIN_REFERENCE_SYNC , +DPLL_A_PIN_PHASE_ADJUST_GRAN , __DPLL_A_PIN_MAX,DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX -1 )
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 318/641 dpll: zl3073x: Specify phase adjustment granularity for pins
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 055a01b29fd643e33b9b1e88e24bbe1afe6fc6d9 ]
Output pins phase adjustment values in the device are expressed in half synth clock cycles. Use this number of cycles as output pins' phase adjust granularity and simplify both get/set callbacks.
Stable-dep-of: 5d41f95f5d0b ("dpll: zl3073x: Fix output pin phase adjustment sign")
--- drivers/dpll/zl3073x/dpll.c | 58 +++++++++---------------------------- drivers/dpll/zl3073x/prop.c | 11 +++++++ 2 files changed, 25 insertions(+), 44 deletions(-)
drivers/dpll/zl3073x/dpll.c +14 -44
@@ -35,6 +35,7 @@* @prio: pin priority <0, 14> * @selectable: pin is selectable in automatic mode * @esync_control: embedded sync is controllable +* @phase_gran: phase adjustment granularity * @pin_state: last saved pin state * @phase_offset: last saved pin phase offset * @freq_offset: last saved fractional frequency offset
@@ -49,6 +50,7 @@ struct zl3073x_dpll_pin {u8 prio;bool selectable;bool esync_control; +s32 phase_gran;enum dpll_pin_state pin_state;s64 phase_offset;s64 freq_offset;
@@ -1388,25 +1390,14 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -u32 synth_freq;s32 phase_comp; -u8 out, synth; +u8 out;int rc; - out =zl3073x_output_pin_out_get (pin->id); - synth =zl3073x_out_synth_get (zldev, out); - synth_freq =zl3073x_synth_freq_get (zldev, synth); - -/* Check synth freq for zero */ -if (!synth_freq) { -dev_err (zldev->dev,"Got zero synth frequency for output %u\n" , - out); -return -EINVAL ; - } -guard (mutex)(&zldev->multiop_lock);/* Read output configuration */ + out =zl3073x_output_pin_out_get (pin->id); rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD ,ZL_REG_OUTPUT_MB_MASK ,BIT (out));if (rc)
@@ -1417,11 +1408,10 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,if (rc)return rc; -/* Value in register is expressed in half synth clock cycles */ - phase_comp *= (int )div_u64 (PSEC_PER_SEC ,2 * synth_freq); - -/* Reverse two's complement negation applied during 'set' */ - *phase_adjust = -phase_comp; +/* Convert value to ps and reverse two's complement negation applied +* during 'set' +*/ + *phase_adjust = -phase_comp * pin->phase_gran;return rc; }
@@ -1437,39 +1427,18 @@ zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -int half_synth_cycle; -u32 synth_freq; -u8 out, synth; +u8 out;int rc; -/* Get attached synth */ - out =zl3073x_output_pin_out_get (pin->id); - synth =zl3073x_out_synth_get (zldev, out); - -/* Get synth's frequency */ - synth_freq =zl3073x_synth_freq_get (zldev, synth); - -/* Value in register is expressed in half synth clock cycles so -* the given phase adjustment a multiple of half synth clock. -*/ - half_synth_cycle = (int )div_u64 (PSEC_PER_SEC ,2 * synth_freq); - -if ((phase_adjust % half_synth_cycle) !=0 ) { -NL_SET_ERR_MSG_FMT (extack, -"Phase adjustment value has to be multiple of %d" , - half_synth_cycle); -return -EINVAL ; - } - phase_adjust /= half_synth_cycle; -/* The value in the register is stored as two's complement negation -* of requested value. +* of requested value and expressed in half synth clock cycles. */ - phase_adjust = -phase_adjust; + phase_adjust = -phase_adjust / pin->phase_gran;guard (mutex)(&zldev->multiop_lock);/* Read output configuration */ + out =zl3073x_output_pin_out_get (pin->id); rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD ,ZL_REG_OUTPUT_MB_MASK ,BIT (out));if (rc)
@@ -1758,9 +1727,10 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)if (IS_ERR (props))return PTR_ERR (props); -/* Save package label & esync capability */ +/* Save package label, esync capability and phase adjust granularity */ strscpy (pin->label, props->package_label); pin->esync_control = props->esync_control; + pin->phase_gran = props->dpll_props.phase_gran;if (zl3073x_dpll_is_input_pin (pin)) { rc =zl3073x_dpll_ref_prio_get (pin, &pin->prio);
drivers/dpll/zl3073x/prop.c +11 -0
@@ -208,7 +208,18 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE ; }else { +u8 out, synth; +u32 f; + props->dpll_props.type =DPLL_PIN_TYPE_GNSS ; + +/* The output pin phase adjustment granularity equals half of +* the synth frequency count. +*/ + out =zl3073x_output_pin_out_get (index); + synth =zl3073x_out_synth_get (zldev, out); + f =2 *zl3073x_synth_freq_get (zldev, synth); + props->dpll_props.phase_gran = f ?div_u64 (PSEC_PER_SEC , f) :1 ; } props->dpll_props.phase_range.min =S32_MIN ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 319/641 dpll: zl3073x: Store raw register values instead of parsed state
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 58fb88d30b0250f928e1afa0eaa4547770d86229 ]
The zl3073x_ref, zl3073x_out and zl3073x_synth structures previously stored state that was parsed from register reads. This included values like boolean 'enabled' flags, synthesizer selections, and pre-calculated frequencies.
This commit refactors the state management to store the raw register values directly in these structures. The various inline helper functions are updated to parse these raw values on-demand using FIELD_GET.
Stable-dep-of: 5d41f95f5d0b ("dpll: zl3073x: Fix output pin phase adjustment sign")
--- drivers/dpll/zl3073x/core.c | 81 ++++++++++++------------------------- drivers/dpll/zl3073x/core.h | 61 ++++++++++++++++------------ 2 files changed, 60 insertions(+), 82 deletions(-)
drivers/dpll/zl3073x/core.c +25 -56
@@ -598,25 +598,22 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,* @zldev: pointer to zl3073x_dev structure * @index: input reference index to fetch state for * -* Function fetches information for the given input reference that are -* invariant and stores them for later use. +* Function fetches state for the given input reference and stores it for +* later user. * * Return: 0 on success, <0 on error */ static int zl3073x_ref_state_fetch (struct zl3073x_dev *zldev,u8 index) { -struct zl3073x_ref *input = &zldev->ref[index]; -u8 ref_config; +struct zl3073x_ref *ref = &zldev->ref[index];int rc;/* If the input is differential then the configuration for N-pin * reference is ignored and P-pin config is used for both. */ -if (zl3073x_is_n_pin (index) && -zl3073x_ref_is_diff (zldev, index -1 )) { - input->enabled =zl3073x_ref_is_enabled (zldev, index -1 ); - input->diff = true; +if (zl3073x_is_n_pin (index) &&zl3073x_ref_is_diff (zldev, index -1 )) { +memcpy (ref, &zldev->ref[index -1 ],sizeof (*ref));return 0 ; }
@@ -630,16 +627,14 @@ zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)return rc;/* Read ref_config register */ - rc =zl3073x_read_u8 (zldev,ZL_REG_REF_CONFIG , &ref_config); + rc =zl3073x_read_u8 (zldev,ZL_REG_REF_CONFIG , &ref->config);if (rc)return rc; - input->enabled =FIELD_GET (ZL_REF_CONFIG_ENABLE , ref_config); - input->diff =FIELD_GET (ZL_REF_CONFIG_DIFF_EN , ref_config); -dev_dbg (zldev->dev,"REF%u is %s and configured as %s\n" , index, -str_enabled_disabled (input->enabled), - input->diff ?"differential" :"single-ended" ); +str_enabled_disabled (zl3073x_ref_is_enabled (zldev, index)), +zl3073x_ref_is_diff (zldev, index) + ?"differential" :"single-ended" );return rc; }
@@ -649,8 +644,8 @@ zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)* @zldev: pointer to zl3073x_dev structure * @index: output index to fetch state for * -* Function fetches information for the given output (not output pin) -* that are invariant and stores them for later use. +* Function fetches state of the given output (not output pin) and stores it +* for later use. * * Return: 0 on success, <0 on error */
@@ -658,22 +653,16 @@ static int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index) {struct zl3073x_out *out = &zldev->out[index]; -u8 output_ctrl, output_mode;int rc;/* Read output configuration */ - rc =zl3073x_read_u8 (zldev,ZL_REG_OUTPUT_CTRL (index), &output_ctrl); + rc =zl3073x_read_u8 (zldev,ZL_REG_OUTPUT_CTRL (index), &out->ctrl);if (rc)return rc; -/* Store info about output enablement and synthesizer the output -* is connected to. -*/ - out->enabled =FIELD_GET (ZL_OUTPUT_CTRL_EN , output_ctrl); - out->synth =FIELD_GET (ZL_OUTPUT_CTRL_SYNTH_SEL , output_ctrl); -dev_dbg (zldev->dev,"OUT%u is %s and connected to SYNTH%u\n" , index, -str_enabled_disabled (out->enabled), out->synth); +str_enabled_disabled (zl3073x_out_is_enabled (zldev, index)), +zl3073x_out_synth_get (zldev, index));guard (mutex)(&zldev->multiop_lock);
@@ -683,17 +672,13 @@ zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)if (rc)return rc; -/* Read output_mode */ - rc =zl3073x_read_u8 (zldev,ZL_REG_OUTPUT_MODE , &output_mode); +/* Read output mode */ + rc =zl3073x_read_u8 (zldev,ZL_REG_OUTPUT_MODE , &out->mode);if (rc)return rc; -/* Extract and store output signal format */ - out->signal_format =FIELD_GET (ZL_OUTPUT_MODE_SIGNAL_FORMAT , - output_mode); -dev_dbg (zldev->dev,"OUT%u has signal format 0x%02x\n" , index, - out->signal_format); +zl3073x_out_signal_format_get (zldev, index));return rc; }
@@ -703,8 +688,7 @@ zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)* @zldev: pointer to zl3073x_dev structure * @index: synth index to fetch state for * -* Function fetches information for the given synthesizer that are -* invariant and stores them for later use. +* Function fetches state of the given synthesizer and stores it for later use. * * Return: 0 on success, <0 on error */
@@ -712,25 +696,13 @@ static int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index) {struct zl3073x_synth *synth = &zldev->synth[index]; -u16 base, m, n; -u8 synth_ctrl; -u32 mult;int rc;/* Read synth control register */ - rc =zl3073x_read_u8 (zldev,ZL_REG_SYNTH_CTRL (index), &synth_ctrl); + rc =zl3073x_read_u8 (zldev,ZL_REG_SYNTH_CTRL (index), &synth->ctrl);if (rc)return rc; -/* Store info about synth enablement and DPLL channel the synth is -* driven by. -*/ - synth->enabled =FIELD_GET (ZL_SYNTH_CTRL_EN , synth_ctrl); - synth->dpll =FIELD_GET (ZL_SYNTH_CTRL_DPLL_SEL , synth_ctrl); - -dev_dbg (zldev->dev,"SYNTH%u is %s and driven by DPLL%u\n" , index, -str_enabled_disabled (synth->enabled), synth->dpll); -guard (mutex)(&zldev->multiop_lock);/* Read synth configuration */
@@ -744,35 +716,32 @@ zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)* * Read registers with these values */ - rc =zl3073x_read_u16 (zldev,ZL_REG_SYNTH_FREQ_BASE , &base); + rc =zl3073x_read_u16 (zldev,ZL_REG_SYNTH_FREQ_BASE , &synth->freq_base);if (rc)return rc; - rc =zl3073x_read_u32 (zldev,ZL_REG_SYNTH_FREQ_MULT , &mult); + rc =zl3073x_read_u32 (zldev,ZL_REG_SYNTH_FREQ_MULT , &synth->freq_mult);if (rc)return rc; - rc =zl3073x_read_u16 (zldev,ZL_REG_SYNTH_FREQ_M , &m); + rc =zl3073x_read_u16 (zldev,ZL_REG_SYNTH_FREQ_M , &synth->freq_m);if (rc)return rc; - rc =zl3073x_read_u16 (zldev,ZL_REG_SYNTH_FREQ_N , &n); + rc =zl3073x_read_u16 (zldev,ZL_REG_SYNTH_FREQ_N , &synth->freq_n);if (rc)return rc;/* Check denominator for zero to avoid div by 0 */ -if (!n) { +if (!synth->freq_n) {dev_err (zldev->dev,"Zero divisor for SYNTH%u retrieved from device\n" , index);return -EINVAL ; } -/* Compute and store synth frequency */ - zldev->synth[index].freq =div_u64 (mul_u32_u32 (base * m, mult), n); -dev_dbg (zldev->dev,"SYNTH%u frequency: %u Hz\n" , index, - zldev->synth[index].freq); +zl3073x_synth_freq_get (zldev, index));return rc; }
drivers/dpll/zl3073x/core.h +35 -26
132 lines not on this page · raw message
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 320/641 dpll: zl3073x: Split ref, out, and synth logic from core
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 607f2c00c61faa3b437dbb0d38287e7a9d398a52 ]
Refactor the zl3073x driver by splitting the logic for input references, outputs and synthesizers out of the monolithic core.[ch] files.
Move the logic for each functional block into its own dedicated files:
ref.[ch], out.[ch] and synth.[ch].
Specifically:
- Move state structures (zl3073x_ref, zl3073x_out, zl3073x_synth) from core.h into their respective new headers
- Move state-fetching functions (..._state_fetch) from core.c to their new .c files
- Move the zl3073x_ref_freq_factorize helper from core.c to ref.c
- Introduce a new helper layer to decouple the core device logic from the state-parsing logic:
1. Move the original inline helpers (e.g., zl3073x_ref_is_enabled) to the new headers (ref.h, etc.) and make them operate on a const struct ... * pointer.
2. Create new zl3073x_dev_... prefixed functions in core.h (e.g., zl3073x_dev_ref_is_enabled) and Implement these _dev_ functions to fetch state using a new ..._state_get() helper and then call the non-prefixed helper.
3. Update all driver-internal callers (in dpll.c, prop.c, etc.) to use
the new zl3073x_dev_... functions.
Stable-dep-of: 5d41f95f5d0b ("dpll: zl3073x: Fix output pin phase adjustment sign")
--- drivers/dpll/zl3073x/Makefile | 3 +- drivers/dpll/zl3073x/core.c | 194 ---------------------------------- drivers/dpll/zl3073x/core.h | 170 +++++++++++++---------------- drivers/dpll/zl3073x/dpll.c | 36 +++---- drivers/dpll/zl3073x/out.c | 67 ++++++++++++ drivers/dpll/zl3073x/out.h | 80 ++++++++++++++ drivers/dpll/zl3073x/prop.c | 12 +-- drivers/dpll/zl3073x/ref.c | 112 ++++++++++++++++++++ drivers/dpll/zl3073x/ref.h | 66 ++++++++++++ drivers/dpll/zl3073x/synth.c | 87 +++++++++++++++ drivers/dpll/zl3073x/synth.h | 72 +++++++++++++ 11 files changed, 584 insertions(+), 315 deletions(-) create mode 100644 drivers/dpll/zl3073x/out.c create mode 100644 drivers/dpll/zl3073x/out.h create mode 100644 drivers/dpll/zl3073x/ref.c create mode 100644 drivers/dpll/zl3073x/ref.h create mode 100644 drivers/dpll/zl3073x/synth.c create mode 100644 drivers/dpll/zl3073x/synth.h
drivers/dpll/zl3073x/Makefile +2 -1
@@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_ZL3073X) += zl3073x.o -zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o prop.o +zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o \ + out.o prop.o ref.o synth.o obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o zl3073x_i2c-objs := i2c.o
drivers/dpll/zl3073x/core.c +0 -194
206 lines not on this page · raw message
drivers/dpll/zl3073x/core.h +74 -96
327 lines not on this page · raw message
drivers/dpll/zl3073x/dpll.c +18 -18
@@ -967,7 +967,7 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,* for N-division is also used for the esync divider so both cannot * be used. */ -switch (zl3073x_out_signal_format_get (zldev, out)) { +switch (zl3073x_dev_out_signal_format_get (zldev, out)) {case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV :case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV :return -EOPNOTSUPP ;
@@ -1001,10 +1001,10 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin, }/* Get synth attached to output pin */ - synth =zl3073x_out_synth_get (zldev, out); + synth =zl3073x_dev_out_synth_get (zldev, out);/* Get synth frequency */ - synth_freq =zl3073x_synth_freq_get (zldev, synth); + synth_freq =zl3073x_dev_synth_freq_get (zldev, synth); clock_type =FIELD_GET (ZL_OUTPUT_MODE_CLOCK_TYPE , output_mode);if (clock_type !=ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC ) {
@@ -1078,7 +1078,7 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,* for N-division is also used for the esync divider so both cannot * be used. */ -switch (zl3073x_out_signal_format_get (zldev, out)) { +switch (zl3073x_dev_out_signal_format_get (zldev, out)) {case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV :case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV :return -EOPNOTSUPP ;
@@ -1117,10 +1117,10 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin, goto write_mailbox;/* Get synth attached to output pin */ - synth =zl3073x_out_synth_get (zldev, out); + synth =zl3073x_dev_out_synth_get (zldev, out);/* Get synth frequency */ - synth_freq =zl3073x_synth_freq_get (zldev, synth); + synth_freq =zl3073x_dev_synth_freq_get (zldev, synth); rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_DIV , &output_div);if (rc)
@@ -1172,8 +1172,8 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,int rc; out =zl3073x_output_pin_out_get (pin->id); - synth =zl3073x_out_synth_get (zldev, out); - synth_freq =zl3073x_synth_freq_get (zldev, synth); + synth =zl3073x_dev_out_synth_get (zldev, out); + synth_freq =zl3073x_dev_synth_freq_get (zldev, synth);guard (mutex)(&zldev->multiop_lock);
@@ -1195,7 +1195,7 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin, }/* Read used signal format for the given output */ - signal_format =zl3073x_out_signal_format_get (zldev, out); + signal_format =zl3073x_dev_out_signal_format_get (zldev, out);switch (signal_format) {case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV :
@@ -1263,12 +1263,12 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,int rc; out =zl3073x_output_pin_out_get (pin->id); - synth =zl3073x_out_synth_get (zldev, out); - synth_freq =zl3073x_synth_freq_get (zldev, synth); + synth =zl3073x_dev_out_synth_get (zldev, out); + synth_freq =zl3073x_dev_synth_freq_get (zldev, synth); new_div = synth_freq / (u32 )frequency;/* Get used signal format for the given output */ - signal_format =zl3073x_out_signal_format_get (zldev, out); + signal_format =zl3073x_dev_out_signal_format_get (zldev, out);guard (mutex)(&zldev->multiop_lock);
@@ -1856,8 +1856,8 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,if (zldpll->refsel_mode ==ZL_DPLL_MODE_REFSEL_MODE_NCO )return false; - is_diff =zl3073x_ref_is_diff (zldev, ref); - is_enabled =zl3073x_ref_is_enabled (zldev, ref); + is_diff =zl3073x_dev_ref_is_diff (zldev, ref); + is_enabled =zl3073x_dev_ref_is_enabled (zldev, ref); }else {/* Output P&N pair shares single HW output */ u8 out =zl3073x_output_pin_out_get (index);
@@ -1865,7 +1865,7 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll, name ="OUT" ;/* Skip the pin if it is connected to different DPLL channel */ -if (zl3073x_out_dpll_get (zldev, out) != zldpll->id) { +if (zl3073x_dev_out_dpll_get (zldev, out) != zldpll->id) {dev_dbg (zldev->dev,"%s%u is driven by different DPLL\n" , name, out);
@@ -1873,8 +1873,8 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,return false; } - is_diff =zl3073x_out_is_diff (zldev, out); - is_enabled =zl3073x_output_pin_is_enabled (zldev, index); + is_diff =zl3073x_dev_out_is_diff (zldev, out); + is_enabled =zl3073x_dev_output_pin_is_enabled (zldev, index); }/* Skip N-pin if the corresponding input/output is differential */
@@ -2124,7 +2124,7 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)return false;/* Get the latest measured ref's ffo */ - ffo =zl3073x_ref_ffo_get (zldev, ref); + ffo =zl3073x_dev_ref_ffo_get (zldev, ref);/* Compare with previous value */ if (pin->freq_offset != ffo) {
drivers/dpll/zl3073x/out.c new file +67 -0
67 lines not on this page · raw message
drivers/dpll/zl3073x/out.h new file +80 -0
80 lines not on this page · raw message
drivers/dpll/zl3073x/prop.c +6 -6
@@ -46,10 +46,10 @@ zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir,/* Get output pin synthesizer */ out =zl3073x_output_pin_out_get (id); - synth =zl3073x_out_synth_get (zldev, out); + synth =zl3073x_dev_out_synth_get (zldev, out);/* Get synth frequency */ - synth_freq =zl3073x_synth_freq_get (zldev, synth); + synth_freq =zl3073x_dev_synth_freq_get (zldev, synth);/* Check the frequency divides synth frequency */ if (synth_freq % (u32 )freq)
@@ -93,13 +93,13 @@ zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev, prefix ="REF" ; ref =zl3073x_input_pin_ref_get (id); - is_diff =zl3073x_ref_is_diff (zldev, ref); + is_diff =zl3073x_dev_ref_is_diff (zldev, ref); }else {u8 out; prefix ="OUT" ; out =zl3073x_output_pin_out_get (id); - is_diff =zl3073x_out_is_diff (zldev, out); + is_diff =zl3073x_dev_out_is_diff (zldev, out); }if (!is_diff)
@@ -217,8 +217,8 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,* the synth frequency count. */ out =zl3073x_output_pin_out_get (index); - synth =zl3073x_out_synth_get (zldev, out); - f =2 *zl3073x_synth_freq_get (zldev, synth); + synth =zl3073x_dev_out_synth_get (zldev, out); + f =2 *zl3073x_dev_synth_freq_get (zldev, synth); props->dpll_props.phase_gran = f ?div_u64 (PSEC_PER_SEC , f) :1 ; }
drivers/dpll/zl3073x/ref.c new file +112 -0
112 lines not on this page · raw message
drivers/dpll/zl3073x/ref.h new file +66 -0
66 lines not on this page · raw message
drivers/dpll/zl3073x/synth.c new file +87 -0
87 lines not on this page · raw message
drivers/dpll/zl3073x/synth.h new file +72 -0
72 lines not on this page · raw message
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 321/641 dpll: zl3073x: Cache all output properties in zl3073x_out
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 5fb9b0d411f81ec46833ea8e43c0263515060c64 ]
Expand the zl3073x_out structure to cache all output-related hardware registers, including divisors, widths, embedded-sync parameters and phase compensation.
Modify zl3073x_out_state_fetch() to read and populate all these new fields at once, including zero-divisor checks. Refactor all dpll "getter" functions in dpll.c to read from this new cached state instead of performing direct register access.
Introduce a new function, zl3073x_out_state_set(), to handle writing changes back to the hardware. This function compares the provided state with the current cached state and writes *only* the modified register values via a single mailbox sequence before updating the local cache.
Refactor all dpll "setter" functions to modify a local copy of the output state and then call zl3073x_out_state_set() to commit the changes.
This change centralizes all output-related register I/O into out.c, significantly reduces bus traffic, and simplifies the logic in dpll.c.
Stable-dep-of: 5d41f95f5d0b ("dpll: zl3073x: Fix output pin phase adjustment sign")
--- drivers/dpll/zl3073x/dpll.c | 380 +++++++++--------------------------- drivers/dpll/zl3073x/out.c | 90 +++++++++ drivers/dpll/zl3073x/out.h | 13 ++ 3 files changed, 193 insertions(+), 290 deletions(-)
drivers/dpll/zl3073x/dpll.c +90 -290
@@ -953,21 +953,19 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -struct device *dev = zldev->dev; -u32 esync_period, esync_width; -u8 clock_type, synth; -u8 out, output_mode; -u32 output_div; +const struct zl3073x_synth *synth; +const struct zl3073x_out *out; +u8 clock_type, out_id;u32 synth_freq; -int rc; - out =zl3073x_output_pin_out_get (pin->id); + out_id =zl3073x_output_pin_out_get (pin->id); + out =zl3073x_out_state_get (zldev, out_id);/* If N-division is enabled, esync is not supported. The register used * for N-division is also used for the esync divider so both cannot * be used. */ -switch (zl3073x_dev_out_signal_format_get (zldev, out)) { +switch (zl3073x_out_signal_format_get (out)) {case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV :case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV :return -EOPNOTSUPP ;
@@ -975,38 +973,11 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,break ; } -guard (mutex)(&zldev->multiop_lock); - -/* Read output configuration into mailbox */ - rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); -if (rc) -return rc; - -/* Read output mode */ - rc =zl3073x_read_u8 (zldev,ZL_REG_OUTPUT_MODE , &output_mode); -if (rc) -return rc; - -/* Read output divisor */ - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_DIV , &output_div); -if (rc) -return rc; - -/* Check output divisor for zero */ -if (!output_div) { -dev_err (dev,"Zero divisor for OUTPUT%u got from device\n" , - out); -return -EINVAL ; - } - -/* Get synth attached to output pin */ - synth =zl3073x_dev_out_synth_get (zldev, out); - -/* Get synth frequency */ - synth_freq =zl3073x_dev_synth_freq_get (zldev, synth); +/* Get attached synth frequency */ + synth =zl3073x_synth_state_get (zldev,zl3073x_out_synth_get (out)); + synth_freq =zl3073x_synth_freq_get (synth); - clock_type =FIELD_GET (ZL_OUTPUT_MODE_CLOCK_TYPE , output_mode); + clock_type =FIELD_GET (ZL_OUTPUT_MODE_CLOCK_TYPE , out->mode);if (clock_type !=ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC ) {/* No need to read esync data if it is not enabled */ esync->freq =0 ;
@@ -1015,38 +986,21 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin, goto finish; } -/* Read esync period */ - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_ESYNC_PERIOD , &esync_period); -if (rc) -return rc; - -/* Check esync divisor for zero */ -if (!esync_period) { -dev_err (dev,"Zero esync divisor for OUTPUT%u got from device\n" , - out); -return -EINVAL ; - } - -/* Get esync pulse width in units of half synth cycles */ - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_ESYNC_WIDTH , &esync_width); -if (rc) -return rc; -/* Compute esync frequency */ - esync->freq = synth_freq / output_div / esync_period; + esync->freq = synth_freq / out->div / out->esync_n_period;/* By comparing the esync_pulse_width to the half of the pulse width * the esync pulse percentage can be determined. * Note that half pulse width is in units of half synth cycles, which * is why it reduces down to be output_div. */ - esync->pulse = (50 * esync_width) / output_div; + esync->pulse = (50 * out->esync_n_width) / out->div; finish:/* Set supported esync ranges if the pin supports esync control and * if the output frequency is > 1 Hz. */ -if (pin->esync_control && (synth_freq / output_div) >1 ) { +if (pin->esync_control && (synth_freq / out->div) >1 ) { esync->range = esync_freq_ranges; esync->range_num =ARRAY_SIZE (esync_freq_ranges); }else {
@@ -1064,21 +1018,22 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin, void *dpll_priv, u64 freq, struct netlink_ext_ack *extack) { - u32 esync_period, esync_width, output_div; struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -u8 clock_type, out, output_mode, synth; +const struct zl3073x_synth *synth; +struct zl3073x_out out; +u8 clock_type, out_id;u32 synth_freq; -int rc; - out =zl3073x_output_pin_out_get (pin->id); + out_id =zl3073x_output_pin_out_get (pin->id); + out = *zl3073x_out_state_get (zldev, out_id);/* If N-division is enabled, esync is not supported. The register used * for N-division is also used for the esync divider so both cannot * be used. */ -switch (zl3073x_dev_out_signal_format_get (zldev, out)) { +switch (zl3073x_out_signal_format_get (&out)) {case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV :case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV :return -EOPNOTSUPP ;
@@ -1086,19 +1041,6 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,break ; } -guard (mutex)(&zldev->multiop_lock); - -/* Read output configuration into mailbox */ - rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); -if (rc) -return rc; - -/* Read output mode */ - rc =zl3073x_read_u8 (zldev,ZL_REG_OUTPUT_MODE , &output_mode); -if (rc) -return rc; -/* Select clock type */ if (freq) clock_type =ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC ;
@@ -1106,38 +1048,19 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin, clock_type =ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL ;/* Update clock type in output mode */ - output_mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE ; - output_mode |=FIELD_PREP (ZL_OUTPUT_MODE_CLOCK_TYPE , clock_type); - rc =zl3073x_write_u8 (zldev,ZL_REG_OUTPUT_MODE , output_mode); -if (rc) -return rc; + out.mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE ; + out.mode |=FIELD_PREP (ZL_OUTPUT_MODE_CLOCK_TYPE , clock_type);/* If esync is being disabled just write mailbox and finish */ if (!freq) goto write_mailbox; -/* Get synth attached to output pin */ - synth =zl3073x_dev_out_synth_get (zldev, out); - -/* Get synth frequency */ - synth_freq =zl3073x_dev_synth_freq_get (zldev, synth); - - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_DIV , &output_div); -if (rc) -return rc; - -/* Check output divisor for zero */ -if (!output_div) { -dev_err (zldev->dev, -"Zero divisor for OUTPUT%u got from device\n" , out); -return -EINVAL ; - } +/* Get attached synth frequency */ + synth =zl3073x_synth_state_get (zldev,zl3073x_out_synth_get (&out)); + synth_freq =zl3073x_synth_freq_get (synth);/* Compute and update esync period */ - esync_period = synth_freq / (u32 )freq / output_div; - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_ESYNC_PERIOD , esync_period); -if (rc) -return rc; + out.esync_n_period = synth_freq / (u32 )freq / out.div;/* Half of the period in units of 1/2 synth cycle can be represented by * the output_div. To get the supported esync pulse width of 25% of the
@@ -1145,15 +1068,11 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,* assumes that output_div is even, otherwise some resolution will be * lost. */ - esync_width = output_div /2 ; - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_ESYNC_WIDTH , esync_width); -if (rc) -return rc; + out.esync_n_width = out.div /2 ; write_mailbox:/* Commit output configuration */ -return zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_WR , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); +return zl3073x_out_state_set (zldev, out_id, &out); } static int
@@ -1166,83 +1085,46 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -struct device *dev = zldev->dev; -u8 out, signal_format, synth; -u32 output_div, synth_freq; -int rc; - - out =zl3073x_output_pin_out_get (pin->id); - synth =zl3073x_dev_out_synth_get (zldev, out); - synth_freq =zl3073x_dev_synth_freq_get (zldev, synth); - -guard (mutex)(&zldev->multiop_lock); - -/* Read output configuration into mailbox */ - rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); -if (rc) -return rc; - - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_DIV , &output_div); -if (rc) -return rc; +const struct zl3073x_synth *synth; +const struct zl3073x_out *out; +u32 synth_freq; +u8 out_id; -/* Check output divisor for zero */ -if (!output_div) { -dev_err (dev,"Zero divisor for output %u got from device\n" , - out); -return -EINVAL ; - } + out_id =zl3073x_output_pin_out_get (pin->id); + out =zl3073x_out_state_get (zldev, out_id); -/* Read used signal format for the given output */ - signal_format =zl3073x_dev_out_signal_format_get (zldev, out); +/* Get attached synth frequency */ + synth =zl3073x_synth_state_get (zldev,zl3073x_out_synth_get (out)); + synth_freq =zl3073x_synth_freq_get (synth); -switch (signal_format) { +switch (zl3073x_out_signal_format_get (out)) {case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV :case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV :/* In case of divided format we have to distiguish between * given output pin type. +* +* For P-pin the resulting frequency is computed as simple +* division of synth frequency and output divisor. +* +* For N-pin we have to divide additionally by divisor stored +* in esync_n_period output mailbox register that is used as +* N-pin divisor for these modes. */ -if (zl3073x_dpll_is_p_pin (pin)) { -/* For P-pin the resulting frequency is computed as -* simple division of synth frequency and output -* divisor. -*/ - *frequency = synth_freq / output_div; - }else { -/* For N-pin we have to divide additionally by -* divisor stored in esync_period output mailbox -* register that is used as N-pin divisor for these -* modes. -*/ -u32 ndiv; - - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_ESYNC_PERIOD , - &ndiv); -if (rc) -return rc; + *frequency = synth_freq / out->div; -/* Check N-pin divisor for zero */ -if (!ndiv) { -dev_err (dev, -"Zero N-pin divisor for output %u got from device\n" , - out); -return -EINVAL ; - } +if (!zl3073x_dpll_is_p_pin (pin)) + *frequency = (u32)*frequency / out->esync_n_period; -/* Compute final divisor for N-pin */ - *frequency = synth_freq / output_div / ndiv; - }break ;default :/* In other modes the resulting frequency is computed as * division of synth frequency and output divisor. */ - *frequency = synth_freq / output_div; + *frequency = synth_freq / out->div;break ; } -return rc; +return 0 ; } static int
@@ -1255,28 +1137,21 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -struct device *dev = zldev->dev; -u32 output_n_freq, output_p_freq; -u8 out, signal_format, synth; -u32 cur_div, new_div, ndiv; -u32 synth_freq; -int rc; +const struct zl3073x_synth *synth; +u8 out_id, signal_format; +u32 new_div, synth_freq; +struct zl3073x_out out; - out =zl3073x_output_pin_out_get (pin->id); - synth =zl3073x_dev_out_synth_get (zldev, out); - synth_freq =zl3073x_dev_synth_freq_get (zldev, synth); + out_id =zl3073x_output_pin_out_get (pin->id); + out = *zl3073x_out_state_get (zldev, out_id); + +/* Get attached synth frequency and compute new divisor */ + synth =zl3073x_synth_state_get (zldev,zl3073x_out_synth_get (&out)); + synth_freq =zl3073x_synth_freq_get (synth); new_div = synth_freq / (u32 )frequency;/* Get used signal format for the given output */ - signal_format =zl3073x_dev_out_signal_format_get (zldev, out); - -guard (mutex)(&zldev->multiop_lock); - -/* Load output configuration */ - rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); -if (rc) -return rc; + signal_format =zl3073x_out_signal_format_get (&out);/* Check signal format */ if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV &&
@@ -1284,99 +1159,50 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,/* For non N-divided signal formats the frequency is computed * as division of synth frequency and output divisor. */ - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_DIV , new_div); -if (rc) -return rc; + out.div = new_div;/* For 50/50 duty cycle the divisor is equal to width */ - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_WIDTH , new_div); -if (rc) -return rc; + out.width = new_div;/* Commit output configuration */ -return zl3073x_mb_op (zldev, -ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_WR , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); +return zl3073x_out_state_set (zldev, out_id, &out); } -/* For N-divided signal format get current divisor */ - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_DIV , &cur_div); -if (rc) -return rc; - -/* Check output divisor for zero */ -if (!cur_div) { -dev_err (dev,"Zero divisor for output %u got from device\n" , - out); -return -EINVAL ; - } - -/* Get N-pin divisor (shares the same register with esync */ - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_ESYNC_PERIOD , &ndiv); -if (rc) -return rc; - -/* Check N-pin divisor for zero */ -if (!ndiv) { -dev_err (dev, -"Zero N-pin divisor for output %u got from device\n" , - out); -return -EINVAL ; - } - -/* Compute current output frequency for P-pin */ - output_p_freq = synth_freq / cur_div; - -/* Compute current N-pin frequency */ - output_n_freq = output_p_freq / ndiv; -if (zl3073x_dpll_is_p_pin (pin)) {/* We are going to change output frequency for P-pin but * if the requested frequency is less than current N-pin * frequency then indicate a failure as we are not able * to compute N-pin divisor to keep its frequency unchanged. +* +* Update divisor for N-pin to keep N-pin frequency. */ -if (frequency <= output_n_freq) + out.esync_n_period = (out.esync_n_period * out.div) / new_div; +if (!out.esync_n_period)return -EINVAL ;/* Update the output divisor */ - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_DIV , new_div); -if (rc) -return rc; + out.div = new_div;/* For 50/50 duty cycle the divisor is equal to width */ - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_WIDTH , new_div); -if (rc) -return rc; - -/* Compute new divisor for N-pin */ - ndiv = (u32 )frequency / output_n_freq; + out.width = out.div; }else {/* We are going to change frequency of N-pin but if * the requested freq is greater or equal than freq of P-pin * in the output pair we cannot compute divisor for the N-pin. * In this case indicate a failure. +* +* Update divisor for N-pin */ -if (output_p_freq <= frequency) + out.esync_n_period =div64_u64 (synth_freq, frequency * out.div); +if (!out.esync_n_period)return -EINVAL ; - -/* Compute new divisor for N-pin */ - ndiv = output_p_freq / (u32 )frequency; } -/* Update divisor for the N-pin */ - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_ESYNC_PERIOD , ndiv); -if (rc) -return rc; -/* For 50/50 duty cycle the divisor is equal to width */ - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_ESYNC_WIDTH , ndiv); -if (rc) -return rc; + out.esync_n_width = out.esync_n_period;/* Commit output configuration */ -return zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_WR , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); +return zl3073x_out_state_set (zldev, out_id, &out); } static int
@@ -1390,30 +1216,18 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -s32 phase_comp; -u8 out; -int rc; - -guard (mutex)(&zldev->multiop_lock); - -/* Read output configuration */ - out =zl3073x_output_pin_out_get (pin->id); - rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); -if (rc) -return rc; +const struct zl3073x_out *out; +u8 out_id; -/* Read current output phase compensation */ - rc =zl3073x_read_u32 (zldev,ZL_REG_OUTPUT_PHASE_COMP , &phase_comp); -if (rc) -return rc; + out_id =zl3073x_output_pin_out_get (pin->id); + out =zl3073x_out_state_get (zldev, out_id);/* Convert value to ps and reverse two's complement negation applied * during 'set' */ - *phase_adjust = -phase_comp * pin->phase_gran; + *phase_adjust = -out->phase_comp * pin->phase_gran; -return rc; +return 0 ; } static int
@@ -1427,31 +1241,19 @@ zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,struct zl3073x_dpll *zldpll = dpll_priv;struct zl3073x_dev *zldev = zldpll->dev;struct zl3073x_dpll_pin *pin = pin_priv; -u8 out; -int rc; +struct zl3073x_out out; +u8 out_id; + + out_id =zl3073x_output_pin_out_get (pin->id); + out = *zl3073x_out_state_get (zldev, out_id);/* The value in the register is stored as two's complement negation * of requested value and expressed in half synth clock cycles. */ - phase_adjust = -phase_adjust / pin->phase_gran; - -guard (mutex)(&zldev->multiop_lock); - -/* Read output configuration */ - out =zl3073x_output_pin_out_get (pin->id); - rc =zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_RD , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); -if (rc) -return rc; - -/* Write the requested value into the compensation register */ - rc =zl3073x_write_u32 (zldev,ZL_REG_OUTPUT_PHASE_COMP , phase_adjust); -if (rc) -return rc; + out.phase_comp = -phase_adjust / pin->phase_gran;/* Update output configuration from mailbox */ -return zl3073x_mb_op (zldev,ZL_REG_OUTPUT_MB_SEM ,ZL_OUTPUT_MB_SEM_WR , -ZL_REG_OUTPUT_MB_MASK ,BIT (out)); +return zl3073x_out_state_set (zldev, out_id, &out); } static int
@@ -1862,17 +1664,15 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,/* Output P&N pair shares single HW output */ u8 out =zl3073x_output_pin_out_get (index); - name ="OUT" ; -/* Skip the pin if it is connected to different DPLL channel */ if (zl3073x_dev_out_dpll_get (zldev, out) != zldpll->id) {dev_dbg (zldev->dev, -"%s%u is driven by different DPLL\n" , name, - out); +"OUT%u is driven by different DPLL\n" , out);return false; } + name ="OUT" ; is_diff =zl3073x_dev_out_is_diff (zldev, out); is_enabled =zl3073x_dev_output_pin_is_enabled (zldev, index); }
drivers/dpll/zl3073x/out.c +90 -0
99 lines not on this page · raw message
drivers/dpll/zl3073x/out.h +13 -0
@@ -12,10 +12,20 @@ struct zl3073x_dev;/** * struct zl3073x_out - output state +* @div: output divisor +* @width: output pulse width +* @esync_n_period: embedded sync or n-pin period (for n-div formats) +* @esync_n_width: embedded sync or n-pin pulse width +* @phase_comp: phase compensation * @ctrl: output control * @mode: output mode */ struct zl3073x_out { +u32 div; +u32 width; +u32 esync_n_period; +u32 esync_n_width; +s32 phase_comp;u8 ctrl;u8 mode; };
@@ -24,6 +34,9 @@ int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index);const struct zl3073x_out *zl3073x_out_state_get (struct zl3073x_dev *zldev,u8 index); +int zl3073x_out_state_set (struct zl3073x_dev *zldev,u8 index, +const struct zl3073x_out *out); +/** * zl3073x_out_signal_format_get - get output signal format * @out: pointer to out state
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 322/641 dpll: zl3073x: Fix output pin phase adjustment sign
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ivan Vecera <[email protected]>
[ Upstream commit 5d41f95f5d0bd9db02f3f16a649d0631f71e9fdb ]
The output pin phase adjustment functions incorrectly negate the phase compensation value.
Per the ZL3073x datasheet, the output phase compensation register is simply a signed two's complement integer where:
- Positive values move the phase later in time
- Negative values move the phase earlier in time
No negation is required. The erroneous negation caused phase adjustments to be applied in the wrong direction.
Note that input pin phase adjustment correctly uses negation because the hardware has an inverted convention for input references (positive moves phase earlier, negative moves phase later).
--- drivers/dpll/zl3073x/dpll.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
drivers/dpll/zl3073x/dpll.c +4 -8
@@ -1222,10 +1222,8 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin, out_id =zl3073x_output_pin_out_get (pin->id); out =zl3073x_out_state_get (zldev, out_id); -/* Convert value to ps and reverse two's complement negation applied -* during 'set' -*/ - *phase_adjust = -out->phase_comp * pin->phase_gran; +/* The value in the register is expressed in half synth clock cycles. */ + *phase_adjust = out->phase_comp * pin->phase_gran;return 0 ; }
@@ -1247,10 +1245,8 @@ zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin, out_id =zl3073x_output_pin_out_get (pin->id); out = *zl3073x_out_state_get (zldev, out_id); -/* The value in the register is stored as two's complement negation -* of requested value and expressed in half synth clock cycles. -*/ - out.phase_comp = -phase_adjust / pin->phase_gran; +/* The value in the register is expressed in half synth clock cycles. */ + out.phase_comp = phase_adjust / pin->phase_gran;/* Update output configuration from mailbox */ return zl3073x_out_state_set (zldev, out_id, &out);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 323/641 net: hns3: fix double free issue for tx spare buffer
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jian Shen <[email protected]>
[ Upstream commit 6d2f142b1e4b203387a92519d9d2e34752a79dbb ]
In hns3_set_ringparam(), a temporary copy (tmp_rings) of the ring structure is created for rollback. However, the tx_spare pointer in the original ring handle is incorrectly left pointing to the old backup memory.
Later, if memory allocation fails in hns3_init_all_ring() during the setup, the error path attempts to free all newly allocated rings. Since tx_spare contains a stale (non-NULL) pointer from the backup, it is mistaken for a newly allocated buffer and is erroneously freed, leading to a double-free of the backup memory.
The root cause is that the tx_spare field was not cleared after its value was saved in tmp_rings, leaving a dangling pointer.
Fix this by setting tx_spare to NULL in the original ring structure when the creation of the new tx_spare fails. This ensures the error cleanup path only frees genuinely newly allocated buffers.
--- drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +9 -2
@@ -1048,13 +1048,13 @@ static void hns3_init_tx_spare_buffer(struct hns3_enet_ring *ring)int order;if (!alloc_size) -return ; + goto not_init; order =get_order (alloc_size);if (order >MAX_PAGE_ORDER ) {if (net_ratelimit ())dev_warn (ring_to_dev (ring),"failed to allocate tx spare buffer, exceed to max order\n" ); -return ; + goto not_init; } tx_spare = devm_kzalloc(ring_to_dev(ring), sizeof(*tx_spare),
@@ -1092,6 +1092,13 @@ static void hns3_init_tx_spare_buffer(struct hns3_enet_ring *ring)devm_kfree (ring_to_dev (ring), tx_spare); devm_kzalloc_error: ring->tqp->handle->kinfo.tx_spare_buf_size =0 ; +not_init: +/* When driver init or reset_init, the ring->tx_spare is always NULL; +* but when called from hns3_set_ringparam, it's usually not NULL, and +* will be restored if hns3_init_all_ring() failed. So it's safe to set +* ring->tx_spare to NULL here. +*/ + ring->tx_spare =NULL ; }/* Use hns3_tx_spare_space() to make sure there is enough buffer
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 324/641 procfs: fix missing RCU protection when reading real_parent in do_task_stat()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jinliang Zheng <[email protected]>
[ Upstream commit 76149d53502cf17ef3ae454ff384551236fba867 ]
When reading /proc/[pid]/stat, do_task_stat() accesses task->real_parent without proper RCU protection, which leads to:
cpu 0 cpu 1
----- -----
do_task_stat
var = task->real_parent
release_task
call_rcu(delayed_put_task_struct)
task_tgid_nr_ns(var)
rcu_read_lock <--- Too late to protect task->real_parent!
task_pid_ptr <--- UAF!
rcu_read_unlockThis patch uses task_ppid_nr_ns() instead of task_tgid_nr_ns() to add proper RCU protection for accessing task->real_parent.
--- fs/proc/array.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/proc/array.c +1 -1
@@ -529,7 +529,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, } sid =task_session_nr_ns (task, ns); - ppid =task_tgid_nr_ns (task->real_parent, ns); + ppid =task_ppid_nr_ns (task, ns); pgid =task_pgrp_nr_ns (task, ns);unlock_task_sighand (task, &flags);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 325/641 smb: client: correct value for smbd_max_fragmented_recv_size
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Metzmacher <[email protected]>
[ Upstream commit 4a93d1ee2d0206970b6eb13fbffe07938cd95948 ]
When we download a file without rdma offload or get
a large directly enumeration from the server,
the server might want to send up to smbd_max_fragmented_recv_size bytes, but if it is too large all our recv buffers might already be moved to the recv_io.reassembly.list and we're no longer able to grant recv credits.
The maximum fragmented upper-layer payload receive size supported
Assume max_payload_per_credit issmbd_max_receive_size - 24 = 1340
The maximum number would besmbd_receive_credit_max * max_payload_per_credit
1340 * 255 = 341700 (0x536C4)
The minimum value from the spec is 131072 (0x20000)
For now we use the logic we used in ksmbd before:
(1364 * 255) / 2 = 173910 (0x2A756)
--- fs/smb/client/smbdirect.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
fs/smb/client/smbdirect.c +17 -2
@@ -101,8 +101,23 @@ int smbd_send_credit_target = 255;/* The maximum single message size can be sent to remote peer */ int smbd_max_send_size =1364 ; -/* The maximum fragmented upper-layer payload receive size supported */ -int smbd_max_fragmented_recv_size =1024 *1024 ; +/* +* The maximum fragmented upper-layer payload receive size supported +* +* Assume max_payload_per_credit is +* smbd_max_receive_size - 24 = 1340 +* +* The maximum number would be +* smbd_receive_credit_max * max_payload_per_credit +* +* 1340 * 255 = 341700 (0x536C4) +* +* The minimum value from the spec is 131072 (0x20000) +* +* For now we use the logic we used in ksmbd before: +* (1364 * 255) / 2 = 173910 (0x2A756) +*/ +int smbd_max_fragmented_recv_size = (1364 *255 ) /2 ;/* The maximum single-message size which can be received */ int smbd_max_receive_size =1364 ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 326/641 net: atm: fix crash due to unvalidated vcc pointer in sigd_send()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit ae88a5d2f29b69819dc7b04086734439d074a643 ]
Reproducer available at [1].
The ATM send path (sendmsg -> vcc_sendmsg -> sigd_send) reads the vcc pointer from msg->vcc and uses it directly without any validation. This pointer comes from userspace via sendmsg() and can be arbitrarily forged:
int fd = socket(AF_ATMSVC, SOCK_DGRAM, 0);
ioctl(fd, ATMSIGD_CTRL); // become ATM signaling daemon
struct msghdr msg = { .msg_iov = &iov, ... };
*(unsigned long *)(buf + 4) = 0xdeadbeef; // fake vcc pointer
sendmsg(fd, &msg, 0); // kernel dereferences 0xdeadbeefIn normal operation, the kernel sends the vcc pointer to the signaling daemon via sigd_enq() when processing operations like connect(), bind(), or listen(). The daemon is expected to return the same pointer when responding. However, a malicious daemon can send arbitrary pointer values.
Fix this by introducing find_get_vcc() which validates the pointer by searching through vcc_hash (similar to how sigd_close() iterates over all VCCs), and acquires a reference via sock_hold() if found.
Since struct atm_vcc embeds struct sock as its first member, they share the same lifetime. Therefore using sock_hold/sock_put is sufficient to keep the vcc alive while it is being used.
Note that there may be a race with sigd_close() which could mark the vcc with various flags (e.g., ATM_VF_RELEASED) after find_get_vcc() returns. However, sock_hold() guarantees the memory remains valid, so this race only affects the logical state, not memory safety.
[1]: https://gist.github.com/mrpre/1ba5949c45529c511152e2f4c755b0f3
--- net/atm/signaling.c | 56 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-)
net/atm/signaling.c +54 -2
@@ -22,6 +22,36 @@struct atm_vcc *sigd =NULL ; +/* +* find_get_vcc - validate and get a reference to a vcc pointer +* @vcc: the vcc pointer to validate +* +* This function validates that @vcc points to a registered VCC in vcc_hash. +* If found, it increments the socket reference count and returns the vcc. +* The caller must call sock_put(sk_atm(vcc)) when done. +* +* Returns the vcc pointer if valid, NULL otherwise. +*/ +static struct atm_vcc *find_get_vcc (struct atm_vcc *vcc) +{ +int i; + +read_lock (&vcc_sklist_lock); +for (i =0 ; i <VCC_HTABLE_SIZE ; i++) { +struct sock *s; + +sk_for_each (s, &vcc_hash[i]) { +if (atm_sk (s) == vcc) { +sock_hold (s); +read_unlock (&vcc_sklist_lock); +return vcc; + } + } + } +read_unlock (&vcc_sklist_lock); +return NULL ; +} +static void sigd_put_skb (struct sk_buff *skb) {if (!sigd) {
@@ -69,7 +99,14 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb) msg = (struct atmsvc_msg *) skb->data;WARN_ON (refcount_sub_and_test (skb->truesize, &sk_atm (vcc)->sk_wmem_alloc)); - vcc = *(struct atm_vcc **) &msg->vcc; + + vcc =find_get_vcc (*(struct atm_vcc **)&msg->vcc); +if (!vcc) { +pr_debug ("invalid vcc pointer in msg\n" ); +dev_kfree_skb (skb); +return -EINVAL ; + } +pr_debug ("%d (0x%lx)\n" , (int )msg->type, (unsigned long )vcc); sk =sk_atm (vcc);
@@ -100,7 +137,16 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)clear_bit (ATM_VF_WAITING , &vcc->flags);break ;case as_indicate: - vcc = *(struct atm_vcc **)&msg->listen_vcc; +/* Release the reference from msg->vcc, we'll use msg->listen_vcc instead */ +sock_put (sk); + + vcc =find_get_vcc (*(struct atm_vcc **)&msg->listen_vcc); +if (!vcc) { +pr_debug ("invalid listen_vcc pointer in msg\n" ); +dev_kfree_skb (skb); +return -EINVAL ; + } + sk =sk_atm (vcc);pr_debug ("as_indicate!!!\n" );lock_sock (sk);
@@ -115,6 +161,8 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb) sk->sk_state_change (sk); as_indicate_complete:release_sock (sk); +/* Paired with find_get_vcc(msg->listen_vcc) above */ +sock_put (sk);return 0 ;case as_close:set_bit (ATM_VF_RELEASED , &vcc->flags);
@@ -131,11 +179,15 @@ static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)break ;default :pr_alert ("bad message type %d\n" , (int )msg->type); +/* Paired with find_get_vcc(msg->vcc) above */ +sock_put (sk);return -EINVAL ; } sk->sk_state_change (sk); out:dev_kfree_skb (skb); +/* Paired with find_get_vcc(msg->vcc) above */ +sock_put (sk);return 0 ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 327/641 net: sunhme: Fix sbus regression
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: René Rebe <[email protected]>
[ Upstream commit 8c5d17834ec104d0abd1bda52fbc04e647fab274 ]
Commit cc216e4b44ce ("net: sunhme: Switch SBUS to devres") changed explicit sized of_ioremap with BMAC_REG_SIZEs to devm_platform_ioremap_resource mapping all the resource. However, this does not work on my Sun Ultra 2 with SBUS HMEs:
hme f0072f38: error -EBUSY: can't request region for resource [mem 0x1ffe8c07000-0x1ffe8c0701f] hme f0072f38: Cannot map TCVR registers.
hme f0072f38: probe with driver hme failed with error -16
hme f007ab44: error -EBUSY: can't request region for resource [mem 0x1ff28c07000-0x1ff28c0701f] hme f007ab44: Cannot map TCVR registers.
hme f007ab44: probe with driver hme failed with error -16
Turns out the open-firmware resources overlap, at least on this machines and PROM version:
hexdump /proc/device-tree/sbus@1f,0/SUNW,hme@2,8c00000/reg:
00 00 00 02 08 c0 00 00 00 00 01 08 00 00 00 02 08 c0 20 00 00 00 20 00 00 00 00 02 08 c0 40 00 00 00 20 00 00 00 00 02 08 c0 60 00 00 00 20 00 00 00 00 02 08 c0 70 00 00 00 00 20
And the driver previously explicitly mapped way smaller mmio regions:
/proc/iomem:
1ff28c00000-1ff28c00107 : HME Global Regs
1ff28c02000-1ff28c02033 : HME TX Regs
1ff28c04000-1ff28c0401f : HME RX Regs
1ff28c06000-1ff28c0635f : HME BIGMAC Regs
1ff28c07000-1ff28c0701f : HME Tranceiver Regs
Quirk this specific issue by truncating the previous resource to not overlap into the TCVR registers.
--- drivers/net/ethernet/sun/sunhme.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/sun/sunhme.c +3 -0
@@ -2551,6 +2551,9 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe) goto err_out_clear_quattro; } +/* BIGMAC may have bogus sizes */ +if ((op->resource[3 ].end - op->resource[3 ].start) >=BMAC_REG_SIZE ) + op->resource[3 ].end = op->resource[3 ].start +BMAC_REG_SIZE -1 ; hp->bigmacregs =devm_platform_ioremap_resource (op,3 );if (IS_ERR (hp->bigmacregs)) {dev_err (&op->dev,"Cannot map BIGMAC registers.\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 328/641 xfrm: fix ip_rt_bug race in icmp_route_lookup reverse path
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 81b84de32bb27ae1ae2eb9acf0420e9d0d14bf00 ]
icmp_route_lookup() performs multiple route lookups to find a suitable route for sending ICMP error messages, with special handling for XFRM (IPsec) policies.
The lookup sequence is:
1. First, lookup output route for ICMP reply (dst = original src)
2. Pass through xfrm_lookup() for policy check
3. If blocked (-EPERM) or dst is not local, enter "reverse path"
4. In reverse path, call xfrm_decode_session_reverse() to get fl4_dec which reverses the original packet's flow (saddr<->daddr swapped)
5. If fl4_dec.saddr is local (we are the original destination), use __ip_route_output_key() for output route lookup
6. If fl4_dec.saddr is NOT local (we are a forwarding node), use ip_route_input() to simulate the reverse packet's input path
7. Finally, pass rt2 through xfrm_lookup() with XFRM_LOOKUP_ICMP flag
The bug occurs in step 6: ip_route_input() is called with fl4_dec.daddr (original packet's source) as destination. If this address becomes local between the initial check and ip_route_input() call (e.g., due to concurrent "ip addr add"), ip_route_input() returns a LOCAL route with dst.output set to ip_rt_bug.
This route is then used for ICMP output, causing dst_output() to call ip_rt_bug(), triggering a WARN_ON:
------------[ cut here ]------------ WARNING: net/ipv4/route.c:1275 at ip_rt_bug+0x21/0x30, CPU#1 Call Trace: <TASK> ip_push_pending_frames+0x202/0x240 icmp_push_reply+0x30d/0x430 __icmp_send+0x1149/0x24f0 ip_options_compile+0xa2/0xd0 ip_rcv_finish_core+0x829/0x1950 ip_rcv+0x2d7/0x420 __netif_receive_skb_one_core+0x185/0x1f0 netif_receive_skb+0x90/0x450 tun_get_user+0x3413/0x3fb0 tun_chr_write_iter+0xe4/0x220 ...
Fix this by checking rt2->rt_type after ip_route_input(). If it's RTN_LOCAL, the route cannot be used for output, so treat it as an error.
The reproducer requires kernel modification to widen the race window, making it unsuitable as a selftest. It is available at:
https://gist.github.com/mrpre/eae853b72ac6a750f5d45d64ddac1e81
--- net/ipv4/icmp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
net/ipv4/icmp.c +15 -0
@@ -554,6 +554,21 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,/* steal dst entry from skb_in, don't drop refcnt */ skb_dstref_steal (skb_in);skb_dstref_restore (skb_in, orefdst); + +/* +* At this point, fl4_dec.daddr should NOT be local (we +* checked fl4_dec.saddr above). However, a race condition +* may occur if the address is added to the interface +* concurrently. In that case, ip_route_input() returns a +* LOCAL route with dst.output=ip_rt_bug, which must not +* be used for output. +*/ +if (!err && rt2 && rt2->rt_type ==RTN_LOCAL ) { +net_warn_ratelimited ("detected local route for %pI4 during ICMP sending, src %pI4\n" , + &fl4_dec.daddr, &fl4_dec.saddr); +dst_release (&rt2->dst); + err = -EINVAL ; + } }if (err)
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 329/641 serial: caif: fix use-after-free in caif_serial ldisc_close()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 308e7e4d0a846359685f40aade023aee7b27284c ]
There is a use-after-free bug in caif_serial where handle_tx() may access ser->tty after the tty has been freed.
The race condition occurs between ldisc_close() and packet transmission:
CPU 0 (close) CPU 1 (xmit)
------------- ------------
ldisc_close()
tty_kref_put(ser->tty)
[tty may be freed here]
<-- race window -->
caif_xmit()
handle_tx()
tty = ser->tty // dangling ptr
tty->ops->write() // UAF!
schedule_work()
ser_release()
unregister_netdevice()The root cause is that tty_kref_put() is called in ldisc_close() while the network device is still active and can receive packets.
Since ser and tty have a 1:1 binding relationship with consistent lifecycles (ser is allocated in ldisc_open and freed in ser_release via unregister_netdevice, and each ser binds exactly one tty), we can safely defer the tty reference release to ser_release() where the network device is unregistered.
Fix this by moving tty_kref_put() from ldisc_close() to ser_release(), after unregister_netdevice(). This ensures the tty reference is held as long as the network device exists, preventing the UAF.
Note: We save ser->tty before unregister_netdevice() because ser is embedded in netdev's private data and will be freed along with netdev (needs_free_netdev = true).
How to reproduce: Add mdelay(500) at the beginning of ldisc_close() to widen the race window, then run the reproducer program [1].
Note: There is a separate deadloop issue in handle_tx() when using PORT_UNKNOWN serial ports (e.g., /dev/ttyS3 in QEMU without proper serial backend). This deadloop exists even without this patch, and is likely caused by inconsistency between uart_write_room() and uart_write() in serial core. It has been addressed in a separate patch [2].
KASAN report:
==================================================================
BUG: KASAN: slab-use-after-free in handle_tx+0x5d1/0x620 Read of size 1 at addr ffff8881131e1490 by task caif_uaf_trigge/9929 Call Trace: <TASK> dump_stack_lvl+0x10e/0x1f0 print_report+0xd0/0x630 kasan_report+0xe4/0x120 handle_tx+0x5d1/0x620 dev_hard_start_xmit+0x9d/0x6c0 __dev_queue_xmit+0x6e2/0x4410 packet_xmit+0x243/0x360 packet_sendmsg+0x26cf/0x5500 __sys_sendto+0x4a3/0x520 __x64_sys_sendto+0xe0/0x1c0 do_syscall_64+0xc9/0xf80 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f615df2c0d7 Allocated by task 9930: Freed by task 64: Last potentially related work creation: The buggy address belongs to the object at ffff8881131e1000 which belongs to the cache kmalloc-cg-2k of size 2048
The buggy address is located 1168 bytes inside of
freed 2048-byte region [ffff8881131e1000, ffff8881131e1800)
The buggy address belongs to the physical page:
page_owner tracks the page as allocated
page last free pid 9778 tgid 9778 stack trace:
Memory state around the buggy address:
ffff8881131e1380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff8881131e1400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff8881131e1480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff8881131e1500: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff8881131e1580: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================[1]: https://gist.github.com/mrpre/f683f244544f7b11e7fa87df9e6c2eeb
[2]: https://lore.kernel.org/linux-serial/[email protected]/T/#u
--- drivers/net/caif/caif_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/caif/caif_serial.c +3 -2
@@ -284,6 +284,7 @@ static void ser_release(struct work_struct *work) {struct list_head list;struct ser_device *ser, *tmp; +struct tty_struct *tty;spin_lock (&ser_lock);list_replace_init (&ser_release_list, &list);
@@ -292,9 +293,11 @@ static void ser_release(struct work_struct *work)if (!list_empty (&list)) {rtnl_lock ();list_for_each_entry_safe (ser, tmp, &list, node) { + tty = ser->tty;dev_close (ser->dev);unregister_netdevice (ser->dev);debugfs_deinit (ser); +tty_kref_put (tty); }rtnl_unlock (); }
@@ -355,8 +358,6 @@ static void ldisc_close(struct tty_struct *tty) {struct ser_device *ser = tty->disc_data; -tty_kref_put (ser->tty); -spin_lock (&ser_lock);list_move (&ser->node, &ser_release_list);spin_unlock (&ser_lock);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 330/641 octeon_ep: disable per ring interrupts
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vimlesh Kumar <[email protected]>
[ Upstream commit 73e6ffa37cebee152c07c5f2b8bc70fd2899ea6e ]
Disable the MSI-X per ring interrupt for every PF ring when PF netdev goes down.
--- .../ethernet/marvell/octeon_ep/octep_cn9k_pf.c | 18 +++++++++++++++--- .../ethernet/marvell/octeon_ep/octep_cnxk_pf.c | 18 +++++++++++++++--- .../marvell/octeon_ep/octep_regs_cn9k_pf.h | 1 + .../marvell/octeon_ep/octep_regs_cnxk_pf.h | 1 + 4 files changed, 32 insertions(+), 6 deletions(-)
drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c +15 -3
@@ -696,14 +696,26 @@ static void octep_enable_interrupts_cn93_pf(struct octep_device *oct)/* Disable all interrupts */ static void octep_disable_interrupts_cn93_pf (struct octep_device *oct) { -u64 intr_mask =0ULL ; +u64 reg_val, intr_mask =0ULL ;int srn, num_rings, i; srn =CFG_GET_PORTS_PF_SRN (oct->conf); num_rings =CFG_GET_PORTS_ACTIVE_IO_RINGS (oct->conf); -for (i =0 ; i < num_rings; i++) - intr_mask |= (0x1ULL << (srn + i)); +for (i =0 ; i < num_rings; i++) { + intr_mask |=BIT_ULL (srn + i); + reg_val =octep_read_csr64 (oct, +CN93_SDP_R_IN_INT_LEVELS (srn + i)); + reg_val &= ~CN93_INT_ENA_BIT ; +octep_write_csr64 (oct, +CN93_SDP_R_IN_INT_LEVELS (srn + i), reg_val); + + reg_val =octep_read_csr64 (oct, +CN93_SDP_R_OUT_INT_LEVELS (srn + i)); + reg_val &= ~CN93_INT_ENA_BIT ; +octep_write_csr64 (oct, +CN93_SDP_R_OUT_INT_LEVELS (srn + i), reg_val); + }octep_write_csr64 (oct,CN93_SDP_EPF_IRERR_RINT_ENA_W1C , intr_mask);octep_write_csr64 (oct,CN93_SDP_EPF_ORERR_RINT_ENA_W1C , intr_mask);
drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c +15 -3
@@ -720,14 +720,26 @@ static void octep_enable_interrupts_cnxk_pf(struct octep_device *oct)/* Disable all interrupts */ static void octep_disable_interrupts_cnxk_pf (struct octep_device *oct) { -u64 intr_mask =0ULL ; +u64 reg_val, intr_mask =0ULL ;int srn, num_rings, i; srn =CFG_GET_PORTS_PF_SRN (oct->conf); num_rings =CFG_GET_PORTS_ACTIVE_IO_RINGS (oct->conf); -for (i =0 ; i < num_rings; i++) - intr_mask |= (0x1ULL << (srn + i)); +for (i =0 ; i < num_rings; i++) { + intr_mask |=BIT_ULL (srn + i); + reg_val =octep_read_csr64 (oct, +CNXK_SDP_R_IN_INT_LEVELS (srn + i)); + reg_val &= ~CNXK_INT_ENA_BIT ; +octep_write_csr64 (oct, +CNXK_SDP_R_IN_INT_LEVELS (srn + i), reg_val); + + reg_val =octep_read_csr64 (oct, +CNXK_SDP_R_OUT_INT_LEVELS (srn + i)); + reg_val &= ~CNXK_INT_ENA_BIT ; +octep_write_csr64 (oct, +CNXK_SDP_R_OUT_INT_LEVELS (srn + i), reg_val); + }octep_write_csr64 (oct,CNXK_SDP_EPF_IRERR_RINT_ENA_W1C , intr_mask);octep_write_csr64 (oct,CNXK_SDP_EPF_ORERR_RINT_ENA_W1C , intr_mask);
drivers/net/ethernet/marvell/octeon_ep/octep_regs_cn9k_pf.h +1 -0
@@ -386,5 +386,6 @@#define CN93_PEM_BAR4_INDEX 7#define CN93_PEM_BAR4_INDEX_SIZE 0x400000ULL#define CN93_PEM_BAR4_INDEX_OFFSET (CN93_PEM_BAR4_INDEX * CN93_PEM_BAR4_INDEX_SIZE) +#define CN93_INT_ENA_BIT BIT_ULL(62)#endif /* _OCTEP_REGS_CN9K_PF_H_ */
drivers/net/ethernet/marvell/octeon_ep/octep_regs_cnxk_pf.h +1 -0
@@ -412,5 +412,6 @@#define CNXK_PEM_BAR4_INDEX 7#define CNXK_PEM_BAR4_INDEX_SIZE 0x400000ULL#define CNXK_PEM_BAR4_INDEX_OFFSET (CNXK_PEM_BAR4_INDEX * CNXK_PEM_BAR4_INDEX_SIZE) +#define CNXK_INT_ENA_BIT BIT_ULL(62)#endif /* _OCTEP_REGS_CNXK_PF_H_ */
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 331/641 octeon_ep: ensure dbell BADDR updation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vimlesh Kumar <[email protected]>
[ Upstream commit ce8fe3fc4f99efd872120301c0f72f2e90ab9769 ]
Make sure the OUT DBELL base address reflects the
latest values written to it.
Fix:
Add a wait until the OUT DBELL base address register is updated with the DMA ring descriptor address, and modify the setup_oq function to properly handle failures.
--- .../marvell/octeon_ep/octep_cn9k_pf.c | 3 +- .../marvell/octeon_ep/octep_cnxk_pf.c | 46 +++++++++++++++---- .../ethernet/marvell/octeon_ep/octep_main.h | 2 +- .../net/ethernet/marvell/octeon_ep/octep_rx.c | 8 +++- 4 files changed, 48 insertions(+), 11 deletions(-)
drivers/net/ethernet/marvell/octeon_ep/octep_cn9k_pf.c +2 -1
@@ -307,7 +307,7 @@ static void octep_setup_iq_regs_cn93_pf(struct octep_device *oct, int iq_no) }/* Setup registers for a hardware Rx Queue */ -static void octep_setup_oq_regs_cn93_pf (struct octep_device *oct,int oq_no) +static int octep_setup_oq_regs_cn93_pf (struct octep_device *oct,int oq_no) {u64 reg_val;u64 oq_ctl =0ULL ;
@@ -355,6 +355,7 @@ static void octep_setup_oq_regs_cn93_pf(struct octep_device *oct, int oq_no) reg_val = ((u64 )time_threshold <<32 ) |CFG_GET_OQ_INTR_PKT (oct->conf);octep_write_csr64 (oct,CN93_SDP_R_OUT_INT_LEVELS (oq_no), reg_val); +return 0 ; }/* Setup registers for a PF mailbox */
drivers/net/ethernet/marvell/octeon_ep/octep_cnxk_pf.c +38 -8
78 lines not on this page · raw message
drivers/net/ethernet/marvell/octeon_ep/octep_main.h +1 -1
@@ -77,7 +77,7 @@ struct octep_pci_win_regs {struct octep_hw_ops {void (*setup_iq_regs)(struct octep_device *oct,int q); -void (*setup_oq_regs)(struct octep_device *oct,int q); +int (*setup_oq_regs)(struct octep_device *oct,int q);void (*setup_mbox_regs)(struct octep_device *oct,int mbox);irqreturn_t (*mbox_intr_handler)(void *ioq_vector);
drivers/net/ethernet/marvell/octeon_ep/octep_rx.c +7 -1
@@ -12,6 +12,8 @@#include "octep_config.h" #include "octep_main.h" +static void octep_oq_free_ring_buffers (struct octep_oq *oq); +static void octep_oq_reset_indices (struct octep_oq *oq) { oq->host_read_idx =0 ;
@@ -170,11 +172,15 @@ static int octep_setup_oq(struct octep_device *oct, int q_no) goto oq_fill_buff_err;octep_oq_reset_indices (oq); - oct->hw_ops.setup_oq_regs (oct, q_no); +if (oct->hw_ops.setup_oq_regs (oct, q_no)) + goto oq_setup_err; + oct->num_oqs++;return 0 ; +oq_setup_err: +octep_oq_free_ring_buffers (oq); oq_fill_buff_err:vfree (oq->buff_info); oq->buff_info =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 332/641 octeon_ep_vf: ensure dbell BADDR updation
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vimlesh Kumar <[email protected]>
[ Upstream commit 484e834d53cffa91c311631271f83130cf6e9e7c ]
Make sure the OUT DBELL base address reflects the
latest values written to it.
Fix:
Add a wait until the OUT DBELL base address register is updated with the DMA ring descriptor address, and modify the setup_oq function to properly handle failures.
--- .../marvell/octeon_ep_vf/octep_vf_cn9k.c | 3 +- .../marvell/octeon_ep_vf/octep_vf_cnxk.c | 39 +++++++++++++++++-- .../marvell/octeon_ep_vf/octep_vf_main.h | 2 +- .../marvell/octeon_ep_vf/octep_vf_rx.c | 8 +++- 4 files changed, 46 insertions(+), 6 deletions(-)
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cn9k.c +2 -1
@@ -196,7 +196,7 @@ static void octep_vf_setup_iq_regs_cn93(struct octep_vf_device *oct, int iq_no) }/* Setup registers for a hardware Rx Queue */ -static void octep_vf_setup_oq_regs_cn93 (struct octep_vf_device *oct,int oq_no) +static int octep_vf_setup_oq_regs_cn93 (struct octep_vf_device *oct,int oq_no) {struct octep_vf_oq *oq = oct->oq[oq_no];u32 time_threshold =0 ;
@@ -239,6 +239,7 @@ static void octep_vf_setup_oq_regs_cn93(struct octep_vf_device *oct, int oq_no) time_threshold =CFG_GET_OQ_INTR_TIME (oct->conf); reg_val = ((u64 )time_threshold <<32 ) |CFG_GET_OQ_INTR_PKT (oct->conf);octep_vf_write_csr64 (oct,CN93_VF_SDP_R_OUT_INT_LEVELS (oq_no), reg_val); +return 0 ; }/* Setup registers for a VF mailbox */
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_cnxk.c +36 -3
@@ -199,11 +199,13 @@ static void octep_vf_setup_iq_regs_cnxk(struct octep_vf_device *oct, int iq_no) }/* Setup registers for a hardware Rx Queue */ -static void octep_vf_setup_oq_regs_cnxk (struct octep_vf_device *oct,int oq_no) +static int octep_vf_setup_oq_regs_cnxk (struct octep_vf_device *oct,int oq_no) {struct octep_vf_oq *oq = oct->oq[oq_no]; +unsigned long t_out_jiffies;u32 time_threshold =0 ;u64 oq_ctl =ULL (0 ); +u64 reg_ba_val;u64 reg_val; reg_val =octep_vf_read_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no));
@@ -214,6 +216,38 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val =octep_vf_read_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no)); }while (!(reg_val &CNXK_VF_R_OUT_CTL_IDLE )); } +octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_WMARK (oq_no), + oq->max_count); +/* Wait for WMARK to get applied */ +usleep_range (10 ,15 ); + +octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_BADDR (oq_no), + oq->desc_ring_dma); +octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_RSIZE (oq_no), + oq->max_count); + reg_ba_val =octep_vf_read_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_BADDR (oq_no)); +if (reg_ba_val != oq->desc_ring_dma) { + t_out_jiffies = jiffies +10 *HZ ; +do { +if (reg_ba_val ==ULLONG_MAX ) +return -EFAULT ; +octep_vf_write_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_BADDR + (oq_no), oq->desc_ring_dma); +octep_vf_write_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_RSIZE + (oq_no), oq->max_count); + reg_ba_val = +octep_vf_read_csr64 (oct, +CNXK_VF_SDP_R_OUT_SLIST_BADDR + (oq_no)); + }while ((reg_ba_val != oq->desc_ring_dma) && +time_before (jiffies, t_out_jiffies)); + +if (reg_ba_val != oq->desc_ring_dma) +return -EAGAIN ; + } reg_val &= ~(CNXK_VF_R_OUT_CTL_IMODE ); reg_val &= ~(CNXK_VF_R_OUT_CTL_ROR_P );
@@ -227,8 +261,6 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val |= (CNXK_VF_R_OUT_CTL_ES_P );octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no), reg_val); -octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_BADDR (oq_no), oq->desc_ring_dma); -octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_SLIST_RSIZE (oq_no), oq->max_count); oq_ctl =octep_vf_read_csr64 (oct,CNXK_VF_SDP_R_OUT_CONTROL (oq_no));/* Clear the ISIZE and BSIZE (22-0) */
@@ -250,6 +282,7 @@ static void octep_vf_setup_oq_regs_cnxk(struct octep_vf_device *oct, int oq_no) reg_val &= ~GENMASK_ULL (31 ,0 ); reg_val |=CFG_GET_OQ_WMARK (oct->conf);octep_vf_write_csr64 (oct,CNXK_VF_SDP_R_OUT_WMARK (oq_no), reg_val); +return 0 ; }/* Setup registers for a VF mailbox */
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_main.h +1 -1
@@ -55,7 +55,7 @@ struct octep_vf_mmio {struct octep_vf_hw_ops {void (*setup_iq_regs)(struct octep_vf_device *oct,int q); -void (*setup_oq_regs)(struct octep_vf_device *oct,int q); +int (*setup_oq_regs)(struct octep_vf_device *oct,int q);void (*setup_mbox_regs)(struct octep_vf_device *oct,int mbox);irqreturn_t (*non_ioq_intr_handler)(void *ioq_vector);
drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c +7 -1
@@ -12,6 +12,8 @@#include "octep_vf_config.h" #include "octep_vf_main.h" +static void octep_vf_oq_free_ring_buffers (struct octep_vf_oq *oq); +static void octep_vf_oq_reset_indices (struct octep_vf_oq *oq) { oq->host_read_idx =0 ;
@@ -171,11 +173,15 @@ static int octep_vf_setup_oq(struct octep_vf_device *oct, int q_no) goto oq_fill_buff_err;octep_vf_oq_reset_indices (oq); - oct->hw_ops.setup_oq_regs (oct, q_no); +if (oct->hw_ops.setup_oq_regs (oct, q_no)) + goto oq_setup_err; + oct->num_oqs++;return 0 ; +oq_setup_err: +octep_vf_oq_free_ring_buffers (oq); oq_fill_buff_err:vfree (oq->buff_info); oq->buff_info =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 333/641 ionic: Rate limit unknown xcvr type messages
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Joyner <[email protected]>
[ Upstream commit cdb1634de3bf197c0d86487d1fb84c128a79cc7c ]
Running ethtool repeatedly with a transceiver unknown to the driver or firmware will cause the driver to spam the kernel logs with "unknown xcvr type" messages which can distract from real issues; and this isn't interesting information outside of debugging. Fix this by rate limiting the output so that there are still notifications but not so many that they flood the log.
Using dev_dbg_once() would reduce the number of messages further, but this would miss the case where a different unknown transceiver type is plugged in, and its status is requested.
--- drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +4 -3
@@ -263,9 +263,10 @@ static int ionic_get_link_ksettings(struct net_device *netdev,/* This means there's no module plugged in */ break ;default : -dev_info (lif->ionic->dev,"unknown xcvr type pid=%d / 0x%x\n" , - idev->port_info->status.xcvr.pid, - idev->port_info->status.xcvr.pid); +dev_dbg_ratelimited (lif->ionic->dev, +"unknown xcvr type pid=%d / 0x%x\n" , + idev->port_info->status.xcvr.pid, + idev->port_info->status.xcvr.pid);break ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 334/641 net: renesas: rswitch: fix forwarding offload statemachine
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Dege <[email protected]>
[ Upstream commit e9a5073a98d940837cbb95e71eed1f28f48e7b30 ]
A change of the port state of one port, caused the state of another port to change. This behvior was unintended.
--- drivers/net/ethernet/renesas/rswitch_l2.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-)
drivers/net/ethernet/renesas/rswitch_l2.c +7 -8
@@ -1,7 +1,7 @@// SPDX-License-Identifier: GPL-2.0 /* Renesas Ethernet Switch device driver * -* Copyright (C) 2025 Renesas Electronics Corporation +* Copyright (C) 2025 - 2026 Renesas Electronics Corporation */ #include <linux/err.h>
@@ -60,6 +60,7 @@ static void rswitch_update_l2_hw_learning(struct rswitch_private *priv)static void rswitch_update_l2_hw_forwarding (struct rswitch_private *priv) {struct rswitch_device *rdev; +bool new_forwarding_offload;unsigned int fwd_mask;/* calculate fwd_mask with zeroes in bits corresponding to ports that
@@ -73,8 +74,9 @@ static void rswitch_update_l2_hw_forwarding(struct rswitch_private *priv) } rswitch_for_all_ports(priv, rdev) { -if ((rdev_for_l2_offload (rdev) && rdev->forwarding_requested) || - rdev->forwarding_offloaded) { + new_forwarding_offload = (rdev_for_l2_offload (rdev) && rdev->forwarding_requested); + +if (new_forwarding_offload || rdev->forwarding_offloaded) {/* Update allowed offload destinations even for ports * with L2 offload enabled earlier. *
@@ -84,13 +86,10 @@ static void rswitch_update_l2_hw_forwarding(struct rswitch_private *priv) priv->addr + FWPC2(rdev->port)); } -if (rdev_for_l2_offload (rdev) && - rdev->forwarding_requested && - !rdev->forwarding_offloaded) { +if (new_forwarding_offload && !rdev->forwarding_offloaded)rswitch_change_l2_hw_offloading (rdev, true, false); - }else if (rdev->forwarding_offloaded) { +else if (!new_forwarding_offload && rdev->forwarding_offloaded)rswitch_change_l2_hw_offloading (rdev, false, false); - } } }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 335/641 octeontx2-pf: Unregister devlink on probe failure
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hariprasad Kelam <[email protected]>
[ Upstream commit 943f3b8bfbf297cf74392b50a7108ce1fe4cbd8c ]
When probe fails after devlink registration, the missing devlink unregister call causing a memory leak.
--- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +1 -0
@@ -3321,6 +3321,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) err_sriov_cleannup:otx2_sriov_vfcfg_cleanup (pf); err_pf_sriov_init: +otx2_unregister_dl (pf);otx2_shutdown_tc (pf); err_mcam_flow_del:otx2_mcam_flow_del (pf);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 336/641 af_unix: Fix memleak of newsk in unix_stream_connect().
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 6884028cd7f275f8bcb854a347265cb1fb0e4bea ]
When prepare_peercred() fails in unix_stream_connect(), unix_release_sock() is not called for newsk, and the memory is leaked.
Let's move prepare_peercred() before unix_create1().
--- net/unix/af_unix.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-)
net/unix/af_unix.c +3 -8
@@ -1671,10 +1671,9 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, timeo =sock_sndtimeo (sk, flags &O_NONBLOCK ); -/* First of all allocate resources. -* If we will make it after state is locked, -* we will have to recheck all again in any case. -*/ + err =prepare_peercred (&peercred); +if (err) + goto out;/* create new sock for complete connection */ newsk =unix_create1 (net,NULL ,0 , sock->type);
@@ -1683,10 +1682,6 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, goto out; } - err =prepare_peercred (&peercred); -if (err) - goto out; -/* Allocate skb for sending to listening sock */ skb =sock_wmalloc (newsk,1 ,0 ,GFP_KERNEL );if (!skb) {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 337/641 RDMA/rtrs: server: remove dead code
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Honggang LI <[email protected]>
[ Upstream commit a3572bdc3a028ca47f77d7166ac95b719cf77d50 ]
As rkey had been initialized to zero, the WARN_ON_ONCE should never been triggered. Remove it.
--- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
drivers/infiniband/ulp/rtrs/rtrs-srv.c +1 -7
@@ -208,7 +208,6 @@ static int rdma_write_sg(struct rtrs_srv_op *id)size_t sg_cnt;int err, offset;bool need_inval; -u32 rkey =0 ;struct ib_reg_wr rwr;struct ib_sge *plist;struct ib_sge list;
@@ -240,11 +239,6 @@ static int rdma_write_sg(struct rtrs_srv_op *id) wr->wr.num_sge =1 ; wr->remote_addr =le64_to_cpu (id->rd_msg->desc[0 ].addr); wr->rkey =le32_to_cpu (id->rd_msg->desc[0 ].key); -if (rkey ==0 ) - rkey = wr->rkey; -else -/* Only one key is actually used */ -WARN_ON_ONCE (rkey != wr->rkey); wr->wr.opcode =IB_WR_RDMA_WRITE ; wr->wr.wr_cqe = &io_comp_cqe;
@@ -277,7 +271,7 @@ static int rdma_write_sg(struct rtrs_srv_op *id) inv_wr.opcode =IB_WR_SEND_WITH_INV ; inv_wr.wr_cqe = &io_comp_cqe; inv_wr.send_flags =0 ; - inv_wr.ex.invalidate_rkey = rkey; + inv_wr.ex.invalidate_rkey = wr->rkey; } imm_wr.wr.next =NULL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 338/641 IB/cache: update gid cache on client reregister event
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Etienne AUJAMES <[email protected]>
[ Upstream commit ddd6c8c873e912cb1ead79def54de5e24ff71c80 ]
Some HCAs (e.g: ConnectX4) do not trigger a IB_EVENT_GID_CHANGE on subnet prefix update from SM (PortInfo).
Since the commit d58c23c92548 ("IB/core: Only update PKEY and GID caches on respective events"), the GID cache is updated exclusively on IB_EVENT_GID_CHANGE. If this event is not emitted, the subnet prefix in the IPoIB interface’s hardware address remains set to its default value (0xfe80000000000000).
Then rdma_bind_addr() failed because it relies on hardware address to find the port GID (subnet_prefix + port GUID).
This patch fixes this issue by updating the GID cache on IB_EVENT_CLIENT_REREGISTER event (emitted on PortInfo::ClientReregister=1).
--- drivers/infiniband/core/cache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/infiniband/core/cache.c +2 -1
@@ -1537,7 +1537,8 @@ static void ib_cache_event_task(struct work_struct *_work)* the cache. */ ret =ib_cache_update (work->event.device, work->event.element.port_num, - work->event.event ==IB_EVENT_GID_CHANGE , + work->event.event ==IB_EVENT_GID_CHANGE || + work->event.event ==IB_EVENT_CLIENT_REREGISTER , work->event.event ==IB_EVENT_PKEY_CHANGE , work->enforce_security);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 339/641 RDMA/hns: Fix WQ_MEM_RECLAIM warning
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengchang Tang <[email protected]>
[ Upstream commit c0a26bbd3f99b7b03f072e3409aff4e6ec8af6f6 ]
When sunrpc is used, if a reset triggered, our wq may lead the following trace:
workqueue: WQ_MEM_RECLAIM xprtiod:xprt_rdma_connect_worker [rpcrdma] is flushing !WQ_MEM_RECLAIM hns_roce_irq_workq:flush_work_handle [hns_roce_hw_v2]
WARNING: CPU: 0 PID: 8250 at kernel/workqueue.c:2644 check_flush_dependency+0xe0/0x144 Call trace: check_flush_dependency+0xe0/0x144 start_flush_work.constprop.0+0x1d0/0x2f0 __flush_work.isra.0+0x40/0xb0 flush_work+0x14/0x30 hns_roce_v2_destroy_qp+0xac/0x1e0 [hns_roce_hw_v2] ib_destroy_qp_user+0x9c/0x2b4 rdma_destroy_qp+0x34/0xb0 rpcrdma_ep_destroy+0x28/0xcc [rpcrdma] rpcrdma_ep_put+0x74/0xb4 [rpcrdma] rpcrdma_xprt_disconnect+0x1d8/0x260 [rpcrdma] xprt_rdma_connect_worker+0xc0/0x120 [rpcrdma] process_one_work+0x1cc/0x4d0 worker_thread+0x154/0x414 kthread+0x104/0x144 ret_from_fork+0x10/0x18
Since QP destruction frees memory, this wq should have the WQ_MEM_RECLAIM.
--- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/infiniband/hw/hns/hns_roce_hw_v2.c +2 -1
@@ -6878,7 +6878,8 @@ static int hns_roce_v2_init_eq_table(struct hns_roce_dev *hr_dev)INIT_WORK (&hr_dev->ecc_work, fmea_ram_ecc_work); - hr_dev->irq_workq =alloc_ordered_workqueue ("hns_roce_irq_workq" ,0 ); + hr_dev->irq_workq =alloc_ordered_workqueue ("hns_roce_irq_workq" , +WQ_MEM_RECLAIM );if (!hr_dev->irq_workq) {dev_err (dev,"failed to create irq workqueue.\n" ); ret = -ENOMEM ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 340/641 RDMA/hns: Fix RoCEv1 failure due to DSCP
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junxian Huang <[email protected]>
[ Upstream commit 84bd5d60f0a2b9c763c5e6d0b3d8f4f61f6c5470 ]
DSCP is not supported in RoCEv1, but get_dscp() is still called. If get_dscp() returns an error, it'll eventually cause create_ah to fail even when using RoCEv1.
Correct the return value and avoid calling get_dscp() when using RoCEv1.
--- drivers/infiniband/hw/hns/hns_roce_ah.c | 23 +++++++++--------- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 28 ++++++++++++---------- 2 files changed, 26 insertions(+), 25 deletions(-)
drivers/infiniband/hw/hns/hns_roce_ah.c +11 -12
@@ -61,7 +61,7 @@ int hns_roce_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,u8 tclass =get_tclass (grh);u8 priority =0 ;u8 tc_mode =0 ; -int ret; +int ret =0 ;if (hr_dev->pci_dev->revision ==PCI_REVISION_ID_HIP08 && udata) { ret = -EOPNOTSUPP ;
@@ -78,19 +78,18 @@ int hns_roce_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr, ah->av.flowlabel = grh->flow_label; ah->av.udp_sport =get_ah_udp_sport (ah_attr); ah->av.tclass = tclass; + ah->av.sl =rdma_ah_get_sl (ah_attr); - ret = hr_dev->hw->get_dscp (hr_dev, tclass, &tc_mode, &priority); -if (ret == -EOPNOTSUPP ) - ret =0 ; - -if (ret && grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) - goto err_out; +if (grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) { + ret = hr_dev->hw->get_dscp (hr_dev, tclass, &tc_mode, &priority); +if (ret == -EOPNOTSUPP ) + ret =0 ; +else if (ret) + goto err_out; -if (tc_mode ==HNAE3_TC_MAP_MODE_DSCP && - grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) - ah->av.sl = priority; -else - ah->av.sl =rdma_ah_get_sl (ah_attr); +if (tc_mode ==HNAE3_TC_MAP_MODE_DSCP ) + ah->av.sl = priority; + }if (!check_sl_valid (hr_dev, ah->av.sl)) { ret = -EINVAL ;
drivers/infiniband/hw/hns/hns_roce_hw_v2.c +15 -13
@@ -4975,20 +4975,22 @@ static int hns_roce_set_sl(struct ib_qp *ibqp,struct ib_device *ibdev = &hr_dev->ib_dev;int ret; - ret =hns_roce_hw_v2_get_dscp (hr_dev,get_tclass (&attr->ah_attr.grh), - &hr_qp->tc_mode, &hr_qp->priority); -if (ret && ret != -EOPNOTSUPP && - grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) { -ibdev_err_ratelimited (ibdev, -"failed to get dscp, ret = %d.\n" , ret); -return ret; - } + hr_qp->sl =rdma_ah_get_sl (&attr->ah_attr); -if (hr_qp->tc_mode ==HNAE3_TC_MAP_MODE_DSCP && - grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) - hr_qp->sl = hr_qp->priority; -else - hr_qp->sl =rdma_ah_get_sl (&attr->ah_attr); +if (grh->sgid_attr->gid_type ==IB_GID_TYPE_ROCE_UDP_ENCAP ) { + ret =hns_roce_hw_v2_get_dscp (hr_dev, +get_tclass (&attr->ah_attr.grh), + &hr_qp->tc_mode, &hr_qp->priority); +if (ret && ret != -EOPNOTSUPP ) { +ibdev_err_ratelimited (ibdev, +"failed to get dscp, ret = %d.\n" , + ret); +return ret; + } + +if (hr_qp->tc_mode ==HNAE3_TC_MAP_MODE_DSCP ) + hr_qp->sl = hr_qp->priority; + }if (!check_sl_valid (hr_dev, hr_qp->sl))return -EINVAL ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 341/641 RDMA/hns: Notify ULP of remaining soft-WCs during reset
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengchang Tang <[email protected]>
[ Upstream commit 0789f929900d85b80b343c5f04f8b9444e991384 ]
During a reset, software-generated WCs cannot be reported via interrupts. This may cause the ULP to miss some WCs.
To avoid this, add check in the CQ arm process: if a hardware reset has occurred and there are still unreported soft-WCs, notify the ULP to handle the remaining WCs, thereby preventing any loss of completions.
--- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+)
drivers/infiniband/hw/hns/hns_roce_hw_v2.c +23 -0
@@ -3661,6 +3661,23 @@ static void hns_roce_v2_write_cqc(struct hns_roce_dev *hr_dev, HNS_ROCE_V2_CQ_DEFAULT_INTERVAL); } +static bool left_sw_wc (struct hns_roce_dev *hr_dev,struct hns_roce_cq *hr_cq) +{ +struct hns_roce_qp *hr_qp; + +list_for_each_entry (hr_qp, &hr_cq->sq_list, sq_node) { +if (hr_qp->sq.head != hr_qp->sq.tail) +return true; + } + +list_for_each_entry (hr_qp, &hr_cq->rq_list, rq_node) { +if (hr_qp->rq.head != hr_qp->rq.tail) +return true; + } + +return false; +} +static int hns_roce_v2_req_notify_cq (struct ib_cq *ibcq,enum ib_cq_notify_flags flags) {
@@ -3669,6 +3686,12 @@ static int hns_roce_v2_req_notify_cq(struct ib_cq *ibcq,struct hns_roce_v2_db cq_db = {};u32 notify_flag; +if (hr_dev->state >=HNS_ROCE_DEVICE_STATE_RST_DOWN ) { +if ((flags &IB_CQ_REPORT_MISSED_EVENTS ) && +left_sw_wc (hr_dev, hr_cq)) +return 1 ; +return 0 ; + }/* * flags = 0, then notify_flag : next * flags = 1, then notify flag : solocited
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 342/641 RDMA/mlx5: Fix ucaps init error flow
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maher Sanalla <[email protected]>
[ Upstream commit 6dc78c53de99e4ed9868d4f0fc6da6e46f52fe4d ]
In mlx5_ib_stage_caps_init(), if mlx5_ib_init_ucaps() fails after mlx5_ib_init_var_table() succeeds, the VAR bitmap is leaked since the function returns without cleanup.
Thus, cleanup the var table bitmap in case of error of initializing ucaps before exiting, preventing the leak above.
--- drivers/infiniband/hw/mlx5/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/infiniband/hw/mlx5/main.c +5 -1
@@ -4462,12 +4462,16 @@ static int mlx5_ib_stage_caps_init(struct mlx5_ib_dev *dev) MLX5_HCA_CAP_2_GENERAL_OBJECT_TYPES_RDMA_CTRL) { err =mlx5_ib_init_ucaps (dev);if (err) -return err; + goto err_ucaps; } dev->ib_dev.use_cq_dim = true;return 0 ; + +err_ucaps: +bitmap_free (dev->var_table.bitmap); +return err; } static const struct ib_device_ops mlx5_ib_dev_port_ops = {
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 343/641 cxl/mem: Fix devm_cxl_memdev_edac_release() confusion
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Williams <[email protected]>
[ Upstream commit 10016118b6fade907143a32a7aeaa777063dc79c ]
A device release method is only for undoing allocations on the path to preparing the device for device_add(). In contrast, devm allocations are post device_add(), are acquired during / after ->probe() and are released synchronous with ->remove().
So, a "devm" helper in a "release" method is a clear anti-pattern.
Move this devm release action where it belongs, an action created at edac object creation time. Otherwise, this leaks resources until cxl_memdev_release() time which may be long after these xarray and error record caches have gone idle.
Note, this also fixes up the type of @cxlmd->err_rec_array which needlessly dropped type-safety.
--- drivers/cxl/core/edac.c | 64 ++++++++++++++++++++++----------------- drivers/cxl/core/memdev.c | 1 - drivers/cxl/cxlmem.h | 5 +-- 3 files changed, 38 insertions(+), 32 deletions(-)
drivers/cxl/core/edac.c +37 -27
@@ -1988,6 +1988,40 @@ static int cxl_memdev_soft_ppr_init(struct cxl_memdev *cxlmd,return 0 ; } +static void err_rec_free (void *_cxlmd) +{ +struct cxl_memdev *cxlmd = _cxlmd; +struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; +struct cxl_event_gen_media *rec_gen_media; +struct cxl_event_dram *rec_dram; +unsigned long index; + + cxlmd->err_rec_array =NULL ; +xa_for_each (&array_rec->rec_dram, index, rec_dram) +kfree (rec_dram); +xa_destroy (&array_rec->rec_dram); + +xa_for_each (&array_rec->rec_gen_media, index, rec_gen_media) +kfree (rec_gen_media); +xa_destroy (&array_rec->rec_gen_media); +kfree (array_rec); +} + +static int devm_cxl_memdev_setup_err_rec (struct cxl_memdev *cxlmd) +{ +struct cxl_mem_err_rec *array_rec = +kzalloc (sizeof (*array_rec),GFP_KERNEL ); + +if (!array_rec) +return -ENOMEM ; + +xa_init (&array_rec->rec_gen_media); +xa_init (&array_rec->rec_dram); + cxlmd->err_rec_array = array_rec; + +return devm_add_action_or_reset (&cxlmd->dev, err_rec_free, cxlmd); +} +int devm_cxl_memdev_edac_register (struct cxl_memdev *cxlmd) {struct edac_dev_feature ras_features[CXL_NR_EDAC_DEV_FEATURES ];
@@ -2038,15 +2072,9 @@ int devm_cxl_memdev_edac_register(struct cxl_memdev *cxlmd) }if (repair_inst) { -struct cxl_mem_err_rec *array_rec = -devm_kzalloc (&cxlmd->dev,sizeof (*array_rec), -GFP_KERNEL ); -if (!array_rec) -return -ENOMEM ; - -xa_init (&array_rec->rec_gen_media); -xa_init (&array_rec->rec_dram); - cxlmd->err_rec_array = array_rec; + rc =devm_cxl_memdev_setup_err_rec (cxlmd); +if (rc) +return rc; } }
@@ -2088,22 +2116,4 @@ int devm_cxl_region_edac_register(struct cxl_region *cxlr) }EXPORT_SYMBOL_NS_GPL (devm_cxl_region_edac_register,"CXL" ); -void devm_cxl_memdev_edac_release (struct cxl_memdev *cxlmd) -{ -struct cxl_mem_err_rec *array_rec = cxlmd->err_rec_array; -struct cxl_event_gen_media *rec_gen_media; -struct cxl_event_dram *rec_dram; -unsigned long index; - -if (!IS_ENABLED (CONFIG_CXL_EDAC_MEM_REPAIR ) || !array_rec) -return ; - -xa_for_each (&array_rec->rec_dram, index, rec_dram) -kfree (rec_dram); -xa_destroy (&array_rec->rec_dram); -xa_for_each (&array_rec->rec_gen_media, index, rec_gen_media) -kfree (rec_gen_media); -xa_destroy (&array_rec->rec_gen_media); -} -EXPORT_SYMBOL_NS_GPL (devm_cxl_memdev_edac_release,"CXL" );
drivers/cxl/core/memdev.c +0 -1
@@ -27,7 +27,6 @@ static void cxl_memdev_release(struct device *dev)struct cxl_memdev *cxlmd =to_cxl_memdev (dev);ida_free (&cxl_memdev_ida, cxlmd->id); -devm_cxl_memdev_edac_release (cxlmd);kfree (cxlmd); }
drivers/cxl/cxlmem.h +1 -4
@@ -63,7 +63,7 @@ struct cxl_memdev {int depth;u8 scrub_cycle;int scrub_region_id; -void *err_rec_array; +struct cxl_mem_err_rec *err_rec_array; };static inline struct cxl_memdev *to_cxl_memdev (struct device *dev)
@@ -877,7 +877,6 @@ int devm_cxl_memdev_edac_register(struct cxl_memdev *cxlmd);int devm_cxl_region_edac_register (struct cxl_region *cxlr);int cxl_store_rec_gen_media (struct cxl_memdev *cxlmd,union cxl_event *evt);int cxl_store_rec_dram (struct cxl_memdev *cxlmd,union cxl_event *evt); -void devm_cxl_memdev_edac_release (struct cxl_memdev *cxlmd);#else static inline int devm_cxl_memdev_edac_register (struct cxl_memdev *cxlmd) {return 0 ; }
@@ -889,8 +888,6 @@ static inline int cxl_store_rec_gen_media(struct cxl_memdev *cxlmd,static inline int cxl_store_rec_dram (struct cxl_memdev *cxlmd,union cxl_event *evt) {return 0 ; } -static inline void devm_cxl_memdev_edac_release (struct cxl_memdev *cxlmd) -{return ; }#endif #ifdef CONFIG_CXL_SUSPEND
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 344/641 power: supply: ab8500: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit c4af8a98bb52825a5331ae1d0604c0ea6956ba4b ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Commit 1c1f13a006ed ("power: supply: ab8500: Move to componentized binding") introduced this issue during a refactorization. Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/ab8500_charger.c | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-)
drivers/power/supply/ab8500_charger.c +20 -20
@@ -3466,26 +3466,6 @@ static int ab8500_charger_probe(struct platform_device *pdev)return ret; } -/* Request interrupts */ -for (i =0 ; i <ARRAY_SIZE (ab8500_charger_irq); i++) { - irq =platform_get_irq_byname (pdev, ab8500_charger_irq[i].name); -if (irq <0 ) -return irq; - - ret =devm_request_threaded_irq (dev, - irq,NULL , ab8500_charger_irq[i].isr, -IRQF_SHARED |IRQF_NO_SUSPEND |IRQF_ONESHOT , - ab8500_charger_irq[i].name, di); - -if (ret !=0 ) { -dev_err (dev,"failed to request %s IRQ %d: %d\n" - , ab8500_charger_irq[i].name, irq, ret); -return ret; - } -dev_dbg (dev,"Requested %s IRQ %d: %d\n" , - ab8500_charger_irq[i].name, irq, ret); - } -/* initialize lock */ spin_lock_init (&di->usb_state.usb_lock);mutex_init (&di->usb_ipt_crnt_lock);
@@ -3614,6 +3594,26 @@ static int ab8500_charger_probe(struct platform_device *pdev)return PTR_ERR (di->usb_chg.psy); } +/* Request interrupts */ +for (i =0 ; i <ARRAY_SIZE (ab8500_charger_irq); i++) { + irq =platform_get_irq_byname (pdev, ab8500_charger_irq[i].name); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (dev, + irq,NULL , ab8500_charger_irq[i].isr, +IRQF_SHARED |IRQF_NO_SUSPEND |IRQF_ONESHOT , + ab8500_charger_irq[i].name, di); + +if (ret !=0 ) { +dev_err (dev,"failed to request %s IRQ %d: %d\n" + , ab8500_charger_irq[i].name, irq, ret); +return ret; + } +dev_dbg (dev,"Requested %s IRQ %d: %d\n" , + ab8500_charger_irq[i].name, irq, ret); + } +/* * Check what battery we have, since we always have the USB * psy, use that as a handle.
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 345/641 power: supply: act8945a: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 3291c51d4684d048dd2eb91b5b65fcfdaf72141f ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/act8945a_charger.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
drivers/power/supply/act8945a_charger.c +8 -8
@@ -597,14 +597,6 @@ static int act8945a_charger_probe(struct platform_device *pdev)return irq ?: -ENXIO ; } - ret =devm_request_irq (&pdev->dev, irq, act8945a_status_changed, -IRQF_TRIGGER_FALLING ,"act8945a_interrupt" , - charger); -if (ret) { -dev_err (&pdev->dev,"failed to request nIRQ pin IRQ\n" ); -return ret; - } - charger->desc.name ="act8945a-charger" ; charger->desc.get_property = act8945a_charger_get_property; charger->desc.properties = act8945a_charger_props;
@@ -625,6 +617,14 @@ static int act8945a_charger_probe(struct platform_device *pdev)return PTR_ERR (charger->psy); } + ret =devm_request_irq (&pdev->dev, irq, act8945a_status_changed, +IRQF_TRIGGER_FALLING ,"act8945a_interrupt" , + charger); +if (ret) { +dev_err (&pdev->dev,"failed to request nIRQ pin IRQ\n" ); +return ret; + } +platform_set_drvdata (pdev, charger);INIT_WORK (&charger->work, act8945a_work);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 346/641 power: supply: bq256xx: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 8005843369723d9c8975b7c4202d1b85d6125302 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/bq256xx_charger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/power/supply/bq256xx_charger.c +6 -6
@@ -1741,6 +1741,12 @@ static int bq256xx_probe(struct i2c_client *client)usb_register_notifier (bq->usb3_phy, &bq->usb_nb); } + ret =bq256xx_power_supply_init (bq, &psy_cfg, dev); +if (ret) { +dev_err (dev,"Failed to register power supply\n" ); +return ret; + } +if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, NULL, bq256xx_irq_handler_thread,
@@ -1753,12 +1759,6 @@ static int bq256xx_probe(struct i2c_client *client) } } - ret =bq256xx_power_supply_init (bq, &psy_cfg, dev); -if (ret) { -dev_err (dev,"Failed to register power supply\n" ); -return ret; - } - ret =bq256xx_hw_init (bq);if (ret) {dev_err (dev,"Cannot initialize the chip.\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 347/641 power: supply: bq25980: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 5f0b1cb41906e86b64bf69f5ededb83b0d757c27 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/bq25980_charger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/power/supply/bq25980_charger.c +6 -6
@@ -1241,6 +1241,12 @@ static int bq25980_probe(struct i2c_client *client)return ret; } + ret =bq25980_power_supply_init (bq, dev); +if (ret) { +dev_err (dev,"Failed to register power supply\n" ); +return ret; + } +if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, NULL, bq25980_irq_handler_thread,
@@ -1251,12 +1257,6 @@ static int bq25980_probe(struct i2c_client *client)return ret; } - ret =bq25980_power_supply_init (bq, dev); -if (ret) { -dev_err (dev,"Failed to register power supply\n" ); -return ret; - } - ret =bq25980_hw_init (bq);if (ret) {dev_err (dev,"Cannot initialize the chip.\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 348/641 power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 642f33e34b969eedec334738fd5df95d2dc42742 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/cpcap-battery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/power/supply/cpcap-battery.c +4 -4
@@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev)platform_set_drvdata (pdev, ddata); - error =cpcap_battery_init_interrupts (pdev, ddata); -if (error) -return error; - error =cpcap_battery_init_iio (ddata);if (error)return error;
@@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev)return error; } + error =cpcap_battery_init_interrupts (pdev, ddata); +if (error) +return error; +atomic_set (&ddata->active,1 ); error =cpcap_battery_calibrate (ddata);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 349/641 power: supply: goldfish: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit b2ce982e2e0c888dc55c888ad0e20ea04daf2e6b ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/goldfish_battery.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/power/supply/goldfish_battery.c +6 -6
@@ -224,12 +224,6 @@ static int goldfish_battery_probe(struct platform_device *pdev)if (data->irq <0 )return -ENODEV ; - ret =devm_request_irq (&pdev->dev, data->irq, - goldfish_battery_interrupt, -IRQF_SHARED , pdev->name, data); -if (ret) -return ret; - psy_cfg.drv_data = data; data->ac = devm_power_supply_register(&pdev->dev,
@@ -244,6 +238,12 @@ static int goldfish_battery_probe(struct platform_device *pdev)if (IS_ERR (data->battery))return PTR_ERR (data->battery); + ret =devm_request_irq (&pdev->dev, data->irq, + goldfish_battery_interrupt, +IRQF_SHARED , pdev->name, data); +if (ret) +return ret; +GOLDFISH_BATTERY_WRITE (data,BATTERY_INT_ENABLE ,BATTERY_INT_MASK );return 0 ; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 350/641 power: supply: pm8916_bms_vm: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 62914959b35e9a1e29cc0f64cb8cfc5075a5366f ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/pm8916_bms_vm.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/power/supply/pm8916_bms_vm.c +9 -9
@@ -167,15 +167,6 @@ static int pm8916_bms_vm_battery_probe(struct platform_device *pdev)if (ret <0 )return -EINVAL ; - irq =platform_get_irq_byname (pdev,"fifo" ); -if (irq <0 ) -return irq; - - ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_bms_vm_fifo_update_done_irq, -IRQF_ONESHOT ,"pm8916_vm_bms" , bat); -if (ret) -return ret; - ret =regmap_bulk_read (bat->regmap, bat->reg +PM8916_PERPH_TYPE , &tmp,2 );if (ret) goto comm_error;
@@ -220,6 +211,15 @@ static int pm8916_bms_vm_battery_probe(struct platform_device *pdev)if (ret)return dev_err_probe (dev, ret,"Unable to get battery info\n" ); + irq =platform_get_irq_byname (pdev,"fifo" ); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_bms_vm_fifo_update_done_irq, +IRQF_ONESHOT ,"pm8916_vm_bms" , bat); +if (ret) +return ret; +platform_set_drvdata (pdev, bat);return 0 ;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 351/641 power: supply: pm8916_lbc: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit b7508129978ae1e2ed9b0410396abc05def9c4eb ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/pm8916_lbc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/power/supply/pm8916_lbc.c +9 -9
@@ -274,15 +274,6 @@ static int pm8916_lbc_charger_probe(struct platform_device *pdev)return dev_err_probe (dev, -EINVAL ,"Wrong amount of reg values: %d (4 expected)\n" , len); - irq =platform_get_irq_byname (pdev,"usb_vbus" ); -if (irq <0 ) -return irq; - - ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_lbc_charger_state_changed_irq, -IRQF_ONESHOT ,"pm8916_lbc" , chg); -if (ret) -return ret; - ret =device_property_read_u32_array (dev,"reg" , chg->reg, len);if (ret)return ret;
@@ -332,6 +323,15 @@ static int pm8916_lbc_charger_probe(struct platform_device *pdev)if (ret)return dev_err_probe (dev, ret,"Unable to get battery info\n" ); + irq =platform_get_irq_byname (pdev,"usb_vbus" ); +if (irq <0 ) +return irq; + + ret =devm_request_threaded_irq (dev, irq,NULL , pm8916_lbc_charger_state_changed_irq, +IRQF_ONESHOT ,"pm8916_lbc" , chg); +if (ret) +return ret; + chg->edev =devm_extcon_dev_allocate (dev, pm8916_lbc_charger_cable);if (IS_ERR (chg->edev))return PTR_ERR (chg->edev);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 352/641 power: supply: rt9455: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit e2febe375e5ea5afed92f4cd9711bde8f24ee6d2 ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
--- drivers/power/supply/rt9455_charger.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
drivers/power/supply/rt9455_charger.c +9 -8
@@ -1663,6 +1663,15 @@ static int rt9455_probe(struct i2c_client *client) rt9455_charger_config.supplied_to = rt9455_charger_supplied_to; rt9455_charger_config.num_supplicants =ARRAY_SIZE (rt9455_charger_supplied_to); + + info->charger =devm_power_supply_register (dev, &rt9455_charger_desc, + &rt9455_charger_config); +if (IS_ERR (info->charger)) { +dev_err (dev,"Failed to register charger\n" ); + ret =PTR_ERR (info->charger); + goto put_usb_notifier; + } + ret = devm_request_threaded_irq(dev, client->irq, NULL, rt9455_irq_handler_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
@@ -1678,14 +1687,6 @@ static int rt9455_probe(struct i2c_client *client) goto put_usb_notifier; } - info->charger =devm_power_supply_register (dev, &rt9455_charger_desc, - &rt9455_charger_config); -if (IS_ERR (info->charger)) { -dev_err (dev,"Failed to register charger\n" ); - ret =PTR_ERR (info->charger); - goto put_usb_notifier; - } -return 0 ; put_usb_notifier:
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 353/641 power: supply: sbs-battery: Fix use-after-free in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 8d59cf3887fbabacef53bfba473e33e8a8d9d07b ]
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle. Keep the old behavior of just printing a warning in case of any failures during the IRQ request and finishing the probe successfully.
--- drivers/power/supply/sbs-battery.c | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-)
drivers/power/supply/sbs-battery.c +18 -18
@@ -1174,24 +1174,6 @@ static int sbs_probe(struct i2c_client *client)i2c_set_clientdata (client, chip); -if (!chip->gpio_detect) - goto skip_gpio; - - irq =gpiod_to_irq (chip->gpio_detect); -if (irq <=0 ) { -dev_warn (&client->dev,"Failed to get gpio as irq: %d\n" , irq); - goto skip_gpio; - } - - rc =devm_request_threaded_irq (&client->dev, irq,NULL , sbs_irq, -IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING |IRQF_ONESHOT , -dev_name (&client->dev), chip); -if (rc) { -dev_warn (&client->dev,"Failed to request irq: %d\n" , rc); - goto skip_gpio; - } - -skip_gpio:/* * Before we register, we might need to make sure we can actually talk * to the battery.
@@ -1217,6 +1199,24 @@ static int sbs_probe(struct i2c_client *client)return dev_err_probe (&client->dev,PTR_ERR (chip->power_supply),"Failed to register power supply\n" ); +if (!chip->gpio_detect) + goto out; + + irq =gpiod_to_irq (chip->gpio_detect); +if (irq <=0 ) { +dev_warn (&client->dev,"Failed to get gpio as irq: %d\n" , irq); + goto out; + } + + rc =devm_request_threaded_irq (&client->dev, irq,NULL , sbs_irq, +IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING |IRQF_ONESHOT , +dev_name (&client->dev), chip); +if (rc) { +dev_warn (&client->dev,"Failed to request irq: %d\n" , rc); + goto out; + } + +out:dev_info (&client->dev,"%s: battery gas gauge device registered\n" , client->name);
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 354/641 power: reset: nvmem-reboot-mode: respect cell size for nvmem_cell_write
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Koskovich <[email protected]>
[ Upstream commit 36b05629226413836cfbb3fbe6689cd188bca156 ]
Some platforms expose reboot mode cells that are smaller than an unsigned int, in which cases lead to write failures. Read the cell first to determine actual size and only write the number of bytes the cell can hold.
--- drivers/power/reset/nvmem-reboot-mode.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/power/reset/nvmem-reboot-mode.c +13 -2
@@ -10,6 +10,7 @@#include <linux/nvmem-consumer.h> #include <linux/platform_device.h> #include <linux/reboot-mode.h> +#include <linux/slab.h> struct nvmem_reboot_mode {struct reboot_mode_driver reboot;
@@ -19,12 +20,22 @@ struct nvmem_reboot_mode {static int nvmem_reboot_mode_write (struct reboot_mode_driver *reboot,unsigned int magic) { -int ret;struct nvmem_reboot_mode *nvmem_rbm; +size_t buf_len; +void *buf; +int ret; nvmem_rbm = container_of(reboot, struct nvmem_reboot_mode, reboot); - ret =nvmem_cell_write (nvmem_rbm->cell, &magic,sizeof (magic)); + buf =nvmem_cell_read (nvmem_rbm->cell, &buf_len); +if (IS_ERR (buf)) +return PTR_ERR (buf); +kfree (buf); + +if (buf_len >sizeof (magic)) +return -EINVAL ; + + ret =nvmem_cell_write (nvmem_rbm->cell, &magic, buf_len);if (ret <0 )dev_err (reboot->dev,"update reboot mode bits failed\n" );
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 355/641 power: supply: bq27xxx: fix wrong errno when bus ops are unsupported
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haotian Zhang <[email protected]>
[ Upstream commit 688364a11647dc09ba1e4429313e0008066ec790 ]
bq27xxx_write(), bq27xxx_read_block(), and bq27xxx_write_block() return -EPERM when the bus callback pointer is NULL. A NULL callback indicates the operation is not supported by the bus/driver, not that permission is denied.
Return -EOPNOTSUPP instead of -EPERM when di->bus.write/ read_bulk/write_bulk is NULL.
--- drivers/power/supply/bq27xxx_battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/power/supply/bq27xxx_battery.c +3 -3
@@ -1172,7 +1172,7 @@ static inline int bq27xxx_write(struct bq27xxx_device_info *di, int reg_index,return -EINVAL ;if (!di->bus.write) -return -EPERM ; +return -EOPNOTSUPP ; ret = di->bus.write (di, di->regs[reg_index], value, single);if (ret <0 )
@@ -1191,7 +1191,7 @@ static inline int bq27xxx_read_block(struct bq27xxx_device_info *di, int reg_indreturn -EINVAL ;if (!di->bus.read_bulk) -return -EPERM ; +return -EOPNOTSUPP ; ret = di->bus.read_bulk (di, di->regs[reg_index], data, len);if (ret <0 )
@@ -1210,7 +1210,7 @@ static inline int bq27xxx_write_block(struct bq27xxx_device_info *di, int reg_inreturn -EINVAL ;if (!di->bus.write_bulk) -return -EPERM ; +return -EOPNOTSUPP ; ret = di->bus.write_bulk (di, di->regs[reg_index], data, len);if (ret <0 )
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 356/641 power: supply: wm97xx: Fix NULL pointer dereference in power_supply_changed()
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waqar Hameed <[email protected]>
[ Upstream commit 39fe0eac6d755ef215026518985fcf8de9360e9e ]
In probe(), request_irq() is called before allocating/registering a power_supply handle. If an interrupt is fired between the call to request_irq() and power_supply_register(), the power_supply handle will be used uninitialized in power_supply_changed() in wm97xx_bat_update() (triggered from the interrupt handler). This will lead to a NULL pointer dereference since
Fix this racy NULL pointer dereference by making sure the IRQ is requested _after_ the registration of the power_supply handle. Since the IRQ is the last thing requests in the probe() now, remove the error path for freeing it. Instead add one for unregistering the power_supply handle when IRQ request fails.
--- drivers/power/supply/wm97xx_battery.c | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-)
drivers/power/supply/wm97xx_battery.c +19 -15
@@ -178,12 +178,6 @@ static int wm97xx_bat_probe(struct platform_device *dev) "failed to get charge GPIO\n");if (charge_gpiod) {gpiod_set_consumer_name (charge_gpiod,"BATT CHRG" ); - ret =request_irq (gpiod_to_irq (charge_gpiod), - wm97xx_chrg_irq,0 , -"AC Detect" , dev); -if (ret) -return dev_err_probe (&dev->dev, ret, -"failed to request GPIO irq\n" ); props++;/* POWER_SUPPLY_PROP_STATUS */ }
@@ -199,10 +193,8 @@ static int wm97xx_bat_probe(struct platform_device *dev) props++;/* POWER_SUPPLY_PROP_VOLTAGE_MIN */ prop =kcalloc (props,sizeof (*prop),GFP_KERNEL ); -if (!prop) { - ret = -ENOMEM ; - goto err3; - } +if (!prop) +return -ENOMEM ; prop[i++] =POWER_SUPPLY_PROP_PRESENT ;if (charge_gpiod)
@@ -236,15 +228,27 @@ static int wm97xx_bat_probe(struct platform_device *dev)schedule_work (&bat_work); }else { ret =PTR_ERR (bat_psy); - goto err4; + goto free; + } + +if (charge_gpiod) { + ret =request_irq (gpiod_to_irq (charge_gpiod), wm97xx_chrg_irq, +0 ,"AC Detect" , dev); +if (ret) { +dev_err_probe (&dev->dev, ret, +"failed to request GPIO irq\n" ); + goto unregister; + } }return 0 ; -err4: + +unregister: +power_supply_unregister (bat_psy); + +free:kfree (prop); -err3: -if (charge_gpiod) -free_irq (gpiod_to_irq (charge_gpiod), dev); +return ret; }
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 357/641 RDMA/rtrs-srv: fix SG mapping
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roman Penyaev <[email protected]>
[ Upstream commit 83835f7c07b523c7ca2a5ad0a511670b5810539e ]
This fixes the following error on the server side:
RTRS server session allocation failed: -EINVAL
caused by the caller of the ib_dma_map_sg(), which does not expect less mapped entries, than requested, which is in the order of things and can be easily reproduced on the machine with enabled IOMMU.
The fix is to treat any positive number of mapped sg entries as a successful mapping and cache DMA addresses by traversing modified SG table.
--- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
drivers/infiniband/ulp/rtrs/rtrs-srv.c +20 -5
@@ -595,7 +595,7 @@ static int map_cont_bufs(struct rtrs_srv_path *srv_path) srv_path->mrs_num++) {struct rtrs_srv_mr *srv_mr = &srv_path->mrs[srv_path->mrs_num];struct scatterlist *s; -int nr, nr_sgt, chunks; +int nr, nr_sgt, chunks, ind; sgt = &srv_mr->sgt; chunks = chunks_per_mr * srv_path->mrs_num;
@@ -625,7 +625,7 @@ static int map_cont_bufs(struct rtrs_srv_path *srv_path) } nr =ib_map_mr_sg (mr, sgt->sgl, nr_sgt,NULL , max_chunk_size); -if (nr != nr_sgt) { +if (nr < nr_sgt) { err = nr <0 ? nr : -EINVAL ; goto dereg_mr; }
@@ -641,9 +641,24 @@ static int map_cont_bufs(struct rtrs_srv_path *srv_path) goto dereg_mr; } } -/* Eventually dma addr for each chunk can be cached */ -for_each_sg (sgt->sgl, s, nr_sgt, i) - srv_path->dma_addr[chunks + i] =sg_dma_address (s); + +/* +* Cache DMA addresses by traversing sg entries. If +* regions were merged, an inner loop is required to +* populate the DMA address array by traversing larger +* regions. +*/ + ind = chunks; +for_each_sg (sgt->sgl, s, nr_sgt, i) { +unsigned int dma_len =sg_dma_len (s); +u64 dma_addr =sg_dma_address (s); +u64 dma_addr_end = dma_addr + dma_len; + +do { + srv_path->dma_addr[ind++] = dma_addr; + dma_addr += max_chunk_size; + }while (dma_addr < dma_addr_end); + }ib_update_fast_reg_key (mr,ib_inc_rkey (mr->rkey)); srv_mr->mr = mr;
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 358/641 RDMA/rxe: Fix double free in rxe_srq_from_init
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiasheng Jiang <[email protected]>
[ Upstream commit 0beefd0e15d962f497aad750b2d5e9c3570b66d1 ]
In rxe_srq_from_init(), the queue pointer 'q' is assigned to 'srq->rq.queue' before copying the SRQ number to user space. If copy_to_user() fails, the function calls rxe_queue_cleanup() to free the queue, but leaves the now-invalid pointer in 'srq->rq.queue'.
The caller of rxe_srq_from_init() (rxe_create_srq) eventually calls rxe_srq_cleanup() upon receiving the error, which triggers a second rxe_queue_cleanup() on the same memory, leading to a double free.
The call trace looks like this:
kmem_cache_free+0x.../0x... rxe_queue_cleanup+0x1a/0x30 [rdma_rxe] rxe_srq_cleanup+0x42/0x60 [rdma_rxe] rxe_elem_release+0x31/0x70 [rdma_rxe] rxe_create_srq+0x12b/0x1a0 [rdma_rxe] ib_create_srq_user+0x9a/0x150 [ib_core]
Fix this by moving 'srq->rq.queue = q' after copy_to_user.
--- drivers/infiniband/sw/rxe/rxe_srq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/infiniband/sw/rxe/rxe_srq.c +3 -3
@@ -77,9 +77,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, goto err_free; } - srq->rq.queue = q; - init->attr.max_wr = srq->rq.max_wr; -if (uresp) {if (copy_to_user (&uresp->srq_num, &srq->srq_num,sizeof (uresp->srq_num))) {
@@ -88,6 +85,9 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, } } + srq->rq.queue = q; + init->attr.max_wr = srq->rq.max_wr; +return 0 ; err_free:
Signature · 1 line
2.51.0
Greg Kroah-Hartman
maintainerPATCH 359/641 RDMA/iwcm: Fix workqueue list corruption by removing work_list
In reply to Greg Kroah-Hartman6.18-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jacob Moroni <[email protected]>
[ Upstream commit 7874eeacfa42177565c01d5198726671acf7adf2 ]
The commit e1168f0 ("RDMA/iwcm: Simplify cm_event_handler()") changed the work submission logic to unconditionally call queue_work() with the expectation that queue_work() would have no effect if work was already pending. The problem is that a free list of struct iwcm_work is used (for which struct work_struct is embedded), so each call to queue_work() is basically unique and therefore does indeed queue the work.
This causes a problem in the work handler which walks the work_list until it's empty to process entries. This means that a single run of the work handler could process item N+1 and release it back to the free list while the actual workqueue entry is still queued. It could then get reused (INIT_WORK...) and lead to list corruption in the workqueue logic.
Fix this by just removing the work_list. The workqueue already does this for us.
This fixes the following error that was observed when stress testing with ucmatose on an Intel E830 in iWARP mode:
[ 151.465780] list_del corruption. next->prev should be ffff9f0915c69c08, but was ffff9f0a1116be08. (next=ffff9f0a15b11c08) [ 151.466639] ------------[ cut here ]------------ [ 151.466986] kernel BUG at lib/list_debug.c:67! [ 151.467349] Oops: invalid opcode: 0000 [#1] SMP NOPTI [ 151.467753] CPU: 14 UID: 0 PID: 2306 Comm: kworker/u64:18 Not tainted 6.19.0-rc4+ #1 PREEMPT(voluntary) [ 151.468466] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 151.469192] Workqueue: 0x0 (iw_cm_wq) [ 151.469478] RIP: 0010:__list_del_entry_valid_or_report+0xf0/0x100 [ 151.469942] Code: c7 58 5f 4c b2 e8 10 50 aa ff 0f 0b 48 89 ef e8 36 57 cb ff 48 8b 55 08 48 89 e9 48 89 de 48 c7 c7 a8 5f 4c b2 e8 f0 4f aa ff <0f> 0b 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 90 90 90 90 90 90 [ 151.471323] RSP: 0000:ffffb15644e7bd68 EFLAGS: 00010046 [ 151.471712] RAX: 000000000000006d RBX: ffff9f0915c69c08 RCX: 0000000000000027 [ 151.472243] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff9f0a37d9c600 [ 151.472768] RBP: ffff9f0a15b11c08 R08: 0000000000000000 R09: c0000000ffff7fff [ 151.473294] R10: 0000000000000001 R11: ffffb15644e7bba8 R12: ffff9f092339ee68 [ 151.473817] R13: ffff9f0900059c28 R14: ffff9f092339ee78 R15: 0000000000000000 [ 151.474344] FS: 0000000000000000(0000) GS:ffff9f0a847b5000(0000) knlGS:0000000000000000 [ 151.474934] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 151.475362] CR2: 0000559e233a9088 CR3: 000000020296b004 CR4: 0000000000770ef0 [ 151.475895] PKRU: 55555554 [ 151.476118] Call Trace: [ 151.476331] <TASK> [ 151.476497] move_linked_works+0x49/0xa0 [ 151.476792] __pwq_activate_work.isra.46+0x2f/0xa0 [ 151.477151] pwq_dec_nr_in_flight+0x1e0/0x2f0 [ 151.477479] process_scheduled_works+0x1c8/0x410 [ 151.477823] worker_thread+0x125/0x260 [ 151.478108] ? __pfx_worker_thread+0x10/0x10 [ 151.478430] kthread+0xfe/0x240 [ 151.478671] ? __pfx_kthread+0x10/0x10 [ 151.478955] ? __pfx_kthread+0x10/0x10 [ 151.479240] ret_from_fork+0x208/0x270 [ 151.479523] ? __pfx_kthread+0x10/0x10 [ 151.479806] ret_from_fork_asm+0x1a/0x30 [ 151.480103] </TASK>
--- drivers/infiniband/core/iwcm.c | 56 +++++++++++++--------------------- drivers/infiniband/core/iwcm.h | 1 - 2 files changed, 21 insertions(+), 36 deletions(-)
drivers/infiniband/core/iwcm.c +21 -35
@@ -95,7 +95,6 @@ static struct workqueue_struct *iwcm_wq;struct iwcm_work {struct work_struct work;struct iwcm_id_private *cm_id; -struct list_head list;struct iw_cm_event event;struct list_head free_list; };
@@ -178,7 +177,6 @@ static int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count)return -ENOMEM ; } work->cm_id = cm_id_priv; -INIT_LIST_HEAD (&work->list);put_work (work); }return 0 ;
@@ -213,7 +211,6 @@ static void free_cm_id(struct iwcm_id_private *cm_id_priv)static bool iwcm_deref_id (struct iwcm_id_private *cm_id_priv) {if (refcount_dec_and_test (&cm_id_priv->refcount)) { -BUG_ON (!list_empty (&cm_id_priv->work_list));free_cm_id (cm_id_priv);return true; }
@@ -260,7 +257,6 @@ struct iw_cm_id *iw_create_cm_id(struct ib_device *device,refcount_set (&cm_id_priv->refcount,1 );init_waitqueue_head (&cm_id_priv->connect_wait);init_completion (&cm_id_priv->destroy_comp); -INIT_LIST_HEAD (&cm_id_priv->work_list);INIT_LIST_HEAD (&cm_id_priv->work_free_list);return &cm_id_priv->id;
@@ -1007,13 +1003,13 @@ static int process_event(struct iwcm_id_private *cm_id_priv, }/* -* Process events on the work_list for the cm_id. If the callback -* function requests that the cm_id be deleted, a flag is set in the -* cm_id flags to indicate that when the last reference is -* removed, the cm_id is to be destroyed. This is necessary to -* distinguish between an object that will be destroyed by the app -* thread asleep on the destroy_comp list vs. an object destroyed -* here synchronously when the last reference is removed. +* Process events for the cm_id. If the callback function requests +* that the cm_id be deleted, a flag is set in the cm_id flags to +* indicate that when the last reference is removed, the cm_id is +* to be destroyed. This is necessary to distinguish between an +* object that will be destroyed by the app thread asleep on the +* destroy_comp list vs. an object destroyed here synchronously +* when the last reference is removed. */ static void cm_work_handler (struct work_struct *_work) {
@@ -1024,35 +1020,26 @@ static void cm_work_handler(struct work_struct *_work)int ret =0 ;spin_lock_irqsave (&cm_id_priv->lock, flags); -while (!list_empty (&cm_id_priv->work_list)) { - work =list_first_entry (&cm_id_priv->work_list, - struct iwcm_work, list); -list_del_init (&work->list); - levent = work->event; -put_work (work); -spin_unlock_irqrestore (&cm_id_priv->lock, flags); - -if (!test_bit (IWCM_F_DROP_EVENTS , &cm_id_priv->flags)) { - ret =process_event (cm_id_priv, &levent); -if (ret) { -destroy_cm_id (&cm_id_priv->id); -WARN_ON_ONCE (iwcm_deref_id (cm_id_priv)); - } - }else -pr_debug ("dropping event %d\n" , levent.event); -if (iwcm_deref_id (cm_id_priv)) -return ; -spin_lock_irqsave (&cm_id_priv->lock, flags); - } + levent = work->event; +put_work (work);spin_unlock_irqrestore (&cm_id_priv->lock, flags); + +if (!test_bit (IWCM_F_DROP_EVENTS , &cm_id_priv->flags)) { + ret =process_event (cm_id_priv, &levent); +if (ret) { +destroy_cm_id (&cm_id_priv->id); +WARN_ON_ONCE (iwcm_deref_id (cm_id_priv)); + } + }else +pr_debug ("dropping event %d\n" , levent.event); +if (iwcm_deref_id (cm_id_priv)) +return ; }/* * This function is called on interrupt context. Schedule events on * the iwcm_wq thread to allow callback functions to downcall into -* the CM and/or block. Events are queued to a per-CM_ID -* work_list. If this is the first event on the work_list, the work -* element is also queued on the iwcm_wq thread. +* the CM and/or block. * * Each event holds a reference on the cm_id. Until the last posted * event has been delivered and processed, the cm_id cannot be
@@ -1094,7 +1081,6 @@ static int cm_event_handler(struct iw_cm_id *cm_id, }refcount_inc (&cm_id_priv->refcount); -list_add_tail (&work->list, &cm_id_priv->work_list);queue_work (iwcm_wq, &work->work); out:spin_unlock_irqrestore (&cm_id_priv->lock, flags);
drivers/infiniband/core/iwcm.h +0 -1
@@ -50,7 +50,6 @@ struct iwcm_id_private {struct ib_qp *qp;struct completion destroy_comp;wait_queue_head_t connect_wait; -struct list_head work_list;spinlock_t lock;refcount_t refcount;struct list_head work_free_list;
Signature · 1 line
2.51.0