* Re: [PATCH v2] iio: dac: mcp47feb02: Fix passing uninitialized vref1_uV for no Vref1 case
From: Jonathan Cameron @ 2026-04-20 12:21 UTC (permalink / raw)
To: Ariana Lazar
Cc: David Lechner, Nuno Sá, Andy Shevchenko, Jonathan Cameron,
linux-iio, linux-kernel, Dan Carpenter
In-Reply-To: <20260420-mcp47feb02-fix4-v2-1-8b758eaa8bcf@microchip.com>
On Mon, 20 Apr 2026 11:04:12 +0300
Ariana Lazar <ariana.lazar@microchip.com> wrote:
> Ensure that if a device has Vref1 but reading the regulator returns an
> error, mcp47feb02_init_ctrl_regs() is not called with an uninitialized
> vref1_uV value.
Just to make it easier for folk to tell if this is critical or not:
Is it a real bug? See my reply (just now) to v1.
Otherwise, same thing I just raised on v1. Forgot to check if a new
version had come in today.
>
> Also add a device_property_present() check for the Vref1 supply before
> reading the regulator.
>
> Fixes: dd154646d292 ("iio: dac: mcp47feb02: Fix Vref validation [1-999] case")
> Reported-by: Dan Carpenter <error27@gmail.com>
> Closes: https://lore.kernel.org/all/adiPnla0M5EzvgD-@stanley.mountain/
> Signed-off-by: Ariana Lazar <ariana.lazar@microchip.com>
> ---
> Changes in v2:
> - return the reading regulator error to not use vref1_uV uninitialized in
> mcp47feb02_init_ctrl_regs() call
> - add device_property_present() check for the Vref1
> - Link to v1: https://lore.kernel.org/r/20260414-mcp47feb02-fix4-v1-1-9d71badfd25e@microchip.com
> ---
> drivers/iio/dac/mcp47feb02.c | 21 +++++++++++----------
> 1 file changed, 11 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c
> index faccb804a5ed548088aaf83266b16ed45a92916c..a876ed71ecdf101784e0f98668a1681a1cd19ed0 100644
> --- a/drivers/iio/dac/mcp47feb02.c
> +++ b/drivers/iio/dac/mcp47feb02.c
> @@ -1095,9 +1095,10 @@ static int mcp47feb02_probe(struct i2c_client *client)
> {
> const struct mcp47feb02_features *chip_features;
> struct device *dev = &client->dev;
> + int vdd_uV, vref_uV, vref1_uV;
> struct mcp47feb02_data *data;
> struct iio_dev *indio_dev;
> - int vref1_uV, vref_uV, vdd_uV, ret;
Solution is fine but this change seems to be unrelated noise.
> + int ret;
>
> indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> if (!indio_dev)
> @@ -1146,16 +1147,16 @@ static int mcp47feb02_probe(struct i2c_client *client)
> dev_dbg(dev, "Vref is unavailable.\n");
> }
>
> - if (chip_features->have_ext_vref1) {
> + if (chip_features->have_ext_vref1 && device_property_present(dev, "vref1")) {
> ret = devm_regulator_get_enable_read_voltage(dev, "vref1");
> - if (ret > 0) {
> - vref1_uV = ret;
> - data->use_vref1 = true;
> - } else {
> - vref1_uV = 0;
> - dev_dbg(dev, "using internal band gap as voltage reference 1.\n");
> - dev_dbg(dev, "Vref1 is unavailable.\n");
> - }
> + if (ret < 0)
> + return ret;
> +
> + vref1_uV = ret;
> + data->use_vref1 = true;
> + } else {
> + vref1_uV = 0;
> + dev_dbg(dev, "using internal band gap as voltage reference 1.\n");
> }
>
> ret = mcp47feb02_init_ctrl_regs(data);
>
> ---
> base-commit: 51e7665ab81f02adc80a1219c260ee678e9c6eb8
> change-id: 20260414-mcp47feb02-fix4-614de9334f22
>
> Best regards,
^ permalink raw reply
* Re: [PATCH] vmalloc: fix buffer overflow in vrealloc_node_align()
From: Vlastimil Babka (SUSE) @ 2026-04-20 12:21 UTC (permalink / raw)
To: Marco Elver, Andrew Morton
Cc: Uladzislau Rezki, linux-mm, linux-kernel, kasan-dev, Vitaly Wool,
stable, Harry Yoo (Oracle)
In-Reply-To: <20260420114805.3572606-2-elver@google.com>
On 4/20/26 13:47, Marco Elver wrote:
> Commit 4c5d3365882d ("mm/vmalloc: allow to set node and align in
> vrealloc") added the ability to force a new allocation if the current
> pointer is on the wrong NUMA node, or if an alignment constraint is not
> met, even if the user is shrinking the allocation.
>
> On this path (need_realloc), the code allocates a new object of 'size'
> bytes and then memcpy()s 'old_size' bytes into it. If the request is to
> shrink the object (size < old_size), this results in an out-of-bounds
> write on the new buffer.
>
> Fix this by bounding the copy length by the new allocation size.
>
> Fixes: 4c5d3365882d ("mm/vmalloc: allow to set node and align in vrealloc")
> Cc: <stable@vger.kernel.org>
> Reported-by: Harry Yoo (Oracle) <harry@kernel.org>
> Signed-off-by: Marco Elver <elver@google.com>
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> ---
> mm/vmalloc.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index 61caa55a4402..8b1124158f54 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -4361,7 +4361,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
> return NULL;
>
> if (p) {
> - memcpy(n, p, old_size);
> + memcpy(n, p, min(size, old_size));
> vfree(p);
> }
>
^ permalink raw reply
* Re: [PATCH RFC] xen/swiotlb: avoid arch_sync_dma_* on per-device DMA memory
From: Jürgen Groß @ 2026-04-20 12:19 UTC (permalink / raw)
To: Peng Fan (OSS), Stefano Stabellini, Oleksandr Tyshchenko
Cc: xen-devel, iommu, linux-kernel, Peng Fan
In-Reply-To: <20260415-xen-swiotlb-v1-1-de24eda3c0fd@nxp.com>
[-- Attachment #1.1.1: Type: text/plain, Size: 1790 bytes --]
On 15.04.26 17:08, Peng Fan (OSS) wrote:
> From: Peng Fan <peng.fan@nxp.com>
>
> On ARM64, arch_sync_dma_for_{cpu,device}() assumes that the
> physical address passed in refers to normal RAM that is part of the
> kernel linear(direct) mapping, as it unconditionally derives a CPU
> virtual address via phys_to_virt().
>
> With Xen swiotlb, devices may use per-device coherent DMA memory,
> such as reserved-memory regions described by 'shared-dma-pool',
> which are assigned to dev->dma_mem. These regions may be marked
> no-map in DT and therefore are not part of the kernel linear map.
> In such cases, pfn_valid() still returns true, but phys_to_virt()
> is not valid and cache maintenance via arch_sync_dma_* will fault.
>
> Prevent this by excluding devices with a private DMA memory pool
> (dev->dma_mem) from the arch_sync_dma_* fast path, and always
> fall back to xen_dma_sync_* for those devices to avoid invalid
> phys_to_virt() conversions for no-map DMA memory while preserving the
> existing fast path for normal, linear-mapped RAM.
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
> drivers/xen/swiotlb-xen.c | 17 +++++++++++++----
> 1 file changed, 13 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
> index 2cbf2b588f5b20cfbf9e83a8339dc22092c9559a..b1445df99d9a8f1d18a83b8c413bada6e5579209 100644
> --- a/drivers/xen/swiotlb-xen.c
> +++ b/drivers/xen/swiotlb-xen.c
> @@ -195,6 +195,11 @@ xen_swiotlb_free_coherent(struct device *dev, size_t size, void *vaddr,
> }
> #endif /* CONFIG_X86 */
>
> +static inline bool dev_has_private_dma_pool(struct device *dev)
> +{
> + return dev && dev->dma_mem;
> +}
> +
I don't think this will compile on x86.
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply
* Re: [PATCH] iio: dac: Fix passing uninitialized vref1_uV for no Vref1 case
From: Jonathan Cameron @ 2026-04-20 12:19 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Ariana.Lazar, dlechner, nuno.sa, linux-iio, Jonathan.Cameron,
linux-kernel, andy, error27
In-Reply-To: <ad5qsFP_7l3FATMA@ashevche-desk.local>
On Tue, 14 Apr 2026 19:26:24 +0300
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> On Tue, Apr 14, 2026 at 02:26:45PM +0000, Ariana.Lazar@microchip.com wrote:
> > On Tue, 2026-04-14 at 15:48 +0300, Andy Shevchenko wrote:
> > > EXTERNAL EMAIL: Do not click links or open attachments unless you
> > > know the content is safe
>
> You should get rid of this message. It's incompatible with OSS development
> process.
>
> > > On Tue, Apr 14, 2026 at 03:33:38PM +0300, Ariana Lazar wrote:
>
> ...
>
> > > > + vref1_uV = 0;
> > > > if (chip_features->have_ext_vref1) {
> > >
> > > I'm wondering what will happen if we do the below unconditionally?
> > >
> > > > ret = devm_regulator_get_enable_read_voltage(dev,
> > > > "vref1");
> > >
> > > If we have no regulator, we get a dummy one, right? What is the
> > > voltage will
> > > be? 0?
> > >
> > > > if (ret > 0) {
> > > > vref1_uV = ret;
> > > > data->use_vref1 = true;
> > > > } else {
> > > > - vref1_uV = 0;
> > > > dev_dbg(dev, "using internal band gap as
> > > > voltage reference 1.\n");
> > > > dev_dbg(dev, "Vref1 is unavailable.\n");
> > > > }
> >
> > Thank you for the review.
> >
> > This is a safety check to ensure the devicetree matches the available
> > hardware. If Vref1 was selected in devicetree but unavailable in
> > hardware, the scales MCP47FEB02_SCALE_GAIN_X1 and
> > MCP47FEB02_SCALE_GAIN_X2 and also voltage readings would be incorrect
> > for the channels that use Vref1.
>
> I didn't get how. What I recommend is to do regulator request unconditionally.
>
> > I did something similiar to what you have suggested in the first patch
> > I have submitted for this driver and checking first was recommended.
> >
> > https://lore.kernel.org/all/20250927185324.2f9e8061@jic23-huawei/
>
> I briefly read that. The check was there, Jonathan just asked to modify
> the check itself IIUC, i.o.w. the semantics of the check was commented
> and not the check presence in the first place.
>
> Did I get it wrong? Jonathan, can we get rid of the check and ask for
> regulator unconditionally? (Maybe it would be good to print an error
> code in the debug message to be sure why it failed to get the regulator
> or its voltage.)
>
Whilst it might be harmless I'm not keen to get a regulator at all if it
doesn't exist. Sure we'll see an error and the code will work (assuming
the non existent regulator isn't in the DT!) but we'll also get a debug
message that is at best misleading. Vref1 would indeed be unavailable
because the specific device doesn't have a pin to connect it to!
So I think the outer guard still makes sense.
There is a reasonable question of why anything is using the value that
isn't initialized, and hence why the original bug exists?
I'm thinking it probably doesn't in practice, but that the
relationship between the flag and channel count etc is sufficiently complex
the compiler can't tell that. So my assumption is this 'bug' is
a false positive. We still want to tidy it up anyway on basis
it's not unreasonable that we are triggering static analysis warnings
on it!
J
^ permalink raw reply
* [GIT PULL] Please pull RDMA subsystem changes
From: Jason Gunthorpe @ 2026-04-20 12:19 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-rdma, linux-kernel, Leon Romanovsky
[-- Attachment #1: Type: text/plain, Size: 23040 bytes --]
Hi Linus,
There was a last minute userspace regression reported, so this is a
little later than I usually send it.
There are three merge conflicts this time, one is silent.
1) The struct member wad renamed in the DMABUF tree:
+++ b/drivers/infiniband/core/umem_dmabuf.c
+static struct dma_buf_attach_ops ib_umem_dmabuf_attach_pinned_revocable_ops = {
+ .allow_peer2peer = true,
- .move_notify = ib_umem_dmabuf_revoke_locked,
++ .invalidate_mappings = ib_umem_dmabuf_revoke_locked,
+};
2) Overlapping removals:
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@@ -46,15 -46,14 +46,13 @@@
#include "data_direct.h"
#include "dmah.h"
- #define MLX5_UMR_ALIGN 2048
-
-enum {
- MAX_PENDING_REG_MR = 8,
-};
+static int mkey_max_umr_order(struct mlx5_ib_dev *dev)
+{
+ if (MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset))
+ return MLX5_MAX_UMR_EXTENDED_SHIFT;
+ return MLX5_MAX_UMR_SHIFT;
+}
-#define MLX5_MR_CACHE_PERSISTENT_ENTRY_MIN_DESCS 4
-
-static void
-create_mkey_callback(int status, struct mlx5_async_work *context);
3) rxe_ns_pernet_sk6:
+++ b/drivers/infiniband/sw/rxe/rxe_net.c
@@@ -141,9 -138,9 +141,7 @@@ static struct dst_entry *rxe_find_route
memcpy(&fl6.daddr, daddr, sizeof(*daddr));
fl6.flowi6_proto = IPPROTO_UDP;
- ndst = ipv6_stub->ipv6_dst_lookup_flow(net,
- rxe_ns_pernet_sk6(net), &fl6,
- NULL);
- ndst = ip6_dst_lookup_flow(sock_net(recv_sockets.sk6->sk),
- recv_sockets.sk6->sk, &fl6,
- NULL);
++ ndst = ip6_dst_lookup_flow(net, rxe_ns_pernet_sk6(net), &fl6, NULL);
if (IS_ERR(ndst)) {
rxe_dbg_qp(qp, "no route to %pI6\n", daddr);
return NULL;
The tag for-linus-merged with my merge resolution to your tree is also
available to pull.
Thanks,
Jason
The following changes since commit 7aaa8047eafd0bd628065b15757d9b48c5f9c07d:
Linux 7.0-rc6 (2026-03-29 15:40:00 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma.git tags/for-linus
for you to fetch changes up to 9091e3b59f2bef11c0a841096327565ae0ca220b:
RDMA/core: Fix user CQ creation for drivers without create_cq (2026-04-17 12:16:00 -0300)
----------------------------------------------------------------
RDMA v7.1 merge window
Usual collection of driver changes, more core infrastructure updates that
typical this cycle:
- Minor cleanups and kernel-doc fixes in bnxt_re, hns, rdmavt, efa, ocrdma,
erdma, rtrs, hfi1, ionic, and pvrdma
- New udata validation framework and driver updates
- Modernize CQ creation interface in mlx4 and mlx5, manage CQ umem in core
- Promote UMEM to a core component, split out DMA block iterator logic
- Introduce FRMR pools with aging, statistics, pinned handles, and netlink
control and use it in mlx5
- Add PCIe TLP emulation support in mlx5
- Extend umem to work with revocable pinned dmabuf's and use it in irdma
- More net namespace improvements for rxe
- GEN4 hardware support in irdma
- First steps to MW and UC support in mana_ib
- Support for CQ umem and doorbells in bnxt_re
- Drop opa_vnic driver from hfi1
- Fixes:
IB/core zero dmac neighbor resolution race
GID table memory free
rxe pad/ICRC validation and r_key async errors
mlx4 external umem for CQ
umem DMA attributes on unmap
mana_ib RX steering on RSS QP destroy
----------------------------------------------------------------
Chen Zhao (1):
IB/core: Fix zero dmac race in neighbor resolution
Cheng Xu (1):
RDMA/erdma: Remove numa_node from struct erdma_devattr
Chiara Meiohas (1):
RDMA/mlx5: Move device async_ctx initialization
Dean Luick (4):
RDMA/OPA: Update OPA link speed list
RDMA/rdmavt: Add ucontext alloc/dealloc passthrough
RDMA/rdmavt: Correct multi-port QP iteration
RDMA/rdmavt: Add driver mmap callback
Dennis Dalessandro (1):
RDMA/hfi1: Remove opa_vnic
Evan Green (1):
RDMA/rxe: Generate async error for r_key violations
Florian Westphal (1):
RDMA/core: Prefer NLA_NUL_STRING
Jacob Moroni (6):
RDMA/umem: Add ib_umem_dmabuf_get_pinned_and_lock helper
RDMA/umem: Move umem dmabuf revoke logic into helper function
RDMA/umem: Add pinned revocable dmabuf import interface
RDMA/umem: Add helpers for umem dmabuf revoke lock
RDMA/irdma: Add support for revocable pinned dmabuf import
RDMA/irdma: Add support for GEN4 hardware
Jason Gunthorpe (29):
RDMA: Use copy_struct_from_user() instead of open coding
RDMA/core: Add rdma_udata_to_dev()
RDMA: Add ib_copy_validate_udata_in()
RDMA: Add ib_copy_validate_udata_in_cm()
RDMA: Add ib_respond_udata()
RDMA: Add ib_is_udata_in_empty()
RDMA: Provide documentation about the uABI compatibility rules
RDMA/bnxt_re: Add compatibility checks to the uapi path
RDMA/bnxt_re: Add compatibility checks to the uapi path for no data
RDMA/bnxt_re: Add missing comp_mask validation
RDMA/bnxt_re: Use ib_respond_udata()
RDMA/bnxt_re: Use ib_respond_empty_udata()
RDMA: Add IB_UVERBS_CORE_SUPPORT_ROBUST_UDATA
RDMA: Consolidate patterns with offsetofend() to ib_copy_validate_udata_in()
RDMA: Consolidate patterns with offsetof() to ib_copy_validate_udata_in()
RDMA: Consolidate patterns with sizeof() to ib_copy_validate_udata_in()
RDMA: Use ib_copy_validate_udata_in() for implicit full structs
RDMA/pvrdma: Use ib_copy_validate_udata_in() for srq
RDMA/mlx5: Use ib_copy_validate_udata_in() for SRQ
RDMA/mlx5: Use ib_copy_validate_udata_in() for MW
RDMA/mlx4: Use ib_copy_validate_udata_in()
RDMA/mlx4: Use ib_copy_validate_udata_in() for QP
RDMA/hns: Use ib_copy_validate_udata_in()
RDMA: Use ib_copy_validate_udata_in_cm() for zero comp_mask
RDMA/mlx5: Pull comp_mask validation into ib_copy_validate_udata_in_cm()
RDMA/hns: Add missing comp_mask check in create_qp
RDMA/irdma: Add missing comp_mask check in alloc_ucontext
RDMA: Remove redundant = {} for udata req structs
RDMA/hns: Remove the duplicate calls to ib_copy_validate_udata_in()
Jay Bhat (1):
RDMA/irdma: Provide scratch buffers to firmware for internal use
Kai Zen (1):
RDMA/ionic: bound node_desc sysfs read with %.64s
Kalesh AP (3):
RDMA/bnxt_re: Move the UAPI methods to a dedicated file
RDMA/bnxt_re: Refactor bnxt_qplib_create_qp() function
RDMA/bnxt_re: Support doorbell extensions
Kexin Sun (2):
RDMA/uverbs: Update outdated reference to remove_commit_idr_uobject()
RDMA: Remove outdated comments referencing hfi1_destroy_qp()
Konstantin Taranov (2):
RDMA/mana_ib: cleanup the usage of mana_gd_send_request()
RDMA/mana_ib: Support memory windows
Leon Romanovsky (27):
RDMA: Move DMA block iterator logic into dedicated files
RDMA/umem: Allow including ib_umem header from any location
RDMA/umem: Remove unnecessary includes and defines from ib_umem header
RDMA/core: Promote UMEM to a core component
RDMA/core: Manage CQ umem in core code
RDMA/efa: Rely on CPU address in create‑QP
RDMA/core: Prepare create CQ path for API unification
RDMA/core: Reject zero CQE count
RDMA/efa: Remove check for zero CQE count
RDMA/mlx5: Save 4 bytes in CQ structure
RDMA/mlx5: Provide a modern CQ creation interface
RDMA/mlx4: Inline mlx4_ib_get_cq_umem into callers
RDMA/mlx4: Introduce a modern CQ creation interface
RDMA/mlx4: Remove unused create_flags field from CQ structure
RDMA: Complete k[z|m|c]alloc-to-k[z|m]alloc_obj conversion
RDMA/core: Delete not-implemented get_vector_affinity
Add support for TLP emulation
RDMA/core: Remove unused ib_resize_cq() implementation
RDMA: Clarify that CQ resize is a user‑space verb
RDMA: Properly propagate the number of CQEs as unsigned int
RDMA/bnxt_re: Simplify bnxt_re_init_depth() callers and implementation
RDMA/bnxt_re: Remove unnecessary checks in kernel CQ creation path
RDMA/bnxt_re: Replace kcalloc() with kzalloc_objs()
RDMA/bnxt_re: Clean up uverbs CQ creation path
Merge branch 'master' into rdma-next
RDMA/umem: Use consistent DMA attributes when unmapping entries
RDMA/mlx4: Restrict external umem for CQ when copy_to_user() is used
Long Li (1):
RDMA/mana_ib: Disable RX steering on RSS QP destroy
Maher Sanalla (3):
RDMA/mlx5: Refactor VAR table to use region abstraction
RDMA/mlx5: Add TLP VAR region support and infrastructure
RDMA/mlx5: Add support for TLP VAR allocation
Marco Crivellari (2):
RDMA/rtrs: add WQ_PERCPU to alloc_workqueue users
RDMA/rxe: Replace use of system_unbound_wq with rxe_wq
Michael Guralnik (10):
IB/core: Introduce FRMR pools
RDMA/core: Add aging to FRMR pools
RDMA/core: Add FRMR pools statistics
RDMA/core: Add pinned handles to FRMR pools
RDMA/mlx5: Switch from MR cache to FRMR pools
net/mlx5: Drop MR cache related code
RDMA/nldev: Add command to get FRMR pools
RDMA/core: Add netlink command to modify FRMR aging
RDMA/nldev: Add command to set pinned FRMR handles
RDMA/nldev: Expose kernel-internal FRMR pools in netlink
Michael Margolin (2):
RDMA/efa: Rename alloc_ucontext comp_mask to supported_caps
RDMA/core: Fix user CQ creation for drivers without create_cq
Randy Dunlap (4):
IB/cache: avoid kernel-doc warnings
RDMA/umem: fix kernel-doc warnings
RDMA/iwcm: fix some kernel-doc issues in iw_cm.h
RDMA/restrack: fix kernel-doc indicator
Rosen Penev (3):
RDMA/ocrdma: kzalloc_objs to kzalloc_flex
IB/hfi1: kzalloc to kzalloc_flex
RDMA/core: Use kzalloc_flex for GID table
Sriharsha Basavapatna (3):
RDMA/bnxt_re: Refactor bnxt_re_create_cq()
RDMA/bnxt_re: Separate kernel and user CQ creation paths
RDMA/bnxt_re: Support application specific CQs
Yonatan Nachum (3):
RDMA/efa: Rename admin queue attributes struct name for extendability
RDMA/efa: Expose new extended max inline buff size
RDMA/efa: Use extended inline buff size for inline validation
Zhu Yanjun (4):
RDMA/nldev: Add dellink function pointer
RDMA/rxe: Add net namespace support for IPv4/IPv6 sockets
RDMA/rxe: Support RDMA link creation and destruction per net namespace
RDMA/rxe: Add testcase for net namespace rxe
hkbinbin (1):
RDMA/rxe: Validate pad and ICRC before payload_size() in rxe_rcv
zhenwei pi (1):
RDMA/core: Fix memory free for GID table
Documentation/driver-api/infiniband.rst | 15 -
Documentation/infiniband/index.rst | 1 -
Documentation/infiniband/opa_vnic.rst | 159 ---
.../translations/zh_CN/infiniband/index.rst | 1 -
.../translations/zh_CN/infiniband/opa_vnic.rst | 156 ---
MAINTAINERS | 8 +-
drivers/infiniband/Kconfig | 2 -
drivers/infiniband/core/Makefile | 6 +-
drivers/infiniband/core/addr.c | 3 +
drivers/infiniband/core/cache.c | 15 +-
drivers/infiniband/core/cq.c | 3 +
drivers/infiniband/core/device.c | 6 +-
drivers/infiniband/core/frmr_pools.c | 547 +++++++++
drivers/infiniband/core/frmr_pools.h | 63 ++
drivers/infiniband/core/ib_core_uverbs.c | 27 +
drivers/infiniband/core/iter.c | 43 +
drivers/infiniband/core/iwpm_msg.c | 6 +-
drivers/infiniband/core/nldev.c | 298 +++++
drivers/infiniband/core/rdma_core.c | 4 +-
drivers/infiniband/core/rdma_core.h | 3 +
drivers/infiniband/core/umem.c | 15 +-
drivers/infiniband/core/umem_dmabuf.c | 138 ++-
drivers/infiniband/core/uverbs_cmd.c | 40 +-
drivers/infiniband/core/uverbs_ioctl.c | 87 ++
drivers/infiniband/core/uverbs_std_types_cq.c | 41 +-
drivers/infiniband/core/uverbs_std_types_device.c | 8 +
drivers/infiniband/core/verbs.c | 59 +-
drivers/infiniband/hw/bnxt_re/Makefile | 2 +-
drivers/infiniband/hw/bnxt_re/ib_verbs.c | 894 ++++++++-------
drivers/infiniband/hw/bnxt_re/ib_verbs.h | 25 +-
drivers/infiniband/hw/bnxt_re/main.c | 4 +-
drivers/infiniband/hw/bnxt_re/qplib_fp.c | 305 ++----
drivers/infiniband/hw/bnxt_re/qplib_fp.h | 8 +
drivers/infiniband/hw/bnxt_re/qplib_res.c | 47 +-
drivers/infiniband/hw/bnxt_re/qplib_res.h | 10 +
drivers/infiniband/hw/bnxt_re/uapi.c | 469 ++++++++
drivers/infiniband/hw/cxgb4/mem.c | 2 +-
drivers/infiniband/hw/efa/efa.h | 6 +-
drivers/infiniband/hw/efa/efa_admin_cmds_defs.h | 23 +-
drivers/infiniband/hw/efa/efa_com_cmd.c | 55 +-
drivers/infiniband/hw/efa/efa_com_cmd.h | 3 +-
drivers/infiniband/hw/efa/efa_main.c | 3 +-
drivers/infiniband/hw/efa/efa_verbs.c | 123 +--
drivers/infiniband/hw/erdma/erdma.h | 1 -
drivers/infiniband/hw/erdma/erdma_eq.c | 3 +-
drivers/infiniband/hw/erdma/erdma_main.c | 1 -
drivers/infiniband/hw/erdma/erdma_verbs.c | 8 +-
drivers/infiniband/hw/hfi1/Makefile | 4 +-
drivers/infiniband/hw/hfi1/aspm.c | 2 +-
drivers/infiniband/hw/hfi1/chip.c | 54 +-
drivers/infiniband/hw/hfi1/chip.h | 2 -
drivers/infiniband/hw/hfi1/driver.c | 13 +-
drivers/infiniband/hw/hfi1/hfi.h | 20 -
drivers/infiniband/hw/hfi1/init.c | 4 +-
drivers/infiniband/hw/hfi1/mad.c | 1 -
drivers/infiniband/hw/hfi1/msix.c | 4 +-
drivers/infiniband/hw/hfi1/netdev.h | 8 +-
drivers/infiniband/hw/hfi1/netdev_rx.c | 3 +-
drivers/infiniband/hw/hfi1/qp.c | 1 -
drivers/infiniband/hw/hfi1/user_exp_rcv.c | 14 +-
drivers/infiniband/hw/hfi1/user_exp_rcv.h | 2 +-
drivers/infiniband/hw/hfi1/verbs.c | 2 -
drivers/infiniband/hw/hfi1/vnic.h | 126 ---
drivers/infiniband/hw/hfi1/vnic_main.c | 615 -----------
drivers/infiniband/hw/hfi1/vnic_sdma.c | 282 -----
drivers/infiniband/hw/hns/hns_roce_alloc.c | 2 +-
drivers/infiniband/hw/hns/hns_roce_cq.c | 16 +-
drivers/infiniband/hw/hns/hns_roce_hem.c | 7 +-
drivers/infiniband/hw/hns/hns_roce_main.c | 6 +-
drivers/infiniband/hw/hns/hns_roce_qp.c | 23 +-
drivers/infiniband/hw/hns/hns_roce_srq.c | 54 +-
drivers/infiniband/hw/ionic/ionic_controlpath.c | 6 +-
drivers/infiniband/hw/ionic/ionic_ibdev.c | 2 +-
drivers/infiniband/hw/ionic/ionic_ibdev.h | 2 +-
drivers/infiniband/hw/irdma/ctrl.c | 44 +-
drivers/infiniband/hw/irdma/defs.h | 4 +
drivers/infiniband/hw/irdma/hw.c | 29 +-
drivers/infiniband/hw/irdma/ig3rdma_hw.c | 1 -
drivers/infiniband/hw/irdma/irdma.h | 1 +
drivers/infiniband/hw/irdma/main.h | 2 +-
drivers/infiniband/hw/irdma/type.h | 2 +
drivers/infiniband/hw/irdma/user.h | 4 +-
drivers/infiniband/hw/irdma/verbs.c | 121 +-
drivers/infiniband/hw/mana/cq.c | 11 +-
drivers/infiniband/hw/mana/device.c | 3 +
drivers/infiniband/hw/mana/main.c | 141 +--
drivers/infiniband/hw/mana/mana_ib.h | 10 +-
drivers/infiniband/hw/mana/mr.c | 92 +-
drivers/infiniband/hw/mana/qp.c | 69 +-
drivers/infiniband/hw/mana/wq.c | 12 +-
drivers/infiniband/hw/mlx4/cq.c | 258 +++--
drivers/infiniband/hw/mlx4/main.c | 14 +-
drivers/infiniband/hw/mlx4/mlx4_ib.h | 8 +-
drivers/infiniband/hw/mlx4/mr.c | 1 +
drivers/infiniband/hw/mlx4/qp.c | 82 +-
drivers/infiniband/hw/mlx4/srq.c | 5 +-
drivers/infiniband/hw/mlx5/cq.c | 183 ++--
drivers/infiniband/hw/mlx5/devx.c | 6 +-
drivers/infiniband/hw/mlx5/dm.c | 2 +-
drivers/infiniband/hw/mlx5/fs.c | 6 +-
drivers/infiniband/hw/mlx5/main.c | 172 ++-
drivers/infiniband/hw/mlx5/mem.c | 1 +
drivers/infiniband/hw/mlx5/mlx5_ib.h | 102 +-
drivers/infiniband/hw/mlx5/mr.c | 1158 ++++----------------
drivers/infiniband/hw/mlx5/odp.c | 19 -
drivers/infiniband/hw/mlx5/qos.c | 2 +-
drivers/infiniband/hw/mlx5/qp.c | 68 +-
drivers/infiniband/hw/mlx5/srq.c | 17 +-
drivers/infiniband/hw/mlx5/umr.c | 1 +
drivers/infiniband/hw/mlx5/umr.h | 1 +
drivers/infiniband/hw/mthca/mthca_provider.c | 36 +-
drivers/infiniband/hw/ocrdma/ocrdma.h | 2 +-
drivers/infiniband/hw/ocrdma/ocrdma_main.c | 2 +-
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 56 +-
drivers/infiniband/hw/ocrdma/ocrdma_verbs.h | 2 +-
drivers/infiniband/hw/qedr/verbs.c | 44 +-
drivers/infiniband/hw/usnic/usnic_ib_verbs.c | 2 +-
drivers/infiniband/hw/vmw_pvrdma/pvrdma.h | 2 +-
drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c | 5 +-
drivers/infiniband/hw/vmw_pvrdma/pvrdma_misc.c | 3 +-
drivers/infiniband/hw/vmw_pvrdma/pvrdma_qp.c | 6 +-
drivers/infiniband/hw/vmw_pvrdma/pvrdma_srq.c | 6 +-
drivers/infiniband/sw/rdmavt/cq.c | 4 +-
drivers/infiniband/sw/rdmavt/cq.h | 2 +-
drivers/infiniband/sw/rdmavt/mcast.c | 1 -
drivers/infiniband/sw/rdmavt/mmap.c | 22 +-
drivers/infiniband/sw/rdmavt/qp.c | 2 +-
drivers/infiniband/sw/rdmavt/vt.c | 10 +-
drivers/infiniband/sw/rxe/Makefile | 3 +-
drivers/infiniband/sw/rxe/rxe.c | 38 +-
drivers/infiniband/sw/rxe/rxe.h | 2 +
drivers/infiniband/sw/rxe/rxe_cq.c | 31 -
drivers/infiniband/sw/rxe/rxe_loc.h | 3 -
drivers/infiniband/sw/rxe/rxe_net.c | 144 ++-
drivers/infiniband/sw/rxe/rxe_net.h | 9 +-
drivers/infiniband/sw/rxe/rxe_ns.c | 124 +++
drivers/infiniband/sw/rxe/rxe_ns.h | 26 +
drivers/infiniband/sw/rxe/rxe_odp.c | 2 +-
drivers/infiniband/sw/rxe/rxe_recv.c | 3 +-
drivers/infiniband/sw/rxe/rxe_resp.c | 56 +-
drivers/infiniband/sw/rxe/rxe_task.c | 2 +-
drivers/infiniband/sw/rxe/rxe_verbs.c | 33 +-
drivers/infiniband/sw/rxe/rxe_verbs.h | 1 +
drivers/infiniband/sw/siw/siw_verbs.c | 6 +-
drivers/infiniband/ulp/Makefile | 1 -
drivers/infiniband/ulp/opa_vnic/Kconfig | 9 -
drivers/infiniband/ulp/opa_vnic/Makefile | 9 -
drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.c | 513 ---------
drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h | 524 ---------
drivers/infiniband/ulp/opa_vnic/opa_vnic_ethtool.c | 183 ----
.../infiniband/ulp/opa_vnic/opa_vnic_internal.h | 329 ------
drivers/infiniband/ulp/opa_vnic/opa_vnic_netdev.c | 400 -------
drivers/infiniband/ulp/opa_vnic/opa_vnic_vema.c | 1056 ------------------
.../infiniband/ulp/opa_vnic/opa_vnic_vema_iface.c | 390 -------
drivers/infiniband/ulp/rtrs/rtrs-clt.c | 2 +-
drivers/infiniband/ulp/rtrs/rtrs-srv.c | 2 +-
drivers/net/ethernet/mellanox/mlx5/core/main.c | 67 +-
drivers/net/ethernet/microsoft/mana/mana_en.c | 11 +-
include/linux/mlx5/driver.h | 11 -
include/net/mana/gdma.h | 5 +
include/net/mana/mana.h | 1 +
include/rdma/frmr_pools.h | 39 +
include/rdma/ib_cache.h | 4 +-
include/rdma/ib_umem.h | 66 +-
include/rdma/ib_verbs.h | 194 ++--
include/rdma/iter.h | 88 ++
include/rdma/iw_cm.h | 14 +-
include/rdma/opa_port_info.h | 8 +-
include/rdma/opa_vnic.h | 96 --
include/rdma/rdma_netlink.h | 2 +
include/rdma/rdma_vt.h | 10 +
include/rdma/restrack.h | 4 +-
include/rdma/uverbs_ioctl.h | 101 ++
include/uapi/rdma/bnxt_re-abi.h | 36 +-
include/uapi/rdma/efa-abi.h | 11 +-
include/uapi/rdma/ib_user_ioctl_verbs.h | 1 +
include/uapi/rdma/mlx5_user_ioctl_cmds.h | 1 +
include/uapi/rdma/mlx5_user_ioctl_verbs.h | 4 +
include/uapi/rdma/rdma_netlink.h | 22 +
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/rdma/Makefile | 7 +
tools/testing/selftests/rdma/config | 3 +
tools/testing/selftests/rdma/rxe_ipv6.sh | 63 ++
.../selftests/rdma/rxe_rping_between_netns.sh | 85 ++
.../selftests/rdma/rxe_socket_with_netns.sh | 76 ++
.../selftests/rdma/rxe_test_NETDEV_UNREGISTER.sh | 63 ++
186 files changed, 4726 insertions(+), 8149 deletions(-)
(diffstat from tag for-linus-merged)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net] can: j1939: fix lockless local-destination check
From: Oleksij Rempel @ 2026-04-20 12:18 UTC (permalink / raw)
To: Shuhao Fu
Cc: Robin van der Gracht, linux-can, kernel, Oliver Hartkopp,
Marc Kleine-Budde, linux-kernel
In-Reply-To: <20260419140614.GA4041240@chcpu16>
Hi,
On Sun, Apr 19, 2026 at 10:06:14PM +0800, Shuhao Fu wrote:
> j1939_priv.ents[].nusers is documented as protected by priv->lock, and
> its updates already happen under that lock. j1939_can_recv() also reads
> it under read_lock_bh(). However, j1939_session_skb_queue() and
> j1939_tp_send() still read priv->ents[da].nusers without taking the
> lock.
>
> Those transport-side checks decide whether to set J1939_ECU_LOCAL_DST, so
> they can race with j1939_local_ecu_get() and j1939_local_ecu_put() while
> userspace is binding or releasing sockets concurrently with TP traffic.
> This can misclassify TP/ETP sessions as local or remote and take the wrong
> transport path.
>
> Fix both transport paths by routing the destination-locality check through
> a helper that reads ents[].nusers under read_lock_bh(&priv->lock).
>
> Fixes: 9d71dd0c7009 ("can: add support of SAE J1939 protocol")
> Signed-off-by: Shuhao Fu <sfual@cse.ust.hk>
Thank you for your work. LGTM.
Tested-by: Oleksij Rempel <o.rempel@pengutronix.de>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* Re: [PATCH v3 2/2] drm/bridge: This patch add new DRM bridge driver for LT9611C(EX/UXD) chip
From: Dmitry Baryshkov @ 2026-04-20 12:18 UTC (permalink / raw)
To: syyang
Cc: robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
maarten.lankhorst, rfoss, mripard, Laurent.pinchart, tzimmermann,
jonas, jernej.skrabec, devicetree, dri-devel, linux-kernel,
yangsunyun1993, xmzhu
In-Reply-To: <20260420061644.1251070-3-syyang@lontium.com>
On Mon, Apr 20, 2026 at 02:16:44PM +0800, syyang@lontium.com wrote:
> From: Sunyun Yang <syyang@lontium.com>
>
> LT9611C(EX/UXD) is a high performance Single/Dual-Port MIPI to
> HDMI 1.4/2.0 converter:
>
> -Single/Dual-port MIPI DSI Receiver
> 1. Compliantwith D-PHY1.2&DSI-2 1.0
> 2. 1/2configurable ports
> 3. 1 clock lane and 1/2/3/4 configurable data lanes per port
> 4. 80Mbps~2.5Gbps per data lane
> 5. Support RGB666, loosely RGB666, RGB888, RGB565,16-bit YCbCr4:2:2
>
> -HDMI 1.4/2.0 Transmitter
> 1.Data rate up to 6Gbps
> 2.Support HDCP1.4/2.3
> 3.Support CEC,HDR10
> 4.Support lane swap
>
> -audio
> 1.sample rates of 32~192 KHz and sample sizes
> of 16~24 bits
> 2.SPDIF interface supports PCM, Dolbydigital, DTS digital audio
> at up to 192KHz frame rate
>
> -Miscellaneous
> 1.CSC:RGB<->YUV444<->YUV422
>
> Signed-off-by: Sunyun Yang <syyang@lontium.com>
> ---
> drivers/gpu/drm/bridge/Kconfig | 18 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/lontium-lt9611c.c | 1365 ++++++++++++++++++++++
> 3 files changed, 1384 insertions(+)
> create mode 100755 drivers/gpu/drm/bridge/lontium-lt9611c.c
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index c3209b0f4678..32b85a2a65d9 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -177,6 +177,24 @@ config DRM_LONTIUM_LT9611
> HDMI signals
> Please say Y if you have such hardware.
>
> +config DRM_LONTIUM_LT9611C
> + tristate "Lontium LT9611C DSI/HDMI bridge"
> + select SND_SOC_HDMI_CODEC if SND_SOC
> + depends on OF
> + select CRC8
> + select FW_LOADER
> + select DRM_PANEL_BRIDGE
> + select DRM_KMS_HELPER
> + select DRM_MIPI_DSI
> + select DRM_DISPLAY_HELPER
> + select DRM_DISPLAY_HDMI_STATE_HELPER
> + select REGMAP_I2C
> + help
> + Driver for Lontium DSI to HDMI bridge
> + chip driver that converts dual DSI and I2S to
> + HDMI signals
> + Please say Y if you have such hardware.
> +
> config DRM_LONTIUM_LT9611UXC
> tristate "Lontium LT9611UXC DSI/HDMI bridge"
> select SND_SOC_HDMI_CODEC if SND_SOC
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index beab5b695a6e..92688be9692f 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -16,6 +16,7 @@ obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
> obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
> obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
> obj-$(CONFIG_DRM_LONTIUM_LT9611) += lontium-lt9611.o
> +obj-$(CONFIG_DRM_LONTIUM_LT9611C) += lontium-lt9611c.o
> obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o
> obj-$(CONFIG_DRM_LONTIUM_LT8713SX) += lontium-lt8713sx.o
> obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
> diff --git a/drivers/gpu/drm/bridge/lontium-lt9611c.c b/drivers/gpu/drm/bridge/lontium-lt9611c.c
> new file mode 100755
> index 000000000000..a6d11d0bddf5
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/lontium-lt9611c.c
> @@ -0,0 +1,1365 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2026 Lontium Semiconductor, Inc.
> + */
> +
> +#include <linux/crc8.h>
> +#include <linux/firmware.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/media-bus-format.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_connector.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_modes.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include <drm/display/drm_hdmi_audio_helper.h>
> +#include <drm/display/drm_hdmi_state_helper.h>
> +#include <sound/hdmi-codec.h>
> +
> +#define FW_SIZE (64 * 1024)
> +#define LT_PAGE_SIZE 256
> +#define FW_FILE "LT9611C.bin"
lt9611c_fw.bin
> +#define LT9611C_CRC_POLYNOMIAL 0x31
> +#define LT9611C_PAGE_CONTROL 0xff
> +
> +struct lt9611c {
> + struct device *dev;
> + struct i2c_client *client;
> + struct drm_bridge bridge;
> + struct drm_bridge *next_bridge;
Use drm_bridge::next_bridge instead.
> + struct regmap *regmap;
> + /* Protects all accesses to registers by stopping the on-chip MCU */
> + struct mutex ocm_lock;
> + struct work_struct work;
> + struct device_node *dsi0_node;
> + struct device_node *dsi1_node;
> + struct mipi_dsi_device *dsi0;
> + struct mipi_dsi_device *dsi1;
> + struct gpio_desc *reset_gpio;
> + struct regulator_bulk_data supplies[2];
> + u32 chip_type;
Define a enum. Having if (chip_type == 2) doesn't help readability.
> + const struct firmware *fw;
Please drop it from the global struct. It is not necessary once the
bridge is up and running.
> + int fw_version;
> + u8 fw_crc;
> + bool hdmi_connected;
You've lost the imortant comment here.
> +};
> +
> +DECLARE_CRC8_TABLE(lt9611c_crc8_table);
> +
> +static const struct regmap_range_cfg lt9611c_ranges[] = {
> + {
> + .name = "register_range",
> + .range_min = 0,
> + .range_max = 0xffff,
> + .selector_reg = LT9611C_PAGE_CONTROL,
> + .selector_mask = 0xff,
> + .selector_shift = 0,
> + .window_start = 0,
> + .window_len = 0x100,
> + },
> +};
> +
> +static const struct regmap_config lt9611c_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = 0xffff,
> + .ranges = lt9611c_ranges,
> + .num_ranges = ARRAY_SIZE(lt9611c_ranges),
> +};
> +
> +static int lt9611c_read_write_flow(struct lt9611c *lt9611c, u8 *params,
> + unsigned int param_count, u8 *return_buffer,
> + unsigned int return_count)
> +{
> + int count, i;
> + unsigned int temp;
> +
> + regmap_write(lt9611c->regmap, 0xe0de, 0x01);
> +
> + count = 0;
> + do {
> + regmap_read(lt9611c->regmap, 0xe0ae, &temp);
> + usleep_range(1000, 2000);
> + count++;
> + } while (count < 100 && temp != 0x01);
read_poll_timeout()
> +
> + if (temp != 0x01)
> + return -1;
-ETIMEDOUT
> +
> + for (i = 0; i < param_count; i++) {
> + if (i > 0xdd - 0xb0)
0xe0dd - 0xe0b0 at least.
> + break;
> +
> + regmap_write(lt9611c->regmap, 0xe0b0 + i, params[i]);
> + }
> +
> + regmap_write(lt9611c->regmap, 0xe0de, 0x02);
> +
> + count = 0;
> + do {
> + regmap_read(lt9611c->regmap, 0xe0ae, &temp);
> + usleep_range(1000, 2000);
> + count++;
> + } while (count < 100 && temp != 0x02);
Again, read_poll_timeout().
> +
> + if (temp != 0x02)
> + return -2;
-ETIMEDOUT
> +
> + regmap_bulk_read(lt9611c->regmap, 0xe085, return_buffer, return_count);
> +
> + return 0;
> +}
> +
> +static int lt9611c_prepare_firmware_data(struct lt9611c *lt9611c)
> +{
> + struct device *dev = lt9611c->dev;
> + int ret;
> + u8 *buffer;
> + size_t total_size = FW_SIZE - 1;
> +
> + ret = request_firmware(<9611c->fw, FW_FILE, dev);
> + if (ret) {
> + dev_err(dev, "failed load file '%s', error type %d\n", FW_FILE, ret);
> + return -EPROBE_DEFER;
> + }
> +
> + if (lt9611c->fw->size > total_size) {
> + dev_err(dev, "firmware too large (%zu > %zu)\n", lt9611c->fw->size, total_size);
> + release_firmware(lt9611c->fw);
> + lt9611c->fw = NULL;
> + return -EINVAL;
> + }
> +
> + dev_dbg(dev, "firmware size: %zu bytes\n", lt9611c->fw->size);
> +
> + buffer = kzalloc(total_size, GFP_KERNEL);
> + if (!buffer) {
> + release_firmware(lt9611c->fw);
> + lt9611c->fw = NULL;
> + return -ENOMEM;
> + }
> +
> + memset(buffer, 0xff, total_size);
> + memcpy(buffer, lt9611c->fw->data, lt9611c->fw->size);
> +
> + lt9611c->fw_crc = crc8(lt9611c_crc8_table, buffer, total_size, 0);
> +
> + dev_dbg(dev, "firmware crc: 0x%02x\n", lt9611c->fw_crc);
> +
> + kfree(buffer);
> + return 0;
> +}
> +
> +static void lt9611c_config_parameters(struct lt9611c *lt9611c)
> +{
> + const struct reg_sequence seq_write_paras[] = {
> + REG_SEQ0(0xe0ee, 0x01),
> + REG_SEQ0(0xe103, 0x3f), //fifo rst
> + REG_SEQ0(0xe103, 0xff),
> + REG_SEQ0(0xe05e, 0xc1),
> + REG_SEQ0(0xe058, 0x00),
> + REG_SEQ0(0xe059, 0x50),
> + REG_SEQ0(0xe05a, 0x10),
> + REG_SEQ0(0xe05a, 0x00),
> + REG_SEQ0(0xe058, 0x21),
> + };
> +
> + regmap_multi_reg_write(lt9611c->regmap, seq_write_paras, ARRAY_SIZE(seq_write_paras));
> +}
> +
> +static void lt9611c_wren(struct lt9611c *lt9611c)
> +{
> + regmap_write(lt9611c->regmap, 0xe05a, 0x04);
> + regmap_write(lt9611c->regmap, 0xe05a, 0x00);
> +}
> +
> +static void lt9611c_wrdi(struct lt9611c *lt9611c)
> +{
> + regmap_write(lt9611c->regmap, 0xe05a, 0x08);
> + regmap_write(lt9611c->regmap, 0xe05a, 0x00);
> +}
> +
> +static void lt9611c_erase_op(struct lt9611c *lt9611c, u32 addr)
> +{
> + const struct reg_sequence seq_write[] = {
> + REG_SEQ0(0xe0ee, 0x01),
> + REG_SEQ0(0xe05a, 0x04),
> + REG_SEQ0(0xe05a, 0x00),
> + REG_SEQ0(0xe05b, (addr >> 16) & 0xff),
> + REG_SEQ0(0xe05c, (addr >> 8) & 0xff),
> + REG_SEQ0(0xe05d, addr & 0xff),
> + REG_SEQ0(0xe05a, 0x01),
> + REG_SEQ0(0xe05a, 0x00),
> + };
> +
> + regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
> +}
> +
> +static void read_flash_reg_status(struct lt9611c *lt9611c, unsigned int *status)
> +{
> + const struct reg_sequence seq_write[] = {
> + REG_SEQ0(0xe103, 0x3f),
> + REG_SEQ0(0xe103, 0xff),
> + REG_SEQ0(0xe05e, 0x40),
> + REG_SEQ0(0xe056, 0x05),
> + REG_SEQ0(0xe055, 0x25),
> + REG_SEQ0(0xe055, 0x01),
> + REG_SEQ0(0xe058, 0x21),
> + };
> +
> + regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
> +
> + regmap_read(lt9611c->regmap, 0xe05f, status);
> +}
> +
> +static void lt9611c_crc_to_sram(struct lt9611c *lt9611c)
> +{
> + const struct reg_sequence seq_write[] = {
> + REG_SEQ0(0xe051, 0x00),
> + REG_SEQ0(0xe055, 0xc0),
> + REG_SEQ0(0xe055, 0x80),
> + REG_SEQ0(0xe05e, 0xc0),
> + REG_SEQ0(0xe058, 0x21),
> + };
> +
> + regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
> +}
> +
> +static void lt9611c_data_to_sram(struct lt9611c *lt9611c)
> +{
> + const struct reg_sequence seq_write[] = {
> + REG_SEQ0(0xe051, 0xff),
> + REG_SEQ0(0xe055, 0x80),
> + REG_SEQ0(0xe05e, 0xc0),
> + REG_SEQ0(0xe058, 0x21),
> + };
> +
> + regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
> +}
> +
> +static void lt9611c_sram_to_flash(struct lt9611c *lt9611c, size_t addr)
> +{
> + const struct reg_sequence seq_write[] = {
> + REG_SEQ0(0xe05b, (addr >> 16) & 0xff),
> + REG_SEQ0(0xe05c, (addr >> 8) & 0xff),
> + REG_SEQ0(0xe05d, addr & 0xff),
> + REG_SEQ0(0xe05a, 0x30),
> + REG_SEQ0(0xe05a, 0x00),
> + };
> +
> + regmap_multi_reg_write(lt9611c->regmap, seq_write, ARRAY_SIZE(seq_write));
> +}
> +
> +static void lt9611c_block_erase(struct lt9611c *lt9611c)
> +{
> + struct device *dev = lt9611c->dev;
> + int i;
> + unsigned int block_num;
> + unsigned int flash_status = 0;
> + u32 flash_addr = 0;
> +
> + for (block_num = 0; block_num < 2; block_num++) {
> + flash_addr = (block_num * 0x008000);
> + lt9611c_erase_op(lt9611c, flash_addr);
> + msleep(100);
> + i = 0;
> + while (1) {
> + read_flash_reg_status(lt9611c, &flash_status);
> + if ((flash_status & 0x01) == 0)
> + break;
> +
> + if (i > 50)
> + break;
> +
> + i++;
> + msleep(50);
> + }
> + }
> +
> + dev_dbg(dev, "erase flash done.\n");
> +}
> +
> +static int lt9611c_write_data(struct lt9611c *lt9611c, size_t addr)
> +{
> + struct device *dev = lt9611c->dev;
> + int ret;
> + unsigned int page = 0, num = 0, i = 0;
> + size_t size, index;
> + const u8 *data;
> + u8 value;
> +
> + data = lt9611c->fw->data;
> + size = lt9611c->fw->size;
> + page = (size + LT_PAGE_SIZE - 1) / LT_PAGE_SIZE;
> + if (page * LT_PAGE_SIZE > FW_SIZE) {
> + dev_err(dev, "firmware size out of range\n");
> + return -EINVAL;
> + }
> +
> + dev_dbg(dev, "%u pages, total size %zu byte\n", page, size);
> +
> + for (num = 0; num < page; num++) {
> + lt9611c_data_to_sram(lt9611c);
> +
> + for (i = 0; i < LT_PAGE_SIZE; i++) {
> + index = num * LT_PAGE_SIZE + i;
> + value = (index < size) ? data[index] : 0xff;
> +
> + ret = regmap_write(lt9611c->regmap, 0xe059, value);
> + if (ret < 0) {
> + dev_err(dev, "write error at page %u, index %u\n", num, i);
> + return ret;
> + }
> + }
> +
> + lt9611c_wren(lt9611c);
> + lt9611c_sram_to_flash(lt9611c, addr);
> +
> + addr += LT_PAGE_SIZE;
> + }
> +
> + lt9611c_wrdi(lt9611c);
> +
> + return 0;
> +}
> +
> +static int lt9611c_write_crc(struct lt9611c *lt9611c, size_t addr)
> +{
> + struct device *dev = lt9611c->dev;
> + int ret;
> + u8 crc;
> +
> + crc = lt9611c->fw_crc;
> + lt9611c_crc_to_sram(lt9611c);
> + ret = regmap_write(lt9611c->regmap, 0xe059, crc);
> + if (ret < 0) {
> + dev_err(dev, "failed to write crc\n");
> + return ret;
> + }
> +
> + lt9611c_wren(lt9611c);
> + lt9611c_sram_to_flash(lt9611c, addr);
> + lt9611c_wrdi(lt9611c);
> +
> + dev_dbg(dev, "crc 0x%02x written to flash at addr 0x%zx\n", crc, addr);
> +
> + return 0;
> +}
> +
> +static int lt9611c_firmware_upgrade(struct lt9611c *lt9611c)
> +{
> + struct device *dev = lt9611c->dev;
> + int ret;
> +
> + ret = lt9611c_prepare_firmware_data(lt9611c);
> + if (ret < 0)
> + return ret;
> +
> + dev_dbg(dev, "starting firmware upgrade, size: %zu bytes\n", lt9611c->fw->size);
> +
> + lt9611c_config_parameters(lt9611c);
> + lt9611c_block_erase(lt9611c);
> +
> + ret = lt9611c_write_data(lt9611c, 0);
> + if (ret < 0) {
> + dev_err(dev, "failed to write firmware data\n");
> + return ret;
> + }
> +
> + release_firmware(lt9611c->fw);
> + lt9611c->fw = NULL;
> +
> + ret = lt9611c_write_crc(lt9611c, FW_SIZE - 1);
> + if (ret < 0) {
> + dev_err(dev, "failed to write firmware crc\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int lt9611c_upgrade_result(struct lt9611c *lt9611c)
> +{
> + struct device *dev = lt9611c->dev;
> + unsigned int crc_result;
> +
> + regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
> + regmap_read(lt9611c->regmap, 0xe021, &crc_result);
> +
> + if (crc_result != lt9611c->fw_crc) {
> + dev_err(dev, "lt9611c fw upgrade failed, expected crc=0x%02x, read crc=0x%02x\n",
> + lt9611c->fw_crc, crc_result);
> + return -1;
> + }
> +
> + dev_dbg(dev, "lt9611c firmware upgrade success, crc=0x%02x\n", crc_result);
> + return 0;
> +}
> +
> +static struct lt9611c *bridge_to_lt9611c(struct drm_bridge *bridge)
> +{
> + return container_of(bridge, struct lt9611c, bridge);
> +}
> +
> +/*read only*/
> +static const struct lt9611c *bridge_to_lt9611c_const(const struct drm_bridge *bridge)
> +{
> + return container_of(bridge, const struct lt9611c, bridge);
> +}
> +
> +static void lt9611c_lock(struct lt9611c *lt9611c)
> +{
> + mutex_lock(<9611c->ocm_lock);
> + regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
> +}
> +
> +static void lt9611c_unlock(struct lt9611c *lt9611c)
> +{
> + regmap_write(lt9611c->regmap, 0xe0ee, 0x00);
> + mutex_unlock(<9611c->ocm_lock);
> +}
> +
> +static irqreturn_t lt9611c_irq_thread_handler(int irq, void *dev_id)
> +{
> + struct lt9611c *lt9611c = dev_id;
> + struct device *dev = lt9611c->dev;
> + int ret;
> + unsigned int irq_status;
> + u8 cmd[5] = {0x52, 0x48, 0x31, 0x3a, 0x00};
> + u8 data[5];
> +
> + mutex_lock(<9611c->ocm_lock);
Where applicable (like here) please switch to
guard(mutex)(<9611c->ocm_lock);
> +
> + regmap_read(lt9611c->regmap, 0xe084, &irq_status);
> + if (!(irq_status & BIT(0))) {
> + mutex_unlock(<9611c->ocm_lock);
> + return IRQ_HANDLED;
> + }
> +
> + ret = lt9611c_read_write_flow(lt9611c, cmd, 5, data, 5);
Here and in all other places. Switch to ARRAY_SIZE instead of specifying
lengths directly.
> + if (ret) {
> + dev_err(dev, "failed to read HPD status\n");
> + } else {
> + lt9611c->hdmi_connected = (data[4] == 0x02);
> + dev_dbg(dev, "HDMI %s\n", lt9611c->hdmi_connected ? "connected" : "disconnected");
> + }
> +
> + schedule_work(<9611c->work);
> +
> + /*clear interrupt*/
> + regmap_write(lt9611c->regmap, 0xe0df, irq_status & BIT(0));
> + //hardware need delay
> + usleep_range(10000, 12000);
> + regmap_write(lt9611c->regmap, 0xe0df, irq_status & (~BIT(0)));
> +
> + mutex_unlock(<9611c->ocm_lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void lt9611c_hpd_work(struct work_struct *work)
> +{
> + struct lt9611c *lt9611c = container_of(work, struct lt9611c, work);
> + bool connected;
> +
> + mutex_lock(<9611c->ocm_lock);
> + connected = lt9611c->hdmi_connected;
> + mutex_unlock(<9611c->ocm_lock);
> +
> + drm_bridge_hpd_notify(<9611c->bridge,
> + connected ? connector_status_connected :
> + connector_status_disconnected);
> +}
> +
> +static void lt9611c_reset(struct lt9611c *lt9611c)
> +{
> + gpiod_set_value_cansleep(lt9611c->reset_gpio, 1);
> + msleep(20);
> +
> + gpiod_set_value_cansleep(lt9611c->reset_gpio, 0);
> + msleep(20);
> +
> + gpiod_set_value_cansleep(lt9611c->reset_gpio, 1);
> + msleep(400);
> +
> + dev_dbg(lt9611c->dev, "lt9611c reset");
> +}
> +
> +static int lt9611c_regulator_init(struct lt9611c *lt9611c)
> +{
> + struct device *dev = lt9611c->dev;
> + int ret;
> +
> + lt9611c->supplies[0].supply = "vcc";
> + lt9611c->supplies[1].supply = "vdd";
> +
> + ret = devm_regulator_bulk_get(dev, 2, lt9611c->supplies);
> +
> + return ret;
> +}
> +
> +static int lt9611c_regulator_enable(struct lt9611c *lt9611c)
> +{
> + int ret;
> +
> + ret = regulator_enable(lt9611c->supplies[0].consumer);
> + if (ret < 0)
> + return ret;
> +
> + usleep_range(5000, 10000);
> +
> + ret = regulator_enable(lt9611c->supplies[1].consumer);
> + if (ret < 0) {
> + regulator_disable(lt9611c->supplies[0].consumer);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int lt9611c_regulator_disable(struct lt9611c *lt9611c)
> +{
> + int ret;
> +
> + ret = regulator_disable(lt9611c->supplies[0].consumer);
> + if (ret < 0)
> + return ret;
> +
> + ret = regulator_disable(lt9611c->supplies[1].consumer);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> +static struct mipi_dsi_device *lt9611c_attach_dsi(struct lt9611c *lt9611c,
> + struct device_node *dsi_node)
> +{
> + const struct mipi_dsi_device_info info = { "lt9611c", 0, NULL };
> + struct mipi_dsi_device *dsi;
> + struct mipi_dsi_host *host;
> + struct device *dev = lt9611c->dev;
> + int ret;
> +
> + host = of_find_mipi_dsi_host_by_node(dsi_node);
> + if (!host) {
> + dev_err(dev, "failed to find dsi host\n");
> + return ERR_PTR(-EPROBE_DEFER);
here and futher, in the probe path use `return dev_err_probe()`.
> + }
> +
> + dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
> + if (IS_ERR(dsi)) {
> + dev_err(dev, "failed to create dsi device\n");
> + return dsi;
> + }
> +
> + dsi->lanes = 4;
> + dsi->format = MIPI_DSI_FMT_RGB888;
> + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
> + MIPI_DSI_MODE_VIDEO_HSE;
> +
> + ret = devm_mipi_dsi_attach(dev, dsi);
> + if (ret < 0) {
> + dev_err(dev, "failed to attach dsi to host\n");
> + return ERR_PTR(ret);
> + }
> +
> + return dsi;
> +}
> +
> +static int lt9611c_bridge_attach(struct drm_bridge *bridge,
> + struct drm_encoder *encoder,
> + enum drm_bridge_attach_flags flags)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> +
> + return drm_bridge_attach(encoder, lt9611c->next_bridge, bridge, flags);
> +}
> +
> +static enum drm_mode_status
> +lt9611c_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
> + const struct drm_display_mode *mode,
> + unsigned long long tmds_rate)
> +{
> + const struct lt9611c *lt9611c = bridge_to_lt9611c_const(bridge);
> +
> + if (lt9611c->chip_type == 2) {
> + /*lt9611uxd use*/
> + if (tmds_rate > 600000000)
> + return MODE_CLOCK_HIGH;
> +
> + } else {
> + if (tmds_rate > 340000000)
> + return MODE_CLOCK_HIGH;
> + }
> +
> + if (tmds_rate < 25000000)
> + return MODE_CLOCK_LOW;
> +
> + return MODE_OK;
> +}
> +
> +static void lt9611c_video_setup(struct lt9611c *lt9611c,
> + const struct drm_display_mode *mode)
> +{
> + struct device *dev = lt9611c->dev;
> + int ret;
> + u32 h_total, hactive, hsync_len, hfront_porch, hback_porch;
> + u32 v_total, vactive, vsync_len, vfront_porch, vback_porch;
> + u8 video_timing_set_cmd[26] = {0x57, 0x4d, 0x33, 0x3a};
> + u8 return_timing_set_param[3];
> + u8 framerate;
> + u8 vic = 0x00;
> +
> + mutex_lock(<9611c->ocm_lock);
> +
> + h_total = mode->htotal;
> + hactive = mode->hdisplay;
> + hsync_len = mode->hsync_end - mode->hsync_start;
> + hfront_porch = mode->hsync_start - mode->hdisplay;
> + hback_porch = mode->htotal - mode->hsync_end;
> +
> + v_total = mode->vtotal;
> + vactive = mode->vdisplay;
> + vsync_len = mode->vsync_end - mode->vsync_start;
> + vfront_porch = mode->vsync_start - mode->vdisplay;
> + vback_porch = mode->vtotal - mode->vsync_end;
> + framerate = drm_mode_vrefresh(mode);
> + vic = drm_match_cea_mode(mode);
> +
> + dev_dbg(dev, "hactive=%d, vactive=%d\n", hactive, vactive);
> + dev_dbg(dev, "framerate=%d\n", framerate);
> + dev_dbg(dev, "vic = 0x%02x\n", vic);
> +
> + video_timing_set_cmd[4] = (h_total >> 8) & 0xff;
> + video_timing_set_cmd[5] = h_total & 0xff;
> + video_timing_set_cmd[6] = (hactive >> 8) & 0xff;
> + video_timing_set_cmd[7] = hactive & 0xff;
> + video_timing_set_cmd[8] = (hfront_porch >> 8) & 0xff;
> + video_timing_set_cmd[9] = hfront_porch & 0xff;
> + video_timing_set_cmd[10] = (hsync_len >> 8) & 0xff;
> + video_timing_set_cmd[11] = hsync_len & 0xff;
> + video_timing_set_cmd[12] = (hback_porch >> 8) & 0xff;
> + video_timing_set_cmd[13] = hback_porch & 0xff;
> + video_timing_set_cmd[14] = (v_total >> 8) & 0xff;
> + video_timing_set_cmd[15] = v_total & 0xff;
> + video_timing_set_cmd[16] = (vactive >> 8) & 0xff;
> + video_timing_set_cmd[17] = vactive & 0xFF;
> + video_timing_set_cmd[18] = (vfront_porch >> 8) & 0xff;
> + video_timing_set_cmd[19] = vfront_porch & 0xff;
> + video_timing_set_cmd[20] = (vsync_len >> 8) & 0xff;
> + video_timing_set_cmd[21] = vsync_len & 0xff;
> + video_timing_set_cmd[22] = (vback_porch >> 8) & 0xff;
> + video_timing_set_cmd[23] = vback_porch & 0xff;
> + video_timing_set_cmd[24] = framerate;
> + video_timing_set_cmd[25] = vic;
> +
> + ret = lt9611c_read_write_flow(lt9611c,
> + video_timing_set_cmd, 26,
> + return_timing_set_param, 3);
> + if (ret)
> + dev_err(dev, "video set failed\n");
> + mutex_unlock(<9611c->ocm_lock);
> +}
> +
> +static void lt9611c_bridge_atomic_enable(struct drm_bridge *bridge,
> + struct drm_atomic_state *state)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> + struct drm_connector *connector;
> + struct drm_connector_state *conn_state;
> + struct drm_crtc_state *crtc_state;
> + struct drm_display_mode *mode;
> +
> + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
> + if (WARN_ON(!connector))
> + return;
> +
> + conn_state = drm_atomic_get_new_connector_state(state, connector);
> + if (WARN_ON(!conn_state))
> + return;
> +
> + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
> + if (WARN_ON(!crtc_state))
> + return;
> +
> + mode = &crtc_state->adjusted_mode;
> +
> + lt9611c_video_setup(lt9611c, mode);
> +}
> +
> +static enum drm_connector_status
> +lt9611c_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> + struct device *dev = lt9611c->dev;
> + int ret;
> + bool connected = false;
> + u8 cmd[5] = {0x52, 0x48, 0x31, 0x3a, 0x00};
> + u8 data[5];
> +
> + mutex_lock(<9611c->ocm_lock);
> +
> + ret = lt9611c_read_write_flow(lt9611c, cmd, 5, data, 5);
> + if (ret)
> + dev_err(dev, "failed to read HPD status (err=%d)\n", ret);
> + else
> + connected = (data[4] == 0x02);
> +
> + lt9611c->hdmi_connected = connected;
> +
> + mutex_unlock(<9611c->ocm_lock);
> +
> + return connected ? connector_status_connected :
> + connector_status_disconnected;
> +}
> +
> +static int lt9611c_get_edid_block(void *data, u8 *buf,
> + unsigned int block, size_t len)
> +{
> + struct lt9611c *lt9611c = data;
> + struct device *dev = lt9611c->dev;
> + u8 cmd[5] = {0x52, 0x48, 0x33, 0x3a, 0x00};
> + u8 packet[37];
> + int ret, i, offset = 0;
> +
> + if (len != 128)
> + return -EINVAL;
> + mutex_lock(<9611c->ocm_lock);
> +
> + for (i = 0; i < 4; i++) {
> + cmd[4] = block * 4 + i;
> + ret = lt9611c_read_write_flow(lt9611c, cmd, sizeof(cmd),
> + packet, sizeof(packet));
> + if (ret) {
> + dev_err(dev, "Failed to read EDID block %u packet %d\n",
> + block, i);
> + mutex_unlock(<9611c->ocm_lock);
> + return ret;
> + }
> +
> + memcpy(buf + offset, &packet[5], 32);
> + offset += 32;
> + }
> +
> + mutex_unlock(<9611c->ocm_lock);
> +
> + return 0;
> +}
> +
> +static const struct drm_edid *lt9611c_bridge_edid_read(struct drm_bridge *bridge,
> + struct drm_connector *connector)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> +
> + return drm_edid_read_custom(connector, lt9611c_get_edid_block, lt9611c);
> +}
> +
> +static void lt9611c_bridge_hpd_notify(struct drm_bridge *bridge,
> + struct drm_connector *connector,
> + enum drm_connector_status status)
> +{
> + const struct drm_edid *drm_edid;
> +
> + if (status == connector_status_disconnected) {
> + drm_connector_hdmi_audio_plugged_notify(connector, false);
> + drm_edid_connector_update(connector, NULL);
> + return;
> + }
> +
> + drm_edid = lt9611c_bridge_edid_read(bridge, connector);
> + drm_edid_connector_update(connector, drm_edid);
> + drm_edid_free(drm_edid);
> +
> + if (status == connector_status_connected)
> + drm_connector_hdmi_audio_plugged_notify(connector, true);
> +}
> +
> +static int lt9611c_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
> + const u8 *buffer, size_t len)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> + u8 audio_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x02};
> + u8 data[5];
> + int i, ret;
> +
> + for (i = 0; i < len; i++)
> + audio_infoframe_cmd[i + 5] = buffer[i];
> +
> + mutex_lock(<9611c->ocm_lock);
> +
> + ret = lt9611c_read_write_flow(lt9611c, audio_infoframe_cmd, sizeof(audio_infoframe_cmd),
> + data, sizeof(data));
> + mutex_unlock(<9611c->ocm_lock);
> +
> + if (ret < 0) {
> + dev_err(lt9611c->dev, "write audio infoframe failed!\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int lt9611c_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
> +{
> + return 0;
Hmm? What if we need to clear the infoframe?
> +}
> +
> +static int lt9611c_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
> + const u8 *buffer, size_t len)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> + u8 avi_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x01};
> + u8 data[5];
> + int i, ret;
> +
> + for (i = 0; i < len; i++)
> + avi_infoframe_cmd[i + 5] = buffer[i];
> +
> + mutex_lock(<9611c->ocm_lock);
> +
> + ret = lt9611c_read_write_flow(lt9611c, avi_infoframe_cmd, sizeof(avi_infoframe_cmd),
> + data, sizeof(data));
> + mutex_unlock(<9611c->ocm_lock);
> +
> + if (ret < 0) {
> + dev_err(lt9611c->dev, "write avi infoframe failed!\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int lt9611c_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
> +{
The same question.
> + return 0;
> +}
> +
> +static int lt9611c_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
> + const u8 *buffer, size_t len)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> + u8 spd_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x04};
> + u8 data[5];
> + int i, ret;
> +
> + for (i = 0; i < len; i++)
> + spd_infoframe_cmd[i + 5] = buffer[i];
> +
> + mutex_lock(<9611c->ocm_lock);
> +
> + ret = lt9611c_read_write_flow(lt9611c, spd_infoframe_cmd, sizeof(spd_infoframe_cmd),
> + data, sizeof(data));
> + mutex_unlock(<9611c->ocm_lock);
> +
> + if (ret < 0) {
> + dev_err(lt9611c->dev, "write spd infoframe failed!\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int lt9611c_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
> +{
> + return 0;
> +}
> +
> +static int lt9611c_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
> + const u8 *buffer, size_t len)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> + u8 spd_infoframe_cmd[16] = {0x57, 0x48, 0x35, 0x3a, 0x05};
> + u8 data[5];
> + int i, ret;
> +
> + for (i = 0; i < len; i++)
> + spd_infoframe_cmd[i + 5] = buffer[i];
> +
> + mutex_lock(<9611c->ocm_lock);
> +
> + ret = lt9611c_read_write_flow(lt9611c, spd_infoframe_cmd, sizeof(spd_infoframe_cmd),
> + data, sizeof(data));
> + mutex_unlock(<9611c->ocm_lock);
> + if (ret < 0) {
> + dev_err(lt9611c->dev, "write hdmi infoframe failed!\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int lt9611c_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
> +{
> + return 0;
> +}
> +
> +static int lt9611c_hdmi_audio_prepare(struct drm_bridge *bridge,
> + struct drm_connector *connector,
> + struct hdmi_codec_daifmt *fmt,
> + struct hdmi_codec_params *hparms)
> +{
> + struct lt9611c *lt9611c = bridge_to_lt9611c(bridge);
> + u8 audio_cmd[6] = {0x57, 0x48, 0x36, 0x3a};
> + u8 data[5];
> + int ret;
> +
> + /* Validate sample rate and width (LT9611C auto-detects but we still check) */
What for? You don't trust ASoC / ALSA core that the rates would match
HDMI_RATES?
> + switch (hparms->sample_rate) {
> + case 32000:
> + case 44100:
> + case 48000:
> + case 88200:
> + case 96000:
> + case 176400:
> + case 192000:
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (hparms->sample_width) {
> + case 16:
> + case 18:
> + case 20:
> + case 24:
and no support for 32?
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt->fmt) {
> + case HDMI_I2S:
> + audio_cmd[4] = 0x01;
> + break;
> + case HDMI_SPDIF:
> + audio_cmd[4] = 0x02;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + audio_cmd[5] = hparms->channels;
> +
> + mutex_lock(<9611c->ocm_lock);
> + ret = lt9611c_read_write_flow(lt9611c, audio_cmd, sizeof(audio_cmd),
> + data, sizeof(data));
> + mutex_unlock(<9611c->ocm_lock);
> +
> + if (ret < 0) {
> + dev_err(lt9611c->dev, "set audio info failed!\n");
> + return ret;
> + }
> +
> + return drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
> + &hparms->cea);
Please check the indentation.
> +}
> +
> +static void lt9611c_hdmi_audio_shutdown(struct drm_bridge *bridge,
> + struct drm_connector *connector)
> +{
> + drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
> +}
> +
> +static int lt9611c_hdmi_audio_startup(struct drm_bridge *bridge,
> + struct drm_connector *connector)
> +{
> + return 0;
> +}
> +
> +static const struct drm_bridge_funcs lt9611c_bridge_funcs = {
> + .attach = lt9611c_bridge_attach,
> + .detect = lt9611c_bridge_detect,
> + .edid_read = lt9611c_bridge_edid_read,
> + .hpd_notify = lt9611c_bridge_hpd_notify,
> + .atomic_enable = lt9611c_bridge_atomic_enable,
> +
> + .hdmi_tmds_char_rate_valid = lt9611c_hdmi_tmds_char_rate_valid,
> + .hdmi_write_audio_infoframe = lt9611c_hdmi_write_audio_infoframe,
> + .hdmi_clear_audio_infoframe = lt9611c_hdmi_clear_audio_infoframe,
> + .hdmi_write_avi_infoframe = lt9611c_hdmi_write_avi_infoframe,
> + .hdmi_clear_avi_infoframe = lt9611c_hdmi_clear_avi_infoframe,
> + .hdmi_write_spd_infoframe = lt9611c_hdmi_write_spd_infoframe,
> + .hdmi_clear_spd_infoframe = lt9611c_hdmi_clear_spd_infoframe,
> + .hdmi_write_hdmi_infoframe = lt9611c_hdmi_write_hdmi_infoframe,
> + .hdmi_clear_hdmi_infoframe = lt9611c_hdmi_clear_hdmi_infoframe,
You implemented all these callbacks, but you haven't tested them. They
won't be called as is. You would need to set one more DRM bridge OP. And
once set, you can drop the .hpd_notify, it will be handled by the
generic code.
> + .hdmi_audio_startup = lt9611c_hdmi_audio_startup,
> + .hdmi_audio_prepare = lt9611c_hdmi_audio_prepare,
> + .hdmi_audio_shutdown = lt9611c_hdmi_audio_shutdown,
> +};
> +
> +static int lt9611c_parse_dt(struct device *dev,
> + struct lt9611c *lt9611c)
> +{
> + lt9611c->dsi0_node = of_graph_get_remote_node(dev->of_node, 0, -1);
> + if (!lt9611c->dsi0_node) {
> + dev_err(dev, "failed to get remote node for primary dsi\n");
> + return -ENODEV;
> + }
> +
> + lt9611c->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1);
> +
> + return drm_of_find_panel_or_bridge(dev->of_node, 2, -1, NULL, <9611c->next_bridge);
> +}
> +
> +static int lt9611c_gpio_init(struct lt9611c *lt9611c)
> +{
> + struct device *dev = lt9611c->dev;
> +
> + lt9611c->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> + if (IS_ERR(lt9611c->reset_gpio)) {
> + dev_err(dev, "failed to acquire reset gpio\n");
> + return PTR_ERR(lt9611c->reset_gpio);
> + }
> +
> + return 0;
> +}
> +
> +static int lt9611c_read_version(struct lt9611c *lt9611c)
> +{
> + u8 buf[2];
> + int ret;
> +
> + ret = regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
> + if (ret)
> + return ret;
> +
> + ret = regmap_bulk_read(lt9611c->regmap, 0xe080, buf, 2);
> + if (ret)
> + return ret;
> +
> + return (buf[0] << 8) | buf[1];
> +}
> +
> +static int lt9611c_read_chipid(struct lt9611c *lt9611c)
> +{
> + struct device *dev = lt9611c->dev;
> + u8 chipid[2];
> + int ret;
> +
> + ret = regmap_write(lt9611c->regmap, 0xe0ee, 0x01);
> + if (ret)
> + return ret;
> +
> + ret = regmap_bulk_read(lt9611c->regmap, 0xe100, chipid, 2);
> + if (ret)
> + return ret;
> +
> + if (chipid[0] != 0x23 || chipid[1] != 0x06) {
> + dev_err(dev, "ChipID: 0x%02x 0x%02x\n", chipid[0], chipid[1]);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t lt9611c_firmware_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct lt9611c *lt9611c = dev_get_drvdata(dev);
> + int ret;
> +
> + lt9611c_lock(lt9611c);
> +
> + ret = lt9611c_firmware_upgrade(lt9611c);
> + if (ret < 0) {
> + dev_err(dev, "upgrade failure\n");
> + goto out;
> + }
> + lt9611c_reset(lt9611c);
> + ret = lt9611c_upgrade_result(lt9611c);
> + if (ret < 0)
> + goto out;
> +
> +out:
> + lt9611c_unlock(lt9611c);
> + lt9611c_reset(lt9611c);
> + if (lt9611c->fw) {
> + release_firmware(lt9611c->fw);
> + lt9611c->fw = NULL;
> + }
> +
> + return ret < 0 ? ret : len;
> +}
> +
> +static ssize_t lt9611c_firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> + struct lt9611c *lt9611c = dev_get_drvdata(dev);
> +
> + return sysfs_emit(buf, "0x%04x\n", lt9611c->fw_version);
> +}
> +
> +static DEVICE_ATTR_RW(lt9611c_firmware);
> +
> +static struct attribute *lt9611c_attrs[] = {
> + &dev_attr_lt9611c_firmware.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group lt9611c_attr_group = {
> + .attrs = lt9611c_attrs,
> +};
> +
> +static const struct attribute_group *lt9611c_attr_groups[] = {
> + <9611c_attr_group,
> + NULL,
> +};
> +
> +static int lt9611c_probe(struct i2c_client *client)
> +{
> + const struct i2c_device_id *id = i2c_client_get_device_id(client);
> + struct lt9611c *lt9611c;
> + struct device *dev = &client->dev;
> + bool fw_updated = false;
> + int ret;
> +
> + crc8_populate_msb(lt9611c_crc8_table, LT9611C_CRC_POLYNOMIAL);
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> + dev_err(dev, "device doesn't support I2C\n");
> + return -ENODEV;
> + }
> +
> + lt9611c = devm_drm_bridge_alloc(dev, struct lt9611c, bridge, <9611c_bridge_funcs);
> + if (IS_ERR(lt9611c))
> + return PTR_ERR(lt9611c);
> +
> + lt9611c->dev = dev;
> + lt9611c->client = client;
> + lt9611c->chip_type = id->driver_data;
> + mutex_init(<9611c->ocm_lock);
> +
> + lt9611c->regmap = devm_regmap_init_i2c(client, <9611c_regmap_config);
> + if (IS_ERR(lt9611c->regmap)) {
> + dev_err(dev, "regmap i2c init failed\n");
> + return PTR_ERR(lt9611c->regmap);
> + }
> +
> + ret = lt9611c_parse_dt(dev, lt9611c);
> + if (ret) {
> + dev_err(dev, "failed to parse device tree\n");
> + return ret;
> + }
> +
> + ret = lt9611c_gpio_init(lt9611c);
> + if (ret < 0)
> + goto err_of_put;
> +
> + ret = lt9611c_regulator_init(lt9611c);
> + if (ret < 0)
> + goto err_of_put;
> +
> + ret = lt9611c_regulator_enable(lt9611c);
> + if (ret)
> + goto err_of_put;
> +
> + lt9611c_reset(lt9611c);
> +
> + lt9611c_lock(lt9611c);
> +
> + ret = lt9611c_read_chipid(lt9611c);
> + if (ret < 0) {
> + dev_err(dev, "failed to read chip id.\n");
> + lt9611c_unlock(lt9611c);
> + goto err_disable_regulators;
> + }
> +
> +retry:
> + ret = lt9611c_read_version(lt9611c);
> + if (ret < 0) {
> + dev_err(dev, "failed to read fw version\n");
> + lt9611c_unlock(lt9611c);
> + goto err_disable_regulators;
> +
> + } else if (ret == 0) {
> + if (!fw_updated) {
> + fw_updated = true;
> + ret = lt9611c_firmware_upgrade(lt9611c);
> + if (ret < 0) {
> + lt9611c_unlock(lt9611c);
> + goto err_disable_regulators;
> + }
> +
> + lt9611c_reset(lt9611c);
> +
> + ret = lt9611c_upgrade_result(lt9611c);
> + if (ret < 0) {
> + lt9611c_unlock(lt9611c);
> + goto err_disable_regulators;
> + }
> +
> + goto retry;
> +
> + } else {
> + dev_err(dev, "fw version 0x%04x, update failed\n", ret);
> + ret = -EOPNOTSUPP;
> + lt9611c_unlock(lt9611c);
> + goto err_disable_regulators;
> + }
> + }
> +
> + lt9611c_unlock(lt9611c);
> + lt9611c->fw_version = ret;
> +
> + dev_dbg(dev, "current version:0x%04x", lt9611c->fw_version);
> +
> + INIT_WORK(<9611c->work, lt9611c_hpd_work);
> +
> + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
> + lt9611c_irq_thread_handler,
> + IRQF_TRIGGER_FALLING |
> + IRQF_ONESHOT |
> + IRQF_NO_AUTOEN,
> + "lt9611c", lt9611c);
> + if (ret) {
> + dev_err(dev, "failed to request irq\n");
> + goto err_disable_regulators;
> + }
> +
> + lt9611c->bridge.of_node = client->dev.of_node;
> + lt9611c->bridge.ops = DRM_BRIDGE_OP_DETECT |
> + DRM_BRIDGE_OP_EDID |
> + DRM_BRIDGE_OP_HPD |
> + DRM_BRIDGE_OP_HDMI_AUDIO;
> + lt9611c->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
> +
> + lt9611c->bridge.hdmi_audio_dev = dev;
> + lt9611c->bridge.hdmi_audio_max_i2s_playback_channels = 8;
> + lt9611c->bridge.hdmi_audio_dai_port = 2;
> +
> + drm_bridge_add(<9611c->bridge);
> +
> + /* Attach primary DSI */
> + lt9611c->dsi0 = lt9611c_attach_dsi(lt9611c, lt9611c->dsi0_node);
> + if (IS_ERR(lt9611c->dsi0)) {
> + ret = PTR_ERR(lt9611c->dsi0);
> + goto err_remove_bridge;
> + }
> +
> + /* Attach secondary DSI, if specified */
> + if (lt9611c->dsi1_node) {
> + lt9611c->dsi1 = lt9611c_attach_dsi(lt9611c, lt9611c->dsi1_node);
> + if (IS_ERR(lt9611c->dsi1)) {
> + ret = PTR_ERR(lt9611c->dsi1);
> + goto err_remove_bridge;
> + }
> + }
> +
> + lt9611c->hdmi_connected = false;
> + i2c_set_clientdata(client, lt9611c);
> + enable_irq(client->irq);
> + lt9611c_reset(lt9611c);
> +
> + return 0;
> +
> +err_remove_bridge:
> + free_irq(client->irq, lt9611c);
> + cancel_work_sync(<9611c->work);
> + drm_bridge_remove(<9611c->bridge);
> +
> +err_disable_regulators:
> + regulator_bulk_disable(ARRAY_SIZE(lt9611c->supplies), lt9611c->supplies);
> +
> +err_of_put:
> + of_node_put(lt9611c->dsi1_node);
> + of_node_put(lt9611c->dsi0_node);
> + if (lt9611c->fw) {
> + release_firmware(lt9611c->fw);
> + lt9611c->fw = NULL;
> + }
> +
> + return ret;
> +}
> +
> +static void lt9611c_remove(struct i2c_client *client)
> +{
> + struct lt9611c *lt9611c = i2c_get_clientdata(client);
> +
> + free_irq(client->irq, lt9611c);
> + cancel_work_sync(<9611c->work);
> + drm_bridge_remove(<9611c->bridge);
> + mutex_destroy(<9611c->ocm_lock);
> + regulator_bulk_disable(ARRAY_SIZE(lt9611c->supplies), lt9611c->supplies);
> + of_node_put(lt9611c->dsi1_node);
> + of_node_put(lt9611c->dsi0_node);
> +}
> +
> +static int lt9611c_bridge_suspend(struct device *dev)
> +{
> + struct lt9611c *lt9611c = dev_get_drvdata(dev);
> + int ret;
> +
> + dev_dbg(lt9611c->dev, "suspend\n");
> + disable_irq(lt9611c->client->irq);
> + ret = lt9611c_regulator_disable(lt9611c);
> + gpiod_set_value_cansleep(lt9611c->reset_gpio, 0);
> +
> + return ret;
> +}
> +
> +static int lt9611c_bridge_resume(struct device *dev)
> +{
> + struct lt9611c *lt9611c = dev_get_drvdata(dev);
> + int ret;
> +
> + ret = lt9611c_regulator_enable(lt9611c);
> + enable_irq(lt9611c->client->irq);
> + lt9611c_reset(lt9611c);
> + dev_dbg(lt9611c->dev, "resume\n");
> +
> + return ret;
> +}
> +
> +static const struct dev_pm_ops lt9611c_bridge_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(lt9611c_bridge_suspend,
> + lt9611c_bridge_resume)
> +};
> +
> +static struct i2c_device_id lt9611c_id[] = {
> + /* chip_type */
> + { "lontium,lt9611c", 0 },
> + { "lontium,lt9611ex", 1 },
> + { "lontium,lt9611uxd", 2 },
> + { /* sentinel */ }
> +};
> +
> +static const struct of_device_id lt9611c_match_table[] = {
> + { .compatible = "lontium,lt9611c" },
> + { .compatible = "lontium,lt9611ex" },
> + { .compatible = "lontium,lt9611uxd" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, lt9611c_match_table);
> +
> +static struct i2c_driver lt9611c_driver = {
> + .driver = {
> + .name = "lt9611c",
> + .of_match_table = lt9611c_match_table,
> + .pm = <9611c_bridge_pm_ops,
> + .dev_groups = lt9611c_attr_groups,
> + },
> + .probe = lt9611c_probe,
> + .remove = lt9611c_remove,
> + .id_table = lt9611c_id,
> +};
> +module_i2c_driver(lt9611c_driver);
> +
> +MODULE_AUTHOR("SunYun Yang <syyang@lontium.com>");
> +MODULE_DESCRIPTION("Lontium lt9611c mipi-dsi to hdmi driver");
MIPI, DSI, HDMI
> +MODULE_LICENSE("GPL v2");
I think, checkpatch.pl should have flagged this.
> +
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH v3 2/8] arm64, unwind: build kernel with sframe V3 info
From: Jens Remus @ 2026-04-20 12:16 UTC (permalink / raw)
To: Dylan Hatch
Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Mark Rutland, Prasanna Kumar T S M, Puranjay Mohan,
Song Liu, joe.lawrence, linux-toolchains, linux-kernel,
live-patching, linux-arm-kernel, Heiko Carstens, Vasily Gorbik,
Ilya Leoshkevich
In-Reply-To: <CADBMgpyFd=id0M0Q+nZouBt9Ph6T=0PfP9xWuKOFWoLQd7zvng@mail.gmail.com>
On 4/18/2026 2:20 AM, Dylan Hatch wrote:
> On Tue, Apr 14, 2026 at 5:43 AM Jens Remus <jremus@linux.ibm.com> wrote:
>> You are introducing two new Kconfig options (SFRAME_UNWINDER and
>> ARCH_SUPPORTS_SFRAME_UNWINDER). I wonder whether they could somehow be
>> combined into a single new option. Although I am not sure how an option
>> can be both selectable and depending at the same time, so that the ARM64
>> config could select it, but it would also depend on the above.
>
> I don't think this is recommended, since the behavior of 'select'
> appears to override a 'depends' requirement.
>
> From Documentation/kbuild/kconfig-language.rst: "select should be used
> with care. select will force a symbol to a value without visiting the
> dependencies. By abusing select you are able to select a symbol FOO
> even if FOO depends on BAR that is not set. In general use select only
> for non-visible symbols (no prompts anywhere) and for symbols with no
> dependencies. That will limit the usefulness but on the other hand
> avoid the illegal configurations all over."
Thanks for the explanation! So both options cannot be merged into one.
Maybe the option names can still be aligned, so that they have
UNWIND_KERNEL_SFRAME in common and the kernel and user space sframe
unwinder options have HAVE_UNWIND in common?
SFRAME_UNWINDER -> HAVE_UNWIND_KERNEL_SFRAME
ARCH_SUPPORTS_SFRAME_UNWINDER -> ARCH_SUPPORTS_UNWIND_KERNEL_SFRAME
That would then align nicely with the existing:
HAVE_UNWIND_USER_SFRAME
The only downside is that the user variant would get selected via
HAVE_UNWIND_USER_SFRAME and the kernel variant via
ARCH_SUPPORTS_UNWIND_KERNEL_SFRAME.
>>> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
>>
>>> @@ -491,6 +491,8 @@
>>> *(.rodata1) \
>>> } \
>>> \
>>> + SFRAME \
>>> + \
>>> /* PCI quirks */ \
>>> .pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
>>> BOUNDED_SECTION_PRE_LABEL(.pci_fixup_early, _pci_fixups_early, __start, __end) \
>>> @@ -911,6 +913,19 @@
>>> #define TRACEDATA
>>> #endif
>>>
>>> +#ifdef CONFIG_SFRAME_UNWINDER
>>> +#define SFRAME \
>>> + /* sframe */ \
>>> + .sframe : AT(ADDR(.sframe) - LOAD_OFFSET) { \
>>> + __start_sframe_header = .; \
>>
>> __start_sframe[_section] = .;
>>
>>> + KEEP(*(.sframe)) \
>>> + KEEP(*(.init.sframe)) \
>>> + __stop_sframe_header = .; \
>>
>> __stop_sframe[_section] = .;
>>
>> Unless I am missing something both are not the start/stop of the .sframe
>> header (in the .sframe section) but the .sframe section itself (see also
>> your subsequent "[PATCH v3 4/8] sframe: Provide PC lookup for vmlinux
>> .sframe section." where you assign both to kernel_sfsec.sframe_start
>> and kernel_sfsec.sframe_end.
>>
>>> + }
>>> +#else
>>> +#define SFRAME
>>> +#endif
>>> +
>>> #ifdef CONFIG_PRINTK_INDEX
>>> #define PRINTK_INDEX \
>>> .printk_index : AT(ADDR(.printk_index) - LOAD_OFFSET) { \
What about the following? Note that I also aligned the indentation in
vmlinux.lds.h to the one in the blocks above/below.
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
@@ -31,6 +31,7 @@
* __irqentry_text_start, __irqentry_text_end
* __softirqentry_text_start, __softirqentry_text_end
* __start_opd, __end_opd
+ * __start_sframe, __end_sframe
*/
extern char _text[], _stext[], _etext[];
extern char _data[], _sdata[], _edata[];
@@ -53,6 +54,9 @@ extern char __ctors_start[], __ctors_end[];
/* Start and end of .opd section - used for function descriptors. */
extern char __start_opd[], __end_opd[];
+/* Stand end end of .sframe section - used for stack unwinding. */
+extern char __start_sframe[], __end_sframe[];
+
/* Start and end of instrumentation protected text section */
extern char __noinstr_text_start[], __noinstr_text_end[];
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
@@ -914,13 +914,13 @@
#endif
#ifdef CONFIG_SFRAME_UNWINDER
-#define SFRAME \
- /* sframe */ \
- .sframe : AT(ADDR(.sframe) - LOAD_OFFSET) { \
- __start_sframe_header = .; \
- KEEP(*(.sframe)) \
- KEEP(*(.init.sframe)) \
- __stop_sframe_header = .; \
+#define SFRAME \
+ /* sframe */ \
+ .sframe : AT(ADDR(.sframe) - LOAD_OFFSET) { \
+ __start_sframe = .; \
+ KEEP(*(.sframe)) \
+ KEEP(*(.init.sframe)) \
+ __end_sframe = .; \
}
#else
#define SFRAME
Regards,
Jens
--
Jens Remus
Linux on Z Development (D3303)
jremus@de.ibm.com / jremus@linux.ibm.com
IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/
^ permalink raw reply
* Re: [PATCH] vmalloc: fix buffer overflow in vrealloc_node_align()
From: Uladzislau Rezki @ 2026-04-20 12:17 UTC (permalink / raw)
To: Marco Elver
Cc: Vlastimil Babka, Andrew Morton, Uladzislau Rezki, linux-mm,
linux-kernel, kasan-dev, Vitaly Wool, stable, Harry Yoo (Oracle)
In-Reply-To: <20260420114805.3572606-2-elver@google.com>
On Mon, Apr 20, 2026 at 01:47:26PM +0200, Marco Elver wrote:
> Commit 4c5d3365882d ("mm/vmalloc: allow to set node and align in
> vrealloc") added the ability to force a new allocation if the current
> pointer is on the wrong NUMA node, or if an alignment constraint is not
> met, even if the user is shrinking the allocation.
>
> On this path (need_realloc), the code allocates a new object of 'size'
> bytes and then memcpy()s 'old_size' bytes into it. If the request is to
> shrink the object (size < old_size), this results in an out-of-bounds
> write on the new buffer.
>
> Fix this by bounding the copy length by the new allocation size.
>
> Fixes: 4c5d3365882d ("mm/vmalloc: allow to set node and align in vrealloc")
> Cc: <stable@vger.kernel.org>
> Reported-by: Harry Yoo (Oracle) <harry@kernel.org>
> Signed-off-by: Marco Elver <elver@google.com>
> ---
> mm/vmalloc.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index 61caa55a4402..8b1124158f54 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -4361,7 +4361,7 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
> return NULL;
>
> if (p) {
> - memcpy(n, p, old_size);
> + memcpy(n, p, min(size, old_size));
> vfree(p);
> }
>
> --
> 2.54.0.rc1.513.gad8abe7a5a-goog
>
Agree with a problem described in commit message:
Reviewed-by: Uladzislau Rezki (Sony) <urezki@gmail.com>
Thank you for fixing it!
--
Uladzislau Rezki
^ permalink raw reply
* [PATCH v2] drm/amdgpu: fix root reservation in amdgpu_vm_handle_fault
From: Pierre-Eric Pelloux-Prayer @ 2026-04-20 12:14 UTC (permalink / raw)
To: Alex Deucher, Christian König, David Airlie, Simona Vetter,
Pierre-Eric Pelloux-Prayer
Cc: amd-gfx, dri-devel, linux-kernel
svm_range_restore_pages might reserve the root bo so it must
be called after unreserving it.
---
v2:
- don't modify amdgpu_vm_lock_by_pasid
- add a TODO
---
Fixes: 32b486e8541c ("drm/amdgpu: extract amdgpu_vm_lock_by_pasid from amdgpu_vm_handle_fault")
Signed-off-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 63156289ae7f..799a1803d941 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -3026,11 +3026,22 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid,
is_compute_context = vm->is_compute_context;
- if (is_compute_context && !svm_range_restore_pages(adev, pasid, vmid,
- node_id, addr >> PAGE_SHIFT, ts, write_fault)) {
+ if (is_compute_context) {
+ /* Unreserve root since svm_range_restore_pages might try to reserve it. */
+ /* TODO: rework svm_range_restore_pages so that this isn't necessary. */
amdgpu_bo_unreserve(root);
+
+ if (!svm_range_restore_pages(adev, pasid, vmid,
+ node_id, addr >> PAGE_SHIFT, ts, write_fault)) {
+ amdgpu_bo_unref(&root);
+ return true;
+ }
amdgpu_bo_unref(&root);
- return true;
+
+ /* Double check that the VM still exists. */
+ vm = amdgpu_vm_lock_by_pasid(adev, &root, pasid);
+ if (!vm)
+ return false;
}
addr /= AMDGPU_GPU_PAGE_SIZE;
--
2.43.0
^ permalink raw reply related
* [RFC PATCH 2/2] mm/gup: add lockless access semantics on entries validation
From: Alexander Gordeev @ 2026-04-20 12:13 UTC (permalink / raw)
To: Kevin Brodsky, David Hildenbrand, Ryan Roberts, Andrew Morton
Cc: linux-s390, linux-mm, linux-kernel, Gerald Schaefer,
Heiko Carstens, Vasily Gorbik
In-Reply-To: <cover.1776684344.git.agordeev@linux.ibm.com>
The PTE validation in gup_fast_pte_range() is inconsistent with the
prior value acquisition in the sense that it drops the lockless
access semantics.
Although this is highly unlikely, prevent a future scenario in which
a semantically mismatching ptep_get() incorrectly yields the same
result as the preceding ptep_get_lockless(), while ptep_get_lockless()
would otherwise return a different value.
Likewise the PMD validation is inconsistent with the prior value
acquisition in gup_fast_pmd_range().
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
---
mm/gup.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mm/gup.c b/mm/gup.c
index d149a4b0df71..236450feea9a 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -2865,8 +2865,8 @@ static int gup_fast_pte_range(pmd_t pmd, pmd_t *pmdp, unsigned long addr,
if (!folio)
goto pte_unmap;
- if (unlikely(pmd_val(pmd) != pmd_val(pmdp_get(pmdp))) ||
- unlikely(pte_val(pte) != pte_val(ptep_get(ptep)))) {
+ if (unlikely(pmd_val(pmd) != pmd_val(pmdp_get_lockless(pmdp))) ||
+ unlikely(pte_val(pte) != pte_val(ptep_get_lockless(ptep)))) {
gup_put_folio(folio, 1, flags);
goto pte_unmap;
}
@@ -2942,7 +2942,7 @@ static int gup_fast_pmd_leaf(pmd_t orig, pmd_t *pmdp, unsigned long addr,
if (!folio)
return 0;
- if (unlikely(pmd_val(orig) != pmd_val(pmdp_get(pmdp)))) {
+ if (unlikely(pmd_val(orig) != pmd_val(pmdp_get_lockless(pmdp)))) {
gup_put_folio(folio, refs, flags);
return 0;
}
--
2.51.0
^ permalink raw reply related
* [RFC PATCH 1/2] mm/gup: add missing pXdp_get() conversions
From: Alexander Gordeev @ 2026-04-20 12:13 UTC (permalink / raw)
To: Kevin Brodsky, David Hildenbrand, Ryan Roberts, Andrew Morton
Cc: linux-s390, linux-mm, linux-kernel, Gerald Schaefer,
Heiko Carstens, Vasily Gorbik
In-Reply-To: <cover.1776684344.git.agordeev@linux.ibm.com>
PMD and PUD entries revalidation has the same semantics as
PTE entry revalidation. Convert the remaining direct entry
dereferences to the corresponding pXdp_get() accessors.
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
---
mm/gup.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mm/gup.c b/mm/gup.c
index 8e7dc2c6ee73..d149a4b0df71 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -2865,7 +2865,7 @@ static int gup_fast_pte_range(pmd_t pmd, pmd_t *pmdp, unsigned long addr,
if (!folio)
goto pte_unmap;
- if (unlikely(pmd_val(pmd) != pmd_val(*pmdp)) ||
+ if (unlikely(pmd_val(pmd) != pmd_val(pmdp_get(pmdp))) ||
unlikely(pte_val(pte) != pte_val(ptep_get(ptep)))) {
gup_put_folio(folio, 1, flags);
goto pte_unmap;
@@ -2942,7 +2942,7 @@ static int gup_fast_pmd_leaf(pmd_t orig, pmd_t *pmdp, unsigned long addr,
if (!folio)
return 0;
- if (unlikely(pmd_val(orig) != pmd_val(*pmdp))) {
+ if (unlikely(pmd_val(orig) != pmd_val(pmdp_get(pmdp)))) {
gup_put_folio(folio, refs, flags);
return 0;
}
@@ -2985,7 +2985,7 @@ static int gup_fast_pud_leaf(pud_t orig, pud_t *pudp, unsigned long addr,
if (!folio)
return 0;
- if (unlikely(pud_val(orig) != pud_val(*pudp))) {
+ if (unlikely(pud_val(orig) != pud_val(pudp_get(pudp)))) {
gup_put_folio(folio, refs, flags);
return 0;
}
--
2.51.0
^ permalink raw reply related
* [RFC PATCH 0/2] mm/gup: pgtable entry acccessors cleanup
From: Alexander Gordeev @ 2026-04-20 12:13 UTC (permalink / raw)
To: Kevin Brodsky, David Hildenbrand, Ryan Roberts, Andrew Morton
Cc: linux-s390, linux-mm, linux-kernel, Gerald Schaefer,
Heiko Carstens, Vasily Gorbik
Hi All,
This rework is necessary to allow a KASAN PTE access sanitizer
(so far s390-only) that hits on direct PTE entries dereferences,
like pte = *ptep as opposed to pte = ptep_get(ptep).
I am not posting the sanitizer itself (yet), since it does not
fly without the suggested patches, but I am not sure whether
these changes make sense for the generic case.
Thus, sending it as a RFC.
Thanks!
Alexander Gordeev (2):
mm/gup: add missing pXdp_get() conversions
mm/gup: add lockless access semantics on entries validation
mm/gup.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--
2.51.0
^ permalink raw reply
* Re: [RFC PATCH 2/7] dma-direct: use DMA_ATTR_CC_DECRYPTED in alloc/free paths
From: Jiri Pirko @ 2026-04-20 12:13 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Aneesh Kumar K.V, iommu, linux-kernel, robin.murphy, m.szyprowski,
will, maz, suzuki.poulose, catalin.marinas, Mostafa Saleh
In-Reply-To: <20260418160617.GM2577880@ziepe.ca>
Sat, Apr 18, 2026 at 06:06:17PM +0200, jgg@ziepe.ca wrote:
>On Sat, Apr 18, 2026 at 11:57:42AM +0530, Aneesh Kumar K.V wrote:
>> Aneesh Kumar K.V <aneesh.kumar@kernel.org> writes:
>>
>> > Jason Gunthorpe <jgg@ziepe.ca> writes:
>> >
>> >> On Fri, Apr 17, 2026 at 02:28:55PM +0530, Aneesh Kumar K.V (Arm) wrote:
>> >>> Propagate force_dma_unencrypted() into DMA_ATTR_CC_DECRYPTED in the
>> >>> dma-direct allocation path and use the attribute to drive the related
>> >>> decisions.
>> >>>
>> >>> This updates dma_direct_alloc(), dma_direct_free(), and
>> >>> dma_direct_alloc_pages() to fold the forced unencrypted case into attrs.
>> >>>
>> >>> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
>> >>> ---
>> >>> kernel/dma/direct.c | 34 ++++++++++++++++++++++++++--------
>> >>> 1 file changed, 26 insertions(+), 8 deletions(-)
>> >>>
>> >>> diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
>> >>> index c2a43e4ef902..3932033f4d8c 100644
>> >>> --- a/kernel/dma/direct.c
>> >>> +++ b/kernel/dma/direct.c
>> >>> @@ -201,16 +201,21 @@ void *dma_direct_alloc(struct device *dev, size_t size,
>> >>> dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
>> >>> {
>> >>> bool remap = false, set_uncached = false;
>> >>> - bool mark_mem_decrypt = true;
>> >>> + bool mark_mem_decrypt = !!(attrs & DMA_ATTR_CC_DECRYPTED);
>> >>> struct page *page;
>> >>
>> >> This is changing the API, I think it should not be hidden in a patch
>> >> like this, also not sure it even makes sense..
>> >>
>> >> DMA_ATTR_CC_DECRYPTED only says the address passed to mapping is
>> >> decrypted. It is like DMA_ATTR_MMIO in this regard.
>> >>
>> >> Passing it to dma_alloc_attrs() is currently invalid, and I think it
>> >> should remain invalid, or at least this new behavior introduced in its
>> >> own patch deliberately.
>> >>
>>
>> Thinking about this further, I am wondering why you consider passing
>> DMA_ATTR_CC_DECRYPTED invalid.
>
>It doesn't do that today. My point is if you want to add then then do
>it in its own patch and justify what it is for..
>
>I cannot think of a reason for anything to want to do this. The
>purpose of decrypted memory is to allow a T=0 device to access it and
>you cannot take memory allocated by dma_alloc and pass it to some
>other device as a matter of API. So it would be *really* suspicious if
>some driver wanted this.
Agreed. I think that allowing map with DMA_ATTR_CC_DECRYPTED for those
devices would need very clear usecase described alongside.
>
>> How about making the change below so that we only prevent
>> dma_alloc_attrs() from accepting DMA_ATTR_CC_DECRYPTED?
>
>Yeah, that is what I was trying to say. No issue with it using it
>inside I think.
>
>Jason
^ permalink raw reply
* Re: [PATCH] iio: dac: Fix passing uninitialized vref1_uV for no Vref1 case
From: Jonathan Cameron @ 2026-04-20 12:11 UTC (permalink / raw)
To: Ariana Lazar
Cc: David Lechner, Nuno Sá, Andy Shevchenko, Jonathan Cameron,
linux-iio, linux-kernel, Dan Carpenter
In-Reply-To: <20260414-mcp47feb02-fix4-v1-1-9d71badfd25e@microchip.com>
On Tue, 14 Apr 2026 15:33:38 +0300
Ariana Lazar <ariana.lazar@microchip.com> wrote:
> Initialize vref1_uV variable to 0 before calling
> mcp47feb02_init_ch_scales() in mcp47feb02_probe() to avoid passing an
> uninitialized value when have_ext_vref1 is false.
>
> Fixes: dd154646d292 ("iio: dac: mcp47feb02: Fix Vref validation [1-999] case")
> Reported-by: Dan Carpenter <error27@gmail.com>
> Closes: https://lore.kernel.org/all/adiPnla0M5EzvgD-@stanley.mountain/
> Signed-off-by: Ariana Lazar <ariana.lazar@microchip.com>
> ---
> drivers/iio/dac/mcp47feb02.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c
> index faccb804a5ed548088aaf83266b16ed45a92916c..efd4847913e46a4fa1b671aa9c5bd3e1430406b7 100644
> --- a/drivers/iio/dac/mcp47feb02.c
> +++ b/drivers/iio/dac/mcp47feb02.c
> @@ -1095,9 +1095,10 @@ static int mcp47feb02_probe(struct i2c_client *client)
> {
> const struct mcp47feb02_features *chip_features;
> struct device *dev = &client->dev;
> + int vdd_uV, vref_uV, vref1_uV;
> struct mcp47feb02_data *data;
> struct iio_dev *indio_dev;
> - int vref1_uV, vref_uV, vdd_uV, ret;
I haven't caught up with the rest of the discussion yet, but this change
seems to be superficial style stuff. Doesn't belong in a fix patch.
Thanks,
Jonathan
> + int ret;
>
> indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> if (!indio_dev)
> @@ -1146,13 +1147,13 @@ static int mcp47feb02_probe(struct i2c_client *client)
> dev_dbg(dev, "Vref is unavailable.\n");
> }
>
> + vref1_uV = 0;
> if (chip_features->have_ext_vref1) {
> ret = devm_regulator_get_enable_read_voltage(dev, "vref1");
> if (ret > 0) {
> vref1_uV = ret;
> data->use_vref1 = true;
> } else {
> - vref1_uV = 0;
> dev_dbg(dev, "using internal band gap as voltage reference 1.\n");
> dev_dbg(dev, "Vref1 is unavailable.\n");
> }
>
> ---
> base-commit: 51e7665ab81f02adc80a1219c260ee678e9c6eb8
> change-id: 20260414-mcp47feb02-fix4-614de9334f22
>
> Best regards,
^ permalink raw reply
* Re: [PATCH v4 0/6] iio: light: vcnl4000: drop enum ID table and use device-managed register
From: Jonathan Cameron @ 2026-04-20 12:09 UTC (permalink / raw)
To: Erikas Bitovtas
Cc: David Lechner, Nuno Sá, Andy Shevchenko, linux-iio,
linux-kernel, Andy Shevchenko
In-Reply-To: <20260414-vcnl4000-drop-enum-v4-0-59fde2932a0f@gmail.com>
On Tue, 14 Apr 2026 15:37:16 +0300
Erikas Bitovtas <xerikasxx@gmail.com> wrote:
> This patch series drops the enum ID table for device enumeration and
> instead adds pointers to chip ID structs directly. Necessary
> information about the device is then taken from i2c_get_match_data()
> call. This removes the association between enum ID table and struct
> table and allows for easier addition and removal of new devices.
>
> Along with that, an iio_device_register() call is replaced with its
> device-managed counterpart, to ensure that device is unregistered
> automatically.
>
> No functional changes are intended.
>
> Signed-off-by: Erikas Bitovtas <xerikasxx@gmail.com>
Applied to the testing branch of iio.git which I'll rebase on rc1 once
available and then push out for linux-next to pick up.
Thanks,
Jonathan
^ permalink raw reply
* Re: [PATCH v7 3/4] KVM: arm64: PMU: Introduce FIXED_COUNTERS_ONLY
From: Akihiko Odaki @ 2026-04-20 12:07 UTC (permalink / raw)
To: Marc Zyngier
Cc: Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, Kees Cook, Gustavo A. R. Silva,
Paolo Bonzini, Jonathan Corbet, Shuah Khan, linux-arm-kernel,
kvmarm, linux-kernel, linux-hardening, devel, kvm, linux-doc,
linux-kselftest
In-Reply-To: <86qzoa0xj6.wl-maz@kernel.org>
On 2026/04/20 18:51, Marc Zyngier wrote:
> On Mon, 20 Apr 2026 09:36:16 +0100,
> Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> wrote:
>>
>> On 2026/04/20 2:19, Marc Zyngier wrote:
>>> On Sat, 18 Apr 2026 09:14:25 +0100,
>>> Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> wrote:
>>>>
>>>> On a heterogeneous arm64 system, KVM's PMU emulation is based on the
>>>> features of a single host PMU instance. When a vCPU is migrated to a
>>>> pCPU with an incompatible PMU, counters such as PMCCNTR_EL0 stop
>>>> incrementing.
>>>>
>>>> Although this behavior is permitted by the architecture, Windows does
>>>> not handle it gracefully and may crash with a division-by-zero error.
>>>>
>>>> The current workaround requires VMMs to pin vCPUs to a set of pCPUs
>>>> that share a compatible PMU. This is difficult to implement correctly in
>>>> QEMU/libvirt, where pinning occurs after vCPU initialization, and it
>>>> also restricts the guest to a subset of available pCPUs.
>>>>
>>>> Introduce the KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY attribute to
>>>> create a "fixed-counters-only" PMU. When set, KVM exposes a PMU that is
>>>> compatible with all pCPUs but that does not support programmable
>>>> event counters which may have different feature sets on different PMUs.
>>>>
>>>> This allows Windows guests to run reliably on heterogeneous systems
>>>> without crashing, even without vCPU pinning, and enables VMMs to
>>>> schedule vCPUs across all available pCPUs, making full use of the host
>>>> hardware.
>>>>
>>>> Much like KVM_ARM_VCPU_PMU_V3_IRQ and other read-write attributes, this
>>>> attribute provides a getter that facilitates kernel and userspace
>>>> debugging/testing.
>>>
>>> OK, so that's the sales pitch. But how is it implemented? I would like
>>> to be able to read a high-level description of the implementation
>>> trade-offs.
>>
>> Implementation-wise it is very trivial. Essentially the following
>> addition in kvm_arm_pmu_v3_get_attr() is the entire implementation:
>> + case KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY:
>> + if (test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY,
>> &vcpu->kvm->arch.flags))
>> + return 0;
>>
>> Both its functionality and code complexity is trivial. So we can argue that:
>> - the functionality is too trivial to be useful or
>> - the interface/implementation complexity is so trivial that it does not
>> incur maintenance burden
>>
>> In this case the selftest uses the getter so I was more inclined to
>> have it, but adding one just for the selftest sounds too ad-hoc, so
>> here I looked into other attributes to ensure that it was not
>> introducing inconsistency with existing interfaces.
>>
>> As the result, I found there are other read-write attributes; in fact
>> there are more read-write attributes than write-only ones.
>
> You're completely missing the point. I'm referring to the whole of the
> commit message, which is more of a marketing slide than a technical
> description.
In terms of implementation, the obvious tradeoff is that it adds more
code to implement the feature. One thing to note is that
kvm_vcpu_load_pmu() is added and is called each time a vCPU migrates
across pCPUs. The heavy part, making the KVM_REQ_RELOAD_PMU request,
only happens when the feature is enabled.
>
> I really don't care about the getter at this stage, which while
> pointless, does not make things more awful than they already are.
>
>>
>>>
>>>>
>>>> Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
>>>> ---
>>>> Documentation/virt/kvm/devices/vcpu.rst | 29 ++++++
>>>> arch/arm64/include/asm/kvm_host.h | 2 +
>>>> arch/arm64/include/uapi/asm/kvm.h | 1 +
>>>> arch/arm64/kvm/arm.c | 1 +
>>>> arch/arm64/kvm/pmu-emul.c | 155 +++++++++++++++++++++++---------
>>>> include/kvm/arm_pmu.h | 2 +
>>>> 6 files changed, 147 insertions(+), 43 deletions(-)
>>>>
>>>> diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst
>>>> index 60bf205cb373..e0aeb1897d77 100644
>>>> --- a/Documentation/virt/kvm/devices/vcpu.rst
>>>> +++ b/Documentation/virt/kvm/devices/vcpu.rst
>>>> @@ -161,6 +161,35 @@ explicitly selected, or the number of counters is out of range for the
>>>> selected PMU. Selecting a new PMU cancels the effect of setting this
>>>> attribute.
>>>> +1.6 ATTRIBUTE: KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY
>>>> +------------------------------------------------------
>>>> +
>>>> +:Parameters: no additional parameter in kvm_device_attr.addr
>>>> +
>>>> +:Returns:
>>>> +
>>>> + ======= =====================================================
>>>> + -EBUSY Attempted to set after initializing PMUv3 or running
>>>> + VCPU, or attempted to set for the first time after
>>>> + setting an event filter
>>>> + -ENXIO Attempted to get before setting
>>>> + -ENODEV Attempted to set while PMUv3 not supported
>>>> + ======= =====================================================
>>>> +
>>>> +If set, PMUv3 will be emulated without programmable event counters. The VCPU
>>>> +will use any compatible hardware PMU. This attribute is particularly useful on
>>>
>>> Not quite "any PMU". It will use *the* PMU of the physical CPU,
>>> irrespective of the implementation.
>>
>> I think:
>>
>> - this comment
>> - one on the KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED note
>> - one on kvm_pmu_create_perf_event()
>> - and one on kvm_arm_pmu_v3_set_pmu_fixed_counters_only()
>>
>> All boil down into one question: will it support all possible CPUs, or
>> will it support a subset? Let me answer here:
>>
>> This patch is written to support a subset instead of all possible
>> CPUs. If a pCPU does not have a compatible PMU, the pCPU will not be
>> supported and cause KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED.
>
> This is not a thing. Either *all* the CPUs have a PMU that can be used
> for KVM, or PMU support is not offered to guests. That's a hard line
> in the sand. And the code already upholds this by checking the
> sanitised PMUVer field.
>
>>
>> This patch does not enforce all possible CPUs are covered by the
>> compatible PMUs. Theoretically speaking,
>> kvm_arm_pmu_get_pmuver_limit() enables the PMU emulation when real
>> PMUv3 hardware covers all possible CPUs *or* the relevant registers
>> can be trapped with IMPDEF, so some pCPU may not have a compatible PMU
>> and only provide the IMPDEF trapping.
>
> How is that possible? Please describe the case where that can happen,
> and I will make sure that such a system stops booting. The intent is
> definitely that that:
>
> - for early CPUs, we take the minimal capability of all CPUs
>
> - for late CPUs, either they match at least the capability recorded by
> early CPUs, or they don't boot.
All CPUs may trap the relevant registers with IMPDEF but some of them
may not have compatible PMUs. As I wrote in the previous email, I don't
think it will happen in practice.
>
>> Practically, I don't think any sane configuration will ever have such
>> a subset support, so we can explicitly enforce all possible CPUs are
>> covered by the compatible PMUs if desired.
>
> That's not just desired. This is a requirement. And it is already
> enforced AFAICS.
>
>>
>>>
>>>> +heterogeneous systems where different hardware PMUs cover different physical
>>>> +CPUs. The compatibility of hardware PMUs can be checked with
>>>> +KVM_ARM_VCPU_PMU_V3_SET_PMU. All VCPUs in a VM share this attribute. It isn't
>>>> +possible to set it for the first time if a PMU event filter is already present.
>>>
>>> "for the first time" gives the impression that it will work if you try
>>> again. I'd rather we say that "This feature is incompatible with the
>>> existence of a PMU event filter".
>>
>> The following sequence will work:
>> 1. Set KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY
>> 2. Set KVM_ARM_VCPU_PMU_V3_FILTER
>> 3. Set KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY
>>
>> This is to make the behavior conistent with KVM_ARM_VCPU_PMU_V3_SET_PMU.
>
> I don't think this is correct. Filtering is completely at odds with
> this patch, and I don't want to have to reason about the combination.
kvm_arm_pmu_v3_set_pmu() has the following condition:
if (kvm_vm_has_ran_once(kvm) ||
(kvm->arch.pmu_filter && kvm->arch.arm_pmu != arm_pmu)) {
ret = -EBUSY;
break;
}
kvm_arm_pmu_v3_set_pmu_fixed_counters_only() has the corresponding
condition for consistency:
if (kvm_vm_has_ran_once(kvm) ||
(kvm->arch.pmu_filter &&
!test_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY,
&kvm->arch.flags)))
return -EBUSY;
We can of course kill the PMU event filter for FIXED_COUNTERS_ONLY. The
filter is effectively no-op with FIXED_COUNTERS_ONLY and I don't think
that consistency matters much.
>
> [...]
>
>>>> + int i;
>>>> +
>>>> + for_each_set_bit(i, &mask, 32) {
>>>> + pmc = kvm_vcpu_idx_to_pmc(vcpu, i);
>>>> + if (!pmc->perf_event)
>>>> + continue;
>>>> +
>>>> + cpu_pmu = to_arm_pmu(pmc->perf_event->pmu);
>>>> + if (!cpumask_test_cpu(vcpu->cpu, &cpu_pmu->supported_cpus)) {
>>>> + kvm_make_request(KVM_REQ_RELOAD_PMU, vcpu);
>>>> + break;
>>>> + }
>>>> + }
>>>> +}
>>>> +
>>>
>>> Why do we need to inflict this on VMs that do not have the fixed
>>> counter restriction?
>>
>> This function is to re-create the perf_event in case the current
>> perf_event does not support the pCPU because e.g., the pCPU is a
>> E-core while the perf_event only covers the P-cores.
>
> That's not what I meant. This code is only here to support the
> fixed-function feature. It makes no sense outside of it, because *we
> don't support counter migration across implementations*.
>
> So what's the purpose of this stuff for the normal KVM setup?
None. It's only for this feature. We can add a check of the feature flag
at the beginning of the function to avoid that loop.
>
>>
>>>
>>> And even then, all you have to reconfigure is the cycle counter. So
>>> why the loop? All we want to find out is whether the cycle counter is
>>> instantiated on the PMU that matches the current CPU.
>>
>> I just wanted to avoid hardcoding assumptions on the fixed
>> counter(s). FEAT_PMUv3_ICNTR will be naturaly handled with a loop, for
>> example.
>
> Well, not that loop, since ICNTR is counter 32. So please let's stop
> the nonsense and only add what is required?
>
> [...]
>
>>>> +
>>>> clear_bit(KVM_ARCH_FLAG_PMU_V3_FIXED_COUNTERS_ONLY,
>>>> &kvm->arch.flags);
>>>
>>> Why does this need to be cleared? I'd rather we make sure it is never
>>> set the first place.
>>
>> KVM_ARM_VCPU_PMU_V3_SET_PMU and
>> KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY can be set on the same
>> VCPU. The last KVM_ARM_VCPU_PMU_V3_SET_PMU or
>> KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY setting will be effective.
>>
>> A VMM may try set these attributes to check if the setting is
>> supported. For example, the RFC QEMU patch first uses
>> KVM_ARM_VCPU_PMU_V3_SET_PMU to find a compatible PMU that covers all
>> pCPUs, and then falls back to
>> KVM_ARM_VCPU_PMU_V3_FIXED_COUNTERS_ONLY. The order of such probing is
>> up to the VMM.
>
> KVM_ARM_VCPU_PMU_V3_SET_PMU is not a probing mechanism. You must probe
> the PMUs by looking in /sys/bus/event_source/devices/, like kvmtool
> does.
>
> So there is no reason to support this stuff, and the two flags should
> be made mutually exclusive.
Thanks for the pointer. I'll make a change to make the flags mutually
exclusive and test it with an amended QEMU patch that follows what
kvmtool does.
>
> [...]
>
>>>>
>>>
>>> In conclusion, I find this patch to be rather messy. For a start, it
>>> needs to be split in at least 5 patches:
>>>
>>> - at least two for the refactoring
>>> - one for the PMU core changes
>>> - one for the UAPI
>>> - one for documentation
>>
>> That clarifies the expected granurarity of patches. The next version
>> will be in that layout, perhaps with more patches if an additional
>> change. Thanks for the guidance.
>>
>>>
>>> I'd also like some clarification on how this is intended to work if we
>>> enable FEAT_PMUv3_ICNTR, because the definition seems to be designed
>>> to encompass all fixed-function counters, and I expect this to grow
>>> over time.
>>
>> Indeed the UAPI was designed to encompass all fixed-function counters
>> as suggested by Oliver.
>>
>> To support the UAPI, the implementation avoids hardcoding the
>> assumption on the fixed counter(s). FEAT_PMUv3_INCTR will be naturaly
>> supported once the common code is properly updated (i.e., the size of
>> the event counter bitmask is grown the corresponding registers are
>> wired up with a proper check of the feature.)
>>
>> I expect migration will be handled with the conventional register
>> getters and setters, but please share if you have a concern.
>
> At the very least I want to see some documentation explaining that.
What kind of documentation do you expect? If we change
kvm_vcpu_load_pmu() to avoid for_each_set_bit(), there would be a good
chance to forget updating it when mechanically updating existing
for_each_set_bit() instances, so it is a candidate for documentation.
But I don't have a good idea where to place it either.
Regards,
Akihiko Odaki
^ permalink raw reply
* Re: [PATCH v3] docs/zh_CN: add module-signing Chinese translation
From: Dongliang Mu @ 2026-04-20 12:03 UTC (permalink / raw)
To: Yan Zhu; +Cc: alexs, seakeel, si.yanteng, corbet, skhan, linux-doc,
linux-kernel
In-Reply-To: <tencent_91B9F750970BCBFEAC86BBACA3DF31480407@qq.com>
On 4/18/26 10:15 PM, Yan Zhu wrote:
> Hi Dongliang,
>
> On 4/18/2026 2:20 PM, Dongliang Mu wrote:
>>
>> On 4/18/26 12:45 PM, Yan Zhu wrote:
>>> Translate .../admin-guide/module-signing.rst into Chinese.
>>>
>>> Update the translation through commit 0ad9a71933e7
>>> ("modsign: Enable ML-DSA module signing")
>>>
>>> Signed-off-by: Yan Zhu <zhuyan2015@qq.com>
>>> ---
>>
>> Hi Yan,
>>
>> Please remember to add your changelog under the "---". For example,
>> v1- >v2: XXX
>>
>
> I replied to the email according to the guidance of how-to.rst in the
> Chinese thanslation. There was no special explanation there, so I
> thought that the Chinese translation did not need the explanation of
> version change. I think I should resend a patch to fill in this
> description.
https://lore.kernel.org/all/20260420115647.2718959-1-dzm91@hust.edu.cn/
I sent a new patch to explain how to deal with changelog.
Dongliang Mu
>
>> And I wonder where the v2 patch is as I don't find it in my mbox. Do
>> I miss something?
>
> There may be something wrong with the mailbox. I see that you are in
> the copy list for the v2 patch. The link is
> https://lore.kernel.org/lkml/tencent_0101EEFDDBC5D532222BFE0EF2487DCC0805@qq.com
>
>> Dongliang Mu
>>
>>> .../zh_CN/admin-guide/module-signing.rst | 249 ++++++++++++++++++
>>> 1 file changed, 249 insertions(+)
>>> create mode 100644 Documentation/translations/zh_CN/admin-guide/
>>> module-signing.rst
>>>
>>> diff --git a/Documentation/translations/zh_CN/admin-guide/module-
>>> signing.rst b/Documentation/translations/zh_CN/admin-guide/module-
>>> signing.rst
>>> new file mode 100644
>>> index 000000000000..04b0f1cbafd5
>>> --- /dev/null
>>> +++ b/Documentation/translations/zh_CN/admin-guide/module-signing.rst
>>> @@ -0,0 +1,249 @@
>>> +.. SPDX-License-Identifier: GPL-2.0
>>> +.. include:: ../disclaimer-zh_CN.rst
>>> +
>>> +:Original: Documentation/admin-guide/module-signing.rst
>>> +:翻译:
>>> + 朱岩 Yan Zhu <zhuyan2015@qq.com>
>>> +
>>> +
>>> +==========================
>>> +内核模块签名机制
>>> +==========================
>>> +
>>> +.. 目录
>>> +..
>>> +.. - 概述
>>> +.. - 配置模块签名
>>> +.. - 生成签名密钥
>>> +.. - 内核中的公钥
>>> +.. - 模块手动签名
>>> +.. - 已签名模块和剥离
>>> +.. - 加载已签名模块
>>> +.. - 无效签名和未签名模块
>>> +.. - 管理/保护私钥
>>> +
>>> +
>>> +概述
>>> +====
>>> +
>>> +内核模块签名机制在安装过程中对模块进行加密签名,然后在加载模块时检查
>>> 签名。这
>>> +通过禁止加载未签名的模块或使用无效密钥签名的模块来提高内核安全性。模
>>> 块签名通
>>> +过使恶意模块更难加载到内核中来增加安全性。模块签名检查在内核中完成,
>>> 因此不需
>>> +要受信任的用户空间位。
>>> +
>>> +此机制使用 X.509 ITU-T 标准证书对涉及的公钥进行编码。签名本身不以任何
>>> 工业标准
>>> +类型编码。内置机制目前仅支持 RSA、NIST P-384 ECDSA 和 NIST FIPS-204
>>> ML-DSA
>>> +公钥签名标准(尽管它是可插拔的并允许使用其他标准)。对于 RSA 和
>>> ECDSA,可以使
>>> +用的可能的哈希算法是大小为 256、384 和 512 的 SHA-2 和 SHA-3(算法由
>>> 签名中的
>>> +数据选择);ML-DSA会自行进行哈希运算,但允许与SHA512哈希算法结合用于
>>> 签名属性。
>>> +
>>> +配置模块签名
>>> +============
>>> +
>>> +通过进入内核配置的 :menuselection:`Enable Loadable Module Support` 菜
>>> 单并打
>>> +开以下选项来启用模块签名机制::
>>> +
>>> + CONFIG_MODULE_SIG "Module signature verification"
>>> +
>>> +这有多个可用选项:
>>> +
>>> + (1) :menuselection:`Require modules to be validly signed`
>>> + (``CONFIG_MODULE_SIG_FORCE``)
>>> +
>>> + 这指定了内核应如何处理其密钥未知或未签名的模块。
>>> +
>>> + 如果关闭(即"宽松模式"),则允许使用不可用密钥和未签名的模块,但
>>> 内核将被
>>> + 标记为受污染,并且相关模块将被标记为受污染,显示字符'E'。
>>> +
>>> + 如果打开(即"限制模式"),只有具有有效签名且可由内核拥有的公钥验
>>> 证的模块
>>> + 才会被加载。所有其他模块将生成错误。
>>> +
>>> + 无论此处的设置如何,如果模块的签名块无法解析,它将被直接拒绝。
>>> +
>>> +
>>> + (2) :menuselection:`Automatically sign all modules`
>>> + (``CONFIG_MODULE_SIG_ALL``)
>>> +
>>> + 如果打开此选项,则在构建的 modules_install
>>> 阶段期间将自动签名模块。
>>> + 如果关闭,则必须使用以下命令手动签名模块::
>>> +
>>> + scripts/sign-file
>>> +
>>> +
>>> + (3) :menuselection:`Which hash algorithm should modules be signed
>>> with?`
>>> +
>>> + 这提供了安装阶段将用于签名模块的哈希算法选择:
>>> +
>>> + ===============================
>>> ==========================================
>>> + ``CONFIG_MODULE_SIG_SHA256`` :menuselection:`Sign modules
>>> with SHA-256`
>>> + ``CONFIG_MODULE_SIG_SHA384`` :menuselection:`Sign modules
>>> with SHA-384`
>>> + ``CONFIG_MODULE_SIG_SHA512`` :menuselection:`Sign modules
>>> with SHA-512`
>>> + ``CONFIG_MODULE_SIG_SHA3_256`` :menuselection:`Sign modules
>>> with SHA3-256`
>>> + ``CONFIG_MODULE_SIG_SHA3_384`` :menuselection:`Sign modules
>>> with SHA3-384`
>>> + ``CONFIG_MODULE_SIG_SHA3_512`` :menuselection:`Sign modules
>>> with SHA3-512`
>>> + ===============================
>>> ==========================================
>>> +
>>> + 此处选择的算法也将被构建到内核中(而不是作为模块),以便使用该算
>>> 法签名的
>>> + 模块可以在不导致循环依赖的情况下检查其签名。
>>> +
>>> +
>>> + (4) :menuselection:`File name or PKCS#11 URI of module signing key`
>>> + (``CONFIG_MODULE_SIG_KEY``)
>>> +
>>> + 将此选项设置为除默认值 ``certs/signing_key.pem`` 之外的其他值将
>>> 禁用签名
>>> + 密钥的自动生成,并允许使用您选择的密钥对内核模块进行签名。提供的
>>> 字符串应
>>> + 标识包含私钥及其对应的 PEM 格式 X.509 证书的文件,或者在 OpenSSL
>>> + ENGINE_pkcs11 功能正常的系统上,使用 RFC7512 定义的 PKCS#11
>>> URI。在后一
>>> + 种情况下,PKCS#11 URI 应引用证书和私钥。
>>> +
>>> + 如果包含私钥的 PEM 文件已加密,或者 PKCS#11 令牌需要 PIN,可以通过
>>>
>>> + ``KBUILD_SIGN_PIN`` 变量在构建时提供。
>>> +
>>> +
>>> + (5) :menuselection:`Additional X.509 keys for default system keyring`
>>> + (``CONFIG_SYSTEM_TRUSTED_KEYS``)
>>> +
>>> + 此选项可设置为包含附加证书的 PEM 编码文件的文件名,这些证书将默
>>> 认包含在
>>> + 系统密钥环中。
>>> +
>>> +请注意,启用模块签名会为内核构建过程添加对执行签名工具的OpenSSL开发包
>>> 的依赖。
>>> +
>>> +
>>> +生成签名密钥
>>> +============
>>> +
>>> +生成和检查签名需要加密密钥对。私钥用于生成签名,相应的公钥用于检查签
>>> 名。私钥
>>> +仅在构建期间需要,之后可以删除或安全存储。公钥被构建到内核中,以便在
>>> 加载模块
>>> +时可以使用它来检查签名。
>>> +
>>> +在正常情况下,当 ``CONFIG_MODULE_SIG_KEY`` 保持默认值时,如果文件中不
>>> 存在密
>>> +钥对,内核构建将使用 openssl 自动生成新的密钥对::
>>> +
>>> + certs/signing_key.pem
>>> +
>>> +在构建 vmlinux 期间(公钥需要构建到 vmlinux 中)使用参数::
>>> +
>>> + certs/x509.genkey
>>> +
>>> +文件(如果尚不存在也会生成)。
>>> +
>>> +可以在 RSA(``MODULE_SIG_KEY_TYPE_RSA``)、
>>> +ECDSA(``MODULE_SIG_KEY_TYPE_ECDSA``)和
>>> +ML-DSA(``MODULE_SIG_KEY_TYPE_MLDSA_*``)之间选择生成 RSA 4k、NIST
>>> P-384
>>> +密钥对或 ML-DSA 44、65 或 87 密钥对。
>>> +
>>> +强烈建议您提供自己的 x509.genkey 文件。
>>> +
>>> +最值得注意的是,在 x509.genkey 文件中,req_distinguished_name 部分应
>>> 从默认值
>>> +更改::
>>> +
>>> + [ req_distinguished_name ]
>>> + #O = Unspecified company
>>> + CN = Build time autogenerated kernel key
>>> + #emailAddress = unspecified.user@unspecified.company
>>> +
>>> +生成的 RSA 密钥大小也可以通过以下方式设置::
>>> +
>>> + [ req ]
>>> + default_bits = 4096
>>> +
>>> +也可以使用位于 Linux 内核源代码树根节点中的 x509.genkey 密钥生成配置
>>> 文件和
>>> +openssl 命令手动生成公钥/私钥文件。以下是生成公钥/私钥文件的示例::
>>> +
>>> + openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \
>>> + -config x509.genkey -outform PEM -out kernel_key.pem \
>>> + -keyout kernel_key.pem
>>> +
>>> +然后可以将生成的 kernel_key.pem 文件的完整路径名指定在
>>> +``CONFIG_MODULE_SIG_KEY``选项中,并且将使用其中的证书和密钥而不是自动
>>> 生成的
>>> +密钥对。
>>> +
>>> +
>>> +内核中的公钥
>>> +============
>>> +
>>> +内核包含一个可由 root 查看的公钥环。它们在名为 ".builtin_trusted_keys"
>>> 的密
>>> +钥环中,可以通过以下方式查看::
>>> +
>>> + [root@deneb ~]# cat /proc/keys
>>> + ...
>>> + 223c7853 I------ 1 perm 1f030000 0 0 keyring
>>> .builtin_trusted_keys: 1
>>> + 302d2d52 I------ 1 perm 1f010000 0 0 asymmetri
>>> Fedora kernel signing key: d69a84e6bce3d216b979e9505b3e3ef9a7118079:
>>> X509.RSA a7118079 []
>>> +
>>> +除了专门为模块签名生成的公钥外,还可以在 ``CONFIG_SYSTEM_TRUSTED_KEYS``
>>> 配置
>>> +选项引用的 PEM 编码文件中提供其他受信任的证书。
>>> +
>>> +此外,架构代码可以从硬件存储中获取公钥并将其添加(例如从 UEFI 密钥数
>>> 据库)。
>>> +
>>> +最后,可以通过以下方式添加其他公钥::
>>> +
>>> + keyctl padd asymmetric "" [.builtin_trusted_keys-ID] <[key-file]
>>> +
>>> +例如::
>>> +
>>> + keyctl padd asymmetric "" 0x223c7853 <my_public_key.x509
>>> +
>>> +但是,请注意,内核只允许将由已驻留在 ``.builtin_trusted_keys`` 中的密
>>> 钥有效
>>> +签名的密钥添加到 ``.builtin_trusted_keys``。
>>> +
>>> +模块手动签名
>>> +============
>>> +
>>> +要手动对模块进行签名,请使用 Linux 内核源代码树中可用的 scripts/sign-
>>> file 工
>>> +具。该脚本需要 4 个参数:
>>> +
>>> + 1. 哈希算法(例如,sha256)
>>> + 2. 私钥文件名或 PKCS#11 URI
>>> + 3. 公钥文件名
>>> + 4. 要签名的内核模块
>>> +
>>> +以下是签名内核模块的示例::
>>> +
>>> + scripts/sign-file sha512 kernel-signkey.priv \
>>> + kernel-signkey.x509 module.ko
>>> +
>>> +使用的哈希算法不必与配置的算法匹配,但如果不同,应确保哈希算法要么内
>>> 置在内核
>>> +中,要么可以在不需要自身的情况下加载。
>>> +
>>> +如果私钥需要密码或 PIN,可以在 $KBUILD_SIGN_PIN 环境变量中提供。
>>> +
>>> +
>>> +已签名模块和剥离
>>> +================
>>> +
>>> +已签名模块在末尾简单地附加了数字签名。模块文件末尾的字符串
>>> +``~Module signature appended~.`` 确认签名存在,但不能确认签名有效!
>>> +
>>> +已签名模块是脆弱的,因为签名在定义的ELF容器之外。因此,一旦计算并附加
>>> 签名,就
>>> +不得剥离它们。请注意,整个模块都是签名的有效载荷,包括签名时存在的任
>>> 何和所有
>>> +调试信息。
>>> +
>>> +
>>> +加载已签名模块
>>> +==============
>>> +
>>> +模块通过 insmod、modprobe、``init_module()`` 或 ``finit_module()`` 加
>>> 载,
>>> +与未签名模块完全一样,因为在用户空间中不进行任何处理。
>>> +所有签名检查都在内核内完成。
>>> +
>>> +
>>> +无效签名和未签名模块
>>> +====================
>>> +
>>> +如果启用了 ``CONFIG_MODULE_SIG_FORCE`` 或在内核启动命令提供了
>>> +module.sig_enforce=1,内核将仅加载具有有效签名且具有公钥的模块。否
>>> 则,它还将
>>> +加载未签名的模块。任何具有不匹配签名的模块将不被允许加载。
>>> +
>>> +任何具有不可解析签名的模块将被拒绝。
>>> +
>>> +
>>> +管理/保护私钥
>>> +==============
>>> +
>>> +由于私钥用于签名模块,病毒和恶意软件可以使用私钥签名模块并危害操作系
>>> 统。私钥
>>> +必须被销毁或移动到安全位置,而不是保存在内核源代码树的根节点中。
>>> +
>>> +如果使用相同的私钥为多个内核配置签名模块,必须确保模块版本信息足以防
>>> 止将模块
>>> +加载到不同的内核中。要么设置 ``CONFIG_MODVERSIONS=y``,要么通过更改
>>> +``EXTRAVERSION`` 或 ``CONFIG_LOCALVERSION`` 确保每个配置具有不同的内
>>> 核发布字
>>> +符串。
>
^ permalink raw reply
* Re: [PATCH] sysrq: add optional logging of caller info on /proc/sysrq-trigger write
From: Xiang Gao @ 2026-04-20 12:03 UTC (permalink / raw)
To: gregkh; +Cc: jirislaby, akpm, linux-kernel, linux-serial, gaoxiang17,
Xiang Gao
In-Reply-To: <2026041830-visibly-underpaid-6dc8@gregkh>
On Sat, Apr 18, 2026 at 09:20:14AM +0200, Greg Kroah-Hartman wrote:
> We really do not want or like #ifdef in .c files, and for stuff like
> this, it is not needed at all.
[...]
> Module parameters are really not the way for stuff like this. And why
> would such a "tiny" option need a config option at all? If you don't
> use/need it, it's only a single bool being used?
[...]
> n is always the default, no need to add it again.
Agreed on all of these. The #ifdef, the module_param, the Kconfig
entry and the 'default n' all go away -- the feature will be
unconditional.
Before respinning I would like to align on the approach, because
your comments on the kernel log and the ancestry walk touch the
core of what this patch should actually be. Let me describe the
real use case first, since I do not think I made it clear in v1.
Use case
- Userspace triggers are typically several processes deep:
some service -> library -> system("echo c > /proc/sysrq-trigger")
-> sh. The immediate writer is "sh", the actually responsible
component is several levels up.
- The sysrq character we care about is almost always 'c', 'b' or
'o' -- the machine panics, reboots, or powers off as a direct
result of the write.
- *After* the device has come back up, we need to identify which
userspace component issued the write. Otherwise there is no way
to debug why devices in the field are rebooting themselves.
> The kernel log is not there for doing audits and the like, so is this
> just a debug option?
I agree that security audit belongs in the audit subsystem, not in
printk. What this patch is trying to do is post-mortem diagnostic
for crash/reboot triage, and audit_log() cannot deliver on that:
1. audit_log() is asynchronous -- the record is queued, then
delivered via kauditd -> netlink to a userspace subscriber.
For sysrq c/b/o the panic/reboot happens before kauditd gets
to run, so the record is never delivered.
2. In our deployment environment there is no userspace auditd
persisting records to disk, and audit records are not
preserved across reboot. The kernel ring buffer, via pstore /
ramoops, is the only channel whose contents survive the
reboot and are readable on the next boot. pr_info() is the
only mechanism that delivers into that persistent path.
I would frame this not as audit but as post-mortem diagnostic,
retitle the commit accordingly ("sysrq: log triggering task info
for post-mortem diagnostics") and drop the audit framing from the
changelog entirely.
> This might cause problems for when the system is hung and sysrq is the
> only way to reboot the box. Have you tried it in that situation?
Fair -- the while-loop walking 5 levels has to go. The reason it
was there in v1 is the "sh from system()/popen()" case above: the
writer's comm is "sh", and the information we actually want is one
or more levels up the parent chain.
On the hung-system concern specifically: /proc/sysrq-trigger only
runs when userspace is alive enough to complete a write(), so the
keyboard sysrq path via handle_sysrq() is not affected by this
change -- my patch only touches write_sysrq_trigger(). Within that
function, rcu_read_lock() / rcu_dereference() take no locks,
allocate nothing, and are bounded-constant work, so on paper they
should not introduce a new hang. I have not yet reproduced a
partially-hung system to verify this empirically; I will do so
before v2 and report back. Please correct me if I am misreading
the concern.
That said, two options I can see, would like your steer before I
respin:
(a) One bounded rcu_dereference() of current->real_parent.
Logs current's pid/comm plus ppid + parent comm. One
level, no loop. Covers the direct case
app -> system("echo c > /proc/sysrq-trigger") -> sh
where sh's parent is the app.
(b) Two unrolled rcu_dereference()s -- current->real_parent
and that task's real_parent. Still no loop, just two
fixed dereferences. Covers the common indirect case
service -> library -> system(...) -> sh
where sh's parent is the thin library wrapper and the
grandparent is the originating service, which is what we
actually need to identify in the field.
In both cases the RCU read is a fixed number of dereferences,
not iteration over a data structure, so the total work is
bounded and constant.
I lean toward (b): one level is not enough to reach the
originating service in the indirect pattern this patch was
written for. If two dereferences in the sysrq path are still
too much, I will fall back to (a).
Which direction would you accept? I will respin based on your
answer.
Thanks,
Xiang
^ permalink raw reply
* Re: [PATCH v3 2/4] arm64: dts: amlogic: t7: Add UART controllers nodes
From: Neil Armstrong @ 2026-04-20 12:03 UTC (permalink / raw)
To: Ronald Claveau, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v3-2-669cd2530ae5@aliel.fr>
On 4/20/26 13:58, Ronald Claveau wrote:
> Add device tree nodes for UART B through F (serial@7a000 to
> serial@82000), completing the UART controller description for the T7
> SoC. Each node includes the peripheral clock.
>
> While at it, move the uart_a node to its correct position in the
> bus address order (0x78000) to comply with the DT requirement that
> nodes be sorted by their reg address. Complete the
> uart_a node with its peripheral clock (CLKID_SYS_UART_A) and the
> associated clock-names, matching the vendor default clock assignment,
> consistent with the other UART nodes.
>
> Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
> ---
> arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi | 61 +++++++++++++++++++++++++----
> 1 file changed, 54 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
> index 4a55d9641bc9b..81c26b1e3e7a4 100644
> --- a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
> @@ -577,13 +577,6 @@ gpio_intc: interrupt-controller@4080 {
> <10 11 12 13 14 15 16 17 18 19 20 21>;
> };
>
> - uart_a: serial@78000 {
> - compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
> - reg = <0x0 0x78000 0x0 0x18>;
> - interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
> - status = "disabled";
> - };
> -
> gp0: clock-controller@8080 {
> compatible = "amlogic,t7-gp0-pll";
> reg = <0x0 0x8080 0x0 0x20>;
> @@ -713,6 +706,60 @@ pwm_ao_cd: pwm@60000 {
> status = "disabled";
> };
>
> + uart_a: serial@78000 {
> + compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
> + reg = <0x0 0x78000 0x0 0x18>;
> + interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_A>, <&xtal>;
> + clock-names = "xtal", "pclk", "baud";
> + status = "disabled";
> + };
> +
> + uart_b: serial@7a000 {
> + compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
> + reg = <0x0 0x7a000 0x0 0x18>;
> + interrupts = <GIC_SPI 169 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_B>, <&xtal>;
> + clock-names = "xtal", "pclk", "baud";
> + status = "disabled";
> + };
> +
> + uart_c: serial@7c000 {
> + compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
> + reg = <0x0 0x7c000 0x0 0x18>;
> + interrupts = <GIC_SPI 170 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_C>, <&xtal>;
> + clock-names = "xtal", "pclk", "baud";
> + status = "disabled";
> + };
> +
> + uart_d: serial@7e000 {
> + compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
> + reg = <0x0 0x7e000 0x0 0x18>;
> + interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_D>, <&xtal>;
> + clock-names = "xtal", "pclk", "baud";
> + status = "disabled";
> + };
> +
> + uart_e: serial@80000 {
> + compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
> + reg = <0x0 0x80000 0x0 0x18>;
> + interrupts = <GIC_SPI 172 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_E>, <&xtal>;
> + clock-names = "xtal", "pclk", "baud";
> + status = "disabled";
> + };
> +
> + uart_f: serial@82000 {
> + compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
> + reg = <0x0 0x82000 0x0 0x18>;
> + interrupts = <GIC_SPI 173 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_F>, <&xtal>;
> + clock-names = "xtal", "pclk", "baud";
> + status = "disabled";
> + };
> +
> sd_emmc_a: mmc@88000 {
> compatible = "amlogic,t7-mmc", "amlogic,meson-axg-mmc";
> reg = <0x0 0x88000 0x0 0x800>;
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply
* Re: [PATCH v3 3/4] arm64: dts: amlogic: t7: khadas-vim4: Remove redundant clocks from UART A
From: Neil Armstrong @ 2026-04-20 12:02 UTC (permalink / raw)
To: Ronald Claveau, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v3-3-669cd2530ae5@aliel.fr>
On 4/20/26 13:58, Ronald Claveau wrote:
> Remove clocks and clock-names for UART A, as they are defined in DTSI.
>
> Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
> ---
> arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
> index 69d6118ba57e7..3227ab27de107 100644
> --- a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
> +++ b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
> @@ -250,6 +250,4 @@ &sd_emmc_c {
>
> &uart_a {
> status = "okay";
> - clocks = <&xtal>, <&xtal>, <&xtal>;
> - clock-names = "xtal", "pclk", "baud";
> };
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply
* Re: [PATCH v3 4/5] counter: Add rockchip-pwm-capture driver
From: Nicolas Frattaroli @ 2026-04-20 12:02 UTC (permalink / raw)
To: William Breathitt Gray
Cc: William Breathitt Gray, Uwe Kleine-König, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Heiko Stuebner, Lee Jones,
kernel, Jonas Karlman, Alexey Charkov, linux-rockchip, linux-pwm,
devicetree, linux-arm-kernel, linux-kernel, linux-iio
In-Reply-To: <20251206093419.40067-1-wbg@kernel.org>
Hi,
finally got an opportunity to work on this again.
I'll respond to some things in-line. If a review isn't directly
addressed, you can assume I acknowledge it and will address it
in the next revision with no further comment needed.
On Saturday, 6 December 2025 10:34:17 Central European Summer Time William Breathitt Gray wrote:
> > +struct rockchip_pwm_capture {
> > + struct rockchip_mfpwm_func *pwmf;
> > + struct counter_device *counter;
>
> Is this structure member used at all? If not, you should just remove it.
The counter member is used in the interrupt handler. I actually
noticed that I request the interrupt before pc->counter is set,
so if an interrupt fires before the probe function finishes then
I think the handler would run with a NULL counter member. Oops,
I'll rectify that.
> > + bool is_enabled;
>
> Does this device offer some way to probe whether PWM capture mode is
> enabled? I suspect so, because I see PWM_ENABLE.pwm_en enables the
> channel and PWM_CTRL.pwm_mode selects capture mode, so perhaps we're
> able to read the current state of those registers. If you're able to
> read those registers to determine the enable state, I'd suggest wrapping
> that into a helper function and calling it when you need to determine
> whether the capture mode is currently enabled.
I'm going to read the hardware state in the next revision, you're right
that this is generally a better idea.
>
> If we're not able to probe the enable state, is it safe to assume we're
> in a disabled state when the module loads, or should we ensure it by
> unconditionally disabling PWM capture mode during
> rockchip_pwm_capture_probe()?
In my next revision, I've now modified it to mfpwm_acquire if the hardware
state has the counter enabled during probe. This sounds niche but I'm also
doing this on the PWM output side, where Uwe rightfully pointed out that
a bootloader may have enabled PWM output in hardware and Linux needs to
recognise that state without any heavy-handed actions. For the counter
PWM capture side, resetting it to a known state wouldn't be disruptive in
the same way as it would be for PWM output, but I think it's a good idea
to keep the state as-is since we can read it.
> [... snip ...]
> > +static int rkpwmc_count_read(struct counter_device *counter,
> > + struct counter_count *count, u64 *value)
> > +{
> > + struct rockchip_pwm_capture *pc = counter_priv(counter);
> > +
> > + guard(spinlock)(&pc->enable_lock);
> > +
> > + if (!pc->is_enabled) {
> > + *value = 0;
> > + return 0;
> > + }
>
> I don't think there's a need to check whether capture mode is disabled.
> The user should be aware of the enable state of the Count by checking
> the respective "enable" attribute; and if they ignore that, a value of
> "0" doesn't inherently tell them that the Count is disabled which makes
> it moot to do so. I'd suggest just removing this check entirely and
> returning the register values unconditionally.
I see what you're going for, but if the counter isn't enabled, we can't
rely in the counter having an mfpwm_acquire, and consequently, we can't
rely on the PWM core clock being on, which is required for reading the
registers.
In my next revision, I'll still be returning 0 if the counter is disabled,
but the is_enabled member is gone, so there's a new function called
rkpwmc_acquire_if_enabled to still make this correct.
I could of course also extend the core driver to let me poke at these
non-shared registers without exclusive control over the hardware, but
that may be more trouble than it's worth.
I'll also no longer return 0 on bogus count IDs when the counter is
disabled.
> > +
> > + switch (count->id) {
> > + case COUNT_LPC:
> > + *value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_LPC);
> > + return 0;
> > + case COUNT_HPC:
> > + *value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_HPC);
> > + return 0;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +static const struct counter_ops rkpwmc_ops = {
> > + .count_read = rkpwmc_count_read,
> > +};
>
> You should implement a signal_read() callback if its possible to probe
> the current state of PWM Clock. You should implement action_read() if
> its possible to probe the current polarity of "pwm_in" in order to set
> which Synapse is currently active.
Unfortunately, it doesn't seem like the hardware allows direct access to
read the signal. "pwm_in" as it appears in the block diagram seems to be
an entirely internal signal that's not accessible through MMIO.
Thank you for the reviews!
Kind regards,
Nicolas Frattaroli
>
> William Breathitt Gray
>
> [^1] https://opensource.rock-chips.com/images/3/36/Rockchip_RK3506_TRM_Part_1_V1.2-20250811.pdf
>
^ permalink raw reply
* Re: [PATCH 03/16] i3c: mipi-i3c-hci: Prevent DMA enqueue while ring is aborting or in error
From: Adrian Hunter @ 2026-04-20 11:59 UTC (permalink / raw)
To: Frank Li; +Cc: alexandre.belloni, linux-i3c, linux-kernel
In-Reply-To: <aeWNfN0x-Ptsj1-g@lizhi-Precision-Tower-5810>
On 20/04/2026 05:20, Frank Li wrote:
> On Fri, Apr 17, 2026 at 08:07:11PM +0300, Adrian Hunter wrote:
>> On 17/04/2026 09:56, Frank Li wrote:
>>> On Thu, Apr 16, 2026 at 08:56:51PM +0300, Adrian Hunter wrote:
>>>> Block the DMA enqueue path while a Ring abort is in progress or after an
>>>> error condition has been detected.
>>>>
>>>> Previously, new transfers could be enqueued while the DMA Ring was being
>>>> aborted or while error handling was underway. This allowed enqueue and
>>>> error-recovery paths to run concurrently, potentially interfering with
>>>> each other and corrupting Ring state.
>>>
>>> why not hold lock at abort and error handler? does it take quite long time
>>> to do that?
>>
>> hci_dma_dequeue_xfer() handles errors. It is called from
>> i3c_hci_process_xfer():
>> on timeout:
>> calls ->dequeue_xfer() == hci_dma_dequeue_xfer()
>> on error:
>> calls ->handle_error() == hci_dma_handle_error()
>> calls hci_dma_dequeue_xfer()
>>
>> It takes the spin lock, but has to release it to wait for the
>> abort. There is no set time for how long the abort can take,
>> but the timeout is 1 second, so it is not suitable to wait
>> for.
>
> Feel like overall equeue/dequeue and timeout's lock is too complex, it is
> quite easy to involve bugs.
>
> Is it possible to use simpler design?
Some kind of locking is needed to block the enqueue path. Using a flag
with wait_event() / wake_up_all() is similar complexity to, say, adding
a semaphore, but more light-weight because there is 1 lock not 2.
>
>
> Frank
>
>>
>>>
>>> Frank
>>>>
>>>> Introduce explicit enqueue blocking and a wait queue to serialize access:
>>>> enqueue operations now wait until abort or error handling has completed
>>>> before proceeding. Enqueue is unblocked once the Ring is safely restarted.
>>>>
>>>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>>>> ---
>>>> drivers/i3c/master/mipi-i3c-hci/core.c | 1 +
>>>> drivers/i3c/master/mipi-i3c-hci/dma.c | 25 +++++++++++++++++++++++--
>>>> drivers/i3c/master/mipi-i3c-hci/hci.h | 2 ++
>>>> 3 files changed, 26 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
>>>> index bb8f2d830b0d..5e1bc6d819cf 100644
>>>> --- a/drivers/i3c/master/mipi-i3c-hci/core.c
>>>> +++ b/drivers/i3c/master/mipi-i3c-hci/core.c
>>>> @@ -976,6 +976,7 @@ static int i3c_hci_probe(struct platform_device *pdev)
>>>>
>>>> spin_lock_init(&hci->lock);
>>>> mutex_init(&hci->control_mutex);
>>>> + init_waitqueue_head(&hci->enqueue_wait_queue);
>>>>
>>>> /*
>>>> * Multi-bus instances share the same MMIO address range, but not
>>>> diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
>>>> index 4cd32e3afa7b..314635e6e190 100644
>>>> --- a/drivers/i3c/master/mipi-i3c-hci/dma.c
>>>> +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
>>>> @@ -484,6 +484,12 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
>>>>
>>>> spin_lock_irq(&hci->lock);
>>>>
>>>> + while (unlikely(hci->enqueue_blocked)) {
>>>> + spin_unlock_irq(&hci->lock);
>>>> + wait_event(hci->enqueue_wait_queue, !READ_ONCE(hci->enqueue_blocked));
>>>> + spin_lock_irq(&hci->lock);
>>>> + }
>>>> +
>>>> if (n > rh->xfer_space) {
>>>> spin_unlock_irq(&hci->lock);
>>>> hci_dma_unmap_xfer(hci, xfer_list, n);
>>>> @@ -539,6 +545,14 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
>>>> return 0;
>>>> }
>>>>
>>>> +static void hci_dma_unblock_enqueue(struct i3c_hci *hci)
>>>> +{
>>>> + if (hci->enqueue_blocked) {
>>>> + hci->enqueue_blocked = false;
>>>> + wake_up_all(&hci->enqueue_wait_queue);
>>>> + }
>>>> +}
>>>> +
>>>> static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
>>>> struct hci_xfer *xfer_list, int n)
>>>> {
>>>> @@ -550,12 +564,17 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
>>>>
>>>> guard(mutex)(&hci->control_mutex);
>>>>
>>>> + spin_lock_irq(&hci->lock);
>>>> +
>>>> ring_status = rh_reg_read(RING_STATUS);
>>>> if (ring_status & RING_STATUS_RUNNING) {
>>>> + hci->enqueue_blocked = true;
>>>> + spin_unlock_irq(&hci->lock);
>>>> /* stop the ring */
>>>> reinit_completion(&rh->op_done);
>>>> rh_reg_write(RING_CONTROL, rh_reg_read(RING_CONTROL) | RING_CTRL_ABORT);
>>>> wait_for_completion_timeout(&rh->op_done, HZ);
>>>> + spin_lock_irq(&hci->lock);
>>>> ring_status = rh_reg_read(RING_STATUS);
>>>> if (ring_status & RING_STATUS_RUNNING) {
>>>> /*
>>>> @@ -567,8 +586,6 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
>>>> }
>>>> }
>>>>
>>>> - spin_lock_irq(&hci->lock);
>>>> -
>>>> for (i = 0; i < n; i++) {
>>>> struct hci_xfer *xfer = xfer_list + i;
>>>> int idx = xfer->ring_entry;
>>>> @@ -604,6 +621,8 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
>>>> rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
>>>> rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_RUN_STOP);
>>>>
>>>> + hci_dma_unblock_enqueue(hci);
>>>> +
>>>> spin_unlock_irq(&hci->lock);
>>>>
>>>> return did_unqueue;
>>>> @@ -647,6 +666,8 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
>>>> }
>>>> if (xfer->completion)
>>>> complete(xfer->completion);
>>>> + if (RESP_STATUS(resp))
>>>> + hci->enqueue_blocked = true;
>>>> }
>>>>
>>>> done_ptr = (done_ptr + 1) % rh->xfer_entries;
>>>> diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
>>>> index f17f43494c1b..d630400ec945 100644
>>>> --- a/drivers/i3c/master/mipi-i3c-hci/hci.h
>>>> +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
>>>> @@ -54,6 +54,8 @@ struct i3c_hci {
>>>> struct mutex control_mutex;
>>>> atomic_t next_cmd_tid;
>>>> bool irq_inactive;
>>>> + bool enqueue_blocked;
>>>> + wait_queue_head_t enqueue_wait_queue;
>>>> u32 caps;
>>>> unsigned int quirks;
>>>> unsigned int DAT_entries;
>>>> --
>>>> 2.51.0
>>>>
>>
^ permalink raw reply
* [PATCH v3 3/4] arm64: dts: amlogic: t7: khadas-vim4: Remove redundant clocks from UART A
From: Ronald Claveau @ 2026-04-20 11:58 UTC (permalink / raw)
To: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel,
Ronald Claveau
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v3-0-669cd2530ae5@aliel.fr>
Remove clocks and clock-names for UART A, as they are defined in DTSI.
Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
---
arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts | 2 --
1 file changed, 2 deletions(-)
diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
index 69d6118ba57e7..3227ab27de107 100644
--- a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
+++ b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
@@ -250,6 +250,4 @@ &sd_emmc_c {
&uart_a {
status = "okay";
- clocks = <&xtal>, <&xtal>, <&xtal>;
- clock-names = "xtal", "pclk", "baud";
};
--
2.49.0
^ permalink raw reply related
* [PATCH v3 4/4] arm64: dts: amlogic: t7: khadas-vim4: Enable Bluetooth
From: Ronald Claveau @ 2026-04-20 11:58 UTC (permalink / raw)
To: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel,
Ronald Claveau
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v3-0-669cd2530ae5@aliel.fr>
Enable UART C on the Khadas VIM4 board and attach the BCM43438
compatible Bluetooth controller to it. The node configures the RTS/CTS
hardware flow control, the associated pinmux, the power supplies (vddao_3v3
and vddao_1v8), the 32 kHz LPO clock shared with the wifi32k fixed
clock, and the GPIO lines used for host wakeup, device wakeup and
shutdown.
Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
---
.../dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
index 3227ab27de107..8ea7ae609fbd5 100644
--- a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
+++ b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
@@ -251,3 +251,22 @@ &sd_emmc_c {
&uart_a {
status = "okay";
};
+
+&uart_c {
+ status = "okay";
+ pinctrl-0 = <&uart_c_pins>;
+ pinctrl-names = "default";
+ uart-has-rtscts;
+
+ bluetooth {
+ compatible = "brcm,bcm43438-bt";
+ shutdown-gpios = <&gpio GPIOX_17 GPIO_ACTIVE_HIGH>;
+ host-wakeup-gpios = <&gpio GPIOX_18 GPIO_ACTIVE_HIGH>;
+ device-wakeup-gpios = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>;
+ max-speed = <3000000>;
+ clocks = <&wifi32k>;
+ clock-names = "lpo";
+ vbat-supply = <&vddao_3v3>;
+ vddio-supply = <&vddao_1v8>;
+ };
+};
--
2.49.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox