Files
ack-tegra/kernel/dma/debug.c
Greg Kroah-Hartman 2ec0c94f9b Merge 6.12.5 into android16-6.12
GKI (arm64) relevant 129 out of 468 changes, affecting 180 files +1843/-980
  e0964a5778 ptp: Add error handling for adjfine callback in ptp_clock_adjtime [1 file, +2/-1]
  a007f8895f net/sched: tbf: correct backlog statistic for GSO packets [1 file, +12/-6]
  9545011e7b net: Fix icmp host relookup triggering ip_rt_bug [1 file, +3/-0]
  01f95357e4 ipv6: avoid possible NULL deref in modify_prefix_route() [1 file, +7/-6]
  8b591bd522 net/ipv6: release expired exception dst cached in socket [1 file, +3/-3]
  e48b211c4c tipc: Fix use-after-free of kernel socket in cleanup_bearer(). [1 file, +1/-1]
  da5cc778e7 netfilter: nft_inner: incorrect percpu area handling under softirq [2 files, +46/-12]
  a36a6d7037 Revert "udp: avoid calling sock_def_readable() if possible" [1 file, +3/-11]
  22074dc1d4 ethtool: Fix wrong mod state in case of verbose and no_mask bitset [1 file, +44/-4]
  316183d583 net: avoid potential UAF in default_operstate() [1 file, +6/-1]
  c00372e41b mmc: sd: SDUC Support Recognition [7 files, +27/-15]
  19e22f1e68 mmc: core: Adjust ACMD22 to SDUC [1 file, +18/-6]
  42311846d3 mmc: core: Use GFP_NOIO in ACMD22 [1 file, +4/-0]
  4e51552bc5 zram: do not mark idle slots that cannot be idle [1 file, +18/-7]
  0ab037634b zram: clear IDLE flag in mark_idle() [1 file, +2/-0]
  405b6d5f90 ntp: Remove invalid cast in time offset math [1 file, +1/-1]
  6358df316d f2fs: clean up w/ F2FS_{BLK_TO_BYTES,BTYES_TO_BLK} [1 file, +29/-39]
  e6a91ed4b9 f2fs: fix to adjust appropriate length for fiemap [2 files, +4/-3]
  8e9fec7f79 f2fs: fix to requery extent which cross boundary of inquiry [1 file, +15/-5]
  815d8f0e52 i3c: master: Replace hard code 2 with macro I3C_ADDR_SLOT_STATUS_BITS [2 files, +5/-3]
  c3806cf647 i3c: master: Extend address status bit to 4 and add I3C_ADDR_SLOT_EXT_DESIRED [2 files, +59/-13]
  1117462773 i3c: master: Fix dynamic address leak when 'assigned-address' is present [1 file, +5/-10]
  7d4e5e33ea scsi: ufs: core: Always initialize the UIC done completion [1 file, +4/-7]
  3ad69f2f08 scsi: ufs: core: Add ufshcd_send_bsg_uic_cmd() for UFS BSG [3 files, +38/-1]
  47f4ad956b bpf, vsock: Fix poll() missing a queue [1 file, +3/-0]
  a222e48fea bpf, vsock: Invoke proto::close on close() [1 file, +40/-27]
  dabaf26846 xsk: always clear DMA mapping information when unmapping the pool [1 file, +2/-3]
  5c9e3bb43a tcp_bpf: Fix the sk_mem_uncharge logic in tcp_bpf_sendmsg [1 file, +4/-7]
  7bc37dd9ea ALSA: usb-audio: Notify xrun for low-latency mode [1 file, +11/-3]
  a78af11806 pmdomain: core: Add missing put_device() [1 file, +1/-0]
  913a3f1c06 pmdomain: core: Fix error path in pm_genpd_init() when ida alloc fails [1 file, +19/-17]
  5548887987 nvme: don't apply NVME_QUIRK_DEALLOCATE_ZEROES when DSM is not supported [1 file, +2/-1]
  bdbf87486d bpf: Ensure reg is PTR_TO_STACK in process_iter_arg [2 files, +7/-2]
  2459a0b149 drivers/virt: pkvm: Don't fail ioremap() call if MMIO_GUARD fails [1 file, +1/-5]
  0da7d4b7ca bpf: Don't mark STACK_INVALID as STACK_MISC in mark_stack_slot_misc [1 file, +6/-3]
  f9f2a2739e bpf: Fix narrow scalar spill onto 64-bit spilled scalar slots [1 file, +1/-0]
  845cc4ee8e nvme-fabrics: handle zero MAXCMD without closing the connection [1 file, +3/-2]
  c2277e2859 scatterlist: fix incorrect func name in kernel-doc [1 file, +1/-1]
  81ec3c6ceb bpf: Handle BPF_EXIST and BPF_NOEXIST for LPM trie [1 file, +20/-3]
  6dc076a257 bpf: Remove unnecessary kfree(im_node) in lpm_trie_update_elem [1 file, +1/-3]
  7218e441ad bpf: Handle in-place update for full LPM trie correctly [1 file, +21/-23]
  412bf01fd5 bpf: Fix exact match conditions in trie_get_next_key() [1 file, +2/-2]
  e689bc6697 HID: wacom: fix when get product name maybe null pointer [1 file, +2/-1]
  3b0c5bb437 can: dev: can_set_termination(): allow sleeping GPIOs [1 file, +1/-1]
  ba0ee489cd tracing: Fix cmp_entries_dup() to respect sort() comparison rules [1 file, +1/-5]
  ec643064ab arm64: mm: Fix zone_dma_limit calculation [1 file, +8/-9]
  34b6197867 arm64: Ensure bits ASID[15:8] are masked out when the kernel uses 8-bit ASIDs [1 file, +2/-2]
  abd614bbfc arm64: ptrace: fix partial SETREGSET for NT_ARM_TAGGED_ADDR_CTRL [1 file, +5/-1]
  8ab73c34e3 arm64: ptrace: fix partial SETREGSET for NT_ARM_FPMR [1 file, +2/-0]
  4105dd76bc arm64: ptrace: fix partial SETREGSET for NT_ARM_POE [1 file, +2/-0]
  7f1292f8d4 ALSA: usb-audio: Fix a DMA to stack memory bug [1 file, +27/-15]
  39c5d89b56 ALSA: usb-audio: Add extra PID for RME Digiface USB [3 files, +176/-168]
  9c191055c7 scsi: ufs: core: sysfs: Prevent div by zero [1 file, +6/-0]
  2e7a3bb033 scsi: ufs: core: Cancel RTC work during ufshcd_remove() [1 file, +1/-0]
  5a717f43c2 scsi: ufs: core: Add missing post notify for power mode change [2 files, +10/-7]
  793e560a6b io_uring: Change res2 parameter type in io_uring_cmd_done [2 files, +3/-3]
  85351e4941 Revert "readahead: properly shorten readahead when falling back to do_page_cache_ra()" [1 file, +2/-3]
  95e197354e cacheinfo: Allocate memory during CPU hotplug if not done from the primary CPU [1 file, +8/-6]
  bc031095d1 modpost: Add .irqentry.text to OTHER_SECTIONS [1 file, +1/-1]
  178e31df1f bpf: fix OOB devmap writes when deleting elements [1 file, +3/-3]
  3dcc20418e dma-buf: fix dma_fence_array_signaled v4 [1 file, +27/-1]
  f3dbb097d6 dma-fence: Fix reference leak on fence merge failure path [1 file, +2/-0]
  4715555964 dma-fence: Use kernel's sort for merging fences [1 file, +61/-67]
  d486b5741d xsk: fix OOB map writes when deleting elements [1 file, +1/-1]
  14258211d6 regmap: detach regmap from dev on regmap_exit [1 file, +12/-0]
  d562b457e1 mmc: core: Further prevent card detect during shutdown [2 files, +5/-0]
  9bfeeeff2c stackdepot: fix stack_depot_save_flags() in NMI context [2 files, +12/-4]
  a71ddd5b87 sched/numa: fix memory leak due to the overwritten vma->numab_state [1 file, +9/-3]
  835ca042df kasan: make report_lock a raw spinlock [1 file, +3/-3]
  69d319450d mm/gup: handle NULL pages in unpin_user_pages() [1 file, +10/-1]
  1dde3fde62 mm: open-code PageTail in folio_flags() and const_folio_flags() [1 file, +2/-2]
  bd4d2333a3 mm: open-code page_folio() in dump_page() [1 file, +5/-2]
  536ffb4014 mm: fix vrealloc()'s KASAN poisoning logic [1 file, +2/-1]
  fe1a34e92a mm: respect mmap hint address when aligning for THP [1 file, +1/-0]
  5c63e24b1b scsi: ufs: pltfrm: Drop PM runtime reference count after ufshcd_remove() [6 files, +2/-5]
  2cec2d916a memblock: allow zero threshold in validate_numa_converage() [1 file, +2/-2]
  d222934627 epoll: annotate racy check [2 files, +5/-3]
  493326c4f1 block: RCU protect disk->conv_zones_bitmap [2 files, +32/-13]
  b6ce2dbe98 ext4: partial zero eof block on unaligned inode size extension [2 files, +42/-16]
  ff599ad2d2 cleanup: Adjust scoped_guard() macros to avoid potential warning [1 file, +42/-10]
  3946e07552 gpio: free irqs that are still requested when the chip is being removed [1 file, +41/-0]
  ea74e9675b HID: add per device quirk to force bind to hid-generic [3 files, +8/-2]
  17db6ed5a3 media: uvcvideo: RealSense D421 Depth module metadata [1 file, +9/-0]
  0c20fadfd0 media: uvcvideo: Add a quirk for the Kaiweets KTI-W02 infrared camera [1 file, +11/-0]
  3cc5228d5b media: uvcvideo: Force UVC version to 1.0a for 0408:4033 [1 file, +11/-0]
  4150f22342 drm: panel-orientation-quirks: Add quirk for AYA NEO 2 model [1 file, +6/-0]
  5d7f35ed5f drm: panel-orientation-quirks: Add quirk for AYA NEO Founder edition [1 file, +6/-0]
  187d5ff497 drm: panel-orientation-quirks: Add quirk for AYA NEO GEEK [1 file, +6/-0]
  fd09880b16 af_packet: avoid erroring out after sock_init_data() in packet_create() [1 file, +6/-6]
  61686abc2f Bluetooth: L2CAP: do not leave dangling sk pointer on error in l2cap_sock_create() [1 file, +1/-0]
  32df687e12 Bluetooth: RFCOMM: avoid leaving dangling sk pointer in rfcomm_sock_alloc() [1 file, +5/-5]
  8df832e6b9 net: af_can: do not leave a dangling sk pointer in can_create() [1 file, +1/-0]
  03caa9bfb9 net: ieee802154: do not leave a dangling sk pointer in ieee802154_create() [1 file, +7/-5]
  691d6d816f net: inet: do not leave a dangling sk pointer in inet_create() [1 file, +10/-12]
  f44fceb71d net: inet6: do not leave a dangling sk pointer in inet6_create() [1 file, +10/-12]
  987aa730ba bpf: Prevent tailcall infinite loop caused by freplace [5 files, +81/-17]
  051f49d517 net/tcp: Add missing lockdep annotations for TCP-AO hlist traversals [4 files, +29/-23]
  920159e1bf ALSA: usb-audio: Make mic volume workarounds globally applicable [3 files, +45/-48]
  a50b4aa300 bpf: Call free_htab_elem() after htab_unlock_bucket() [1 file, +39/-17]
  da561d5fb6 Bluetooth: hci_conn: Reduce hci_conn_drop() calls in two functions [1 file, +3/-10]
  c55a4c5a04 Bluetooth: hci_conn: Use disable_delayed_work_sync [1 file, +3/-3]
  93a6160dc1 Bluetooth: hci_core: Fix not checking skb length on hci_acldata_packet [1 file, +9/-4]
  b04b4fb91d Bluetooth: Add new quirks for ATS2851 [2 files, +20/-4]
  359fc41e3c Bluetooth: Support new quirks for ATS2851 [2 files, +15/-1]
  166cf43070 net/neighbor: clear error in case strict check is not set [1 file, +1/-0]
  f63a1caae9 tracing/ftrace: disable preemption in syscall probe [2 files, +44/-4]
  d1133dd57e tracing: Use atomic64_inc_return() in trace_clock_counter() [1 file, +1/-1]
  09c083fbea ring-buffer: Limit time with disabled interrupts in rb_check_pages() [1 file, +72/-26]
  c11e2ec9a7 pinmux: Use sequential access to access desc->pinmux data [3 files, +100/-77]
  b865d4e569 scsi: ufs: core: Make DMA mask configuration more flexible [3 files, +13/-9]
  2fcb921c27 bpf: put bpf_link's program when link is safe to be deallocated [1 file, +17/-5]
  bb4a6236a4 leds: class: Protect brightness_show() with led_cdev->led_access mutex [2 files, +12/-4]
  7214d3a64e tracing: Fix function name for trampoline [3 files, +36/-8]
  9e28513fd2 f2fs: fix f2fs_bug_on when uninstalling filesystem call f2fs_evict_inode. [1 file, +3/-1]
  b51aa6a07e PCI: qcom: Add support for IPQ9574 [1 file, +1/-0]
  617bd1e6c3 PCI: Add ACS quirk for Wangxun FF5xxx NICs [1 file, +9/-6]
  1f51ae217d i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock [1 file, +2/-1]
  6d41a2d5c1 f2fs: print message if fscorrupted was found in f2fs_new_node_page() [1 file, +6/-1]
  924f7dd1e8 f2fs: fix to shrink read extent node in batches [1 file, +41/-28]
  1648c7000f serial: 8250_dw: Add Sophgo SG2044 quirk [1 file, +3/-2]
  950210c9c7 Revert "nvme: make keep-alive synchronous operation" [1 file, +10/-7]
  d5b2ddf1f9 io_uring/tctx: work around xa_store() allocation error issue [1 file, +12/-1]
  cd188519d2 scsi: ufs: pltfrm: Dellocate HBA during ufshcd_pltfrm_remove() [1 file, +1/-0]
  a39ad4f507 sched/core: Remove the unnecessary need_resched() check in nohz_csd_func() [1 file, +1/-1]
  f9e144a544 sched/fair: Check idle_cpu() before need_resched() to detect ilb CPU turning busy [1 file, +1/-1]
  b4ec68868c sched/core: Prevent wakeup of ksoftirqd during idle load balance [1 file, +1/-1]
  364dc8070b tracing/eprobe: Fix to release eprobe when failed to add dyn_event [1 file, +5/-0]
  1a678f6829 clocksource: Make negative motion detection more robust [4 files, +20/-7]
  6aeef0214d softirq: Allow raising SCHED_SOFTIRQ from SMP-call-function on RT kernel [1 file, +11/-4]

Changes in 6.12.5
	iTCO_wdt: mask NMI_NOW bit for update_no_reboot_bit() call
	watchdog: xilinx_wwdt: Calculate max_hw_heartbeat_ms using clock frequency
	watchdog: apple: Actually flush writes after requesting watchdog restart
	watchdog: mediatek: Make sure system reset gets asserted in mtk_wdt_restart()
	can: gs_usb: add usb endpoint address detection at driver probe step
	can: c_can: c_can_handle_bus_err(): update statistics if skb allocation fails
	can: sun4i_can: sun4i_can_err(): call can_change_state() even if cf is NULL
	can: hi311x: hi3110_can_ist(): fix potential use-after-free
	can: m_can: m_can_handle_lec_err(): fix {rx,tx}_errors statistics
	can: ifi_canfd: ifi_canfd_handle_lec_err(): fix {rx,tx}_errors statistics
	can: hi311x: hi3110_can_ist(): fix {rx,tx}_errors statistics
	can: sja1000: sja1000_err(): fix {rx,tx}_errors statistics
	can: sun4i_can: sun4i_can_err(): fix {rx,tx}_errors statistics
	can: ems_usb: ems_usb_rx_err(): fix {rx,tx}_errors statistics
	can: f81604: f81604_handle_can_bus_errors(): fix {rx,tx}_errors statistics
	ipvs: fix UB due to uninitialized stack access in ip_vs_protocol_init()
	netfilter: x_tables: fix LED ID check in led_tg_check()
	netfilter: nft_socket: remove WARN_ON_ONCE on maximum cgroup level
	selftests: hid: fix typo and exit code
	net: enetc: Do not configure preemptible TCs if SIs do not support
	ptp: Add error handling for adjfine callback in ptp_clock_adjtime
	net/sched: tbf: correct backlog statistic for GSO packets
	net: hsr: avoid potential out-of-bound access in fill_frame_info()
	bnxt_en: ethtool: Supply ntuple rss context action
	net: Fix icmp host relookup triggering ip_rt_bug
	ipv6: avoid possible NULL deref in modify_prefix_route()
	can: j1939: j1939_session_new(): fix skb reference counting
	platform/x86: asus-wmi: Ignore return value when writing thermal policy
	net: phy: microchip: Reset LAN88xx PHY to ensure clean link state on LAN7800/7850
	net/ipv6: release expired exception dst cached in socket
	dccp: Fix memory leak in dccp_feat_change_recv
	tipc: Fix use-after-free of kernel socket in cleanup_bearer().
	net/smc: initialize close_work early to avoid warning
	net/smc: fix LGR and link use-after-free issue
	net/qed: allow old cards not supporting "num_images" to work
	net: hsr: must allocate more bytes for RedBox support
	ice: fix PHY Clock Recovery availability check
	ice: fix PHY timestamp extraction for ETH56G
	ice: Fix VLAN pruning in switchdev mode
	idpf: set completion tag for "empty" bufs associated with a packet
	ixgbevf: stop attempting IPSEC offload on Mailbox API 1.5
	ixgbe: downgrade logging of unsupported VF API version to debug
	ixgbe: Correct BASE-BX10 compliance code
	igb: Fix potential invalid memory access in igb_init_module()
	netfilter: nft_inner: incorrect percpu area handling under softirq
	Revert "udp: avoid calling sock_def_readable() if possible"
	net: sched: fix erspan_opt settings in cls_flower
	netfilter: ipset: Hold module reference while requesting a module
	netfilter: nft_set_hash: skip duplicated elements pending gc run
	ethtool: Fix wrong mod state in case of verbose and no_mask bitset
	mlxsw: spectrum_acl_flex_keys: Constify struct mlxsw_afk_element_inst
	mlxsw: spectrum_acl_flex_keys: Use correct key block on Spectrum-4
	geneve: do not assume mac header is set in geneve_xmit_skb()
	net/mlx5: HWS: Fix memory leak in mlx5hws_definer_calc_layout
	net/mlx5: HWS: Properly set bwc queue locks lock classes
	net/mlx5e: SD, Use correct mdev to build channel param
	net/mlx5e: Remove workaround to avoid syndrome for internal port
	vsock/test: fix failures due to wrong SO_RCVLOWAT parameter
	vsock/test: fix parameter types in SO_VM_SOCKETS_* calls
	net: avoid potential UAF in default_operstate()
	gpio: grgpio: use a helper variable to store the address of ofdev->dev
	gpio: grgpio: Add NULL check in grgpio_probe
	mmc: mtk-sd: use devm_mmc_alloc_host
	mmc: mtk-sd: Fix error handle of probe function
	mmc: mtk-sd: fix devm_clk_get_optional usage
	mmc: mtk-sd: Fix MMC_CAP2_CRYPTO flag setting
	mmc: sd: SDUC Support Recognition
	mmc: core: Adjust ACMD22 to SDUC
	mmc: core: Use GFP_NOIO in ACMD22
	zram: do not mark idle slots that cannot be idle
	zram: clear IDLE flag in mark_idle()
	ntp: Remove invalid cast in time offset math
	f2fs: clean up w/ F2FS_{BLK_TO_BYTES,BTYES_TO_BLK}
	f2fs: fix to adjust appropriate length for fiemap
	f2fs: fix to requery extent which cross boundary of inquiry
	i3c: master: Replace hard code 2 with macro I3C_ADDR_SLOT_STATUS_BITS
	i3c: master: Extend address status bit to 4 and add I3C_ADDR_SLOT_EXT_DESIRED
	i3c: master: Fix dynamic address leak when 'assigned-address' is present
	drm/amd/display: calculate final viewport before TAP optimization
	drm/amd/display: Ignore scalar validation failure if pipe is phantom
	scsi: ufs: core: Always initialize the UIC done completion
	scsi: ufs: core: Add ufshcd_send_bsg_uic_cmd() for UFS BSG
	bpf, vsock: Fix poll() missing a queue
	bpf, vsock: Invoke proto::close on close()
	xsk: always clear DMA mapping information when unmapping the pool
	bpftool: fix potential NULL pointer dereferencing in prog_dump()
	drm/sti: Add __iomem for mixer_dbg_mxn's parameter
	tcp_bpf: Fix the sk_mem_uncharge logic in tcp_bpf_sendmsg
	ALSA: seq: ump: Fix seq port updates per FB info notify
	ALSA: usb-audio: Notify xrun for low-latency mode
	tools: Override makefile ARCH variable if defined, but empty
	spi: mpc52xx: Add cancel_work_sync before module remove
	ASoC: SOF: ipc3-topology: Convert the topology pin index to ALH dai index
	ASoC: SOF: ipc3-topology: fix resource leaks in sof_ipc3_widget_setup_comp_dai()
	pmdomain: core: Add missing put_device()
	pmdomain: core: Fix error path in pm_genpd_init() when ida alloc fails
	nvme: don't apply NVME_QUIRK_DEALLOCATE_ZEROES when DSM is not supported
	x86/pkeys: Change caller of update_pkru_in_sigframe()
	x86/pkeys: Ensure updated PKRU value is XRSTOR'd
	bpf: Ensure reg is PTR_TO_STACK in process_iter_arg
	irqchip/stm32mp-exti: CONFIG_STM32MP_EXTI should not default to y when compile-testing
	drivers/virt: pkvm: Don't fail ioremap() call if MMIO_GUARD fails
	bpf: Don't mark STACK_INVALID as STACK_MISC in mark_stack_slot_misc
	bpf: Fix narrow scalar spill onto 64-bit spilled scalar slots
	nvme-fabrics: handle zero MAXCMD without closing the connection
	nvme-tcp: fix the memleak while create new ctrl failed
	nvme-rdma: unquiesce admin_q before destroy it
	scsi: sg: Fix slab-use-after-free read in sg_release()
	scsi: scsi_debug: Fix hrtimer support for ndelay
	ASoC: mediatek: mt8188-mt6359: Remove hardcoded dmic codec
	drm/v3d: Enable Performance Counters before clearing them
	ocfs2: free inode when ocfs2_get_init_inode() fails
	scatterlist: fix incorrect func name in kernel-doc
	iio: magnetometer: yas530: use signed integer type for clamp limits
	smb: client: fix potential race in cifs_put_tcon()
	bpf: Handle BPF_EXIST and BPF_NOEXIST for LPM trie
	bpf: Remove unnecessary kfree(im_node) in lpm_trie_update_elem
	bpf: Handle in-place update for full LPM trie correctly
	bpf: Fix exact match conditions in trie_get_next_key()
	x86/CPU/AMD: WARN when setting EFER.AUTOIBRS if and only if the WRMSR fails
	rust: allow `clippy::needless_lifetimes`
	HID: i2c-hid: Revert to using power commands to wake on resume
	HID: wacom: fix when get product name maybe null pointer
	LoongArch: Add architecture specific huge_pte_clear()
	LoongArch: KVM: Protect kvm_check_requests() with SRCU
	ksmbd: fix Out-of-Bounds Read in ksmbd_vfs_stream_read
	ksmbd: fix Out-of-Bounds Write in ksmbd_vfs_stream_write
	watchdog: rti: of: honor timeout-sec property
	can: dev: can_set_termination(): allow sleeping GPIOs
	can: mcp251xfd: mcp251xfd_get_tef_len(): work around erratum DS80000789E 6.
	tracing: Fix cmp_entries_dup() to respect sort() comparison rules
	net :mana :Request a V2 response version for MANA_QUERY_GF_STAT
	iommufd: Fix out_fput in iommufd_fault_alloc()
	arm64: mm: Fix zone_dma_limit calculation
	arm64: Ensure bits ASID[15:8] are masked out when the kernel uses 8-bit ASIDs
	arm64: ptrace: fix partial SETREGSET for NT_ARM_TAGGED_ADDR_CTRL
	arm64: ptrace: fix partial SETREGSET for NT_ARM_FPMR
	arm64: ptrace: fix partial SETREGSET for NT_ARM_POE
	ALSA: usb-audio: Fix a DMA to stack memory bug
	ALSA: usb-audio: Add extra PID for RME Digiface USB
	ALSA: hda/realtek: fix micmute LEDs don't work on HP Laptops
	ALSA: usb-audio: add mixer mapping for Corsair HS80
	ALSA: hda/realtek: Enable mute and micmute LED on HP ProBook 430 G8
	ALSA: hda/realtek: Add support for Samsung Galaxy Book3 360 (NP730QFG)
	scsi: qla2xxx: Fix abort in bsg timeout
	scsi: qla2xxx: Fix NVMe and NPIV connect issue
	scsi: qla2xxx: Supported speed displayed incorrectly for VPorts
	scsi: qla2xxx: Fix use after free on unload
	scsi: qla2xxx: Remove check req_sg_cnt should be equal to rsp_sg_cnt
	scsi: ufs: core: sysfs: Prevent div by zero
	scsi: ufs: core: Cancel RTC work during ufshcd_remove()
	scsi: ufs: qcom: Only free platform MSIs when ESI is enabled
	scsi: ufs: pltfrm: Disable runtime PM during removal of glue drivers
	scsi: ufs: core: Add missing post notify for power mode change
	nilfs2: fix potential out-of-bounds memory access in nilfs_find_entry()
	fs/smb/client: avoid querying SMB2_OP_QUERY_WSL_EA for SMB3 POSIX
	fs/smb/client: Implement new SMB3 POSIX type
	fs/smb/client: cifs_prime_dcache() for SMB3 POSIX reparse points
	smb3.1.1: fix posix mounts to older servers
	io_uring: Change res2 parameter type in io_uring_cmd_done
	bcache: revert replacing IS_ERR_OR_NULL with IS_ERR again
	Revert "readahead: properly shorten readahead when falling back to do_page_cache_ra()"
	pmdomain: imx: gpcv2: Adjust delay after power up handshake
	selftests/damon: add _damon_sysfs.py to TEST_FILES
	selftest: hugetlb_dio: fix test naming
	cacheinfo: Allocate memory during CPU hotplug if not done from the primary CPU
	x86/cacheinfo: Delete global num_cache_leaves
	drm/amdkfd: hard-code cacheline for gc943,gc944
	drm/dp_mst: Fix MST sideband message body length check
	drm/amdkfd: add MEC version that supports no PCIe atomics for GFX12
	drm/amd/pm: fix and simplify workload handling
	drm/dp_mst: Verify request type in the corresponding down message reply
	drm/dp_mst: Fix resetting msg rx state after topology removal
	drm/amd/display: Correct prefetch calculation
	drm/amd/display: Limit VTotal range to max hw cap minus fp
	drm/amd/display: Add a left edge pixel if in YCbCr422 or YCbCr420 and odm
	drm/amdgpu/hdp6.0: do a posting read when flushing HDP
	drm/amdgpu/hdp4.0: do a posting read when flushing HDP
	drm/amdgpu/hdp5.0: do a posting read when flushing HDP
	drm/amdgpu/hdp7.0: do a posting read when flushing HDP
	drm/amdgpu/hdp5.2: do a posting read when flushing HDP
	modpost: Add .irqentry.text to OTHER_SECTIONS
	x86/kexec: Restore GDT on return from ::preserve_context kexec
	bpf: fix OOB devmap writes when deleting elements
	dma-buf: fix dma_fence_array_signaled v4
	dma-fence: Fix reference leak on fence merge failure path
	dma-fence: Use kernel's sort for merging fences
	xsk: fix OOB map writes when deleting elements
	regmap: detach regmap from dev on regmap_exit
	arch_numa: Restore nid checks before registering a memblock with a node
	mmc: sdhci-pci: Add DMI quirk for missing CD GPIO on Vexia Edu Atla 10 tablet
	mmc: core: Further prevent card detect during shutdown
	x86/cpu: Add Lunar Lake to list of CPUs with a broken MONITOR implementation
	ocfs2: update seq_file index in ocfs2_dlm_seq_next
	stackdepot: fix stack_depot_save_flags() in NMI context
	lib: stackinit: hide never-taken branch from compiler
	sched/numa: fix memory leak due to the overwritten vma->numab_state
	kasan: make report_lock a raw spinlock
	mm/gup: handle NULL pages in unpin_user_pages()
	mm/mempolicy: fix migrate_to_node() assuming there is at least one VMA in a MM
	x86/cpu/topology: Remove limit of CPUs due to disabled IO/APIC
	x86/mm: Add _PAGE_NOPTISHADOW bit to avoid updating userspace page tables
	mm/damon: fix order of arguments in damos_before_apply tracepoint
	mm: memcg: declare do_memsw_account inline
	mm: open-code PageTail in folio_flags() and const_folio_flags()
	mm: open-code page_folio() in dump_page()
	mm: fix vrealloc()'s KASAN poisoning logic
	mm: respect mmap hint address when aligning for THP
	scsi: ufs: pltfrm: Drop PM runtime reference count after ufshcd_remove()
	memblock: allow zero threshold in validate_numa_converage()
	rust: enable arbitrary_self_types and remove `Receiver`
	s390/pci: Sort PCI functions prior to creating virtual busses
	s390/pci: Use topology ID for multi-function devices
	s390/pci: Ignore RID for isolated VFs
	epoll: annotate racy check
	kselftest/arm64: Log fp-stress child startup errors to stdout
	s390/cpum_sf: Handle CPU hotplug remove during sampling
	block: RCU protect disk->conv_zones_bitmap
	btrfs: don't take dev_replace rwsem on task already holding it
	btrfs: avoid unnecessary device path update for the same device
	btrfs: canonicalize the device path before adding it
	btrfs: do not clear read-only when adding sprout device
	kselftest/arm64: Don't leak pipe fds in pac.exec_sign_all()
	ext4: partial zero eof block on unaligned inode size extension
	crypto: ecdsa - Avoid signed integer overflow on signature decoding
	kcsan: Turn report_filterlist_lock into a raw_spinlock
	hwmon: (nct6775) Add 665-ACE/600M-CL to ASUS WMI monitoring list
	ACPI: x86: Make UART skip quirks work on PCI UARTs without an UID
	ACPI: x86: Add adev NULL check to acpi_quirk_skip_serdev_enumeration()
	ACPI: video: force native for Apple MacbookPro11,2 and Air7,2
	perf/x86/amd: Warn only on new bits set
	cleanup: Adjust scoped_guard() macros to avoid potential warning
	iio: magnetometer: fix if () scoped_guard() formatting
	timekeeping: Always check for negative motion
	gpio: free irqs that are still requested when the chip is being removed
	spi: spi-fsl-lpspi: Adjust type of scldiv
	soc: qcom: llcc: Use designated initializers for LLC settings
	HID: add per device quirk to force bind to hid-generic
	firmware: qcom: scm: Allow QSEECOM on Lenovo Yoga Slim 7x
	soc: qcom: pd-mapper: Add QCM6490 PD maps
	media: uvcvideo: RealSense D421 Depth module metadata
	media: uvcvideo: Add a quirk for the Kaiweets KTI-W02 infrared camera
	media: uvcvideo: Force UVC version to 1.0a for 0408:4033
	media: cx231xx: Add support for Dexatek USB Video Grabber 1d19:6108
	mmc: core: Add SD card quirk for broken poweroff notification
	mmc: sdhci-esdhc-imx: enable quirks SDHCI_QUIRK_NO_LED
	firmware: qcom: scm: Allow QSEECOM on Dell XPS 13 9345
	soc: imx8m: Probe the SoC driver as platform driver
	regmap: maple: Provide lockdep (sub)class for maple tree's internal lock
	selftests/resctrl: Protect against array overflow when reading strings
	sched_ext: add a missing rcu_read_lock/unlock pair at scx_select_cpu_dfl()
	HID: magicmouse: Apple Magic Trackpad 2 USB-C driver support
	drm/xe/pciids: separate RPL-U and RPL-P PCI IDs
	drm/xe/pciids: separate ARL and MTL PCI IDs
	drm/vc4: hdmi: Avoid log spam for audio start failure
	drm/vc4: hvs: Set AXI panic modes for the HVS
	drm/xe/pciids: Add PVC's PCI device ID macros
	wifi: rtw88: use ieee80211_purge_tx_queue() to purge TX skb
	drm/xe/pciid: Add new PCI id for ARL
	drm: panel-orientation-quirks: Add quirk for AYA NEO 2 model
	drm: panel-orientation-quirks: Add quirk for AYA NEO Founder edition
	drm: panel-orientation-quirks: Add quirk for AYA NEO GEEK
	drm/bridge: it6505: Enable module autoloading
	drm/mcde: Enable module autoloading
	wifi: rtw89: check return value of ieee80211_probereq_get() for RNR
	drm/amd/display: Fix out-of-bounds access in 'dcn21_link_encoder_create'
	drm/radeon/r600_cs: Fix possible int overflow in r600_packet3_check()
	ASoC: Intel: sof_rt5682: Add HDMI-In capture with rt5682 support for MTL.
	dlm: fix possible lkb_resource null dereference
	drm/amd/display: skip disable CRTC in seemless bootup case
	drm/amd/display: Fix garbage or black screen when resetting otg
	drm/amd/display: disable SG displays on cyan skillfish
	drm/xe/ptl: L3bank mask is not available on the media GT
	drm/xe/xe3: Add initial set of workarounds
	drm/display: Fix building with GCC 15
	ALSA: hda: Use own quirk lookup helper
	ALSA: hda/conexant: Use the new codec SSID matching
	ALSA: hda/realtek: Use codec SSID matching for Lenovo devices
	r8169: don't apply UDP padding quirk on RTL8126A
	samples/bpf: Fix a resource leak
	wifi: ath12k: fix atomic calls in ath12k_mac_op_set_bitrate_mask()
	accel/qaic: Add AIC080 support
	drm/amd/display: Full exit out of IPS2 when all allow signals have been cleared
	net: fec_mpc52xx_phy: Use %pa to format resource_size_t
	net: ethernet: fs_enet: Use %pa to format resource_size_t
	net/sched: cbs: Fix integer overflow in cbs_set_port_rate()
	af_packet: avoid erroring out after sock_init_data() in packet_create()
	Bluetooth: L2CAP: do not leave dangling sk pointer on error in l2cap_sock_create()
	Bluetooth: RFCOMM: avoid leaving dangling sk pointer in rfcomm_sock_alloc()
	net: af_can: do not leave a dangling sk pointer in can_create()
	net: ieee802154: do not leave a dangling sk pointer in ieee802154_create()
	net: inet: do not leave a dangling sk pointer in inet_create()
	net: inet6: do not leave a dangling sk pointer in inet6_create()
	wifi: ath10k: avoid NULL pointer error during sdio remove
	wifi: ath5k: add PCI ID for SX76X
	wifi: ath5k: add PCI ID for Arcadyan devices
	fanotify: allow reporting errors on failure to open fd
	bpf: Prevent tailcall infinite loop caused by freplace
	ASoC: sdw_utils: Add support for exclusion DAI quirks
	ASoC: sdw_utils: Add a quirk to allow the cs42l43 mic DAI to be ignored
	ASoC: Intel: sof_sdw: Add quirk for cs42l43 system using host DMICs
	ASoC: Intel: sof_sdw: Add quirks for some new Lenovo laptops
	drm/xe/guc/ct: Flush g2h worker in case of g2h response timeout
	drm/panel: simple: Add Microchip AC69T88A LVDS Display panel
	net: sfp: change quirks for Alcatel Lucent G-010S-P
	net: stmmac: Programming sequence for VLAN packets with split header
	drm/sched: memset() 'job' in drm_sched_job_init()
	drm/amd/display: Adding array index check to prevent memory corruption
	drm/amdgpu/gfx9: Add cleaner shader for GFX9.4.2
	drm/amdgpu: clear RB_OVERFLOW bit when enabling interrupts for vega20_ih
	drm/amdgpu: Dereference the ATCS ACPI buffer
	netlink: specs: Add missing bitset attrs to ethtool spec
	drm/amdgpu: refine error handling in amdgpu_ttm_tt_pin_userptr
	ASoC: sdw_utils: Add quirk to exclude amplifier function
	ASoC: Intel: soc-acpi-intel-arl-match: Add rt722 and rt1320 support
	drm/amd/display: Fix underflow when playing 8K video in full screen mode
	mptcp: annotate data-races around subflow->fully_established
	dma-debug: fix a possible deadlock on radix_lock
	jfs: array-index-out-of-bounds fix in dtReadFirst
	jfs: fix shift-out-of-bounds in dbSplit
	jfs: fix array-index-out-of-bounds in jfs_readdir
	jfs: add a check to prevent array-index-out-of-bounds in dbAdjTree
	fsl/fman: Validate cell-index value obtained from Device Tree
	net/tcp: Add missing lockdep annotations for TCP-AO hlist traversals
	drm/panic: Add ABGR2101010 support
	drm/amd/display: Remove hw w/a toggle if on DP2/HPO
	drm/amd/display: parse umc_info or vram_info based on ASIC
	drm/amd/display: Prune Invalid Modes For HDMI Output
	drm/amdgpu: skip amdgpu_device_cache_pci_state under sriov
	virtio-net: fix overflow inside virtnet_rq_alloc
	ALSA: usb-audio: Make mic volume workarounds globally applicable
	drm/amdgpu: set the right AMDGPU sg segment limitation
	wifi: ipw2x00: libipw_rx_any(): fix bad alignment
	wifi: brcmfmac: Fix oops due to NULL pointer dereference in brcmf_sdiod_sglist_rw()
	bpf: Call free_htab_elem() after htab_unlock_bucket()
	mptcp: fix possible integer overflow in mptcp_reset_tout_timer
	dsa: qca8k: Use nested lock to avoid splat
	i2c: i801: Add support for Intel Panther Lake
	Bluetooth: hci_conn: Reduce hci_conn_drop() calls in two functions
	Bluetooth: btusb: Add RTL8852BE device 0489:e123 to device tables
	Bluetooth: btusb: Add USB HW IDs for MT7920/MT7925
	Bluetooth: hci_conn: Use disable_delayed_work_sync
	Bluetooth: hci_core: Fix not checking skb length on hci_acldata_packet
	Bluetooth: Add new quirks for ATS2851
	Bluetooth: Support new quirks for ATS2851
	Bluetooth: Set quirks for ATS2851
	Bluetooth: btusb: Add new VID/PID 0489/e111 for MT7925
	Bluetooth: btusb: Add new VID/PID 0489/e124 for MT7925
	Bluetooth: btusb: Add 3 HWIDs for MT7925
	ASoC: hdmi-codec: reorder channel allocation list
	rocker: fix link status detection in rocker_carrier_init()
	net/neighbor: clear error in case strict check is not set
	netpoll: Use rcu_access_pointer() in __netpoll_setup
	pinctrl: freescale: fix COMPILE_TEST error with PINCTRL_IMX_SCU
	rtla: Fix consistency in getopt_long for timerlat_hist
	tracing/ftrace: disable preemption in syscall probe
	tracing: Use atomic64_inc_return() in trace_clock_counter()
	tools/rtla: fix collision with glibc sched_attr/sched_set_attr
	rtla/timerlat: Make timerlat_top_cpu->*_count unsigned long long
	rtla/timerlat: Make timerlat_hist_cpu->*_count unsigned long long
	scsi: hisi_sas: Add cond_resched() for no forced preemption model
	scsi: hisi_sas: Create all dump files during debugfs initialization
	ring-buffer: Limit time with disabled interrupts in rb_check_pages()
	pinmux: Use sequential access to access desc->pinmux data
	scsi: ufs: core: Make DMA mask configuration more flexible
	iommu/amd: Fix corruption when mapping large pages from 0
	bpf: put bpf_link's program when link is safe to be deallocated
	scsi: lpfc: Call lpfc_sli4_queue_unset() in restart and rmmod paths
	scsi: lpfc: Check SLI_ACTIVE flag in FDMI cmpl before submitting follow up FDMI
	scsi: lpfc: Prevent NDLP reference count underflow in dev_loss_tmo callback
	clk: qcom: rcg2: add clk_rcg2_shared_floor_ops
	clk: qcom: rpmh: add support for SAR2130P
	clk: qcom: tcsrcc-sm8550: add SAR2130P support
	clk: qcom: dispcc-sm8550: enable support for SAR2130P
	clk: qcom: clk-alpha-pll: Add NSS HUAYRA ALPHA PLL support for ipq9574
	leds: class: Protect brightness_show() with led_cdev->led_access mutex
	scsi: st: Don't modify unknown block number in MTIOCGET
	scsi: st: Add MTIOCGET and MTLOAD to ioctls allowed after device reset
	pinctrl: qcom-pmic-gpio: add support for PM8937
	pinctrl: qcom: spmi-mpp: Add PM8937 compatible
	thermal/drivers/qcom/tsens-v1: Add support for MSM8937 tsens
	nvdimm: rectify the illogical code within nd_dax_probe()
	smb: client: memcpy() with surrounding object base address
	tracing: Fix function name for trampoline
	tools/rtla: Enhance argument parsing in timerlat_load.py
	verification/dot2: Improve dot parser robustness
	mailbox: pcc: Check before sending MCTP PCC response ACK
	f2fs: fix f2fs_bug_on when uninstalling filesystem call f2fs_evict_inode.
	KMSAN: uninit-value in inode_go_dump (5)
	i3c: mipi-i3c-hci: Mask ring interrupts before ring stop request
	PCI: qcom: Add support for IPQ9574
	PCI: vmd: Add DID 8086:B06F and 8086:B60B for Intel client SKUs
	PCI: vmd: Set devices to D0 before enabling PM L1 Substates
	PCI: Detect and trust built-in Thunderbolt chips
	PCI: starfive: Enable controller runtime PM before probing host bridge
	PCI: Add 'reset_subordinate' to reset hierarchy below bridge
	PCI: Add ACS quirk for Wangxun FF5xxx NICs
	remoteproc: qcom: pas: enable SAR2130P audio DSP support
	i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock
	f2fs: print message if fscorrupted was found in f2fs_new_node_page()
	f2fs: fix to shrink read extent node in batches
	f2fs: add a sysfs node to limit max read extent count per-inode
	ACPI: x86: Add skip i2c clients quirk for Acer Iconia One 8 A1-840
	ACPI: x86: Clean up Asus entries in acpi_quirk_skip_dmi_ids[]
	LoongArch: Fix sleeping in atomic context for PREEMPT_RT
	fs/ntfs3: Fix warning in ni_fiemap
	fs/ntfs3: Fix case when unmarked clusters intersect with zone
	regulator: qcom-rpmh: Update ranges for FTSMPS525
	usb: chipidea: add CI_HDRC_HAS_SHORT_PKT_LIMIT flag
	usb: chipidea: udc: limit usb request length to max 16KB
	usb: chipidea: udc: create bounce buffer for problem sglist entries if possible
	usb: chipidea: udc: handle USB Error Interrupt if IOC not set
	usb: typec: ucsi: Do not call ACPI _DSM method for UCSI read operations
	iio: adc: ad7192: properly check spi_get_device_match_data()
	iio: light: ltr501: Add LTER0303 to the supported devices
	usb: typec: ucsi: glink: be more precise on orientation-aware ports
	ASoC: amd: yc: fix internal mic on Redmi G 2022
	drm/amdgpu/vcn: reset fw_shared when VCPU buffers corrupted on vcn v4.0.3
	MIPS: Loongson64: DTS: Really fix PCIe port nodes for ls7a
	ASoC: amd: yc: Add quirk for microphone on Lenovo Thinkpad T14s Gen 6 21M1CTO1WW
	powerpc/prom_init: Fixup missing powermac #size-cells
	misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle
	rtc: cmos: avoid taking rtc_lock for extended period of time
	serial: 8250_dw: Add Sophgo SG2044 quirk
	Revert "nvme: make keep-alive synchronous operation"
	irqchip/gicv3-its: Add workaround for hip09 ITS erratum 162100801
	smb: client: don't try following DFS links in cifs_tree_connect()
	setlocalversion: work around "git describe" performance
	io_uring/tctx: work around xa_store() allocation error issue
	scsi: ufs: pltfrm: Dellocate HBA during ufshcd_pltfrm_remove()
	drm/xe/devcoredump: Use drm_puts and already cached local variables
	drm/xe/devcoredump: Improve section headings and add tile info
	drm/xe/devcoredump: Add ASCII85 dump helper function
	drm/xe/guc: Copy GuC log prior to dumping
	drm/xe/forcewake: Add a helper xe_force_wake_ref_has_domain()
	drm/xe/devcoredump: Update handling of xe_force_wake_get return
	drm/amd/display: Add option to retrieve detile buffer size
	sched: fix warning in sched_setaffinity
	sched/core: Remove the unnecessary need_resched() check in nohz_csd_func()
	sched/fair: Check idle_cpu() before need_resched() to detect ilb CPU turning busy
	sched/core: Prevent wakeup of ksoftirqd during idle load balance
	sched/deadline: Fix warning in migrate_enable for boosted tasks
	btrfs: drop unused parameter options from open_ctree()
	btrfs: drop unused parameter data from btrfs_fill_super()
	btrfs: fix mount failure due to remount races
	btrfs: fix missing snapshot drew unlock when root is dead during swap activation
	clk: en7523: Initialize num before accessing hws in en7523_register_clocks()
	tracing/eprobe: Fix to release eprobe when failed to add dyn_event
	x86: Fix build regression with CONFIG_KEXEC_JUMP enabled
	Revert "unicode: Don't special case ignorable code points"
	vfio/mlx5: Align the page tracking max message size with the device capability
	selftests/ftrace: adjust offset for kprobe syntax error test
	KVM: x86/mmu: Ensure that kvm_release_pfn_clean() takes exact pfn from kvm_faultin_pfn()
	jffs2: Prevent rtime decompress memory corruption
	jffs2: Fix rtime decompressor
	media: ipu6: use the IPU6 DMA mapping APIs to do mapping
	ocfs2: Revert "ocfs2: fix the la space leak when unmounting an ocfs2 volume"
	net/mlx5: unique names for per device caches
	ASoC: Intel: avs: Fix return status of avs_pcm_hw_constraints_init()
	drm/amdgpu: rework resume handling for display (v2)
	ALSA: hda: Fix build error without CONFIG_SND_DEBUG
	Revert "drm/amd/display: parse umc_info or vram_info based on ASIC"
	s390/pci: Fix leak of struct zpci_dev when zpci_add_device() fails
	ALSA: hda/realtek: Fix spelling mistake "Firelfy" -> "Firefly"
	timekeeping: Remove CONFIG_DEBUG_TIMEKEEPING
	clocksource: Make negative motion detection more robust
	softirq: Allow raising SCHED_SOFTIRQ from SMP-call-function on RT kernel
	Linux 6.12.5

Change-Id: If1b834954ed2ee1a16886f9a9909c6ca62d93b6c
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
2024-12-15 11:57:47 +00:00

1618 lines
42 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 Advanced Micro Devices, Inc.
*
* Author: Joerg Roedel <joerg.roedel@amd.com>
*/
#define pr_fmt(fmt) "DMA-API: " fmt
#include <linux/sched/task_stack.h>
#include <linux/scatterlist.h>
#include <linux/dma-map-ops.h>
#include <linux/sched/task.h>
#include <linux/stacktrace.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/export.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/ctype.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <asm/sections.h>
#include "debug.h"
#define HASH_SIZE 16384ULL
#define HASH_FN_SHIFT 13
#define HASH_FN_MASK (HASH_SIZE - 1)
#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
/* If the pool runs out, add this many new entries at once */
#define DMA_DEBUG_DYNAMIC_ENTRIES (PAGE_SIZE / sizeof(struct dma_debug_entry))
enum {
dma_debug_single,
dma_debug_sg,
dma_debug_coherent,
dma_debug_resource,
};
enum map_err_types {
MAP_ERR_CHECK_NOT_APPLICABLE,
MAP_ERR_NOT_CHECKED,
MAP_ERR_CHECKED,
};
#define DMA_DEBUG_STACKTRACE_ENTRIES 5
/**
* struct dma_debug_entry - track a dma_map* or dma_alloc_coherent mapping
* @list: node on pre-allocated free_entries list
* @dev: 'dev' argument to dma_map_{page|single|sg} or dma_alloc_coherent
* @dev_addr: dma address
* @size: length of the mapping
* @type: single, page, sg, coherent
* @direction: enum dma_data_direction
* @sg_call_ents: 'nents' from dma_map_sg
* @sg_mapped_ents: 'mapped_ents' from dma_map_sg
* @pfn: page frame of the start address
* @offset: offset of mapping relative to pfn
* @map_err_type: track whether dma_mapping_error() was checked
* @stack_len: number of backtrace entries in @stack_entries
* @stack_entries: stack of backtrace history
*/
struct dma_debug_entry {
struct list_head list;
struct device *dev;
u64 dev_addr;
u64 size;
int type;
int direction;
int sg_call_ents;
int sg_mapped_ents;
unsigned long pfn;
size_t offset;
enum map_err_types map_err_type;
#ifdef CONFIG_STACKTRACE
unsigned int stack_len;
unsigned long stack_entries[DMA_DEBUG_STACKTRACE_ENTRIES];
#endif
} ____cacheline_aligned_in_smp;
typedef bool (*match_fn)(struct dma_debug_entry *, struct dma_debug_entry *);
struct hash_bucket {
struct list_head list;
spinlock_t lock;
};
/* Hash list to save the allocated dma addresses */
static struct hash_bucket dma_entry_hash[HASH_SIZE];
/* List of pre-allocated dma_debug_entry's */
static LIST_HEAD(free_entries);
/* Lock for the list above */
static DEFINE_SPINLOCK(free_entries_lock);
/* Global disable flag - will be set in case of an error */
static bool global_disable __read_mostly;
/* Early initialization disable flag, set at the end of dma_debug_init */
static bool dma_debug_initialized __read_mostly;
static inline bool dma_debug_disabled(void)
{
return global_disable || !dma_debug_initialized;
}
/* Global error count */
static u32 error_count;
/* Global error show enable*/
static u32 show_all_errors __read_mostly;
/* Number of errors to show */
static u32 show_num_errors = 1;
static u32 num_free_entries;
static u32 min_free_entries;
static u32 nr_total_entries;
/* number of preallocated entries requested by kernel cmdline */
static u32 nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES;
/* per-driver filter related state */
#define NAME_MAX_LEN 64
static char current_driver_name[NAME_MAX_LEN] __read_mostly;
static struct device_driver *current_driver __read_mostly;
static DEFINE_RWLOCK(driver_name_lock);
static const char *const maperr2str[] = {
[MAP_ERR_CHECK_NOT_APPLICABLE] = "dma map error check not applicable",
[MAP_ERR_NOT_CHECKED] = "dma map error not checked",
[MAP_ERR_CHECKED] = "dma map error checked",
};
static const char *type2name[] = {
[dma_debug_single] = "single",
[dma_debug_sg] = "scatter-gather",
[dma_debug_coherent] = "coherent",
[dma_debug_resource] = "resource",
};
static const char *dir2name[] = {
[DMA_BIDIRECTIONAL] = "DMA_BIDIRECTIONAL",
[DMA_TO_DEVICE] = "DMA_TO_DEVICE",
[DMA_FROM_DEVICE] = "DMA_FROM_DEVICE",
[DMA_NONE] = "DMA_NONE",
};
/*
* The access to some variables in this macro is racy. We can't use atomic_t
* here because all these variables are exported to debugfs. Some of them even
* writeable. This is also the reason why a lock won't help much. But anyway,
* the races are no big deal. Here is why:
*
* error_count: the addition is racy, but the worst thing that can happen is
* that we don't count some errors
* show_num_errors: the subtraction is racy. Also no big deal because in
* worst case this will result in one warning more in the
* system log than the user configured. This variable is
* writeable via debugfs.
*/
static inline void dump_entry_trace(struct dma_debug_entry *entry)
{
#ifdef CONFIG_STACKTRACE
if (entry) {
pr_warn("Mapped at:\n");
stack_trace_print(entry->stack_entries, entry->stack_len, 0);
}
#endif
}
static bool driver_filter(struct device *dev)
{
struct device_driver *drv;
unsigned long flags;
bool ret;
/* driver filter off */
if (likely(!current_driver_name[0]))
return true;
/* driver filter on and initialized */
if (current_driver && dev && dev->driver == current_driver)
return true;
/* driver filter on, but we can't filter on a NULL device... */
if (!dev)
return false;
if (current_driver || !current_driver_name[0])
return false;
/* driver filter on but not yet initialized */
drv = dev->driver;
if (!drv)
return false;
/* lock to protect against change of current_driver_name */
read_lock_irqsave(&driver_name_lock, flags);
ret = false;
if (drv->name &&
strncmp(current_driver_name, drv->name, NAME_MAX_LEN - 1) == 0) {
current_driver = drv;
ret = true;
}
read_unlock_irqrestore(&driver_name_lock, flags);
return ret;
}
#define err_printk(dev, entry, format, arg...) do { \
error_count += 1; \
if (driver_filter(dev) && \
(show_all_errors || show_num_errors > 0)) { \
WARN(1, pr_fmt("%s %s: ") format, \
dev ? dev_driver_string(dev) : "NULL", \
dev ? dev_name(dev) : "NULL", ## arg); \
dump_entry_trace(entry); \
} \
if (!show_all_errors && show_num_errors > 0) \
show_num_errors -= 1; \
} while (0);
/*
* Hash related functions
*
* Every DMA-API request is saved into a struct dma_debug_entry. To
* have quick access to these structs they are stored into a hash.
*/
static int hash_fn(struct dma_debug_entry *entry)
{
/*
* Hash function is based on the dma address.
* We use bits 20-27 here as the index into the hash
*/
return (entry->dev_addr >> HASH_FN_SHIFT) & HASH_FN_MASK;
}
/*
* Request exclusive access to a hash bucket for a given dma_debug_entry.
*/
static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry,
unsigned long *flags)
__acquires(&dma_entry_hash[idx].lock)
{
int idx = hash_fn(entry);
unsigned long __flags;
spin_lock_irqsave(&dma_entry_hash[idx].lock, __flags);
*flags = __flags;
return &dma_entry_hash[idx];
}
/*
* Give up exclusive access to the hash bucket
*/
static void put_hash_bucket(struct hash_bucket *bucket,
unsigned long flags)
__releases(&bucket->lock)
{
spin_unlock_irqrestore(&bucket->lock, flags);
}
static bool exact_match(struct dma_debug_entry *a, struct dma_debug_entry *b)
{
return ((a->dev_addr == b->dev_addr) &&
(a->dev == b->dev)) ? true : false;
}
static bool containing_match(struct dma_debug_entry *a,
struct dma_debug_entry *b)
{
if (a->dev != b->dev)
return false;
if ((b->dev_addr <= a->dev_addr) &&
((b->dev_addr + b->size) >= (a->dev_addr + a->size)))
return true;
return false;
}
/*
* Search a given entry in the hash bucket list
*/
static struct dma_debug_entry *__hash_bucket_find(struct hash_bucket *bucket,
struct dma_debug_entry *ref,
match_fn match)
{
struct dma_debug_entry *entry, *ret = NULL;
int matches = 0, match_lvl, last_lvl = -1;
list_for_each_entry(entry, &bucket->list, list) {
if (!match(ref, entry))
continue;
/*
* Some drivers map the same physical address multiple
* times. Without a hardware IOMMU this results in the
* same device addresses being put into the dma-debug
* hash multiple times too. This can result in false
* positives being reported. Therefore we implement a
* best-fit algorithm here which returns the entry from
* the hash which fits best to the reference value
* instead of the first-fit.
*/
matches += 1;
match_lvl = 0;
entry->size == ref->size ? ++match_lvl : 0;
entry->type == ref->type ? ++match_lvl : 0;
entry->direction == ref->direction ? ++match_lvl : 0;
entry->sg_call_ents == ref->sg_call_ents ? ++match_lvl : 0;
if (match_lvl == 4) {
/* perfect-fit - return the result */
return entry;
} else if (match_lvl > last_lvl) {
/*
* We found an entry that fits better then the
* previous one or it is the 1st match.
*/
last_lvl = match_lvl;
ret = entry;
}
}
/*
* If we have multiple matches but no perfect-fit, just return
* NULL.
*/
ret = (matches == 1) ? ret : NULL;
return ret;
}
static struct dma_debug_entry *bucket_find_exact(struct hash_bucket *bucket,
struct dma_debug_entry *ref)
{
return __hash_bucket_find(bucket, ref, exact_match);
}
static struct dma_debug_entry *bucket_find_contain(struct hash_bucket **bucket,
struct dma_debug_entry *ref,
unsigned long *flags)
{
struct dma_debug_entry *entry, index = *ref;
int limit = min(HASH_SIZE, (index.dev_addr >> HASH_FN_SHIFT) + 1);
for (int i = 0; i < limit; i++) {
entry = __hash_bucket_find(*bucket, ref, containing_match);
if (entry)
return entry;
/*
* Nothing found, go back a hash bucket
*/
put_hash_bucket(*bucket, *flags);
index.dev_addr -= (1 << HASH_FN_SHIFT);
*bucket = get_hash_bucket(&index, flags);
}
return NULL;
}
/*
* Add an entry to a hash bucket
*/
static void hash_bucket_add(struct hash_bucket *bucket,
struct dma_debug_entry *entry)
{
list_add_tail(&entry->list, &bucket->list);
}
/*
* Remove entry from a hash bucket list
*/
static void hash_bucket_del(struct dma_debug_entry *entry)
{
list_del(&entry->list);
}
static unsigned long long phys_addr(struct dma_debug_entry *entry)
{
if (entry->type == dma_debug_resource)
return __pfn_to_phys(entry->pfn) + entry->offset;
return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset;
}
/*
* For each mapping (initial cacheline in the case of
* dma_alloc_coherent/dma_map_page, initial cacheline in each page of a
* scatterlist, or the cacheline specified in dma_map_single) insert
* into this tree using the cacheline as the key. At
* dma_unmap_{single|sg|page} or dma_free_coherent delete the entry. If
* the entry already exists at insertion time add a tag as a reference
* count for the overlapping mappings. For now, the overlap tracking
* just ensures that 'unmaps' balance 'maps' before marking the
* cacheline idle, but we should also be flagging overlaps as an API
* violation.
*
* Memory usage is mostly constrained by the maximum number of available
* dma-debug entries in that we need a free dma_debug_entry before
* inserting into the tree. In the case of dma_map_page and
* dma_alloc_coherent there is only one dma_debug_entry and one
* dma_active_cacheline entry to track per event. dma_map_sg(), on the
* other hand, consumes a single dma_debug_entry, but inserts 'nents'
* entries into the tree.
*
* Use __GFP_NOWARN because the printk from an OOM, to netconsole, could end
* up right back in the DMA debugging code, leading to a deadlock.
*/
static RADIX_TREE(dma_active_cacheline, GFP_ATOMIC | __GFP_NOWARN);
static DEFINE_SPINLOCK(radix_lock);
#define ACTIVE_CACHELINE_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1)
#define CACHELINE_PER_PAGE_SHIFT (PAGE_SHIFT - L1_CACHE_SHIFT)
#define CACHELINES_PER_PAGE (1 << CACHELINE_PER_PAGE_SHIFT)
static phys_addr_t to_cacheline_number(struct dma_debug_entry *entry)
{
return (entry->pfn << CACHELINE_PER_PAGE_SHIFT) +
(entry->offset >> L1_CACHE_SHIFT);
}
static int active_cacheline_read_overlap(phys_addr_t cln)
{
int overlap = 0, i;
for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--)
if (radix_tree_tag_get(&dma_active_cacheline, cln, i))
overlap |= 1 << i;
return overlap;
}
static int active_cacheline_set_overlap(phys_addr_t cln, int overlap)
{
int i;
if (overlap > ACTIVE_CACHELINE_MAX_OVERLAP || overlap < 0)
return overlap;
for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--)
if (overlap & 1 << i)
radix_tree_tag_set(&dma_active_cacheline, cln, i);
else
radix_tree_tag_clear(&dma_active_cacheline, cln, i);
return overlap;
}
static void active_cacheline_inc_overlap(phys_addr_t cln)
{
int overlap = active_cacheline_read_overlap(cln);
overlap = active_cacheline_set_overlap(cln, ++overlap);
/* If we overflowed the overlap counter then we're potentially
* leaking dma-mappings.
*/
WARN_ONCE(overlap > ACTIVE_CACHELINE_MAX_OVERLAP,
pr_fmt("exceeded %d overlapping mappings of cacheline %pa\n"),
ACTIVE_CACHELINE_MAX_OVERLAP, &cln);
}
static int active_cacheline_dec_overlap(phys_addr_t cln)
{
int overlap = active_cacheline_read_overlap(cln);
return active_cacheline_set_overlap(cln, --overlap);
}
static int active_cacheline_insert(struct dma_debug_entry *entry)
{
phys_addr_t cln = to_cacheline_number(entry);
unsigned long flags;
int rc;
/* If the device is not writing memory then we don't have any
* concerns about the cpu consuming stale data. This mitigates
* legitimate usages of overlapping mappings.
*/
if (entry->direction == DMA_TO_DEVICE)
return 0;
spin_lock_irqsave(&radix_lock, flags);
rc = radix_tree_insert(&dma_active_cacheline, cln, entry);
if (rc == -EEXIST)
active_cacheline_inc_overlap(cln);
spin_unlock_irqrestore(&radix_lock, flags);
return rc;
}
static void active_cacheline_remove(struct dma_debug_entry *entry)
{
phys_addr_t cln = to_cacheline_number(entry);
unsigned long flags;
/* ...mirror the insert case */
if (entry->direction == DMA_TO_DEVICE)
return;
spin_lock_irqsave(&radix_lock, flags);
/* since we are counting overlaps the final put of the
* cacheline will occur when the overlap count is 0.
* active_cacheline_dec_overlap() returns -1 in that case
*/
if (active_cacheline_dec_overlap(cln) < 0)
radix_tree_delete(&dma_active_cacheline, cln);
spin_unlock_irqrestore(&radix_lock, flags);
}
/*
* Dump mappings entries on kernel space for debugging purposes
*/
void debug_dma_dump_mappings(struct device *dev)
{
int idx;
phys_addr_t cln;
for (idx = 0; idx < HASH_SIZE; idx++) {
struct hash_bucket *bucket = &dma_entry_hash[idx];
struct dma_debug_entry *entry;
unsigned long flags;
spin_lock_irqsave(&bucket->lock, flags);
list_for_each_entry(entry, &bucket->list, list) {
if (!dev || dev == entry->dev) {
cln = to_cacheline_number(entry);
dev_info(entry->dev,
"%s idx %d P=%llx N=%lx D=%llx L=%llx cln=%pa %s %s\n",
type2name[entry->type], idx,
phys_addr(entry), entry->pfn,
entry->dev_addr, entry->size,
&cln, dir2name[entry->direction],
maperr2str[entry->map_err_type]);
}
}
spin_unlock_irqrestore(&bucket->lock, flags);
cond_resched();
}
}
/*
* Dump mappings entries on user space via debugfs
*/
static int dump_show(struct seq_file *seq, void *v)
{
int idx;
phys_addr_t cln;
for (idx = 0; idx < HASH_SIZE; idx++) {
struct hash_bucket *bucket = &dma_entry_hash[idx];
struct dma_debug_entry *entry;
unsigned long flags;
spin_lock_irqsave(&bucket->lock, flags);
list_for_each_entry(entry, &bucket->list, list) {
cln = to_cacheline_number(entry);
seq_printf(seq,
"%s %s %s idx %d P=%llx N=%lx D=%llx L=%llx cln=%pa %s %s\n",
dev_driver_string(entry->dev),
dev_name(entry->dev),
type2name[entry->type], idx,
phys_addr(entry), entry->pfn,
entry->dev_addr, entry->size,
&cln, dir2name[entry->direction],
maperr2str[entry->map_err_type]);
}
spin_unlock_irqrestore(&bucket->lock, flags);
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(dump);
/*
* Wrapper function for adding an entry to the hash.
* This function takes care of locking itself.
*/
static void add_dma_entry(struct dma_debug_entry *entry, unsigned long attrs)
{
struct hash_bucket *bucket;
unsigned long flags;
int rc;
bucket = get_hash_bucket(entry, &flags);
hash_bucket_add(bucket, entry);
put_hash_bucket(bucket, flags);
rc = active_cacheline_insert(entry);
if (rc == -ENOMEM) {
pr_err_once("cacheline tracking ENOMEM, dma-debug disabled\n");
global_disable = true;
} else if (rc == -EEXIST && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
err_printk(entry->dev, entry,
"cacheline tracking EEXIST, overlapping mappings aren't supported\n");
}
}
static int dma_debug_create_entries(gfp_t gfp)
{
struct dma_debug_entry *entry;
int i;
entry = (void *)get_zeroed_page(gfp);
if (!entry)
return -ENOMEM;
for (i = 0; i < DMA_DEBUG_DYNAMIC_ENTRIES; i++)
list_add_tail(&entry[i].list, &free_entries);
num_free_entries += DMA_DEBUG_DYNAMIC_ENTRIES;
nr_total_entries += DMA_DEBUG_DYNAMIC_ENTRIES;
return 0;
}
static struct dma_debug_entry *__dma_entry_alloc(void)
{
struct dma_debug_entry *entry;
entry = list_entry(free_entries.next, struct dma_debug_entry, list);
list_del(&entry->list);
memset(entry, 0, sizeof(*entry));
num_free_entries -= 1;
if (num_free_entries < min_free_entries)
min_free_entries = num_free_entries;
return entry;
}
/*
* This should be called outside of free_entries_lock scope to avoid potential
* deadlocks with serial consoles that use DMA.
*/
static void __dma_entry_alloc_check_leak(u32 nr_entries)
{
u32 tmp = nr_entries % nr_prealloc_entries;
/* Shout each time we tick over some multiple of the initial pool */
if (tmp < DMA_DEBUG_DYNAMIC_ENTRIES) {
pr_info("dma_debug_entry pool grown to %u (%u00%%)\n",
nr_entries,
(nr_entries / nr_prealloc_entries));
}
}
/* struct dma_entry allocator
*
* The next two functions implement the allocator for
* struct dma_debug_entries.
*/
static struct dma_debug_entry *dma_entry_alloc(void)
{
bool alloc_check_leak = false;
struct dma_debug_entry *entry;
unsigned long flags;
u32 nr_entries;
spin_lock_irqsave(&free_entries_lock, flags);
if (num_free_entries == 0) {
if (dma_debug_create_entries(GFP_ATOMIC)) {
global_disable = true;
spin_unlock_irqrestore(&free_entries_lock, flags);
pr_err("debugging out of memory - disabling\n");
return NULL;
}
alloc_check_leak = true;
nr_entries = nr_total_entries;
}
entry = __dma_entry_alloc();
spin_unlock_irqrestore(&free_entries_lock, flags);
if (alloc_check_leak)
__dma_entry_alloc_check_leak(nr_entries);
#ifdef CONFIG_STACKTRACE
entry->stack_len = stack_trace_save(entry->stack_entries,
ARRAY_SIZE(entry->stack_entries),
1);
#endif
return entry;
}
static void dma_entry_free(struct dma_debug_entry *entry)
{
unsigned long flags;
active_cacheline_remove(entry);
/*
* add to beginning of the list - this way the entries are
* more likely cache hot when they are reallocated.
*/
spin_lock_irqsave(&free_entries_lock, flags);
list_add(&entry->list, &free_entries);
num_free_entries += 1;
spin_unlock_irqrestore(&free_entries_lock, flags);
}
/*
* DMA-API debugging init code
*
* The init code does two things:
* 1. Initialize core data structures
* 2. Preallocate a given number of dma_debug_entry structs
*/
static ssize_t filter_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[NAME_MAX_LEN + 1];
unsigned long flags;
int len;
if (!current_driver_name[0])
return 0;
/*
* We can't copy to userspace directly because current_driver_name can
* only be read under the driver_name_lock with irqs disabled. So
* create a temporary copy first.
*/
read_lock_irqsave(&driver_name_lock, flags);
len = scnprintf(buf, NAME_MAX_LEN + 1, "%s\n", current_driver_name);
read_unlock_irqrestore(&driver_name_lock, flags);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t filter_write(struct file *file, const char __user *userbuf,
size_t count, loff_t *ppos)
{
char buf[NAME_MAX_LEN];
unsigned long flags;
size_t len;
int i;
/*
* We can't copy from userspace directly. Access to
* current_driver_name is protected with a write_lock with irqs
* disabled. Since copy_from_user can fault and may sleep we
* need to copy to temporary buffer first
*/
len = min(count, (size_t)(NAME_MAX_LEN - 1));
if (copy_from_user(buf, userbuf, len))
return -EFAULT;
buf[len] = 0;
write_lock_irqsave(&driver_name_lock, flags);
/*
* Now handle the string we got from userspace very carefully.
* The rules are:
* - only use the first token we got
* - token delimiter is everything looking like a space
* character (' ', '\n', '\t' ...)
*
*/
if (!isalnum(buf[0])) {
/*
* If the first character userspace gave us is not
* alphanumerical then assume the filter should be
* switched off.
*/
if (current_driver_name[0])
pr_info("switching off dma-debug driver filter\n");
current_driver_name[0] = 0;
current_driver = NULL;
goto out_unlock;
}
/*
* Now parse out the first token and use it as the name for the
* driver to filter for.
*/
for (i = 0; i < NAME_MAX_LEN - 1; ++i) {
current_driver_name[i] = buf[i];
if (isspace(buf[i]) || buf[i] == ' ' || buf[i] == 0)
break;
}
current_driver_name[i] = 0;
current_driver = NULL;
pr_info("enable driver filter for driver [%s]\n",
current_driver_name);
out_unlock:
write_unlock_irqrestore(&driver_name_lock, flags);
return count;
}
static const struct file_operations filter_fops = {
.read = filter_read,
.write = filter_write,
.llseek = default_llseek,
};
static int __init dma_debug_fs_init(void)
{
struct dentry *dentry = debugfs_create_dir("dma-api", NULL);
debugfs_create_bool("disabled", 0444, dentry, &global_disable);
debugfs_create_u32("error_count", 0444, dentry, &error_count);
debugfs_create_u32("all_errors", 0644, dentry, &show_all_errors);
debugfs_create_u32("num_errors", 0644, dentry, &show_num_errors);
debugfs_create_u32("num_free_entries", 0444, dentry, &num_free_entries);
debugfs_create_u32("min_free_entries", 0444, dentry, &min_free_entries);
debugfs_create_u32("nr_total_entries", 0444, dentry, &nr_total_entries);
debugfs_create_file("driver_filter", 0644, dentry, NULL, &filter_fops);
debugfs_create_file("dump", 0444, dentry, NULL, &dump_fops);
return 0;
}
core_initcall_sync(dma_debug_fs_init);
static int device_dma_allocations(struct device *dev, struct dma_debug_entry **out_entry)
{
struct dma_debug_entry *entry;
unsigned long flags;
int count = 0, i;
for (i = 0; i < HASH_SIZE; ++i) {
spin_lock_irqsave(&dma_entry_hash[i].lock, flags);
list_for_each_entry(entry, &dma_entry_hash[i].list, list) {
if (entry->dev == dev) {
count += 1;
*out_entry = entry;
}
}
spin_unlock_irqrestore(&dma_entry_hash[i].lock, flags);
}
return count;
}
static int dma_debug_device_change(struct notifier_block *nb, unsigned long action, void *data)
{
struct device *dev = data;
struct dma_debug_entry *entry;
int count;
if (dma_debug_disabled())
return 0;
switch (action) {
case BUS_NOTIFY_UNBOUND_DRIVER:
count = device_dma_allocations(dev, &entry);
if (count == 0)
break;
err_printk(dev, entry, "device driver has pending "
"DMA allocations while released from device "
"[count=%d]\n"
"One of leaked entries details: "
"[device address=0x%016llx] [size=%llu bytes] "
"[mapped with %s] [mapped as %s]\n",
count, entry->dev_addr, entry->size,
dir2name[entry->direction], type2name[entry->type]);
break;
default:
break;
}
return 0;
}
void dma_debug_add_bus(const struct bus_type *bus)
{
struct notifier_block *nb;
if (dma_debug_disabled())
return;
nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
if (nb == NULL) {
pr_err("dma_debug_add_bus: out of memory\n");
return;
}
nb->notifier_call = dma_debug_device_change;
bus_register_notifier(bus, nb);
}
static int dma_debug_init(void)
{
int i, nr_pages;
/* Do not use dma_debug_initialized here, since we really want to be
* called to set dma_debug_initialized
*/
if (global_disable)
return 0;
for (i = 0; i < HASH_SIZE; ++i) {
INIT_LIST_HEAD(&dma_entry_hash[i].list);
spin_lock_init(&dma_entry_hash[i].lock);
}
nr_pages = DIV_ROUND_UP(nr_prealloc_entries, DMA_DEBUG_DYNAMIC_ENTRIES);
for (i = 0; i < nr_pages; ++i)
dma_debug_create_entries(GFP_KERNEL);
if (num_free_entries >= nr_prealloc_entries) {
pr_info("preallocated %d debug entries\n", nr_total_entries);
} else if (num_free_entries > 0) {
pr_warn("%d debug entries requested but only %d allocated\n",
nr_prealloc_entries, nr_total_entries);
} else {
pr_err("debugging out of memory error - disabled\n");
global_disable = true;
return 0;
}
min_free_entries = num_free_entries;
dma_debug_initialized = true;
pr_info("debugging enabled by kernel config\n");
return 0;
}
core_initcall(dma_debug_init);
static __init int dma_debug_cmdline(char *str)
{
if (!str)
return -EINVAL;
if (strncmp(str, "off", 3) == 0) {
pr_info("debugging disabled on kernel command line\n");
global_disable = true;
}
return 1;
}
static __init int dma_debug_entries_cmdline(char *str)
{
if (!str)
return -EINVAL;
if (!get_option(&str, &nr_prealloc_entries))
nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES;
return 1;
}
__setup("dma_debug=", dma_debug_cmdline);
__setup("dma_debug_entries=", dma_debug_entries_cmdline);
static void check_unmap(struct dma_debug_entry *ref)
{
struct dma_debug_entry *entry;
struct hash_bucket *bucket;
unsigned long flags;
bucket = get_hash_bucket(ref, &flags);
entry = bucket_find_exact(bucket, ref);
if (!entry) {
/* must drop lock before calling dma_mapping_error */
put_hash_bucket(bucket, flags);
if (dma_mapping_error(ref->dev, ref->dev_addr)) {
err_printk(ref->dev, NULL,
"device driver tries to free an "
"invalid DMA memory address\n");
} else {
err_printk(ref->dev, NULL,
"device driver tries to free DMA "
"memory it has not allocated [device "
"address=0x%016llx] [size=%llu bytes]\n",
ref->dev_addr, ref->size);
}
return;
}
if (ref->size != entry->size) {
err_printk(ref->dev, entry, "device driver frees "
"DMA memory with different size "
"[device address=0x%016llx] [map size=%llu bytes] "
"[unmap size=%llu bytes]\n",
ref->dev_addr, entry->size, ref->size);
}
if (ref->type != entry->type) {
err_printk(ref->dev, entry, "device driver frees "
"DMA memory with wrong function "
"[device address=0x%016llx] [size=%llu bytes] "
"[mapped as %s] [unmapped as %s]\n",
ref->dev_addr, ref->size,
type2name[entry->type], type2name[ref->type]);
} else if ((entry->type == dma_debug_coherent) &&
(phys_addr(ref) != phys_addr(entry))) {
err_printk(ref->dev, entry, "device driver frees "
"DMA memory with different CPU address "
"[device address=0x%016llx] [size=%llu bytes] "
"[cpu alloc address=0x%016llx] "
"[cpu free address=0x%016llx]",
ref->dev_addr, ref->size,
phys_addr(entry),
phys_addr(ref));
}
if (ref->sg_call_ents && ref->type == dma_debug_sg &&
ref->sg_call_ents != entry->sg_call_ents) {
err_printk(ref->dev, entry, "device driver frees "
"DMA sg list with different entry count "
"[map count=%d] [unmap count=%d]\n",
entry->sg_call_ents, ref->sg_call_ents);
}
/*
* This may be no bug in reality - but most implementations of the
* DMA API don't handle this properly, so check for it here
*/
if (ref->direction != entry->direction) {
err_printk(ref->dev, entry, "device driver frees "
"DMA memory with different direction "
"[device address=0x%016llx] [size=%llu bytes] "
"[mapped with %s] [unmapped with %s]\n",
ref->dev_addr, ref->size,
dir2name[entry->direction],
dir2name[ref->direction]);
}
/*
* Drivers should use dma_mapping_error() to check the returned
* addresses of dma_map_single() and dma_map_page().
* If not, print this warning message. See Documentation/core-api/dma-api.rst.
*/
if (entry->map_err_type == MAP_ERR_NOT_CHECKED) {
err_printk(ref->dev, entry,
"device driver failed to check map error"
"[device address=0x%016llx] [size=%llu bytes] "
"[mapped as %s]",
ref->dev_addr, ref->size,
type2name[entry->type]);
}
hash_bucket_del(entry);
put_hash_bucket(bucket, flags);
/*
* Free the entry outside of bucket_lock to avoid ABBA deadlocks
* between that and radix_lock.
*/
dma_entry_free(entry);
}
static void check_for_stack(struct device *dev,
struct page *page, size_t offset)
{
void *addr;
struct vm_struct *stack_vm_area = task_stack_vm_area(current);
if (!stack_vm_area) {
/* Stack is direct-mapped. */
if (PageHighMem(page))
return;
addr = page_address(page) + offset;
if (object_is_on_stack(addr))
err_printk(dev, NULL, "device driver maps memory from stack [addr=%p]\n", addr);
} else {
/* Stack is vmalloced. */
int i;
for (i = 0; i < stack_vm_area->nr_pages; i++) {
if (page != stack_vm_area->pages[i])
continue;
addr = (u8 *)current->stack + i * PAGE_SIZE + offset;
err_printk(dev, NULL, "device driver maps memory from stack [probable addr=%p]\n", addr);
break;
}
}
}
static void check_for_illegal_area(struct device *dev, void *addr, unsigned long len)
{
if (memory_intersects(_stext, _etext, addr, len) ||
memory_intersects(__start_rodata, __end_rodata, addr, len))
err_printk(dev, NULL, "device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n", addr, len);
}
static void check_sync(struct device *dev,
struct dma_debug_entry *ref,
bool to_cpu)
{
struct dma_debug_entry *entry;
struct hash_bucket *bucket;
unsigned long flags;
bucket = get_hash_bucket(ref, &flags);
entry = bucket_find_contain(&bucket, ref, &flags);
if (!entry) {
err_printk(dev, NULL, "device driver tries "
"to sync DMA memory it has not allocated "
"[device address=0x%016llx] [size=%llu bytes]\n",
(unsigned long long)ref->dev_addr, ref->size);
goto out;
}
if (ref->size > entry->size) {
err_printk(dev, entry, "device driver syncs"
" DMA memory outside allocated range "
"[device address=0x%016llx] "
"[allocation size=%llu bytes] "
"[sync offset+size=%llu]\n",
entry->dev_addr, entry->size,
ref->size);
}
if (entry->direction == DMA_BIDIRECTIONAL)
goto out;
if (ref->direction != entry->direction) {
err_printk(dev, entry, "device driver syncs "
"DMA memory with different direction "
"[device address=0x%016llx] [size=%llu bytes] "
"[mapped with %s] [synced with %s]\n",
(unsigned long long)ref->dev_addr, entry->size,
dir2name[entry->direction],
dir2name[ref->direction]);
}
if (to_cpu && !(entry->direction == DMA_FROM_DEVICE) &&
!(ref->direction == DMA_TO_DEVICE))
err_printk(dev, entry, "device driver syncs "
"device read-only DMA memory for cpu "
"[device address=0x%016llx] [size=%llu bytes] "
"[mapped with %s] [synced with %s]\n",
(unsigned long long)ref->dev_addr, entry->size,
dir2name[entry->direction],
dir2name[ref->direction]);
if (!to_cpu && !(entry->direction == DMA_TO_DEVICE) &&
!(ref->direction == DMA_FROM_DEVICE))
err_printk(dev, entry, "device driver syncs "
"device write-only DMA memory to device "
"[device address=0x%016llx] [size=%llu bytes] "
"[mapped with %s] [synced with %s]\n",
(unsigned long long)ref->dev_addr, entry->size,
dir2name[entry->direction],
dir2name[ref->direction]);
/* sg list count can be less than map count when partial cache sync */
if (ref->sg_call_ents && ref->type == dma_debug_sg &&
ref->sg_call_ents > entry->sg_call_ents) {
err_printk(ref->dev, entry, "device driver syncs "
"DMA sg list count larger than map count "
"[map count=%d] [sync count=%d]\n",
entry->sg_call_ents, ref->sg_call_ents);
}
out:
put_hash_bucket(bucket, flags);
}
static void check_sg_segment(struct device *dev, struct scatterlist *sg)
{
#ifdef CONFIG_DMA_API_DEBUG_SG
unsigned int max_seg = dma_get_max_seg_size(dev);
u64 start, end, boundary = dma_get_seg_boundary(dev);
/*
* Either the driver forgot to set dma_parms appropriately, or
* whoever generated the list forgot to check them.
*/
if (sg->length > max_seg)
err_printk(dev, NULL, "mapping sg segment longer than device claims to support [len=%u] [max=%u]\n",
sg->length, max_seg);
/*
* In some cases this could potentially be the DMA API
* implementation's fault, but it would usually imply that
* the scatterlist was built inappropriately to begin with.
*/
start = sg_dma_address(sg);
end = start + sg_dma_len(sg) - 1;
if ((start ^ end) & ~boundary)
err_printk(dev, NULL, "mapping sg segment across boundary [start=0x%016llx] [end=0x%016llx] [boundary=0x%016llx]\n",
start, end, boundary);
#endif
}
void debug_dma_map_single(struct device *dev, const void *addr,
unsigned long len)
{
if (unlikely(dma_debug_disabled()))
return;
if (!virt_addr_valid(addr))
err_printk(dev, NULL, "device driver maps memory from invalid area [addr=%p] [len=%lu]\n",
addr, len);
if (is_vmalloc_addr(addr))
err_printk(dev, NULL, "device driver maps memory from vmalloc area [addr=%p] [len=%lu]\n",
addr, len);
}
EXPORT_SYMBOL(debug_dma_map_single);
void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
size_t size, int direction, dma_addr_t dma_addr,
unsigned long attrs)
{
struct dma_debug_entry *entry;
if (unlikely(dma_debug_disabled()))
return;
if (dma_mapping_error(dev, dma_addr))
return;
entry = dma_entry_alloc();
if (!entry)
return;
entry->dev = dev;
entry->type = dma_debug_single;
entry->pfn = page_to_pfn(page);
entry->offset = offset;
entry->dev_addr = dma_addr;
entry->size = size;
entry->direction = direction;
entry->map_err_type = MAP_ERR_NOT_CHECKED;
check_for_stack(dev, page, offset);
if (!PageHighMem(page)) {
void *addr = page_address(page) + offset;
check_for_illegal_area(dev, addr, size);
}
add_dma_entry(entry, attrs);
}
void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
struct dma_debug_entry ref;
struct dma_debug_entry *entry;
struct hash_bucket *bucket;
unsigned long flags;
if (unlikely(dma_debug_disabled()))
return;
ref.dev = dev;
ref.dev_addr = dma_addr;
bucket = get_hash_bucket(&ref, &flags);
list_for_each_entry(entry, &bucket->list, list) {
if (!exact_match(&ref, entry))
continue;
/*
* The same physical address can be mapped multiple
* times. Without a hardware IOMMU this results in the
* same device addresses being put into the dma-debug
* hash multiple times too. This can result in false
* positives being reported. Therefore we implement a
* best-fit algorithm here which updates the first entry
* from the hash which fits the reference value and is
* not currently listed as being checked.
*/
if (entry->map_err_type == MAP_ERR_NOT_CHECKED) {
entry->map_err_type = MAP_ERR_CHECKED;
break;
}
}
put_hash_bucket(bucket, flags);
}
EXPORT_SYMBOL(debug_dma_mapping_error);
void debug_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
size_t size, int direction)
{
struct dma_debug_entry ref = {
.type = dma_debug_single,
.dev = dev,
.dev_addr = dma_addr,
.size = size,
.direction = direction,
};
if (unlikely(dma_debug_disabled()))
return;
check_unmap(&ref);
}
void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
int nents, int mapped_ents, int direction,
unsigned long attrs)
{
struct dma_debug_entry *entry;
struct scatterlist *s;
int i;
if (unlikely(dma_debug_disabled()))
return;
for_each_sg(sg, s, nents, i) {
check_for_stack(dev, sg_page(s), s->offset);
if (!PageHighMem(sg_page(s)))
check_for_illegal_area(dev, sg_virt(s), s->length);
}
for_each_sg(sg, s, mapped_ents, i) {
entry = dma_entry_alloc();
if (!entry)
return;
entry->type = dma_debug_sg;
entry->dev = dev;
entry->pfn = page_to_pfn(sg_page(s));
entry->offset = s->offset;
entry->size = sg_dma_len(s);
entry->dev_addr = sg_dma_address(s);
entry->direction = direction;
entry->sg_call_ents = nents;
entry->sg_mapped_ents = mapped_ents;
check_sg_segment(dev, s);
add_dma_entry(entry, attrs);
}
}
static int get_nr_mapped_entries(struct device *dev,
struct dma_debug_entry *ref)
{
struct dma_debug_entry *entry;
struct hash_bucket *bucket;
unsigned long flags;
int mapped_ents;
bucket = get_hash_bucket(ref, &flags);
entry = bucket_find_exact(bucket, ref);
mapped_ents = 0;
if (entry)
mapped_ents = entry->sg_mapped_ents;
put_hash_bucket(bucket, flags);
return mapped_ents;
}
void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
int nelems, int dir)
{
struct scatterlist *s;
int mapped_ents = 0, i;
if (unlikely(dma_debug_disabled()))
return;
for_each_sg(sglist, s, nelems, i) {
struct dma_debug_entry ref = {
.type = dma_debug_sg,
.dev = dev,
.pfn = page_to_pfn(sg_page(s)),
.offset = s->offset,
.dev_addr = sg_dma_address(s),
.size = sg_dma_len(s),
.direction = dir,
.sg_call_ents = nelems,
};
if (mapped_ents && i >= mapped_ents)
break;
if (!i)
mapped_ents = get_nr_mapped_entries(dev, &ref);
check_unmap(&ref);
}
}
void debug_dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t dma_addr, void *virt,
unsigned long attrs)
{
struct dma_debug_entry *entry;
if (unlikely(dma_debug_disabled()))
return;
if (unlikely(virt == NULL))
return;
/* handle vmalloc and linear addresses */
if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt))
return;
entry = dma_entry_alloc();
if (!entry)
return;
entry->type = dma_debug_coherent;
entry->dev = dev;
entry->offset = offset_in_page(virt);
entry->size = size;
entry->dev_addr = dma_addr;
entry->direction = DMA_BIDIRECTIONAL;
if (is_vmalloc_addr(virt))
entry->pfn = vmalloc_to_pfn(virt);
else
entry->pfn = page_to_pfn(virt_to_page(virt));
add_dma_entry(entry, attrs);
}
void debug_dma_free_coherent(struct device *dev, size_t size,
void *virt, dma_addr_t dma_addr)
{
struct dma_debug_entry ref = {
.type = dma_debug_coherent,
.dev = dev,
.offset = offset_in_page(virt),
.dev_addr = dma_addr,
.size = size,
.direction = DMA_BIDIRECTIONAL,
};
/* handle vmalloc and linear addresses */
if (!is_vmalloc_addr(virt) && !virt_addr_valid(virt))
return;
if (is_vmalloc_addr(virt))
ref.pfn = vmalloc_to_pfn(virt);
else
ref.pfn = page_to_pfn(virt_to_page(virt));
if (unlikely(dma_debug_disabled()))
return;
check_unmap(&ref);
}
void debug_dma_map_resource(struct device *dev, phys_addr_t addr, size_t size,
int direction, dma_addr_t dma_addr,
unsigned long attrs)
{
struct dma_debug_entry *entry;
if (unlikely(dma_debug_disabled()))
return;
entry = dma_entry_alloc();
if (!entry)
return;
entry->type = dma_debug_resource;
entry->dev = dev;
entry->pfn = PHYS_PFN(addr);
entry->offset = offset_in_page(addr);
entry->size = size;
entry->dev_addr = dma_addr;
entry->direction = direction;
entry->map_err_type = MAP_ERR_NOT_CHECKED;
add_dma_entry(entry, attrs);
}
void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr,
size_t size, int direction)
{
struct dma_debug_entry ref = {
.type = dma_debug_resource,
.dev = dev,
.dev_addr = dma_addr,
.size = size,
.direction = direction,
};
if (unlikely(dma_debug_disabled()))
return;
check_unmap(&ref);
}
void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
size_t size, int direction)
{
struct dma_debug_entry ref;
if (unlikely(dma_debug_disabled()))
return;
ref.type = dma_debug_single;
ref.dev = dev;
ref.dev_addr = dma_handle;
ref.size = size;
ref.direction = direction;
ref.sg_call_ents = 0;
check_sync(dev, &ref, true);
}
void debug_dma_sync_single_for_device(struct device *dev,
dma_addr_t dma_handle, size_t size,
int direction)
{
struct dma_debug_entry ref;
if (unlikely(dma_debug_disabled()))
return;
ref.type = dma_debug_single;
ref.dev = dev;
ref.dev_addr = dma_handle;
ref.size = size;
ref.direction = direction;
ref.sg_call_ents = 0;
check_sync(dev, &ref, false);
}
void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
int nelems, int direction)
{
struct scatterlist *s;
int mapped_ents = 0, i;
if (unlikely(dma_debug_disabled()))
return;
for_each_sg(sg, s, nelems, i) {
struct dma_debug_entry ref = {
.type = dma_debug_sg,
.dev = dev,
.pfn = page_to_pfn(sg_page(s)),
.offset = s->offset,
.dev_addr = sg_dma_address(s),
.size = sg_dma_len(s),
.direction = direction,
.sg_call_ents = nelems,
};
if (!i)
mapped_ents = get_nr_mapped_entries(dev, &ref);
if (i >= mapped_ents)
break;
check_sync(dev, &ref, true);
}
}
void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
int nelems, int direction)
{
struct scatterlist *s;
int mapped_ents = 0, i;
if (unlikely(dma_debug_disabled()))
return;
for_each_sg(sg, s, nelems, i) {
struct dma_debug_entry ref = {
.type = dma_debug_sg,
.dev = dev,
.pfn = page_to_pfn(sg_page(s)),
.offset = s->offset,
.dev_addr = sg_dma_address(s),
.size = sg_dma_len(s),
.direction = direction,
.sg_call_ents = nelems,
};
if (!i)
mapped_ents = get_nr_mapped_entries(dev, &ref);
if (i >= mapped_ents)
break;
check_sync(dev, &ref, false);
}
}
static int __init dma_debug_driver_setup(char *str)
{
int i;
for (i = 0; i < NAME_MAX_LEN - 1; ++i, ++str) {
current_driver_name[i] = *str;
if (*str == 0)
break;
}
if (current_driver_name[0])
pr_info("enable driver filter for driver [%s]\n",
current_driver_name);
return 1;
}
__setup("dma_debug_driver=", dma_debug_driver_setup);