GKI (arm64) relevant 129 out of 468 changes, affecting 180 files +1843/-980e0964a5778ptp: Add error handling for adjfine callback in ptp_clock_adjtime [1 file, +2/-1]a007f8895fnet/sched: tbf: correct backlog statistic for GSO packets [1 file, +12/-6]9545011e7bnet: Fix icmp host relookup triggering ip_rt_bug [1 file, +3/-0]01f95357e4ipv6: avoid possible NULL deref in modify_prefix_route() [1 file, +7/-6]8b591bd522net/ipv6: release expired exception dst cached in socket [1 file, +3/-3]e48b211c4ctipc: Fix use-after-free of kernel socket in cleanup_bearer(). [1 file, +1/-1]da5cc778e7netfilter: nft_inner: incorrect percpu area handling under softirq [2 files, +46/-12]a36a6d7037Revert "udp: avoid calling sock_def_readable() if possible" [1 file, +3/-11]22074dc1d4ethtool: Fix wrong mod state in case of verbose and no_mask bitset [1 file, +44/-4]316183d583net: avoid potential UAF in default_operstate() [1 file, +6/-1]c00372e41bmmc: sd: SDUC Support Recognition [7 files, +27/-15]19e22f1e68mmc: core: Adjust ACMD22 to SDUC [1 file, +18/-6]42311846d3mmc: core: Use GFP_NOIO in ACMD22 [1 file, +4/-0]4e51552bc5zram: do not mark idle slots that cannot be idle [1 file, +18/-7]0ab037634bzram: clear IDLE flag in mark_idle() [1 file, +2/-0]405b6d5f90ntp: Remove invalid cast in time offset math [1 file, +1/-1]6358df316df2fs: clean up w/ F2FS_{BLK_TO_BYTES,BTYES_TO_BLK} [1 file, +29/-39]e6a91ed4b9f2fs: fix to adjust appropriate length for fiemap [2 files, +4/-3]8e9fec7f79f2fs: fix to requery extent which cross boundary of inquiry [1 file, +15/-5]815d8f0e52i3c: master: Replace hard code 2 with macro I3C_ADDR_SLOT_STATUS_BITS [2 files, +5/-3]c3806cf647i3c: master: Extend address status bit to 4 and add I3C_ADDR_SLOT_EXT_DESIRED [2 files, +59/-13]1117462773i3c: master: Fix dynamic address leak when 'assigned-address' is present [1 file, +5/-10]7d4e5e33eascsi: ufs: core: Always initialize the UIC done completion [1 file, +4/-7]3ad69f2f08scsi: ufs: core: Add ufshcd_send_bsg_uic_cmd() for UFS BSG [3 files, +38/-1]47f4ad956bbpf, vsock: Fix poll() missing a queue [1 file, +3/-0]a222e48feabpf, vsock: Invoke proto::close on close() [1 file, +40/-27]dabaf26846xsk: always clear DMA mapping information when unmapping the pool [1 file, +2/-3]5c9e3bb43atcp_bpf: Fix the sk_mem_uncharge logic in tcp_bpf_sendmsg [1 file, +4/-7]7bc37dd9eaALSA: usb-audio: Notify xrun for low-latency mode [1 file, +11/-3]a78af11806pmdomain: core: Add missing put_device() [1 file, +1/-0]913a3f1c06pmdomain: core: Fix error path in pm_genpd_init() when ida alloc fails [1 file, +19/-17]5548887987nvme: don't apply NVME_QUIRK_DEALLOCATE_ZEROES when DSM is not supported [1 file, +2/-1]bdbf87486dbpf: Ensure reg is PTR_TO_STACK in process_iter_arg [2 files, +7/-2]2459a0b149drivers/virt: pkvm: Don't fail ioremap() call if MMIO_GUARD fails [1 file, +1/-5]0da7d4b7cabpf: Don't mark STACK_INVALID as STACK_MISC in mark_stack_slot_misc [1 file, +6/-3]f9f2a2739ebpf: Fix narrow scalar spill onto 64-bit spilled scalar slots [1 file, +1/-0]845cc4ee8envme-fabrics: handle zero MAXCMD without closing the connection [1 file, +3/-2]c2277e2859scatterlist: fix incorrect func name in kernel-doc [1 file, +1/-1]81ec3c6cebbpf: Handle BPF_EXIST and BPF_NOEXIST for LPM trie [1 file, +20/-3]6dc076a257bpf: Remove unnecessary kfree(im_node) in lpm_trie_update_elem [1 file, +1/-3]7218e441adbpf: Handle in-place update for full LPM trie correctly [1 file, +21/-23]412bf01fd5bpf: Fix exact match conditions in trie_get_next_key() [1 file, +2/-2]e689bc6697HID: wacom: fix when get product name maybe null pointer [1 file, +2/-1]3b0c5bb437can: dev: can_set_termination(): allow sleeping GPIOs [1 file, +1/-1]ba0ee489cdtracing: Fix cmp_entries_dup() to respect sort() comparison rules [1 file, +1/-5]ec643064abarm64: mm: Fix zone_dma_limit calculation [1 file, +8/-9]34b6197867arm64: Ensure bits ASID[15:8] are masked out when the kernel uses 8-bit ASIDs [1 file, +2/-2]abd614bbfcarm64: ptrace: fix partial SETREGSET for NT_ARM_TAGGED_ADDR_CTRL [1 file, +5/-1]8ab73c34e3arm64: ptrace: fix partial SETREGSET for NT_ARM_FPMR [1 file, +2/-0]4105dd76bcarm64: ptrace: fix partial SETREGSET for NT_ARM_POE [1 file, +2/-0]7f1292f8d4ALSA: usb-audio: Fix a DMA to stack memory bug [1 file, +27/-15]39c5d89b56ALSA: usb-audio: Add extra PID for RME Digiface USB [3 files, +176/-168]9c191055c7scsi: ufs: core: sysfs: Prevent div by zero [1 file, +6/-0]2e7a3bb033scsi: ufs: core: Cancel RTC work during ufshcd_remove() [1 file, +1/-0]5a717f43c2scsi: ufs: core: Add missing post notify for power mode change [2 files, +10/-7]793e560a6bio_uring: Change res2 parameter type in io_uring_cmd_done [2 files, +3/-3]85351e4941Revert "readahead: properly shorten readahead when falling back to do_page_cache_ra()" [1 file, +2/-3]95e197354ecacheinfo: Allocate memory during CPU hotplug if not done from the primary CPU [1 file, +8/-6]bc031095d1modpost: Add .irqentry.text to OTHER_SECTIONS [1 file, +1/-1]178e31df1fbpf: fix OOB devmap writes when deleting elements [1 file, +3/-3]3dcc20418edma-buf: fix dma_fence_array_signaled v4 [1 file, +27/-1]f3dbb097d6dma-fence: Fix reference leak on fence merge failure path [1 file, +2/-0]4715555964dma-fence: Use kernel's sort for merging fences [1 file, +61/-67]d486b5741dxsk: fix OOB map writes when deleting elements [1 file, +1/-1]14258211d6regmap: detach regmap from dev on regmap_exit [1 file, +12/-0]d562b457e1mmc: core: Further prevent card detect during shutdown [2 files, +5/-0]9bfeeeff2cstackdepot: fix stack_depot_save_flags() in NMI context [2 files, +12/-4]a71ddd5b87sched/numa: fix memory leak due to the overwritten vma->numab_state [1 file, +9/-3]835ca042dfkasan: make report_lock a raw spinlock [1 file, +3/-3]69d319450dmm/gup: handle NULL pages in unpin_user_pages() [1 file, +10/-1]1dde3fde62mm: open-code PageTail in folio_flags() and const_folio_flags() [1 file, +2/-2]bd4d2333a3mm: open-code page_folio() in dump_page() [1 file, +5/-2]536ffb4014mm: fix vrealloc()'s KASAN poisoning logic [1 file, +2/-1]fe1a34e92amm: respect mmap hint address when aligning for THP [1 file, +1/-0]5c63e24b1bscsi: ufs: pltfrm: Drop PM runtime reference count after ufshcd_remove() [6 files, +2/-5]2cec2d916amemblock: allow zero threshold in validate_numa_converage() [1 file, +2/-2]d222934627epoll: annotate racy check [2 files, +5/-3]493326c4f1block: RCU protect disk->conv_zones_bitmap [2 files, +32/-13]b6ce2dbe98ext4: partial zero eof block on unaligned inode size extension [2 files, +42/-16]ff599ad2d2cleanup: Adjust scoped_guard() macros to avoid potential warning [1 file, +42/-10]3946e07552gpio: free irqs that are still requested when the chip is being removed [1 file, +41/-0]ea74e9675bHID: add per device quirk to force bind to hid-generic [3 files, +8/-2]17db6ed5a3media: uvcvideo: RealSense D421 Depth module metadata [1 file, +9/-0]0c20fadfd0media: uvcvideo: Add a quirk for the Kaiweets KTI-W02 infrared camera [1 file, +11/-0]3cc5228d5bmedia: uvcvideo: Force UVC version to 1.0a for 0408:4033 [1 file, +11/-0]4150f22342drm: panel-orientation-quirks: Add quirk for AYA NEO 2 model [1 file, +6/-0]5d7f35ed5fdrm: panel-orientation-quirks: Add quirk for AYA NEO Founder edition [1 file, +6/-0]187d5ff497drm: panel-orientation-quirks: Add quirk for AYA NEO GEEK [1 file, +6/-0]fd09880b16af_packet: avoid erroring out after sock_init_data() in packet_create() [1 file, +6/-6]61686abc2fBluetooth: L2CAP: do not leave dangling sk pointer on error in l2cap_sock_create() [1 file, +1/-0]32df687e12Bluetooth: RFCOMM: avoid leaving dangling sk pointer in rfcomm_sock_alloc() [1 file, +5/-5]8df832e6b9net: af_can: do not leave a dangling sk pointer in can_create() [1 file, +1/-0]03caa9bfb9net: ieee802154: do not leave a dangling sk pointer in ieee802154_create() [1 file, +7/-5]691d6d816fnet: inet: do not leave a dangling sk pointer in inet_create() [1 file, +10/-12]f44fceb71dnet: inet6: do not leave a dangling sk pointer in inet6_create() [1 file, +10/-12]987aa730babpf: Prevent tailcall infinite loop caused by freplace [5 files, +81/-17]051f49d517net/tcp: Add missing lockdep annotations for TCP-AO hlist traversals [4 files, +29/-23]920159e1bfALSA: usb-audio: Make mic volume workarounds globally applicable [3 files, +45/-48]a50b4aa300bpf: Call free_htab_elem() after htab_unlock_bucket() [1 file, +39/-17]da561d5fb6Bluetooth: hci_conn: Reduce hci_conn_drop() calls in two functions [1 file, +3/-10]c55a4c5a04Bluetooth: hci_conn: Use disable_delayed_work_sync [1 file, +3/-3]93a6160dc1Bluetooth: hci_core: Fix not checking skb length on hci_acldata_packet [1 file, +9/-4]b04b4fb91dBluetooth: Add new quirks for ATS2851 [2 files, +20/-4]359fc41e3cBluetooth: Support new quirks for ATS2851 [2 files, +15/-1]166cf43070net/neighbor: clear error in case strict check is not set [1 file, +1/-0]f63a1caae9tracing/ftrace: disable preemption in syscall probe [2 files, +44/-4]d1133dd57etracing: Use atomic64_inc_return() in trace_clock_counter() [1 file, +1/-1]09c083fbearing-buffer: Limit time with disabled interrupts in rb_check_pages() [1 file, +72/-26]c11e2ec9a7pinmux: Use sequential access to access desc->pinmux data [3 files, +100/-77]b865d4e569scsi: ufs: core: Make DMA mask configuration more flexible [3 files, +13/-9]2fcb921c27bpf: put bpf_link's program when link is safe to be deallocated [1 file, +17/-5]bb4a6236a4leds: class: Protect brightness_show() with led_cdev->led_access mutex [2 files, +12/-4]7214d3a64etracing: Fix function name for trampoline [3 files, +36/-8]9e28513fd2f2fs: fix f2fs_bug_on when uninstalling filesystem call f2fs_evict_inode. [1 file, +3/-1]b51aa6a07ePCI: qcom: Add support for IPQ9574 [1 file, +1/-0]617bd1e6c3PCI: Add ACS quirk for Wangxun FF5xxx NICs [1 file, +9/-6]1f51ae217di3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock [1 file, +2/-1]6d41a2d5c1f2fs: print message if fscorrupted was found in f2fs_new_node_page() [1 file, +6/-1]924f7dd1e8f2fs: fix to shrink read extent node in batches [1 file, +41/-28]1648c7000fserial: 8250_dw: Add Sophgo SG2044 quirk [1 file, +3/-2]950210c9c7Revert "nvme: make keep-alive synchronous operation" [1 file, +10/-7]d5b2ddf1f9io_uring/tctx: work around xa_store() allocation error issue [1 file, +12/-1]cd188519d2scsi: ufs: pltfrm: Dellocate HBA during ufshcd_pltfrm_remove() [1 file, +1/-0]a39ad4f507sched/core: Remove the unnecessary need_resched() check in nohz_csd_func() [1 file, +1/-1]f9e144a544sched/fair: Check idle_cpu() before need_resched() to detect ilb CPU turning busy [1 file, +1/-1]b4ec68868csched/core: Prevent wakeup of ksoftirqd during idle load balance [1 file, +1/-1]364dc8070btracing/eprobe: Fix to release eprobe when failed to add dyn_event [1 file, +5/-0]1a678f6829clocksource: Make negative motion detection more robust [4 files, +20/-7]6aeef0214dsoftirq: 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>
1618 lines
42 KiB
C
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);
|