Pull drm updates from Dave Airlie:
 "Highlights:

   - dmemcg eviction support is good for low VRAM things like Steam
     Machine

   - AMD adds gfx6-8 modifier support for older GPUs that enables a
     bunch of wayland stuff

   - i915/xe has some new hw support but also a lot of display
     refactoring

  Everything:

  perf:
   - export perf_allow_ APIs for xe

  udmabuf:
   - remove default size limit of 64MB

  rust:
   - i/o rework (signed tag from driver-core tree)
   - add registration guard and registration data
   - fix unbounded lifetimes in ioctl handler args
   - fix a drm_dev_register race
   - gem_shmem: add DmaResvGuard helper
   - gpuvm: require send/sync for driver data
       - implement send/sync for GpuVaAlloc and GpuVmBo
       - add SmContext lifetime
   - rename dma_handle to dma_address
   - change pci_sriov_get_totalvfs return to unsigned int

  core:
   - create drm_of_get_panel_orientation
   - send per-connector hotplug events
   - add thunderbolt UBHR tunneling support

  connector:
   - add color format property

  dmem:
   - introduce a peak file
   - accept one region per limit
   - add dmemcg support for eviction

  gpusvm:
   - reorg code to give drivers more flexibility

  atomic:
   - add create_state callback and helper
   - add documentation on atomic commit lifetime

  buddy:
   - add per-order free
   - add used block scoreboard
   - fix UAF
   - test buffer clearance on resume
   - add phys_addr->block helper

  gem:
   - drop DRIVER_GEM_GPUVA flag

  ttm:
   - be more aggressive allocating below protection limit

  sched:
   - add test suite for concurrent job submissions

  hdmi:
   - hook the color format property in helpers

  mipi-dsi:
   - add MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT

  bridge:
   - add atomic create callbacks
   - drop atomic reset
   - display-connector: don't autoenable HPD IRQ
   - trigger initial HPD for DP
   - ti-sn65dsi83: remove NO_HFP and NO_HBP mode flags
   - analogix_dp: switch to DP link training helpers

  dp:
   - add support for DSC max delta BPP

  edid:
   - parse panel type from DisplayID 2.x Display Parameters

  sysfb:
   - improve panel, stride, framebuffer size validation

  panel:
   - implement ref counting for struct drm_panel
   - himax-hx83121a: add backlight regulator support
   - novatek-nt36672a: Inline panel init sequences
   - visionox-vtdr6130: enable DSC
   - novatek-nt37801: Use mipi_dsi_*_multi() functions
   - samsung-s6d16d0: Fix prepare error handling
   - support Novatek NT36536 plus DT bindings
   - sofef00: fix backlight updates
   - osd101t2587: use mipi_dsi_*_multi interface
   - panel-edp: adjust timing for AUO displays
   - panel-lvds: support Opto Logic SCX1001511GGC49
   - panel-simple: support Kyocera tcg070wvlq
   - panel-edp: quirks
       - AUO B116XAT04.3, CMN N116BCP-EA2, CSW MNB601LS1-8
       - BOE NV116WH2-M30, BOE NT116WHM-N21, BOE NV116FH1-M31
       - BOE NV116FH1-M30, NV140FHM-N5B, TM156VDXP25
       - BOE NE160QDM-NY1, MB116AS01
   - new:
       - Samsung ATNA40HQ08-0, Anbernic TD4310
       - Chipone ICNA35XX, Ilitek ILI9488
       - Ilitek ILI7807S, Renesas R63419
       - MNE001BS6-2, MNF601BS4-1, Sharp LQ120P1JX51

  virtio:
   - add support for save/restore virtio_gpu_objects
   - abort vq wait on device removal

  amdgpu:
   - add color format DRM property
   - initial compute pipe reset support
   - add GFX 6-8 modifier support
   - initial DCN 6.0.0 support
   - dmemcg eviction support
   - improved boundary checking for bios parsing
   - RAS updates and rework
   - VCN secure submission fixes
   - 8K panel fix
   - Display KUNIT tests
   - parse panel type from DisplayID
   - Align IP discovery to pci device lifetime
   - SOC15 register macro cleanups
   - UVD memory placement fixes
   - GFX9 mode2 reset fixes
   - drop unnecessary BUG/BUG_ON
   - GFX8 soft reset rework
   - enable soft reset on GFX8
   - PSP/SMU 15.0.9 update
   - VI ASPM fix
   - userq fixes
   - amdgpu_vm_get_task_info_pasid lifetime fix
   - DC CACP support
   - change system_unbound_wq with system_dfl_wq
   - Loosen VFCT bios parsing to deal with pci=realloc
   - SI/SMU7 AC/DC switch fix
   - VM fence handling fix
   - GEM close optimisation
   - Apple Studio Display fixes
   - DC FRL fixes

  amdkfd:
   - initial compute pipe reset support
   - allow applications to opt out of sigbus on fatal errors
   - improve CRIU boundary checks
   - MQD handling rework
   - move TBA/TMA from system to device memory
   - avoid topology-lock in kfd_mmap
   - SVM eviction fixes

  radeon:
   - fix unset CONFIG_ACPI build

  i915:
   - Novalake (NVL display version 35) timing generator enabling
   - NVL DC3CO enabling
   - enable UBHR link rates on thunderbolt tunnels
   - Reduce Xe3+ PM demand peak bandwidth
   - enable pipe DMC error interrupts for display 30+
   - add kunit tests for DP link config selection
   - refactor and document DP link recovery
   - i915/xe driver display probe/remove/suspend/resume/shutdown cleanup
     and unification
   - i915/xe display runtime PM unified
   - Break i915 and xe panic dependency on struct intel_framebuffer
   - Streamline Pre/Post-CSC LUT loops
   - drop TGL DC3DO support
   - CDCLK santization
   - fix HDMI scrambling enable
   - fix phys bo pread/pwrite with offset
   - add missing nospec on parallel submit slot
   - fix some NULL derefs

  xe:
   - drop force_execlist module param
   - gate observation streams with perf_allow_cpu
   - skip FORCE_WC and vm_bound check for external dma-bufs
   - dmemcg eviction support
   - remove unused NVL-S GuC
   - TLB invalidation improvements
   - NVL-S updated PCI-IDs and w/a
   - madvise: optimise invalidation path
   - fix infinite gt-reset loop in timeout recovery
   - update TTM device benefical_order
   - wait on external BO kernel fences in exec ioctl
   - add/use more KLV helpers
   - sriov: disable display in admin only PF mode
   - add RAS GPU health indicator
   - optimise TTM populate for DONTNEED BO
   - drop force_probe for NVL-s
   - add debugfs for pcode info

  amdxdna:
   - disable device buffer export

  nova:
   - build nova-core/nova-drm from drivers/gpu
   - export nova-core rust symbols (workaround)
   - GSP boot process consolidation
   - Boot GSP with vGPU enabled
   - TLV firmware image format support
   - Hopper/Blackwell fixes and cleanups
   - I/O projection adoption

  tyr:
   - firmware loading and MCU boot
   - add generic slot manager + MMU
   - GPU VM support ARM64 LPAE page tables
   - add kernel buffer object for internal allocations
   - add parser for Mali CSF
   - add MCU booting

  nouveau:
   - race fixes
   - check instmem iomapping at first use
   - add dmemcg support
   - expose NVDEC channels
   - add scanline position/head state support for GSP

  qxl:
   - convert simple encoder to regular

  ethosu:
   - add perf counter support

  etnaviv:
   - force flush on power register ops

  msm:
   - support DSC configuration with slice_per_pkt > 1

  mxsfb:
   - fix disable sequence

  panthor:
   - support sparse mappings

  rockchip:
   - switch away from simple helpers
   - support YUV background color
   - fix layer config timeout
   - add edp support for rk3576
   - add batch command submission function

  rocket:
   - error handling and NULL ptr deref fixes

  sun4i:
   - switch away from simple helpers

  imagination:
   - mark BXM-4-64 MC1 as support

  host1x:
   - support tegra264

  tegra:
   - add DSI for tegra 20/30

  v3d:
   - reduce PM runtime autosuspend delay
   - scheduler fixes and refactoring
   - deprecate v3d 3.3 and 4.1
   - validate CPU job query boundaries

  hibmc:
   - improve plane format handling
   - switch to gem shmem

  mediatek:
   - cec: correct compat for mt7623-8167?

  exynos:
   - remove simple dependency
   - add error handling to encoder paths
   - take i2c adapter module reference"

* tag 'drm-next-2026-08-20' of https://gitlab.freedesktop.org/drm/kernel: (2074 commits)
  drm/xe/mcr: Take vcs1/vecs1 into account for first media slice
  drm/xe: Fix a bug in pc_adjust_freq_bounds()
  drm/xe: Fix xe_device_probe() failure
  drm/xe/drm_ras: Move has_drm_ras check to drm_ras layer
  drm/xe/ras: Fix boot-time ras error processing
  drm/amd/display: make DC_RUN_WITH_PREEMPTION_ENABLED misuse a build error
  drm/amd/pm: silence uninitialized variable warnings
  drm/amdgpu: skip BOs being torn down during GTT recovery
  drm/amdgpu: Reject UVD message with invalid number of h265 refs
  drm/amdgpu: keep PRT mappings off the vm_bo state lists
  drm/amdgpu: fix nbif 6.3.1 l1 low power not functional
  drm/amd/display: fix BT.2020 YCbCr output CSC matrices for DCE
  drm/amd/display: fix BT.2020 YCbCr limited output CSC matrix
  drm/amdgpu: Implement insert_end for VCE 3
  drm/amdgpu: Fix UVD min buffer sizes
  drm/amdgpu: Fix UVD decode image min size calculation
  drm/amdgpu: Fix UVD dpb min size calculation for H264
  drm/amdgpu: Reject UVD message with dimensions above 4096
  drm/amdgpu: check ASPM on the dGPU host link
  drm/radeon: fix autosuspend cleanup during teardown
  ...
This commit is contained in:
Linus Torvalds
2026-08-21 08:41:00 -07:00
1576 changed files with 229739 additions and 39844 deletions
+1 -1
View File
@@ -167,7 +167,7 @@ ForEachMacros:
- 'drm_connector_for_each_possible_encoder'
- 'drm_exec_for_each_locked_object'
- 'drm_exec_for_each_locked_object_reverse'
- 'drm_for_each_bridge_in_chain_scoped'
- 'drm_for_each_bridge_in_chain'
- 'drm_for_each_connector_iter'
- 'drm_for_each_crtc'
- 'drm_for_each_crtc_reverse'
+3 -1
View File
@@ -579,6 +579,7 @@ Martyna Szapar-Mudlaw <martyna.szapar-mudlaw@linux.intel.com> <martyna.szapar-mu
Mathieu Othacehe <othacehe@gnu.org> <m.othacehe@gmail.com>
Mat Martineau <martineau@kernel.org> <mathew.j.martineau@linux.intel.com>
Mat Martineau <martineau@kernel.org> <mathewm@codeaurora.org>
Matt Coster <opensource@mtcoster.net> <matt.coster@imgtec.com>
Matthew Wilcox <willy@infradead.org> <matthew.r.wilcox@intel.com>
Matthew Wilcox <willy@infradead.org> <matthew@wil.cx>
Matthew Wilcox <willy@infradead.org> <mawilcox@linuxonhyperv.com>
@@ -645,7 +646,8 @@ Nadav Amit <nadav.amit@gmail.com> <namit@cs.technion.ac.il>
Nadia Yvette Chambers <nyc@holomorphy.com> William Lee Irwin III <wli@holomorphy.com>
Naoya Horiguchi <nao.horiguchi@gmail.com> <n-horiguchi@ah.jp.nec.com>
Naoya Horiguchi <nao.horiguchi@gmail.com> <naoya.horiguchi@nec.com>
Natalie Vock <natalie.vock@gmx.de> <friedrich.vock@gmx.de>
Natalie Vock <nat@pixelcluster.dev> <friedrich.vock@gmx.de>
Natalie Vock <nat@pixelcluster.dev> <natalie.vock@gmx.de>
Nathan Chancellor <nathan@kernel.org> <natechancellor@gmail.com>
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.ibm.com>
Naveen N Rao <naveen@kernel.org> <naveen.n.rao@linux.vnet.ibm.com>
@@ -251,6 +251,13 @@ Description: RO. Fan 2 speed in RPM.
Only supported for particular Intel Xe graphics platforms.
On DG2 the driver always shows two fan channels, because the
FSC_READ_NUM_FANS command does not work on some cards. OEMs
decide how the fans map to tach channels, so two fans can share
one tach line. When that happens, the second channel
reads 0 RPM even though the fan is spinning. This is normal, not
a bug.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan3_input
Date: March 2025
KernelVersion: 6.16
@@ -0,0 +1,30 @@
What: /sys/bus/pci/drivers/xe/.../gpu_health
Date: July 2026
KernelVersion: 7.3
Contact: intel-xe@lists.freedesktop.org
Description:
This file exposes the current gpu health state and allows the gpu
health state to be updated.
This sysfs file is present only on Intel Xe platforms that support
the gpu health indicator interface for RAS. Reading the current
health state is available to all users, while updating the health
state is restricted to administrative users only.
Read returns a single line containing one of the valid values for
the current gpu health state. Writing one of the valid values
updates the current gpu health state.
The valid values for the gpu health state are:
ok
The gpu is healthy and operating within normal
parameters.
warning
The gpu is experiencing minor issues but remains
operational.
critical
The gpu is in a critical state and may not be
operational.
+6
View File
@@ -2918,6 +2918,12 @@ DMEM Interface Files
The semantics are the same as for the memory cgroup controller, and are
calculated in the same way.
dmem.peak
A read-only nested-keyed file that exists on non-root cgroups.
The max device memory usage recorded for the cgroup and its
descendants since the creation of the cgroup for each region.
dmem.capacity
A read-only file that describes maximum region capacity.
It only exists on the root cgroup. Not all memory can be
@@ -28,6 +28,7 @@ properties:
- const: renesas,r9a09g057-mipi-dsi
- enum:
- renesas,r9a08g046-mipi-dsi # RZ/G3L
- renesas,r9a09g057-mipi-dsi # RZ/V2H(P)
reg:
@@ -0,0 +1,120 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/bridge/renesas,r9a08g046-lvds.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G3L LVDS Encoder
maintainers:
- Biju Das <biju.das.jz@bp.renesas.com>
- Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
description: |
This binding describes the LVDS encoder embedded in the Renesas RZ/G3L
SoC. The encoder can operate in LVDS Single-link mode with 4 lanes
(Data) + 1 lane (Clock).
properties:
compatible:
const: renesas,r9a08g046-lvds
reg:
maxItems: 1
clocks:
items:
- description: Peripheral clock
- description: PHY clock
- description: Dot clock
clock-names:
items:
- const: pclk
- const: phyclk
- const: dotclk
resets:
items:
- description: LVDS_RESET_N
- description: MIPI_DSI_PRESET_N
- description: MIPI_DSI_CMN_RSTB
- description: MIPI_DSI_ARESET_N
reset-names:
items:
- const: lvdrst
- const: prst
- const: rst
- const: arst
power-domains:
maxItems: 1
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Input channel, directly connected to the Display Unit.
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: |
Output channel, directly connected to the LVDS panel or bridge.
required:
- port@0
- port@1
required:
- compatible
- reg
- clocks
- clock-names
- resets
- reset-names
- power-domains
- ports
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/renesas,r9a08g046-cpg.h>
lvds@108a0000 {
compatible = "renesas,r9a08g046-lvds";
reg = <0x108a0000 0x10000>;
clocks = <&cpg CPG_MOD R9A08G046_MIPI_DSI_PCLK>,
<&cpg CPG_MOD R9A08G046_LVDS_PLLCLK>,
<&cpg CPG_MOD R9A08G046_LVDS_CLK_DOT0>;
clock-names = "pclk", "phyclk", "dotclk";
resets = <&cpg R9A08G046_LVDS_RESET_N>,
<&cpg R9A08G046_MIPI_DSI_PRESET_N>,
<&cpg R9A08G046_MIPI_DSI_CMN_RSTB>,
<&cpg R9A08G046_MIPI_DSI_ARESET_N>;
reset-names = "lvdrst", "prst", "rst", "arst";
power-domains = <&cpg>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
lvds0_in: endpoint {
remote-endpoint = <&du_out_lvds0>;
};
};
port@1 {
reg = <1>;
lvds0_out: endpoint {
remote-endpoint = <&panel_in>;
};
};
};
};
...
@@ -15,10 +15,13 @@ description: |
properties:
compatible:
enum:
- mediatek,mt7623-cec
- mediatek,mt8167-cec
- mediatek,mt8173-cec
oneOf:
- const: mediatek,mt8173-cec
- items:
- enum:
- mediatek,mt7623-cec
- mediatek,mt8167-cec
- const: mediatek,mt8173-cec
reg:
maxItems: 1
@@ -0,0 +1,66 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/anbernic,td4310.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Anbernic TD4310 Based Panels
maintainers:
- Chris Morgan <macromorgan@hotmail.com>
description:
Anbernic TD4310 Based Panels, such as the RG-Vita-Pro panel
(a 1080x1920 5.5 inch panel).
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- anbernic,panel-vita-pro
- const: anbernic,td4310
reg:
maxItems: 1
vdd-supply:
description: Panel power supply
required:
- compatible
- port
- reg
- reset-gpios
- vdd-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "anbernic,panel-vita-pro", "anbernic,td4310";
reg = <0>;
backlight = <&backlight>;
enable-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
rotation = <270>;
vdd-supply = <&vdd_lcd>;
port {
endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
...
@@ -0,0 +1,79 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/chipone,icna3512.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Chipone ICNA3512 and ICNA3520 display drivers
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
description:
The Chipone ICNA3512 and ICNA3520 are DDICs connected
using a MIPI-DSI video interface.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
oneOf:
- items:
- enum:
- ayaneo,pocketds-panel-top
- ayntec,odin2portal-panel
- const: chipone,icna3512
- items:
- enum:
- ayntec,odin3-panel
- ayntec,thor-panel-top
- const: chipone,icna3520
reg:
maxItems: 1
description: DSI virtual channel
vdd-supply: true
vddio-supply: true
vci-supply: true
disp-supply: true
blvdd-supply: true
port: true
reset-gpios: true
rotation: true
required:
- compatible
- reg
- vdd-supply
- vddio-supply
- vci-supply
- disp-supply
- blvdd-supply
- reset-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "ayntec,odin2portal-panel", "chipone,icna3512";
reg = <0>;
vdd-supply = <&vreg_l11b_1p2>;
vddio-supply = <&vreg_l12b_1p8>;
vci-supply = <&vreg_l13b_3p0>;
disp-supply = <&vdd_disp_2v8>;
blvdd-supply = <&vdd_bl_5v0>;
reset-gpios = <&tlmm 133 GPIO_ACTIVE_LOW>;
};
};
...
@@ -40,6 +40,9 @@ properties:
vddi-supply:
description: power supply for IC
bl-supply:
description: power supply for backlight, in case it's managed via DSC
backlight: true
ports: true
@@ -0,0 +1,71 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/ilitek,ili7807s.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ilitek ILI7807S-based DSI panels
maintainers:
- Arpit Saini <arpit.saini@oss.qualcomm.com>
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- dlc,dlc0697
- const: ilitek,ili7807s
reg:
maxItems: 1
description: DSI virtual channel
vddi-supply:
description: I/O voltage supply (1.8V)
avdd-supply:
description: Positive LCD bias supply (AVDD), typically +5.5V
(range 4.5V to 6.3V)
avee-supply:
description: Negative LCD bias supply (AVEE), typically -5.5V
(range -6.3V to -4.5V)
required:
- compatible
- reg
- reset-gpios
- vddi-supply
- avdd-supply
- avee-supply
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "dlc,dlc0697", "ilitek,ili7807s";
reg = <0>;
reset-gpios = <&tlmm 3 GPIO_ACTIVE_LOW>;
vddi-supply = <&pm4125_l15>;
avdd-supply = <&avdd>;
avee-supply = <&avee>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};
@@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/ilitek,ili9488.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ilitek ILI9488 based MIPI-DSI panels
maintainers:
- Igor Reznichenko <igor@reznichenko.net>
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- focuslcds,e35gh-i-mw800cb
- const: ilitek,ili9488
reg:
maxItems: 1
vci-supply: true
iovcc-supply: true
required:
- compatible
- reg
- vci-supply
- iovcc-supply
- reset-gpios
- backlight
- port
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "focuslcds,e35gh-i-mw800cb", "ilitek,ili9488";
reg = <0>;
vci-supply = <&reg_vci_panel>;
iovcc-supply = <&reg_iovcc_panel>;
reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>;
backlight = <&pwm_bl>;
port {
panel_in: endpoint {
remote-endpoint = <&dsi_out>;
};
};
};
};
...
@@ -19,6 +19,7 @@ properties:
- anbernic,rg-ds-display-bottom
- anbernic,rg-ds-display-top
- chongzhou,cz101b4001
- dcltek,dt300250
- kingdisplay,kd101ne3-40ti
- melfas,lmfbx101117480
- radxa,display-10hd-ad001
@@ -0,0 +1,91 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/novatek,nt36536.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Novatek NT36536 based DSI display Panels
maintainers:
- Pengyu Luo <mitltlatltl@gmail.com>
description:
The Novatek NT36536 is a generic DSI Panel IC used to drive dsi
panels. Support video mode panels from China Star Optoelectronics
Technology (CSOT), such as PP8807HB1-1 which is a dual-link 10-bit
panel.
allOf:
- $ref: panel-common-dual.yaml#
properties:
compatible:
items:
- enum:
- csot,pp8807hb1-1
- const: novatek,nt36536
reg:
maxItems: 1
reset-gpios:
maxItems: 1
vddio-supply:
description: I/O source voltage rail
vsp-supply:
description: Positive source voltage rail
vsn-supply:
description: Negative source voltage rail
backlight: true
ports: true
required:
- compatible
- reg
- vddio-supply
- reset-gpios
- ports
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "csot,pp8807hb1-1", "novatek,nt36536";
reg = <0>;
vddio-supply = <&vreg_iovdd_1p8>;
reset-gpios = <&tlmm 98 GPIO_ACTIVE_LOW>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel_in_0: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@1{
reg = <1>;
panel_in_1: endpoint {
remote-endpoint = <&dsi1_out>;
};
};
};
};
};
...
@@ -58,6 +58,8 @@ properties:
- hydis,hv070wx2-1e0
# Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel
- jenson,bl-jt60050-01a
# Opto Logic SCX1001511GGC49 10.1" WXGA (1280x800) TFT LCD LVDS panel
- optologic,scx1001511ggc49
# Riverdi RVT101HVLNWC00 10.1" WXGA (1280x800) TFT LCD LVDS panel
- riverdi,rvt101hvlnwc00
# Riverdi RVT70HSLNWCA0 7.0" WSVGA (1024x600) TFT LCD LVDS panel
@@ -37,6 +37,8 @@ properties:
- ampire,am-480272h3tmqw-t01h
# Ampire AM-800480L1TMQW-T00H 5" WVGA TFT LCD panel
- ampire,am-800480l1tmqw-t00h
# Ampire AM-800480N3TZQW-00H 5" WVGA TFT LCD panel
- ampire,am-800480n3tzqw-00h
# Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel
- ampire,am800480r3tmqwa1h
# Ampire AM-800600P5TMQW-TB8H 8.0" SVGA TFT LCD panel
@@ -0,0 +1,98 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/renesas,r63419.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas R63419 based dual-DSI video mode Display Panel
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
description:
The Renesas R63419 is a generic DDIC used to control dual-DSI LCD panels.
allOf:
- $ref: panel-common-dual.yaml#
properties:
compatible:
items:
- enum:
- ayaneo,wt0600-2k
- ayaneo,wt0630-2k
- const: renesas,r63419
reg:
maxItems: 1
vdd-supply: true
vddio-supply: true
vsp-supply: true
vsn-supply: true
vci-supply: true
backlight: true
reset-gpios: true
rotation: true
ports: true
required:
- compatible
- reg
- vdd-supply
- vddio-supply
- vsp-supply
- vsn-supply
- vci-supply
- backlight
- reset-gpios
- ports
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "ayaneo,wt0600-2k", "renesas,r63419";
reg = <0>;
reset-gpios = <&gpio 176 GPIO_ACTIVE_LOW>;
vdd-supply = <&vdd_3v0_lcd>;
vddio-supply = <&vdd_1v8_io>;
vsn-supply = <&vdd_5v0_neg>;
vsp-supply = <&vdd_5v0_pos>;
vci-supply = <&vdd_3v0_vci>;
backlight = <&backlight>;
rotation = <90>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel_in0: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
port@1 {
reg = <1>;
panel_in1: endpoint {
remote-endpoint = <&dsi1_out>;
};
};
};
};
};
...
@@ -22,6 +22,8 @@ properties:
enum:
# Anberic RG353V-V2 5.0" 640x480 TFT LCD panel
- anbernic,rg353v-panel-v2
# AYANEO Pocket DS lower 5.0" 1024x768 IPS LCD panel
- ayaneo,pocket-ds-lower-panel
# GameForce Chi 3.5" 640x480 TFT LCD panel
- gameforce,chi-panel
# Powkiddy RGB10MAX3 5.0" 720x1280 TFT LCD panel
@@ -25,6 +25,8 @@ properties:
- samsung,atna40ct06
# Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel
- samsung,atna40cu11
# Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel
- samsung,atna40hq08
# Samsung 14" WQXGA+ (2880×1800 pixels) eDP AMOLED panel
- samsung,atna40yk20
# Samsung 14.5" WQXGA+ (2880x1800 pixels) eDP AMOLED panel
@@ -20,6 +20,7 @@ properties:
- enum:
- renesas,r9a07g043u-du # RZ/G2UL
- renesas,r9a07g044-du # RZ/G2{L,LC}
- renesas,r9a08g046-du # RZ/G3L
- renesas,r9a09g057-du # RZ/V2H(P)
- renesas,r9a09g077-du # RZ/T2H
- items:
@@ -65,7 +66,7 @@ properties:
model-dependent. Each port shall have a single endpoint.
patternProperties:
"^port@[0-1]$":
"^port@[0-2]$":
$ref: /schemas/graph.yaml#/properties/port
unevaluatedProperties: false
@@ -108,6 +109,7 @@ allOf:
port@0:
description: DPI
port@1: false
port@2: false
required:
- port@0
@@ -124,10 +126,31 @@ allOf:
description: DSI
port@1:
description: DPI
port@2: false
required:
- port@0
- port@1
- if:
properties:
compatible:
contains:
const: renesas,r9a08g046-du
then:
properties:
ports:
properties:
port@0:
description: DSI
port@1:
description: DPI
port@2:
description: LVDS
required:
- port@0
- port@1
- port@2
- if:
properties:
compatible:
@@ -140,6 +163,7 @@ allOf:
port@0:
description: DSI
port@1: false
port@2: false
required:
- port@0
@@ -15,18 +15,24 @@ properties:
enum:
- rockchip,rk3288-dp
- rockchip,rk3399-edp
- rockchip,rk3576-edp
- rockchip,rk3588-edp
clocks:
minItems: 2
maxItems: 3
items:
- description: Reference clock
- description: APB bus clock
- description: GRF or AHB bus clock
clock-names:
minItems: 2
items:
- const: dp
- const: pclk
- const: grf
- enum:
- grf
- hclk
power-domains:
maxItems: 1
@@ -65,9 +71,46 @@ allOf:
compatible:
contains:
enum:
- rockchip,rk3288-dp
then:
properties:
clocks:
maxItems: 2
clock-names:
maxItems: 2
- if:
properties:
compatible:
contains:
enum:
- rockchip,rk3399-edp
then:
properties:
clocks:
minItems: 3
clock-names:
items:
- const: dp
- const: pclk
- const: grf
- if:
properties:
compatible:
contains:
enum:
- rockchip,rk3576-edp
- rockchip,rk3588-edp
then:
properties:
clocks:
minItems: 3
clock-names:
items:
- const: dp
- const: pclk
- const: hclk
resets:
minItems: 2
reset-names:
@@ -22,6 +22,7 @@ properties:
- nvidia,tegra186-vic
- nvidia,tegra194-vic
- nvidia,tegra234-vic
- nvidia,tegra264-vic
- items:
- const: nvidia,tegra132-vic
@@ -25,6 +25,7 @@ properties:
- nvidia,tegra186-host1x
- nvidia,tegra194-host1x
- nvidia,tegra234-host1x
- nvidia,tegra264-host1x
- items:
- const: nvidia,tegra132-host1x
@@ -57,7 +58,8 @@ properties:
enum: [1, 2]
ranges:
maxItems: 1
minItems: 1
maxItems: 2
clocks:
description: Must contain one entry, for the module clock. See
@@ -192,6 +194,7 @@ allOf:
contains:
enum:
- nvidia,tegra234-host1x
- nvidia,tegra264-host1x
then:
properties:
reg-names:
@@ -239,6 +242,21 @@ allOf:
required:
- reg-names
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra264-host1x
then:
properties:
ranges:
minItems: 2
maxItems: 2
else:
properties:
ranges:
maxItems: 1
examples:
- |
@@ -8,7 +8,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Imagination Technologies PowerVR and IMG Rogue GPUs
maintainers:
- Frank Binns <frank.binns@imgtec.com>
- Alessio Belle <alessio.belle@imgtec.com>
- Luigi Santivetti <luigi.santivetti@imgtec.com>
properties:
compatible:
@@ -9,7 +9,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Imagination Technologies PowerVR SGX GPUs
maintainers:
- Frank Binns <frank.binns@imgtec.com>
- Alessio Belle <alessio.belle@imgtec.com>
- Luigi Santivetti <luigi.santivetti@imgtec.com>
properties:
compatible:
@@ -227,6 +227,8 @@ patternProperties:
description: Axis Communications AB
"^ayaneo,.*":
description: Anyun Intelligent Technology (Hong Kong) Co., Ltd
"^ayntec,.*":
description: AYN Technologies Co., Ltd.
"^azoteq,.*":
description: Azoteq (Pty) Ltd
"^azw,.*":
@@ -411,6 +413,8 @@ patternProperties:
description: DataImage, Inc.
"^davicom,.*":
description: DAVICOM Semiconductor, Inc.
"^dcltek,.*":
description: DCL Technologies Inc.
"^deepcomputing,.*":
description: DeepComputing (HK) Limited
"^dell,.*":
@@ -610,6 +614,8 @@ patternProperties:
description: Flipkart Inc.
"^focaltech,.*":
description: FocalTech Systems Co.,Ltd
"^focuslcds,.*":
description: Focus Display Solutions, Inc.
"^forlinx,.*":
description: Baoding Forlinx Embedded Technology Co., Ltd.
"^foursemi,.*":
@@ -1265,6 +1271,8 @@ patternProperties:
description: OpenWrt
"^option,.*":
description: Option NV
"^optologic,.*":
description: Opto Logic S.A.
"^oranth,.*":
description: Shenzhen Oranth Technology Co., Ltd.
"^ORCL,.*":
@@ -1427,6 +1435,8 @@ patternProperties:
description: Embest RIoT
"^riscv,.*":
description: RISC-V Foundation
"^riverdi,.*":
description: Riverdi Sp. z o.o
"^rockchip,.*":
description: Rockchip Electronics Co., Ltd.
"^rocktech,.*":
@@ -32,6 +32,9 @@ Interrupts
.. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
:functions: register_hpd_handlers dm_crtc_high_irq dm_pflip_high_irq
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
:functions: amdgpu_display_hotplug_work_func
Atomic Implementation
=====================
@@ -178,3 +181,9 @@ following path:
2. On DC interface, :c:type:`struct mpcc_blnd_cfg <mpcc_blnd_cfg>` programs the
MPCC blend configuration considering the :c:type:`dc_plane_info
<dc_plane_info>` input from DPP.
Display Properties
==================
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
:doc: property for adaptive backlight modulation
+2 -1
View File
@@ -99,7 +99,8 @@ How to enable automated testing on your tree
============================================
1. Create a Linux tree in https://gitlab.freedesktop.org/ if you don't have one
yet
yet, by forking https://gitlab.freedesktop.org/drm/kernel (this allows GitLab
to internally track that these are the same git objects).
2. In your kernel repo's configuration (eg.
https://gitlab.freedesktop.org/janedoe/linux/-/settings/ci_cd), change the
+6
View File
@@ -171,6 +171,12 @@ Bridge Operations
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: bridge operations
Bridge Chain Format Selection
-----------------------------
.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: bridge chain format selection
Bridge Connector Helper
-----------------------
+12
View File
@@ -287,6 +287,12 @@ structure, ordering of committing state changes to hardware is sequenced using
Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed
coverage of specific topics.
Atomic State Lifetime
---------------------
.. kernel-doc:: drivers/gpu/drm/drm_atomic.c
:doc: state lifetime
Handling Driver Private State
-----------------------------
@@ -604,6 +610,12 @@ Color Management Properties
.. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c
:doc: overview
Color Format Property
---------------------
.. kernel-doc:: drivers/gpu/drm/drm_connector.c
:doc: Color format
Tile Group Property
-------------------
@@ -0,0 +1,11 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
DisplayPort Link Capabilities
=============================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_caps.c
:doc: DisplayPort link capabilities
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_caps.h
:internal:
@@ -0,0 +1,8 @@
.. SPDX-License-Identifier: MIT
.. Copyright © 2026 Intel Corporation
DisplayPort Link Training
=========================
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_training.c
:doc: DisplayPort link training
@@ -38,6 +38,8 @@ driver. The display driver isn't an independent driver in that sense.
fifo-underrun
frontbuffer
hotplug
dp-link-training
dp-link-capabilities
plane
psr
snps-phy
+142
View File
@@ -0,0 +1,142 @@
.. SPDX-License-Identifier: GPL-2.0
===================================================
FSP (Foundation Security Processor) and Secure Boot
===================================================
This document describes the role of the FSP in the GPU boot sequence on
Hopper and Blackwell GPUs, and how it differs from the earlier Ampere boot
flow. It also provides a brief overview of the PRC (Product Reconfiguration
Control) protocol used to query device configuration through FSP. As with
other documents in this directory, the information is subject to change and
is intended to help developers understand the corresponding kernel code.
What is FSP?
============
The Foundation Security Processor (FSP) is the GPU's Internal Root of Trust
(IROT). It is a dedicated security processor that boots from immutable ROM
(Boot ROM) inside the GPU and is responsible for establishing the Chain of
Trust before any other firmware is allowed to run.
FSP runs independently of the host CPU and starts executing as soon as the
GPU is powered on. By the time the nova-core driver is loaded, FSP has
already completed its own secure boot and is ready to accept commands from
the driver.
Simplified boot flow (Hopper/Blackwell)
=======================================
Starting with Hopper, the boot flow is significantly simplified compared to
earlier GPU generations like Ampere.
On an **Ampere** GPU, the boot verification chain involves multiple Falcon
engines and multiple ucode stages (see falcon.rst for details)::
Hardware BROM (SEC2)
-> HS Booter (SEC2)
-> LS GSP-RM (GSP)
The driver must extract ucode from VBIOS, manage SEC2 and GSP, and
orchestrate the Booter to load GSP-RM. This involves FWSEC-FRTS, devinit,
and the Booter stages.
On **Hopper/Blackwell** GPUs, FSP replaces this multi-stage process with a
single message-driven interface::
FSP (hardware root of trust, boots from ROM)
-> FMC (Falcon Microcontroller, verified by FSP)
-> GSP-RM (verified and loaded by FMC)
The driver only needs to:
1. Wait for FSP to complete its own secure boot (polling a scratch register).
2. Send a Chain of Trust (COT) message to FSP with the FMC firmware location,
cryptographic signatures, and GSP boot parameters.
3. FSP authenticates the FMC firmware and boots it, FMC in turn loads GSP-RM.
There is no SEC2 involvement, no Booter ucode, and no FWSEC-FRTS stage. The
entire secure boot is driven by a single FSP message exchange.
Chain of Trust (COT) protocol
=============================
The Chain of Trust establishes a cryptographically enforced boot sequence,
ensuring the GPU reaches a known, trusted state.
The driver communicates with FSP using a message queue (Falcon MSGQ
interface). Each message consists of an MCTP (Management Component Transport
Protocol) transport header and an NVDM (NVIDIA Vendor Defined Message) header,
followed by a protocol-specific payload.
For Chain of Trust, the payload includes:
- The system memory address of the FMC firmware image.
- Cryptographic material: a SHA-384 hash, RSA-3K public key, and RSA-3K
signature extracted from the FMC ELF firmware.
- FRTS (Firmware Runtime Services) region information (vidmem offset and size).
- The system memory address of the GSP boot arguments structure.
FSP verifies the signature against the provided public key and hash, and if
verification succeeds, boots the FMC. The FMC then authenticates and launches
GSP-RM.
The message flow is::
nova-core FSP
| |
| 1. Poll scratch register |
| (wait for FSP boot complete) |
| |
| 2. COT message ------------> |
| (FMC addr, signatures, |
| boot params) |
| |
| |--- Verify FMC signature
| |--- Boot FMC
| |--- FMC loads GSP-RM
| |
| 3. COT response <------------ |
| (success/error) |
| |
FSP message format
==================
All FSP messages share a common header format consisting of two 32-bit words:
**MCTP header** (Management Component Transport Protocol):
- Bit 31: SOM (Start of Message)
- Bit 30: EOM (End of Message)
- Bits 29:28: Packet sequence number
- Bits 23:16: Source Endpoint ID
**NVDM header** (NVIDIA Vendor Defined Message):
- Bits 6:0: MCTP message type (0x7e = vendor-defined PCI)
- Bits 23:8: PCI vendor ID (0x10de = NVIDIA)
- Bits 31:24: NVDM type (0x14 = COT, 0x13 = PRC, 0x15 = FSP response)
PRC (Product Reconfiguration Control) protocol
===============================================
PRC is an API system exposed through FSP's Management Partition that allows
querying and modifying device configuration without firmware updates.
Configuration parameters are called "knobs". Each knob has a unique object
ID and controls a specific device behavior. Examples include vGPU mode, ECC
enable, confidential computing mode, and NVLINK configuration.
Each knob has two values:
- **Active**: the currently effective value for this boot cycle.
- **Persistent**: the value stored in InfoROM, applied on subsequent boots.
The nova-core driver uses PRC to read the vGPU mode knob (object ID 0x29)
during early boot, before firmware loading, to determine whether the GPU
should operate in vGPU mode.
The PRC message format follows the same MCTP/NVDM header structure as COT,
with NVDM type 0x13. The payload contains:
- A sub-command (e.g., 0x0c for read).
- Flags indicating which value to read (bit 0 = persistent, bit 1 = active).
- The knob object ID.
The response includes the common FSP response header (with error status)
followed by the knob's 16-bit state value.
+184
View File
@@ -0,0 +1,184 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
==================================
TLV Tags in Nova Firmware Images
==================================
Nova firmware images use a Type-Length-Value (TLV) format to encapsulate
firmware components and metadata. The TLV file begins with a 4-byte "magic"
header that contains the string "NVFW". Following the header is a sequence of
TLV blocks.
Each block consists of a 4-byte tag of ASCII characters, a 4-byte length
encoded as a little-endian unsigned integer, and a sequence of bytes, the size
of which is equal to the length rounded up to the next multiple of 4.
The driver code that reads the TLV and uses its contents is called the parser.
It is the responsibility of the parser to handle missing or malformed tags,
lengths, and values in the TLV.
::
+------+------+------+------+
| 'N' | 'V' | 'F' | 'W' | Magic header
+------+------+------+------+
| Tag (4 bytes, ASCII) | TLV block 0
+---------------------------+
| Length (4 bytes, LE) |
+---------------------------+
| |
| Value (length bytes, |
| padded to 4-byte align) |
| |
+---------------------------+
| Tag (4 bytes, ASCII) | TLV block 1
+---------------------------+
| Length (4 bytes, LE) |
+---------------------------+
| |
| Value (length bytes, |
| padded to 4-byte align) |
| |
+---------------------------+
| ... | More TLV blocks
+---------------------------+
Tags and Length
===============
TLV tags are always four-character words, with all letters being upper case.
Duplicate tags are not allowed.
A TLV file may contain additional tags not described in this document.
Values
======
Values are one of four types. The type is not encoded in the format; rather,
the parser expects a given tag to have a value of a given type.
1) Integers, encoded in 32-bit or 64-bit little-endian format.
2) Strings, encoded as-is and required to be only printable ASCII characters
and without a null terminator.
3) An array of bytes, for binary data.
4) Boolean, encoded as single byte, with a value of 0 for False or 1 for True.
Common Tags
===========
These tags are shared across firmware types and carry the same meaning
wherever they appear. Unlike the firmware-specific tags below, a common tag
is reserved: its meaning is fixed and may never be redefined for a particular
firmware type.
``VERS`` (string)
Human-readable firmware version string. Present in all TLV files.
A TLV image must contain either a single ``BLOB`` tag (firmware embedded
inline) or a ``SIZE``/``FILE`` pair (firmware stored in a separate file).
``BLOB`` (bytes)
If the firmware microcode binary is stored in the TLV, this tag contains
the actual firmware image bytes.
``FILE`` (string)
If the firmware binary is stored as a separate file, this tag contains the
name of that file, which is required to be in the same directory as the TLV,
so no paths are allowed in the filename. This tag is always paired with
``SIZE``, so as to allow the driver to pre-allocate the buffer before
loading the file.
``SIZE`` (u32)
Total size in bytes of the firmware image to be loaded from the companion
file named by ``FILE``. This tag is mandatory if ``FILE`` exists, so the
size of the firmware image must be known when the TLV is created. If the
firmware image is updated and its size changes, then the TLV must be
updated with it.
GSP Firmware Tags
=================
``SIGN`` (bytes)
Cryptographic signature for the GSP firmware.
``BLID`` (string)
The build ID, extracted from the ".note.gnu.build-id" section.
Booter Firmware Tags
====================
``DAOF`` (u32) - ``os_data_offset``
OS data section offset within the firmware image (absolute byte offset).
Maps to the DMEM load source.
``DASZ`` (u32) - ``os_data_size``
OS data section size in bytes.
``CDOF`` (u32) - ``os_code_offset``
OS code section offset within the firmware image (absolute byte offset).
Maps to the non-secure IMEM load source.
``CDSZ`` (u32) - ``os_code_size``
OS code section size in bytes.
``PLOC`` (u32) - ``patch_loc``
Signature patch location -- byte offset within the firmware image where the
selected signature should be written.
``FUSE`` (u32) - ``fuse_version``
Fuse version of the firmware, used with the hardware fuse register to
select the correct signature index.
``ENID`` (u32) - ``engine_id``
Engine ID mask identifying the falcon engine this firmware targets.
``UCID`` (u32) - ``ucode_id``
Microcode ID used together with the engine ID to query hardware signature
fuse registers.
``A0CO`` (u32) - ``app0_code_offset``
App0 code offset -- start of the secure code region within the firmware
image. Used as the IMEM secure section source.
``A0CS`` (u32) - ``app0_code_size``
App0 code size in bytes.
``NSIG`` (u32) - ``num_sigs``
Number of signatures included in the ``SIGN`` tag.
``SIGN`` (bytes)
Concatenated array of firmware signatures. The size of each signature is
the total length of the ``SIGN`` value divided by ``NSIG``. The correct
signature is selected using the fuse-version-derived index.
Generic Bootloader Tags
=======================
``CDSZ`` (u32) - ``code_size``
Size in bytes of the bootloader code to copy from the ``BLOB`` tag and
PIO-load into falcon IMEM.
``STRT`` (u32) - ``start_tag``
Start tag identifying the IMEM block where execution begins. The falcon
boot address is derived as ``start_tag << 8``.
GSP Bootloader Tags
===================
``CDOF`` (u32) - ``code_offset``
Offset within the firmware image at which the code section starts.
``DAOF`` (u32) - ``data_offset``
Offset within the firmware image at which the data section starts.
``MFOF`` (u32) - ``manifest_offset``
Offset within the firmware image at which the manifest starts.
``APPV`` (u32) - ``app_version``
Application version of the firmware.
FMC Firmware Tags
=================
``HASH`` (bytes)
SHA-384 hash of the FMC firmware, exactly 48 bytes long.
``PKEY`` (bytes)
Public key used to verify the FMC firmware. At most 384 bytes (RSA-3072),
but may be shorter.
``SIGN`` (bytes)
Signature of the FMC firmware. At most 384 bytes (RSA-3072), but may
be shorter.
+2
View File
@@ -30,5 +30,7 @@ vGPU manager VFIO driver and the nova-drm driver.
core/todo
core/vbios
core/devinit
core/fsp
core/fwsec
core/falcon
core/tlv
+46 -4
View File
@@ -55,7 +55,7 @@ There are still drivers that use drm_simple_display_pipe. The task here is to
convert them to use regular atomic helpers. Search for a driver that calls
drm_simple_display_pipe_init() and inline all helpers from drm_simple_kms_helper.c
into the driver, such that no simple-KMS interfaces are required. Please also
rename all inlined fucntions according to driver conventions.
rename all inlined functions according to driver conventions.
Contact: Thomas Zimmermann, respective driver maintainer
@@ -896,10 +896,11 @@ complection of submission.
One minor feature still missing is a generic DRM IOCTL to query the error
status of binary and timeline drm_syncobj.
This should probably be improved by implementing the necessary kernel interface
and adding support for that in the userspace stack.
This was already improved by implementing the necessary kernel interface
(patches are on the dri-devel mailing list) but adding support for that in the
userspace stack is still missing.
Contact: Christian König
Contact: Christian König, Michel Dänzer
Level: Starter
@@ -948,6 +949,47 @@ Contact: Philipp Stanner <phasta@kernel.org>
Level: Intermediate
Replace the lockless queue with a locked list
---------------------------------------------
drm_sched is the only user in the entire kernel of a special lockless queue, the
spsc_queue. This queue utilizes:
- preempt_disable()
- atomic instructions
- memory barriers
- ACCESS_ONCE()
whereas a conventional spinlock utilizes:
- preempt_disable()
- 1 atomic instruction for taking / releasing the lock
- memory barriers
Moreover, drm_sched_entity_push_job(), the only user of spsc_queue_push(), has
to take a lock in some situations anyways and calls to it are often serialized
with a driver lock.
It is, thus, highly questionable whether the lockless queue grants any advantage
at all. Considering that its internals are not well documented and its correctness
is not formally proven, it seems desirable to replace the queue with a mere list
or hlist that is protected by a spinlock.
Tasks:
- Replace the spsc_queue in drm/sched (and those who might access the scheduler's
internal queue) with a spinlock + (h)list.
- Ideally, check with some micro benchmarks and real world tests (preferably
with amdgpu) for relevant performance regressions.
- Remove the spsc_queue from the kernel altogether.
Contact:
- Philipp Stanner <phasta@kernel.org>
- Christian König <christian.koenig@amd.com>
Level: Beginner
Outside DRM
===========
+7
View File
@@ -8,3 +8,10 @@ Xe Device Wedging
.. kernel-doc:: drivers/gpu/drm/xe/xe_device.c
:doc: Xe Device Wedging
====================
GPU Health Indicator
====================
.. kernel-doc:: drivers/gpu/drm/xe/xe_ras.c
:doc: GPU Health Indicator
+17 -5
View File
@@ -6641,7 +6641,7 @@ F: tools/testing/selftests/cgroup/test_cpuset_v1_base.sh
CONTROL GROUP - DEVICE MEMORY CONTROLLER (DMEM)
M: Maarten Lankhorst <dev@lankhorst.se>
M: Maxime Ripard <mripard@kernel.org>
M: Natalie Vock <natalie.vock@gmx.de>
M: Natalie Vock <nat@pixelcluster.dev>
L: cgroups@vger.kernel.org
L: dri-devel@lists.freedesktop.org
S: Maintained
@@ -8138,6 +8138,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/ilitek,ili9486.yaml
F: drivers/gpu/drm/tiny/ili9486.c
DRM DRIVER FOR ILITEK ILI9488 PANELS
M: Igor Reznichenko <igor@reznichenko.net>
S: Maintained
F: Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml
F: drivers/gpu/drm/panel/panel-ilitek-ili9488.c
DRM DRIVER FOR ILITEK ILI9805 PANELS
M: Michael Trimarchi <michael@amarulasolutions.com>
S: Maintained
@@ -8870,11 +8876,14 @@ M: Sandy Huang <hjc@rock-chips.com>
M: Heiko Stübner <heiko@sntech.de>
M: Andy Yan <andy.yan@rock-chips.com>
L: dri-devel@lists.freedesktop.org
L: linux-rockchip@lists.infradead.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/rockchip/
F: drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.*
F: drivers/gpu/drm/ci/xfails/rockchip*
F: drivers/gpu/drm/rockchip/
F: include/drm/bridge/dw_hdmi_qp.h
DRM DRIVERS FOR STI
M: Alain Volmat <alain.volmat@foss.st.com>
@@ -12687,12 +12696,15 @@ S: Orphan
F: drivers/media/rc/img-ir/
IMGTEC POWERVR DRM DRIVER
M: Frank Binns <frank.binns@imgtec.com>
M: Matt Coster <matt.coster@imgtec.com>
M: Alessio Belle <alessio.belle@imgtec.com>
M: Luigi Santivetti <luigi.santivetti@imgtec.com>
L: imagination@lists.freedesktop.org
S: Supported
Q: https://patchwork.freedesktop.org/project/imagination/list/
B: https://gitlab.freedesktop.org/imagination/linux/-/issues
C: irc://irc.oftc.net/powervr
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml
F: Documentation/devicetree/bindings/gpu/img,powervr-sgx.yaml
F: Documentation/devicetree/bindings/gpu/img,powervr-*.yaml
F: Documentation/gpu/imagination/
F: drivers/gpu/drm/ci/xfails/powervr*
F: drivers/gpu/drm/imagination/
+6
View File
@@ -758,9 +758,15 @@ static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map
return 0;
}
static struct dma_buf *amdxdna_gem_dev_obj_export(struct drm_gem_object *gobj, int flags)
{
return ERR_PTR(-EOPNOTSUPP);
}
static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = {
.free = amdxdna_gem_dev_obj_free,
.vmap = amdxdna_gem_dev_obj_vmap,
.export = amdxdna_gem_dev_obj_export,
};
static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
+1 -1
View File
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o
ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o
ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o ethosu_perfmon.o
+33 -2
View File
@@ -6,6 +6,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <drm/drm_device.h>
@@ -43,6 +44,15 @@ struct gen_pool;
#define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8)
#define NPU_BASEP_REGION_MAX 8
#define NPU_REG_PMCR 0x0180
#define NPU_REG_PMCNTENSET 0x0184
#define NPU_REG_PMCNTENCLR 0x0188
#define NPU_REG_PMCCNTR_LO 0x01A0
#define NPU_REG_PMCCNTR_HI 0x01A4
#define NPU_REG_PMCCNTR_CFG 0x01A8
#define NPU_REG_PMU_EVCNTR(x) (0x0300 + (x) * 4)
#define NPU_REG_PMU_EVTYPER(x) (0x0380 + (x) * 4)
#define ID_ARCH_MAJOR_MASK GENMASK(31, 28)
#define ID_ARCH_MINOR_MASK GENMASK(27, 20)
#define ID_ARCH_PATCH_MASK GENMASK(19, 16)
@@ -67,6 +77,15 @@ struct gen_pool;
#define PROT_ACTIVE_CSL BIT(1)
#define PMCR_NUM_EVENT_CNT_MASK GENMASK(15, 11)
#define PMCR_CYCLE_CNT_RST BIT(2)
#define PMCR_EVENT_CNT_RST BIT(1)
#define PMCR_CNT_EN BIT(0)
#define PMU_EV_TYPE_NONE 0
#define PMU_EV_TYPE_CYCLES 0x11
#define PMU_EV_TYPE_IDLE 0x20
enum ethosu_cmds {
NPU_OP_CONV = 0x2,
NPU_OP_DEPTHWISE = 0x3,
@@ -152,6 +171,8 @@ enum ethosu_cmds {
#define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */
struct ethosu_perfmon;
/**
* struct ethosu_device - Ethosu device
*/
@@ -161,6 +182,7 @@ struct ethosu_device {
/** @iomem: CPU mapping of the registers. */
void __iomem *regs;
void __iomem *pmu_regs;
void __iomem *sram;
struct gen_pool *srampool;
@@ -173,8 +195,6 @@ struct ethosu_device {
struct drm_ethosu_npu_info npu_info;
struct ethosu_job *in_flight_job;
/* For in_flight_job and ethosu_job_hw_submit() */
struct mutex job_lock;
/* For dma_fence */
spinlock_t fence_lock;
@@ -184,6 +204,17 @@ struct ethosu_device {
struct mutex sched_lock;
u64 fence_context;
u64 emit_seqno;
/* Tracks the performance monitor state. */
struct {
/* Protects @active. */
struct mutex lock;
/* Perfmon currently programmed in HW (or NULL if none). */
struct ethosu_perfmon *active;
} perfmon_state;
struct ethosu_perfmon *global_perfmon;
};
#define to_ethosu_device(drm_dev) \
+21 -2
View File
@@ -15,6 +15,7 @@
#include <drm/drm_utils.h>
#include <drm/drm_gem.h>
#include <drm/drm_accel.h>
#include <drm/drm_managed.h>
#include <drm/ethosu_accel.h>
#include "ethosu_drv.h"
@@ -154,6 +155,7 @@ static int ethosu_open(struct drm_device *ddev, struct drm_file *file)
if (ret)
goto err_put_mod;
ethosu_perfmon_open_file(priv);
file->driver_priv = no_free_ptr(priv);
return 0;
@@ -165,6 +167,7 @@ err_put_mod:
static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file)
{
ethosu_job_close(file->driver_priv);
ethosu_perfmon_close_file(file->driver_priv);
kfree(file->driver_priv);
module_put(THIS_MODULE);
}
@@ -179,6 +182,10 @@ static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = {
ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0),
ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0),
ETHOSU_IOCTL(SUBMIT, submit, 0),
ETHOSU_IOCTL(PERFMON_CREATE, perfmon_create, 0),
ETHOSU_IOCTL(PERFMON_DESTROY, perfmon_destroy, 0),
ETHOSU_IOCTL(PERFMON_GET_VALUES, perfmon_get_values, 0),
ETHOSU_IOCTL(PERFMON_SET_GLOBAL, perfmon_set_global, 0),
};
DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops);
@@ -314,8 +321,14 @@ static int ethosu_init(struct ethosu_device *ethosudev)
ethosu_sram_init(ethosudev);
if (!ethosu_is_u65(ethosudev))
ethosudev->pmu_regs += 0x1000;
ethosudev->npu_info.pmu_counters = FIELD_GET(PMCR_NUM_EVENT_CNT_MASK,
readl_relaxed(ethosudev->pmu_regs + NPU_REG_PMCR));
dev_info(ethosudev->base.dev,
"Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n",
"Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM, %d PMU cntrs\n",
FIELD_GET(ID_ARCH_MAJOR_MASK, id),
FIELD_GET(ID_ARCH_MINOR_MASK, id),
FIELD_GET(ID_ARCH_PATCH_MASK, id),
@@ -323,7 +336,8 @@ static int ethosu_init(struct ethosu_device *ethosudev)
FIELD_GET(ID_VER_MINOR_MASK, id),
FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config),
1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config),
ethosudev->npu_info.sram_size / 1024);
ethosudev->npu_info.sram_size / 1024,
ethosudev->npu_info.pmu_counters);
return 0;
}
@@ -342,11 +356,16 @@ static int ethosu_probe(struct platform_device *pdev)
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
ethosudev->regs = devm_platform_ioremap_resource(pdev, 0);
ethosudev->pmu_regs = ethosudev->regs;
ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, &ethosudev->clks);
if (ethosudev->num_clks < 0)
return ethosudev->num_clks;
ret = drmm_mutex_init(&ethosudev->base, &ethosudev->perfmon_state.lock);
if (ret)
return ret;
ret = ethosu_job_init(ethosudev);
if (ret)
return ret;
+60 -1
View File
@@ -1,15 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
/* Copyright 2025 Arm, Ltd. */
/* Copyright 2025-2026 Arm, Ltd. */
#ifndef __ETHOSU_DRV_H__
#define __ETHOSU_DRV_H__
#include <linux/mutex.h>
#include <linux/xarray.h>
#include <drm/gpu_scheduler.h>
struct ethosu_device;
struct drm_device;
struct drm_file;
struct ethosu_file_priv {
struct ethosu_device *edev;
struct drm_sched_entity sched_entity;
struct xarray perfmons;
};
/* Performance monitor object. The perfmon lifetime is controlled by userspace
* using perfmon related ioctls. A perfmon can be attached to a DRM_ETHOSU_SUBMIT
* request, and when this is the case, HW perf counters will be activated just
* before the job is submitted to the NPU and disabled when the job is
* done. This way, only events related to a specific job will be counted.
*/
struct ethosu_perfmon {
/* Tracks the number of users of the perfmon, when this counter reaches
* zero the perfmon is destroyed.
*/
refcount_t refcnt;
/* Number of counters activated in this perfmon instance
* (should be less than or equal to DRM_ETHOSU_MAX_PERF_COUNTERS).
*/
u8 ncounters;
/* Events counted by the HW perf counters. */
u16 counters[DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS];
/*
* Storage for counter values. Counters are incremented by the HW
* perf counter values every time the perfmon is attached to an
* NPU job. This way, perfmon users don't have to retrieve the
* results after each job if they want to track events covering
* several submissions. Note that counter values can't be reset,
* but you can fake a reset by destroying the perfmon and
* creating a new one.
*/
u64 values[] __counted_by(ncounters);
};
/* ethosu_perfmon.c */
void ethosu_perfmon_get(struct ethosu_perfmon *perfmon);
void ethosu_perfmon_put(struct ethosu_perfmon *perfmon);
void ethosu_perfmon_start(struct ethosu_device *ethosu,
struct ethosu_perfmon *perfmon);
void ethosu_perfmon_stop(struct ethosu_device *ethosu,
struct ethosu_perfmon *perfmon, bool capture);
void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon,
bool capture);
struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv,
int id);
void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv);
void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv);
int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data,
struct drm_file *file_priv);
#endif
+39 -22
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
/* Copyright 2025 Arm, Ltd. */
/* Copyright 2025-2026 Arm, Ltd. */
#include <linux/bitfield.h>
#include <linux/genalloc.h>
@@ -147,6 +147,8 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job)
{
unsigned int i;
ethosu_perfmon_put(job->perfmon);
for (i = 0; i < job->region_cnt; i++)
drm_gem_object_put(job->region_bo[i]);
@@ -181,6 +183,26 @@ static void ethosu_job_free(struct drm_sched_job *sched_job)
ethosu_job_put(job);
}
static void
ethosu_switch_perfmon(struct ethosu_device *ethosu, struct ethosu_job *job)
{
struct ethosu_perfmon *perfmon;
guard(mutex)(&ethosu->perfmon_state.lock);
perfmon = ethosu->global_perfmon;
if (!perfmon)
perfmon = job->perfmon;
if (perfmon == ethosu->perfmon_state.active)
return;
ethosu_perfmon_stop_locked(ethosu, ethosu->perfmon_state.active, true);
if (perfmon)
ethosu_perfmon_start(ethosu, perfmon);
}
static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
{
struct ethosu_job *job = to_ethosu_job(sched_job);
@@ -194,10 +216,10 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
dev->fence_context, ++dev->emit_seqno);
dma_fence_get(fence);
scoped_guard(mutex, &dev->job_lock) {
dev->in_flight_job = job;
ethosu_job_hw_submit(dev, job);
}
ethosu_switch_perfmon(dev, job);
WRITE_ONCE(dev->in_flight_job, job);
ethosu_job_hw_submit(dev, job);
return fence;
}
@@ -205,6 +227,7 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
static void ethosu_job_handle_irq(struct ethosu_device *dev)
{
u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
struct ethosu_job *job;
if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) {
dev_err(dev->base.dev, "Error IRQ - %x\n", status);
@@ -212,11 +235,10 @@ static void ethosu_job_handle_irq(struct ethosu_device *dev)
return;
}
scoped_guard(mutex, &dev->job_lock) {
if (dev->in_flight_job) {
dma_fence_signal(dev->in_flight_job->done_fence);
dev->in_flight_job = NULL;
}
job = READ_ONCE(dev->in_flight_job);
if (job) {
WRITE_ONCE(dev->in_flight_job, NULL);
dma_fence_signal(job->done_fence);
}
}
@@ -272,8 +294,7 @@ static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad)
drm_sched_stop(&dev->sched, bad);
scoped_guard(mutex, &dev->job_lock)
dev->in_flight_job = NULL;
WRITE_ONCE(dev->in_flight_job, NULL);
/* Proceed with reset now. */
pm_runtime_force_suspend(dev->base.dev);
@@ -305,9 +326,6 @@ int ethosu_job_init(struct ethosu_device *edev)
int ret;
spin_lock_init(&edev->fence_lock);
ret = devm_mutex_init(dev, &edev->job_lock);
if (ret)
return ret;
ret = devm_mutex_init(dev, &edev->sched_lock);
if (ret)
return ret;
@@ -366,7 +384,8 @@ void ethosu_job_close(struct ethosu_file_priv *ethosu_priv)
}
static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
struct drm_ethosu_job *job)
struct drm_ethosu_job *job,
int perfmon_id)
{
struct ethosu_device *edev = to_ethosu_device(dev);
struct ethosu_file_priv *file_priv = file->driver_priv;
@@ -390,6 +409,9 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
ejob->dev = edev;
ejob->sram_size = job->sram_size;
if (perfmon_id)
ejob->perfmon = ethosu_perfmon_find(file_priv, perfmon_id);
ejob->done_fence = kzalloc_obj(*ejob->done_fence);
if (!ejob->done_fence) {
ret = -ENOMEM;
@@ -492,11 +514,6 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil
int ret = 0;
unsigned int i = 0;
if (args->pad) {
drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n");
return -EINVAL;
}
struct drm_ethosu_job __free(kvfree) *jobs =
kvmalloc_objs(*jobs, args->job_count);
if (!jobs)
@@ -510,7 +527,7 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil
}
for (i = 0; i < args->job_count; i++) {
ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]);
ret = ethosu_ioctl_submit_job(dev, file, &jobs[i], args->perfmon_id);
if (ret)
return ret;
}
+2
View File
@@ -21,6 +21,8 @@ struct ethosu_job {
u8 region_cnt;
u32 sram_size;
struct ethosu_perfmon *perfmon;
/* Fence to be signaled by drm-sched once its done with the job */
struct dma_fence *inference_done_fence;
+301
View File
@@ -0,0 +1,301 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/* Copyright 2026 Arm, Ltd. */
/* Based on v3d_perfmon.c, Copyright (C) 2021 Raspberry Pi */
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <uapi/drm/ethosu_accel.h>
#include "ethosu_drv.h"
#include "ethosu_device.h"
void ethosu_perfmon_get(struct ethosu_perfmon *perfmon)
{
if (perfmon)
refcount_inc(&perfmon->refcnt);
}
void ethosu_perfmon_put(struct ethosu_perfmon *perfmon)
{
if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
kfree(perfmon);
}
void ethosu_perfmon_start(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon)
{
unsigned int i;
u8 ncounters;
u32 mask;
lockdep_assert_held(&ethosu->perfmon_state.lock);
if (WARN_ON_ONCE(!perfmon || ethosu->perfmon_state.active))
return;
writel_relaxed(PMCR_CNT_EN, ethosu->pmu_regs + NPU_REG_PMCR);
writel_relaxed(PMU_EV_TYPE_CYCLES, ethosu->pmu_regs + NPU_REG_PMCCNTR_CFG);
mask = 0x80000000;
ncounters = perfmon->ncounters - 1;
if (ncounters)
mask |= GENMASK(ncounters - 1, 0);
for (i = 0; i < ncounters; i++)
writel_relaxed(perfmon->counters[i], ethosu->pmu_regs + NPU_REG_PMU_EVTYPER(i));
writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENSET);
writel_relaxed(PMCR_CNT_EN | PMCR_EVENT_CNT_RST | PMCR_CYCLE_CNT_RST,
ethosu->pmu_regs + NPU_REG_PMCR);
ethosu->perfmon_state.active = perfmon;
}
void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon,
bool capture)
{
unsigned int i;
u8 ncounters;
u32 mask;
lockdep_assert_held(&ethosu->perfmon_state.lock);
if (!perfmon || perfmon != ethosu->perfmon_state.active)
return;
ncounters = perfmon->ncounters - 1;
if (!pm_runtime_get_if_active(ethosu->base.dev)) {
ethosu->perfmon_state.active = NULL;
return;
}
if (capture) {
for (i = 0; i < ncounters; i++)
perfmon->values[i] += readl_relaxed(ethosu->pmu_regs + NPU_REG_PMU_EVCNTR(i));
perfmon->values[ncounters] +=
readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_LO) |
(u64)readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_HI) << 32;
}
mask = 0x80000000;
if (ncounters)
mask |= GENMASK(ncounters - 1, 0);
writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENCLR);
writel_relaxed(0, ethosu->pmu_regs + NPU_REG_PMCR);
ethosu->perfmon_state.active = NULL;
pm_runtime_put(ethosu->base.dev);
}
void ethosu_perfmon_stop(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon,
bool capture)
{
if (!perfmon)
return;
guard(mutex)(&ethosu->perfmon_state.lock);
ethosu_perfmon_stop_locked(ethosu, perfmon, capture);
}
struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, int id)
{
struct ethosu_perfmon *perfmon;
xa_lock(&ethosu_priv->perfmons);
perfmon = xa_load(&ethosu_priv->perfmons, id);
ethosu_perfmon_get(perfmon);
xa_unlock(&ethosu_priv->perfmons);
return perfmon;
}
void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv)
{
xa_init_flags(&ethosu_priv->perfmons, XA_FLAGS_ALLOC1);
}
static void ethosu_perfmon_delete(struct ethosu_file_priv *ethosu_priv,
struct ethosu_perfmon *perfmon)
{
struct ethosu_device *ethosu = ethosu_priv->edev;
/* If the active perfmon is being destroyed, stop it first */
scoped_guard(mutex, &ethosu->perfmon_state.lock) {
/* If the global perfmon is being destroyed, set it to NULL */
if (ethosu->global_perfmon == perfmon) {
ethosu->global_perfmon = NULL;
ethosu_perfmon_put(perfmon);
}
ethosu_perfmon_stop_locked(ethosu, perfmon, false);
}
ethosu_perfmon_put(perfmon);
}
void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv)
{
struct ethosu_perfmon *perfmon;
unsigned long id;
xa_for_each(&ethosu_priv->perfmons, id, perfmon)
ethosu_perfmon_delete(ethosu_priv, perfmon);
xa_destroy(&ethosu_priv->perfmons);
}
int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
struct drm_ethosu_perfmon_create *req = data;
struct ethosu_device *ethosu = to_ethosu_device(dev);
struct ethosu_perfmon *perfmon;
unsigned int i, event_max;
int ret;
u32 id;
/* Number of monitored counters cannot exceed HW limits. */
if (req->ncounters > ethosu->npu_info.pmu_counters)
return -EINVAL;
/* Make sure all counters are valid. */
event_max = ethosu_is_u65(ethosu) ? 433 : 671;
for (i = 0; i < req->ncounters; i++) {
if (req->counters[i] > event_max)
return -EINVAL;
}
/* Add 1 more counter for cycle counter */
req->ncounters++;
perfmon = kzalloc_flex(*perfmon, values, req->ncounters);
if (!perfmon)
return -ENOMEM;
for (i = 0; i < req->ncounters - 1; i++)
perfmon->counters[i] = req->counters[i];
perfmon->ncounters = req->ncounters;
refcount_set(&perfmon->refcnt, 1);
ret = xa_alloc(&ethosu_priv->perfmons, &id, perfmon, xa_limit_32b,
GFP_KERNEL);
if (ret < 0) {
kfree(perfmon);
return ret;
}
req->id = id;
return 0;
}
int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
struct drm_ethosu_perfmon_destroy *req = data;
struct ethosu_perfmon *perfmon;
perfmon = xa_erase(&ethosu_priv->perfmons, req->id);
if (!perfmon)
return -EINVAL;
ethosu_perfmon_delete(ethosu_priv, perfmon);
return 0;
}
int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct ethosu_device *ethosu = to_ethosu_device(dev);
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
struct drm_ethosu_perfmon_get_values *req = data;
struct ethosu_perfmon *perfmon;
int ret = 0;
if (req->pad != 0)
return -EINVAL;
perfmon = ethosu_perfmon_find(ethosu_priv, req->id);
if (!perfmon)
return -EINVAL;
ret = pm_runtime_resume_and_get(dev->dev);
if (ret) {
ethosu_perfmon_put(perfmon);
return ret;
}
ethosu_perfmon_stop(ethosu, perfmon, true);
pm_runtime_put_autosuspend(dev->dev);
if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values,
perfmon->ncounters * sizeof(u64)))
ret = -EFAULT;
ethosu_perfmon_put(perfmon);
return ret;
}
int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv;
struct drm_ethosu_perfmon_set_global *req = data;
struct ethosu_device *ethosu = to_ethosu_device(dev);
struct ethosu_perfmon *perfmon;
if (req->flags & ~DRM_ETHOSU_PERFMON_CLEAR_GLOBAL)
return -EINVAL;
perfmon = ethosu_perfmon_find(ethosu_priv, req->id);
if (!perfmon)
return -EINVAL;
/* If the request is to clear the global performance monitor */
if (req->flags & DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) {
struct ethosu_perfmon *old;
scoped_guard(mutex, &ethosu->perfmon_state.lock) {
old = ethosu->global_perfmon;
if (!old) {
ethosu_perfmon_put(perfmon);
return -EINVAL;
}
ethosu->global_perfmon = NULL;
ethosu_perfmon_stop_locked(ethosu, old, true);
}
ethosu_perfmon_put(old);
ethosu_perfmon_put(perfmon);
return 0;
}
scoped_guard(mutex, &ethosu->perfmon_state.lock) {
if (ethosu->perfmon_state.active || ethosu->global_perfmon) {
ethosu_perfmon_put(perfmon);
return -EBUSY;
}
ethosu->global_perfmon = perfmon;
}
return 0;
}
+24 -4
View File
@@ -307,6 +307,11 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file)
return -ENODEV;
limits = ivpu_user_limits_get(vdev);
if (IS_ERR(limits) && PTR_ERR(limits) == -EMFILE) {
/* Context limit may be held by jobs pending deferred cleanup */
flush_work(&vdev->job_destroy_work);
limits = ivpu_user_limits_get(vdev);
}
if (IS_ERR(limits)) {
ret = PTR_ERR(limits);
goto err_dev_exit;
@@ -510,9 +515,9 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev)
{
ivpu_hw_irq_disable(vdev);
disable_irq(vdev->irq);
flush_work(&vdev->irq_ipc_work);
flush_work(&vdev->irq_dct_work);
flush_work(&vdev->context_abort_work);
flush_work(&vdev->job_destroy_work);
ivpu_ipc_disable(vdev);
ivpu_mmu_disable(vdev);
}
@@ -584,6 +589,11 @@ static const struct drm_driver driver = {
.major = 1,
};
static void ivpu_destroy_workqueue(void *wq)
{
destroy_workqueue(wq);
}
static int ivpu_irq_init(struct ivpu_device *vdev)
{
struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
@@ -595,16 +605,26 @@ static int ivpu_irq_init(struct ivpu_device *vdev)
return ret;
}
INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn);
INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn);
INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn);
init_llist_head(&vdev->job_destroy_list);
INIT_WORK(&vdev->job_destroy_work, ivpu_job_destroy_work_fn);
vdev->job_destroy_wq = alloc_workqueue("ivpu_job_destroy", WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
if (!vdev->job_destroy_wq)
return -ENOMEM;
ret = devm_add_action_or_reset(vdev->drm.dev, ivpu_destroy_workqueue, vdev->job_destroy_wq);
if (ret)
return ret;
ivpu_irq_handlers_init(vdev);
vdev->irq = pci_irq_vector(pdev, 0);
ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
ivpu_ipc_irq_thread_handler, IRQF_NO_AUTOEN,
DRIVER_NAME, vdev);
if (ret)
ivpu_err(vdev, "Failed to request an IRQ %d\n", ret);
+4 -1
View File
@@ -13,6 +13,7 @@
#include <drm/drm_print.h>
#include <linux/hashtable.h>
#include <linux/llist.h>
#include <linux/pci.h>
#include <linux/xarray.h>
#include <uapi/drm/ivpu_accel.h>
@@ -157,9 +158,11 @@ struct ivpu_device {
struct xa_limit db_limit;
u32 db_next;
struct work_struct irq_ipc_work;
struct work_struct irq_dct_work;
struct work_struct context_abort_work;
struct llist_head job_destroy_list;
struct work_struct job_destroy_work;
struct workqueue_struct *job_destroy_wq;
struct mutex bo_list_lock; /* Protects bo_list */
struct list_head bo_list;
+1 -1
View File
@@ -318,7 +318,7 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
fw->shave_nn_size = PAGE_ALIGN(fw_hdr->shave_nn_fw_size);
fw->cold_boot_entry_point = fw_hdr->entry_point;
fw->trace_level = min_t(u32, ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
fw->trace_level = min(ivpu_fw_log_level, IVPU_FW_LOG_FATAL);
fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING;
fw->trace_hw_component_mask = -1;
+4
View File
@@ -399,6 +399,10 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr)
return IRQ_NONE;
pm_runtime_mark_last_busy(vdev->drm.dev);
if (ip_handled)
return IRQ_WAKE_THREAD;
return IRQ_HANDLED;
}
+18 -7
View File
@@ -146,7 +146,7 @@ ivpu_ipc_rx_msg_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
lockdep_assert_held(&ipc->cons_lock);
rx_msg = kzalloc_obj(*rx_msg, GFP_ATOMIC);
rx_msg = kmem_cache_zalloc(ipc->rx_msg_cache, GFP_ATOMIC);
if (!rx_msg) {
ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg);
return;
@@ -174,7 +174,7 @@ ivpu_ipc_rx_msg_del(struct ivpu_device *vdev, struct ivpu_ipc_rx_msg *rx_msg)
list_del(&rx_msg->link);
ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg);
atomic_dec(&vdev->ipc->rx_msg_count);
kfree(rx_msg);
kmem_cache_free(vdev->ipc->rx_msg_cache, rx_msg);
}
void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
@@ -462,13 +462,11 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev)
ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg);
}
}
queue_work(system_percpu_wq, &vdev->irq_ipc_work);
}
void ivpu_ipc_irq_work_fn(struct work_struct *work)
irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr)
{
struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_ipc_work);
struct ivpu_device *vdev = ptr;
struct ivpu_ipc_info *ipc = vdev->ipc;
struct ivpu_ipc_rx_msg *rx_msg, *r;
struct list_head cb_msg_list;
@@ -483,6 +481,8 @@ void ivpu_ipc_irq_work_fn(struct work_struct *work)
rx_msg->callback(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg);
ivpu_ipc_rx_msg_del(vdev, rx_msg);
}
return IRQ_HANDLED;
}
int ivpu_ipc_init(struct ivpu_device *vdev)
@@ -490,10 +490,18 @@ int ivpu_ipc_init(struct ivpu_device *vdev)
struct ivpu_ipc_info *ipc = vdev->ipc;
int ret;
ipc->rx_msg_cache = kmem_cache_create("ivpu_ipc_rx_msg", sizeof(struct ivpu_ipc_rx_msg), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!ipc->rx_msg_cache) {
ivpu_err(vdev, "Failed to create rx_msg_cache\n");
return -ENOMEM;
}
ipc->mem_tx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!ipc->mem_tx) {
ivpu_err(vdev, "Failed to allocate mem_tx\n");
return -ENOMEM;
ret = -ENOMEM;
goto err_destroy_cache;
}
ipc->mem_rx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
@@ -532,6 +540,8 @@ err_free_rx:
ivpu_bo_free(ipc->mem_rx);
err_free_tx:
ivpu_bo_free(ipc->mem_tx);
err_destroy_cache:
kmem_cache_destroy(ipc->rx_msg_cache);
return ret;
}
@@ -544,6 +554,7 @@ void ivpu_ipc_fini(struct ivpu_device *vdev)
drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0);
ivpu_ipc_mem_fini(vdev);
kmem_cache_destroy(ipc->rx_msg_cache);
}
void ivpu_ipc_enable(struct ivpu_device *vdev)
+2 -1
View File
@@ -70,6 +70,7 @@ struct ivpu_ipc_info {
struct gen_pool *mm_tx;
struct ivpu_bo *mem_tx;
struct ivpu_bo *mem_rx;
struct kmem_cache *rx_msg_cache;
atomic_t rx_msg_count;
@@ -90,7 +91,7 @@ void ivpu_ipc_disable(struct ivpu_device *vdev);
void ivpu_ipc_reset(struct ivpu_device *vdev);
void ivpu_ipc_irq_handler(struct ivpu_device *vdev);
void ivpu_ipc_irq_work_fn(struct work_struct *work);
irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr);
void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
u32 channel, ivpu_ipc_rx_callback_t callback);
+53 -6
View File
@@ -535,6 +535,20 @@ static void ivpu_job_destroy(struct ivpu_job *job)
kfree(job);
}
void ivpu_job_destroy_work_fn(struct work_struct *work)
{
struct ivpu_device *vdev = container_of(work, struct ivpu_device, job_destroy_work);
struct ivpu_job *job, *tmp;
struct llist_node *list;
list = llist_del_all(&vdev->job_destroy_list);
llist_for_each_entry_safe(job, tmp, list, destroy_node) {
ivpu_job_destroy(job);
ivpu_rpm_put(vdev);
}
}
static struct ivpu_job *
ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)
{
@@ -619,7 +633,7 @@ bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_
return false;
}
static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
static struct ivpu_job *ivpu_job_signal(struct ivpu_device *vdev, u32 job_id, u32 job_status)
{
struct ivpu_job *job;
@@ -627,7 +641,7 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
job = xa_load(&vdev->submitted_jobs_xa, job_id);
if (!job)
return -ENOENT;
return NULL;
ivpu_job_remove_from_submitted_jobs(vdev, job_id);
@@ -646,14 +660,37 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx,
job->job_status);
ivpu_job_destroy(job);
ivpu_stop_job_timeout_detection(vdev);
ivpu_rpm_put(vdev);
if (!xa_empty(&vdev->submitted_jobs_xa))
ivpu_start_job_timeout_detection(vdev);
return job;
}
static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
{
struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status);
if (!job)
return -ENOENT;
ivpu_job_destroy(job);
ivpu_rpm_put(vdev);
return 0;
}
static int ivpu_job_signal_and_defer_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
{
struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status);
if (!job)
return -ENOENT;
llist_add(&job->destroy_node, &vdev->job_destroy_list);
queue_work(vdev->job_destroy_wq, &vdev->job_destroy_work);
return 0;
}
@@ -689,6 +726,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
struct ivpu_file_priv *file_priv = job->file_priv;
struct ivpu_device *vdev = job->vdev;
struct ivpu_cmdq *cmdq;
bool flushed = false;
bool is_first_job;
int ret;
@@ -696,6 +734,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
if (ret < 0)
return ret;
retry:
mutex_lock(&vdev->submitted_jobs_lock);
mutex_lock(&file_priv->lock);
@@ -709,6 +748,14 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
}
ret = ivpu_cmdq_register(file_priv, cmdq);
if (ret == -EBUSY && !flushed) {
/* Doorbell may be held by jobs pending deferred cleanup */
mutex_unlock(&file_priv->lock);
mutex_unlock(&vdev->submitted_jobs_lock);
flush_work(&vdev->job_destroy_work);
flushed = true;
goto retry;
}
if (ret) {
ivpu_err(vdev, "Failed to register command queue: %d\n", ret);
goto err_unlock;
@@ -1101,7 +1148,7 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
mutex_lock(&vdev->submitted_jobs_lock);
if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status))
/* No engine error, complete the job normally */
ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
ivpu_job_signal_and_defer_destroy(vdev, payload->job_id, payload->job_status);
mutex_unlock(&vdev->submitted_jobs_lock);
}
+6 -1
View File
@@ -6,8 +6,10 @@
#ifndef __IVPU_JOB_H__
#define __IVPU_JOB_H__
#include <linux/kref.h>
#include <linux/idr.h>
#include <linux/kref.h>
#include <linux/llist.h>
#include <linux/workqueue.h>
#include "ivpu_gem.h"
@@ -47,6 +49,7 @@ struct ivpu_cmdq {
* @vdev: Pointer to the VPU device
* @file_priv: The client context that submitted this job
* @done_fence: Fence signaled when job completes
* @destroy_node: List node for deferred resource cleanup after job completion
* @cmd_buf_vpu_addr: VPU address of the command buffer for this job
* @cmdq_id: Command queue ID used for submission
* @job_id: Unique job ID for tracking and status reporting
@@ -61,6 +64,7 @@ struct ivpu_job {
struct ivpu_device *vdev;
struct ivpu_file_priv *file_priv;
struct dma_fence *done_fence;
struct llist_node destroy_node;
u64 cmd_buf_vpu_addr;
u32 cmdq_id;
u32 job_id;
@@ -87,6 +91,7 @@ void ivpu_job_done_consumer_init(struct ivpu_device *vdev);
void ivpu_job_done_consumer_fini(struct ivpu_device *vdev);
bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status);
void ivpu_context_abort_work_fn(struct work_struct *work);
void ivpu_job_destroy_work_fn(struct work_struct *work);
void ivpu_jobs_abort_all(struct ivpu_device *vdev);
+2 -2
View File
@@ -588,8 +588,8 @@ void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ct
start = vdev->hw->ranges.runtime.start;
end = vdev->hw->ranges.shave.end;
} else {
start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
end = max_t(u64, vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
start = min(vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
end = max(vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end);
}
drm_mm_init(&ctx->mm, start, end - start);
+23 -9
View File
@@ -8,6 +8,7 @@
#include <drm/drm_gem.h>
#include <drm/rocket_accel.h>
#include <linux/interrupt.h>
#include <linux/overflow.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -102,6 +103,7 @@ rocket_copy_tasks(struct drm_device *dev,
fail:
kvfree(rjob->tasks);
rjob->tasks = NULL;
return ret;
}
@@ -188,14 +190,19 @@ static int rocket_job_push(struct rocket_job *job)
struct rocket_device *rdev = job->rdev;
struct drm_gem_object **bos;
struct ww_acquire_ctx acquire_ctx;
u32 bo_count;
int ret = 0;
bos = kvmalloc_array(job->in_bo_count + job->out_bo_count, sizeof(void *),
GFP_KERNEL);
if (check_add_overflow(job->in_bo_count, job->out_bo_count, &bo_count))
return -EINVAL;
bos = kvmalloc_array(bo_count, sizeof(*bos), GFP_KERNEL);
if (!bos)
return -ENOMEM;
memcpy(bos, job->in_bos, job->in_bo_count * sizeof(void *));
memcpy(&bos[job->in_bo_count], job->out_bos, job->out_bo_count * sizeof(void *));
ret = drm_gem_lock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
ret = drm_gem_lock_reservations(bos, bo_count, &acquire_ctx);
if (ret)
goto err;
@@ -220,7 +227,7 @@ static int rocket_job_push(struct rocket_job *job)
rocket_attach_object_fences(job->out_bos, job->out_bo_count, job->inference_done_fence);
err_unlock:
drm_gem_unlock_reservations(bos, job->in_bo_count + job->out_bo_count, &acquire_ctx);
drm_gem_unlock_reservations(bos, bo_count, &acquire_ctx);
err:
kvfree(bos);
@@ -310,13 +317,13 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
dma_fence_put(job->done_fence);
job->done_fence = dma_fence_get(fence);
ret = pm_runtime_get_sync(core->dev);
ret = pm_runtime_resume_and_get(core->dev);
if (ret < 0)
return fence;
goto err_put_fences;
ret = iommu_attach_group(job->domain->domain, core->iommu_group);
if (ret < 0)
return fence;
goto err_put_pm;
scoped_guard(mutex, &core->job_lock) {
core->in_flight_job = job;
@@ -324,6 +331,14 @@ static struct dma_fence *rocket_job_run(struct drm_sched_job *sched_job)
}
return fence;
err_put_pm:
pm_runtime_put(core->dev);
err_put_fences:
dma_fence_put(job->done_fence);
job->done_fence = NULL;
dma_fence_put(fence);
return ERR_PTR(ret);
}
static void rocket_job_handle_irq(struct rocket_core *core)
@@ -549,6 +564,7 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
kref_init(&rjob->refcount);
rjob->rdev = rdev;
rjob->domain = rocket_iommu_domain_get(file_priv);
ret = drm_sched_job_init(&rjob->base,
&file_priv->sched_entity,
@@ -574,8 +590,6 @@ static int rocket_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
rjob->out_bo_count = job->out_bo_handle_count;
rjob->domain = rocket_iommu_domain_get(file_priv);
ret = rocket_job_push(rjob);
if (ret)
goto out_cleanup_job;
+4 -4
View File
@@ -93,9 +93,9 @@ static int fence_cmp(const void *_a, const void *_b)
*
* Return: Number of unique fences remaining in the array.
*/
int dma_fence_dedup_array(struct dma_fence **fences, int num_fences)
size_t dma_fence_dedup_array(struct dma_fence **fences, size_t num_fences)
{
int i, j;
size_t i, j;
if (!num_fences)
return 0;
@@ -118,14 +118,14 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences)
EXPORT_SYMBOL_GPL(dma_fence_dedup_array);
/* Implementation for the dma_fence_merge() marco, don't use directly */
struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences,
struct dma_fence **fences,
struct dma_fence_unwrap *iter)
{
struct dma_fence *tmp, *unsignaled = NULL, **array;
struct dma_fence_array *result;
ktime_t timestamp;
int i, count;
size_t i, count;
count = 0;
timestamp = ns_to_ktime(0);
+12 -10
View File
@@ -534,7 +534,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout)
__dma_fence_might_wait();
dma_fence_enable_sw_signaling(fence);
dma_fence_enable_signaling(fence);
rcu_read_lock();
ops = rcu_dereference(fence->ops);
@@ -656,14 +656,14 @@ static bool __dma_fence_enable_signaling(struct dma_fence *fence)
}
/**
* dma_fence_enable_sw_signaling - enable signaling on fence
* dma_fence_enable_signaling - enable signaling on fence
* @fence: the fence to enable
*
* This will request for sw signaling to be enabled, to make the fence
* complete as soon as possible. This calls &dma_fence_ops.enable_signaling
* internally.
*/
void dma_fence_enable_sw_signaling(struct dma_fence *fence)
void dma_fence_enable_signaling(struct dma_fence *fence)
{
unsigned long flags;
@@ -671,7 +671,7 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence)
__dma_fence_enable_signaling(fence);
dma_fence_unlock_irqrestore(fence, flags);
}
EXPORT_SYMBOL(dma_fence_enable_sw_signaling);
EXPORT_SYMBOL(dma_fence_enable_signaling);
/**
* dma_fence_add_callback - add a callback to be called when the fence
@@ -1102,9 +1102,12 @@ __dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops,
* context and seqno are used for easy comparison between fences, allowing
* to check which fence is later by simply using dma_fence_later().
*
* It is strongly discouraged to provide an external lock because this couples
* lock and fence life time. This is only allowed for legacy use cases when
* multiple fences need to be prevented from signaling out of order.
* External locks are a relic of legacy use cases that needed a shared lock
* to serialize signaling when no out-of-order signaling was possible through
* &dma_fence_ops.signaled. Drivers have abandoned this concept since the
* introduction of the callback, but the external lock is still around. New
* users MUST NOT use external locks, as they force the issuer to outlive all
* fences that reference the lock.
*/
void
dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops,
@@ -1129,9 +1132,8 @@ EXPORT_SYMBOL(dma_fence_init);
* Context and seqno are used for easy comparison between fences, allowing
* to check which fence is later by simply using dma_fence_later().
*
* It is strongly discouraged to provide an external lock because this couples
* lock and fence life time. This is only allowed for legacy use cases when
* multiple fences need to be prevented from signaling out of order.
* New users MUST NOT use external locks. Check the documentation in
* dma_fence_init() to understand the motives behind the legacy use cases.
*/
void
dma_fence_init64(struct dma_fence *fence, const struct dma_fence_ops *ops,
+2 -1
View File
@@ -13,6 +13,7 @@
#include <linux/err.h>
#include <linux/export.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/nospec.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
@@ -154,7 +155,7 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
in_size = 0;
if ((ucmd & kcmd & IOC_OUT) == 0)
out_size = 0;
ksize = max(max(in_size, out_size), drv_size);
ksize = max3(in_size, out_size, drv_size);
/* If necessary, allocate buffer for ioctl argument */
if (ksize > sizeof(stack_kdata)) {
+2 -2
View File
@@ -82,7 +82,7 @@ static void test_sanitycheck(struct kunit *test)
chain = mock_chain(NULL, f, 1);
if (chain)
dma_fence_enable_sw_signaling(chain);
dma_fence_enable_signaling(chain);
else
KUNIT_FAIL(test, "Failed to create chain");
@@ -139,7 +139,7 @@ static int fence_chains_init(struct fence_chains *fc, unsigned int count,
fc->tail = fc->chains[i];
dma_fence_enable_sw_signaling(fc->chains[i]);
dma_fence_enable_signaling(fc->chains[i]);
}
fc->chain_length = i;
+21 -21
View File
@@ -103,7 +103,7 @@ static void test_sanitycheck(struct kunit *test)
f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
array = mock_array(1, f);
KUNIT_ASSERT_NOT_NULL(test, array);
@@ -122,7 +122,7 @@ static void test_unwrap_array(struct kunit *test)
f1 = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = mock_fence();
if (!f2) {
@@ -131,7 +131,7 @@ static void test_unwrap_array(struct kunit *test)
return;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
array = mock_array(2, f1, f2);
KUNIT_ASSERT_NOT_NULL(test, array);
@@ -160,7 +160,7 @@ static void test_unwrap_chain(struct kunit *test)
f1 = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = mock_fence();
if (!f2) {
@@ -169,7 +169,7 @@ static void test_unwrap_chain(struct kunit *test)
return;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
chain = mock_chain(f1, f2);
KUNIT_ASSERT_NOT_NULL(test, chain);
@@ -198,7 +198,7 @@ static void test_unwrap_chain_array(struct kunit *test)
f1 = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = mock_fence();
if (!f2) {
@@ -207,7 +207,7 @@ static void test_unwrap_chain_array(struct kunit *test)
return;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
array = mock_array(2, f1, f2);
KUNIT_ASSERT_NOT_NULL(test, array);
@@ -239,7 +239,7 @@ static void test_unwrap_merge(struct kunit *test)
f1 = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = mock_fence();
if (!f2) {
@@ -247,7 +247,7 @@ static void test_unwrap_merge(struct kunit *test)
goto error_put_f1;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
f3 = dma_fence_unwrap_merge(f1, f2);
if (!f3) {
@@ -285,7 +285,7 @@ static void test_unwrap_merge_duplicate(struct kunit *test)
f1 = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = dma_fence_unwrap_merge(f1, f1);
if (!f2) {
@@ -322,7 +322,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
f1 = __mock_fence(ctx[1], 1);
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = __mock_fence(ctx[1], 2);
if (!f2) {
@@ -330,7 +330,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
goto error_put_f1;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
f3 = __mock_fence(ctx[0], 1);
if (!f3) {
@@ -338,7 +338,7 @@ static void test_unwrap_merge_seqno(struct kunit *test)
goto error_put_f2;
}
dma_fence_enable_sw_signaling(f3);
dma_fence_enable_signaling(f3);
f4 = dma_fence_unwrap_merge(f1, f2, f3);
if (!f4) {
@@ -378,7 +378,7 @@ static void test_unwrap_merge_order(struct kunit *test)
f1 = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = mock_fence();
if (!f2) {
@@ -387,7 +387,7 @@ static void test_unwrap_merge_order(struct kunit *test)
return;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
a1 = mock_array(2, f1, f2);
KUNIT_ASSERT_NOT_NULL(test, a1);
@@ -442,7 +442,7 @@ static void test_unwrap_merge_complex(struct kunit *test)
f1 = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = mock_fence();
if (!f2) {
@@ -450,7 +450,7 @@ static void test_unwrap_merge_complex(struct kunit *test)
goto error_put_f1;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
f3 = dma_fence_unwrap_merge(f1, f2);
if (!f3) {
@@ -510,7 +510,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
f1 = __mock_fence(ctx[0], 2);
KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
f2 = __mock_fence(ctx[1], 1);
if (!f2) {
@@ -518,7 +518,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
goto error_put_f1;
}
dma_fence_enable_sw_signaling(f2);
dma_fence_enable_signaling(f2);
f3 = __mock_fence(ctx[0], 1);
if (!f3) {
@@ -526,7 +526,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
goto error_put_f2;
}
dma_fence_enable_sw_signaling(f3);
dma_fence_enable_signaling(f3);
f4 = __mock_fence(ctx[1], 2);
if (!f4) {
@@ -534,7 +534,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test)
goto error_put_f3;
}
dma_fence_enable_sw_signaling(f4);
dma_fence_enable_signaling(f4);
f5 = mock_array(2, dma_fence_get(f1), dma_fence_get(f2));
if (!f5) {
+8 -8
View File
@@ -42,7 +42,7 @@ static void test_sanitycheck(struct kunit *test)
f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_fence_signal(f);
dma_fence_put(f);
@@ -55,7 +55,7 @@ static void test_signaling(struct kunit *test)
f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
if (dma_fence_is_signaled(f)) {
KUNIT_FAIL(test, "Fence unexpectedly signaled on creation");
@@ -127,7 +127,7 @@ static void test_late_add_callback(struct kunit *test)
f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_fence_signal(f);
@@ -209,7 +209,7 @@ static void test_status(struct kunit *test)
f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
if (dma_fence_get_status(f)) {
KUNIT_FAIL(test, "Fence unexpectedly has signaled status on creation");
@@ -233,7 +233,7 @@ static void test_error(struct kunit *test)
f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_fence_set_error(f, -EIO);
@@ -260,7 +260,7 @@ static void test_wait(struct kunit *test)
f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
if (dma_fence_wait_timeout(f, false, 0) != 0) {
KUNIT_FAIL(test, "Wait reported complete before being signaled");
@@ -300,7 +300,7 @@ static void test_wait_timeout(struct kunit *test)
wt.f = mock_fence();
KUNIT_ASSERT_NOT_NULL(test, wt.f);
dma_fence_enable_sw_signaling(wt.f);
dma_fence_enable_signaling(wt.f);
if (dma_fence_wait_timeout(wt.f, false, 1) != 0) {
KUNIT_FAIL(test, "Wait reported complete before being signaled");
@@ -379,7 +379,7 @@ static int thread_signal_callback(void *arg)
break;
}
dma_fence_enable_sw_signaling(f1);
dma_fence_enable_signaling(f1);
rcu_assign_pointer(t->fences[t->id], f1);
smp_wmb();
+5 -5
View File
@@ -48,7 +48,7 @@ static void test_sanitycheck(struct kunit *test)
f = alloc_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_fence_signal(f);
dma_fence_put(f);
@@ -73,7 +73,7 @@ static void test_signaling(struct kunit *test)
f = alloc_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
@@ -117,7 +117,7 @@ static void test_for_each(struct kunit *test)
f = alloc_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
@@ -176,7 +176,7 @@ static void test_for_each_unlocked(struct kunit *test)
f = alloc_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
@@ -246,7 +246,7 @@ static void test_get_fences(struct kunit *test)
f = alloc_fence();
KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_enable_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
+2 -2
View File
@@ -20,9 +20,9 @@ static int list_limit = 1024;
module_param(list_limit, int, 0644);
MODULE_PARM_DESC(list_limit, "udmabuf_create_list->count limit. Default is 1024.");
static int size_limit_mb = 64;
static int size_limit_mb = INT_MAX;
module_param(size_limit_mb, int, 0644);
MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is 64.");
MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is INT_MAX.");
struct udmabuf {
pgoff_t pagecount;
+8 -20
View File
@@ -208,32 +208,20 @@ config SYSFB
select SCREEN_INFO
config SYSFB_SIMPLEFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
bool "Mark VGA/VBE/EFI FB as generic system framebuffer (deprecated)"
depends on X86 || EFI
select SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
Firmware often provides initial graphics framebuffers so the BIOS,
bootloader or kernel can show basic video-output during boot for
user-guidance and debugging. Historically, x86 used the VESA BIOS
Extensions and EFI-framebuffers for this, which are mostly limited
to x86 BIOS or EFI systems.
This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
framebuffers so the new generic system-framebuffer drivers can be
used instead. If the framebuffer is not compatible with the generic
modes, it is advertised as fallback platform framebuffer so legacy
drivers like efifb, vesafb and uvesafb can pick it up.
If this option is not selected, all system framebuffers are always
marked as fallback platform framebuffers as usual.
user-guidance and debugging.
Note: Legacy fbdev drivers, including vesafb, efifb, uvesafb, will
not be able to pick up generic system framebuffers if this option
is selected. You are highly encouraged to enable simplefb as
replacement if you select this option. simplefb can correctly deal
with generic system framebuffers. But you should still keep vesafb
and others enabled as fallback if a system framebuffer is
incompatible with simplefb.
This option, if enabled, marks VBE/EFI framebuffers as system
framebuffers so the generic simpledrm driver can be used.
If unsure, say Y.
This option is deprecated and will be removed in the near future. If
unsure, say N and select efidrm, vesadrm instead. The dedicated DRM
drivers provide the same functionality plus additional features.
config TH1520_AON_PROTOCOL
tristate "Always-On firmware protocol"
+2 -1
View File
@@ -7,7 +7,8 @@ config GPU_BUDDY
config GPU_BUDDY_KUNIT_TEST
tristate "KUnit tests for GPU buddy allocator" if !KUNIT_ALL_TESTS
depends on GPU_BUDDY && KUNIT
depends on KUNIT
select GPU_BUDDY
default KUNIT_ALL_TESTS
help
KUnit tests for the GPU buddy allocator.
+57 -1
View File
@@ -7,4 +7,60 @@ obj-$(CONFIG_GPU_BUDDY) += buddy.o
obj-y += host1x/ drm/ vga/ tests/
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
obj-$(CONFIG_NOVA_CORE) += nova-core/
# nova-core and nova-drm are built from this Makefile so nova-drm's dependency
# on nova-core can be expressed as a plain Make prerequisite rather than a
# recursive sub-make. This is a temporary workaround until the Rust build
# system supports cross-crate dependencies natively.
obj-$(CONFIG_NOVA_CORE) += nova-core.o
nova-core-y := nova-core/nova_core.o nova-core/nova_core_exports.o
obj-$(CONFIG_DRM_NOVA) += nova-drm.o
nova-drm-y := drm/nova/nova.o
# Export Rust symbols from nova-core only if nova-drm actually references them.
nova-core-export-deps := $(if $(CONFIG_DRM_NOVA),$(obj)/drm/nova/nova.o)
rust_needed_exports = \
{ $(if $(strip $(2)),$(NM) -u $(2);,) echo "__DEFINED_RUST_SYMBOLS__"; \
$(NM) -p --defined-only $(1); } | \
awk -v fmt='$(3)' ' \
/^__DEFINED_RUST_SYMBOLS__$$/ { defs = 1; next } \
!defs { if ($$NF ~ /^_R/) needed[$$NF] = 1; next } \
defs && $$2 ~ /(T|R|D|B)/ && $$3 ~ /^_R/ && \
$$3 !~ /_(init|cleanup)_module$$/ && \
$$3 !~ /__(pfx|cfi|odr_asan)/ && \
$$3 in needed { printf fmt, $$3 } \
'
quiet_cmd_exports = EXPORTS $@
cmd_exports = \
$(call rust_needed_exports,$<,$(nova-core-export-deps),EXPORT_SYMBOL_RUST_GPL(%s);\n) > $@
$(obj)/nova-core/exports_nova_core_generated.h: $(obj)/nova-core/nova_core.o $(nova-core-export-deps) FORCE
$(call if_changed,exports)
targets += nova-core/exports_nova_core_generated.h
$(obj)/nova-core/nova_core_exports.o: $(obj)/nova-core/exports_nova_core_generated.h
CFLAGS_nova-core/nova_core_exports.o := -I $(objtree)/$(obj)/nova-core
ifdef CONFIG_MODVERSIONS
# The C export shim declares Rust symbols as `extern int`, so reuse its export
# list but generate symbol CRCs from the Rust object instead of the shim's DWARF.
$(obj)/nova-core/nova_core_exports.o: private cmd_gensymtypes_c = \
$(call getexportsymbols,\1) | \
$(objtree)/scripts/gendwarfksyms/gendwarfksyms \
$(if $(KBUILD_GENDWARFKSYMS_STABLE), --stable) \
$(if $(KBUILD_SYMTYPES), --symtypes $(@:.o=.symtypes),) \
$(obj)/nova-core/nova_core.o
endif
# Output nova-core's crate metadata for use by nova-drm at compile time.
RUSTFLAGS_nova-core/nova_core.o += \
--emit=metadata=$(objtree)/$(obj)/nova-core/libnova_core.rmeta
# Allow nova-drm to import nova-core's types.
$(obj)/drm/nova/nova.o: $(obj)/nova-core/nova_core.o
RUSTFLAGS_drm/nova/nova.o := -L $(objtree)/$(obj)/nova-core --extern nova_core
+118 -40
View File
@@ -193,6 +193,9 @@ static void mark_allocated(struct gpu_buddy *mm,
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_ALLOCATED;
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
mm->used_scoreboard[gpu_buddy_block_order(block)]++;
rbtree_remove(mm, block);
}
@@ -201,9 +204,14 @@ static void mark_free(struct gpu_buddy *mm,
{
enum gpu_buddy_free_tree tree;
if (gpu_buddy_block_is_allocated(block))
mm->used_scoreboard[gpu_buddy_block_order(block)]--;
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_FREE;
mm->free_scoreboard[gpu_buddy_block_order(block)]++;
tree = get_block_tree(block);
rbtree_insert(mm, block, tree);
}
@@ -214,6 +222,8 @@ static void mark_split(struct gpu_buddy *mm,
block->header &= ~GPU_BUDDY_HEADER_STATE;
block->header |= GPU_BUDDY_SPLIT;
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
rbtree_remove(mm, block);
}
@@ -271,9 +281,13 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm,
}
rbtree_remove(mm, buddy);
mm->free_scoreboard[gpu_buddy_block_order(buddy)]--;
if (force_merge && gpu_buddy_block_is_clear(buddy))
mm->clear_avail -= gpu_buddy_block_size(mm, buddy);
if (gpu_buddy_block_is_allocated(block))
mm->used_scoreboard[gpu_buddy_block_order(block)]--;
gpu_block_free(mm, block);
gpu_block_free(mm, buddy);
@@ -335,6 +349,7 @@ static int __force_merge(struct gpu_buddy *mm,
iter = rb_prev(iter);
rbtree_remove(mm, block);
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
if (gpu_buddy_block_is_clear(block))
mm->clear_avail -= gpu_buddy_block_size(mm, block);
@@ -384,11 +399,23 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER);
mm->free_scoreboard = kcalloc(mm->max_order + 1,
sizeof(*mm->free_scoreboard),
GFP_KERNEL);
if (!mm->free_scoreboard)
return -ENOMEM;
mm->used_scoreboard = kcalloc(mm->max_order + 1,
sizeof(*mm->used_scoreboard),
GFP_KERNEL);
if (!mm->used_scoreboard)
goto out_free_free_scoreboard;
mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES,
sizeof(*mm->free_trees),
GFP_KERNEL);
if (!mm->free_trees)
return -ENOMEM;
goto out_free_used_scoreboard;
for_each_free_tree(i) {
mm->free_trees[i] = kmalloc_array(mm->max_order + 1,
@@ -450,6 +477,10 @@ out_free_tree:
while (i--)
kfree(mm->free_trees[i]);
kfree(mm->free_trees);
out_free_used_scoreboard:
kfree(mm->used_scoreboard);
out_free_free_scoreboard:
kfree(mm->free_scoreboard);
return -ENOMEM;
}
EXPORT_SYMBOL(gpu_buddy_init);
@@ -484,10 +515,15 @@ void gpu_buddy_fini(struct gpu_buddy *mm)
gpu_buddy_assert(mm->avail == mm->size);
for (i = 0; i <= mm->max_order; ++i)
gpu_buddy_assert(!mm->used_scoreboard[i]);
for_each_free_tree(i)
kfree(mm->free_trees[i]);
kfree(mm->free_trees);
kfree(mm->roots);
kfree(mm->free_scoreboard);
kfree(mm->used_scoreboard);
}
EXPORT_SYMBOL(gpu_buddy_fini);
@@ -594,6 +630,59 @@ void gpu_buddy_free_block(struct gpu_buddy *mm,
}
EXPORT_SYMBOL(gpu_buddy_free_block);
/**
* gpu_buddy_allocated_addr_to_block - given relative address find the allocated block
*
* @mm: GPU buddy manager
* @addr: Relative address
*
* Returns:
* gpu_buddy_block on success, NULL or error code on failure
*/
struct gpu_buddy_block *gpu_buddy_allocated_addr_to_block(struct gpu_buddy *mm, u64 addr)
{
struct gpu_buddy_block *block;
LIST_HEAD(dfs);
u64 end;
int i;
gpu_buddy_driver_lock_held(mm);
end = addr + mm->chunk_size - 1;
for (i = 0; i < mm->n_roots; ++i)
list_add_tail(&mm->roots[i]->tmp_link, &dfs);
do {
u64 block_start;
u64 block_end;
block = list_first_entry_or_null(&dfs,
struct gpu_buddy_block,
tmp_link);
if (!block)
break;
list_del(&block->tmp_link);
block_start = gpu_buddy_block_offset(block);
block_end = block_start + gpu_buddy_block_size(mm, block) - 1;
if (!overlaps(addr, end, block_start, block_end))
continue;
if (gpu_buddy_block_is_allocated(block))
return block;
else if (gpu_buddy_block_is_free(block))
return NULL;
list_add(&block->right->tmp_link, &dfs);
list_add(&block->left->tmp_link, &dfs);
} while (1);
return ERR_PTR(-ENXIO);
}
EXPORT_SYMBOL(gpu_buddy_allocated_addr_to_block);
static void __gpu_buddy_free_list(struct gpu_buddy *mm,
struct list_head *objects,
bool mark_clear,
@@ -650,6 +739,20 @@ static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags
return needs_clear != gpu_buddy_block_is_clear(block);
}
static void __gpu_buddy_undo_splits(struct gpu_buddy *mm,
struct gpu_buddy_block *block)
{
struct gpu_buddy_block *buddy = __get_buddy(block);
if (buddy &&
(gpu_buddy_block_is_free(block) &&
gpu_buddy_block_is_free(buddy))) {
rbtree_remove(mm, block);
mm->free_scoreboard[gpu_buddy_block_order(block)]--;
__gpu_buddy_free(mm, block, false);
}
}
static struct gpu_buddy_block *
__alloc_range_bias(struct gpu_buddy *mm,
u64 start, u64 end,
@@ -659,7 +762,6 @@ __alloc_range_bias(struct gpu_buddy *mm,
{
u64 req_size = mm->chunk_size << order;
struct gpu_buddy_block *block;
struct gpu_buddy_block *buddy;
LIST_HEAD(dfs);
int err;
int i;
@@ -734,11 +836,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
buddy = __get_buddy(block);
if (buddy &&
(gpu_buddy_block_is_free(block) &&
gpu_buddy_block_is_free(buddy)))
__gpu_buddy_free(mm, block, false);
__gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -847,8 +945,7 @@ alloc_from_freetree(struct gpu_buddy *mm,
return block;
err_undo:
if (tmp != order)
__gpu_buddy_free(mm, block, false);
__gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -912,7 +1009,6 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm,
{
struct gpu_buddy_block *block = NULL;
unsigned int order, tmp, alignment;
struct gpu_buddy_block *buddy;
enum gpu_buddy_free_tree tree;
unsigned long pages;
int err;
@@ -965,11 +1061,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
buddy = __get_buddy(block);
if (buddy &&
(gpu_buddy_block_is_free(block) &&
gpu_buddy_block_is_free(buddy)))
__gpu_buddy_free(mm, block, false);
__gpu_buddy_undo_splits(mm, block);
return ERR_PTR(err);
}
@@ -980,7 +1072,6 @@ static int __alloc_range(struct gpu_buddy *mm,
u64 *total_allocated_on_err)
{
struct gpu_buddy_block *block;
struct gpu_buddy_block *buddy;
u64 total_allocated = 0;
LIST_HEAD(allocated);
u64 end;
@@ -1051,11 +1142,7 @@ err_undo:
* bigger is better, so make sure we merge everything back before we
* free the allocated blocks.
*/
buddy = __get_buddy(block);
if (buddy &&
(gpu_buddy_block_is_free(block) &&
gpu_buddy_block_is_free(buddy)))
__gpu_buddy_free(mm, block, false);
__gpu_buddy_undo_splits(mm, block);
err_free:
if (err == -ENOSPC && total_allocated_on_err) {
@@ -1515,27 +1602,18 @@ void gpu_buddy_print(struct gpu_buddy *mm)
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
for (order = mm->max_order; order >= 0; order--) {
struct gpu_buddy_block *block, *tmp;
struct rb_root *root;
u64 count = 0, free;
unsigned int tree;
u64 free_count = mm->free_scoreboard[order];
u64 used_count = mm->used_scoreboard[order];
u64 block_size = mm->chunk_size << order;
u64 free = free_count * block_size;
u64 used = used_count * block_size;
for_each_free_tree(tree) {
root = &mm->free_trees[tree][order];
rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
BUG_ON(!gpu_buddy_block_is_free(block));
count++;
}
}
free = count * (mm->chunk_size << order);
if (free < SZ_1M)
pr_info("order-%2d free: %8llu KiB, blocks: %llu\n",
order, free >> 10, count);
if (block_size < SZ_1M)
pr_info("order-%2d free: %8llu KiB, used: %8llu KiB, free_blocks: %llu, used_blocks: %llu\n",
order, free >> 10, used >> 10, free_count, used_count);
else
pr_info("order-%2d free: %8llu MiB, blocks: %llu\n",
order, free >> 20, count);
pr_info("order-%2d free: %8llu MiB, used: %8llu MiB, free_blocks: %llu, used_blocks: %llu\n",
order, free >> 20, used >> 20, free_count, used_count);
}
}
EXPORT_SYMBOL(gpu_buddy_print);
+1 -1
View File
@@ -186,7 +186,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VGEM) += vgem/
obj-$(CONFIG_DRM_VKMS) += vkms/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_NOVA) += nova/
# nova-drm is built from drivers/gpu/Makefile together with nova-core.
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
+4
View File
@@ -5,6 +5,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_mipi_dsi.h>
@@ -222,6 +223,9 @@ static int adp_dsi_bridge_attach(struct drm_bridge *bridge,
}
static const struct drm_bridge_funcs adp_dsi_bridge_funcs = {
.atomic_create_state = drm_atomic_helper_bridge_create_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.attach = adp_dsi_bridge_attach,
};
+3 -2
View File
@@ -70,8 +70,9 @@ amdgpu-y += amdgpu_device.o amdgpu_reg_access.o amdgpu_doorbell_mgr.o amdgpu_kms
amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o amdgpu_rap.o \
amdgpu_fw_attestation.o amdgpu_securedisplay.o \
amdgpu_eeprom.o amdgpu_mca.o amdgpu_psp_ta.o amdgpu_lsdma.o amdgpu_lockdep.o \
amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_aca.o amdgpu_dev_coredump.o \
amdgpu_cper.o amdgpu_userq_fence.o amdgpu_eviction_fence.o amdgpu_ip.o
amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_dev_coredump.o \
amdgpu_cper.o amdgpu_userq_fence.o amdgpu_eviction_fence.o amdgpu_ip.o \
amdgpu_wb.o
amdgpu-$(CONFIG_PROC_FS) += amdgpu_fdinfo.o
+15 -306
View File
@@ -44,6 +44,7 @@
#include <linux/hashtable.h>
#include <linux/dma-fence.h>
#include <linux/pci.h>
#include <linux/xarray.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_placement.h>
@@ -95,7 +96,6 @@
#include "amdgpu_doorbell.h"
#include "amdgpu_amdkfd.h"
#include "amdgpu_discovery.h"
#include "amdgpu_mes.h"
#include "amdgpu_umc.h"
#include "amdgpu_mmhub.h"
#include "amdgpu_gfxhub.h"
@@ -103,7 +103,6 @@
#include "amdgpu_smuio.h"
#include "amdgpu_fdinfo.h"
#include "amdgpu_mca.h"
#include "amdgpu_aca.h"
#include "amdgpu_ras.h"
#include "amdgpu_lockdep.h"
#include "amdgpu_cper.h"
@@ -112,7 +111,11 @@
#include "amdgpu_reg_state.h"
#include "amdgpu_userq.h"
#include "amdgpu_eviction_fence.h"
#include "amdgpu_wb.h"
#include "amdgpu_ip.h"
#include "amdgpu_mes.h"
#include "amdgpu_sa.h"
#include "amdgpu_acpi.h"
#if defined(CONFIG_DRM_AMD_ISP)
#include "amdgpu_isp.h"
#endif
@@ -134,13 +137,6 @@ struct amdgpu_mgpu_info {
uint32_t num_apu;
};
enum amdgpu_ss {
AMDGPU_SS_DRV_LOAD,
AMDGPU_SS_DEV_D0,
AMDGPU_SS_DEV_D3,
AMDGPU_SS_DRV_UNLOAD
};
struct amdgpu_hwip_reg_entry {
u32 hwip;
u32 inst;
@@ -227,6 +223,7 @@ extern int amdgpu_smartshift_bias;
extern int amdgpu_use_xgmi_p2p;
extern int amdgpu_mtype_local;
extern int amdgpu_enforce_isolation;
extern uint amdgpu_debug_mask;
#ifdef CONFIG_HSA_AMD
extern int sched_policy;
extern bool debug_evictions;
@@ -272,7 +269,6 @@ extern int amdgpu_ptl;
extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
#define AMDGPU_VM_MAX_NUM_CTX 4096
#define AMDGPU_SG_THRESHOLD (256*1024*1024)
#define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000
#define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */
@@ -305,9 +301,10 @@ extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
/* reset mask */
#define AMDGPU_RESET_TYPE_FULL (1 << 0) /* full adapter reset, mode1/mode2/BACO/etc. */
#define AMDGPU_RESET_TYPE_SOFT_RESET (1 << 1) /* IP level soft reset */
#define AMDGPU_RESET_TYPE_SOFT_RECOVERY (1 << 1) /* soft recovery, eg. kill shaders */
#define AMDGPU_RESET_TYPE_PER_QUEUE (1 << 2) /* per queue */
#define AMDGPU_RESET_TYPE_PER_PIPE (1 << 3) /* per pipe */
#define AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET (1 << 4) /* soft-resets an IP block */
/* max cursor sizes (in pixels) */
#define CIK_CURSOR_WIDTH 128
@@ -330,6 +327,7 @@ struct amdgpu_hive_info;
struct amdgpu_reset_context;
struct amdgpu_reset_control;
struct amdgpu_coredump_info;
struct amdgpu_video_codecs;
enum amdgpu_cp_irq {
AMDGPU_CP_IRQ_GFX_ME0_PIPE0_EOP = 0,
@@ -387,37 +385,6 @@ struct amdgpu_clock {
uint32_t max_pixel_clock;
};
/* sub-allocation manager, it has to be protected by another lock.
* By conception this is an helper for other part of the driver
* like the indirect buffer or semaphore, which both have their
* locking.
*
* Principe is simple, we keep a list of sub allocation in offset
* order (first entry has offset == 0, last entry has the highest
* offset).
*
* When allocating new object we first check if there is room at
* the end total_size - (last_object_offset + last_object_size) >=
* alloc_size. If so we allocate new object there.
*
* When there is not enough room at the end, we start waiting for
* each sub object until we reach object_offset+object_size >=
* alloc_size, this object then become the sub object we return.
*
* Alignment can't be bigger than page size.
*
* Hole are not considered for allocation to keep things simple.
* Assumption is that there won't be hole (all object on same
* alignment).
*/
struct amdgpu_sa_manager {
struct drm_suballoc_manager base;
struct amdgpu_bo *bo;
uint64_t gpu_addr;
void *cpu_ptr;
};
/*
* IRQS.
*/
@@ -446,8 +413,7 @@ struct amdgpu_fpriv {
struct amdgpu_bo_va *prt_va;
struct amdgpu_bo_va *csa_va;
struct amdgpu_bo_va *seq64_va;
struct mutex bo_list_lock;
struct idr bo_list_handles;
struct xarray bo_list_handles;
struct amdgpu_ctx_mgr ctx_mgr;
struct amdgpu_userq_mgr userq_mgr;
@@ -460,106 +426,11 @@ struct amdgpu_fpriv {
int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv);
/*
* Writeback
*/
#define AMDGPU_MAX_WB 1024 /* Reserve at most 1024 WB slots for amdgpu-owned rings. */
/**
* struct amdgpu_wb - This struct is used for small GPU memory allocation.
*
* This struct is used to allocate a small amount of GPU memory that can be
* used to shadow certain states into the memory. This is especially useful for
* providing easy CPU access to some states without requiring register access
* (e.g., if some block is power gated, reading register may be problematic).
*
* Note: the term writeback was initially used because many of the amdgpu
* components had some level of writeback memory, and this struct initially
* described those components.
*/
struct amdgpu_wb {
/**
* @wb_obj:
*
* Buffer Object used for the writeback memory.
*/
struct amdgpu_bo *wb_obj;
/**
* @wb:
*
* Pointer to the first writeback slot. In terms of CPU address
* this value can be accessed directly by using the offset as an index.
* For the GPU address, it is necessary to use gpu_addr and the offset.
*/
uint32_t *wb;
/**
* @gpu_addr:
*
* Writeback base address in the GPU.
*/
uint64_t gpu_addr;
/**
* @num_wb:
*
* Number of writeback slots reserved for amdgpu.
*/
u32 num_wb;
/**
* @used:
*
* Track the writeback slot already used.
*/
unsigned long used[DIV_ROUND_UP(AMDGPU_MAX_WB, BITS_PER_LONG)];
/**
* @lock:
*
* Protects read and write of the used field array.
*/
spinlock_t lock;
};
int amdgpu_device_wb_get(struct amdgpu_device *adev, u32 *wb);
void amdgpu_device_wb_free(struct amdgpu_device *adev, u32 wb);
/*
* Benchmarking
*/
int amdgpu_benchmark(struct amdgpu_device *adev, int test_number);
/*
* ASIC specific register table accessible by UMD
*/
struct amdgpu_allowed_register_entry {
uint32_t reg_offset;
bool grbm_indexed;
};
struct amdgpu_video_codec_info {
u32 codec_type;
u32 max_width;
u32 max_height;
u32 max_pixels_per_frame;
u32 max_level;
};
#define codec_info_build(type, width, height, level) \
.codec_type = type,\
.max_width = width,\
.max_height = height,\
.max_pixels_per_frame = height * width,\
.max_level = level,
struct amdgpu_video_codecs {
const u32 codec_count;
const struct amdgpu_video_codec_info *codec_array;
};
/*
* ASIC specific functions.
*/
@@ -587,8 +458,6 @@ struct amdgpu_asic_funcs {
/* invalidate hdp read cache */
void (*invalidate_hdp)(struct amdgpu_device *adev,
struct amdgpu_ring *ring);
/* check if the asic needs a full reset of if soft reset will work */
bool (*need_full_reset)(struct amdgpu_device *adev);
/* initialize doorbell layout for specific asic*/
void (*init_doorbell_index)(struct amdgpu_device *adev);
/* PCIe bandwidth usage */
@@ -665,38 +534,6 @@ struct amdgpu_uid {
struct amdgpu_device *adev;
};
#define MAX_UMA_OPTION_NAME 28
#define MAX_UMA_OPTION_ENTRIES 19
#define AMDGPU_UMA_FLAG_AUTO BIT(1)
#define AMDGPU_UMA_FLAG_CUSTOM BIT(0)
/**
* struct amdgpu_uma_carveout_option - single UMA carveout option
* @name: Name of the carveout option
* @memory_carved_mb: Amount of memory carved in MB
* @flags: ATCS flags supported by this option
*/
struct amdgpu_uma_carveout_option {
char name[MAX_UMA_OPTION_NAME];
uint32_t memory_carved_mb;
uint8_t flags;
};
/**
* struct amdgpu_uma_carveout_info - table of available UMA carveout options
* @num_entries: Number of available options
* @uma_option_index: The index of the option currently applied
* @update_lock: Lock to serialize changes to the option
* @entries: The array of carveout options
*/
struct amdgpu_uma_carveout_info {
uint8_t num_entries;
uint8_t uma_option_index;
struct mutex update_lock;
struct amdgpu_uma_carveout_option entries[MAX_UMA_OPTION_ENTRIES];
};
struct amd_powerplay {
void *pp_handle;
const struct amd_pm_funcs *pp_funcs;
@@ -741,44 +578,6 @@ struct amd_powerplay {
(rid == 0x01) || \
(rid == 0x10))))
enum amdgpu_mqd_update_flag {
AMDGPU_UPDATE_FLAG_DBG_WA_ENABLE = 1,
AMDGPU_UPDATE_FLAG_DBG_WA_DISABLE = 2,
AMDGPU_UPDATE_FLAG_IS_GWS = 4, /* quirk for gfx9 IP */
};
struct amdgpu_mqd_prop {
uint64_t mqd_gpu_addr;
uint64_t hqd_base_gpu_addr;
uint64_t rptr_gpu_addr;
uint64_t wptr_gpu_addr;
uint32_t queue_size;
bool use_doorbell;
uint32_t doorbell_index;
uint64_t eop_gpu_addr;
uint32_t hqd_pipe_priority;
uint32_t hqd_queue_priority;
uint32_t mqd_stride_size;
bool allow_tunneling;
bool hqd_active;
uint64_t shadow_addr;
uint64_t gds_bkup_addr;
uint64_t csa_addr;
uint64_t fence_address;
bool tmz_queue;
bool kernel_queue;
uint32_t *cu_mask;
uint32_t cu_mask_count;
uint32_t cu_flags;
bool is_user_cu_masked;
};
struct amdgpu_mqd {
unsigned mqd_size;
int (*init_mqd)(struct amdgpu_device *adev, void *mqd,
struct amdgpu_mqd_prop *p);
};
struct amdgpu_pcie_reset_ctx {
bool in_link_reset;
bool occurs_dpc;
@@ -851,6 +650,7 @@ struct amdgpu_device {
struct dev_pm_domain vga_pm_domain;
bool have_disp_power_ref;
bool have_atomics_support;
bool is_sw_smu;
/* BIOS */
bool is_atom_fw;
@@ -1022,9 +822,6 @@ struct amdgpu_device {
/* MCA */
struct amdgpu_mca mca;
/* ACA */
struct amdgpu_aca aca;
/* CPER */
struct amdgpu_cper cper;
@@ -1130,12 +927,13 @@ struct amdgpu_device {
bool debug_largebar;
bool debug_disable_soft_recovery;
bool debug_use_vram_fw_buf;
bool debug_enable_ras_aca;
bool debug_exp_resets;
bool debug_disable_gpu_ring_reset;
bool debug_vm_userptr;
bool debug_disable_ce_logs;
bool debug_enable_ce_cs;
bool debug_hibernation_thaw_resume_gpu;
bool debug_disable_ip_block_soft_reset;
/* Protection for the following isolation structure */
struct mutex enforce_isolation_mutex;
@@ -1165,14 +963,6 @@ struct amdgpu_device {
struct amdgpu_kfd_dev kfd;
};
/*
* MES FW uses address(mqd_addr + sizeof(struct mqd) + 3*sizeof(uint32_t))
* as fence address and writes a 32 bit fence value to this address.
* Driver needs to allocate at least 4 DWs extra memory in addition to
* sizeof(struct mqd). Add 8 DWs and align to AMDGPU_GPU_PAGE_SIZE for safety.
*/
#define AMDGPU_MQD_SIZE_ALIGN(mqd_size) AMDGPU_GPU_PAGE_ALIGN(((mqd_size) + 32))
static inline uint32_t amdgpu_ip_version(const struct amdgpu_device *adev,
uint8_t ip, uint8_t inst)
{
@@ -1356,7 +1146,6 @@ int emu_soc_asic_init(struct amdgpu_device *adev);
#define amdgpu_asic_read_bios_from_rom(adev, b, l) (adev)->asic_funcs->read_bios_from_rom((adev), (b), (l))
#define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v)))
#define amdgpu_asic_get_config_memsize(adev) (adev)->asic_funcs->get_config_memsize((adev))
#define amdgpu_asic_need_full_reset(adev) (adev)->asic_funcs->need_full_reset((adev))
#define amdgpu_asic_init_doorbell_index(adev) (adev)->asic_funcs->init_doorbell_index((adev))
#define amdgpu_asic_get_pcie_usage(adev, cnt0, cnt1) ((adev)->asic_funcs->get_pcie_usage((adev), (cnt0), (cnt1)))
#define amdgpu_asic_need_reset_on_init(adev) (adev)->asic_funcs->need_reset_on_init((adev))
@@ -1468,6 +1257,8 @@ int amdgpu_enable_vblank_kms(struct drm_crtc *crtc);
void amdgpu_disable_vblank_kms(struct drm_crtc *crtc);
int amdgpu_info_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
/*
* functions used by amdgpu_encoder.c
@@ -1488,88 +1279,6 @@ struct amdgpu_afmt_acr {
struct amdgpu_afmt_acr amdgpu_afmt_acr(uint32_t clock);
/* amdgpu_acpi.c */
struct amdgpu_numa_info {
uint64_t size;
int pxm;
int nid;
};
/* ATCS Device/Driver State */
#define AMDGPU_ATCS_PSC_DEV_STATE_D0 0
#define AMDGPU_ATCS_PSC_DEV_STATE_D3_HOT 3
#define AMDGPU_ATCS_PSC_DRV_STATE_OPR 0
#define AMDGPU_ATCS_PSC_DRV_STATE_NOT_OPR 1
#if defined(CONFIG_ACPI)
int amdgpu_acpi_init(struct amdgpu_device *adev);
void amdgpu_acpi_fini(struct amdgpu_device *adev);
bool amdgpu_acpi_is_pcie_performance_request_supported(struct amdgpu_device *adev);
bool amdgpu_acpi_is_power_shift_control_supported(void);
bool amdgpu_acpi_is_set_uma_allocation_size_supported(void);
int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev,
u8 perf_req, bool advertise);
int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
u8 dev_state, bool drv_state);
int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
enum amdgpu_ss ss_state);
int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type);
int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev);
int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev, u64 *tmr_offset,
u64 *tmr_size);
int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev, int xcc_id,
struct amdgpu_numa_info *numa_info);
void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps);
bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev);
void amdgpu_acpi_detect(void);
void amdgpu_acpi_release(void);
#else
static inline int amdgpu_acpi_init(struct amdgpu_device *adev) { return 0; }
static inline int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev,
u64 *tmr_offset, u64 *tmr_size)
{
return -EINVAL;
}
static inline int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev,
int xcc_id,
struct amdgpu_numa_info *numa_info)
{
return -EINVAL;
}
static inline void amdgpu_acpi_fini(struct amdgpu_device *adev) { }
static inline bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev) { return false; }
static inline void amdgpu_acpi_detect(void) { }
static inline void amdgpu_acpi_release(void) { }
static inline bool amdgpu_acpi_is_power_shift_control_supported(void) { return false; }
static inline bool amdgpu_acpi_is_set_uma_allocation_size_supported(void) { return false; }
static inline int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
u8 dev_state, bool drv_state) { return 0; }
static inline int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
enum amdgpu_ss ss_state)
{
return 0;
}
static inline int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type)
{
return -EINVAL;
}
static inline void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps) { }
#endif
#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev);
bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev);
#else
static inline bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev) { return false; }
static inline bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev) { return false; }
#endif
#if defined(CONFIG_DRM_AMD_ISP)
int amdgpu_acpi_get_isp4_dev(struct acpi_device **dev);
#endif
void amdgpu_register_gpu_instance(struct amdgpu_device *adev);
void amdgpu_unregister_gpu_instance(struct amdgpu_device *adev);
-985
View File
@@ -1,985 +0,0 @@
/*
* Copyright 2023 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <linux/list.h>
#include "amdgpu.h"
#include "amdgpu_aca.h"
#include "amdgpu_ras.h"
#define ACA_BANK_HWID(type, hwid, mcatype) [ACA_HWIP_TYPE_##type] = {hwid, mcatype}
typedef int bank_handler_t(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data);
static struct aca_hwip aca_hwid_mcatypes[ACA_HWIP_TYPE_COUNT] = {
ACA_BANK_HWID(SMU, 0x01, 0x01),
ACA_BANK_HWID(PCS_XGMI, 0x50, 0x00),
ACA_BANK_HWID(UMC, 0x96, 0x00),
};
static void aca_banks_init(struct aca_banks *banks)
{
if (!banks)
return;
memset(banks, 0, sizeof(*banks));
INIT_LIST_HEAD(&banks->list);
}
static int aca_banks_add_bank(struct aca_banks *banks, struct aca_bank *bank)
{
struct aca_bank_node *node;
if (!bank)
return -EINVAL;
node = kvzalloc_obj(*node);
if (!node)
return -ENOMEM;
memcpy(&node->bank, bank, sizeof(*bank));
INIT_LIST_HEAD(&node->node);
list_add_tail(&node->node, &banks->list);
banks->nr_banks++;
return 0;
}
static void aca_banks_release(struct aca_banks *banks)
{
struct aca_bank_node *node, *tmp;
if (list_empty(&banks->list))
return;
list_for_each_entry_safe(node, tmp, &banks->list, node) {
list_del(&node->node);
kvfree(node);
banks->nr_banks--;
}
}
static int aca_smu_get_valid_aca_count(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count)
{
struct amdgpu_aca *aca = &adev->aca;
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
if (!count)
return -EINVAL;
if (!smu_funcs || !smu_funcs->get_valid_aca_count)
return -EOPNOTSUPP;
return smu_funcs->get_valid_aca_count(adev, type, count);
}
static struct aca_regs_dump {
const char *name;
int reg_idx;
} aca_regs[] = {
{"CONTROL", ACA_REG_IDX_CTL},
{"STATUS", ACA_REG_IDX_STATUS},
{"ADDR", ACA_REG_IDX_ADDR},
{"MISC", ACA_REG_IDX_MISC0},
{"CONFIG", ACA_REG_IDX_CONFIG},
{"IPID", ACA_REG_IDX_IPID},
{"SYND", ACA_REG_IDX_SYND},
{"DESTAT", ACA_REG_IDX_DESTAT},
{"DEADDR", ACA_REG_IDX_DEADDR},
{"CONTROL_MASK", ACA_REG_IDX_CTL_MASK},
};
static void aca_smu_bank_dump(struct amdgpu_device *adev, int idx, int total, struct aca_bank *bank,
struct ras_query_context *qctx)
{
u64 event_id = qctx ? qctx->evid.event_id : RAS_EVENT_INVALID_ID;
int i;
if (adev->debug_disable_ce_logs &&
bank->smu_err_type == ACA_SMU_TYPE_CE &&
!ACA_BANK_ERR_IS_DEFFERED(bank))
return;
RAS_EVENT_LOG(adev, event_id, HW_ERR "Accelerator Check Architecture events logged\n");
/* plus 1 for output format, e.g: ACA[08/08]: xxxx */
for (i = 0; i < ARRAY_SIZE(aca_regs); i++)
RAS_EVENT_LOG(adev, event_id, HW_ERR "ACA[%02d/%02d].%s=0x%016llx\n",
idx + 1, total, aca_regs[i].name, bank->regs[aca_regs[i].reg_idx]);
if (ACA_REG__STATUS__SCRUB(bank->regs[ACA_REG_IDX_STATUS]))
RAS_EVENT_LOG(adev, event_id, HW_ERR "hardware error logged by the scrubber\n");
}
static bool aca_bank_hwip_is_matched(struct aca_bank *bank, enum aca_hwip_type type)
{
struct aca_hwip *hwip;
int hwid, mcatype;
u64 ipid;
if (!bank || type == ACA_HWIP_TYPE_UNKNOW)
return false;
hwip = &aca_hwid_mcatypes[type];
if (!hwip->hwid)
return false;
ipid = bank->regs[ACA_REG_IDX_IPID];
hwid = ACA_REG__IPID__HARDWAREID(ipid);
mcatype = ACA_REG__IPID__MCATYPE(ipid);
return hwip->hwid == hwid && hwip->mcatype == mcatype;
}
static int aca_smu_get_valid_aca_banks(struct amdgpu_device *adev, enum aca_smu_type type,
int start, int count,
struct aca_banks *banks, struct ras_query_context *qctx)
{
struct amdgpu_aca *aca = &adev->aca;
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
struct aca_bank bank;
int i, max_count, ret;
if (!count)
return 0;
if (!smu_funcs || !smu_funcs->get_valid_aca_bank)
return -EOPNOTSUPP;
switch (type) {
case ACA_SMU_TYPE_UE:
max_count = smu_funcs->max_ue_bank_count;
break;
case ACA_SMU_TYPE_CE:
max_count = smu_funcs->max_ce_bank_count;
break;
default:
return -EINVAL;
}
if (start + count > max_count)
return -EINVAL;
count = min_t(int, count, max_count);
for (i = 0; i < count; i++) {
memset(&bank, 0, sizeof(bank));
ret = smu_funcs->get_valid_aca_bank(adev, type, start + i, &bank);
if (ret)
return ret;
bank.smu_err_type = type;
/*
* Poison being consumed when injecting a UE while running background workloads,
* which are unexpected.
*/
if (type == ACA_SMU_TYPE_UE &&
ACA_REG__STATUS__POISON(bank.regs[ACA_REG_IDX_STATUS]) &&
!aca_bank_hwip_is_matched(&bank, ACA_HWIP_TYPE_UMC))
continue;
aca_smu_bank_dump(adev, i, count, &bank, qctx);
ret = aca_banks_add_bank(banks, &bank);
if (ret)
return ret;
}
return 0;
}
static bool aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type)
{
const struct aca_bank_ops *bank_ops = handle->bank_ops;
/* Parse all deferred errors with UMC aca handle */
if (ACA_BANK_ERR_IS_DEFFERED(bank))
return handle->hwip == ACA_HWIP_TYPE_UMC;
if (!aca_bank_hwip_is_matched(bank, handle->hwip))
return false;
if (!bank_ops->aca_bank_is_valid)
return true;
return bank_ops->aca_bank_is_valid(handle, bank, type, handle->data);
}
static struct aca_bank_error *new_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
{
struct aca_bank_error *bank_error;
bank_error = kvzalloc_obj(*bank_error);
if (!bank_error)
return NULL;
INIT_LIST_HEAD(&bank_error->node);
memcpy(&bank_error->info, info, sizeof(*info));
mutex_lock(&aerr->lock);
list_add_tail(&bank_error->node, &aerr->list);
aerr->nr_errors++;
mutex_unlock(&aerr->lock);
return bank_error;
}
static struct aca_bank_error *find_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
{
struct aca_bank_error *bank_error = NULL;
struct aca_bank_info *tmp_info;
bool found = false;
mutex_lock(&aerr->lock);
list_for_each_entry(bank_error, &aerr->list, node) {
tmp_info = &bank_error->info;
if (tmp_info->socket_id == info->socket_id &&
tmp_info->die_id == info->die_id) {
found = true;
goto out_unlock;
}
}
out_unlock:
mutex_unlock(&aerr->lock);
return found ? bank_error : NULL;
}
static void aca_bank_error_remove(struct aca_error *aerr, struct aca_bank_error *bank_error)
{
if (!aerr || !bank_error)
return;
list_del(&bank_error->node);
aerr->nr_errors--;
kvfree(bank_error);
}
static struct aca_bank_error *get_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
{
struct aca_bank_error *bank_error;
if (!aerr || !info)
return NULL;
bank_error = find_bank_error(aerr, info);
if (bank_error)
return bank_error;
return new_bank_error(aerr, info);
}
int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info,
enum aca_error_type type, u64 count)
{
struct aca_error_cache *error_cache = &handle->error_cache;
struct aca_bank_error *bank_error;
struct aca_error *aerr;
if (!handle || !info || type >= ACA_ERROR_TYPE_COUNT)
return -EINVAL;
if (!count)
return 0;
aerr = &error_cache->errors[type];
bank_error = get_bank_error(aerr, info);
if (!bank_error)
return -ENOMEM;
bank_error->count += count;
return 0;
}
static int aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type)
{
const struct aca_bank_ops *bank_ops = handle->bank_ops;
if (!bank)
return -EINVAL;
if (!bank_ops->aca_bank_parser)
return -EOPNOTSUPP;
return bank_ops->aca_bank_parser(handle, bank, type,
handle->data);
}
static int handler_aca_log_bank_error(struct aca_handle *handle, struct aca_bank *bank,
enum aca_smu_type type, void *data)
{
int ret;
ret = aca_bank_parser(handle, bank, type);
if (ret)
return ret;
return 0;
}
static int aca_dispatch_bank(struct aca_handle_manager *mgr, struct aca_bank *bank,
enum aca_smu_type type, bank_handler_t handler, void *data)
{
struct aca_handle *handle;
int ret;
if (list_empty(&mgr->list))
return 0;
list_for_each_entry(handle, &mgr->list, node) {
if (!aca_bank_is_valid(handle, bank, type))
continue;
ret = handler(handle, bank, type, data);
if (ret)
return ret;
}
return 0;
}
static int aca_dispatch_banks(struct aca_handle_manager *mgr, struct aca_banks *banks,
enum aca_smu_type type, bank_handler_t handler, void *data)
{
struct aca_bank_node *node;
struct aca_bank *bank;
int ret;
if (!mgr || !banks)
return -EINVAL;
/* pre check to avoid unnecessary operations */
if (list_empty(&mgr->list) || list_empty(&banks->list))
return 0;
list_for_each_entry(node, &banks->list, node) {
bank = &node->bank;
ret = aca_dispatch_bank(mgr, bank, type, handler, data);
if (ret)
return ret;
}
return 0;
}
static bool aca_bank_should_update(struct amdgpu_device *adev, enum aca_smu_type type)
{
struct amdgpu_aca *aca = &adev->aca;
bool ret = true;
/*
* Because the UE Valid MCA count will only be cleared after reset,
* in order to avoid repeated counting of the error count,
* the aca bank is only updated once during the gpu recovery stage.
*/
if (type == ACA_SMU_TYPE_UE) {
if (amdgpu_ras_intr_triggered())
ret = atomic_cmpxchg(&aca->ue_update_flag, 0, 1) == 0;
else
atomic_set(&aca->ue_update_flag, 0);
}
return ret;
}
static void aca_banks_generate_cper(struct amdgpu_device *adev,
enum aca_smu_type type,
struct aca_banks *banks,
int count)
{
struct aca_bank_node *node;
struct aca_bank *bank;
int r;
if (!adev->cper.enabled)
return;
if (!banks || !count) {
dev_warn(adev->dev, "fail to generate cper records\n");
return;
}
/* UEs must be encoded into separate CPER entries */
if (type == ACA_SMU_TYPE_UE) {
struct aca_banks de_banks;
aca_banks_init(&de_banks);
list_for_each_entry(node, &banks->list, node) {
bank = &node->bank;
if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) {
r = aca_banks_add_bank(&de_banks, bank);
if (r)
dev_warn(adev->dev, "fail to add de banks, ret = %d\n", r);
} else {
if (amdgpu_cper_generate_ue_record(adev, bank))
dev_warn(adev->dev, "fail to generate ue cper records\n");
}
}
if (!list_empty(&de_banks.list)) {
if (amdgpu_cper_generate_ce_records(adev, &de_banks, de_banks.nr_banks))
dev_warn(adev->dev, "fail to generate de cper records\n");
}
aca_banks_release(&de_banks);
} else {
/*
* SMU_TYPE_CE banks are combined into 1 CPER entries,
* they could be CEs or DEs or both
*/
if (amdgpu_cper_generate_ce_records(adev, banks, count))
dev_warn(adev->dev, "fail to generate ce cper records\n");
}
}
static int aca_banks_update(struct amdgpu_device *adev, enum aca_smu_type type,
bank_handler_t handler, struct ras_query_context *qctx, void *data)
{
struct amdgpu_aca *aca = &adev->aca;
struct aca_banks banks;
u32 count = 0;
int ret;
if (list_empty(&aca->mgr.list))
return 0;
if (!aca_bank_should_update(adev, type))
return 0;
ret = aca_smu_get_valid_aca_count(adev, type, &count);
if (ret)
return ret;
if (!count)
return 0;
aca_banks_init(&banks);
ret = aca_smu_get_valid_aca_banks(adev, type, 0, count, &banks, qctx);
if (ret)
goto err_release_banks;
if (list_empty(&banks.list)) {
ret = 0;
goto err_release_banks;
}
ret = aca_dispatch_banks(&aca->mgr, &banks, type,
handler, data);
if (ret)
goto err_release_banks;
aca_banks_generate_cper(adev, type, &banks, count);
err_release_banks:
aca_banks_release(&banks);
return ret;
}
static int aca_log_aca_error_data(struct aca_bank_error *bank_error, enum aca_error_type type, struct ras_err_data *err_data)
{
struct aca_bank_info *info;
struct amdgpu_smuio_mcm_config_info mcm_info;
u64 count;
if (type >= ACA_ERROR_TYPE_COUNT)
return -EINVAL;
count = bank_error->count;
if (!count)
return 0;
info = &bank_error->info;
mcm_info.die_id = info->die_id;
mcm_info.socket_id = info->socket_id;
switch (type) {
case ACA_ERROR_TYPE_UE:
amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, count);
break;
case ACA_ERROR_TYPE_CE:
amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, count);
break;
case ACA_ERROR_TYPE_DEFERRED:
amdgpu_ras_error_statistic_de_count(err_data, &mcm_info, count);
break;
default:
break;
}
return 0;
}
static int aca_log_aca_error(struct aca_handle *handle, enum aca_error_type type, struct ras_err_data *err_data)
{
struct aca_error_cache *error_cache = &handle->error_cache;
struct aca_error *aerr = &error_cache->errors[type];
struct aca_bank_error *bank_error, *tmp;
mutex_lock(&aerr->lock);
if (list_empty(&aerr->list))
goto out_unlock;
list_for_each_entry_safe(bank_error, tmp, &aerr->list, node) {
aca_log_aca_error_data(bank_error, type, err_data);
aca_bank_error_remove(aerr, bank_error);
}
out_unlock:
mutex_unlock(&aerr->lock);
return 0;
}
static int __aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle, enum aca_error_type type,
struct ras_err_data *err_data, struct ras_query_context *qctx)
{
enum aca_smu_type smu_type;
int ret;
switch (type) {
case ACA_ERROR_TYPE_UE:
smu_type = ACA_SMU_TYPE_UE;
break;
case ACA_ERROR_TYPE_CE:
case ACA_ERROR_TYPE_DEFERRED:
smu_type = ACA_SMU_TYPE_CE;
break;
default:
return -EINVAL;
}
/* update aca bank to aca source error_cache first */
ret = aca_banks_update(adev, smu_type, handler_aca_log_bank_error, qctx, NULL);
if (ret)
return ret;
/* DEs may contain in CEs or UEs */
if (type != ACA_ERROR_TYPE_DEFERRED)
aca_log_aca_error(handle, ACA_ERROR_TYPE_DEFERRED, err_data);
return aca_log_aca_error(handle, type, err_data);
}
static bool aca_handle_is_valid(struct aca_handle *handle)
{
if (!handle->mask || !list_empty(&handle->node))
return false;
return true;
}
int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle,
enum aca_error_type type, struct ras_err_data *err_data,
struct ras_query_context *qctx)
{
if (!handle || !err_data)
return -EINVAL;
if (aca_handle_is_valid(handle))
return -EOPNOTSUPP;
if ((type < 0) || (!(BIT(type) & handle->mask)))
return 0;
return __aca_get_error_data(adev, handle, type, err_data, qctx);
}
static void aca_error_init(struct aca_error *aerr, enum aca_error_type type)
{
mutex_init(&aerr->lock);
INIT_LIST_HEAD(&aerr->list);
aerr->type = type;
aerr->nr_errors = 0;
}
static void aca_init_error_cache(struct aca_handle *handle)
{
struct aca_error_cache *error_cache = &handle->error_cache;
int type;
for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++)
aca_error_init(&error_cache->errors[type], type);
}
static void aca_error_fini(struct aca_error *aerr)
{
struct aca_bank_error *bank_error, *tmp;
mutex_lock(&aerr->lock);
if (list_empty(&aerr->list))
goto out_unlock;
list_for_each_entry_safe(bank_error, tmp, &aerr->list, node)
aca_bank_error_remove(aerr, bank_error);
out_unlock:
mutex_unlock(&aerr->lock);
mutex_destroy(&aerr->lock);
}
static void aca_fini_error_cache(struct aca_handle *handle)
{
struct aca_error_cache *error_cache = &handle->error_cache;
int type;
for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++)
aca_error_fini(&error_cache->errors[type]);
}
static int add_aca_handle(struct amdgpu_device *adev, struct aca_handle_manager *mgr, struct aca_handle *handle,
const char *name, const struct aca_info *ras_info, void *data)
{
memset(handle, 0, sizeof(*handle));
handle->adev = adev;
handle->mgr = mgr;
handle->name = name;
handle->hwip = ras_info->hwip;
handle->mask = ras_info->mask;
handle->bank_ops = ras_info->bank_ops;
handle->data = data;
aca_init_error_cache(handle);
INIT_LIST_HEAD(&handle->node);
list_add_tail(&handle->node, &mgr->list);
mgr->nr_handles++;
return 0;
}
static ssize_t aca_sysfs_read(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct aca_handle *handle = container_of(attr, struct aca_handle, aca_attr);
/* NOTE: the aca cache will be auto cleared once read,
* So the driver should unify the query entry point, forward request to ras query interface directly */
return amdgpu_ras_aca_sysfs_read(dev, attr, handle, buf, handle->data);
}
static int add_aca_sysfs(struct amdgpu_device *adev, struct aca_handle *handle)
{
struct device_attribute *aca_attr = &handle->aca_attr;
snprintf(handle->attr_name, sizeof(handle->attr_name) - 1, "aca_%s", handle->name);
aca_attr->show = aca_sysfs_read;
aca_attr->attr.name = handle->attr_name;
aca_attr->attr.mode = S_IRUGO;
sysfs_attr_init(&aca_attr->attr);
return sysfs_add_file_to_group(&adev->dev->kobj,
&aca_attr->attr,
"ras");
}
int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle,
const char *name, const struct aca_info *ras_info, void *data)
{
struct amdgpu_aca *aca = &adev->aca;
int ret;
if (!amdgpu_aca_is_enabled(adev))
return 0;
ret = add_aca_handle(adev, &aca->mgr, handle, name, ras_info, data);
if (ret)
return ret;
return add_aca_sysfs(adev, handle);
}
static void remove_aca_handle(struct aca_handle *handle)
{
struct aca_handle_manager *mgr = handle->mgr;
aca_fini_error_cache(handle);
list_del(&handle->node);
mgr->nr_handles--;
}
static void remove_aca_sysfs(struct aca_handle *handle)
{
struct amdgpu_device *adev = handle->adev;
struct device_attribute *aca_attr = &handle->aca_attr;
if (adev->dev->kobj.sd)
sysfs_remove_file_from_group(&adev->dev->kobj,
&aca_attr->attr,
"ras");
}
void amdgpu_aca_remove_handle(struct aca_handle *handle)
{
if (!handle || list_empty(&handle->node))
return;
remove_aca_sysfs(handle);
remove_aca_handle(handle);
}
static int aca_manager_init(struct aca_handle_manager *mgr)
{
INIT_LIST_HEAD(&mgr->list);
mgr->nr_handles = 0;
return 0;
}
static void aca_manager_fini(struct aca_handle_manager *mgr)
{
struct aca_handle *handle, *tmp;
if (list_empty(&mgr->list))
return;
list_for_each_entry_safe(handle, tmp, &mgr->list, node)
amdgpu_aca_remove_handle(handle);
}
bool amdgpu_aca_is_enabled(struct amdgpu_device *adev)
{
return (adev->aca.is_enabled ||
adev->debug_enable_ras_aca);
}
int amdgpu_aca_init(struct amdgpu_device *adev)
{
struct amdgpu_aca *aca = &adev->aca;
int ret;
atomic_set(&aca->ue_update_flag, 0);
ret = aca_manager_init(&aca->mgr);
if (ret)
return ret;
return 0;
}
void amdgpu_aca_fini(struct amdgpu_device *adev)
{
struct amdgpu_aca *aca = &adev->aca;
aca_manager_fini(&aca->mgr);
atomic_set(&aca->ue_update_flag, 0);
}
int amdgpu_aca_reset(struct amdgpu_device *adev)
{
struct amdgpu_aca *aca = &adev->aca;
atomic_set(&aca->ue_update_flag, 0);
return 0;
}
void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs)
{
struct amdgpu_aca *aca = &adev->aca;
WARN_ON(aca->smu_funcs);
aca->smu_funcs = smu_funcs;
}
int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info)
{
u64 ipid;
u32 instidhi, instidlo;
if (!bank || !info)
return -EINVAL;
ipid = bank->regs[ACA_REG_IDX_IPID];
info->hwid = ACA_REG__IPID__HARDWAREID(ipid);
info->mcatype = ACA_REG__IPID__MCATYPE(ipid);
/*
* Unfied DieID Format: SAASS. A:AID, S:Socket.
* Unfied DieID[4:4] = InstanceId[0:0]
* Unfied DieID[0:3] = InstanceIdHi[0:3]
*/
instidhi = ACA_REG__IPID__INSTANCEIDHI(ipid);
instidlo = ACA_REG__IPID__INSTANCEIDLO(ipid);
info->die_id = ((instidhi >> 2) & 0x03);
info->socket_id = ((instidlo & 0x1) << 2) | (instidhi & 0x03);
return 0;
}
static int aca_bank_get_error_code(struct amdgpu_device *adev, struct aca_bank *bank)
{
struct amdgpu_aca *aca = &adev->aca;
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
if (!smu_funcs || !smu_funcs->parse_error_code)
return -EOPNOTSUPP;
return smu_funcs->parse_error_code(adev, bank);
}
int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size)
{
int i, error_code;
if (!bank || !err_codes)
return -EINVAL;
error_code = aca_bank_get_error_code(adev, bank);
if (error_code < 0)
return error_code;
for (i = 0; i < size; i++) {
if (err_codes[i] == error_code)
return 0;
}
return -EINVAL;
}
int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en)
{
struct amdgpu_aca *aca = &adev->aca;
const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
if (!smu_funcs || !smu_funcs->set_debug_mode)
return -EOPNOTSUPP;
return smu_funcs->set_debug_mode(adev, en);
}
#if defined(CONFIG_DEBUG_FS)
static int amdgpu_aca_smu_debug_mode_set(void *data, u64 val)
{
struct amdgpu_device *adev = (struct amdgpu_device *)data;
int ret;
ret = amdgpu_ras_set_aca_debug_mode(adev, val ? true : false);
if (ret)
return ret;
dev_info(adev->dev, "amdgpu set smu aca debug mode %s success\n", val ? "on" : "off");
return 0;
}
static void aca_dump_entry(struct seq_file *m, struct aca_bank *bank, enum aca_smu_type type, int idx)
{
struct aca_bank_info info;
int i, ret;
ret = aca_bank_info_decode(bank, &info);
if (ret)
return;
seq_printf(m, "aca entry[%d].type: %s\n", idx, type == ACA_SMU_TYPE_UE ? "UE" : "CE");
seq_printf(m, "aca entry[%d].info: socketid:%d aid:%d hwid:0x%03x mcatype:0x%04x\n",
idx, info.socket_id, info.die_id, info.hwid, info.mcatype);
for (i = 0; i < ARRAY_SIZE(aca_regs); i++)
seq_printf(m, "aca entry[%d].regs[%d]: 0x%016llx\n", idx, aca_regs[i].reg_idx, bank->regs[aca_regs[i].reg_idx]);
}
struct aca_dump_context {
struct seq_file *m;
int idx;
};
static int handler_aca_bank_dump(struct aca_handle *handle, struct aca_bank *bank,
enum aca_smu_type type, void *data)
{
struct aca_dump_context *ctx = (struct aca_dump_context *)data;
aca_dump_entry(ctx->m, bank, type, ctx->idx++);
return handler_aca_log_bank_error(handle, bank, type, NULL);
}
static int aca_dump_show(struct seq_file *m, enum aca_smu_type type)
{
struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
struct aca_dump_context context = {
.m = m,
.idx = 0,
};
return aca_banks_update(adev, type, handler_aca_bank_dump, NULL, (void *)&context);
}
static int aca_dump_ce_show(struct seq_file *m, void *unused)
{
return aca_dump_show(m, ACA_SMU_TYPE_CE);
}
static int aca_dump_ce_open(struct inode *inode, struct file *file)
{
return single_open(file, aca_dump_ce_show, inode->i_private);
}
static const struct file_operations aca_ce_dump_debug_fops = {
.owner = THIS_MODULE,
.open = aca_dump_ce_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int aca_dump_ue_show(struct seq_file *m, void *unused)
{
return aca_dump_show(m, ACA_SMU_TYPE_UE);
}
static int aca_dump_ue_open(struct inode *inode, struct file *file)
{
return single_open(file, aca_dump_ue_show, inode->i_private);
}
static const struct file_operations aca_ue_dump_debug_fops = {
.owner = THIS_MODULE,
.open = aca_dump_ue_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
DEFINE_DEBUGFS_ATTRIBUTE(aca_debug_mode_fops, NULL, amdgpu_aca_smu_debug_mode_set, "%llu\n");
#endif
void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root)
{
#if defined(CONFIG_DEBUG_FS)
if (!root)
return;
debugfs_create_file("aca_debug_mode", 0200, root, adev, &aca_debug_mode_fops);
debugfs_create_file("aca_ue_dump", 0400, root, adev, &aca_ue_dump_debug_fops);
debugfs_create_file("aca_ce_dump", 0400, root, adev, &aca_ce_dump_debug_fops);
#endif
}
-232
View File
@@ -1,232 +0,0 @@
/*
* Copyright 2023 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef __AMDGPU_ACA_H__
#define __AMDGPU_ACA_H__
#include <linux/list.h>
struct ras_err_data;
struct ras_query_context;
#define ACA_MAX_REGS_COUNT (16)
#define ACA_REG_FIELD(x, h, l) (((x) & GENMASK_ULL(h, l)) >> l)
#define ACA_REG__STATUS__VAL(x) ACA_REG_FIELD(x, 63, 63)
#define ACA_REG__STATUS__OVERFLOW(x) ACA_REG_FIELD(x, 62, 62)
#define ACA_REG__STATUS__UC(x) ACA_REG_FIELD(x, 61, 61)
#define ACA_REG__STATUS__EN(x) ACA_REG_FIELD(x, 60, 60)
#define ACA_REG__STATUS__MISCV(x) ACA_REG_FIELD(x, 59, 59)
#define ACA_REG__STATUS__ADDRV(x) ACA_REG_FIELD(x, 58, 58)
#define ACA_REG__STATUS__PCC(x) ACA_REG_FIELD(x, 57, 57)
#define ACA_REG__STATUS__ERRCOREIDVAL(x) ACA_REG_FIELD(x, 56, 56)
#define ACA_REG__STATUS__TCC(x) ACA_REG_FIELD(x, 55, 55)
#define ACA_REG__STATUS__SYNDV(x) ACA_REG_FIELD(x, 53, 53)
#define ACA_REG__STATUS__CECC(x) ACA_REG_FIELD(x, 46, 46)
#define ACA_REG__STATUS__UECC(x) ACA_REG_FIELD(x, 45, 45)
#define ACA_REG__STATUS__DEFERRED(x) ACA_REG_FIELD(x, 44, 44)
#define ACA_REG__STATUS__POISON(x) ACA_REG_FIELD(x, 43, 43)
#define ACA_REG__STATUS__SCRUB(x) ACA_REG_FIELD(x, 40, 40)
#define ACA_REG__STATUS__ERRCOREID(x) ACA_REG_FIELD(x, 37, 32)
#define ACA_REG__STATUS__ADDRLSB(x) ACA_REG_FIELD(x, 29, 24)
#define ACA_REG__STATUS__ERRORCODEEXT(x) ACA_REG_FIELD(x, 21, 16)
#define ACA_REG__STATUS__ERRORCODE(x) ACA_REG_FIELD(x, 15, 0)
#define ACA_REG__IPID__MCATYPE(x) ACA_REG_FIELD(x, 63, 48)
#define ACA_REG__IPID__INSTANCEIDHI(x) ACA_REG_FIELD(x, 47, 44)
#define ACA_REG__IPID__HARDWAREID(x) ACA_REG_FIELD(x, 43, 32)
#define ACA_REG__IPID__INSTANCEIDLO(x) ACA_REG_FIELD(x, 31, 0)
#define ACA_REG__MISC0__VALID(x) ACA_REG_FIELD(x, 63, 63)
#define ACA_REG__MISC0__OVRFLW(x) ACA_REG_FIELD(x, 48, 48)
#define ACA_REG__MISC0__ERRCNT(x) ACA_REG_FIELD(x, 43, 32)
#define ACA_REG__SYND__ERRORINFORMATION(x) ACA_REG_FIELD(x, 17, 0)
/* NOTE: The following codes refers to the smu header file */
#define ACA_EXTERROR_CODE_CE 0x3a
#define ACA_EXTERROR_CODE_FAULT 0x3b
#define ACA_ERROR_UE_MASK BIT_MASK(ACA_ERROR_TYPE_UE)
#define ACA_ERROR_CE_MASK BIT_MASK(ACA_ERROR_TYPE_CE)
#define ACA_ERROR_DEFERRED_MASK BIT_MASK(ACA_ERROR_TYPE_DEFERRED)
#define mmSMNAID_AID0_MCA_SMU 0x03b30400 /* SMN AID AID0 */
#define mmSMNAID_XCD0_MCA_SMU 0x36430400 /* SMN AID XCD0 */
#define mmSMNAID_XCD1_MCA_SMU 0x38430400 /* SMN AID XCD1 */
#define mmSMNXCD_XCD0_MCA_SMU 0x40430400 /* SMN XCD XCD0 */
#define ACA_BANK_ERR_IS_DEFFERED(bank) \
(ACA_REG__STATUS__POISON((bank)->regs[ACA_REG_IDX_STATUS]) || \
ACA_REG__STATUS__DEFERRED((bank)->regs[ACA_REG_IDX_STATUS]))
enum aca_reg_idx {
ACA_REG_IDX_CTL = 0,
ACA_REG_IDX_STATUS = 1,
ACA_REG_IDX_ADDR = 2,
ACA_REG_IDX_MISC0 = 3,
ACA_REG_IDX_CONFIG = 4,
ACA_REG_IDX_IPID = 5,
ACA_REG_IDX_SYND = 6,
ACA_REG_IDX_DESTAT = 8,
ACA_REG_IDX_DEADDR = 9,
ACA_REG_IDX_CTL_MASK = 10,
ACA_REG_IDX_COUNT = 16,
};
enum aca_hwip_type {
ACA_HWIP_TYPE_UNKNOW = -1,
ACA_HWIP_TYPE_PSP = 0,
ACA_HWIP_TYPE_UMC,
ACA_HWIP_TYPE_SMU,
ACA_HWIP_TYPE_PCS_XGMI,
ACA_HWIP_TYPE_COUNT,
};
enum aca_error_type {
ACA_ERROR_TYPE_INVALID = -1,
ACA_ERROR_TYPE_UE = 0,
ACA_ERROR_TYPE_CE,
ACA_ERROR_TYPE_DEFERRED,
ACA_ERROR_TYPE_COUNT
};
enum aca_smu_type {
ACA_SMU_TYPE_INVALID = -1,
ACA_SMU_TYPE_UE = 0,
ACA_SMU_TYPE_CE,
ACA_SMU_TYPE_COUNT,
};
struct aca_hwip {
int hwid;
int mcatype;
};
struct aca_bank {
enum aca_error_type aca_err_type;
enum aca_smu_type smu_err_type;
u64 regs[ACA_MAX_REGS_COUNT];
};
struct aca_bank_node {
struct aca_bank bank;
struct list_head node;
};
struct aca_banks {
int nr_banks;
struct list_head list;
};
struct aca_bank_info {
int die_id;
int socket_id;
int hwid;
int mcatype;
};
struct aca_bank_error {
struct list_head node;
struct aca_bank_info info;
u64 count;
};
struct aca_error {
struct list_head list;
struct mutex lock;
enum aca_error_type type;
int nr_errors;
};
struct aca_handle_manager {
struct list_head list;
int nr_handles;
};
struct aca_error_cache {
struct aca_error errors[ACA_ERROR_TYPE_COUNT];
};
struct aca_handle {
struct list_head node;
enum aca_hwip_type hwip;
struct amdgpu_device *adev;
struct aca_handle_manager *mgr;
struct aca_error_cache error_cache;
const struct aca_bank_ops *bank_ops;
struct device_attribute aca_attr;
char attr_name[64];
const char *name;
u32 mask;
void *data;
};
struct aca_bank_ops {
int (*aca_bank_parser)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data);
bool (*aca_bank_is_valid)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type,
void *data);
};
struct aca_smu_funcs {
int max_ue_bank_count;
int max_ce_bank_count;
int (*set_debug_mode)(struct amdgpu_device *adev, bool enable);
int (*get_valid_aca_count)(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count);
int (*get_valid_aca_bank)(struct amdgpu_device *adev, enum aca_smu_type type, int idx, struct aca_bank *bank);
int (*parse_error_code)(struct amdgpu_device *adev, struct aca_bank *bank);
};
struct amdgpu_aca {
struct aca_handle_manager mgr;
const struct aca_smu_funcs *smu_funcs;
atomic_t ue_update_flag;
bool is_enabled;
};
struct aca_info {
enum aca_hwip_type hwip;
const struct aca_bank_ops *bank_ops;
u32 mask;
};
int amdgpu_aca_init(struct amdgpu_device *adev);
void amdgpu_aca_fini(struct amdgpu_device *adev);
int amdgpu_aca_reset(struct amdgpu_device *adev);
void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs);
bool amdgpu_aca_is_enabled(struct amdgpu_device *adev);
int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info);
int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size);
int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle,
const char *name, const struct aca_info *aca_info, void *data);
void amdgpu_aca_remove_handle(struct aca_handle *handle);
int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle,
enum aca_error_type type, struct ras_err_data *err_data,
struct ras_query_context *qctx);
int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en);
void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root);
int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info,
enum aca_error_type type, u64 count);
#endif
+34 -34
View File
@@ -140,13 +140,15 @@ static struct amdgpu_acpi_priv {
* @atif: atif structure
* @function: the ATIF function to execute
* @params: ATIF function params
* @min_size: minimum size of the expected output buffer in bytes
*
* Executes the requested ATIF function (all asics).
* Returns a pointer to the acpi output buffer.
*/
static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif,
int function,
struct acpi_buffer *params)
struct acpi_buffer *params,
size_t min_size)
{
acpi_status status;
union acpi_object *obj;
@@ -189,6 +191,28 @@ static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif,
return NULL;
}
if (obj->buffer.length < sizeof(u16)) {
DRM_DEBUG_DRIVER("ATIF buffer too small to hold size field: %u\n",
obj->buffer.length);
kfree(obj);
return NULL;
}
if (obj->buffer.length < *(u16 *)obj->buffer.pointer) {
DRM_DEBUG_DRIVER("ATIF buffer length mismatch: reported %u, actual %u\n",
*(u16 *)obj->buffer.pointer,
obj->buffer.length);
kfree(obj);
return NULL;
}
if (*(u16 *)obj->buffer.pointer < min_size) {
DRM_DEBUG_DRIVER("ATIF buffer too small: expected %zu, got %u\n",
min_size, *(u16 *)obj->buffer.pointer);
kfree(obj);
return NULL;
}
return obj;
}
@@ -251,19 +275,14 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif)
size_t size;
int err = 0;
info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL);
info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL,
sizeof(output));
if (!info)
return -EIO;
memset(&output, 0, sizeof(output));
size = *(u16 *) info->buffer.pointer;
if (size < 12) {
DRM_INFO("ATIF buffer is too small: %zu\n", size);
err = -EINVAL;
goto out;
}
size = min(sizeof(output), size);
size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&output, info->buffer.pointer, size);
@@ -273,7 +292,6 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif)
amdgpu_atif_parse_notification(&atif->notifications, output.notification_mask);
amdgpu_atif_parse_functions(&atif->functions, output.function_bits);
out:
kfree(info);
return err;
}
@@ -299,20 +317,14 @@ int amdgpu_atif_get_notification_params(struct amdgpu_atif *atif)
int err = 0;
info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS,
NULL);
NULL, offsetof(struct atif_system_params, command_code));
if (!info) {
err = -EIO;
goto out;
}
size = *(u16 *) info->buffer.pointer;
if (size < 10) {
err = -EINVAL;
goto out;
}
memset(&params, 0, sizeof(params));
size = min(sizeof(params), size);
size = min(sizeof(params), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&params, info->buffer.pointer, size);
DRM_DEBUG_DRIVER("SYSTEM_PARAMS: mask = %#x, flags = %#x\n",
@@ -376,20 +388,14 @@ int amdgpu_atif_query_backlight_caps(struct amdgpu_atif *atif)
info = amdgpu_atif_call(atif,
ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS,
&params);
&params, offsetof(struct atif_qbtc_output, data_points));
if (!info) {
err = -EIO;
goto out;
}
size = *(u16 *) info->buffer.pointer;
if (size < 10) {
err = -EINVAL;
goto out;
}
memset(&characteristics, 0, sizeof(characteristics));
size = min(sizeof(characteristics), size);
size = min(sizeof(characteristics), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&characteristics, info->buffer.pointer, size);
atif->backlight_caps.caps_valid = true;
@@ -427,24 +433,18 @@ static int amdgpu_atif_get_sbios_requests(struct amdgpu_atif *atif,
int count = 0;
info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS,
NULL);
NULL, sizeof(*req));
if (!info)
return -EIO;
size = *(u16 *)info->buffer.pointer;
if (size < 0xd) {
count = -EINVAL;
goto out;
}
memset(req, 0, sizeof(*req));
size = min(sizeof(*req), size);
size = min(sizeof(*req), (size_t)*(u16 *)info->buffer.pointer);
memcpy(req, info->buffer.pointer, size);
DRM_DEBUG_DRIVER("SBIOS pending requests: %#x\n", req->pending);
count = hweight32(req->pending);
out:
kfree(info);
return count;
}
+151
View File
@@ -0,0 +1,151 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT
*
* Copyright 2026 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __AMDGPU_ACPI_H__
#define __AMDGPU_ACPI_H__
#include <linux/types.h>
#include <linux/mutex_types.h>
struct amdgpu_device;
struct acpi_device;
struct amdgpu_dm_backlight_caps;
#define MAX_UMA_OPTION_NAME 28
#define MAX_UMA_OPTION_ENTRIES 19
#define AMDGPU_UMA_FLAG_AUTO BIT(1)
#define AMDGPU_UMA_FLAG_CUSTOM BIT(0)
/* ATCS Device/Driver State */
#define AMDGPU_ATCS_PSC_DEV_STATE_D0 0
#define AMDGPU_ATCS_PSC_DEV_STATE_D3_HOT 3
#define AMDGPU_ATCS_PSC_DRV_STATE_OPR 0
#define AMDGPU_ATCS_PSC_DRV_STATE_NOT_OPR 1
enum amdgpu_ss {
AMDGPU_SS_DRV_LOAD,
AMDGPU_SS_DEV_D0,
AMDGPU_SS_DEV_D3,
AMDGPU_SS_DRV_UNLOAD
};
/**
* struct amdgpu_uma_carveout_option - single UMA carveout option
* @name: Name of the carveout option
* @memory_carved_mb: Amount of memory carved in MB
* @flags: ATCS flags supported by this option
*/
struct amdgpu_uma_carveout_option {
char name[MAX_UMA_OPTION_NAME];
uint32_t memory_carved_mb;
uint8_t flags;
};
/**
* struct amdgpu_uma_carveout_info - table of available UMA carveout options
* @num_entries: Number of available options
* @uma_option_index: The index of the option currently applied
* @update_lock: Lock to serialize changes to the option
* @entries: The array of carveout options
*/
struct amdgpu_uma_carveout_info {
uint8_t num_entries;
uint8_t uma_option_index;
struct mutex update_lock;
struct amdgpu_uma_carveout_option entries[MAX_UMA_OPTION_ENTRIES];
};
struct amdgpu_numa_info {
uint64_t size;
int pxm;
int nid;
};
#if defined(CONFIG_ACPI)
int amdgpu_acpi_init(struct amdgpu_device *adev);
void amdgpu_acpi_fini(struct amdgpu_device *adev);
bool amdgpu_acpi_is_pcie_performance_request_supported(struct amdgpu_device *adev);
bool amdgpu_acpi_is_power_shift_control_supported(void);
bool amdgpu_acpi_is_set_uma_allocation_size_supported(void);
int amdgpu_acpi_pcie_performance_request(struct amdgpu_device *adev,
u8 perf_req, bool advertise);
int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
u8 dev_state, bool drv_state);
int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
enum amdgpu_ss ss_state);
int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type);
int amdgpu_acpi_pcie_notify_device_ready(struct amdgpu_device *adev);
int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev, u64 *tmr_offset,
u64 *tmr_size);
int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev, int xcc_id,
struct amdgpu_numa_info *numa_info);
void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps);
bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev);
void amdgpu_acpi_detect(void);
void amdgpu_acpi_release(void);
#else
static inline int amdgpu_acpi_init(struct amdgpu_device *adev) { return 0; }
static inline int amdgpu_acpi_get_tmr_info(struct amdgpu_device *adev,
u64 *tmr_offset, u64 *tmr_size)
{
return -EINVAL;
}
static inline int amdgpu_acpi_get_mem_info(struct amdgpu_device *adev,
int xcc_id,
struct amdgpu_numa_info *numa_info)
{
return -EINVAL;
}
static inline void amdgpu_acpi_fini(struct amdgpu_device *adev) { }
static inline bool amdgpu_acpi_should_gpu_reset(struct amdgpu_device *adev) { return false; }
static inline void amdgpu_acpi_detect(void) { }
static inline void amdgpu_acpi_release(void) { }
static inline bool amdgpu_acpi_is_power_shift_control_supported(void) { return false; }
static inline bool amdgpu_acpi_is_set_uma_allocation_size_supported(void) { return false; }
static inline int amdgpu_acpi_power_shift_control(struct amdgpu_device *adev,
u8 dev_state, bool drv_state) { return 0; }
static inline int amdgpu_acpi_smart_shift_update(struct amdgpu_device *adev,
enum amdgpu_ss ss_state)
{
return 0;
}
static inline int amdgpu_acpi_set_uma_allocation_size(struct amdgpu_device *adev, u8 index, u8 type)
{
return -EINVAL;
}
static inline void amdgpu_acpi_get_backlight_caps(struct amdgpu_dm_backlight_caps *caps) { }
#endif
#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev);
bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev);
#else
static inline bool amdgpu_acpi_is_s0ix_active(struct amdgpu_device *adev) { return false; }
static inline bool amdgpu_acpi_is_s3_active(struct amdgpu_device *adev) { return false; }
#endif
#if defined(CONFIG_DRM_AMD_ISP)
int amdgpu_acpi_get_isp4_dev(struct acpi_device **dev);
#endif
#endif /* __AMDGPU_ACPI_H__ */
+17 -1
View File
@@ -39,6 +39,7 @@
#if IS_ENABLED(CONFIG_HSA_AMD)
#include "kfd_priv.h"
#endif
#include "kfd_svm.h"
/* Total memory size in system memory and all GPU VRAM. Used to
* estimate worst case amount of memory to reserve for page tables
@@ -711,7 +712,8 @@ int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev,
goto err;
}
ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, &job, 0);
ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, 0, GFP_KERNEL,
&job);
if (ret)
goto err;
@@ -957,3 +959,17 @@ int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id,
return r;
}
/* Reset an MES queue */
int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev,
uint32_t node_id,
int queue_type,
int pipe, int queue,
unsigned int db)
{
if (!adev->kfd.init_complete)
return 0;
return kgd2kfd_reset_mes_queue(adev->kfd.dev, node_id, queue_type,
pipe, queue, db);
}
+24 -6
View File
@@ -99,7 +99,6 @@ struct amdgpu_amdkfd_fence {
struct mm_struct *mm;
spinlock_t lock;
char timeline_name[TASK_COMM_LEN];
struct svm_range_bo *svm_bo;
uint16_t context_id;
};
@@ -194,7 +193,6 @@ int amdgpu_queue_mask_bit_to_set_resource_bit(struct amdgpu_device *adev,
struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
struct mm_struct *mm,
struct svm_range_bo *svm_bo,
u16 context_id);
int amdgpu_amdkfd_drm_client_create(struct amdgpu_device *adev);
@@ -210,6 +208,7 @@ int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni,
int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo,
uint32_t domain,
struct dma_fence *fence);
int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms);
#else
static inline
bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm)
@@ -241,6 +240,11 @@ int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo,
{
return 0;
}
static inline
int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms)
{
return -EOPNOTSUPP;
}
#endif
/* Shared API */
int amdgpu_amdkfd_alloc_kernel_mem(struct amdgpu_device *adev, size_t size,
@@ -275,7 +279,11 @@ int amdgpu_amdkfd_stop_sched(struct amdgpu_device *adev, uint32_t node_id);
int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id,
bool core_override_enable, bool reg_override_enable, bool perfmon_override_enable);
bool amdgpu_amdkfd_compute_active(struct amdgpu_device *adev, uint32_t node_id);
int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev,
uint32_t node_id,
int queue_type,
int pipe, int queue,
unsigned int db);
/* Read user wptr from a specified user address space with page fault
* disabled. The memory must be pinned and mapped to the hardware when
@@ -326,9 +334,9 @@ int amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu(
int amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv);
int amdgpu_amdkfd_gpuvm_sync_memory(
struct amdgpu_device *adev, struct kgd_mem *mem, bool intr);
int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
void **kptr, uint64_t *size);
void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem);
int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr,
u64 *size, u32 domain);
void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem);
int amdgpu_amdkfd_map_gtt_bo_to_gart(struct amdgpu_bo *bo, struct amdgpu_bo **bo_gart);
@@ -446,6 +454,9 @@ bool kgd2kfd_vmfault_fast_path(struct amdgpu_device *adev, struct amdgpu_iv_entr
bool retry_fault);
void kgd2kfd_lock_kfd(void);
void kgd2kfd_teardown_processes(struct amdgpu_device *adev);
int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
int queue_type, int pipe, int queue,
unsigned int db);
#else
static inline int kgd2kfd_init(void)
@@ -576,5 +587,12 @@ static inline void kgd2kfd_teardown_processes(struct amdgpu_device *adev)
{
}
static inline int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
int queue_type, int pipe, int queue,
unsigned int db)
{
return 0;
}
#endif
#endif /* AMDGPU_AMDKFD_H_INCLUDED */
@@ -62,7 +62,6 @@ static atomic_t fence_seq = ATOMIC_INIT(0);
struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
struct mm_struct *mm,
struct svm_range_bo *svm_bo,
u16 context_id)
{
struct amdgpu_amdkfd_fence *fence;
@@ -76,7 +75,6 @@ struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context,
fence->mm = mm;
get_task_comm(fence->timeline_name, current);
spin_lock_init(&fence->lock);
fence->svm_bo = svm_bo;
fence->context_id = context_id;
dma_fence_init(&fence->base, &amdkfd_fence_ops, &fence->lock,
context, atomic_inc_return(&fence_seq));
@@ -128,17 +126,8 @@ static bool amdkfd_fence_enable_signaling(struct dma_fence *f)
if (dma_fence_is_signaled(f))
return true;
/* if fence->svm_bo is NULL, means this fence is created through
* init_kfd_vm() or amdgpu_amdkfd_gpuvm_restore_process_bos().
* Therefore, this fence is amdgpu_amdkfd_fence->eviction_fence.
*/
if (!fence->svm_bo) {
if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, fence->context_id, f))
return true;
} else {
if (!svm_range_schedule_evict_svm_bo(fence))
return true;
}
if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, fence->context_id, f))
return true;
return false;
}
@@ -172,7 +161,6 @@ static void amdkfd_fence_release(struct dma_fence *f)
*
* Check if @mm is same as that of the fence @f, if same return TRUE else
* return FALSE.
* For svm bo, which support vram overcommitment, always return FALSE.
*/
bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm)
{
@@ -180,7 +168,7 @@ bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm)
if (!fence)
return false;
else if (fence->mm == mm && !fence->svm_bo)
else if (fence->mm == mm)
return true;
return false;
@@ -530,6 +530,66 @@ static uint32_t kgd_v9_4_3_ptl_ctrl(struct amdgpu_device *adev,
ptl_state, fmt1, fmt2);
}
static int kgd_gfx_v9_4_3_hqd_sdma_get_counter(struct amdgpu_device *adev,
void *mqd, uint32_t num_sdma_queues_per_eng,
uint64_t *val)
{
struct v9_sdma_mqd *m = get_sdma_mqd(mqd);
uint32_t sdma_rlc_reg_offset = 0;
uint32_t sdma_rlc_rb_cntl;
uint32_t engine_id, queue_id;
uint32_t engines = adev->sdma.num_instances;
uint32_t sdma_rlcx_rb_base, sdma_rlcx_rb_base_hi;
bool found = false;
if (!m)
return -EINVAL;
if (((amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) ||
amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4)) &&
adev->gfx.mec_fw_version < 194) ||
(amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0) &&
adev->gfx.mec_fw_version < 44)) {
pr_warn_once("MEC FW doesn't support SDMA counter!\n");
return -EOPNOTSUPP;
}
/* SDMA doesn't support over-subscription, there must be
* a HQD associated with a MQD, so found must be true in
* the finding loop.
*/
for (engine_id = 0; engine_id < engines && !found; engine_id++) {
for (queue_id = 0; queue_id < num_sdma_queues_per_eng; queue_id++) {
sdma_rlc_reg_offset = get_sdma_rlc_reg_offset(adev,
engine_id, queue_id);
sdma_rlcx_rb_base = RREG32(sdma_rlc_reg_offset +
regSDMA_RLC0_RB_BASE);
sdma_rlcx_rb_base_hi = RREG32(sdma_rlc_reg_offset +
regSDMA_RLC0_RB_BASE_HI);
if (m->sdmax_rlcx_rb_base == sdma_rlcx_rb_base &&
m->sdmax_rlcx_rb_base_hi == sdma_rlcx_rb_base_hi) {
found = true;
break;
}
}
}
sdma_rlc_rb_cntl = RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_RB_CNTL);
/* Read sdma activity counter from utilization register
* if hw queue is enabled, otherwise read from MQD.
*/
if (sdma_rlc_rb_cntl & SDMA_RLC0_RB_CNTL__RB_ENABLE_MASK)
*val = (uint64_t)RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_HI) << 32 |
RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_LO);
else
*val = (uint64_t)m->sdmax_rlcx_utilization_hi << 32 |
m->sdmax_rlcx_utilization_lo;
return 0;
}
const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = {
.program_sh_mem_settings = kgd_gfx_v9_program_sh_mem_settings,
.set_pasid_vmid_mapping = kgd_gfx_v9_4_3_set_pasid_vmid_mapping,
@@ -566,5 +626,6 @@ const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = {
.hqd_get_pq_addr = kgd_gfx_v9_hqd_get_pq_addr,
.hqd_reset = kgd_gfx_v9_hqd_reset,
.hqd_sdma_get_doorbell = kgd_gfx_v9_4_3_hqd_sdma_get_doorbell,
.ptl_ctrl = kgd_v9_4_3_ptl_ctrl
.ptl_ctrl = kgd_v9_4_3_ptl_ctrl,
.hqd_sdma_get_counter = kgd_gfx_v9_4_3_hqd_sdma_get_counter
};
@@ -139,7 +139,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev,
regSDMA1_QUEUE0_RB_CNTL) - regSDMA0_QUEUE0_RB_CNTL;
break;
default:
BUG();
WARN(1, "Invalid SDMA engine id %d\n", engine_id);
break;
}
sdma_rlc_reg_offset = sdma_engine_reg_base
@@ -90,7 +90,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev,
regSDMA1_QUEUE0_RB_CNTL) - regSDMA0_QUEUE0_RB_CNTL;
break;
default:
BUG();
WARN(1, "Invalid SDMA engine id %d\n", engine_id);
break;
}
sdma_rlc_reg_offset = sdma_engine_reg_base
@@ -93,7 +93,8 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev,
regSDMA1_SDMA_QUEUE0_RB_CNTL) - regSDMA0_SDMA_QUEUE0_RB_CNTL;
break;
default:
BUG();
WARN(1, "Invalid SDMA engine id %d\n", engine_id);
break;
}
sdma_rlc_reg_offset = sdma_engine_reg_base
@@ -1423,7 +1423,7 @@ static int init_kfd_vm(struct amdgpu_vm *vm, void **process_info,
info->eviction_fence =
amdgpu_amdkfd_fence_create(dma_fence_context_alloc(1),
current->mm,
NULL, process->context_id);
process->context_id);
if (!info->eviction_fence) {
pr_err("Failed to create eviction fence\n");
ret = -ENOMEM;
@@ -2271,11 +2271,14 @@ err_reserve_bo_failed:
return ret;
}
/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Map a GTT BO for kernel CPU access
/** amdgpu_amdkfd_gpuvm_map_bo_to_kernel() - Map GTT or VRAM BO for kernel CPU access
*
* @mem: Buffer object to be mapped for CPU access
* @kptr[out]: pointer in kernel CPU address space
* @size[out]: size of the buffer
* @domain[IN]: domain for pinning (AMDGPU_GEM_DOMAIN_GTT, AMDGPU_GEM_DOMAIN_VRAM,
* or their combination to let the driver choose). CPU visibility is
* automatically enforced by amdgpu_bo_pin()
*
* Pins the BO and maps it for kernel CPU access. The eviction fence is removed
* from the BO, since pinned BOs cannot be evicted. The bo must remain on the
@@ -2284,8 +2287,8 @@ err_reserve_bo_failed:
*
* Return: 0 on success, error code on failure
*/
int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
void **kptr, uint64_t *size)
int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr,
u64 *size, u32 domain)
{
int ret;
struct amdgpu_bo *bo = mem->bo;
@@ -2295,6 +2298,11 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
return -EINVAL;
}
if (!(domain & (AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM))) {
pr_debug("Invalid domain 0x%x for kernel mapping\n", domain);
return -EINVAL;
}
mutex_lock(&mem->process_info->lock);
ret = amdgpu_bo_reserve(bo, true);
@@ -2303,7 +2311,7 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
goto bo_reserve_failed;
}
ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);
ret = amdgpu_bo_pin(bo, domain);
if (ret) {
pr_err("Failed to pin bo. ret %d\n", ret);
goto pin_failed;
@@ -2336,7 +2344,7 @@ bo_reserve_failed:
return ret;
}
/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Unmap a GTT BO for kernel CPU access
/** amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel() - Unmap GTT or VRAM BO for kernel CPU access
*
* @mem: Buffer object to be unmapped for CPU access
*
@@ -2344,7 +2352,7 @@ bo_reserve_failed:
* eviction fence, so this function should only be used for cleanup before the
* BO is destroyed.
*/
void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem)
void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem)
{
struct amdgpu_bo *bo = mem->bo;
@@ -3085,7 +3093,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu *
amdgpu_amdkfd_fence_create(
process_info->eviction_fence->base.context,
process_info->eviction_fence->mm,
NULL, process_info->context_id);
process_info->context_id);
if (!new_fence) {
pr_err("Failed to create eviction fence\n");
+1 -1
View File
@@ -1923,7 +1923,7 @@ int amdgpu_atombios_init(struct amdgpu_device *adev)
atom_card_info->pll_read = cail_pll_read;
atom_card_info->pll_write = cail_pll_write;
adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios);
adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios, adev->bios_size);
if (!adev->mode_info.atom_context) {
amdgpu_atombios_fini(adev);
return -ENOMEM;
@@ -917,8 +917,6 @@ int amdgpu_atomfirmware_get_clock_info(struct amdgpu_device *adev)
(crev != 6)) {
spll->reference_freq = le32_to_cpu(gfx_info->v22.rlc_gpu_timer_refclk);
ret = 0;
} else {
BUG();
}
}
}
@@ -89,6 +89,15 @@ bool amdgpu_is_atpx_hybrid(void)
return amdgpu_atpx_priv.atpx.is_hybrid;
}
static bool amdgpu_atpx_buffer_validate(const union acpi_object *obj,
size_t min_size)
{
return obj && obj->type == ACPI_TYPE_BUFFER &&
obj->buffer.length >= sizeof(u16) &&
obj->buffer.length >= *(u16 *)obj->buffer.pointer &&
*(u16 *)obj->buffer.pointer >= min_size;
}
/**
* amdgpu_atpx_call - call an ATPX method
*
@@ -179,15 +188,15 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
if (!info)
return -EIO;
memset(&output, 0, sizeof(output));
size = *(u16 *) info->buffer.pointer;
if (size < 10) {
pr_err("ATPX buffer is too small: %zu\n", size);
if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) {
pr_err("Invalid ATPX GET_PX_PARAMETERS response\n");
kfree(info);
return -EINVAL;
}
size = min(sizeof(output), size);
memset(&output, 0, sizeof(output));
size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&output, info->buffer.pointer, size);
@@ -258,15 +267,15 @@ static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)
if (!info)
return -EIO;
memset(&output, 0, sizeof(output));
size = *(u16 *) info->buffer.pointer;
if (size < 8) {
pr_err("ATPX buffer is too small: %zu\n", size);
if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) {
pr_err("Invalid ATPX VERIFY_INTERFACE response\n");
err = -EINVAL;
goto out;
}
size = min(sizeof(output), size);
memset(&output, 0, sizeof(output));
size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&output, info->buffer.pointer, size);
+8 -2
View File
@@ -296,8 +296,14 @@ static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
}
obj = (union acpi_object *)buffer.pointer;
memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length);
len = obj->buffer.length;
if (!obj || obj->type != ACPI_TYPE_BUFFER) {
DRM_ERROR("ATRM returned an invalid object\n");
kfree(buffer.pointer);
return -EINVAL;
}
len = min_t(size_t, obj->buffer.length, len);
memcpy(bios+offset, obj->buffer.pointer, len);
kfree(buffer.pointer);
return len;
}
+75 -109
View File
@@ -38,14 +38,6 @@
#define AMDGPU_BO_LIST_NUM_BUCKETS (AMDGPU_BO_LIST_MAX_PRIORITY + 1)
#define AMDGPU_BO_LIST_MAX_ENTRIES (128 * 1024)
static void amdgpu_bo_list_free_rcu(struct rcu_head *rcu)
{
struct amdgpu_bo_list *list = container_of(rcu, struct amdgpu_bo_list,
rhead);
mutex_destroy(&list->bo_list_mutex);
kvfree(list);
}
static void amdgpu_bo_list_free(struct kref *ref)
{
struct amdgpu_bo_list *list = container_of(ref, struct amdgpu_bo_list,
@@ -54,7 +46,8 @@ static void amdgpu_bo_list_free(struct kref *ref)
amdgpu_bo_list_for_each_entry(e, list)
amdgpu_bo_unref(&e->bo);
call_rcu(&list->rhead, amdgpu_bo_list_free_rcu);
kvfree(list);
}
static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b)
@@ -66,9 +59,9 @@ static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b)
return (int)a->priority - (int)b->priority;
}
int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
struct drm_amdgpu_bo_list_entry *info,
size_t num_entries, struct amdgpu_bo_list **result)
struct amdgpu_bo_list *
amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
struct drm_amdgpu_bo_list_entry *info, size_t num_entries)
{
unsigned last_entry = 0, first_userptr = num_entries;
struct amdgpu_bo_list_entry *array;
@@ -79,7 +72,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
list = kvzalloc_flex(*list, entries, num_entries);
if (!list)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
kref_init(&list->refcount);
@@ -134,9 +127,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
trace_amdgpu_cs_bo_status(list->num_entries, total_size);
mutex_init(&list->bo_list_mutex);
*result = list;
return 0;
return list;
error_free:
for (i = 0; i < last_entry; ++i)
@@ -144,150 +135,125 @@ error_free:
for (i = first_userptr; i < num_entries; ++i)
amdgpu_bo_unref(&array[i].bo);
kvfree(list);
return r;
return ERR_PTR(r);
}
static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id)
struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id)
{
struct amdgpu_bo_list *list;
mutex_lock(&fpriv->bo_list_lock);
list = idr_remove(&fpriv->bo_list_handles, id);
mutex_unlock(&fpriv->bo_list_lock);
xa_lock(&fpriv->bo_list_handles);
list = xa_load(&fpriv->bo_list_handles, id);
if (list)
kref_put(&list->refcount, amdgpu_bo_list_free);
}
kref_get(&list->refcount);
else
list = ERR_PTR(-ENOENT);
xa_unlock(&fpriv->bo_list_handles);
int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id,
struct amdgpu_bo_list **result)
{
rcu_read_lock();
*result = idr_find(&fpriv->bo_list_handles, id);
if (*result && kref_get_unless_zero(&(*result)->refcount)) {
rcu_read_unlock();
return 0;
}
rcu_read_unlock();
*result = NULL;
return -ENOENT;
return list;
}
void amdgpu_bo_list_put(struct amdgpu_bo_list *list)
{
kref_put(&list->refcount, amdgpu_bo_list_free);
if (list)
kref_put(&list->refcount, amdgpu_bo_list_free);
}
int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in,
struct drm_amdgpu_bo_list_entry **info_param)
struct drm_amdgpu_bo_list_entry *
amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in)
{
const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry);
const void __user *uptr = u64_to_user_ptr(in->bo_info_ptr);
const uint32_t bo_info_size = in->bo_info_size;
const uint32_t bo_number = in->bo_number;
struct drm_amdgpu_bo_list_entry *info;
if (bo_number > AMDGPU_BO_LIST_MAX_ENTRIES)
return -EINVAL;
return ERR_PTR(-EINVAL);
/* copy the handle array from userspace to a kernel buffer */
if (likely(info_size == bo_info_size)) {
info = vmemdup_array_user(uptr, bo_number, info_size);
if (IS_ERR(info))
return PTR_ERR(info);
} else {
const uint32_t bytes = min(bo_info_size, info_size);
unsigned i;
if (in->bo_info_size != sizeof(struct drm_amdgpu_bo_list_entry))
return ERR_PTR(-EINVAL);
info = kvmalloc_array(bo_number, info_size, GFP_KERNEL);
if (!info)
return -ENOMEM;
memset(info, 0, bo_number * info_size);
for (i = 0; i < bo_number; ++i, uptr += bo_info_size) {
if (copy_from_user(&info[i], uptr, bytes)) {
kvfree(info);
return -EFAULT;
}
}
}
*info_param = info;
return 0;
return vmemdup_array_user(uptr, bo_number,
sizeof(struct drm_amdgpu_bo_list_entry));
}
int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp)
{
struct amdgpu_device *adev = drm_to_adev(dev);
struct amdgpu_fpriv *fpriv = filp->driver_priv;
struct amdgpu_device *adev = drm_to_adev(dev);
struct amdgpu_bo_list *list, *prev, *curr;
union drm_amdgpu_bo_list *args = data;
uint32_t handle = args->in.list_handle;
struct drm_amdgpu_bo_list_entry *info = NULL;
struct amdgpu_bo_list *list, *old;
struct drm_amdgpu_bo_list_entry *info;
int r;
r = amdgpu_bo_create_list_entry_array(&args->in, &info);
if (r)
return r;
switch (args->in.operation) {
case AMDGPU_BO_LIST_OP_CREATE:
r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number,
&list);
if (r)
goto error_free;
case AMDGPU_BO_LIST_OP_UPDATE:
info = amdgpu_bo_create_list_entry_array(&args->in);
if (IS_ERR(info))
return PTR_ERR(info);
mutex_lock(&fpriv->bo_list_lock);
r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL);
mutex_unlock(&fpriv->bo_list_lock);
if (r < 0) {
goto error_put_list;
}
list = amdgpu_bo_list_create(adev, filp, info,
args->in.bo_number);
kvfree(info);
if (IS_ERR(list))
return PTR_ERR(list);
handle = r;
break;
case AMDGPU_BO_LIST_OP_DESTROY:
amdgpu_bo_list_destroy(fpriv, handle);
list = xa_erase(&fpriv->bo_list_handles, handle);
amdgpu_bo_list_put(list);
handle = 0;
break;
case AMDGPU_BO_LIST_OP_UPDATE:
r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number,
&list);
if (r)
goto error_free;
mutex_lock(&fpriv->bo_list_lock);
old = idr_replace(&fpriv->bo_list_handles, list, handle);
mutex_unlock(&fpriv->bo_list_lock);
if (IS_ERR(old)) {
r = PTR_ERR(old);
goto error_put_list;
}
amdgpu_bo_list_put(old);
break;
default:
r = -EINVAL;
goto error_free;
return -EINVAL;
};
switch (args->in.operation) {
case AMDGPU_BO_LIST_OP_CREATE:
r = xa_alloc(&fpriv->bo_list_handles, &handle, list,
xa_limit_32b, GFP_KERNEL);
if (r)
goto error_put_list;
break;
case AMDGPU_BO_LIST_OP_UPDATE:
curr = xa_load(&fpriv->bo_list_handles, handle);
if (!curr) {
r = -ENOENT;
goto error_put_list;
}
prev = xa_cmpxchg(&fpriv->bo_list_handles, handle, curr, list,
GFP_KERNEL);
if (xa_is_err(prev)) {
r = xa_err(prev);
goto error_put_list;
} else if (prev != curr) {
r = -ENOENT;
goto error_put_list;
}
amdgpu_bo_list_put(curr);
break;
case AMDGPU_BO_LIST_OP_DESTROY:
default:
/* Handled above. */
break;
}
memset(args, 0, sizeof(*args));
args->out.list_handle = handle;
kvfree(info);
return 0;
error_put_list:
amdgpu_bo_list_put(list);
error_free:
kvfree(info);
return r;
}
+8 -14
View File
@@ -43,7 +43,6 @@ struct amdgpu_bo_list_entry {
};
struct amdgpu_bo_list {
struct rcu_head rhead;
struct kref refcount;
struct amdgpu_bo *gds_obj;
struct amdgpu_bo *gws_obj;
@@ -51,24 +50,19 @@ struct amdgpu_bo_list {
unsigned first_userptr;
unsigned num_entries;
/* Protect access during command submission.
*/
struct mutex bo_list_mutex;
struct amdgpu_bo_list_entry entries[] __counted_by(num_entries);
};
int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id,
struct amdgpu_bo_list **result);
struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id);
void amdgpu_bo_list_put(struct amdgpu_bo_list *list);
int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in,
struct drm_amdgpu_bo_list_entry **info_param);
struct drm_amdgpu_bo_list_entry *
amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in);
int amdgpu_bo_list_create(struct amdgpu_device *adev,
struct drm_file *filp,
struct drm_amdgpu_bo_list_entry *info,
size_t num_entries,
struct amdgpu_bo_list **list);
struct amdgpu_bo_list *
amdgpu_bo_list_create(struct amdgpu_device *adev,
struct drm_file *filp,
struct drm_amdgpu_bo_list_entry *info,
size_t num_entries);
#define amdgpu_bo_list_for_each_entry(e, list) \
for (e = list->entries; \
+29 -33
View File
@@ -76,10 +76,9 @@ static uint32_t amdgpu_cgs_read_ind_register(struct cgs_device *cgs_device,
DRM_ERROR("audio endpt register access not implemented.\n");
return 0;
default:
BUG();
WARN(1, "Invalid indirect register space");
return 0;
}
WARN(1, "Invalid indirect register space");
return 0;
}
static void amdgpu_cgs_write_ind_register(struct cgs_device *cgs_device,
@@ -104,9 +103,8 @@ static void amdgpu_cgs_write_ind_register(struct cgs_device *cgs_device,
DRM_ERROR("audio endpt register access not implemented.\n");
return;
default:
BUG();
WARN(1, "Invalid indirect register space");
}
WARN(1, "Invalid indirect register space");
}
static uint32_t fw_type_convert(struct cgs_device *cgs_device, uint32_t fw_type)
@@ -241,7 +239,7 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
info->fw_version = amdgpu_get_firmware_version(cgs_device, type);
info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);
} else {
char fw_name[30] = {0};
const char *fw_name = NULL;
int err = 0;
uint32_t ucode_size;
uint32_t ucode_start_address;
@@ -257,17 +255,17 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
(adev->pdev->revision == 0x81) ||
(adev->pdev->device == 0x665f)) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/bonaire_k_smc.bin");
fw_name = "bonaire_k_smc.bin";
} else {
strscpy(fw_name, "amdgpu/bonaire_smc.bin");
fw_name = "bonaire_smc.bin";
}
break;
case CHIP_HAWAII:
if (adev->pdev->revision == 0x80) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/hawaii_k_smc.bin");
fw_name = "hawaii_k_smc.bin";
} else {
strscpy(fw_name, "amdgpu/hawaii_smc.bin");
fw_name = "hawaii_smc.bin";
}
break;
case CHIP_TOPAZ:
@@ -277,76 +275,76 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD1)) ||
((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD3))) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/topaz_k_smc.bin");
fw_name = "topaz_k_smc.bin";
} else
strscpy(fw_name, "amdgpu/topaz_smc.bin");
fw_name = "topaz_smc.bin";
break;
case CHIP_TONGA:
if (((adev->pdev->device == 0x6939) && (adev->pdev->revision == 0xf1)) ||
((adev->pdev->device == 0x6938) && (adev->pdev->revision == 0xf1))) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/tonga_k_smc.bin");
fw_name = "tonga_k_smc.bin";
} else
strscpy(fw_name, "amdgpu/tonga_smc.bin");
fw_name = "tonga_smc.bin";
break;
case CHIP_FIJI:
strscpy(fw_name, "amdgpu/fiji_smc.bin");
fw_name = "fiji_smc.bin";
break;
case CHIP_POLARIS11:
if (type == CGS_UCODE_ID_SMU) {
if (ASICID_IS_P21(adev->pdev->device, adev->pdev->revision)) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/polaris11_k_smc.bin");
fw_name = "polaris11_k_smc.bin";
} else if (ASICID_IS_P31(adev->pdev->device, adev->pdev->revision)) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/polaris11_k2_smc.bin");
fw_name = "polaris11_k2_smc.bin";
} else {
strscpy(fw_name, "amdgpu/polaris11_smc.bin");
fw_name = "polaris11_smc.bin";
}
} else if (type == CGS_UCODE_ID_SMU_SK) {
strscpy(fw_name, "amdgpu/polaris11_smc_sk.bin");
fw_name = "polaris11_smc_sk.bin";
}
break;
case CHIP_POLARIS10:
if (type == CGS_UCODE_ID_SMU) {
if (ASICID_IS_P20(adev->pdev->device, adev->pdev->revision)) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/polaris10_k_smc.bin");
fw_name = "polaris10_k_smc.bin";
} else if (ASICID_IS_P30(adev->pdev->device, adev->pdev->revision)) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/polaris10_k2_smc.bin");
fw_name = "polaris10_k2_smc.bin";
} else {
strscpy(fw_name, "amdgpu/polaris10_smc.bin");
fw_name = "polaris10_smc.bin";
}
} else if (type == CGS_UCODE_ID_SMU_SK) {
strscpy(fw_name, "amdgpu/polaris10_smc_sk.bin");
fw_name = "polaris10_smc_sk.bin";
}
break;
case CHIP_POLARIS12:
if (ASICID_IS_P23(adev->pdev->device, adev->pdev->revision)) {
info->is_kicker = true;
strscpy(fw_name, "amdgpu/polaris12_k_smc.bin");
fw_name = "polaris12_k_smc.bin";
} else {
strscpy(fw_name, "amdgpu/polaris12_smc.bin");
fw_name = "polaris12_smc.bin";
}
break;
case CHIP_VEGAM:
strscpy(fw_name, "amdgpu/vegam_smc.bin");
fw_name = "vegam_smc.bin";
break;
case CHIP_VEGA10:
if ((adev->pdev->device == 0x687f) &&
((adev->pdev->revision == 0xc0) ||
(adev->pdev->revision == 0xc1) ||
(adev->pdev->revision == 0xc3)))
strscpy(fw_name, "amdgpu/vega10_acg_smc.bin");
fw_name = "vega10_acg_smc.bin";
else
strscpy(fw_name, "amdgpu/vega10_smc.bin");
fw_name = "vega10_smc.bin";
break;
case CHIP_VEGA12:
strscpy(fw_name, "amdgpu/vega12_smc.bin");
fw_name = "vega12_smc.bin";
break;
case CHIP_VEGA20:
strscpy(fw_name, "amdgpu/vega20_smc.bin");
fw_name = "vega20_smc.bin";
break;
default:
drm_err(adev_to_drm(adev), "SMC firmware not supported\n");
@@ -355,10 +353,8 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
err = amdgpu_ucode_request(adev, &adev->pm.fw,
AMDGPU_UCODE_REQUIRED,
"%s", fw_name);
"amdgpu/%s", fw_name);
if (err) {
drm_err(adev_to_drm(adev),
"Failed to load firmware \"%s\"\n", fw_name);
amdgpu_ucode_release(&adev->pm.fw);
return err;
}
+21 -114
View File
@@ -289,40 +289,6 @@ struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev,
return hdr;
}
int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev,
struct aca_bank *bank)
{
struct cper_hdr *fatal = NULL;
struct cper_sec_crashdump_reg_data reg_data = { 0 };
struct amdgpu_ring *ring = &adev->cper.ring_buf;
int ret;
fatal = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_FATAL, 1);
if (!fatal) {
dev_err(adev->dev, "fail to alloc cper entry for ue record\n");
return -ENOMEM;
}
reg_data.status_lo = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
reg_data.status_hi = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
reg_data.addr_lo = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
reg_data.addr_hi = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
reg_data.ipid_lo = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]);
reg_data.ipid_hi = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]);
reg_data.synd_lo = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]);
reg_data.synd_hi = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]);
amdgpu_cper_entry_fill_hdr(adev, fatal, AMDGPU_CPER_TYPE_FATAL, CPER_SEV_FATAL_UNCORRECTED);
ret = amdgpu_cper_entry_fill_fatal_section(adev, fatal, 0, reg_data);
if (ret)
return ret;
amdgpu_cper_ring_write(ring, fatal, fatal->record_length);
kfree(fatal);
return 0;
}
int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev)
{
struct cper_hdr *bp_threshold = NULL;
@@ -348,83 +314,6 @@ int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev)
return 0;
}
static enum cper_error_severity amdgpu_aca_err_type_to_cper_sev(struct amdgpu_device *adev,
enum aca_error_type aca_err_type)
{
switch (aca_err_type) {
case ACA_ERROR_TYPE_UE:
return CPER_SEV_FATAL_UNCORRECTED;
case ACA_ERROR_TYPE_CE:
return CPER_SEV_NON_FATAL_CORRECTED;
case ACA_ERROR_TYPE_DEFERRED:
return CPER_SEV_NON_FATAL_UNCORRECTED;
default:
dev_err(adev->dev, "Unknown ACA error type!\n");
return CPER_SEV_FATAL_UNCORRECTED;
}
}
int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev,
struct aca_banks *banks,
uint16_t bank_count)
{
struct cper_hdr *corrected = NULL;
enum cper_error_severity sev = CPER_SEV_NON_FATAL_CORRECTED;
struct amdgpu_ring *ring = &adev->cper.ring_buf;
uint32_t reg_data[CPER_ACA_REG_COUNT] = { 0 };
struct aca_bank_node *node;
struct aca_bank *bank;
uint32_t i = 0;
int ret;
corrected = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_RUNTIME, bank_count);
if (!corrected) {
dev_err(adev->dev, "fail to allocate cper entry for ce records\n");
return -ENOMEM;
}
/* Raise severity if any DE is detected in the ACA bank list */
list_for_each_entry(node, &banks->list, node) {
bank = &node->bank;
if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) {
sev = CPER_SEV_NON_FATAL_UNCORRECTED;
break;
}
}
amdgpu_cper_entry_fill_hdr(adev, corrected, AMDGPU_CPER_TYPE_RUNTIME, sev);
/* Combine CE and DE in cper record */
list_for_each_entry(node, &banks->list, node) {
bank = &node->bank;
reg_data[CPER_ACA_REG_CTL_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CTL]);
reg_data[CPER_ACA_REG_CTL_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CTL]);
reg_data[CPER_ACA_REG_STATUS_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
reg_data[CPER_ACA_REG_STATUS_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
reg_data[CPER_ACA_REG_ADDR_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
reg_data[CPER_ACA_REG_ADDR_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
reg_data[CPER_ACA_REG_MISC0_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_MISC0]);
reg_data[CPER_ACA_REG_MISC0_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_MISC0]);
reg_data[CPER_ACA_REG_CONFIG_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CONFIG]);
reg_data[CPER_ACA_REG_CONFIG_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CONFIG]);
reg_data[CPER_ACA_REG_IPID_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]);
reg_data[CPER_ACA_REG_IPID_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]);
reg_data[CPER_ACA_REG_SYND_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]);
reg_data[CPER_ACA_REG_SYND_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]);
ret = amdgpu_cper_entry_fill_runtime_section(adev, corrected, i++,
amdgpu_aca_err_type_to_cper_sev(adev, bank->aca_err_type),
reg_data, CPER_ACA_REG_COUNT);
if (ret)
return ret;
}
amdgpu_cper_ring_write(ring, corrected, corrected->record_length);
kfree(corrected);
return 0;
}
static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos)
{
char signature[CPER_SIGNATURE_SZ];
@@ -592,8 +481,7 @@ int amdgpu_cper_init(struct amdgpu_device *adev)
if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_cper_en(adev))
return 0;
else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev) &&
!amdgpu_aca_is_enabled(adev))
else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev))
return 0;
r = amdgpu_cper_ring_init(adev);
@@ -610,9 +498,28 @@ int amdgpu_cper_init(struct amdgpu_device *adev)
return 0;
}
int amdgpu_cper_deferred_init(struct amdgpu_device *adev)
{
int r;
if (adev->cper.enabled)
return 0;
r = amdgpu_cper_init(adev);
if (r || !adev->cper.enabled)
return r;
#if defined(CONFIG_DEBUG_FS)
if (adev_to_drm(adev)->primary->debugfs_root)
amdgpu_debugfs_ring_init(adev, &adev->cper.ring_buf);
#endif
return 0;
}
int amdgpu_cper_fini(struct amdgpu_device *adev)
{
if (!amdgpu_aca_is_enabled(adev) && !amdgpu_sriov_ras_cper_en(adev))
if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_cper_en(adev))
return 0;
adev->cper.enabled = false;
+1 -8
View File
@@ -26,7 +26,6 @@
#define __AMDGPU_CPER_H__
#include "amd_cper.h"
#include "amdgpu_aca.h"
#define CPER_MAX_ALLOWED_COUNT 0x1000
#define CPER_MAX_RING_SIZE 0X100000
@@ -88,18 +87,12 @@ int amdgpu_cper_entry_fill_bad_page_threshold_section(struct amdgpu_device *adev
struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev,
enum amdgpu_cper_type type,
uint16_t section_count);
/* UE must be encoded into separated cper entries, 1 UE 1 cper */
int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev,
struct aca_bank *bank);
/* CEs and DEs are combined into 1 cper entry */
int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev,
struct aca_banks *banks,
uint16_t bank_count);
/* Bad page threshold is encoded into separated cper entry */
int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev);
void amdgpu_cper_ring_write(struct amdgpu_ring *ring,
void *src, int count);
int amdgpu_cper_init(struct amdgpu_device *adev);
int amdgpu_cper_deferred_init(struct amdgpu_device *adev);
int amdgpu_cper_fini(struct amdgpu_device *adev);
#endif
+32 -42
View File
@@ -160,24 +160,19 @@ static int amdgpu_cs_p1_bo_handles(struct amdgpu_cs_parser *p,
struct drm_amdgpu_bo_list_in *data)
{
struct drm_amdgpu_bo_list_entry *info;
int r;
struct amdgpu_bo_list *list;
r = amdgpu_bo_create_list_entry_array(data, &info);
if (r)
return r;
r = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number,
&p->bo_list);
if (r)
goto error_free;
info = amdgpu_bo_create_list_entry_array(data);
if (IS_ERR(info))
return PTR_ERR(info);
list = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number);
kvfree(info);
if (IS_ERR(list))
return PTR_ERR(list);
p->bo_list = list;
return 0;
error_free:
kvfree(info);
return r;
}
/* Copy the data from userspace and go over it the first time */
@@ -296,8 +291,8 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p,
for (i = 0; i < p->gang_size; ++i) {
ret = amdgpu_job_alloc(p->adev, vm, p->entities[i], vm,
num_ibs[i], &p->jobs[i],
p->filp->client_id);
num_ibs[i], p->filp->client_id,
GFP_KERNEL, &p->jobs[i]);
if (ret)
goto free_all_kdata;
switch (p->adev->enforce_isolation[fpriv->xcp_id]) {
@@ -875,6 +870,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
{
struct amdgpu_fpriv *fpriv = p->filp->driver_priv;
struct ttm_operation_ctx ctx = { true, false };
struct amdgpu_bo_list *list = NULL;
struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_bo_list_entry *e;
struct drm_gem_object *obj;
@@ -886,25 +882,24 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
if (p->bo_list)
return -EINVAL;
r = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle,
&p->bo_list);
if (r)
return r;
list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle);
} else if (!p->bo_list) {
/* Create a empty bo_list when no handle is provided */
r = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0,
&p->bo_list);
if (r)
return r;
list = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0);
}
mutex_lock(&p->bo_list->bo_list_mutex);
if (IS_ERR(list))
return PTR_ERR(list);
else if (list)
p->bo_list = list;
else
list = p->bo_list;
/* Get userptr backing pages. If pages are updated after registered
* in amdgpu_gem_userptr_ioctl(), amdgpu_cs_list_validate() will do
* amdgpu_ttm_backend_bind() to flush and invalidate new pages
*/
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
amdgpu_bo_list_for_each_userptr_entry(e, list) {
bool userpage_invalidated = false;
struct amdgpu_bo *bo = e->bo;
@@ -934,7 +929,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
if (unlikely(r))
goto out_free_user_pages;
amdgpu_bo_list_for_each_entry(e, p->bo_list) {
amdgpu_bo_list_for_each_entry(e, list) {
r = drm_exec_prepare_obj(&p->exec, &e->bo->tbo.base,
TTM_NUM_MOVE_FENCES + p->gang_size);
drm_exec_retry_on_contention(&p->exec);
@@ -953,7 +948,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
}
}
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
amdgpu_bo_list_for_each_userptr_entry(e, list) {
struct mm_struct *usermm;
usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm);
@@ -1006,17 +1001,15 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
p->bytes_moved_vis);
for (i = 0; i < p->gang_size; ++i)
amdgpu_job_set_resources(p->jobs[i], p->bo_list->gds_obj,
p->bo_list->gws_obj,
p->bo_list->oa_obj);
amdgpu_job_set_resources(p->jobs[i], list->gds_obj,
list->gws_obj, list->oa_obj);
return 0;
out_free_user_pages:
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
amdgpu_bo_list_for_each_userptr_entry(e, list) {
amdgpu_hmm_range_free(e->range);
e->range = NULL;
}
mutex_unlock(&p->bo_list->bo_list_mutex);
return r;
}
@@ -1153,7 +1146,8 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
if (fpriv->csa_va) {
bo_va = fpriv->csa_va;
BUG_ON(!bo_va);
if (!bo_va)
return -ENOMEM;
r = amdgpu_vm_bo_update(adev, bo_va, false);
if (r)
return r;
@@ -1385,7 +1379,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm);
mutex_unlock(&p->adev->notifier_lock);
mutex_unlock(&p->bo_list->bo_list_mutex);
return 0;
}
@@ -1472,28 +1465,25 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
r = amdgpu_cs_patch_jobs(&parser);
if (r)
goto error_backoff;
goto error_fini;
r = amdgpu_cs_vm_handling(&parser);
if (r)
goto error_backoff;
goto error_fini;
r = amdgpu_cs_sync_rings(&parser);
if (r)
goto error_backoff;
goto error_fini;
trace_amdgpu_cs_ibs(&parser);
r = amdgpu_cs_submit(&parser, data);
if (r)
goto error_backoff;
goto error_fini;
amdgpu_cs_parser_fini(&parser);
return 0;
error_backoff:
mutex_unlock(&parser.bo_list->bo_list_mutex);
error_fini:
amdgpu_cs_parser_fini(&parser);
return r;
+65 -150
View File
@@ -283,6 +283,8 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev,
if (!entity)
return res;
drm_sched_entity_destroy(&entity->entity);
for (i = 0; i < amdgpu_sched_jobs; ++i) {
res = ktime_add(res, amdgpu_ctx_fence_time(entity->fences[i]));
dma_fence_put(entity->fences[i]);
@@ -294,32 +296,20 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev,
return res;
}
static int amdgpu_ctx_get_stable_pstate(struct amdgpu_ctx *ctx,
u32 *stable_pstate)
static u32 amdgpu_get_stable_pstate(struct amdgpu_device *adev)
{
struct amdgpu_device *adev = ctx->mgr->adev;
enum amd_dpm_forced_level current_level;
current_level = amdgpu_dpm_get_performance_level(adev);
switch (current_level) {
switch (amdgpu_dpm_get_performance_level(adev)) {
case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_STANDARD;
break;
return AMDGPU_CTX_STABLE_PSTATE_STANDARD;
case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK;
break;
return AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK;
case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK;
break;
return AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK;
case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_PEAK;
break;
return AMDGPU_CTX_STABLE_PSTATE_PEAK;
default:
*stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE;
break;
return AMDGPU_CTX_STABLE_PSTATE_NONE;
}
return 0;
}
static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority,
@@ -383,9 +373,9 @@ static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
if (current_ctx && current_ctx != ctx)
return -EBUSY;
r = amdgpu_ctx_get_stable_pstate(ctx, &current_stable_pstate);
if (r || current_stable_pstate == stable_pstate)
return r;
current_stable_pstate = amdgpu_get_stable_pstate(adev);
if (current_stable_pstate == stable_pstate)
return 0;
r = amdgpu_dpm_force_performance_level(adev, level);
if (r)
@@ -416,7 +406,7 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
return r;
}
static void amdgpu_ctx_fini(struct kref *ref)
void amdgpu_ctx_fini(struct kref *ref)
{
struct amdgpu_ctx *ctx = container_of(ref, struct amdgpu_ctx, refcount);
struct amdgpu_ctx_mgr *mgr = ctx->mgr;
@@ -504,53 +494,26 @@ static int amdgpu_ctx_alloc(struct amdgpu_device *adev,
if (!ctx)
return -ENOMEM;
mutex_lock(&mgr->lock);
r = idr_alloc(&mgr->ctx_handles, ctx, 1, AMDGPU_VM_MAX_NUM_CTX, GFP_KERNEL);
if (r < 0) {
mutex_unlock(&mgr->lock);
r = amdgpu_ctx_init(mgr, priority, filp, ctx);
if (r) {
kfree(ctx);
return r;
}
*id = (uint32_t)r;
r = amdgpu_ctx_init(mgr, priority, filp, ctx);
if (r) {
idr_remove(&mgr->ctx_handles, *id);
*id = 0;
kfree(ctx);
}
mutex_unlock(&mgr->lock);
r = xa_alloc(&mgr->ctx_handles, id, ctx, xa_limit_32b, GFP_KERNEL);
if (r)
amdgpu_ctx_put(ctx);
return r;
}
static void amdgpu_ctx_do_release(struct kref *ref)
{
struct amdgpu_ctx *ctx;
u32 i, j;
ctx = container_of(ref, struct amdgpu_ctx, refcount);
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
if (!ctx->entities[i][j])
continue;
drm_sched_entity_destroy(&ctx->entities[i][j]->entity);
}
}
amdgpu_ctx_fini(ref);
}
static int amdgpu_ctx_free(struct amdgpu_fpriv *fpriv, uint32_t id)
{
struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
struct amdgpu_ctx *ctx;
mutex_lock(&mgr->lock);
ctx = idr_remove(&mgr->ctx_handles, id);
if (ctx)
kref_put(&ctx->refcount, amdgpu_ctx_do_release);
mutex_unlock(&mgr->lock);
ctx = xa_erase(&fpriv->ctx_mgr.ctx_handles, id);
amdgpu_ctx_put(ctx);
return ctx ? 0 : -EINVAL;
}
@@ -559,20 +522,12 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
union drm_amdgpu_ctx_out *out)
{
struct amdgpu_ctx *ctx;
struct amdgpu_ctx_mgr *mgr;
unsigned reset_counter;
if (!fpriv)
ctx = amdgpu_ctx_get(fpriv, id);
if (!ctx)
return -EINVAL;
mgr = &fpriv->ctx_mgr;
mutex_lock(&mgr->lock);
ctx = idr_find(&mgr->ctx_handles, id);
if (!ctx) {
mutex_unlock(&mgr->lock);
return -EINVAL;
}
/* TODO: these two are always zero */
out->state.flags = 0x0;
out->state.hangs = 0x0;
@@ -586,7 +541,8 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
out->state.reset_status = AMDGPU_CTX_UNKNOWN_RESET;
ctx->reset_counter_query = reset_counter;
mutex_unlock(&mgr->lock);
amdgpu_ctx_put(ctx);
return 0;
}
@@ -619,19 +575,11 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev,
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct amdgpu_ctx *ctx;
struct amdgpu_ctx_mgr *mgr;
if (!fpriv)
ctx = amdgpu_ctx_get(fpriv, id);
if (!ctx)
return -EINVAL;
mgr = &fpriv->ctx_mgr;
mutex_lock(&mgr->lock);
ctx = idr_find(&mgr->ctx_handles, id);
if (!ctx) {
mutex_unlock(&mgr->lock);
return -EINVAL;
}
out->state.flags = 0x0;
out->state.hangs = 0x0;
@@ -671,7 +619,8 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev,
msecs_to_jiffies(AMDGPU_RAS_COUNTE_DELAY_MS));
}
mutex_unlock(&mgr->lock);
amdgpu_ctx_put(ctx);
return 0;
}
@@ -680,26 +629,26 @@ static int amdgpu_ctx_stable_pstate(struct amdgpu_device *adev,
bool set, u32 *stable_pstate)
{
struct amdgpu_ctx *ctx;
struct amdgpu_ctx_mgr *mgr;
int r;
int r = 0;
if (!fpriv)
ctx = amdgpu_ctx_get(fpriv, id);
if (!ctx)
return -EINVAL;
mgr = &fpriv->ctx_mgr;
mutex_lock(&mgr->lock);
ctx = idr_find(&mgr->ctx_handles, id);
if (!ctx) {
mutex_unlock(&mgr->lock);
return -EINVAL;
}
/*
* The get path is odd in this uapi - it will check whether the context
* id exist, but otherwise does nothing with it. In other words, the
* uapi has historically been implemented as being able to query the
* global device state, as long as the caller supplies a random valid
* context id.
*/
if (set)
r = amdgpu_ctx_set_stable_pstate(ctx, *stable_pstate);
else
r = amdgpu_ctx_get_stable_pstate(ctx, stable_pstate);
*stable_pstate = amdgpu_get_stable_pstate(adev);
mutex_unlock(&mgr->lock);
amdgpu_ctx_put(ctx);
return r;
}
@@ -778,23 +727,14 @@ struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id)
mgr = &fpriv->ctx_mgr;
mutex_lock(&mgr->lock);
ctx = idr_find(&mgr->ctx_handles, id);
xa_lock(&mgr->ctx_handles);
ctx = xa_load(&mgr->ctx_handles, id);
if (ctx)
kref_get(&ctx->refcount);
mutex_unlock(&mgr->lock);
xa_unlock(&mgr->ctx_handles);
return ctx;
}
int amdgpu_ctx_put(struct amdgpu_ctx *ctx)
{
if (ctx == NULL)
return -EINVAL;
kref_put(&ctx->refcount, amdgpu_ctx_do_release);
return 0;
}
uint64_t amdgpu_ctx_add_fence(struct amdgpu_ctx *ctx,
struct drm_sched_entity *entity,
struct dma_fence *fence)
@@ -928,8 +868,7 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr,
unsigned int i;
mgr->adev = adev;
mutex_init(&mgr->lock);
idr_init_base(&mgr->ctx_handles, 1);
xa_init_flags(&mgr->ctx_handles, XA_FLAGS_ALLOC1);
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i)
atomic64_set(&mgr->time_spend[i], 0);
@@ -938,13 +877,13 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr,
long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout)
{
struct amdgpu_ctx *ctx;
struct idr *idp;
uint32_t id, i, j;
unsigned long id;
int i, j;
idp = &mgr->ctx_handles;
mutex_lock(&mgr->lock);
idr_for_each_entry(idp, ctx, id) {
xa_lock(&mgr->ctx_handles);
xa_for_each(&mgr->ctx_handles, id, ctx) {
kref_get(&ctx->refcount);
xa_unlock(&mgr->ctx_handles);
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
struct drm_sched_entity *entity;
@@ -956,45 +895,21 @@ long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout)
timeout = drm_sched_entity_flush(entity, timeout);
}
}
amdgpu_ctx_put(ctx);
xa_lock(&mgr->ctx_handles);
}
mutex_unlock(&mgr->lock);
xa_unlock(&mgr->ctx_handles);
return timeout;
}
static void amdgpu_ctx_mgr_entity_fini(struct amdgpu_ctx_mgr *mgr)
{
struct amdgpu_ctx *ctx;
struct idr *idp;
uint32_t id, i, j;
idp = &mgr->ctx_handles;
idr_for_each_entry(idp, ctx, id) {
if (kref_read(&ctx->refcount) != 1) {
drm_err(adev_to_drm(mgr->adev), "ctx %p is still alive\n", ctx);
continue;
}
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
struct drm_sched_entity *entity;
if (!ctx->entities[i][j])
continue;
entity = &ctx->entities[i][j]->entity;
drm_sched_entity_fini(entity);
}
}
kref_put(&ctx->refcount, amdgpu_ctx_fini);
}
}
void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr)
{
amdgpu_ctx_mgr_entity_fini(mgr);
idr_destroy(&mgr->ctx_handles);
mutex_destroy(&mgr->lock);
struct amdgpu_ctx *ctx;
unsigned long id;
xa_for_each(&mgr->ctx_handles, id, ctx)
amdgpu_ctx_put(ctx);
xa_destroy(&mgr->ctx_handles);
}
void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
@@ -1002,21 +917,21 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
{
struct amdgpu_ctx *ctx;
unsigned int hw_ip, i;
uint32_t id;
unsigned long id;
/*
* This is a little bit racy because it can be that a ctx or a fence are
* destroyed just in the moment we try to account them. But that is ok
* since exactly that case is explicitely allowed by the interface.
*/
mutex_lock(&mgr->lock);
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
uint64_t ns = atomic64_read(&mgr->time_spend[hw_ip]);
usage[hw_ip] = ns_to_ktime(ns);
}
idr_for_each_entry(&mgr->ctx_handles, ctx, id) {
xa_lock(&mgr->ctx_handles);
xa_for_each(&mgr->ctx_handles, id, ctx) {
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
for (i = 0; i < amdgpu_ctx_num_entities[hw_ip]; ++i) {
struct amdgpu_ctx_entity *centity;
@@ -1030,5 +945,5 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
}
}
}
mutex_unlock(&mgr->lock);
xa_unlock(&mgr->ctx_handles);
}
+10 -4
View File
@@ -25,6 +25,7 @@
#include <linux/ktime.h>
#include <linux/types.h>
#include <linux/xarray.h>
#include "amdgpu_ring.h"
@@ -60,16 +61,21 @@ struct amdgpu_ctx {
struct amdgpu_ctx_mgr {
struct amdgpu_device *adev;
struct mutex lock;
/* protected by lock */
struct idr ctx_handles;
struct xarray ctx_handles;
atomic64_t time_spend[AMDGPU_HW_IP_NUM];
};
extern const unsigned int amdgpu_ctx_num_entities[AMDGPU_HW_IP_NUM];
struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id);
int amdgpu_ctx_put(struct amdgpu_ctx *ctx);
void amdgpu_ctx_fini(struct kref *kref);
static inline void amdgpu_ctx_put(struct amdgpu_ctx *ctx)
{
if (ctx)
kref_put(&ctx->refcount, amdgpu_ctx_fini);
}
int amdgpu_ctx_get_entity(struct amdgpu_ctx *ctx, u32 hw_ip, u32 instance,
u32 ring, struct drm_sched_entity **entity);
@@ -26,6 +26,7 @@
#include <linux/kthread.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/security.h>
#include <linux/pm_runtime.h>
#include "amdgpu.h"
@@ -1748,6 +1749,12 @@ int amdgpu_debugfs_regs_init(struct amdgpu_device *adev)
struct dentry *ent, *root = minor->debugfs_root;
unsigned int i;
if (security_locked_down(LOCKDOWN_PCI_ACCESS)) {
drm_info(adev_to_drm(adev),
"amdgpu: HW debugfs nodes disabled (kernel lockdown)\n");
return 0;
}
for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) {
ent = debugfs_create_file(debugfs_regs_names[i],
S_IFREG | 0400, root,
@@ -2180,6 +2187,8 @@ int amdgpu_debugfs_init(struct amdgpu_device *adev)
if (!ring)
continue;
if (ring == &adev->cper.ring_buf && !adev->cper.enabled)
continue;
amdgpu_debugfs_ring_init(adev, ring);
}
@@ -64,6 +64,7 @@ const char *hw_ip_names[MAX_HWIP] = {
[VCN1_HWIP] = "VCN1",
[VCE_HWIP] = "VCE",
[VPE_HWIP] = "VPE",
[UMSCH_HWIP] = "UMSCH",
[DF_HWIP] = "DF",
[DCE_HWIP] = "DCE",
[OSSSYS_HWIP] = "OSSSYS",

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