diff --git a/config b/config index 918c0e3..178bb6b 100644 --- a/config +++ b/config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86 6.4.3 Kernel Configuration +# Linux/x86 6.5.0-rc1 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 13.1.1 20230525" CONFIG_CC_IS_GCC=y @@ -187,6 +187,7 @@ CONFIG_RCU_NOCB_CPU_DEFAULT_ALL=y # CONFIG_RCU_NOCB_CPU_CB_BOOST is not set # CONFIG_TASKS_TRACE_RCU_READ_MB is not set CONFIG_RCU_LAZY=y +# CONFIG_RCU_DOUBLE_CHECK_CB_TIME is not set # end of RCU Subsystem CONFIG_IKCONFIG=y @@ -305,6 +306,7 @@ CONFIG_KALLSYMS_BASE_RELATIVE=y CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y CONFIG_KCMP=y CONFIG_RSEQ=y +CONFIG_CACHESTAT_SYSCALL=y # CONFIG_EMBEDDED is not set CONFIG_HAVE_PERF_EVENTS=y CONFIG_GUEST_PERF_EVENTS=y @@ -354,7 +356,6 @@ CONFIG_CC_HAS_SANE_STACKPROTECTOR=y # Processor type and features # CONFIG_SMP=y -CONFIG_X86_FEATURE_NAMES=y CONFIG_X86_X2APIC=y CONFIG_X86_MPPARSE=y # CONFIG_GOLDFISH is not set @@ -565,8 +566,6 @@ CONFIG_RANDOMIZE_MEMORY=y CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING=0xa CONFIG_ADDRESS_MASKING=y CONFIG_HOTPLUG_CPU=y -# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set -# CONFIG_DEBUG_HOTPLUG_CPU0 is not set # CONFIG_COMPAT_VDSO is not set CONFIG_LEGACY_VSYSCALL_XONLY=y # CONFIG_LEGACY_VSYSCALL_NONE is not set @@ -727,6 +726,7 @@ CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_X86_INTEL_PSTATE=y CONFIG_X86_PCC_CPUFREQ=m CONFIG_X86_AMD_PSTATE=y +CONFIG_X86_AMD_PSTATE_DEFAULT_MODE=3 CONFIG_X86_AMD_PSTATE_UT=m CONFIG_X86_ACPI_CPUFREQ=m CONFIG_X86_ACPI_CPUFREQ_CPB=y @@ -818,6 +818,11 @@ CONFIG_AS_GFNI=y CONFIG_CRASH_CORE=y CONFIG_KEXEC_CORE=y CONFIG_HOTPLUG_SMT=y +CONFIG_HOTPLUG_CORE_SYNC=y +CONFIG_HOTPLUG_CORE_SYNC_DEAD=y +CONFIG_HOTPLUG_CORE_SYNC_FULL=y +CONFIG_HOTPLUG_SPLIT_STARTUP=y +CONFIG_HOTPLUG_PARALLEL=y CONFIG_GENERIC_ENTRY=y CONFIG_KPROBES=y CONFIG_JUMP_LABEL=y @@ -847,6 +852,7 @@ CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_ARCH_HAS_FORTIFY_SOURCE=y CONFIG_ARCH_HAS_SET_MEMORY=y CONFIG_ARCH_HAS_SET_DIRECT_MAP=y +CONFIG_ARCH_HAS_CPU_FINALIZE_INIT=y CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y CONFIG_ARCH_WANTS_NO_INSTR=y @@ -1108,6 +1114,7 @@ CONFIG_ZPOOL=y CONFIG_SWAP=y CONFIG_ZSWAP=y CONFIG_ZSWAP_DEFAULT_ON=y +CONFIG_ZSWAP_EXCLUSIVE_LOADS_DEFAULT_ON=y # CONFIG_ZSWAP_COMPRESSOR_DEFAULT_DEFLATE is not set # CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO is not set # CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set @@ -1128,7 +1135,7 @@ CONFIG_ZSMALLOC_CHAIN_SIZE=8 # # SLAB allocator options # -# CONFIG_SLAB is not set +# CONFIG_SLAB_DEPRECATED is not set CONFIG_SLUB=y CONFIG_SLAB_MERGE_DEFAULT=y CONFIG_SLAB_FREELIST_RANDOM=y @@ -2285,6 +2292,7 @@ CONFIG_CXL_PORT=m CONFIG_CXL_SUSPEND=y CONFIG_CXL_REGION=y # CONFIG_CXL_REGION_INVALIDATION_TEST is not set +CONFIG_CXL_PMU=m CONFIG_PCCARD=m CONFIG_PCMCIA=m CONFIG_PCMCIA_LOAD_CIS=y @@ -2423,6 +2431,7 @@ CONFIG_EFI_EARLYCON=y CONFIG_EFI_CUSTOM_SSDT_OVERLAYS=y # CONFIG_EFI_DISABLE_RUNTIME is not set CONFIG_EFI_COCO_SECRET=y +CONFIG_UNACCEPTED_MEMORY=y CONFIG_EFI_EMBEDDED_FIRMWARE=y # end of EFI (Extensible Firmware Interface) Support @@ -2684,6 +2693,8 @@ CONFIG_DW_XDATA_PCIE=m CONFIG_PCI_ENDPOINT_TEST=m CONFIG_XILINX_SDFEC=m CONFIG_MISC_RTSX=m +CONFIG_TPS6594_ESM=m +CONFIG_TPS6594_PFSM=m CONFIG_C2PORT=m CONFIG_C2PORT_DURAMAR_2150=m @@ -2718,6 +2729,7 @@ CONFIG_INTEL_MEI_TXE=m CONFIG_INTEL_MEI_GSC=m CONFIG_INTEL_MEI_HDCP=m CONFIG_INTEL_MEI_PXP=m +CONFIG_INTEL_MEI_GSC_PROXY=m CONFIG_VMWARE_VMCI=m CONFIG_GENWQE=m CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY=0 @@ -3275,6 +3287,7 @@ CONFIG_THUNDER_NIC_VF=m CONFIG_THUNDER_NIC_BGX=m CONFIG_THUNDER_NIC_RGX=m CONFIG_CAVIUM_PTP=m +CONFIG_LIQUIDIO_CORE=m CONFIG_LIQUIDIO=m CONFIG_LIQUIDIO_VF=m CONFIG_NET_VENDOR_CHELSIO=y @@ -3679,6 +3692,7 @@ CONFIG_CAN_8DEV_USB=m CONFIG_CAN_EMS_USB=m CONFIG_CAN_ESD_USB=m CONFIG_CAN_ETAS_ES58X=m +CONFIG_CAN_F81604=m CONFIG_CAN_GS_USB=m CONFIG_CAN_KVASER_USB=m CONFIG_CAN_MCBA_USB=m @@ -3707,6 +3721,7 @@ CONFIG_MDIO_GPIO=m CONFIG_MDIO_I2C=m CONFIG_MDIO_MVUSB=m CONFIG_MDIO_MSCC_MIIM=m +CONFIG_MDIO_REGMAP=m CONFIG_MDIO_THUNDER=m # @@ -3719,7 +3734,6 @@ CONFIG_MDIO_THUNDER=m CONFIG_PCS_XPCS=m CONFIG_PCS_LYNX=m CONFIG_PCS_MTK_LYNXI=m -CONFIG_PCS_ALTERA_TSE=m # end of PCS device drivers CONFIG_PLIP=m @@ -3731,6 +3745,11 @@ CONFIG_PPP_MPPE=m CONFIG_PPP_MULTILINK=y CONFIG_PPPOATM=m CONFIG_PPPOE=m +# CONFIG_PPPOE_HASH_BITS_1 is not set +# CONFIG_PPPOE_HASH_BITS_2 is not set +CONFIG_PPPOE_HASH_BITS_4=y +# CONFIG_PPPOE_HASH_BITS_8 is not set +CONFIG_PPPOE_HASH_BITS=4 CONFIG_PPTP=m CONFIG_PPPOL2TP=m CONFIG_PPP_ASYNC=m @@ -4079,6 +4098,7 @@ CONFIG_RTW88_8822CE=m CONFIG_RTW88_8822CS=m CONFIG_RTW88_8822CU=m CONFIG_RTW88_8723DE=m +CONFIG_RTW88_8723DS=m CONFIG_RTW88_8723DU=m CONFIG_RTW88_8821CE=m CONFIG_RTW88_8821CS=m @@ -4088,9 +4108,11 @@ CONFIG_RTW88_DEBUGFS=y CONFIG_RTW89=m CONFIG_RTW89_CORE=m CONFIG_RTW89_PCI=m +CONFIG_RTW89_8851B=m CONFIG_RTW89_8852A=m CONFIG_RTW89_8852B=m CONFIG_RTW89_8852C=m +CONFIG_RTW89_8851BE=m CONFIG_RTW89_8852AE=m CONFIG_RTW89_8852BE=m CONFIG_RTW89_8852CE=m @@ -4371,7 +4393,6 @@ CONFIG_TOUCHSCREEN_MTOUCH=m CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS=m CONFIG_TOUCHSCREEN_IMAGIS=m CONFIG_TOUCHSCREEN_INEXIO=m -CONFIG_TOUCHSCREEN_MK712=m CONFIG_TOUCHSCREEN_PENMOUNT=m CONFIG_TOUCHSCREEN_EDT_FT5X06=m CONFIG_TOUCHSCREEN_TOUCHRIGHT=m @@ -4684,6 +4705,7 @@ CONFIG_XILLYBUS_CLASS=m CONFIG_XILLYBUS=m CONFIG_XILLYBUS_PCIE=m CONFIG_XILLYUSB=m +CONFIG_DDCCI=m # end of Character devices # @@ -4955,13 +4977,13 @@ CONFIG_GPIO_MB86S7X=m CONFIG_GPIO_MENZ127=m CONFIG_GPIO_SIOX=m CONFIG_GPIO_TANGIER=m -CONFIG_GPIO_VX855=m CONFIG_GPIO_AMD_FCH=m # end of Memory mapped GPIO drivers # # Port-mapped I/O GPIO drivers # +CONFIG_GPIO_VX855=m CONFIG_GPIO_F7188X=m CONFIG_GPIO_IT87=m CONFIG_GPIO_SCH=m @@ -5164,6 +5186,7 @@ CONFIG_CHARGER_TPS65090=m CONFIG_BATTERY_GAUGE_LTC2941=m CONFIG_BATTERY_GOLDFISH=m CONFIG_BATTERY_RT5033=m +CONFIG_CHARGER_RT5033=m CONFIG_CHARGER_RT9455=m CONFIG_CHARGER_RT9467=m CONFIG_CHARGER_RT9471=m @@ -5262,6 +5285,7 @@ CONFIG_SENSORS_MAX197=m CONFIG_SENSORS_MAX31722=m CONFIG_SENSORS_MAX31730=m CONFIG_SENSORS_MAX31760=m +CONFIG_MAX31827=m CONFIG_SENSORS_MAX6620=m CONFIG_SENSORS_MAX6621=m CONFIG_SENSORS_MAX6639=m @@ -5429,6 +5453,7 @@ CONFIG_SENSORS_ACPI_POWER=m CONFIG_SENSORS_ATK0110=m CONFIG_SENSORS_ASUS_WMI=m CONFIG_SENSORS_ASUS_EC=m +CONFIG_SENSORS_HP_WMI=m CONFIG_THERMAL=y CONFIG_THERMAL_NETLINK=y # CONFIG_THERMAL_STATISTICS is not set @@ -5440,6 +5465,7 @@ CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y # CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set # CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set # CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_DEFAULT_GOV_BANG_BANG is not set CONFIG_THERMAL_GOV_FAIR_SHARE=y CONFIG_THERMAL_GOV_STEP_WISE=y CONFIG_THERMAL_GOV_BANG_BANG=y @@ -5647,6 +5673,7 @@ CONFIG_MFD_88PM800=m CONFIG_MFD_88PM805=m CONFIG_MFD_88PM860X=y CONFIG_MFD_MAX14577=m +CONFIG_MFD_MAX77541=m CONFIG_MFD_MAX77693=m CONFIG_MFD_MAX77843=y CONFIG_MFD_MAX8907=m @@ -5692,6 +5719,9 @@ CONFIG_MFD_TPS65910=y CONFIG_MFD_TPS65912=m CONFIG_MFD_TPS65912_I2C=m CONFIG_MFD_TPS65912_SPI=m +CONFIG_MFD_TPS6594=m +CONFIG_MFD_TPS6594_I2C=m +CONFIG_MFD_TPS6594_SPI=m CONFIG_TWL4030_CORE=y CONFIG_MFD_TWL4030_AUDIO=y CONFIG_TWL6040_CORE=y @@ -5761,6 +5791,7 @@ CONFIG_REGULATOR_LTC3589=m CONFIG_REGULATOR_LTC3676=m CONFIG_REGULATOR_MAX14577=m CONFIG_REGULATOR_MAX1586=m +CONFIG_REGULATOR_MAX77541=m CONFIG_REGULATOR_MAX8649=m CONFIG_REGULATOR_MAX8660=m CONFIG_REGULATOR_MAX8893=m @@ -5795,6 +5826,7 @@ CONFIG_REGULATOR_PV88060=m CONFIG_REGULATOR_PV88080=m CONFIG_REGULATOR_PV88090=m CONFIG_REGULATOR_PWM=m +CONFIG_REGULATOR_RAA215300=m CONFIG_REGULATOR_RC5T583=m CONFIG_REGULATOR_RT4801=m CONFIG_REGULATOR_RT4803=m @@ -6026,7 +6058,6 @@ CONFIG_VIDEO_PVRUSB2=m CONFIG_VIDEO_PVRUSB2_SYSFS=y CONFIG_VIDEO_PVRUSB2_DVB=y # CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set -CONFIG_VIDEO_STK1160_COMMON=m CONFIG_VIDEO_STK1160=m # @@ -6380,6 +6411,7 @@ CONFIG_VIDEO_MT9V011=m CONFIG_VIDEO_MT9V032=m CONFIG_VIDEO_MT9V111=m CONFIG_VIDEO_OG01A1B=m +CONFIG_VIDEO_OV01A10=m CONFIG_VIDEO_OV02A10=m CONFIG_VIDEO_OV08D10=m CONFIG_VIDEO_OV08X40=m @@ -6860,6 +6892,9 @@ CONFIG_FB_SYS_IMAGEBLIT=y # CONFIG_FB_FOREIGN_ENDIAN is not set CONFIG_FB_SYS_FOPS=y CONFIG_FB_DEFERRED_IO=y +CONFIG_FB_IO_HELPERS=y +CONFIG_FB_SYS_HELPERS=y +CONFIG_FB_SYS_HELPERS_DEFERRED=y # CONFIG_FB_MODE_HELPERS is not set # CONFIG_FB_TILEBLITTING is not set @@ -6941,6 +6976,7 @@ CONFIG_BACKLIGHT_MAX8925=m CONFIG_BACKLIGHT_MT6370=m CONFIG_BACKLIGHT_APPLE=m CONFIG_BACKLIGHT_QCOM_WLED=m +CONFIG_BACKLIGHT_DDCCI=m CONFIG_BACKLIGHT_RT4831=m CONFIG_BACKLIGHT_SAHARA=m CONFIG_BACKLIGHT_WM831X=m @@ -7033,6 +7069,7 @@ CONFIG_SND_SEQ_MIDI_EVENT=m CONFIG_SND_SEQ_MIDI=m CONFIG_SND_SEQ_MIDI_EMUL=m CONFIG_SND_SEQ_VIRMIDI=m +CONFIG_SND_SEQ_UMP=y CONFIG_SND_MPU401_UART=m CONFIG_SND_OPL3_LIB=m CONFIG_SND_OPL3_LIB_SEQ=m @@ -7042,6 +7079,7 @@ CONFIG_SND_DRIVERS=y # CONFIG_SND_PCSP is not set CONFIG_SND_DUMMY=m CONFIG_SND_ALOOP=m +CONFIG_SND_PCMTEST=m CONFIG_SND_VIRMIDI=m CONFIG_SND_MTPAV=m CONFIG_SND_MTS64=m @@ -7172,6 +7210,7 @@ CONFIG_SND_INTEL_SOUNDWIRE_ACPI=m CONFIG_SND_SPI=y CONFIG_SND_USB=y CONFIG_SND_USB_AUDIO=m +# CONFIG_SND_USB_AUDIO_MIDI_V2 is not set CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER=y CONFIG_SND_USB_UA101=m CONFIG_SND_USB_USX2Y=m @@ -7259,6 +7298,7 @@ CONFIG_SND_SOC_FSL_RPMSG=m # CONFIG_SND_SOC_IMX_AUDMUX is not set # end of SoC Audio for Freescale CPUs +CONFIG_SND_SOC_CHV3_I2S=m CONFIG_SND_I2S_HI6210_I2S=m CONFIG_SND_SOC_IMG=y CONFIG_SND_SOC_IMG_I2S_IN=m @@ -7457,6 +7497,7 @@ CONFIG_SND_SOC_AW88395_LIB=m CONFIG_SND_SOC_AW88395=m CONFIG_SND_SOC_BD28623=m # CONFIG_SND_SOC_BT_SCO is not set +CONFIG_SND_SOC_CHV3_CODEC=m CONFIG_SND_SOC_CROS_EC_CODEC=m CONFIG_SND_SOC_CS35L32=m CONFIG_SND_SOC_CS35L33=m @@ -7526,6 +7567,7 @@ CONFIG_SND_SOC_MAX98363=m CONFIG_SND_SOC_MAX98373=m CONFIG_SND_SOC_MAX98373_I2C=m CONFIG_SND_SOC_MAX98373_SDW=m +CONFIG_SND_SOC_MAX98388=m CONFIG_SND_SOC_MAX98390=m CONFIG_SND_SOC_MAX98396=m CONFIG_SND_SOC_MAX9860=m @@ -7588,6 +7630,7 @@ CONFIG_SND_SOC_RT711_SDW=m CONFIG_SND_SOC_RT711_SDCA_SDW=m CONFIG_SND_SOC_RT712_SDCA_SDW=m CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW=m +CONFIG_SND_SOC_RT722_SDCA_SDW=m CONFIG_SND_SOC_RT715=m CONFIG_SND_SOC_RT715_SDW=m CONFIG_SND_SOC_RT715_SDCA_SDW=m @@ -7618,6 +7661,9 @@ CONFIG_SND_SOC_TAS2562=m CONFIG_SND_SOC_TAS2764=m CONFIG_SND_SOC_TAS2770=m CONFIG_SND_SOC_TAS2780=m +CONFIG_SND_SOC_TAS2781_COMLIB=m +CONFIG_SND_SOC_TAS2781_FMWLIB=m +CONFIG_SND_SOC_TAS2781_I2C=m CONFIG_SND_SOC_TAS5086=m CONFIG_SND_SOC_TAS571X=m CONFIG_SND_SOC_TAS5720=m @@ -7678,6 +7724,7 @@ CONFIG_SND_SOC_WM8978=m CONFIG_SND_SOC_WM8985=m CONFIG_SND_SOC_WSA881X=m CONFIG_SND_SOC_WSA883X=m +CONFIG_SND_SOC_WSA884X=m CONFIG_SND_SOC_ZL38060=m CONFIG_SND_SOC_MAX9759=m CONFIG_SND_SOC_MT6351=m @@ -7791,6 +7838,8 @@ CONFIG_HID_NINTENDO=m CONFIG_NINTENDO_FF=y CONFIG_HID_NTI=m CONFIG_HID_NTRIG=m +CONFIG_HID_NVIDIA_SHIELD=m +CONFIG_NVIDIA_SHIELD_FF=y CONFIG_HID_ORTEK=m CONFIG_HID_PANTHERLORD=m CONFIG_PANTHERLORD_FF=y @@ -8180,6 +8229,7 @@ CONFIG_USB_NET2280=m CONFIG_USB_GOKU=m CONFIG_USB_EG20T=m CONFIG_USB_MAX3420_UDC=m +CONFIG_USB_CDNS2_UDC=m CONFIG_USB_DUMMY_HCD=m # end of USB Peripheral Controller @@ -8290,6 +8340,7 @@ CONFIG_TYPEC_MUX_FSA4480=m CONFIG_TYPEC_MUX_GPIO_SBU=m CONFIG_TYPEC_MUX_PI3USB30532=m CONFIG_TYPEC_MUX_INTEL_PMC=m +CONFIG_TYPEC_MUX_NB7VPQ904M=m # end of USB Type-C Multiplexer/DeMultiplexer Switch support # @@ -8374,6 +8425,8 @@ CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y # CONFIG_LEDS_88PM860X=m CONFIG_LEDS_APU=m +CONFIG_LEDS_AW200XX=m +CONFIG_LEDS_CHT_WCOVE=m CONFIG_LEDS_LM3530=m CONFIG_LEDS_LM3532=m CONFIG_LEDS_LM3533=m @@ -8466,6 +8519,8 @@ CONFIG_LEDS_TRIGGER_BLKDEV=m # Simple LED drivers # CONFIG_LEDS_SIEMENS_SIMATIC_IPC=m +CONFIG_LEDS_SIEMENS_SIMATIC_IPC_APOLLOLAKE=m +CONFIG_LEDS_SIEMENS_SIMATIC_IPC_F7188X=m CONFIG_ACCESSIBILITY=y CONFIG_A11Y_BRAILLE_CONSOLE=y @@ -8762,6 +8817,10 @@ CONFIG_VFIO_CONTAINER=y CONFIG_VFIO_IOMMU_TYPE1=m # CONFIG_VFIO_NOIOMMU is not set CONFIG_VFIO_VIRQFD=y + +# +# VFIO support for PCI devices +# CONFIG_VFIO_PCI_CORE=m CONFIG_VFIO_PCI_MMAP=y CONFIG_VFIO_PCI_INTX=y @@ -8769,6 +8828,8 @@ CONFIG_VFIO_PCI=m CONFIG_VFIO_PCI_VGA=y CONFIG_VFIO_PCI_IGD=y CONFIG_MLX5_VFIO_PCI=m +# end of VFIO support for PCI devices + CONFIG_VFIO_MDEV=m CONFIG_IRQ_BYPASS_MANAGER=m CONFIG_VIRT_DRIVERS=y @@ -8806,6 +8867,7 @@ CONFIG_MLX5_VDPA_NET=m CONFIG_VP_VDPA=m CONFIG_ALIBABA_ENI_VDPA=m CONFIG_SNET_VDPA=m +CONFIG_PDS_VDPA=m CONFIG_VHOST_IOTLB=m CONFIG_VHOST_RING=m CONFIG_VHOST_TASK=y @@ -9005,11 +9067,12 @@ CONFIG_MXM_WMI=m CONFIG_NVIDIA_WMI_EC_BACKLIGHT=m CONFIG_XIAOMI_WMI=m CONFIG_GIGABYTE_WMI=m -CONFIG_YOGABOOK_WMI=m +CONFIG_YOGABOOK=m CONFIG_ACERHDF=m CONFIG_ACER_WIRELESS=m CONFIG_ACER_WMI=m CONFIG_AMD_PMF=m +# CONFIG_AMD_PMF_DEBUG is not set CONFIG_AMD_PMC=m CONFIG_AMD_HSMP=m CONFIG_ADV_SWBUTTON=m @@ -9086,6 +9149,7 @@ CONFIG_INTEL_WMI_THUNDERBOLT=m # # Intel Uncore Frequency Control # +CONFIG_INTEL_UNCORE_FREQ_CONTROL_TPMI=m CONFIG_INTEL_UNCORE_FREQ_CONTROL=m # end of Intel Uncore Frequency Control @@ -9448,6 +9512,7 @@ CONFIG_MAX11205=m CONFIG_MAX11410=m CONFIG_MAX1241=m CONFIG_MAX1363=m +CONFIG_MAX77541_ADC=m CONFIG_MAX9611=m CONFIG_MCP320X=m CONFIG_MCP3422=m @@ -9764,6 +9829,7 @@ CONFIG_ISL29125=m CONFIG_HID_SENSOR_ALS=m CONFIG_HID_SENSOR_PROX=m CONFIG_JSA1212=m +CONFIG_ROHM_BU27008=m CONFIG_ROHM_BU27034=m CONFIG_RPR0521=m CONFIG_SENSORS_LM3533=m @@ -9774,6 +9840,7 @@ CONFIG_MAX44000=m CONFIG_MAX44009=m CONFIG_NOA1305=m CONFIG_OPT3001=m +CONFIG_OPT4001=m CONFIG_PA12203001=m CONFIG_SI1133=m CONFIG_SI1145=m @@ -9865,6 +9932,7 @@ CONFIG_MCP4131=m CONFIG_MCP4531=m CONFIG_MCP41010=m CONFIG_TPL0102=m +CONFIG_X9250=m # end of Digital potentiometers # @@ -9890,6 +9958,7 @@ CONFIG_MPL115=m CONFIG_MPL115_I2C=m CONFIG_MPL115_SPI=m CONFIG_MPL3115=m +CONFIG_MPRLS0025PA=m CONFIG_MS5611=m CONFIG_MS5611_I2C=m CONFIG_MS5611_SPI=m @@ -10023,6 +10092,7 @@ CONFIG_PHY_INTEL_LGM_EMMC=m CONFIG_POWERCAP=y CONFIG_INTEL_RAPL_CORE=m CONFIG_INTEL_RAPL=m +CONFIG_INTEL_RAPL_TPMI=m CONFIG_IDLE_INJECT=y CONFIG_MCB=m CONFIG_MCB_PCI=m @@ -10592,7 +10662,6 @@ CONFIG_SECURITY_NETWORK_XFRM=y CONFIG_SECURITY_PATH=y # CONFIG_INTEL_TXT is not set CONFIG_LSM_MMAP_MIN_ADDR=65536 -CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y CONFIG_HARDENED_USERCOPY=y CONFIG_FORTIFY_SOURCE=y # CONFIG_STATIC_USERMODEHELPER is not set @@ -10691,6 +10760,7 @@ CONFIG_CRYPTO_ALGAPI=y CONFIG_CRYPTO_ALGAPI2=y CONFIG_CRYPTO_AEAD=m CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_SIG2=y CONFIG_CRYPTO_SKCIPHER=y CONFIG_CRYPTO_SKCIPHER2=y CONFIG_CRYPTO_HASH=y @@ -10708,7 +10778,7 @@ CONFIG_CRYPTO_MANAGER2=y CONFIG_CRYPTO_USER=m CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y CONFIG_CRYPTO_NULL=m -CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_NULL2=m CONFIG_CRYPTO_PCRYPT=m CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_AUTHENC=m @@ -10779,6 +10849,7 @@ CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_CHACHA20POLY1305=m CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m +CONFIG_CRYPTO_GENIV=m CONFIG_CRYPTO_SEQIV=m CONFIG_CRYPTO_ECHAINIV=m CONFIG_CRYPTO_ESSIV=m @@ -10800,7 +10871,7 @@ CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_SHA3=m +CONFIG_CRYPTO_SHA3=y CONFIG_CRYPTO_SM3=m CONFIG_CRYPTO_SM3_GENERIC=m CONFIG_CRYPTO_STREEBOG=m @@ -10840,6 +10911,7 @@ CONFIG_CRYPTO_DRBG_HASH=y CONFIG_CRYPTO_DRBG_CTR=y CONFIG_CRYPTO_DRBG=y CONFIG_CRYPTO_JITTERENTROPY=y +# CONFIG_CRYPTO_JITTERENTROPY_TESTINTERFACE is not set CONFIG_CRYPTO_KDF800108_CTR=y # end of Random number generation @@ -11065,6 +11137,7 @@ CONFIG_HAS_IOPORT=y CONFIG_HAS_IOPORT_MAP=y CONFIG_HAS_DMA=y CONFIG_DMA_OPS=y +CONFIG_NEED_SG_DMA_FLAGS=y CONFIG_NEED_SG_DMA_LENGTH=y CONFIG_NEED_DMA_MAP_STATE=y CONFIG_ARCH_DMA_ADDR_T_64BIT=y @@ -11276,14 +11349,20 @@ CONFIG_PANIC_TIMEOUT=0 CONFIG_LOCKUP_DETECTOR=y CONFIG_SOFTLOCKUP_DETECTOR=y # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_HARDLOCKUP_DETECTOR_PERF=y -CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y +CONFIG_HAVE_HARDLOCKUP_DETECTOR_BUDDY=y CONFIG_HARDLOCKUP_DETECTOR=y +# CONFIG_HARDLOCKUP_DETECTOR_PREFER_BUDDY is not set +CONFIG_HARDLOCKUP_DETECTOR_PERF=y +# CONFIG_HARDLOCKUP_DETECTOR_BUDDY is not set +# CONFIG_HARDLOCKUP_DETECTOR_ARCH is not set +CONFIG_HARDLOCKUP_DETECTOR_COUNTS_HRTIMER=y +CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y # CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set CONFIG_DETECT_HUNG_TASK=y CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 # CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set # CONFIG_WQ_WATCHDOG is not set +# CONFIG_WQ_CPU_INTENSIVE_REPORT is not set # CONFIG_TEST_LOCKUP is not set # end of Debug Oops, Lockups and Hangs @@ -11360,6 +11439,7 @@ CONFIG_HAVE_RETHOOK=y CONFIG_RETHOOK=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_RETVAL=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y @@ -11385,6 +11465,7 @@ CONFIG_FTRACE=y CONFIG_BOOTTIME_TRACING=y CONFIG_FUNCTION_TRACER=y CONFIG_FUNCTION_GRAPH_TRACER=y +# CONFIG_FUNCTION_GRAPH_RETVAL is not set CONFIG_DYNAMIC_FTRACE=y CONFIG_DYNAMIC_FTRACE_WITH_REGS=y CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y @@ -11405,6 +11486,8 @@ CONFIG_TRACER_SNAPSHOT=y CONFIG_BRANCH_PROFILE_NONE=y # CONFIG_PROFILE_ANNOTATED_BRANCHES is not set CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_FPROBE_EVENTS=y +CONFIG_PROBE_EVENTS_BTF_ARGS=y CONFIG_KPROBE_EVENTS=y # CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set CONFIG_UPROBE_EVENTS=y diff --git a/patches/0001-cachy-all.patch b/patches/0001-cachy-all.patch index 1f68942..1c37f59 100644 --- a/patches/0001-cachy-all.patch +++ b/patches/0001-cachy-all.patch @@ -1,7 +1,7 @@ -From fa0fa964d3d7500eedd2d76639075887583968bc Mon Sep 17 00:00:00 2001 +From a34c2671419dc12fbea9f81528eda4dd6158d320 Mon Sep 17 00:00:00 2001 From: Peter Jung -Date: Tue, 11 Jul 2023 19:24:11 +0200 -Subject: [PATCH 1/8] bbr2 +Date: Mon, 10 Jul 2023 17:08:52 +0200 +Subject: [PATCH 1/7] bbr2 Signed-off-by: Peter Jung --- @@ -51,10 +51,10 @@ index c2b15f7e5516..d85858efa571 100644 }; diff --git a/include/net/tcp.h b/include/net/tcp.h -index 5066e4586cf0..b34661204315 100644 +index 226bce6d1e8c..64f1ec99c8f0 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h -@@ -375,6 +375,7 @@ static inline void tcp_dec_quickack_mode(struct sock *sk, +@@ -370,6 +370,7 @@ static inline void tcp_dec_quickack_mode(struct sock *sk, #define TCP_ECN_QUEUE_CWR 2 #define TCP_ECN_DEMAND_CWR 4 #define TCP_ECN_SEEN 8 @@ -62,7 +62,7 @@ index 5066e4586cf0..b34661204315 100644 enum tcp_tw_status { TCP_TW_SUCCESS = 0, -@@ -824,6 +825,11 @@ static inline u32 tcp_stamp_us_delta(u64 t1, u64 t0) +@@ -819,6 +820,11 @@ static inline u32 tcp_stamp_us_delta(u64 t1, u64 t0) return max_t(s64, t1 - t0, 0); } @@ -74,7 +74,7 @@ index 5066e4586cf0..b34661204315 100644 static inline u32 tcp_skb_timestamp(const struct sk_buff *skb) { return tcp_ns_to_ts(skb->skb_mstamp_ns); -@@ -899,9 +905,14 @@ struct tcp_skb_cb { +@@ -894,9 +900,14 @@ struct tcp_skb_cb { /* pkts S/ACKed so far upon tx of skb, incl retrans: */ __u32 delivered; /* start of send pipeline phase */ @@ -91,7 +91,7 @@ index 5066e4586cf0..b34661204315 100644 } tx; /* only used for outgoing skbs */ union { struct inet_skb_parm h4; -@@ -1027,7 +1038,11 @@ enum tcp_ca_ack_event_flags { +@@ -1022,7 +1033,11 @@ enum tcp_ca_ack_event_flags { #define TCP_CONG_NON_RESTRICTED 0x1 /* Requires ECN/ECT set on all packets */ #define TCP_CONG_NEEDS_ECN 0x2 @@ -104,7 +104,7 @@ index 5066e4586cf0..b34661204315 100644 union tcp_cc_info; -@@ -1047,8 +1062,11 @@ struct ack_sample { +@@ -1042,8 +1057,11 @@ struct ack_sample { */ struct rate_sample { u64 prior_mstamp; /* starting timestamp for interval */ @@ -116,7 +116,7 @@ index 5066e4586cf0..b34661204315 100644 s32 delivered; /* number of packets delivered over interval */ s32 delivered_ce; /* number of packets delivered w/ CE marks*/ long interval_us; /* time for tp->delivered to incr "delivered" */ -@@ -1062,6 +1080,7 @@ struct rate_sample { +@@ -1057,6 +1075,7 @@ struct rate_sample { bool is_app_limited; /* is sample from packet with bubble in pipe? */ bool is_retrans; /* is sample from retransmission? */ bool is_ack_delayed; /* is this (likely) a delayed ACK? */ @@ -124,7 +124,7 @@ index 5066e4586cf0..b34661204315 100644 }; struct tcp_congestion_ops { -@@ -1085,8 +1104,11 @@ struct tcp_congestion_ops { +@@ -1080,8 +1099,11 @@ struct tcp_congestion_ops { /* hook for packet ack accounting (optional) */ void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample); @@ -138,7 +138,7 @@ index 5066e4586cf0..b34661204315 100644 /* call when packets are delivered to update cwnd and pacing rate, * after all the ca_state processing. (optional) -@@ -1152,6 +1174,14 @@ static inline char *tcp_ca_get_name_by_key(u32 key, char *buffer) +@@ -1147,6 +1169,14 @@ static inline char *tcp_ca_get_name_by_key(u32 key, char *buffer) } #endif @@ -153,7 +153,7 @@ index 5066e4586cf0..b34661204315 100644 static inline bool tcp_ca_needs_ecn(const struct sock *sk) { const struct inet_connection_sock *icsk = inet_csk(sk); -@@ -1171,6 +1201,7 @@ static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event) +@@ -1166,6 +1196,7 @@ static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event) void tcp_set_ca_state(struct sock *sk, const u8 ca_state); /* From tcp_rate.c */ @@ -268,10 +268,10 @@ index b18ba8ef93ad..b4e3dcb27a20 100644 obj-$(CONFIG_TCP_CONG_CDG) += tcp_cdg.o obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c -index 8d20d9221238..99c2e0357dec 100644 +index e03e08745308..326b2c4bacf6 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c -@@ -3192,6 +3192,7 @@ int tcp_disconnect(struct sock *sk, int flags) +@@ -3083,6 +3083,7 @@ int tcp_disconnect(struct sock *sk, int flags) tp->rx_opt.dsack = 0; tp->rx_opt.num_sacks = 0; tp->rcv_ooopack = 0; @@ -3037,7 +3037,7 @@ index 1b34050a7538..66d40449b3f4 100644 icsk->icsk_ca_ops->init(sk); if (tcp_ca_needs_ecn(sk)) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c -index 57f1e4883b76..3879af9b5b69 100644 +index 57c8af1859c1..3193ef5aac61 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -349,7 +349,7 @@ static void __tcp_ecn_check_ce(struct sock *sk, const struct sk_buff *skb) @@ -3123,10 +3123,10 @@ index 57f1e4883b76..3879af9b5b69 100644 tcp_in_quickack_mode(sk) || /* Protocol state mandates a one-time immediate ACK */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c -index cfe128b81a01..6166db9a8571 100644 +index 2cb39b6dad02..703d166c1778 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c -@@ -375,7 +375,8 @@ static void tcp_ecn_send(struct sock *sk, struct sk_buff *skb, +@@ -377,7 +377,8 @@ static void tcp_ecn_send(struct sock *sk, struct sk_buff *skb, th->cwr = 1; skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; } @@ -3136,12 +3136,12 @@ index cfe128b81a01..6166db9a8571 100644 /* ACK or retransmitted segment: clear ECT|CE */ INET_ECN_dontxmit(sk); } -@@ -1530,7 +1531,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue, +@@ -1532,7 +1533,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue, { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *buff; -- int nsize, old_factor; -+ int nsize, old_factor, inflight_prev; +- int old_factor; ++ int old_factor, inflight_prev; long limit; int nlen; u8 flags; @@ -3161,7 +3161,7 @@ index cfe128b81a01..6166db9a8571 100644 } /* Link BUFF into the send queue. */ -@@ -1990,13 +2000,12 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now, +@@ -1982,13 +1992,12 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now, static u32 tcp_tso_segs(struct sock *sk, unsigned int mss_now) { const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops; @@ -3180,7 +3180,7 @@ index cfe128b81a01..6166db9a8571 100644 return min_t(u32, tso_segs, sk->sk_gso_max_segs); } -@@ -2632,6 +2641,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, +@@ -2674,6 +2683,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, skb_set_delivery_time(skb, tp->tcp_wstamp_ns, true); list_move_tail(&skb->tcp_tsorted_anchor, &tp->tsorted_sent_queue); tcp_init_tso_segs(skb, mss_now); @@ -3269,10 +3269,10 @@ index a8f6d9d06f2e..a8b4c9504570 100644 rs->interval_us = max(snd_us, ack_us); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c -index 39eb947fe392..61ab4ee55b22 100644 +index 470f581eedd4..2b8d7e94a369 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c -@@ -615,6 +615,7 @@ void tcp_write_timer_handler(struct sock *sk) +@@ -624,6 +624,7 @@ void tcp_write_timer_handler(struct sock *sk) return; } @@ -3283,14 +3283,14 @@ index 39eb947fe392..61ab4ee55b22 100644 -- 2.41.0 -From 3af499ab9158feb0393935535517c2a38f6017ed Mon Sep 17 00:00:00 2001 +From 15fb201317f2aaf349c0929478acd92a068be6d1 Mon Sep 17 00:00:00 2001 From: Peter Jung -Date: Wed, 19 Jul 2023 18:48:44 +0200 -Subject: [PATCH 2/8] cachy +Date: Mon, 10 Jul 2023 17:09:03 +0200 +Subject: [PATCH 2/7] cachy Signed-off-by: Peter Jung --- - .../admin-guide/kernel-parameters.txt | 12 + + .../admin-guide/kernel-parameters.txt | 9 + Makefile | 8 +- arch/arc/configs/axs101_defconfig | 1 + arch/arc/configs/axs103_defconfig | 1 + @@ -3306,9 +3306,7 @@ Signed-off-by: Peter Jung arch/arc/configs/vdk_hs38_defconfig | 1 + arch/arc/configs/vdk_hs38_smp_defconfig | 1 + arch/x86/Kconfig.cpu | 427 ++- - arch/x86/Makefile | 46 +- - arch/x86/Makefile.postlink | 47 + - arch/x86/boot/compressed/Makefile | 8 +- + arch/x86/Makefile | 44 +- arch/x86/include/asm/pci.h | 6 + arch/x86/include/asm/vermagic.h | 74 + arch/x86/pci/common.c | 7 +- @@ -3316,7 +3314,6 @@ Signed-off-by: Peter Jung drivers/Makefile | 15 +- drivers/ata/ahci.c | 23 +- drivers/cpufreq/Kconfig.x86 | 2 - - drivers/cpufreq/intel_pstate.c | 2 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-nct6775.c | 647 ++++ @@ -3343,28 +3340,17 @@ Signed-off-by: Peter Jung mm/swap.c | 5 + mm/vmpressure.c | 4 + mm/vmscan.c | 8 + - 53 files changed, 5344 insertions(+), 59 deletions(-) - create mode 100644 arch/x86/Makefile.postlink + 50 files changed, 5288 insertions(+), 53 deletions(-) create mode 100644 drivers/i2c/busses/i2c-nct6775.c create mode 100644 drivers/pci/controller/intel-nvme-remap.c create mode 100644 drivers/platform/x86/legion-laptop.c create mode 100644 drivers/platform/x86/steamdeck.c diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt -index 9e5bab29685f..794e7a91219a 100644 +index a1457995fd41..0b33c7960259 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt -@@ -2117,6 +2117,9 @@ - disable - Do not enable intel_pstate as the default - scaling driver for the supported processors -+ enable -+ Enable intel_pstate in-case "disable" was passed -+ previously in the kernel boot parameters - passive - Use intel_pstate as a scaling driver, but configure it - to work with generic cpufreq governors (instead of -@@ -4235,6 +4238,15 @@ +@@ -4250,6 +4250,15 @@ nomsi [MSI] If the PCI_MSI kernel config parameter is enabled, this kernel boot option can be used to disable the use of MSI interrupts system-wide. @@ -3381,10 +3367,10 @@ index 9e5bab29685f..794e7a91219a 100644 Safety option to keep boot IRQs enabled. This should never be necessary. diff --git a/Makefile b/Makefile -index c324529158cc..e83e1d3420f9 100644 +index 47690c28456a..79abb476e260 100644 --- a/Makefile +++ b/Makefile -@@ -818,6 +818,9 @@ KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member) +@@ -819,6 +819,9 @@ KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member) ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE KBUILD_CFLAGS += -O2 KBUILD_RUSTFLAGS += -Copt-level=2 @@ -3394,7 +3380,7 @@ index c324529158cc..e83e1d3420f9 100644 else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE KBUILD_CFLAGS += -Os KBUILD_RUSTFLAGS += -Copt-level=s -@@ -1060,11 +1063,6 @@ KBUILD_CFLAGS += -fno-strict-overflow +@@ -1064,11 +1067,6 @@ KBUILD_CFLAGS += -fno-strict-overflow # Make sure -fstack-check isn't enabled (like gentoo apparently did) KBUILD_CFLAGS += -fno-stack-check @@ -3431,7 +3417,7 @@ index d5181275490e..7d868e148d9a 100644 CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/axs103_smp_defconfig b/arch/arc/configs/axs103_smp_defconfig -index 2f336d99a8cf..777a9f21eb6b 100644 +index 07c89281c2e3..1513324ddb00 100644 --- a/arch/arc/configs/axs103_smp_defconfig +++ b/arch/arc/configs/axs103_smp_defconfig @@ -9,6 +9,7 @@ CONFIG_NAMESPACES=y @@ -3443,7 +3429,7 @@ index 2f336d99a8cf..777a9f21eb6b 100644 CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/haps_hs_defconfig b/arch/arc/configs/haps_hs_defconfig -index 899b2fd5c71d..bda15a876849 100644 +index 8c3ed5d6e6c3..2db643853e8f 100644 --- a/arch/arc/configs/haps_hs_defconfig +++ b/arch/arc/configs/haps_hs_defconfig @@ -11,6 +11,7 @@ CONFIG_NAMESPACES=y @@ -3455,7 +3441,7 @@ index 899b2fd5c71d..bda15a876849 100644 CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set diff --git a/arch/arc/configs/haps_hs_smp_defconfig b/arch/arc/configs/haps_hs_smp_defconfig -index 0d32aac8069f..dbd74fea69aa 100644 +index 61107e8bac33..d764007e5ada 100644 --- a/arch/arc/configs/haps_hs_smp_defconfig +++ b/arch/arc/configs/haps_hs_smp_defconfig @@ -11,6 +11,7 @@ CONFIG_NAMESPACES=y @@ -3467,7 +3453,7 @@ index 0d32aac8069f..dbd74fea69aa 100644 CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arc/configs/hsdk_defconfig b/arch/arc/configs/hsdk_defconfig -index d18378d2c2a6..2396ca417182 100644 +index 4ee2a1507b57..ce6a4431a76d 100644 --- a/arch/arc/configs/hsdk_defconfig +++ b/arch/arc/configs/hsdk_defconfig @@ -9,6 +9,7 @@ CONFIG_NAMESPACES=y @@ -3527,7 +3513,7 @@ index 1419fc946a08..2477b7c80977 100644 # CONFIG_COMPAT_BRK is not set CONFIG_KPROBES=y diff --git a/arch/arc/configs/tb10x_defconfig b/arch/arc/configs/tb10x_defconfig -index 6f0d2be9d926..cf02ad0fc210 100644 +index 941bbadd6bf2..e61132ba4f89 100644 --- a/arch/arc/configs/tb10x_defconfig +++ b/arch/arc/configs/tb10x_defconfig @@ -14,6 +14,7 @@ CONFIG_INITRAMFS_SOURCE="../tb10x-rootfs.cpio" @@ -3563,7 +3549,7 @@ index 944b347025fd..ed64319f7eb2 100644 CONFIG_PERF_EVENTS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu -index 542377cd419d..c54f7f062288 100644 +index 00468adf180f..46cc91cb622f 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -157,7 +157,7 @@ config MPENTIUM4 @@ -4096,18 +4082,9 @@ index 542377cd419d..c54f7f062288 100644 config IA32_FEAT_CTL def_bool y diff --git a/arch/x86/Makefile b/arch/x86/Makefile -index fdc2e3abd615..c7463d290ce7 100644 +index fdc2e3abd615..3787493b0247 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile -@@ -67,7 +67,7 @@ export BITS - # - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53383 - # --KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -+KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -O3 -fno-tree-vectorize - KBUILD_RUSTFLAGS += -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2 - - ifeq ($(CONFIG_X86_KERNEL_IBT),y) @@ -151,8 +151,48 @@ else # FIXME - should be integrated in Makefile.cpu (Makefile_32.cpu) cflags-$(CONFIG_MK8) += -march=k8 @@ -4159,78 +4136,6 @@ index fdc2e3abd615..c7463d290ce7 100644 cflags-$(CONFIG_GENERIC_CPU) += -mtune=generic KBUILD_CFLAGS += $(cflags-y) -diff --git a/arch/x86/Makefile.postlink b/arch/x86/Makefile.postlink -new file mode 100644 -index 000000000000..936093d29160 ---- /dev/null -+++ b/arch/x86/Makefile.postlink -@@ -0,0 +1,47 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# =========================================================================== -+# Post-link x86 pass -+# =========================================================================== -+# -+# 1. Separate relocations from vmlinux into vmlinux.relocs. -+# 2. Strip relocations from vmlinux. -+ -+PHONY := __archpost -+__archpost: -+ -+-include include/config/auto.conf -+include $(srctree)/scripts/Kbuild.include -+ -+CMD_RELOCS = arch/x86/tools/relocs -+OUT_RELOCS = arch/x86/boot/compressed -+quiet_cmd_relocs = RELOCS $(OUT_RELOCS)/$@.relocs -+ cmd_relocs = \ -+ mkdir -p $(OUT_RELOCS); \ -+ $(CMD_RELOCS) $@ > $(OUT_RELOCS)/$@.relocs; \ -+ $(CMD_RELOCS) --abs-relocs $@ -+ -+quiet_cmd_strip_relocs = RSTRIP $@ -+ cmd_strip_relocs = \ -+ $(OBJCOPY) --remove-section='.rel.*' --remove-section='.rel__*' \ -+ --remove-section='.rela.*' --remove-section='.rela__*' $@ -+ -+# `@true` prevents complaint when there is nothing to be done -+ -+vmlinux: FORCE -+ @true -+ifeq ($(CONFIG_X86_NEED_RELOCS),y) -+ $(call cmd,relocs) -+ $(call cmd,strip_relocs) -+endif -+ -+%.ko: FORCE -+ @true -+ -+clean: -+ @rm -f $(OUT_RELOCS)/vmlinux.relocs -+ -+PHONY += FORCE clean -+ -+FORCE: -+ -+.PHONY: $(PHONY) -diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile -index 6b6cfe607bdb..0f78dbbbdcdd 100644 ---- a/arch/x86/boot/compressed/Makefile -+++ b/arch/x86/boot/compressed/Makefile -@@ -121,11 +121,9 @@ $(obj)/vmlinux.bin: vmlinux FORCE - - targets += $(patsubst $(obj)/%,%,$(vmlinux-objs-y)) vmlinux.bin.all vmlinux.relocs - --CMD_RELOCS = arch/x86/tools/relocs --quiet_cmd_relocs = RELOCS $@ -- cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $< --$(obj)/vmlinux.relocs: vmlinux FORCE -- $(call if_changed,relocs) -+# vmlinux.relocs is created by the vmlinux postlink step. -+$(obj)/vmlinux.relocs: vmlinux -+ @true - - vmlinux.bin.all-y := $(obj)/vmlinux.bin - vmlinux.bin.all-$(CONFIG_X86_NEED_RELOCS) += $(obj)/vmlinux.relocs diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index b40c462b4af3..c4e66e60d559 100644 --- a/arch/x86/include/asm/pci.h @@ -4370,18 +4275,18 @@ index ddb798603201..7c20387d8202 100644 } -#endif diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c -index 3164e3177965..8f1fa5500231 100644 +index 3cce6de464a7..9176bc4f07da 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c -@@ -7618,6 +7618,7 @@ MODULE_ALIAS("bfq-iosched"); +@@ -7627,6 +7627,7 @@ MODULE_ALIAS("bfq-iosched"); static int __init bfq_init(void) { int ret; -+ char msg[60] = "BFQ I/O-scheduler: BFQ-CachyOS v6.4"; ++ char msg[60] = "BFQ I/O-scheduler: BFQ-CachyOS v6.5"; #ifdef CONFIG_BFQ_GROUP_IOSCHED ret = blkcg_policy_register(&blkcg_policy_bfq); -@@ -7649,6 +7650,11 @@ static int __init bfq_init(void) +@@ -7658,6 +7659,11 @@ static int __init bfq_init(void) if (ret) goto slab_kill; @@ -4485,7 +4390,7 @@ index addba109406b..f819ee132ffa 100644 sysfs_add_file_to_group(&pdev->dev.kobj, &dev_attr_remapped_nvme.attr, diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 -index 00476e94db90..c3a219218fac 100644 +index 438c9e75a04d..1bbfeca5f01e 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -9,7 +9,6 @@ config X86_INTEL_PSTATE @@ -4504,21 +4409,8 @@ index 00476e94db90..c3a219218fac 100644 help This driver adds a CPUFreq driver which utilizes a fine grain processor performance frequency control range instead of legacy -diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c -index f29182512b98..873e46f8c459 100644 ---- a/drivers/cpufreq/intel_pstate.c -+++ b/drivers/cpufreq/intel_pstate.c -@@ -3487,6 +3487,8 @@ static int __init intel_pstate_setup(char *str) - - if (!strcmp(str, "disable")) - no_load = 1; -+ else if (!strcmp(str, "enable")) -+ no_load = 0; - else if (!strcmp(str, "active")) - default_driver = &intel_pstate; - else if (!strcmp(str, "passive")) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 87600b4aacb3..9cf3bae52edf 100644 +index 9cfe8fc509d7..efc3b0c0b4ad 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -229,6 +229,15 @@ config I2C_CHT_WC @@ -5221,10 +5113,10 @@ index 809fbd014cd6..d54b35b147ee 100644 /* If the SMBus is still busy, we give up */ if (timeout == MAX_TIMEOUT) { diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c -index 8b47b913ee83..b0209ed12cb7 100644 +index 1dc6227d353e..bab1009ccef7 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c -@@ -3213,6 +3213,11 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) +@@ -3240,6 +3240,11 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad; } @@ -5720,10 +5612,10 @@ index 000000000000..e105e6f5cc91 +MODULE_AUTHOR("Daniel Drake "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index b7c65193e786..1b55bec81999 100644 +index 321156ca273d..5dda26c737e2 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c -@@ -3625,6 +3625,106 @@ static void quirk_no_bus_reset(struct pci_dev *dev) +@@ -3718,6 +3718,106 @@ static void quirk_no_bus_reset(struct pci_dev *dev) dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET; } @@ -5830,7 +5722,7 @@ index b7c65193e786..1b55bec81999 100644 /* * Some NVIDIA GPU devices do not work with bus reset, SBR needs to be * prevented for those affected devices. -@@ -5017,6 +5117,7 @@ static const struct pci_dev_acs_enabled { +@@ -5112,6 +5212,7 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_ZHAOXIN, PCI_ANY_ID, pci_quirk_zhaoxin_pcie_ports_acs }, /* Wangxun nics */ { PCI_VENDOR_ID_WANGXUN, PCI_ANY_ID, pci_quirk_wangxun_nic_acs }, @@ -5839,10 +5731,10 @@ index b7c65193e786..1b55bec81999 100644 }; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig -index 22052031c719..4f2765d6e0d9 100644 +index 49c2c4cd8d00..956f4eff85b5 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig -@@ -642,6 +642,16 @@ config THINKPAD_LMI +@@ -643,6 +643,16 @@ config THINKPAD_LMI To compile this driver as a module, choose M here: the module will be called think-lmi. @@ -5859,7 +5751,7 @@ index 22052031c719..4f2765d6e0d9 100644 source "drivers/platform/x86/intel/Kconfig" config MSI_EC -@@ -1093,6 +1103,20 @@ config WINMATE_FM07_KEYS +@@ -1094,6 +1104,20 @@ config WINMATE_FM07_KEYS buttons below the display. This module adds an input device that delivers key events when these buttons are pressed. @@ -5881,13 +5773,13 @@ index 22052031c719..4f2765d6e0d9 100644 config P2SB diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile -index 2cafe51ec4d8..e4276599c334 100644 +index 52dfdf574ac2..d32b6d87219f 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile -@@ -66,6 +66,7 @@ obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o - obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o +@@ -66,6 +66,7 @@ obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o + obj-$(CONFIG_YOGABOOK) += lenovo-yogabook.o +obj-$(CONFIG_LEGION_LAPTOP) += legion-laptop.o # Intel @@ -9218,7 +9110,7 @@ index 000000000000..77a6677ec19e +MODULE_DESCRIPTION("Steam Deck ACPI platform driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mm.h b/include/linux/mm.h -index 9e10485f37e7..3c6c4c836da7 100644 +index 2dd73e4f3d8e..e0706755c7c3 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -191,7 +191,7 @@ static inline void __mm_zero_struct_page(struct page *page) @@ -9231,10 +9123,10 @@ index 9e10485f37e7..3c6c4c836da7 100644 extern int sysctl_max_map_count; diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h -index a56308a9d1a4..08328b5793b5 100644 +index 716953ee1ebd..dace360dc38d 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h -@@ -1179,7 +1179,7 @@ struct readahead_control { +@@ -1181,7 +1181,7 @@ struct readahead_control { ._index = i, \ } @@ -9266,7 +9158,7 @@ index 45f09bec02c4..87b20e2ee274 100644 { return &init_user_ns; diff --git a/init/Kconfig b/init/Kconfig -index 32c24950c4ce..b6d38eccca10 100644 +index f7f65af4ee12..71755cc8ed3e 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -123,6 +123,10 @@ config THREAD_INFO_IN_TASK @@ -9359,7 +9251,7 @@ index 38ef6d06888e..0f78364efd4f 100644 config SCHED_HRTICK diff --git a/kernel/fork.c b/kernel/fork.c -index 8103ffd217e9..f405763e06ae 100644 +index d2e12b6d2b18..95ca80492a37 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -100,6 +100,10 @@ @@ -9373,7 +9265,7 @@ index 8103ffd217e9..f405763e06ae 100644 #include #include #include -@@ -2267,6 +2271,10 @@ __latent_entropy struct task_struct *copy_process( +@@ -2263,6 +2267,10 @@ __latent_entropy struct task_struct *copy_process( if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS)) return ERR_PTR(-EINVAL); @@ -9384,7 +9276,7 @@ index 8103ffd217e9..f405763e06ae 100644 /* * Thread groups must share signals as well, and detached threads * can only be started up within the thread group. -@@ -3420,6 +3428,12 @@ int ksys_unshare(unsigned long unshare_flags) +@@ -3416,6 +3424,12 @@ int ksys_unshare(unsigned long unshare_flags) if (unshare_flags & CLONE_NEWNS) unshare_flags |= CLONE_FS; @@ -9398,7 +9290,7 @@ index 8103ffd217e9..f405763e06ae 100644 if (err) goto bad_unshare_out; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c -index 4da5f3541762..6742b1e1a359 100644 +index a80a73909dc2..b097a9f4d817 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -69,9 +69,13 @@ @@ -9458,7 +9350,7 @@ index 4da5f3541762..6742b1e1a359 100644 #ifdef CONFIG_NUMA_BALANCING /* Restrict the NUMA promotion throughput (MB/s) for each target node. */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c -index bfe53e835524..e58d0e98abad 100644 +index 354a2d294f52..4dc780aa3bcc 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -95,6 +95,9 @@ EXPORT_SYMBOL_GPL(sysctl_long_vals); @@ -9506,10 +9398,10 @@ index 1d8e47bed3f1..fec01d016a35 100644 static DEFINE_MUTEX(userns_state_mutex); diff --git a/mm/Kconfig b/mm/Kconfig -index e3454087fd31..bc617f00f97c 100644 +index 09130434e30d..f772ba88df87 100644 --- a/mm/Kconfig +++ b/mm/Kconfig -@@ -605,7 +605,7 @@ config COMPACTION +@@ -631,7 +631,7 @@ config COMPACTION config COMPACT_UNEVICTABLE_DEFAULT int depends on COMPACTION @@ -9519,7 +9411,7 @@ index e3454087fd31..bc617f00f97c 100644 # diff --git a/mm/page-writeback.c b/mm/page-writeback.c -index 6faa09f1783b..bc1f17fd658e 100644 +index d3f42009bb70..39b9fd060630 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -71,7 +71,11 @@ static long ratelimit_pages = 32; @@ -9547,7 +9439,7 @@ index 6faa09f1783b..bc1f17fd658e 100644 EXPORT_SYMBOL_GPL(dirty_writeback_interval); diff --git a/mm/swap.c b/mm/swap.c -index 423199ee8478..adef27bd3f8b 100644 +index cd8f0150ba3a..42c405a4f114 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -1090,6 +1090,10 @@ void folio_batch_remove_exceptionals(struct folio_batch *fbatch) @@ -9584,7 +9476,7 @@ index b52644771cc4..11a4b0e3b583 100644 /* diff --git a/mm/vmscan.c b/mm/vmscan.c -index 6114a1fc6c68..6aa2d0a0b1d6 100644 +index 1080209a568b..f76aa8268215 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -186,7 +186,11 @@ struct scan_control { @@ -9599,7 +9491,7 @@ index 6114a1fc6c68..6aa2d0a0b1d6 100644 LIST_HEAD(shrinker_list); DECLARE_RWSEM(shrinker_rwsem); -@@ -4558,7 +4562,11 @@ static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc +@@ -4593,7 +4597,11 @@ static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc } /* to protect the working set of the last N jiffies */ @@ -9614,319 +9506,2614 @@ index 6114a1fc6c68..6aa2d0a0b1d6 100644 -- 2.41.0 -From 9be3158c9f2e0568c8f532aebd5a19cde1aaa806 Mon Sep 17 00:00:00 2001 +From 924ab3ea3113d6e31ad314896faee2c528d917ac Mon Sep 17 00:00:00 2001 From: Peter Jung -Date: Wed, 19 Jul 2023 18:49:32 +0200 -Subject: [PATCH 3/8] fixes +Date: Mon, 10 Jul 2023 17:09:16 +0200 +Subject: [PATCH 3/7] ddcci Signed-off-by: Peter Jung --- - Documentation/ABI/stable/sysfs-block | 10 + - .../testing/sysfs-class-led-trigger-blkdev | 78 ++ - Documentation/leds/index.rst | 1 + - Documentation/leds/ledtrig-blkdev.rst | 158 +++ - drivers/bluetooth/btusb.c | 3 +- - drivers/leds/trigger/Kconfig | 9 + - drivers/leds/trigger/Makefile | 1 + - drivers/leds/trigger/ledtrig-blkdev.c | 1221 +++++++++++++++++ - include/linux/pageblock-flags.h | 2 +- - kernel/padata.c | 4 +- - lib/decompress_inflate.c | 2 +- - lib/decompress_unxz.c | 2 + - lib/decompress_unzstd.c | 2 + - scripts/Makefile.vmlinux_o | 2 +- - sound/pci/hda/cs35l41_hda.c | 2 +- - 15 files changed, 1490 insertions(+), 7 deletions(-) - create mode 100644 Documentation/ABI/testing/sysfs-class-led-trigger-blkdev - create mode 100644 Documentation/leds/ledtrig-blkdev.rst - create mode 100644 drivers/leds/trigger/ledtrig-blkdev.c + drivers/char/Kconfig | 11 + + drivers/char/Makefile | 1 + + drivers/char/ddcci.c | 1909 +++++++++++++++++++++ + drivers/video/backlight/Kconfig | 11 + + drivers/video/backlight/Makefile | 1 + + drivers/video/backlight/ddcci-backlight.c | 413 +++++ + include/linux/ddcci.h | 164 ++ + 7 files changed, 2510 insertions(+) + create mode 100644 drivers/char/ddcci.c + create mode 100644 drivers/video/backlight/ddcci-backlight.c + create mode 100644 include/linux/ddcci.h -diff --git a/Documentation/ABI/stable/sysfs-block b/Documentation/ABI/stable/sysfs-block -index c57e5b7cb532..2d1df6c9b463 100644 ---- a/Documentation/ABI/stable/sysfs-block -+++ b/Documentation/ABI/stable/sysfs-block -@@ -101,6 +101,16 @@ Description: - devices that support receiving integrity metadata. +diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig +index 625af75833fc..3930aeb8e17b 100644 +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -422,4 +422,15 @@ config ADI + and SSM (Silicon Secured Memory). Intended consumers of this + driver include crash and makedumpfile. ++config DDCCI ++ tristate "DDCCI display protocol support" ++ depends on I2C ++ help ++ Display Data Channel Command Interface is an ++ interface that allows the kernel to "talk" ++ to most displays made after 2005. Check your ++ display's specification to see if it has ++ support for this. This depends on I2C to ++ compile. ++ + endmenu +diff --git a/drivers/char/Makefile b/drivers/char/Makefile +index c5f532e412f1..b12476014311 100644 +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -3,6 +3,7 @@ + # Makefile for the kernel character device drivers. + # -+What: /sys/block//linked_leds -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Directory that contains symbolic links to all LEDs that -+ are associated with (linked to) this block device by the -+ blkdev LED trigger. Only present when at least one LED -+ is linked. (See Documentation/leds/ledtrig-blkdev.rst.) -+ -+ - What: /sys/block///alignment_offset - Date: April 2009 - Contact: Martin K. Petersen -diff --git a/Documentation/ABI/testing/sysfs-class-led-trigger-blkdev b/Documentation/ABI/testing/sysfs-class-led-trigger-blkdev ++obj-$(CONFIG_DDCCI) += ddcci.o + obj-y += mem.o random.o + obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o + obj-y += misc.o +diff --git a/drivers/char/ddcci.c b/drivers/char/ddcci.c new file mode 100644 -index 000000000000..28ce8c814fb7 +index 000000000000..129aede43651 --- /dev/null -+++ b/Documentation/ABI/testing/sysfs-class-led-trigger-blkdev -@@ -0,0 +1,78 @@ -+What: /sys/class/leds//blink_time -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Time (in milliseconds) that the LED will be on during a single -+ "blink". ++++ b/drivers/char/ddcci.c +@@ -0,0 +1,1909 @@ ++/* ++ * DDC/CI sub-bus driver ++ * ++ * Copyright (c) 2015 Christoph Grenz ++ */ + -+What: /sys/class/leds//check_interval -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Interval (in milliseconds) between checks of the block devices -+ linked to this LED. The LED will be blinked if the correct type -+ of activity (see blink_on_{read,write,discard,flush} attributes) -+ has occurred on any of the linked devices since the previous -+ check. ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ + -+What: /sys/class/leds//blink_on_read -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Boolean that determines whether the LED will blink in response -+ to read activity on any of its linked block devices. ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+What: /sys/class/leds//blink_on_write -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Boolean that determines whether the LED will blink in response -+ to write activity on any of its linked block devices. ++#include + -+What: /sys/class/leds//blink_on_discard -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Boolean that determines whether the LED will blink in response -+ to discard activity on any of its linked block devices. ++#define DDCCI_RECV_BUFFER_SIZE 130 ++#define DEVICE_NAME "ddcci" ++#define DDCCI_MAX_CAP_CHUNKS 200 + -+What: /sys/class/leds//blink_on_flush -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Boolean that determines whether the LED will blink in response -+ to cache flush activity on any of its linked block devices. ++static unsigned int delay = 60; ++static unsigned short autoprobe_addrs[127] = {0xF0, 0xF2, 0xF4, 0xF6, 0xF8}; ++static int autoprobe_addr_count = 5; + -+What: /sys/class/leds//link_dev_by_path -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Associate a block device with this LED by writing the path to -+ the device special file (e.g. /dev/sda) to this attribute. -+ Symbolic links are followed. ++static dev_t ddcci_cdev_first; ++static dev_t ddcci_cdev_next; ++static dev_t ddcci_cdev_end; ++static DEFINE_SEMAPHORE(core_lock, 1); + -+What: /sys/class/leds//unlink_dev_by_path -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Remove the association between this LED and a block device by -+ writing the path to the device special file (e.g. /dev/sda) to -+ this attribute. Symbolic links are followed. ++struct bus_type ddcci_bus_type; ++EXPORT_SYMBOL_GPL(ddcci_bus_type); ++static bool ddcci_bus_registered; + -+What: /sys/class/leds//unlink_dev_by_name -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Remove the association between this LED and a block device by -+ writing the kernel name of the device (e.g. sda) to this -+ attribute. ++/* Assert neccessary string array sizes */ ++#ifndef sizeof_field ++# define sizeof_field(t,m) FIELD_SIZEOF(t,m) ++#endif ++static_assert(sizeof_field(struct ddcci_device, prot) > 8); ++static_assert(sizeof_field(struct ddcci_device, type) > 8); ++static_assert(sizeof_field(struct ddcci_device, model) > 8); ++static_assert(sizeof_field(struct ddcci_device, vendor) > 8); ++static_assert(sizeof_field(struct ddcci_device, module) > 8); + -+What: /sys/class/leds//linked_devices -+Date: January 2023 -+Contact: Ian Pilcher -+Description: -+ Directory containing links to all block devices that are -+ associated with this LED. (Note that the names of the -+ symbolic links in this directory are *kernel* names, which -+ may not match the device special file paths written to -+ link_device and unlink_device.) -diff --git a/Documentation/leds/index.rst b/Documentation/leds/index.rst -index ce57254cb871..bcbd55bf1149 100644 ---- a/Documentation/leds/index.rst -+++ b/Documentation/leds/index.rst -@@ -10,6 +10,7 @@ LEDs - leds-class - leds-class-flash - leds-class-multicolor -+ ledtrig-blkdev - ledtrig-oneshot - ledtrig-transient - ledtrig-usbport -diff --git a/Documentation/leds/ledtrig-blkdev.rst b/Documentation/leds/ledtrig-blkdev.rst ++/* Internal per-i2c-client driver data */ ++struct ddcci_bus_drv_data { ++ unsigned long quirks; ++ struct i2c_client *i2c_dev; ++ struct semaphore sem; ++ unsigned char recv_buffer[DDCCI_RECV_BUFFER_SIZE]; ++}; ++ ++/* Replace non-alphanumeric characters in a string (used for modalias) */ ++static void ddcci_modalias_clean(char *string, size_t n, char replacement) ++{ ++ int i; ++ for (i = 0; i < n; ++i) { ++ char c = string[i]; ++ if (c == 0) { ++ return; ++ } else if (c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || c > 'z') { ++ string[i] = replacement; ++ } ++ } ++} ++ ++/* Write a message to the DDC/CI bus using i2c_smbus_write_byte() */ ++static int __ddcci_write_bytewise(struct i2c_client *client, unsigned char addr, ++ bool p_flag, const unsigned char * __restrict buf, ++ unsigned char len) ++{ ++ int ret = 0; ++ unsigned char outer_addr = (unsigned char)(client->addr << 1); ++ unsigned xor = outer_addr; /* initial xor value */ ++ ++ /* Consistency checks */ ++ if (len > 127) ++ return -EINVAL; ++ ++ /* Special case: sender to 0x6E is always 0x51 */ ++ if (addr == DDCCI_DEFAULT_DEVICE_ADDR) { ++ addr = DDCCI_HOST_ADDR_ODD; ++ } else { ++ /* When sending the odd address is used */ ++ addr = addr | 1; ++ } ++ ++ /* first byte: sender address */ ++ xor ^= addr; ++ ret = i2c_smbus_write_byte(client, addr); ++ if (ret < 0) ++ return ret; ++ ++ /* second byte: protocol flag and message size */ ++ xor ^= ((p_flag << 7) | len); ++ ret = i2c_smbus_write_byte(client, (p_flag << 7)|len); ++ if (ret < 0) ++ return ret; ++ ++ /* send payload */ ++ while (len--) { ++ xor ^= (*buf); ++ ret = i2c_smbus_write_byte(client, (*buf)); ++ if (ret < 0) ++ return ret; ++ buf++; ++ } ++ ++ /* send checksum */ ++ ret = i2c_smbus_write_byte(client, xor); ++ return ret; ++} ++ ++/* Write a message to the DDC/CI bus using i2c_master_send() */ ++static int __ddcci_write_block(struct i2c_client *client, unsigned char addr, ++ unsigned char *sendbuf, bool p_flag, ++ const unsigned char *data, unsigned char len) ++{ ++ unsigned char outer_addr = (unsigned char)(client->addr << 1); ++ unsigned xor = outer_addr; /* initial xor value */ ++ unsigned char *ptr = sendbuf; ++ ++ /* Consistency checks */ ++ if (len > 127) ++ return -EINVAL; ++ ++ /* Special case: sender to 0x6E is always 0x51 */ ++ if (addr == DDCCI_DEFAULT_DEVICE_ADDR) { ++ addr = DDCCI_HOST_ADDR_ODD; ++ } else { ++ /* When sending the odd address is used */ ++ addr = addr | 1; ++ } ++ ++ /* first byte: sender address */ ++ xor ^= addr; ++ *(ptr++) = addr; ++ /* second byte: protocol flag and message size */ ++ xor ^= ((p_flag << 7) | len); ++ *(ptr++) = (p_flag << 7)|len; ++ /* payload */ ++ while (len--) { ++ xor ^= (*data); ++ *(ptr++) = (*data); ++ data++; ++ } ++ /* checksum */ ++ (*ptr) = xor; ++ ++ /* Send it */ ++ return i2c_master_send(client, sendbuf, ptr - sendbuf + 1); ++} ++ ++/* ++ * Write a message to the DDC/CI bus. ++ * ++ * You must hold the bus semaphore when calling this function. ++ */ ++static int ddcci_write(struct i2c_client *client, unsigned char addr, ++ bool p_flag, const unsigned char *data, ++ unsigned char len) ++{ ++ struct ddcci_bus_drv_data *drv_data; ++ unsigned char *sendbuf; ++ int ret; ++ ++ drv_data = i2c_get_clientdata(client); ++ ++ ++ pr_debug("sending to %d:%02x:%02x: %*ph\n", client->adapter->nr, ++ client->addr << 1, addr, len, data); ++ if (drv_data->quirks & DDCCI_QUIRK_WRITE_BYTEWISE) { ++ ret = __ddcci_write_bytewise(client, addr, p_flag, data, len); ++ } else { ++ sendbuf = drv_data->recv_buffer; ++ ret = __ddcci_write_block(client, addr, sendbuf, p_flag, data, len); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Read a response from the DDC/CI bus with headers directly into a buffer. ++ * Always check for DDCCI_QUIRK_SKIP_FIRST_BYTE when using this function. ++ * The returned length contains the whole unmodified response. ++ * If -EMSGSIZE is returned, the buffer contains the response up to `len`. ++ * If any other negative error code is returned, the buffer content is ++ * unspecified. ++ */ ++static int __ddcci_read(struct i2c_client *client, unsigned char addr, ++ bool p_flag, unsigned long quirks, unsigned char *buf, ++ unsigned char len) ++{ ++ int i, payload_len, packet_length, ret; ++ unsigned char xor = DDCCI_HOST_ADDR_EVEN; ++ ++ /* Consistency checks */ ++ if (len < 3) ++ return -EINVAL; ++ ++ /* Read frame */ ++ ret = i2c_master_recv(client, buf, len); ++ if (ret < 0) ++ goto out_err; ++ packet_length = ret; ++ ++ /* Skip first byte if quirk active */ ++ if ((quirks & DDCCI_QUIRK_SKIP_FIRST_BYTE) && ret > 0 && len > 0) { ++ ret--; ++ len--; ++ buf++; ++ } ++ ++ /* If answer too short (= incomplete) break out */ ++ if (ret < 3) { ++ ret = -EIO; ++ goto out_err; ++ } ++ ++ /* validate first byte */ ++ if (unlikely(buf[0] != addr)) { ++ ret = (buf[0] == '\0') ? -EAGAIN : -EIO; ++ goto out_err; ++ } ++ ++ /* validate second byte (protocol flag) */ ++ if (unlikely((buf[1] & 0x80) != (p_flag << 7))) { ++ if (!p_flag || !(quirks & DDCCI_QUIRK_NO_PFLAG)) { ++ ret = -EIO; ++ goto out_err; ++ } ++ } ++ ++ /* get and check payload length */ ++ payload_len = buf[1] & 0x7F; ++ if (3+payload_len > packet_length) ++ return -EBADMSG; ++ if (3+payload_len > len) ++ return -EMSGSIZE; ++ ++ /* calculate checksum */ ++ for (i = 0; i < 3+payload_len; i++) ++ xor ^= buf[i]; ++ ++ /* verify checksum */ ++ if (xor != 0) { ++ dev_err(&client->dev, "invalid DDC/CI response, corrupted data - xor is 0x%02x, length 0x%02x\n", ++ xor, payload_len); ++ ret = -EBADMSG; ++ goto out_err; ++ } ++ ++ /* return result */ ++ ret = payload_len+3+((quirks & DDCCI_QUIRK_SKIP_FIRST_BYTE)?1:0); ++ ++out_err: ++ return ret; ++} ++ ++/* ++ * Read a response from the DDC/CI bus ++ * ++ * You must hold the bus semaphore when calling this function. ++ */ ++static int ddcci_read(struct i2c_client *client, unsigned char addr, ++ bool p_flag, unsigned char *buf, unsigned char len) ++{ ++ struct ddcci_bus_drv_data *drv_data; ++ unsigned char *recvbuf; ++ int ret; ++ ++ drv_data = i2c_get_clientdata(client); ++ recvbuf = drv_data->recv_buffer; ++ ++ /* Read frame */ ++ ret = __ddcci_read(client, addr, p_flag, ++ drv_data->quirks, recvbuf, DDCCI_RECV_BUFFER_SIZE); ++ if (ret < 0) ++ return ret; ++ ++ if (drv_data->quirks & DDCCI_QUIRK_SKIP_FIRST_BYTE) ++ recvbuf++; ++ ++ /* return result */ ++ if (buf) { ++ if (ret > 3) { ++ ret = ret-3; ++ /* copy to caller buffer */ ++ memcpy(buf, &recvbuf[2], (ret < len) ? ret : len); ++ ++ if (ret > len) { ++ /* if message was truncated, return -EMSGSIZE */ ++ pr_debug("received from %d:%02x:%02x: [%u/%u] %*ph ...\n", ++ client->adapter->nr, client->addr << 1, ++ addr, ret, len, len, buf); ++ ret = -EMSGSIZE; ++ } else { ++ pr_debug("received from %d:%02x:%02x: [%u/%u] %*ph\n", ++ client->adapter->nr, client->addr << 1, ++ addr, ret, len, ret, buf); ++ } ++ } ++ } ++ if (!(drv_data->quirks & DDCCI_QUIRK_WRITE_BYTEWISE)) { ++ /* second read to clear buffers, needed on some devices */ ++ __ddcci_read(client, addr, true, drv_data->quirks, recvbuf, 1); ++ } ++ return ret; ++} ++ ++/* Request the capability string for a device and put it into buf */ ++static int ddcci_get_caps(struct i2c_client *client, unsigned char addr, ++ unsigned char *buf, unsigned int len) ++{ ++ int result = 0, counter = 0, offset = 0; ++ unsigned char cmd[3] = { DDCCI_COMMAND_CAPS, 0x00, 0x00 }; ++ unsigned char *chunkbuf = kzalloc(35, GFP_KERNEL); ++ ++ if (!chunkbuf) ++ return -ENOMEM; ++ ++ do { ++ /* Send command */ ++ result = ddcci_write(client, addr, true, cmd, sizeof(cmd)); ++ if (result < 0) ++ goto err_free; ++ msleep(delay); ++ /* read result chunk */ ++ result = ddcci_read(client, addr, true, chunkbuf, ++ (len > 32) ? 35 : len+3); ++ if (result < 0) ++ goto err_free; ++ ++ if (result > 0) { ++ /* check chunk header */ ++ if (chunkbuf[0] != DDCCI_REPLY_CAPS) { ++ result = -EIO; ++ goto err_free; ++ } ++ if (chunkbuf[1] != cmd[1] || chunkbuf[2] != cmd[2]) { ++ result = -EIO; ++ goto err_free; ++ } ++ if (result < 3) { ++ result = -EIO; ++ goto err_free; ++ } ++ memcpy(buf, chunkbuf+3, min((unsigned int)result-3, len)); ++ ++ counter++; ++ /* adjust offset, etc. */ ++ offset += result-3; ++ len -= result-3; ++ buf += result-3; ++ cmd[1] = offset >> 8; ++ cmd[2] = offset & 0xFF; ++ /* Another superfluous read to make some devices happy... */ ++ ddcci_read(client, addr, true, NULL, 2); ++ } ++ } while (result > 3 && counter < DDCCI_MAX_CAP_CHUNKS); ++ ++ kfree(chunkbuf); ++ return offset+result-3; ++err_free: ++ kfree(chunkbuf); ++ return result; ++} ++ ++/* ++ * Request the device identification and put it into buf. ++ * ++ * Also detects all communication quirks and sets the corresponding flags ++ * in the ddcci_bus_drv_data structure associated with client. ++ * ++ * The identification command will fail on most DDC devices, as it is optional ++ * to support, but even the "failed" response suffices to detect quirks. ++ */ ++static int ddcci_identify_device(struct i2c_client *client, unsigned char addr, ++ unsigned char *buf, unsigned char len) ++{ ++ int i, payload_len, ret = -ENODEV; ++ unsigned long quirks; ++ unsigned char cmd[1] = { DDCCI_COMMAND_ID }; ++ unsigned char *buffer; ++ unsigned char xor = DDCCI_HOST_ADDR_EVEN; ++ struct ddcci_bus_drv_data *bus_drv_data; ++ ++ bus_drv_data = i2c_get_clientdata(client); ++ quirks = bus_drv_data->quirks; ++ buffer = bus_drv_data->recv_buffer; ++ ++ /* Send Identification command */ ++ if (!(quirks & DDCCI_QUIRK_WRITE_BYTEWISE)) { ++ ret = __ddcci_write_block(client, addr, buffer, true, cmd, sizeof(cmd)); ++ dev_dbg(&client->dev, ++ "[%02x:%02x] writing identification command in block mode: %d\n", ++ client->addr << 1, addr, ret); ++ if ((ret == -ENXIO) ++ && i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_WRITE_BYTE)) { ++ quirks |= DDCCI_QUIRK_WRITE_BYTEWISE; ++ dev_info(&client->dev, ++ "DDC/CI bus quirk detected: writes must be done bytewise\n"); ++ /* Some devices need writing twice after a failed blockwise write */ ++ __ddcci_write_bytewise(client, addr, true, cmd, sizeof(cmd)); ++ msleep(delay); ++ } ++ } ++ if (ret < 0 && (quirks & DDCCI_QUIRK_WRITE_BYTEWISE)) { ++ ret = __ddcci_write_bytewise(client, addr, true, cmd, sizeof(cmd)); ++ dev_dbg(&client->dev, ++ "[%02x:%02x] writing identification command in bytewise mode: %d\n", ++ client->addr << 1, addr, ret); ++ } ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* Wait */ ++ msleep(delay); ++ ++ /* Receive response */ ++ ret = i2c_master_recv(client, buffer, DDCCI_RECV_BUFFER_SIZE); ++ if (ret < 0) { ++ dev_dbg(&client->dev, ++ "[%02x:%02x] receiving identification response resulted in errno %d\n", ++ client->addr << 1, addr, ret); ++ return ret; ++ } ++ ++ if (ret == 0) { ++ dev_dbg(&client->dev, ++ "[%02x:%02x] no identification response received\n", ++ client->addr << 1, addr); ++ return ret; ++ } ++ ++ /* Skip first byte if quirk already active */ ++ if (quirks & DDCCI_QUIRK_SKIP_FIRST_BYTE && ret > 1) { ++ dev_dbg(&client->dev, ++ "[%02x:%02x] doubled first byte quirk in effect\n", ++ client->addr << 1, addr); ++ ret--; ++ buffer++; ++ } ++ ++ /* If answer too short (= incomplete) break out */ ++ if (ret < 3) { ++ dev_dbg(&client->dev, ++ "[%02x:%02x] identification response is too short (%d bytes)\n", ++ client->addr << 1, addr, ret); ++ return -EIO; ++ } ++ ++ /* validate first byte */ ++ if (buffer[0] != addr) { ++ dev_dbg(&client->dev, ++ "[%02x:%02x] identification response: %*ph\n", ++ client->addr << 1, addr, (ret > 32 ? 32 : ret), buffer); ++ ++ dev_dbg(&client->dev, ++ "[%02x:%02x] identification response invalid (expected first byte %02x, got %02x)\n", ++ client->addr << 1, addr, addr, buffer[0]); ++ return -ENODEV; ++ } ++ ++ /* Check if first byte is doubled (QUIRK_SKIP_FIRST_BYTE) */ ++ if (!(quirks & DDCCI_QUIRK_SKIP_FIRST_BYTE)) { ++ if (buffer[0] == buffer[1]) { ++ quirks |= DDCCI_QUIRK_SKIP_FIRST_BYTE; ++ dev_info(&client->dev, ++ "DDC/CI bus quirk detected: doubled first byte on read\n"); ++ ret--; ++ buffer++; ++ if (ret < 3) ++ return -EIO; ++ } ++ } ++ ++ /* validate second byte (protocol flag) */ ++ if ((buffer[1] & 0x80) != 0x80 && !(quirks & DDCCI_QUIRK_NO_PFLAG)) { ++ dev_info(&client->dev, ++ "DDC/CI bus quirk detected: device omits protocol flag on responses\n"); ++ quirks |= DDCCI_QUIRK_NO_PFLAG; ++ } ++ ++ /* get and check payload length */ ++ payload_len = buffer[1] & 0x7F; ++ if (3+payload_len > ret) { ++ dev_dbg(&client->dev, ++ "[%02x:%02x] identification response: %*ph ...\n", ++ client->addr << 1, addr, ret, buffer); ++ dev_dbg(&client->dev, ++ "[%02x:%02x] identification response was truncated (expected %d bytes, got %d)\n", ++ client->addr << 1, addr, 3+payload_len, ret); ++ return -EBADMSG; ++ } ++ ++ dev_dbg(&client->dev, ++ "[%02x:%02x] identification response: %*ph\n", ++ client->addr << 1, addr, 3+payload_len, buffer); ++ ++ /* calculate checksum */ ++ for (i = 0; i < 3+payload_len; i++) ++ xor ^= buffer[i]; ++ ++ /* verify checksum */ ++ if (xor != 0) { ++ dev_err(&client->dev, ++ "[%02x:%02x] invalid DDC/CI response, corrupted data - xor is 0x%02x, length 0x%02x\n", ++ client->addr << 1, addr, xor, payload_len); ++ return -EBADMSG; ++ } ++ ++ /* save quirks */ ++ bus_drv_data->quirks = quirks; ++ ++ /* return result */ ++ if (payload_len <= len) { ++ ret = payload_len; ++ memcpy(buf, &buffer[2], payload_len); ++ } else { ++ ret = -EMSGSIZE; ++ memcpy(buf, &buffer[2], len); ++ } ++ return ret; ++} ++ ++/* Character device */ ++ ++/* Data structure for an open file handle */ ++struct ddcci_fp_data { ++ struct ddcci_device *dev; ++ bool exclusive; ++ unsigned char buffer[129]; ++}; ++ ++/* Called when the character device is opened */ ++static int ddcci_cdev_open(struct inode *inode, struct file *filp) ++{ ++ struct ddcci_device *dev = container_of(inode->i_cdev, ++ struct ddcci_device, cdev); ++ struct ddcci_fp_data *fp_data = NULL; ++ ++ fp_data = kzalloc(sizeof(struct ddcci_fp_data), GFP_KERNEL); ++ ++ if (!fp_data) ++ return -ENOMEM; ++ ++ fp_data->exclusive = filp->f_flags & O_EXCL; ++ ++ if (fp_data->exclusive) { ++ if (down_write_trylock(&dev->cdev_sem) == 0) { ++ kfree(fp_data); ++ return -EBUSY; ++ } ++ } else { ++ if (down_read_trylock(&dev->cdev_sem) == 0) { ++ kfree(fp_data); ++ return -EBUSY; ++ } ++ } ++ ++ fp_data->dev = dev; ++ filp->private_data = fp_data; ++ ++ return 0; ++} ++ ++/* Called when the character device is closed */ ++static int ddcci_cdev_close(struct inode *inode, struct file *filp) ++{ ++ struct ddcci_fp_data *fp_data = filp->private_data; ++ struct ddcci_device *dev = fp_data->dev; ++ ++ if (fp_data->exclusive) ++ up_write(&dev->cdev_sem); ++ else ++ up_read(&dev->cdev_sem); ++ ++ filp->private_data = NULL; ++ kfree(fp_data); ++ return 0; ++} ++ ++/* Called when reading from the character device */ ++static ssize_t ddcci_cdev_read(struct file *filp, char __user *buffer, ++ size_t length, loff_t *offset) ++{ ++ struct ddcci_fp_data *fp_data = filp->private_data; ++ struct ddcci_device *dev = fp_data->dev; ++ unsigned char *buf = fp_data->buffer; ++ const bool nonblocking = (filp->f_flags & O_NONBLOCK) != 0; ++ int ret = 0; ++ ++ if ((filp->f_mode & FMODE_READ) == 0) ++ return -EBADF; ++ ++ /* Lock mutex */ ++ if (nonblocking) { ++ if (down_trylock(&dev->bus_drv_data->sem)) ++ return -EAGAIN; ++ } else { ++ if (down_interruptible(&dev->bus_drv_data->sem)) ++ return -ERESTARTSYS; ++ } ++ ++ /* Execute read */ ++ ret = ddcci_read(dev->bus_drv_data->i2c_dev, dev->inner_addr, true, buf, ++ length); ++ ++ if (ret > 0) { ++ /* Copy data from user space */ ++ if (copy_to_user(buffer, buf, ret)) { ++ ret = -EFAULT; ++ goto out; ++ } ++ } ++ ++out: ++ up(&dev->bus_drv_data->sem); ++ return ret; ++} ++ ++/* Called when writing to the character device */ ++static ssize_t ddcci_cdev_write(struct file *filp, const char __user *buffer, ++ size_t count, loff_t *offset) ++{ ++ struct ddcci_fp_data *fp_data = filp->private_data; ++ struct ddcci_device *dev = fp_data->dev; ++ unsigned char *buf = fp_data->buffer; ++ const bool nonblocking = (filp->f_flags & O_NONBLOCK) != 0; ++ int ret = 0; ++ ++ if ((filp->f_mode & FMODE_WRITE) == 0) ++ return -EBADF; ++ ++ if (count > 127) ++ return -EINVAL; ++ ++ /* Lock mutex */ ++ if (nonblocking) { ++ if (down_trylock(&dev->bus_drv_data->sem)) ++ return -EAGAIN; ++ } else { ++ if (down_interruptible(&dev->bus_drv_data->sem)) ++ return -ERESTARTSYS; ++ } ++ ++ if (count > 0) { ++ /* Copy data from user space */ ++ if (copy_from_user(buf, buffer, count)) { ++ ret = -EFAULT; ++ goto err_out; ++ } ++ ++ /* Execute write */ ++ ret = ddcci_write(dev->bus_drv_data->i2c_dev, dev->inner_addr, ++ true, buf, count); ++ } ++ ++ if (ret >= 0) { ++ msleep(delay); ++ up(&dev->bus_drv_data->sem); ++ return count; ++ } ++ ++err_out: ++ up(&dev->bus_drv_data->sem); ++ return ret; ++} ++ ++/* Called when seeking the character device */ ++static loff_t ddcci_cdev_seek(struct file *filp, loff_t offset, int anchor) ++{ ++ return -EINVAL; ++} ++ ++static const struct file_operations ddcci_fops = { ++ .owner = THIS_MODULE, ++ .read = ddcci_cdev_read, ++ .write = ddcci_cdev_write, ++ .open = ddcci_cdev_open, ++ .release = ddcci_cdev_close, ++ .llseek = ddcci_cdev_seek ++}; ++ ++/* Set up the character device for a DDC/CI device */ ++static int ddcci_setup_char_device(struct ddcci_device *device) ++{ ++ int ret = -EINVAL; ++ ++ /* Check if free minor exists */ ++ if (ddcci_cdev_next == ddcci_cdev_end) { ++ dev_err(&device->dev, "no free major/minor\n"); ++ ret = -ENFILE; ++ goto out; ++ } ++ ++ /* Initialize rwsem */ ++ init_rwsem(&device->cdev_sem); ++ ++ /* Initialize character device node */ ++ cdev_init(&device->cdev, &ddcci_fops); ++ device->cdev.owner = THIS_MODULE; ++ ++ /* Publish char device */ ++ device->dev.devt = ddcci_cdev_next; ++ ret = cdev_add(&device->cdev, ddcci_cdev_next, 1); ++ if (ret) { ++ device->dev.devt = 0; ++ goto out; ++ } ++ ++ ddcci_cdev_next++; ++out: ++ return ret; ++} ++ ++/* sysfs attributes */ ++ ++static ssize_t ddcci_attr_capabilities_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ size_t len; ++ ++ if (likely(device != NULL)) { ++ len = device->capabilities_len; ++ if (unlikely(len > PAGE_SIZE)) ++ len = PAGE_SIZE; ++ if (len == 0) { ++ ret = len; ++ } else { ++ memcpy(buf, device->capabilities, len); ++ if (likely(len < PAGE_SIZE)) { ++ buf[len] = '\n'; ++ ret = len+1; ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++static ssize_t ddcci_attr_prot_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ size_t len; ++ ++ if (likely(device != NULL)) { ++ len = strnlen(device->prot, sizeof(device->prot)); ++ strncpy(buf, device->prot, PAGE_SIZE); ++ if (len == 0) { ++ ret = len; ++ } else if (likely(len < PAGE_SIZE)) { ++ buf[len] = '\n'; ++ ret = len+1; ++ } else { ++ ret = PAGE_SIZE; ++ } ++ } ++ return ret; ++} ++ ++static ssize_t ddcci_attr_type_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ size_t len; ++ ++ if (likely(device != NULL)) { ++ len = strnlen(device->type, sizeof(device->type)); ++ strncpy(buf, device->type, PAGE_SIZE); ++ if (len == 0) { ++ ret = len; ++ } else if (likely(len < PAGE_SIZE)) { ++ buf[len] = '\n'; ++ ret = len+1; ++ } else { ++ ret = PAGE_SIZE; ++ } ++ } ++ return ret; ++} ++ ++static ssize_t ddcci_attr_model_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ size_t len; ++ ++ if (likely(device != NULL)) { ++ len = strnlen(device->model, sizeof(device->model)); ++ strncpy(buf, device->model, PAGE_SIZE); ++ if (len == 0) { ++ ret = len; ++ } else if (likely(len < PAGE_SIZE)) { ++ buf[len] = '\n'; ++ ret = len+1; ++ } else { ++ ret = PAGE_SIZE; ++ } ++ } ++ return ret; ++} ++ ++static ssize_t ddcci_attr_vendor_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ size_t len; ++ ++ if (likely(device != NULL)) { ++ len = strnlen(device->vendor, sizeof(device->vendor)); ++ strncpy(buf, device->vendor, PAGE_SIZE); ++ if (len == 0) { ++ ret = len; ++ } else if (likely(len < PAGE_SIZE)) { ++ buf[len] = '\n'; ++ ret = len+1; ++ } else { ++ ret = PAGE_SIZE; ++ } ++ } ++ return ret; ++} ++ ++static ssize_t ddcci_attr_module_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ size_t len; ++ ++ if (likely(device != NULL)) { ++ len = strnlen(device->module, sizeof(device->module)); ++ strncpy(buf, device->module, PAGE_SIZE); ++ if (len == 0) { ++ ret = len; ++ } else if (likely(len < PAGE_SIZE)) { ++ buf[len] = '\n'; ++ ret = len+1; ++ } else { ++ ret = PAGE_SIZE; ++ } ++ } ++ return ret; ++} ++ ++static ssize_t ddcci_attr_serial_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ ++ if (likely(device != NULL)) ++ ret = scnprintf(buf, PAGE_SIZE, "%d\n", device->device_number); ++ ++ return ret; ++} ++ ++static ssize_t ddcci_attr_modalias_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ ssize_t ret = -ENOENT; ++ char model[ARRAY_SIZE(device->model)]; ++ char vendor[ARRAY_SIZE(device->model)]; ++ char module[ARRAY_SIZE(device->model)]; ++ ++ if (likely(device != NULL)) { ++ memcpy(model, device->model, sizeof(model)); ++ memcpy(vendor, device->vendor, sizeof(vendor)); ++ memcpy(module, device->module, sizeof(module)); ++ ddcci_modalias_clean(model, sizeof(model), '_'); ++ ddcci_modalias_clean(vendor, sizeof(vendor), '_'); ++ ddcci_modalias_clean(module, sizeof(module), '_'); ++ ++ ret = scnprintf(buf, PAGE_SIZE, "%s%s-%s-%s-%s-%s\n", ++ DDCCI_MODULE_PREFIX, ++ device->prot, ++ device->type, ++ model, ++ vendor, ++ module ++ ); ++ } ++ return ret; ++} ++ ++static DEVICE_ATTR(capabilities, S_IRUGO, ddcci_attr_capabilities_show, NULL); ++static DEVICE_ATTR(idProt, S_IRUGO, ddcci_attr_prot_show, NULL); ++static DEVICE_ATTR(idType, S_IRUGO, ddcci_attr_type_show, NULL); ++static DEVICE_ATTR(idModel, S_IRUGO, ddcci_attr_model_show, NULL); ++static DEVICE_ATTR(idVendor, S_IRUGO, ddcci_attr_vendor_show, NULL); ++static DEVICE_ATTR(idModule, S_IRUGO, ddcci_attr_module_show, NULL); ++static DEVICE_ATTR(idSerial, S_IRUGO, ddcci_attr_serial_show, NULL); ++static DEVICE_ATTR(modalias, S_IRUGO, ddcci_attr_modalias_show, NULL); ++ ++static struct attribute *ddcci_char_device_attrs[] = { ++ &dev_attr_capabilities.attr, ++ &dev_attr_idProt.attr, ++ &dev_attr_idType.attr, ++ &dev_attr_idModel.attr, ++ &dev_attr_idVendor.attr, ++ &dev_attr_idModule.attr, ++ &dev_attr_idSerial.attr, ++ &dev_attr_modalias.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(ddcci_char_device); ++ ++/* DDC/CI bus */ ++ ++static int ddcci_device_uevent(const struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct ddcci_device *device = to_ddcci_device(dev); ++ char model[ARRAY_SIZE(device->model)]; ++ char vendor[ARRAY_SIZE(device->vendor)]; ++ char module[ARRAY_SIZE(device->module)]; ++ ++ memcpy(model, device->model, sizeof(model)); ++ memcpy(vendor, device->vendor, sizeof(vendor)); ++ memcpy(module, device->module, sizeof(module)); ++ ddcci_modalias_clean(model, sizeof(model), '_'); ++ ddcci_modalias_clean(vendor, sizeof(vendor), '_'); ++ ddcci_modalias_clean(module, sizeof(module), '_'); ++ ++ if (add_uevent_var(env, "MODALIAS=%s%s-%s-%s-%s-%s", ++ DDCCI_MODULE_PREFIX, ++ device->prot, ++ device->type, ++ model, ++ vendor, ++ module ++ )) ++ return -ENOMEM; ++ ++ if (device->prot[0]) ++ if (add_uevent_var(env, "DDCCI_PROT=%s", device->prot)) ++ return -ENOMEM; ++ ++ if (device->type[0]) ++ if (add_uevent_var(env, "DDCCI_TYPE=%s", device->type)) ++ return -ENOMEM; ++ ++ if (device->model[0]) ++ if (add_uevent_var(env, "DDCCI_MODEL=%s", device->model)) ++ return -ENOMEM; ++ ++ if (device->vendor[0]) { ++ if (add_uevent_var(env, "DDCCI_VENDOR=%s", device->vendor)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, "DDCCI_MODULE=%s", device->module)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(env, "DDCCI_UNIQ=%d", device->device_number)) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void ddcci_device_release(struct device *dev) ++{ ++ struct ddcci_device *device = to_ddcci_device(dev); ++ struct ddcci_driver *driver; ++ ++ /* Notify driver */ ++ if (dev->driver) { ++ driver = to_ddcci_driver(dev->driver); ++ if (driver->remove) ++ driver->remove(device); ++ } ++ ++ /* Teardown chardev */ ++ if (dev->devt) { ++ down(&core_lock); ++ if (device->cdev.dev == ddcci_cdev_next-1) ++ ddcci_cdev_next--; ++ cdev_del(&device->cdev); ++ up(&core_lock); ++ } ++ ++ /* Free capability string */ ++ if (device->capabilities) { ++ device->capabilities_len = 0; ++ kfree(device->capabilities); ++ } ++ /* Free device */ ++ kfree(device); ++} ++ ++static char *ddcci_devnode(const struct device *dev, ++ umode_t *mode, kuid_t *uid, kgid_t *gid) ++{ ++ struct ddcci_device *device; ++ ++ device = to_ddcci_device(dev); ++ return kasprintf(GFP_KERNEL, "bus/ddcci/%d/display", ++ device->i2c_client->adapter->nr); ++} ++ ++static char *ddcci_dependent_devnode(const struct device *dev, ++ umode_t *mode, kuid_t *uid, kgid_t *gid) ++{ ++ struct ddcci_device *device; ++ ++ device = to_ddcci_device(dev); ++ if (device->flags & DDCCI_FLAG_EXTERNAL) { ++ if (device->outer_addr == device->inner_addr) ++ return kasprintf(GFP_KERNEL, "bus/ddcci/%d/e%02x", ++ device->i2c_client->adapter->nr, ++ device->outer_addr); ++ else ++ return kasprintf(GFP_KERNEL, "bus/ddcci/%d/e%02x%02x", ++ device->i2c_client->adapter->nr, ++ device->outer_addr, device->inner_addr); ++ } else { ++ return kasprintf(GFP_KERNEL, "bus/ddcci/%d/i%02x", ++ device->i2c_client->adapter->nr, ++ device->inner_addr); ++ } ++} ++ ++/* Device type for main DDC/CI devices*/ ++static struct device_type ddcci_device_type = { ++ .name = "ddcci-device", ++ .uevent = ddcci_device_uevent, ++ .groups = ddcci_char_device_groups, ++ .release = ddcci_device_release, ++ .devnode = ddcci_devnode ++}; ++ ++/* Device type for dependent DDC/CI devices*/ ++static struct device_type ddcci_dependent_type = { ++ .name = "ddcci-dependent-device", ++ .uevent = ddcci_device_uevent, ++ .groups = ddcci_char_device_groups, ++ .release = ddcci_device_release, ++ .devnode = ddcci_dependent_devnode ++}; ++ ++/** ++ * ddcci_verify_device - return parameter as ddcci_device, or NULL ++ * @dev: device, probably from some driver model iterator ++ */ ++struct ddcci_device *ddcci_verify_device(struct device *dev) ++{ ++ if (unlikely(!dev)) ++ return NULL; ++ return (dev->type == &ddcci_device_type ++ || dev->type == &ddcci_dependent_type) ++ ? to_ddcci_device(dev) ++ : NULL; ++} ++EXPORT_SYMBOL(ddcci_verify_device); ++ ++/** ++ * ddcci_quirks - Get quirks for DDC/CI device ++ * @dev: Target DDC/CI device ++ */ ++unsigned long ddcci_quirks(struct ddcci_device *dev) ++{ ++ if (unlikely(WARN_ON(!dev))) ++ return ~0L; ++ if (unlikely(WARN_ON(!dev->bus_drv_data))) ++ return ~0L; ++ return dev->bus_drv_data->quirks; ++} ++EXPORT_SYMBOL(ddcci_quirks); ++ ++/** ++ * ddcci_register_driver - register DDC/CI driver ++ * @owner: the owning module ++ * @driver: the driver to register ++ */ ++int ddcci_register_driver(struct module *owner, struct ddcci_driver *driver) ++{ ++ int ret; ++ ++ /* Can't register until after driver model init */ ++ if (unlikely(WARN_ON(!ddcci_bus_registered))) ++ return -EAGAIN; ++ ++ pr_debug("registering driver [%s]\n", driver->driver.name); ++ ++ /* add the driver to the list of ddcci drivers in the driver core */ ++ driver->driver.owner = owner; ++ driver->driver.bus = &ddcci_bus_type; ++ ++ /* When registration returns, the driver core ++ * will have called probe() for all matching-but-unbound devices. ++ */ ++ ret = driver_register(&driver->driver); ++ if (ret) ++ return ret; ++ ++ pr_debug("driver [%s] registered\n", driver->driver.name); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ddcci_register_driver); ++ ++/** ++ * ddcci_del_driver - unregister DDC/CI driver ++ * @driver: the driver being unregistered ++ */ ++void ddcci_del_driver(struct ddcci_driver *driver) ++{ ++ driver_unregister(&driver->driver); ++ pr_debug("driver [%s] unregistered\n", driver->driver.name); ++} ++EXPORT_SYMBOL(ddcci_del_driver); ++ ++/** ++ * ddcci_device_write - Write a message to a DDC/CI device ++ * @dev: Target DDC/CI device ++ * @p_flag: Protocol flag, true for standard control messages ++ * @data: Data that will be written to the device ++ * @length: How many bytes to write ++ * ++ * Writes the message to the device and sleeps (see module parameter 'delay') ++ */ ++int ddcci_device_write(struct ddcci_device *dev, bool p_flag, ++ unsigned char *data, unsigned char length) ++{ ++ int ret; ++ ++ if (down_interruptible(&dev->bus_drv_data->sem)) ++ return -EAGAIN; ++ ++ ret = ddcci_write(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, data, length); ++ msleep(delay); ++ up(&dev->bus_drv_data->sem); ++ return ret; ++} ++EXPORT_SYMBOL(ddcci_device_write); ++ ++/** ++ * ddcci_device_read - Read a response from a DDC/CI device ++ * @dev: Target DDC/CI device ++ * @p_flag: Protocol flag, must match the corresponding write ++ * @buffer: Where to store data read from the device ++ * @length: Buffer size ++ */ ++int ddcci_device_read(struct ddcci_device *dev, bool p_flag, ++ unsigned char *buffer, unsigned char length) ++{ ++ int ret; ++ ++ if (down_interruptible(&dev->bus_drv_data->sem)) ++ return -EAGAIN; ++ ++ ret = ddcci_read(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, buffer, length); ++ up(&dev->bus_drv_data->sem); ++ return ret; ++} ++EXPORT_SYMBOL(ddcci_device_read); ++ ++/** ++ * ddcci_device_writeread - Write a message to a device and read the response ++ * @dev: Target DDC/CI device ++ * @p_flag: Protocol flag, true for standard control messages ++ * @buffer: Buffer used for write and read ++ * @length: How many bytes to write ++ * @maxlength: Buffer size on read ++ * ++ * Writing, sleeping and reading are done without releasing the DDC/CI bus. ++ * This provides atomicity in respect to other DDC/CI accesses on the same bus. ++ */ ++int ddcci_device_writeread(struct ddcci_device *dev, bool p_flag, ++ unsigned char *buffer, unsigned char length, ++ unsigned char maxlength) ++{ ++ int ret; ++ ++ if (down_interruptible(&dev->bus_drv_data->sem)) ++ return -EAGAIN; ++ ++ ret = ddcci_write(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, buffer, length); ++ if (ret < 0) ++ goto err; ++ msleep(delay); ++ ret = ddcci_read(dev->bus_drv_data->i2c_dev, dev->inner_addr, p_flag, buffer, maxlength); ++err: ++ up(&dev->bus_drv_data->sem); ++ return ret; ++} ++EXPORT_SYMBOL(ddcci_device_writeread); ++ ++#define IS_ANY_ID(x) (((x)[0] == '\xFF') && ((x)[7] == '\xFF')) ++ ++/* Check if any device id in the array matches the device and return the matching id */ ++static const struct ddcci_device_id *ddcci_match_id(const struct ddcci_device_id *id, ++ const struct ddcci_device *device) ++{ ++ while (id->prot[0] || id->type[0] || id->model[0] || id->vendor[0] || id->module[0]) { ++ if ((IS_ANY_ID(id->prot) || (strcmp(device->prot, id->prot) == 0)) ++ && (IS_ANY_ID(id->type) || (strcmp(device->type, id->type) == 0)) ++ && (IS_ANY_ID(id->model) || (strcmp(device->model, id->model) == 0)) ++ && (IS_ANY_ID(id->vendor) || (strcmp(device->vendor, id->vendor) == 0)) ++ && (IS_ANY_ID(id->module) || (strcmp(device->module, id->module) == 0))) { ++ return id; ++ } ++ id++; ++ } ++ return NULL; ++} ++ ++static int ddcci_device_match(struct device *dev, struct device_driver *drv) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ struct ddcci_driver *driver; ++ ++ if (!device) ++ return 0; ++ ++ driver = to_ddcci_driver(drv); ++ /* match on an id table if there is one */ ++ if (driver->id_table) ++ return ddcci_match_id(driver->id_table, device) != NULL; ++ ++ return 0; ++} ++ ++static int ddcci_device_probe(struct device *dev) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ struct ddcci_driver *driver; ++ const struct ddcci_device_id *id; ++ int ret = 0; ++ ++ if (!device) ++ return -EINVAL; ++ driver = to_ddcci_driver(dev->driver); ++ ++ id = ddcci_match_id(driver->id_table, device); ++ if (!id) ++ return -ENODEV; ++ ++ if (driver->probe) ++ ret = driver->probe(device, id); ++ ++ return ret; ++} ++ ++static int ddcci_device_remove(struct device *dev) ++{ ++ struct ddcci_device *device = ddcci_verify_device(dev); ++ struct ddcci_driver *driver; ++ int ret = 0; ++ ++ if (!device) ++ return -EINVAL; ++ driver = to_ddcci_driver(dev->driver); ++ ++ if (driver->remove) ++ ret = driver->remove(device); ++ ++ return ret; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) ++static void ddcci_device_remove_void(struct device *dev) ++{ ++ ddcci_device_remove(dev); ++} ++#endif ++ ++/** ++ * DDCCI bus type structure ++ */ ++struct bus_type ddcci_bus_type = { ++ .name = "ddcci", ++ .match = ddcci_device_match, ++ .probe = ddcci_device_probe, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) ++ .remove = ddcci_device_remove_void ++#else ++ .remove = ddcci_device_remove ++#endif ++}; ++ ++/* Main I2C driver */ ++ ++/* Get a pointer to the closing parenthesis */ ++static char *ddcci_capstr_tok(const char *s, int depth) ++{ ++ const char *ptr = s; ++ char *end; ++ ++ if (s == NULL || s[0] == '\0') ++ return NULL; ++ ++ while ((end = strpbrk(ptr, "()"))) { ++ if (!end || depth == INT_MAX) ++ return NULL; ++ if (*end == '(') ++ depth++; ++ else if (depth > 0) ++ depth--; ++ else ++ break; ++ ptr = end+1; ++ } ++ return end; ++} ++ ++/** ++ * ddcci_find_capstr_item - Search capability string for a tag ++ * @capabilities: Capability string to search ++ * @tag: Tag to find ++ * @length: Buffer for the length of the found tag value (optional) ++ * ++ * Return a pointer to the start of the tag value (directly after the '(') on ++ * success and write the length of the value (excluding the ')') into `length`. ++ * ++ * If the tag is not found or another error occurs, an ERR_PTR is returned ++ * and `length` stays untouched. ++ */ ++const char *ddcci_find_capstr_item(const char * capabilities, ++ const char * __restrict tag, ++ size_t *length) ++{ ++ const char *src = capabilities, *ptr; ++ ptrdiff_t len; ++ int taglen = strnlen(tag, 1024); ++ ++ /* Check length of requested tag */ ++ if (unlikely(taglen <= 0 || taglen > 1023)) ++ return ERR_PTR(-EINVAL); ++ ++ /* Find tag */ ++ while (src && (strncmp(src+1, tag, taglen) != 0 || src[1+taglen] != '(')) ++ src = ddcci_capstr_tok(src+1, -1); ++ if (!src || src[0] == '\0') ++ return ERR_PTR(-ENOENT); ++ ++ /* Locate end of value */ ++ src += taglen+2; ++ ptr = ddcci_capstr_tok(src, 0); ++ if (unlikely(!ptr)) ++ return ERR_PTR(-EOVERFLOW); ++ ++ /* Check length of tag data */ ++ len = ptr-src; ++ if (unlikely(len < 0 || len > 65535)) ++ return ERR_PTR(-EMSGSIZE); ++ ++ /* Return pointer and length */ ++ if (likely(length != NULL)) ++ *length = (size_t)len; ++ return src; ++} ++EXPORT_SYMBOL(ddcci_find_capstr_item); ++ ++/* Search the capability string for a tag and copy the value to dest */ ++static int ddcci_cpy_capstr_item(char *dest, const char *src, ++ const char * __restrict tag, size_t maxlen) ++{ ++ const char *ptr; ++ size_t len; ++ ++ /* Find tag */ ++ ptr = ddcci_find_capstr_item(src, tag, &len); ++ if (IS_ERR(ptr)) { ++ return PTR_ERR(ptr); ++ } ++ ++ /* Copy value */ ++ memcpy(dest, ptr, min(len, maxlen)); ++ return 0; ++} ++ ++/* Fill fields in device by parsing the capability string */ ++static int ddcci_parse_capstring(struct ddcci_device *device) ++{ ++ const char *capstr = device->capabilities; ++ int ret = 0; ++ ++ if (!capstr) ++ return -EINVAL; ++ ++ /* capability string start with a paren */ ++ if (capstr[0] != '(') ++ return -EINVAL; ++ ++ /* get prot(...) */ ++ ret = ddcci_cpy_capstr_item(device->prot, capstr, "prot", sizeof(device->prot)-1); ++ if (ret) { ++ if (ret == -ENOENT) { ++ dev_warn(&device->dev, "malformed capability string: no protocol tag"); ++ memset(device->prot, 0, sizeof(device->prot)-1); ++ } else { ++ return ret; ++ } ++ } ++ ++ /* get type(...) */ ++ ret = ddcci_cpy_capstr_item(device->type, capstr, "type", sizeof(device->type)-1); ++ if (ret) { ++ if (ret == -ENOENT) { ++ dev_warn(&device->dev, "malformed capability string: no type tag"); ++ memset(device->type, 0, sizeof(device->type)-1); ++ } else { ++ return ret; ++ } ++ } ++ ++ /* and then model(...) */ ++ ret = ddcci_cpy_capstr_item(device->model, capstr, "model", sizeof(device->model)-1); ++ if (ret) { ++ if (ret == -ENOENT) { ++ dev_warn(&device->dev, "malformed capability string: no model tag"); ++ memset(device->model, 0, sizeof(device->model)-1); ++ } else { ++ return ret; ++ } ++ } ++ ++ /* if there is no protocol tag */ ++ if (!device->prot[0]) { ++ /* and no type tag: give up. */ ++ if (!device->type[0]) ++ return -ENOENT; ++ ++ /* Assume protocol "monitor" if type is "LCD" or "CRT" */ ++ if (strncasecmp(device->type, "LCD", sizeof(device->type)-1) == 0 ++ || strncasecmp(device->type, "CRT", sizeof(device->type)-1) == 0) { ++ memcpy(device->prot, "monitor", 7); ++ } ++ } ++ ++ /* skip the rest for now */ ++ ++ return 0; ++} ++ ++/* Probe for a device on an inner address and create a ddcci_device for it */ ++static int ddcci_detect_device(struct i2c_client *client, unsigned char addr, ++ int dependent) ++{ ++ int ret; ++ unsigned char outer_addr = client->addr << 1; ++ unsigned char *buffer = NULL; ++ struct ddcci_bus_drv_data *drv_data = i2c_get_clientdata(client); ++ struct ddcci_device *device = NULL; ++ ++ down(&drv_data->sem); ++ ++ /* Allocate buffer big enough for any capability string */ ++ buffer = kmalloc(16384, GFP_KERNEL); ++ if (!buffer) { ++ ret = -ENOMEM; ++ goto err_end; ++ } ++ ++ /* Allocate device struct */ ++ device = kzalloc(sizeof(struct ddcci_device), GFP_KERNEL); ++ if (!device) { ++ ret = -ENOMEM; ++ goto err_end; ++ } ++ ++ /* Initialize device */ ++ device_initialize(&device->dev); ++ device->dev.parent = &client->dev; ++ device->dev.bus = &ddcci_bus_type; ++ device->outer_addr = outer_addr; ++ device->inner_addr = addr; ++ device->bus_drv_data = drv_data; ++ device->i2c_client = client; ++ ++ if (!dependent) { ++ device->dev.type = &ddcci_device_type; ++ ret = dev_set_name(&device->dev, "ddcci%d", client->adapter->nr); ++ } else if (outer_addr == dependent) { ++ /* Internal dependent device */ ++ device->dev.type = &ddcci_dependent_type; ++ device->flags = DDCCI_FLAG_DEPENDENT; ++ ret = dev_set_name(&device->dev, "ddcci%di%02x", client->adapter->nr, addr); ++ } else if (outer_addr == addr) { ++ /* External dependent device */ ++ device->dev.type = &ddcci_dependent_type; ++ device->flags = DDCCI_FLAG_DEPENDENT | DDCCI_FLAG_EXTERNAL; ++ ret = dev_set_name(&device->dev, "ddcci%de%02x", client->adapter->nr, addr); ++ } else { ++ /* Dependent device of external dependent device ++ Just in case something like this exists */ ++ device->dev.type = &ddcci_dependent_type; ++ device->flags = DDCCI_FLAG_DEPENDENT | DDCCI_FLAG_EXTERNAL; ++ ret = dev_set_name(&device->dev, "ddcci%de%02x%02x", client->adapter->nr, outer_addr, addr); ++ } ++ ++ if (ret) ++ goto err_free; ++ ++ /* Read identification and check for quirks */ ++ ret = ddcci_identify_device(client, addr, buffer, 29); ++ if (ret < 0) { ++ if (!dependent && (ret == -EBADMSG || ret == -EMSGSIZE)) { ++ dev_warn(&device->dev, "DDC/CI main device sent broken response on identification. Trying to detect solely based on capability information.\n"); ++ } else { ++ goto err_free; ++ } ++ } ++ ++ if (ret == 29 && buffer[0] == DDCCI_REPLY_ID) { ++ memcpy(device->vendor, &buffer[7], 8); ++ memcpy(device->module, &buffer[17], 8); ++ device->device_number = be32_to_cpu(*(__force __be32 *)&buffer[18]); ++ } ++ ++ /* Read capabilities */ ++ ret = ddcci_get_caps(client, addr, buffer, 16384); ++ if (ret > 0) { ++ /* Fixup unparenthesized capability strings, but only if the first ++ character is an ascii lower case letter. ++ This should still allow an early exit for completely garbled ++ data but helps detecting devices where only the parentheses are ++ missing, as the second char must be the first character of a ++ keyword. */ ++ if (ret > 2 && buffer[0] >= 'a' && buffer[0] <= 'z') { ++ dev_err(&device->dev, "DDC/CI device quirk detected: unparenthesized capability string\n"); ++ device->capabilities = kzalloc(ret+3, GFP_KERNEL); ++ if (!device->capabilities) { ++ ret = -ENOMEM; ++ goto err_free; ++ } ++ device->capabilities_len = ret+2; ++ memcpy(&(device->capabilities[1]), buffer, ret); ++ device->capabilities[0] = '('; ++ device->capabilities[ret+1] = ')'; ++ } else { ++ /* Standard case: simply copy the received string */ ++ device->capabilities = kzalloc(ret+1, GFP_KERNEL); ++ if (!device->capabilities) { ++ ret = -ENOMEM; ++ goto err_free; ++ } ++ device->capabilities_len = ret; ++ memcpy(device->capabilities, buffer, ret); ++ } ++ ++ ret = ddcci_parse_capstring(device); ++ if (ret) { ++ dev_err(&device->dev, "malformed capability string: \"%s\" errno %d\n", device->capabilities, ret); ++ ret = -EINVAL; ++ goto err_free; ++ } ++ } ++ ++ /* Found a device if either identification or capabilities succeeded */ ++ if (!device->capabilities && device->vendor[0] == '\0') { ++ dev_dbg(&client->dev, ++ "[%02x:%02x] got neither valid identification nor capability data\n", ++ client->addr << 1, addr); ++ ret = -ENODEV; ++ goto err_free; ++ } ++ ++ /* Setup chardev */ ++ down(&core_lock); ++ ret = ddcci_setup_char_device(device); ++ up(&core_lock); ++ if (ret) ++ goto err_free; ++ ++ /* Release semaphore and add device to the tree */ ++ up(&drv_data->sem); ++ pr_debug("found device at %d:%02x:%02x\n", client->adapter->nr, outer_addr, addr); ++ ret = device_add(&device->dev); ++ if (ret) ++ goto err_free; ++ ++ goto end; ++err_free: ++ put_device(&device->dev); ++err_end: ++ up(&drv_data->sem); ++end: ++ kfree(buffer); ++ return ret; ++} ++ ++/* I2C detect function: check if a main or external dependent device exists */ ++static int ddcci_detect(struct i2c_client *client, struct i2c_board_info *info) ++{ ++ int ret; ++ unsigned char outer_addr; ++ unsigned char inner_addr; ++ unsigned char buf[32]; ++ unsigned char cmd_id[1] = { DDCCI_COMMAND_ID }; ++ unsigned char cmd_caps[3] = { DDCCI_COMMAND_CAPS, 0x00, 0x00}; ++ unsigned char *cmd; ++ unsigned int cmd_len; ++ ++ /* Check for i2c_master_* functionality */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ pr_debug("i2c adapter %d unsuitable: no i2c_master functionality\n", client->adapter->nr); ++ return -ENODEV; ++ } ++ ++ /* send Capabilities Request (for main) or Identification Request command (for dependent devices) */ ++ outer_addr = client->addr << 1; ++ inner_addr = (outer_addr == DDCCI_DEFAULT_DEVICE_ADDR) ? DDCCI_HOST_ADDR_ODD : outer_addr | 1; ++ cmd = (outer_addr == DDCCI_DEFAULT_DEVICE_ADDR) ? cmd_caps : cmd_id; ++ cmd_len = (outer_addr == DDCCI_DEFAULT_DEVICE_ADDR) ? sizeof(cmd_caps) : sizeof(cmd_id); ++ pr_debug("detecting %d:%02x\n", client->adapter->nr, outer_addr); ++ ++ ret = __ddcci_write_block(client, inner_addr, buf, true, cmd, cmd_len); ++ ++ if (ret == -ENXIO || ret == -EIO) { ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) { ++ pr_debug("i2c write failed with ENXIO or EIO but bytewise writing is not supported\n"); ++ return -ENODEV; ++ } ++ pr_debug("i2c write failed with ENXIO or EIO, trying bytewise writing\n"); ++ ret = __ddcci_write_bytewise(client, inner_addr, true, cmd, cmd_len); ++ if (ret == 0) { ++ msleep(delay); ++ ret = __ddcci_write_bytewise(client, inner_addr, true, cmd, cmd_len); ++ } ++ } ++ ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* wait for device */ ++ msleep(delay); ++ /* receive answer */ ++ ret = i2c_master_recv(client, buf, 32); ++ if (ret < 3) { ++ pr_debug("detection failed: no answer\n"); ++ return -ENODEV; ++ } ++ ++ /* check response starts with outer addr */ ++ if (buf[0] != outer_addr) { ++ pr_debug("detection failed: invalid %s response (%02x != %02x)\n", (cmd == cmd_id) ? "identification" : "capabilities", buf[0], outer_addr); ++ pr_debug("received message was %*ph \n", ret, buf); ++ return -ENODEV; ++ } ++ ++ pr_debug("detected %d:%02x\n", client->adapter->nr, outer_addr); ++ ++ /* set device type */ ++ strlcpy(info->type, (outer_addr == DDCCI_DEFAULT_DEVICE_ADDR) ? "ddcci" : "ddcci-dependent", I2C_NAME_SIZE); ++ ++ return 0; ++} ++ ++/* I2C probe function */ ++static int ddcci_probe(struct i2c_client *client) ++{ ++ const struct i2c_device_id *id = i2c_client_get_device_id(client); ++ int i, ret = -ENODEV, tmp; ++ unsigned char main_addr, addr; ++ struct ddcci_bus_drv_data *drv_data; ++ ++ /* Initialize driver data structure */ ++ drv_data = devm_kzalloc(&client->dev, sizeof(struct ddcci_bus_drv_data), GFP_KERNEL); ++ if (!drv_data) ++ return -ENOMEM; ++ drv_data->i2c_dev = client; ++ sema_init(&drv_data->sem, 1); ++ ++ /* Set i2c client data */ ++ i2c_set_clientdata(client, drv_data); ++ ++ if (id->driver_data == 0) { ++ /* Core device, probe at 0x6E */ ++ main_addr = DDCCI_DEFAULT_DEVICE_ADDR; ++ dev_dbg(&client->dev, "probing core device [%02x]\n", ++ client->addr << 1); ++ ret = ddcci_detect_device(client, main_addr, 0); ++ if (ret) { ++ dev_info(&client->dev, "core device [%02x] probe failed: %d\n", ++ client->addr << 1, ret); ++ if (ret == -EIO) ++ ret = -ENODEV; ++ goto err_free; ++ } ++ ++ /* Detect internal dependent devices */ ++ dev_dbg(&client->dev, "probing internal dependent devices\n"); ++ for (i = 0; i < autoprobe_addr_count; ++i) { ++ addr = (unsigned short)autoprobe_addrs[i]; ++ if ((addr & 1) == 0 && addr != main_addr) { ++ tmp = ddcci_detect_device(client, addr, main_addr); ++ if (tmp < 0 && tmp != -ENODEV) { ++ dev_info(&client->dev, "internal dependent device [%02x:%02x] probe failed: %d\n", ++ client->addr << 1, addr, ret); ++ } ++ } ++ } ++ } else if (id->driver_data == 1) { ++ /* External dependent device */ ++ main_addr = client->addr << 1; ++ dev_dbg(&client->dev, "probing external dependent device [%02x]\n", main_addr); ++ ret = ddcci_detect_device(client, main_addr, -1); ++ if (ret) { ++ dev_info(&client->dev, "external dependent device [%02x] probe failed: %d\n", ++ main_addr, ret); ++ if (ret == -EIO) ++ ret = -ENODEV; ++ goto err_free; ++ } ++ } else { ++ dev_warn(&client->dev, ++ "probe() called with invalid i2c device id\n"); ++ ret = -EINVAL; ++ } ++ ++ goto end; ++err_free: ++ devm_kfree(&client->dev, drv_data); ++end: ++ return ret; ++} ++ ++/* ++ * Callback for bus_find_device() used in ddcci_remove() ++ * ++ * Find next device on i2c_client not flagged with ++ * DDCCI_FLAG_REMOVED and flag it. ++ */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0) ++static int ddcci_remove_helper(struct device *dev, const void *p) ++#else ++static int ddcci_remove_helper(struct device *dev, void *p) ++#endif ++{ ++ struct ddcci_device *device; ++ ++ device = ddcci_verify_device(dev); ++ if (!device || device->flags & DDCCI_FLAG_REMOVED) ++ return 0; ++ ++ if (!p || (dev->parent == p)) { ++ device->flags |= DDCCI_FLAG_REMOVED; ++ wmb(); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* I2C driver remove callback: unregister all subdevices */ ++static int ddcci_remove(struct i2c_client *client) ++{ ++ struct ddcci_bus_drv_data *drv_data = i2c_get_clientdata(client); ++ struct device *dev; ++ ++ down(&drv_data->sem); ++ while (1) { ++ dev = bus_find_device(&ddcci_bus_type, NULL, client, ++ ddcci_remove_helper); ++ if (!dev) ++ break; ++ device_unregister(dev); ++ put_device(dev); ++ } ++ up(&drv_data->sem); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) ++static void ddcci_remove_void(struct i2c_client *client) ++{ ++ ddcci_remove(client); ++} ++#endif ++ ++/* ++ * I2C driver device identification table. ++ */ ++static const struct i2c_device_id ddcci_idtable[] = { ++ { "ddcci", 0 }, ++ { "ddcci-dependent", 1 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, ddcci_idtable); ++ ++/* ++ * I2C driver description structure ++ */ ++static struct i2c_driver ddcci_driver = { ++ .driver = { ++ .name = "ddcci", ++ .owner = THIS_MODULE, ++ }, ++ ++ .id_table = ddcci_idtable, ++ .probe = ddcci_probe, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) ++ .remove = ddcci_remove_void, ++#else ++ .remove = ddcci_remove, ++#endif ++ .class = I2C_CLASS_DDC, ++ .detect = ddcci_detect, ++ .address_list = I2C_ADDRS( ++ DDCCI_DEFAULT_DEVICE_ADDR>>1 ++ ), ++}; ++ ++/* ++ * Module initialization function. Called when the module is inserted or ++ * (if builtin) at boot time. ++ */ ++static int __init ddcci_module_init(void) ++{ ++ int ret; ++ ++ pr_debug("initializing ddcci driver\n"); ++ /* Allocate a device number region for the character devices */ ++ ret = alloc_chrdev_region(&ddcci_cdev_first, 0, 128, DEVICE_NAME); ++ if (ret < 0) { ++ pr_err("failed to register device region: error %d\n", ret); ++ goto err_chrdevreg; ++ } ++ ddcci_cdev_next = ddcci_cdev_first; ++ ddcci_cdev_end = MKDEV(MAJOR(ddcci_cdev_first), MINOR(ddcci_cdev_first)+128); ++ ++ /* Register bus */ ++ ret = bus_register(&ddcci_bus_type); ++ if (ret) { ++ pr_err("failed to register bus 'ddcci'\n"); ++ goto err_busreg; ++ } ++ ddcci_bus_registered = true; ++ ++ /* Register I2C driver */ ++ ret = i2c_add_driver(&ddcci_driver); ++ if (ret) { ++ pr_err("failed to register i2c driver\n"); ++ goto err_drvreg; ++ } ++ ++ pr_debug("ddcci driver initialized\n"); ++ ++ return 0; ++ ++err_drvreg: ++ bus_unregister(&ddcci_bus_type); ++err_busreg: ++ unregister_chrdev_region(ddcci_cdev_first, 128); ++err_chrdevreg: ++ return ret; ++} ++ ++/* ++ * Module clean-up function. Called when the module is removed. ++ */ ++static void __exit ddcci_module_exit(void) ++{ ++ struct device *dev; ++ ++ while (1) { ++ dev = bus_find_device(&ddcci_bus_type, NULL, NULL, ddcci_remove_helper); ++ if (!dev) ++ break; ++ device_unregister(dev); ++ put_device(dev); ++ } ++ ++ i2c_del_driver(&ddcci_driver); ++ bus_unregister(&ddcci_bus_type); ++ unregister_chrdev_region(ddcci_cdev_first, 128); ++} ++ ++/* Let the kernel know the calls for module init and exit */ ++module_init(ddcci_module_init); ++module_exit(ddcci_module_exit); ++ ++/* Module parameter description */ ++module_param(delay, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(delay, "default delay after bus writes (in ms, default 60)"); ++module_param_array(autoprobe_addrs, ushort, &autoprobe_addr_count, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(autoprobe_addrs, "internal dependent device addresses to autoprobe"); ++ ++/* Module description */ ++MODULE_AUTHOR("Christoph Grenz"); ++MODULE_DESCRIPTION("DDC/CI bus driver"); ++MODULE_VERSION("0.4.2"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig +index 51387b1ef012..4b8bfd7c02c6 100644 +--- a/drivers/video/backlight/Kconfig ++++ b/drivers/video/backlight/Kconfig +@@ -297,6 +297,17 @@ config BACKLIGHT_QCOM_WLED + If you have the Qualcomm PMIC, say Y to enable a driver for the + WLED block. Currently it supports PM8941 and PMI8998. + ++config BACKLIGHT_DDCCI ++ tristate "DDCCI Backlight Driver" ++ depends on DDCCI ++ help ++ If you have a DDC/CI supporing monitor, say Y to enable a driver ++ to control its backlight using DDC/CI. This could be useful if ++ your monitor does not include a backlight driver. For this to be ++ useful you need to enable DDCCI support which can be found in ++ Device Drivers -> Character devices and that further depends on ++ I2C. ++ + config BACKLIGHT_RT4831 + tristate "Richtek RT4831 Backlight Driver" + depends on MFD_RT4831 +diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile +index f72e1c3c59e9..656dea21c0ee 100644 +--- a/drivers/video/backlight/Makefile ++++ b/drivers/video/backlight/Makefile +@@ -58,3 +58,4 @@ obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o + obj-$(CONFIG_BACKLIGHT_ARCXCNN) += arcxcnn_bl.o + obj-$(CONFIG_BACKLIGHT_RAVE_SP) += rave-sp-backlight.o + obj-$(CONFIG_BACKLIGHT_LED) += led_bl.o ++obj-$(CONFIG_BACKLIGHT_DDCCI) += ddcci-backlight.o +diff --git a/drivers/video/backlight/ddcci-backlight.c b/drivers/video/backlight/ddcci-backlight.c new file mode 100644 -index 000000000000..9ff5b99de451 +index 000000000000..7a9852207f0b --- /dev/null -+++ b/Documentation/leds/ledtrig-blkdev.rst -@@ -0,0 +1,158 @@ -+.. SPDX-License-Identifier: GPL-2.0 ++++ b/drivers/video/backlight/ddcci-backlight.c +@@ -0,0 +1,413 @@ ++/* ++ * DDC/CI monitor backlight driver ++ * ++ * Copyright (c) 2015 Christoph Grenz ++ */ + -+================================= -+Block Device (blkdev) LED Trigger -+================================= ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ + -+Available when ``CONFIG_LEDS_TRIGGER_BLKDEV=y`` or -+``CONFIG_LEDS_TRIGGER_BLKDEV=m``. ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++#include ++#include ++#include ++#include + -+See also: ++#include + -+* ``Documentation/ABI/testing/sysfs-class-led-trigger-blkdev`` -+* ``Documentation/ABI/stable/sysfs-block`` (``/sys/block//linked_leds``) + -+Overview -+======== ++#define DDCCI_COMMAND_READ 0x01 /* read ctrl value */ ++#define DDCCI_REPLY_READ 0x02 /* read ctrl value reply */ ++#define DDCCI_COMMAND_WRITE 0x03 /* write ctrl value */ ++#define DDCCI_COMMAND_SAVE 0x0c /* save current settings */ + -+.. note:: -+ The examples below use ```` to refer to the name of a -+ system-specific LED. If no suitable LED is available on a test -+ system (in a virtual machine, for example), it is possible to -+ use a userspace LED. (See ``Documentation/leds/uleds.rst``.) ++#define DDCCI_MONITOR_LUMINANCE 0x10 ++#define DDCCI_MONITOR_BACKLIGHT 0x13 ++#define DDCCI_MONITOR_BL_WHITE 0x6B + -+Verify that the ``blkdev`` LED trigger is available:: ++static bool convenience_symlink = true; + -+ # grep blkdev /sys/class/leds//trigger -+ ... rfkill-none blkdev ++struct ddcci_monitor_drv_data { ++ struct ddcci_device *device; ++ struct backlight_device *bl_dev; ++ struct device *fb_dev; ++ unsigned char used_vcp; ++}; + -+(If the previous command produces no output, you may need to load the trigger -+module - ``modprobe ledtrig_blkdev``. If the module is not available, check -+the value of ``CONFIG_LEDS_TRIGGER_BLKDEV`` in your kernel configuration.) ++static int ddcci_monitor_writectrl(struct ddcci_device *device, ++ unsigned char ctrl, unsigned short value) ++{ ++ unsigned char buf[4]; ++ int ret; + -+Associate the LED with the ``blkdev`` LED trigger:: ++ buf[0] = DDCCI_COMMAND_WRITE; ++ buf[1] = ctrl; ++ buf[2] = (value >> 8); ++ buf[3] = (value & 255); + -+ # echo blkdev > /sys/class/leds//trigger ++ ret = ddcci_device_write(device, true, buf, sizeof(buf)); + -+ # cat /sys/class/leds//trigger -+ ... rfkill-none [blkdev] ++ return ret; ++} + -+Note that several new device attributes are available in the -+``/sys/class/leds/`` directory. ++static int ddcci_monitor_readctrl(struct ddcci_device *device, ++ unsigned char ctrl, unsigned short *value, ++ unsigned short *maximum) ++{ ++ int ret; ++ unsigned char buf[10]; + -+* ``link_dev_by_path``, ``unlink_dev_by_path``, and ``unlink_dev_by_name`` are -+ used to manage the set of block devices associated with this LED. The LED -+ will blink when activity occurs on any of its linked devices. ++ buf[0] = DDCCI_COMMAND_READ; ++ buf[1] = ctrl; + -+* ``blink_on_read``, ``blink_on_write``, ``blink_on_discard``, and -+ ``blink_on_flush`` are boolean values that determine whether the LED will -+ blink when a particular type of activity is detected on one of its linked -+ block devices. ++ ret = ddcci_device_writeread(device, true, buf, 2, sizeof(buf)); ++ if (ret < 0) ++ return ret; + -+* ``blink_time`` is the duration (in milliseconds) of each blink of this LED. -+ (The minimum value is 10 milliseconds.) ++ if (ret == 0) ++ return -ENOTSUPP; + -+* ``check_interval`` is the frequency (in milliseconds) with which block devices -+ linked to this LED will be checked for activity and the LED blinked (if the -+ correct type of activity has occurred). ++ if (ret == 8 && buf[0] == DDCCI_REPLY_READ && buf[2] == ctrl) { ++ if (value) ++ *value = buf[6] * 256 + buf[7]; + -+* The ``linked_devices`` directory will contain a symbolic link to every device -+ that is associated with this LED. ++ if (maximum) ++ *maximum = buf[4] * 256 + buf[5]; + -+Link a block device to the LED:: ++ if (buf[1] == 1) ++ return -ENOTSUPP; ++ if (buf[1] != 0) ++ return -EIO; ++ return 0; ++ } + -+ # echo /dev/sda > /sys/class/leds//link_dev_by_path ++ return -EIO; ++} + -+ # ls /sys/class/leds//linked_devices -+ sda ++static int ddcci_backlight_check_fb(struct backlight_device *bl, ++ struct fb_info *info) ++{ ++ struct ddcci_monitor_drv_data *drv_data = bl_get_data(bl); + -+(The value written to ``link_dev_by_path`` must be the path of the device -+special file, such as ``/dev/sda``, that represents the block device - or the -+path of a symbolic link to such a device special file.) ++ return drv_data->fb_dev == NULL || drv_data->fb_dev == info->dev; ++} + -+Activity on the device will now cause the LED to blink. The duration of each -+blink (in milliseconds) can be adjusted by setting -+``/sys/class/leds//blink_time``. (But see **check_interval and -+blink_time** below.) ++static int ddcci_backlight_update_status(struct backlight_device *bl) ++{ ++ struct ddcci_monitor_drv_data *drv_data = bl_get_data(bl); ++ int brightness = bl->props.brightness; ++ int ret; + -+Associate a second device with the LED:: ++ if (bl->props.power != FB_BLANK_UNBLANK || ++ bl->props.state & BL_CORE_FBBLANK) ++ brightness = 0; + -+ # echo /dev/sdb > /sys/class/leds//link_dev_by_path ++ ret = ddcci_monitor_writectrl(drv_data->device, drv_data->used_vcp, ++ brightness); ++ if (ret > 0) ++ ret = 0; ++ return ret; ++} + -+ # ls /sys/class/leds//linked_devices -+ sda sdb ++static int ddcci_backlight_get_brightness(struct backlight_device *bl) ++{ ++ unsigned short value = 0, maxval = 0; ++ int ret; ++ struct ddcci_monitor_drv_data *drv_data = bl_get_data(bl); + -+When a block device is linked to one or more LEDs, the LEDs are linked from -+the device's ``linked_leds`` directory:: ++ ret = ddcci_monitor_readctrl(drv_data->device, drv_data->used_vcp, ++ &value, &maxval); ++ if (ret < 0) ++ return ret; + -+ # ls /sys/class/block/sd{a,b}/linked_leds -+ /sys/class/block/sda/linked_leds: -+ ++ bl->props.brightness = value; ++ bl->props.max_brightness = maxval; ++ ret = value; + -+ /sys/class/block/sdb/linked_leds: -+ ++ return ret; ++} + -+(The ``linked_leds`` directory only exists when the block device is linked to -+at least one LED.) ++static const struct backlight_ops ddcci_backlight_ops = { ++ .options = 0, ++ .update_status = ddcci_backlight_update_status, ++ .get_brightness = ddcci_backlight_get_brightness, ++ .check_fb = ddcci_backlight_check_fb, ++}; + -+``check_interval`` and ``blink_time`` -+===================================== ++static const char *ddcci_monitor_vcp_name(unsigned char vcp) ++{ ++ switch (vcp) { ++ case DDCCI_MONITOR_BL_WHITE: ++ return "backlight"; ++ case DDCCI_MONITOR_LUMINANCE: ++ return "luminance"; ++ default: ++ return "???"; ++ } ++} + -+* By default, linked block devices are checked for activity every 100 -+ milliseconds. This frequency can be changed for an LED via the -+ ``/sys/class/leds//check_interval`` attribute. (The minimum value is 25 -+ milliseconds.) ++static const char *ddcci_monitor_next_vcp_item(const char *ptr) ++{ ++ int depth = 0; + -+* All block devices associated with an LED are checked for activity every -+ ``check_interval`` milliseconds, and a blink is triggered if the correct type -+ of activity (as determined by the LED's ``blink_on_*`` attributes) is -+ detected. The duration of an LED's blink is determined by its ``blink_time`` -+ attribute. Thus (when the correct type of activity is detected), the LED will -+ be on for ``blink_time`` milliseconds and off for -+ ``check_interval - blink_time`` milliseconds. ++ /* Sanity check */ ++ if (unlikely(ptr == NULL || ptr[0] == '\0')) ++ return NULL; + -+* The LED subsystem ignores new blink requests for an LED that is already in -+ in the process of blinking, so setting a ``blink_time`` greater than or equal -+ to ``check_interval`` will cause some blinks to be missed. ++ /* Find next white space outside of parentheses */ ++ while ((ptr = strpbrk(ptr, " ()"))) { ++ if (!ptr || depth == INT_MAX) { ++ return NULL; ++ } else if (*ptr == '(') { ++ depth++; ++ } else if (depth > 0) { ++ if (*ptr == ')') ++ depth--; ++ } else { ++ break; ++ } ++ ++ptr; ++ } + -+* Because of processing times, scheduling latencies, etc., avoiding missed -+ blinks actually requires a difference of at least a few milliseconds between -+ the ``blink_time`` and ``check_interval``. The required difference is likely -+ to vary from system to system. As a reference, a Thecus N5550 NAS requires a -+ difference of 7 milliseconds (e.g. ``check_interval == 100``, -+ ``blink_time == 93``). ++ /* Skip over whitespace */ ++ ptr = skip_spaces(ptr); + -+* The default values (``check_interval == 100``, ``blink_time == 75``) cause the -+ LED associated with a continuously active device to blink rapidly. For a more -+ "always on" effect, increase the ``blink_time`` (but not too much; see the -+ previous bullet). ++ /* Check if we're now at the end of the list */ ++ if (unlikely(*ptr == '\0' || *ptr == ')')) ++ return NULL; + -+Other Notes -+=========== ++ return ptr; ++} + -+* Many (possibly all) types of block devices work with this trigger, including: ++static bool ddcci_monitor_find_vcp(unsigned char vcp, const char *s) ++{ ++ const char *ptr = s; ++ char vcp_hex[3]; + -+ * SCSI (including SATA and USB) hard disk drives and SSDs -+ * SCSI (including SATA and USB) optical drives -+ * NVMe SSDs -+ * SD cards -+ * loopback block devices (``/dev/loop*``) -+ * device mapper devices, such as LVM logical volumes -+ * MD RAID devices -+ * zRAM compressed RAM-disks -+ * partitions on block devices that support them ++ /* Sanity check */ ++ if (unlikely(s == NULL || s[0] == '\0')) ++ return false; + -+* The names of the symbolic links in ``/sys/class/leds//linked_devices`` -+ are **kernel** names, which may not match the paths used for -+ ``link_dev_by_path`` and ``unlink_dev_by_path``. This is most likely when a -+ symbolic link is used to refer to the device (as is common with logical -+ volumes), but it can be true for any device, because nothing prevents the -+ creation of device special files with arbitrary names (e.g. -+ ``sudo mknod /foo b 8 0``). ++ /* Create hex representation of VCP */ ++ if (unlikely(snprintf(vcp_hex, 3, "%02hhX", vcp) != 2)) { ++ pr_err("snprintf failed to convert to hex. This should not happen.\n"); ++ return false; ++ } + -+ Kernel names can be used to unlink block devices from LEDs by writing them to -+ the LED's ``unlink_dev_by_name`` attribute. ++ /* Search for it */ ++ do { ++ if (strncasecmp(vcp_hex, ptr, 2) == 0) { ++ if (ptr[2] == ' ' || ptr[2] == '(' || ptr[2] == ')') { ++ return true; ++ } ++ } ++ } while ((ptr = ddcci_monitor_next_vcp_item(ptr))); + -+* The ``blkdev`` LED trigger supports many-to-many device/LED associations. -+ A device can be associated with multiple LEDs, and an LED can be associated -+ with multiple devices. ++ return false; ++} ++ ++static int ddcci_backlight_create_symlink(struct ddcci_device *ddcci_dev) ++{ ++ int i, result; ++ struct device *dev = &ddcci_dev->dev; ++ struct kernfs_node *dirent; ++ for (i = 0; i < 3; ++i) { ++ dev = dev->parent; ++ if (!dev) { ++ dev_dbg(&ddcci_dev->dev, "failed to create convenience symlink: ancestor device not found\n"); ++ return -ENOENT; ++ } ++ } ++ dirent = sysfs_get_dirent(dev->kobj.sd, "ddcci_backlight"); ++ if (dirent) { ++ sysfs_put(dirent); ++ dev_dbg(&ddcci_dev->dev, "failed to create convenience symlink: %s/ddcci_backlight already exists\n", dev_name(dev)); ++ return -EEXIST; ++ } ++ ++ result = sysfs_create_link(&dev->kobj, &ddcci_dev->dev.kobj, "ddcci_backlight"); ++ if (result == 0) { ++ dev_dbg(&ddcci_dev->dev, "created symlink %s/ddcci_backlight\n", dev_name(dev)); ++ } else { ++ dev_info(&ddcci_dev->dev, "failed to create convenience symlink: %d\n", result); ++ } ++ return result; ++} ++ ++static int ddcci_backlight_remove_symlink(struct ddcci_device *ddcci_dev) ++{ ++ int i; ++ struct device *dev = &ddcci_dev->dev; ++ struct kernfs_node *dirent; ++ for (i = 0; i < 3; ++i) { ++ dev = dev->parent; ++ if (!dev) ++ return -ENOENT; ++ } ++ dirent = sysfs_get_dirent(dev->kobj.sd, "ddcci_backlight"); ++ if (!dirent) { ++ return -ENOENT; ++ } ++ ++ if ((dirent->flags & KERNFS_LINK) == 0) { ++ sysfs_put(dirent); ++ dev_dbg(&ddcci_dev->dev, "won't remove %s/ddcci_backlight: not a symlink\n", dev_name(dev)); ++ return -EINVAL; ++ } ++ ++ if (dirent->symlink.target_kn != ddcci_dev->dev.kobj.sd) { ++ sysfs_put(dirent); ++ dev_dbg(&ddcci_dev->dev, "won't remove %s/ddcci_backlight: we are not the link target\n", dev_name(dev)); ++ return -EINVAL; ++ } ++ ++ sysfs_put(dirent); ++ ++ sysfs_remove_link(&dev->kobj, "ddcci_backlight"); ++ dev_dbg(&ddcci_dev->dev, "removed symlink %s/ddcci_backlight\n", dev_name(dev)); ++ return 0; ++} ++ ++static int ddcci_monitor_probe(struct ddcci_device *dev, ++ const struct ddcci_device_id *id) ++{ ++ struct ddcci_monitor_drv_data *drv_data; ++ struct backlight_properties props; ++ struct backlight_device *bl = NULL; ++ int ret = 0; ++ bool support_luminance, support_bl_white; ++ unsigned short brightness = 0, max_brightness = 0; ++ const char *vcps; ++ ++ dev_dbg(&dev->dev, "probing monitor backlight device\n"); ++ ++ /* Get VCP list */ ++ vcps = ddcci_find_capstr_item(dev->capabilities, "vcp", NULL); ++ if (IS_ERR(vcps)) { ++ dev_info(&dev->dev, ++ "monitor doesn't provide a list of supported controls.\n"); ++ support_bl_white = support_luminance = true; ++ } else { ++ /* Check VCP list for supported VCPs */ ++ support_bl_white = ddcci_monitor_find_vcp(DDCCI_MONITOR_BL_WHITE, vcps); ++ support_luminance = ddcci_monitor_find_vcp(DDCCI_MONITOR_LUMINANCE, vcps); ++ /* Fallback to trying if no support is found */ ++ if (!support_bl_white && !support_luminance) { ++ dev_info(&dev->dev, ++ "monitor doesn't announce support for backlight or luminance controls.\n"); ++ support_bl_white = support_luminance = true; ++ } ++ } ++ ++ /* Initialize driver data structure */ ++ drv_data = devm_kzalloc(&dev->dev, sizeof(struct ddcci_monitor_drv_data), ++ GFP_KERNEL); ++ if (!drv_data) ++ return -ENOMEM; ++ drv_data->device = dev; ++ ++ if (support_bl_white) { ++ /* Try getting backlight level */ ++ dev_dbg(&dev->dev, ++ "trying to access \"backlight level white\" control\n"); ++ ret = ddcci_monitor_readctrl(drv_data->device, DDCCI_MONITOR_BL_WHITE, ++ &brightness, &max_brightness); ++ if (ret < 0) { ++ if (ret == -ENOTSUPP) ++ dev_info(&dev->dev, ++ "monitor does not support reading backlight level\n"); ++ else ++ goto err_free; ++ } else { ++ drv_data->used_vcp = DDCCI_MONITOR_BL_WHITE; ++ } ++ } ++ ++ if (support_luminance && !drv_data->used_vcp) { ++ /* Try getting luminance */ ++ dev_dbg(&dev->dev, ++ "trying to access \"luminance\" control\n"); ++ ret = ddcci_monitor_readctrl(drv_data->device, DDCCI_MONITOR_LUMINANCE, ++ &brightness, &max_brightness); ++ if (ret < 0) { ++ if (ret == -ENOTSUPP) ++ dev_info(&dev->dev, ++ "monitor does not support reading luminance\n"); ++ else ++ goto err_free; ++ } else { ++ drv_data->used_vcp = DDCCI_MONITOR_LUMINANCE; ++ } ++ drv_data->used_vcp = DDCCI_MONITOR_LUMINANCE; ++ } ++ ++ if (!drv_data->used_vcp) ++ goto err_free; ++ ++ /* Create brightness device */ ++ memset(&props, 0, sizeof(props)); ++ props.type = BACKLIGHT_RAW; ++ props.max_brightness = max_brightness; ++ props.brightness = brightness; ++ bl = devm_backlight_device_register(&dev->dev, dev_name(&dev->dev), ++ &dev->dev, drv_data, ++ &ddcci_backlight_ops, &props); ++ drv_data->bl_dev = bl; ++ if (IS_ERR(bl)) { ++ dev_err(&dev->dev, "failed to register backlight\n"); ++ return PTR_ERR(bl); ++ } ++ dev_info(&dev->dev, "registered %s as backlight device %s\n", ++ ddcci_monitor_vcp_name(drv_data->used_vcp), ++ dev_name(&dev->dev)); ++ ++ if (convenience_symlink) { ++ ddcci_backlight_create_symlink(dev); ++ } ++ ++ goto end; ++err_free: ++ devm_kfree(&dev->dev, drv_data); ++end: ++ return ret; ++} ++ ++static int ddcci_monitor_remove(struct ddcci_device *dev) ++{ ++ dev_dbg(&dev->dev, "removing device\n"); ++ ddcci_backlight_remove_symlink(dev); ++ return 0; ++} ++ ++static struct ddcci_device_id ddcci_monitor_idtable[] = { ++ { "monitor", DDCCI_ANY_ID, DDCCI_ANY_ID, DDCCI_ANY_ID, DDCCI_ANY_ID, 0 }, ++ {} ++}; ++ ++static struct ddcci_driver ddcci_backlight_driver = { ++ .driver = { ++ .name = "ddcci-backlight", ++ .owner = THIS_MODULE, ++ }, ++ ++ .id_table = ddcci_monitor_idtable, ++ .probe = ddcci_monitor_probe, ++ .remove = ddcci_monitor_remove, ++}; ++ ++module_ddcci_driver(ddcci_backlight_driver); ++ ++/* Module parameter description */ ++module_param(convenience_symlink, bool, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(convenience_symlink, "add convenience symlink \"ddcci_backlight\" to ancestor device in sysfs (default true)"); ++ ++MODULE_AUTHOR("Christoph Grenz"); ++MODULE_DESCRIPTION("DDC/CI generic monitor backlight driver"); ++MODULE_VERSION("0.4.2"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_ALIAS("ddcci:monitor-*-*-*-*"); +diff --git a/include/linux/ddcci.h b/include/linux/ddcci.h +new file mode 100644 +index 000000000000..a219f031e584 +--- /dev/null ++++ b/include/linux/ddcci.h +@@ -0,0 +1,164 @@ ++/* ++ * DDC/CI bus driver ++ * ++ * Copyright (c) 2015 Christoph Grenz ++ */ ++ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ */ ++ ++#ifndef _DDCCI_H ++#define _DDCCI_H ++ ++#include ++#include ++#include ++ ++#define DDCCI_MODULE_PREFIX "ddcci:" ++ ++/* Special addresses */ ++ ++/* default device address (even) */ ++#define DDCCI_DEFAULT_DEVICE_ADDR 0x6E ++/* receiving host address for communication with default device address */ ++#define DDCCI_HOST_ADDR_EVEN 0x50 ++/* sending host address for communication with default device address */ ++#define DDCCI_HOST_ADDR_ODD 0x51 ++ ++/* Command codes */ ++ ++/* Identification Request */ ++#define DDCCI_COMMAND_ID 0xf1 ++/* Identification Reply */ ++#define DDCCI_REPLY_ID 0xe1 ++/* Capabilities Request */ ++#define DDCCI_COMMAND_CAPS 0xf3 ++/* Capabilities Reply */ ++#define DDCCI_REPLY_CAPS 0xe3 ++ ++/* Quirks */ ++ ++/* Device always responds with unset protocol flag */ ++#define DDCCI_QUIRK_NO_PFLAG BIT(1) ++/* Device needs writing one byte at a time */ ++#define DDCCI_QUIRK_WRITE_BYTEWISE BIT(2) ++/* Device repeats first byte on read */ ++#define DDCCI_QUIRK_SKIP_FIRST_BYTE BIT(3) ++ ++/* Flags */ ++ ++#define DDCCI_FLAG_REMOVED BIT(1) ++#define DDCCI_FLAG_DEPENDENT BIT(2) ++#define DDCCI_FLAG_EXTERNAL BIT(3) ++ ++extern struct bus_type ddcci_bus_type; ++ ++struct ddcci_bus_drv_data; ++ ++/* struct ddcci_device_id - identifies DDC/CI devices for probing */ ++struct ddcci_device_id { ++ char prot[9]; ++ char type[9]; ++ char model[9]; ++ char vendor[9]; ++ char module[9]; ++ kernel_ulong_t driver_data; /* Data private to the driver */ ++}; ++#define DDCCI_ANY_ID "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" ++ ++/** ++ * struct ddcci_device - represent an DDC/CI device ++ * @outer_addr: Outer device address (I2C address << 1). ++ * @inner_addr: Inner device address. ++ * @flags: Device flags. ++ * @capabilities: Device capability string. ++ * @capabilities_len: Length of capability string. ++ * @i2c_client: Parent I2C device. ++ * @bus_drv_data: Driver internal data structure. ++ * @dev: Driver model device node for the slave. ++ * @cdev: Character device structure ++ * @cdev_sem: RW semaphore for exclusive access on character device. ++ * @prot: Device class ("protocol", from capability string) ++ * @type: Device subclass ("type", from capability string) ++ * @model: Device model (from capability string) ++ * @vendor: Device vendor (from identification command response) ++ * @module: Device module (from identification command response) ++ * @device_number: Device serial (from identification command response) ++ */ ++struct ddcci_device { ++ unsigned short outer_addr; ++ unsigned short inner_addr; ++ int flags; ++ char *capabilities; ++ size_t capabilities_len; ++ struct i2c_client *i2c_client; ++ struct ddcci_bus_drv_data *bus_drv_data; ++ struct device dev; ++ struct cdev cdev; ++ struct rw_semaphore cdev_sem; ++ char prot[9]; ++ char type[9]; ++ char model[9]; ++ char vendor[9]; ++ char module[9]; ++ int device_number; ++}; ++#define to_ddcci_device(d) container_of(d, struct ddcci_device, dev) ++ ++/** ++ * struct ddcci_driver - represent an DDC/CI device driver ++ * @probe: Callback for device binding ++ * @remove: Callback for device unbinding ++ * @driver: Device driver model driver ++ * @id_table: List of DDC/CI devices supported by this driver ++ * ++ * The driver.owner field should be set to the module owner of this driver. ++ * The driver.name field should be set to the name of this driver. ++ */ ++struct ddcci_driver { ++ int (*probe)(struct ddcci_device *, const struct ddcci_device_id *); ++ int (*remove)(struct ddcci_device *); ++ struct device_driver driver; ++ struct ddcci_device_id *id_table; ++}; ++#define to_ddcci_driver(d) container_of(d, struct ddcci_driver, driver) ++ ++int ddcci_register_driver(struct module *owner, struct ddcci_driver *driver); ++#define ddcci_add_driver(driver) \ ++ ddcci_register_driver(THIS_MODULE, driver) ++void ddcci_del_driver(struct ddcci_driver *driver); ++ ++struct ddcci_device *ddcci_verify_device(struct device *dev); ++ ++#define module_ddcci_driver(__ddcci_driver) \ ++ module_driver(__ddcci_driver, ddcci_add_driver, \ ++ ddcci_del_driver) ++ ++int ddcci_device_write(struct ddcci_device *, bool p_flag, unsigned char *data, ++ unsigned char length); ++int ddcci_device_read(struct ddcci_device *, bool p_flag, unsigned char *buffer, ++ unsigned char length); ++int ddcci_device_writeread(struct ddcci_device *, bool p_flag, ++ unsigned char *buffer, unsigned char length, ++ unsigned char maxlength); ++ ++static inline void *ddcci_get_drvdata(const struct ddcci_device *dev) ++{ ++ return dev_get_drvdata(&dev->dev); ++} ++ ++static inline void ddcci_set_drvdata(struct ddcci_device *dev, void *data) ++{ ++ dev_set_drvdata(&dev->dev, data); ++} ++ ++unsigned long ddcci_quirks(struct ddcci_device *dev); ++ ++const char *ddcci_find_capstr_item(const char *capabilities, const char *tag, ++ size_t *length); ++ ++#endif +-- +2.41.0 + +From 641b0ffc98fe2842735ec30d31c68b555d559a47 Mon Sep 17 00:00:00 2001 +From: Peter Jung +Date: Mon, 10 Jul 2023 18:29:38 +0200 +Subject: [PATCH 4/7] fixes + +Signed-off-by: Peter Jung +--- + drivers/bluetooth/btusb.c | 2 +- + include/linux/pageblock-flags.h | 2 +- + kernel/padata.c | 4 ++-- + mm/readahead.c | 10 +++++++++- + sound/pci/hda/cs35l41_hda.c | 2 +- + 5 files changed, 14 insertions(+), 6 deletions(-) + diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c -index 2a8e2bb038f5..15b79f558a16 100644 +index 5ec4ad0a5c86..15c5649bde4d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c -@@ -940,7 +940,7 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev) +@@ -945,7 +945,7 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev) } gpiod_set_value_cansleep(reset_gpio, 0); @@ -9935,1268 +12122,6 @@ index 2a8e2bb038f5..15b79f558a16 100644 gpiod_set_value_cansleep(reset_gpio, 1); return; -@@ -4099,6 +4099,7 @@ static int btusb_probe(struct usb_interface *intf, - BT_DBG("intf %p id %p", intf, id); - - if ((id->driver_info & BTUSB_IFNUM_2) && -+ (intf->cur_altsetting->desc.bInterfaceNumber != 0) && - (intf->cur_altsetting->desc.bInterfaceNumber != 2)) - return -ENODEV; - -diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig -index 2a57328eca20..05e80cfd0ed8 100644 ---- a/drivers/leds/trigger/Kconfig -+++ b/drivers/leds/trigger/Kconfig -@@ -155,4 +155,13 @@ config LEDS_TRIGGER_TTY - - When build as a module this driver will be called ledtrig-tty. - -+config LEDS_TRIGGER_BLKDEV -+ tristate "LED Trigger for block devices" -+ depends on BLOCK -+ help -+ The blkdev LED trigger allows LEDs to be controlled by block device -+ activity (reads and writes). -+ -+ See Documentation/leds/ledtrig-blkdev.rst. -+ - endif # LEDS_TRIGGERS -diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile -index 25c4db97cdd4..d53bab5d93f1 100644 ---- a/drivers/leds/trigger/Makefile -+++ b/drivers/leds/trigger/Makefile -@@ -16,3 +16,4 @@ obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o - obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o - obj-$(CONFIG_LEDS_TRIGGER_AUDIO) += ledtrig-audio.o - obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o -+obj-$(CONFIG_LEDS_TRIGGER_BLKDEV) += ledtrig-blkdev.o -diff --git a/drivers/leds/trigger/ledtrig-blkdev.c b/drivers/leds/trigger/ledtrig-blkdev.c -new file mode 100644 -index 000000000000..067eedb003b5 ---- /dev/null -+++ b/drivers/leds/trigger/ledtrig-blkdev.c -@@ -0,0 +1,1221 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+ -+/* -+ * Block device LED trigger -+ * -+ * Copyright 2021-2022 Ian Pilcher -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+/** -+ * DOC: Overview -+ * -+ * The ``blkdev`` LED trigger works by periodically checking the activity -+ * counters of block devices that have been linked to one or more LEDs and -+ * blinking those LED(s) if the correct type of activity has occurred. The -+ * periodic check is scheduled with the Linux kernel's deferred work facility. -+ * -+ * Trigger-specific data about block devices and LEDs is stored in two data -+ * structures --- &struct blkdev_trig_bdev (a "BTB") and &struct blkdev_trig_led -+ * (a "BTL"). Each structure contains a &struct xarray that holds links to any -+ * linked devices of the other type. I.e. &blkdev_trig_bdev.linked_btls -+ * contains links to all BTLs whose LEDs have been linked to the BTB's block -+ * device, and &blkdev_trig_led.linked_btbs contains links to all BTBs whose -+ * block devices have been linked to the BTL's LED. Thus, a block device can -+ * be linked to more than one LED, and an LED can be linked to more than one -+ * block device. -+ */ -+ -+/* Default, minimum & maximum blink duration (milliseconds) */ -+#define BLKDEV_TRIG_BLINK_DEF 75 -+#define BLKDEV_TRIG_BLINK_MIN 10 -+#define BLKDEV_TRIG_BLINK_MAX 86400000 /* 24 hours */ -+ -+/* Default, minimum & maximum activity check interval (milliseconds) */ -+#define BLKDEV_TRIG_CHECK_DEF 100 -+#define BLKDEV_TRIG_CHECK_MIN 25 -+#define BLKDEV_TRIG_CHECK_MAX 86400000 /* 24 hours */ -+ -+/* -+ * If blkdev_trig_check() can't lock the mutex, how long to wait before trying -+ * again (milliseconds) -+ */ -+#define BLKDEV_TRIG_CHECK_RETRY 5 -+ -+/* Mode argument for calls to blkdev_get_by_path() and blkdev_put() */ -+#define BLKDEV_TRIG_FMODE 0 -+ -+/** -+ * struct blkdev_trig_bdev - Trigger-specific data about a block device. -+ * @last_checked: Time (in jiffies) at which the trigger last checked this -+ * block device for activity. -+ * @last_activity: Time (in jiffies) at which the trigger last detected -+ * activity of each type. -+ * @ios: Activity counter values for each type, corresponding to -+ * the timestamps in &last_activity. -+ * @index: &xarray index, so the BTB can be included in one or more -+ * &blkdev_trig_led.linked_btbs. -+ * @bdev: The block device. -+ * @linked_btls: The BTLs that represent the LEDs linked to the BTB's -+ * block device. -+ * -+ * Every block device linked to at least one LED gets a "BTB." A BTB is created -+ * when a block device that is not currently linked to any LEDs is linked to an -+ * LED. -+ * -+ * A BTB is freed when one of the following occurs: -+ * -+ * * The number of LEDs linked to the block device becomes zero, because it has -+ * been unlinked from its last LED using the trigger's &sysfs interface. -+ * -+ * * The number of LEDs linked to the block device becomes zero, because the -+ * last LED to which it was linked has been disassociated from the trigger -+ * (which happens automatically if the LED device is removed from the system). -+ * -+ * * The BTB's block device is removed from the system. To accomodate this -+ * scenario, BTB's are created as device resources, so that the release -+ * function will be called by the driver core when the device is removed. -+ */ -+struct blkdev_trig_bdev { -+ unsigned long last_checked; -+ unsigned long last_activity[NR_STAT_GROUPS]; -+ unsigned long ios[NR_STAT_GROUPS]; -+ unsigned long index; -+ struct block_device *bdev; -+ struct xarray linked_btls; -+}; -+ -+/** -+ * struct blkdev_trig_led - Trigger-specific data about an LED. -+ * @last_checked: Time (in jiffies) at which the trigger last checked the -+ * the block devices linked to this LED for activity. -+ * @index: &xarray index, so the BTL can be included in one or more -+ * &blkdev_trig_bdev.linked_btls. -+ * @mode: Bitmask for types of block device activity that will -+ * cause this LED to blink --- reads, writes, discards, -+ * etc. -+ * @led: The LED device. -+ * @blink_msec: Duration of a blink (milliseconds). -+ * @check_jiffies: Frequency with which block devices linked to this LED -+ * should be checked for activity (jiffies). -+ * @linked_btbs: The BTBs that represent the block devices linked to the -+ * BTL's LED. -+ * @all_btls_node: The BTL's node in the module's list of all BTLs. -+ * -+ * Every LED associated with the block device trigger gets a "BTL." A BTL is -+ * created when the trigger is "activated" on an LED (usually by writing -+ * ``blkdev`` to the LED's &sysfs &trigger attribute). A BTL is freed wnen its -+ * LED is disassociated from the trigger, either through the trigger's &sysfs -+ * interface or because the LED device is removed from the system. -+ */ -+struct blkdev_trig_led { -+ unsigned long last_checked; -+ unsigned long index; -+ unsigned long mode; /* must be ulong for atomic bit ops */ -+ struct led_classdev *led; -+ unsigned int blink_msec; -+ unsigned int check_jiffies; -+ struct xarray linked_btbs; -+ struct hlist_node all_btls_node; -+}; -+ -+/* Protects everything except atomic LED attributes */ -+static DEFINE_MUTEX(blkdev_trig_mutex); -+ -+/* BTB device resource release function */ -+static void blkdev_trig_btb_release(struct device *dev, void *res); -+ -+/* Index for next BTB or BTL */ -+static unsigned long blkdev_trig_next_index; -+ -+/* All LEDs associated with the trigger */ -+static HLIST_HEAD(blkdev_trig_all_btls); -+ -+/* Delayed work to periodically check for activity & blink LEDs */ -+static void blkdev_trig_check(struct work_struct *work); -+static DECLARE_DELAYED_WORK(blkdev_trig_work, blkdev_trig_check); -+ -+/* When is the delayed work scheduled to run next (jiffies) */ -+static unsigned long blkdev_trig_next_check; -+ -+/* Total number of BTB-to-BTL links */ -+static unsigned int blkdev_trig_link_count; -+ -+/* Empty sysfs attribute list for next 2 declarations */ -+static struct attribute *blkdev_trig_attrs_empty[] = { NULL }; -+ -+/* linked_leds sysfs directory for block devs linked to 1 or more LEDs */ -+static const struct attribute_group blkdev_trig_linked_leds = { -+ .name = "linked_leds", -+ .attrs = blkdev_trig_attrs_empty, -+}; -+ -+/* linked_devices sysfs directory for each LED associated with the trigger */ -+static const struct attribute_group blkdev_trig_linked_devs = { -+ .name = "linked_devices", -+ .attrs = blkdev_trig_attrs_empty, -+}; -+ -+ -+/* -+ * -+ * Delayed work to check for activity & blink LEDs -+ * -+ */ -+ -+/** -+ * blkdev_trig_blink() - Blink an LED, if the correct type of activity has -+ * occurred on the block device. -+ * @btl: The BTL that represents the LED -+ * @btb: The BTB that represents the block device -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ * Return: &true if the LED is blinked, &false if not. -+ */ -+static bool blkdev_trig_blink(const struct blkdev_trig_led *btl, -+ const struct blkdev_trig_bdev *btb) -+{ -+ unsigned long mode, mask, delay_on, delay_off; -+ enum stat_group i; -+ -+ mode = READ_ONCE(btl->mode); -+ -+ for (i = STAT_READ, mask = 1; i <= STAT_FLUSH; ++i, mask <<= 1) { -+ -+ if (!(mode & mask)) -+ continue; -+ -+ if (time_before_eq(btb->last_activity[i], btl->last_checked)) -+ continue; -+ -+ delay_on = READ_ONCE(btl->blink_msec); -+ delay_off = 1; /* 0 leaves LED turned on */ -+ -+ led_blink_set_oneshot(btl->led, &delay_on, &delay_off, 0); -+ return true; -+ } -+ -+ return false; -+} -+ -+/** -+ * blkdev_trig_update_btb() - Update a BTB's activity counters and timestamps. -+ * @btb: The BTB -+ * @now: Timestamp (in jiffies) -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_update_btb(struct blkdev_trig_bdev *btb, -+ unsigned long now) -+{ -+ unsigned long new_ios; -+ enum stat_group i; -+ -+ for (i = STAT_READ; i <= STAT_FLUSH; ++i) { -+ -+ new_ios = part_stat_read(btb->bdev, ios[i]); -+ -+ if (new_ios != btb->ios[i]) { -+ btb->ios[i] = new_ios; -+ btb->last_activity[i] = now; -+ } -+ } -+ -+ btb->last_checked = now; -+} -+ -+/** -+ * blkdev_trig_check() - Check linked devices for activity and blink LEDs. -+ * @work: Delayed work (&blkdev_trig_work) -+ * -+ * Context: Process context. Takes and releases &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_check(struct work_struct *work) -+{ -+ struct blkdev_trig_led *btl; -+ struct blkdev_trig_bdev *btb; -+ unsigned long index, delay, now, led_check, led_delay; -+ bool blinked; -+ -+ if (!mutex_trylock(&blkdev_trig_mutex)) { -+ delay = msecs_to_jiffies(BLKDEV_TRIG_CHECK_RETRY); -+ goto exit_reschedule; -+ } -+ -+ now = jiffies; -+ delay = ULONG_MAX; -+ -+ hlist_for_each_entry (btl, &blkdev_trig_all_btls, all_btls_node) { -+ -+ led_check = btl->last_checked + btl->check_jiffies; -+ -+ if (time_before_eq(led_check, now)) { -+ -+ blinked = false; -+ -+ xa_for_each (&btl->linked_btbs, index, btb) { -+ -+ if (btb->last_checked != now) -+ blkdev_trig_update_btb(btb, now); -+ if (!blinked) -+ blinked = blkdev_trig_blink(btl, btb); -+ } -+ -+ btl->last_checked = now; -+ led_delay = btl->check_jiffies; -+ -+ } else { -+ led_delay = led_check - now; -+ } -+ -+ if (led_delay < delay) -+ delay = led_delay; -+ } -+ -+ mutex_unlock(&blkdev_trig_mutex); -+ -+exit_reschedule: -+ WARN_ON_ONCE(delay == ULONG_MAX); -+ WARN_ON_ONCE(!schedule_delayed_work(&blkdev_trig_work, delay)); -+} -+ -+/** -+ * blkdev_trig_sched_led() - Set the schedule of the delayed work when a new -+ * LED is added to the schedule. -+ * @btl: The BTL that represents the LED -+ * -+ * Called when the number of block devices to which an LED is linked becomes -+ * non-zero. -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_sched_led(const struct blkdev_trig_led *btl) -+{ -+ unsigned long delay = READ_ONCE(btl->check_jiffies); -+ unsigned long check_by = jiffies + delay; -+ -+ /* -+ * If no other LED-to-block device links exist, simply schedule the -+ * delayed work according to this LED's check_interval attribute -+ * (check_jiffies). -+ */ -+ if (blkdev_trig_link_count == 0) { -+ WARN_ON(!schedule_delayed_work(&blkdev_trig_work, delay)); -+ blkdev_trig_next_check = check_by; -+ return; -+ } -+ -+ /* -+ * If the next check is already scheduled to occur soon enough to -+ * accomodate this LED's check_interval, the schedule doesn't need -+ * to be changed. -+ */ -+ if (time_after_eq(check_by, blkdev_trig_next_check)) -+ return; -+ -+ /* -+ * Modify the schedule, so that the delayed work runs soon enough for -+ * this LED. -+ */ -+ WARN_ON(!mod_delayed_work(system_wq, &blkdev_trig_work, delay)); -+ blkdev_trig_next_check = check_by; -+} -+ -+ -+/* -+ * -+ * Linking and unlinking LEDs and block devices -+ * -+ */ -+ -+/** -+ * blkdev_trig_link() - Link a block device to an LED. -+ * @btl: The BTL that represents the LED -+ * @btb: The BTB that represents the block device -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ * Return: &0 on success, negative &errno on error. -+ */ -+static int blkdev_trig_link(struct blkdev_trig_led *btl, -+ struct blkdev_trig_bdev *btb) -+{ -+ bool led_first_link; -+ int err; -+ -+ led_first_link = xa_empty(&btl->linked_btbs); -+ -+ err = xa_insert(&btb->linked_btls, btl->index, btl, GFP_KERNEL); -+ if (err) -+ return err; -+ -+ err = xa_insert(&btl->linked_btbs, btb->index, btb, GFP_KERNEL); -+ if (err) -+ goto error_erase_btl; -+ -+ /* Create /sys/class/block//linked_leds/ symlink */ -+ err = sysfs_add_link_to_group(bdev_kobj(btb->bdev), -+ blkdev_trig_linked_leds.name, -+ &btl->led->dev->kobj, btl->led->name); -+ if (err) -+ goto error_erase_btb; -+ -+ /* Create /sys/class/leds//linked_devices/ symlink */ -+ err = sysfs_add_link_to_group(&btl->led->dev->kobj, -+ blkdev_trig_linked_devs.name, -+ bdev_kobj(btb->bdev), -+ dev_name(&btb->bdev->bd_device)); -+ if (err) -+ goto error_remove_symlink; -+ -+ /* -+ * If this is the first block device linked to this LED, the delayed -+ * work schedule may need to be changed. -+ */ -+ if (led_first_link) -+ blkdev_trig_sched_led(btl); -+ -+ ++blkdev_trig_link_count; -+ -+ return 0; -+ -+error_remove_symlink: -+ sysfs_remove_link_from_group(bdev_kobj(btb->bdev), -+ blkdev_trig_linked_leds.name, -+ btl->led->name); -+error_erase_btb: -+ xa_erase(&btl->linked_btbs, btb->index); -+error_erase_btl: -+ xa_erase(&btb->linked_btls, btl->index); -+ return err; -+} -+ -+/** -+ * blkdev_trig_put_btb() - Remove and free a BTB, if it is no longer needed. -+ * @btb: The BTB -+ * -+ * Does nothing if the BTB (block device) is still linked to at least one LED. -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_put_btb(struct blkdev_trig_bdev *btb) -+{ -+ struct block_device *bdev = btb->bdev; -+ int err; -+ -+ if (xa_empty(&btb->linked_btls)) { -+ -+ sysfs_remove_group(bdev_kobj(bdev), &blkdev_trig_linked_leds); -+ err = devres_destroy(&bdev->bd_device, blkdev_trig_btb_release, -+ NULL, NULL); -+ WARN_ON(err); -+ } -+} -+ -+/** -+ * _blkdev_trig_unlink_always() - Perform the unconditionally required steps of -+ * unlinking a block device from an LED. -+ * @btl: The BTL that represents the LED -+ * @btb: The BTB that represents the block device -+ * -+ * When a block device is unlinked from an LED, certain steps must be performed -+ * only if the block device is **not** being released. This function performs -+ * those steps that are **always** required, whether or not the block device is -+ * being released. -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ */ -+static void _blkdev_trig_unlink_always(struct blkdev_trig_led *btl, -+ struct blkdev_trig_bdev *btb) -+{ -+ --blkdev_trig_link_count; -+ -+ if (blkdev_trig_link_count == 0) -+ WARN_ON(!cancel_delayed_work_sync(&blkdev_trig_work)); -+ -+ xa_erase(&btb->linked_btls, btl->index); -+ xa_erase(&btl->linked_btbs, btb->index); -+ -+ /* Remove /sys/class/leds//linked_devices/ symlink */ -+ sysfs_remove_link_from_group(&btl->led->dev->kobj, -+ blkdev_trig_linked_devs.name, -+ dev_name(&btb->bdev->bd_device)); -+} -+ -+/** -+ * blkdev_trig_unlink_norelease() - Unlink an LED from a block device that is -+ * **not** being released. -+ * @btl: The BTL that represents the LED. -+ * @btb: The BTB that represents the block device. -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_unlink_norelease(struct blkdev_trig_led *btl, -+ struct blkdev_trig_bdev *btb) -+{ -+ _blkdev_trig_unlink_always(btl, btb); -+ -+ /* Remove /sys/class/block//linked_leds/ symlink */ -+ sysfs_remove_link_from_group(bdev_kobj(btb->bdev), -+ blkdev_trig_linked_leds.name, -+ btl->led->name); -+ -+ blkdev_trig_put_btb(btb); -+} -+ -+/** -+ * blkdev_trig_unlink_release() - Unlink an LED from a block device that is -+ * being released. -+ * @btl: The BTL that represents the LED -+ * @btb: The BTB that represents the block device -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_unlink_release(struct blkdev_trig_led *btl, -+ struct blkdev_trig_bdev *btb) -+{ -+ _blkdev_trig_unlink_always(btl, btb); -+ -+ /* -+ * If the BTB is being released, the driver core has already removed the -+ * device's attribute groups, and the BTB will be freed automatically, -+ * so there's nothing else to do. -+ */ -+} -+ -+ -+/* -+ * -+ * BTB creation -+ * -+ */ -+ -+/** -+ * blkdev_trig_btb_release() - BTB device resource release function. -+ * @dev: The block device -+ * @res: The BTB -+ * -+ * Called by the driver core when a block device with a BTB is removed. -+ * -+ * Context: Process context. Takes and releases &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_btb_release(struct device *dev, void *res) -+{ -+ struct blkdev_trig_bdev *btb = res; -+ struct blkdev_trig_led *btl; -+ unsigned long index; -+ -+ mutex_lock(&blkdev_trig_mutex); -+ -+ xa_for_each (&btb->linked_btls, index, btl) -+ blkdev_trig_unlink_release(btl, btb); -+ -+ mutex_unlock(&blkdev_trig_mutex); -+} -+ -+/** -+ * blkdev_trig_get_bdev() - Get a block device by path. -+ * @path: The value written to an LED's &link_dev_by_path or -+ * &unlink_dev_by_path attribute, which should be the path to a -+ * special file that represents a block device -+ * @len: The number of characters in &path (not including its -+ * terminating null) -+ * -+ * The caller must call blkdev_put() when finished with the device. -+ * -+ * Context: Process context. -+ * Return: The block device, or an error pointer. -+ */ -+static struct block_device *blkdev_trig_get_bdev(const char *path, size_t len) -+{ -+ struct block_device *bdev; -+ char *buf; -+ -+ buf = kmemdup(path, len + 1, GFP_KERNEL); /* +1 to include null */ -+ if (buf == NULL) -+ return ERR_PTR(-ENOMEM); -+ -+ bdev = blkdev_get_by_path(strim(buf), BLKDEV_TRIG_FMODE, THIS_MODULE); -+ kfree(buf); -+ return bdev; -+} -+ -+/** -+ * blkdev_trig_get_btb() - Find or create the BTB for a block device. -+ * @path: The value written to an LED's &link_dev_by_path attribute, -+ * which should be the path to a special file that represents a -+ * block device -+ * @len: The number of characters in &path -+ * -+ * If a new BTB is created, because the block device was not previously linked -+ * to any LEDs, the block device's &linked_leds &sysfs directory is created. -+ * -+ * Context: Process context. Caller must hold &blkdev_trig_mutex. -+ * Return: Pointer to the BTB, error pointer on error. -+ */ -+static struct blkdev_trig_bdev *blkdev_trig_get_btb(const char *path, -+ size_t len) -+{ -+ struct block_device *bdev; -+ struct blkdev_trig_bdev *btb; -+ int err; -+ -+ bdev = blkdev_trig_get_bdev(path, len); -+ if (IS_ERR(bdev)) -+ return ERR_CAST(bdev); -+ -+ btb = devres_find(&bdev->bd_device, blkdev_trig_btb_release, -+ NULL, NULL); -+ if (btb != NULL) { -+ err = 0; -+ goto exit_put_bdev; -+ } -+ -+ if (blkdev_trig_next_index == ULONG_MAX) { -+ err = -EOVERFLOW; -+ goto exit_put_bdev; -+ } -+ -+ btb = devres_alloc(blkdev_trig_btb_release, sizeof(*btb), GFP_KERNEL); -+ if (btb == NULL) { -+ err = -ENOMEM; -+ goto exit_put_bdev; -+ } -+ -+ err = sysfs_create_group(bdev_kobj(bdev), &blkdev_trig_linked_leds); -+ if (err) -+ goto exit_free_btb; -+ -+ btb->index = blkdev_trig_next_index++; -+ btb->bdev = bdev; -+ xa_init(&btb->linked_btls); -+ -+ /* Populate BTB activity counters */ -+ blkdev_trig_update_btb(btb, jiffies); -+ -+ devres_add(&bdev->bd_device, btb); -+ -+exit_free_btb: -+ if (err) -+ devres_free(btb); -+exit_put_bdev: -+ blkdev_put(bdev, BLKDEV_TRIG_FMODE); -+ return err ? ERR_PTR(err) : btb; -+} -+ -+ -+/* -+ * -+ * Activating and deactivating the trigger on an LED -+ * -+ */ -+ -+/** -+ * blkdev_trig_activate() - Called by the LEDs subsystem when an LED is -+ * associated with the trigger. -+ * @led: The LED -+ * -+ * Context: Process context. Takes and releases &blkdev_trig_mutex. -+ * Return: &0 on success, negative &errno on error. -+ */ -+static int blkdev_trig_activate(struct led_classdev *led) -+{ -+ struct blkdev_trig_led *btl; -+ int err; -+ -+ btl = kzalloc(sizeof(*btl), GFP_KERNEL); -+ if (btl == NULL) -+ return -ENOMEM; -+ -+ err = mutex_lock_interruptible(&blkdev_trig_mutex); -+ if (err) -+ goto exit_free; -+ -+ if (blkdev_trig_next_index == ULONG_MAX) { -+ err = -EOVERFLOW; -+ goto exit_unlock; -+ } -+ -+ btl->index = blkdev_trig_next_index++; -+ btl->last_checked = jiffies; -+ btl->mode = -1; /* set all bits */ -+ btl->led = led; -+ btl->blink_msec = BLKDEV_TRIG_BLINK_DEF; -+ btl->check_jiffies = msecs_to_jiffies(BLKDEV_TRIG_CHECK_DEF); -+ xa_init(&btl->linked_btbs); -+ -+ hlist_add_head(&btl->all_btls_node, &blkdev_trig_all_btls); -+ led_set_trigger_data(led, btl); -+ -+exit_unlock: -+ mutex_unlock(&blkdev_trig_mutex); -+exit_free: -+ if (err) -+ kfree(btl); -+ return err; -+} -+ -+/** -+ * blkdev_trig_deactivate() - Called by the the LEDs subsystem when an LED is -+ * disassociated from the trigger. -+ * @led: The LED -+ * -+ * The LEDs subsystem also calls this function when an LED associated with the -+ * trigger is removed or when the trigger is unregistered (if the module is -+ * unloaded). -+ * -+ * Context: Process context. Takes and releases &blkdev_trig_mutex. -+ */ -+static void blkdev_trig_deactivate(struct led_classdev *led) -+{ -+ struct blkdev_trig_led *btl = led_get_trigger_data(led); -+ struct blkdev_trig_bdev *btb; -+ unsigned long index; -+ -+ mutex_lock(&blkdev_trig_mutex); -+ -+ xa_for_each (&btl->linked_btbs, index, btb) -+ blkdev_trig_unlink_norelease(btl, btb); -+ -+ hlist_del(&btl->all_btls_node); -+ kfree(btl); -+ -+ mutex_unlock(&blkdev_trig_mutex); -+} -+ -+ -+/* -+ * -+ * Link-related attribute store functions -+ * -+ */ -+ -+/** -+ * link_dev_by_path_store() - &link_dev_by_path device attribute store function. -+ * @dev: The LED device -+ * @attr: The &link_dev_by_path attribute (&dev_attr_link_dev_by_path) -+ * @buf: The value written to the attribute, which should be the path to -+ * a special file that represents a block device to be linked to -+ * the LED (e.g. ``/dev/sda``) -+ * @count: The number of characters in &buf -+ * -+ * Context: Process context. Takes and releases &blkdev_trig_mutex. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t link_dev_by_path_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct blkdev_trig_led *btl = led_trigger_get_drvdata(dev); -+ struct blkdev_trig_bdev *btb; -+ int err; -+ -+ err = mutex_lock_interruptible(&blkdev_trig_mutex); -+ if (err) -+ return err; -+ -+ btb = blkdev_trig_get_btb(buf, count); -+ if (IS_ERR(btb)) { -+ err = PTR_ERR(btb); -+ goto exit_unlock; -+ } -+ -+ if (xa_load(&btb->linked_btls, btl->index) != NULL) { -+ err = -EEXIST; -+ goto exit_put_btb; -+ } -+ -+ err = blkdev_trig_link(btl, btb); -+ -+exit_put_btb: -+ if (err) -+ blkdev_trig_put_btb(btb); -+exit_unlock: -+ mutex_unlock(&blkdev_trig_mutex); -+ return err ? : count; -+} -+ -+/** -+ * unlink_dev_by_path_store() - &unlink_dev_by_path device attribute store -+ * function. -+ * @dev: The LED device -+ * @attr: The &unlink_dev_by_path attribute (&dev_attr_unlink_dev_by_path) -+ * @buf: The value written to the attribute, which should be the path to -+ * a special file that represents a block device to be unlinked -+ * from the LED (e.g. ``/dev/sda``) -+ * @count: The number of characters in &buf -+ * -+ * Context: Process context. Takes and releases &blkdev_trig_mutex. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t unlink_dev_by_path_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct blkdev_trig_led *btl = led_trigger_get_drvdata(dev); -+ struct block_device *bdev; -+ struct blkdev_trig_bdev *btb; -+ int err; -+ -+ bdev = blkdev_trig_get_bdev(buf, count); -+ if (IS_ERR(bdev)) -+ return PTR_ERR(bdev); -+ -+ err = mutex_lock_interruptible(&blkdev_trig_mutex); -+ if (err) -+ goto exit_put_bdev; -+ -+ btb = devres_find(&bdev->bd_device, blkdev_trig_btb_release, -+ NULL, NULL); -+ if (btb == NULL) { -+ err = -EUNATCH; /* bdev isn't linked to any LED */ -+ goto exit_unlock; -+ } -+ -+ if (xa_load(&btb->linked_btls, btl->index) == NULL) { -+ err = -EUNATCH; /* bdev isn't linked to this LED */ -+ goto exit_unlock; -+ } -+ -+ blkdev_trig_unlink_norelease(btl, btb); -+ -+exit_unlock: -+ mutex_unlock(&blkdev_trig_mutex); -+exit_put_bdev: -+ blkdev_put(bdev, BLKDEV_TRIG_FMODE); -+ return err ? : count; -+} -+ -+/** -+ * unlink_dev_by_name_store() - &unlink_dev_by_name device attribute store -+ * function. -+ * @dev: The LED device -+ * @attr: The &unlink_dev_by_name attribute (&dev_attr_unlink_dev_by_name) -+ * @buf: The value written to the attribute, which should be the kernel -+ * name of a block device to be unlinked from the LED (e.g. -+ * ``sda``) -+ * @count: The number of characters in &buf -+ * -+ * Context: Process context. Takes and releases &blkdev_trig_mutex. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t unlink_dev_by_name_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct blkdev_trig_led *btl = led_trigger_get_drvdata(dev); -+ struct blkdev_trig_bdev *btb; -+ unsigned long index; -+ int err; -+ -+ err = mutex_lock_interruptible(&blkdev_trig_mutex); -+ if (err) -+ return err; -+ -+ err = -EUNATCH; -+ -+ xa_for_each (&btl->linked_btbs, index, btb) { -+ -+ if (sysfs_streq(dev_name(&btb->bdev->bd_device), buf)) { -+ blkdev_trig_unlink_norelease(btl, btb); -+ err = 0; -+ break; -+ } -+ } -+ -+ mutex_unlock(&blkdev_trig_mutex); -+ return err ? : count; -+} -+ -+ -+/* -+ * -+ * Atomic attribute show & store functions -+ * -+ */ -+ -+/** -+ * blink_time_show() - &blink_time device attribute show function. -+ * @dev: The LED device -+ * @attr: The &blink_time attribute (&dev_attr_blink_time) -+ * @buf: Output buffer -+ * -+ * Writes the value of &blkdev_trig_led.blink_msec to &buf. -+ * -+ * Context: Process context. -+ * Return: The number of characters written to &buf. -+ */ -+static ssize_t blink_time_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ const struct blkdev_trig_led *btl = led_trigger_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "%u\n", READ_ONCE(btl->blink_msec)); -+} -+ -+/** -+ * blink_time_store() - &blink_time device attribute store function. -+ * @dev: The LED device -+ * @attr: The &blink_time attribute (&dev_attr_blink_time) -+ * @buf: The new value (as written to the &sysfs attribute) -+ * @count: The number of characters in &buf -+ * -+ * Sets &blkdev_trig_led.blink_msec to the value in &buf. -+ * -+ * Context: Process context. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t blink_time_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct blkdev_trig_led *btl = led_trigger_get_drvdata(dev); -+ unsigned int value; -+ int err; -+ -+ err = kstrtouint(buf, 0, &value); -+ if (err) -+ return err; -+ -+ if (value < BLKDEV_TRIG_BLINK_MIN || value > BLKDEV_TRIG_BLINK_MAX) -+ return -ERANGE; -+ -+ WRITE_ONCE(btl->blink_msec, value); -+ return count; -+} -+ -+/** -+ * check_interval_show() - &check_interval device attribute show function. -+ * @dev: The LED device -+ * @attr: The &check_interval attribute (&dev_attr_check_interval) -+ * @buf: Output buffer -+ * -+ * Writes the value of &blkdev_trig_led.check_jiffies (converted to -+ * milliseconds) to &buf. -+ * -+ * Context: Process context. -+ * Return: The number of characters written to &buf. -+ */ -+static ssize_t check_interval_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct blkdev_trig_led *btl = led_trigger_get_drvdata(dev); -+ -+ return sysfs_emit(buf, "%u\n", -+ jiffies_to_msecs(READ_ONCE(btl->check_jiffies))); -+} -+ -+/** -+ * check_interval_store() - &check_interval device attribute store function -+ * @dev: The LED device -+ * @attr: The &check_interval attribute (&dev_attr_check_interval) -+ * @buf: The new value (as written to the &sysfs attribute) -+ * @count: The number of characters in &buf -+ * -+ * Sets &blkdev_trig_led.check_jiffies to the value in &buf (after converting -+ * from milliseconds). -+ * -+ * Context: Process context. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t check_interval_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct blkdev_trig_led *led = led_trigger_get_drvdata(dev); -+ unsigned int value; -+ int err; -+ -+ err = kstrtouint(buf, 0, &value); -+ if (err) -+ return err; -+ -+ if (value < BLKDEV_TRIG_CHECK_MIN || value > BLKDEV_TRIG_CHECK_MAX) -+ return -ERANGE; -+ -+ WRITE_ONCE(led->check_jiffies, msecs_to_jiffies(value)); -+ -+ return count; -+} -+ -+/** -+ * blkdev_trig_mode_show() - Helper for boolean attribute show functions. -+ * @led: The LED -+ * @buf: Output buffer -+ * @bit: Which bit to show -+ * -+ * Context: Process context. -+ * Return: The number of characters written to &buf. -+ */ -+static int blkdev_trig_mode_show(const struct blkdev_trig_led *led, char *buf, -+ enum stat_group bit) -+{ -+ return sysfs_emit(buf, -+ READ_ONCE(led->mode) & (1 << bit) ? "Y\n" : "N\n"); -+} -+ -+/** -+ * blkdev_trig_mode_store() - Helper for boolean attribute store functions. -+ * @led: The LED -+ * @buf: The new value (as written to the &sysfs attribute) -+ * @count: The number of characters in &buf -+ * @bit: Which bit to set -+ * -+ * Context: Process context. -+ * Return: &count on success, negative &errno on error. -+ */ -+static int blkdev_trig_mode_store(struct blkdev_trig_led *led, -+ const char *buf, size_t count, -+ enum stat_group bit) -+{ -+ bool set; -+ int err; -+ -+ err = kstrtobool(buf, &set); -+ if (err) -+ return err; -+ -+ if (set) -+ set_bit(bit, &led->mode); -+ else -+ clear_bit(bit, &led->mode); -+ -+ return count; -+} -+ -+/** -+ * blink_on_read_show() - &blink_on_read device attribute show function. -+ * @dev: The LED device -+ * @attr: The &blink_on_read attribute (&dev_attr_blink_on_read) -+ * @buf: Output buffer -+ * -+ * Writes ``Y`` or ``N`` to &buf, depending on whether the &STAT_READ bit in -+ * &blkdev_trig_led.mode is set or cleared. -+ * -+ * Context: Process context. -+ * Return: The number of characters written to &buf. -+ */ -+static ssize_t blink_on_read_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ return blkdev_trig_mode_show(led_trigger_get_drvdata(dev), -+ buf, STAT_READ); -+} -+ -+/** -+ * blink_on_read_store() - &blink_on_read device attribute store function. -+ * @dev: The LED device -+ * @attr: The &blink_on_read attribute (&dev_attr_blink_on_read) -+ * @buf: The new value (as written to the &sysfs attribute) -+ * @count: The number of characters in &buf -+ * -+ * Sets the &STAT_READ bit in &blkdev_trig_led.mode to the value in &buf -+ * (interpretted as a boolean). -+ * -+ * Context: Process context. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t blink_on_read_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ return blkdev_trig_mode_store(led_trigger_get_drvdata(dev), -+ buf, count, STAT_READ); -+} -+ -+/** -+ * blink_on_write_show() - &blink_on_write device attribute show function. -+ * @dev: The LED device -+ * @attr: The &blink_on_write attribute (&dev_attr_blink_on_write) -+ * @buf: Output buffer -+ * -+ * Writes ``Y`` or ``N`` to &buf, depending on whether the &STAT_WRITE bit in -+ * in &blkdev_trig_led.mode is set or cleared. -+ * -+ * Context: Process context. -+ * Return: The number of characters written to &buf. -+ */ -+static ssize_t blink_on_write_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ return blkdev_trig_mode_show(led_trigger_get_drvdata(dev), -+ buf, STAT_WRITE); -+} -+ -+/** -+ * blink_on_write_store() - &blink_on_write device attribute store function. -+ * @dev: The LED device -+ * @attr: The &blink_on_write attribute (&dev_attr_blink_on_write) -+ * @buf: The new value (as written to the &sysfs attribute) -+ * @count: The number of characters in &buf -+ * -+ * Sets the &STAT_WRITE bit in &blkdev_trig_led.mode to the value in &buf -+ * (interpretted as a boolean). -+ * -+ * Context: Process context. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t blink_on_write_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ return blkdev_trig_mode_store(led_trigger_get_drvdata(dev), -+ buf, count, STAT_WRITE); -+} -+ -+/** -+ * blink_on_flush_show() - &blink_on_flush device attribute show function. -+ * @dev: The LED device -+ * @attr: The &blink_on_flush attribute (&dev_attr_blink_on_flush) -+ * @buf: Output buffer -+ * -+ * Writes ``Y`` or ``N`` to &buf, depending whether the &STAT_FLUSH bit in -+ * &blkdev_trig_led.mode is set or cleared. -+ * -+ * Context: Process context. -+ * Return: The number of characters written to &buf. -+ */ -+static ssize_t blink_on_flush_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ return blkdev_trig_mode_show(led_trigger_get_drvdata(dev), -+ buf, STAT_FLUSH); -+} -+ -+/** -+ * blink_on_flush_store() - &blink_on_flush device attribute store function. -+ * @dev: The LED device -+ * @attr: The &blink_on_flush attribute (&dev_attr_blink_on_flush) -+ * @buf: The new value (as written to the &sysfs attribute) -+ * @count: The number of characters in &buf -+ * -+ * Sets the &STAT_FLUSH bit in &blkdev_trig_led.mode to the value in &buf -+ * (interpretted as a boolean). -+ * -+ * Context: Process context. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t blink_on_flush_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ return blkdev_trig_mode_store(led_trigger_get_drvdata(dev), -+ buf, count, STAT_FLUSH); -+} -+ -+/** -+ * blink_on_discard_show() - &blink_on_discard device attribute show function. -+ * @dev: The LED device -+ * @attr: The &blink_on_discard attribute (&dev_attr_blink_on_discard) -+ * @buf: Output buffer -+ * -+ * Writes ``Y`` or ``N`` to &buf, depending on whether the &STAT_DISCARD bit in -+ * &blkdev_trig_led.mode is set or cleared. -+ * -+ * Context: Process context. -+ * Return: The number of characters written to &buf. -+ */ -+static ssize_t blink_on_discard_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ return blkdev_trig_mode_show(led_trigger_get_drvdata(dev), -+ buf, STAT_DISCARD); -+} -+ -+/** -+ * blink_on_discard_store() - &blink_on_discard device attribute store function. -+ * @dev: The LED device -+ * @attr: The &blink_on_discard attribute (&dev_attr_blink_on_discard) -+ * @buf: The new value (as written to the &sysfs attribute) -+ * @count: The number of characters in &buf -+ * -+ * Sets the &STAT_DISCARD bit in &blkdev_trig_led.mode to the value in &buf -+ * (interpretted as a boolean). -+ * -+ * Context: Process context. -+ * Return: &count on success, negative &errno on error. -+ */ -+static ssize_t blink_on_discard_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ return blkdev_trig_mode_store(led_trigger_get_drvdata(dev), -+ buf, count, STAT_DISCARD); -+} -+ -+/* Device attributes */ -+static DEVICE_ATTR_WO(link_dev_by_path); -+static DEVICE_ATTR_WO(unlink_dev_by_path); -+static DEVICE_ATTR_WO(unlink_dev_by_name); -+static DEVICE_ATTR_RW(blink_time); -+static DEVICE_ATTR_RW(check_interval); -+static DEVICE_ATTR_RW(blink_on_read); -+static DEVICE_ATTR_RW(blink_on_write); -+static DEVICE_ATTR_RW(blink_on_flush); -+static DEVICE_ATTR_RW(blink_on_discard); -+ -+/* Device attributes in LED directory (/sys/class/leds//...) */ -+static struct attribute *blkdev_trig_attrs[] = { -+ &dev_attr_link_dev_by_path.attr, -+ &dev_attr_unlink_dev_by_path.attr, -+ &dev_attr_unlink_dev_by_name.attr, -+ &dev_attr_blink_time.attr, -+ &dev_attr_check_interval.attr, -+ &dev_attr_blink_on_read.attr, -+ &dev_attr_blink_on_write.attr, -+ &dev_attr_blink_on_flush.attr, -+ &dev_attr_blink_on_discard.attr, -+ NULL -+}; -+ -+/* Unnamed attribute group == no subdirectory */ -+static const struct attribute_group blkdev_trig_attr_group = { -+ .attrs = blkdev_trig_attrs, -+}; -+ -+/* Attribute groups for the trigger */ -+static const struct attribute_group *blkdev_trig_attr_groups[] = { -+ &blkdev_trig_attr_group, /* /sys/class/leds//... */ -+ &blkdev_trig_linked_devs, /* /sys/class/leds//linked_devices/ */ -+ NULL -+}; -+ -+/* Trigger registration data */ -+static struct led_trigger blkdev_trig_trigger = { -+ .name = "blkdev", -+ .activate = blkdev_trig_activate, -+ .deactivate = blkdev_trig_deactivate, -+ .groups = blkdev_trig_attr_groups, -+}; -+ -+/** -+ * blkdev_trig_init() - Block device LED trigger initialization. -+ * -+ * Registers the ``blkdev`` LED trigger. -+ * -+ * Return: &0 on success, negative &errno on failure. -+ */ -+static int __init blkdev_trig_init(void) -+{ -+ return led_trigger_register(&blkdev_trig_trigger); -+} -+module_init(blkdev_trig_init); -+ -+/** -+ * blkdev_trig_exit() - Block device LED trigger module exit. -+ * -+ * Unregisters the ``blkdev`` LED trigger. -+ */ -+static void __exit blkdev_trig_exit(void) -+{ -+ led_trigger_unregister(&blkdev_trig_trigger); -+} -+module_exit(blkdev_trig_exit); -+ -+MODULE_DESCRIPTION("Block device LED trigger"); -+MODULE_AUTHOR("Ian Pilcher "); -+MODULE_LICENSE("GPL v2"); diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h index e83c4c095041..21b8dfa5d828 100644 --- a/include/linux/pageblock-flags.h @@ -11232,63 +12157,34 @@ index 222d60195de6..b8e6b7c48746 100644 { struct padata_work *pw = container_of(w, struct padata_work, pw_work); struct padata_mt_job_state *ps = pw->pw_data; -diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c -index 6130c42b8e59..e19199f4a684 100644 ---- a/lib/decompress_inflate.c -+++ b/lib/decompress_inflate.c -@@ -39,7 +39,7 @@ static long INIT nofill(void *buffer, unsigned long len) - } +diff --git a/mm/readahead.c b/mm/readahead.c +index a9c999aa19af..797494cec490 100644 +--- a/mm/readahead.c ++++ b/mm/readahead.c +@@ -613,9 +613,17 @@ static void ondemand_readahead(struct readahead_control *ractl, + max_pages); + rcu_read_unlock(); - /* Included from initramfs et al code */ --STATIC int INIT __gunzip(unsigned char *buf, long len, -+static int INIT __gunzip(unsigned char *buf, long len, - long (*fill)(void*, unsigned long), - long (*flush)(void*, unsigned long), - unsigned char *out_buf, long out_len, -diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c -index 9f4262ee33a5..353268b9f129 100644 ---- a/lib/decompress_unxz.c -+++ b/lib/decompress_unxz.c -@@ -102,6 +102,8 @@ - */ - #ifdef STATIC - # define XZ_PREBOOT -+#else -+#include - #endif - #ifdef __KERNEL__ - # include -diff --git a/lib/decompress_unzstd.c b/lib/decompress_unzstd.c -index a512b99ae16a..bba2c0bb10cb 100644 ---- a/lib/decompress_unzstd.c -+++ b/lib/decompress_unzstd.c -@@ -69,6 +69,8 @@ - # define UNZSTD_PREBOOT - # include "xxhash.c" - # include "zstd/decompress_sources.h" -+#else -+#include - #endif - - #include -diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o -index 0edfdb40364b..ae52d3b3f063 100644 ---- a/scripts/Makefile.vmlinux_o -+++ b/scripts/Makefile.vmlinux_o -@@ -19,7 +19,7 @@ quiet_cmd_gen_initcalls_lds = GEN $@ - - .tmp_initcalls.lds: $(srctree)/scripts/generate_initcall_order.pl \ - vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE -- $(call if_changed,gen_initcalls_lds) -+ +$(call if_changed,gen_initcalls_lds) - - targets := .tmp_initcalls.lds +- if (!start || start - index > max_pages) ++ if (!start || start - index - 1 > max_pages) + return; ++ /* ++ * If no gaps in the range, page_cache_next_miss() returns ++ * index beyond range. Adjust it back to make sure ++ * ractl->_index is updated correctly later. ++ */ ++ if ((start - index - 1) == max_pages) ++ start--; ++ + ra->start = start; + ra->size = start - index; /* old async_size */ + ra->size += req_size; diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c -index b5210abb5141..4d8936e1f769 100644 +index ce5faa620517..1f0f2b8df300 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c -@@ -1239,7 +1239,7 @@ static int cs35l41_no_acpi_dsd(struct cs35l41_hda *cs35l41, struct device *physd +@@ -1235,7 +1235,7 @@ static int cs35l41_no_acpi_dsd(struct cs35l41_hda *cs35l41, struct device *physd if (strncmp(hid, "CLSA0100", 8) == 0) { hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; @@ -11300,2440 +12196,387 @@ index b5210abb5141..4d8936e1f769 100644 -- 2.41.0 -From b26229592714772a1f76c9b86f0651568efdc2c9 Mon Sep 17 00:00:00 2001 +From 96777542ac5d53c962cdfb032cf34cfe4ee57dc8 Mon Sep 17 00:00:00 2001 From: Peter Jung -Date: Wed, 19 Jul 2023 18:52:10 +0200 -Subject: [PATCH 4/8] HDR +Date: Mon, 10 Jul 2023 17:10:25 +0200 +Subject: [PATCH 5/7] ksm Signed-off-by: Peter Jung --- - drivers/gpu/drm/amd/amdgpu/amdgpu_display.c | 125 ++++ - drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 69 ++ - .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 28 +- - .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 110 +++- - .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 612 ++++++++++++++++-- - .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 72 ++- - .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 213 +++++- - drivers/gpu/drm/amd/display/dc/core/dc.c | 49 +- - drivers/gpu/drm/amd/display/dc/dc.h | 8 + - .../amd/display/dc/dcn10/dcn10_cm_common.c | 109 +++- - .../drm/amd/display/dc/dcn20/dcn20_hwseq.c | 5 +- - .../drm/amd/display/dc/dcn30/dcn30_hwseq.c | 9 +- - .../amd/display/dc/dcn301/dcn301_resource.c | 26 +- - .../gpu/drm/amd/display/include/fixed31_32.h | 12 + - drivers/gpu/drm/arm/malidp_crtc.c | 2 +- - drivers/gpu/drm/drm_atomic.c | 1 + - drivers/gpu/drm/drm_atomic_state_helper.c | 1 + - drivers/gpu/drm/drm_atomic_uapi.c | 43 +- - drivers/gpu/drm/drm_property.c | 49 ++ - include/drm/drm_mode_object.h | 2 +- - include/drm/drm_plane.h | 7 + - include/drm/drm_property.h | 6 + - 22 files changed, 1417 insertions(+), 141 deletions(-) - -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c -index d60fe7eb5579..708866da7863 100644 ---- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c -@@ -1248,6 +1248,127 @@ amdgpu_display_user_framebuffer_create(struct drm_device *dev, - return &amdgpu_fb->base; - } - -+static const struct drm_prop_enum_list drm_transfer_function_enum_list[] = { -+ { DRM_TRANSFER_FUNCTION_DEFAULT, "Default" }, -+ { DRM_TRANSFER_FUNCTION_SRGB, "sRGB" }, -+ { DRM_TRANSFER_FUNCTION_BT709, "BT.709" }, -+ { DRM_TRANSFER_FUNCTION_PQ, "PQ (Perceptual Quantizer)" }, -+ { DRM_TRANSFER_FUNCTION_LINEAR, "Linear" }, -+ { DRM_TRANSFER_FUNCTION_UNITY, "Unity" }, -+ { DRM_TRANSFER_FUNCTION_HLG, "HLG (Hybrid Log Gamma)" }, -+ { DRM_TRANSFER_FUNCTION_GAMMA22, "Gamma 2.2" }, -+ { DRM_TRANSFER_FUNCTION_GAMMA24, "Gamma 2.4" }, -+ { DRM_TRANSFER_FUNCTION_GAMMA26, "Gamma 2.6" }, -+}; -+ -+#ifdef AMD_PRIVATE_COLOR -+static int -+amdgpu_display_create_color_properties(struct amdgpu_device *adev) -+{ -+ struct drm_property *prop; -+ -+ prop = drm_property_create_enum(adev_to_drm(adev), -+ DRM_MODE_PROP_ENUM, -+ "AMD_REGAMMA_TF", -+ drm_transfer_function_enum_list, -+ ARRAY_SIZE(drm_transfer_function_enum_list)); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.regamma_tf_property = prop; -+ -+ prop = drm_property_create(adev_to_drm(adev), -+ DRM_MODE_PROP_BLOB, -+ "AMD_PLANE_DEGAMMA_LUT", 0); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_degamma_lut_property = prop; -+ -+ prop = drm_property_create_range(adev_to_drm(adev), -+ DRM_MODE_PROP_IMMUTABLE, -+ "AMD_PLANE_DEGAMMA_LUT_SIZE", 0, UINT_MAX); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_degamma_lut_size_property = prop; -+ -+ prop = drm_property_create_enum(adev_to_drm(adev), -+ DRM_MODE_PROP_ENUM, -+ "AMD_PLANE_DEGAMMA_TF", -+ drm_transfer_function_enum_list, -+ ARRAY_SIZE(drm_transfer_function_enum_list)); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_degamma_tf_property = prop; -+ -+ prop = drm_property_create_range(adev_to_drm(adev), -+ 0, "AMD_PLANE_HDR_MULT", 0, U64_MAX); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_hdr_mult_property = prop; -+ -+ prop = drm_property_create(adev_to_drm(adev), -+ DRM_MODE_PROP_BLOB, -+ "AMD_PLANE_SHAPER_LUT", 0); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_shaper_lut_property = prop; -+ -+ prop = drm_property_create_range(adev_to_drm(adev), -+ DRM_MODE_PROP_IMMUTABLE, -+ "AMD_PLANE_SHAPER_LUT_SIZE", 0, UINT_MAX); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_shaper_lut_size_property = prop; -+ -+ prop = drm_property_create_enum(adev_to_drm(adev), -+ DRM_MODE_PROP_ENUM, -+ "AMD_PLANE_SHAPER_TF", -+ drm_transfer_function_enum_list, -+ ARRAY_SIZE(drm_transfer_function_enum_list)); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_shaper_tf_property = prop; -+ -+ prop = drm_property_create(adev_to_drm(adev), -+ DRM_MODE_PROP_BLOB, -+ "AMD_PLANE_LUT3D", 0); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_lut3d_property = prop; -+ -+ prop = drm_property_create_range(adev_to_drm(adev), -+ DRM_MODE_PROP_IMMUTABLE, -+ "AMD_PLANE_LUT3D_SIZE", 0, UINT_MAX); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_lut3d_size_property = prop; -+ -+ prop = drm_property_create(adev_to_drm(adev), -+ DRM_MODE_PROP_BLOB, -+ "AMD_PLANE_BLEND_LUT", 0); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_blend_lut_property = prop; -+ -+ prop = drm_property_create_range(adev_to_drm(adev), -+ DRM_MODE_PROP_IMMUTABLE, -+ "AMD_PLANE_BLEND_LUT_SIZE", 0, UINT_MAX); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_blend_lut_size_property = prop; -+ -+ prop = drm_property_create_enum(adev_to_drm(adev), -+ DRM_MODE_PROP_ENUM, -+ "AMD_PLANE_BLEND_TF", -+ drm_transfer_function_enum_list, -+ ARRAY_SIZE(drm_transfer_function_enum_list)); -+ if (!prop) -+ return -ENOMEM; -+ adev->mode_info.plane_blend_tf_property = prop; -+ -+ return 0; -+} -+#endif -+ - const struct drm_mode_config_funcs amdgpu_mode_funcs = { - .fb_create = amdgpu_display_user_framebuffer_create, - }; -@@ -1324,6 +1445,10 @@ int amdgpu_display_modeset_create_props(struct amdgpu_device *adev) - return -ENOMEM; - } - -+#ifdef AMD_PRIVATE_COLOR -+ if (amdgpu_display_create_color_properties(adev)) -+ return -ENOMEM; -+#endif - return 0; - } - -diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h -index 32fe05c810c6..34291cd134a1 100644 ---- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h -+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h -@@ -343,6 +343,75 @@ struct amdgpu_mode_info { - int disp_priority; - const struct amdgpu_display_funcs *funcs; - const enum drm_plane_type *plane_type; -+ -+ /* Driver-private color mgmt props */ -+ -+ /* @regamma_tf_property: Transfer function for CRTC regamma -+ * (post-blending). Possible values are defined by `enum -+ * drm_transfer_function`. -+ */ -+ struct drm_property *regamma_tf_property; -+ /* @plane_degamma_lut_property: Plane property to set a degamma LUT to -+ * convert color space before blending. -+ */ -+ struct drm_property *plane_degamma_lut_property; -+ /* @plane_degamma_lut_size_property: Plane property to define the max -+ * size of degamma LUT as supported by the driver (read-only). -+ */ -+ struct drm_property *plane_degamma_lut_size_property; -+ /** -+ * @plane_degamma_tf_property: Predefined transfer function to -+ * linearize content with or without LUT. -+ */ -+ struct drm_property *plane_degamma_tf_property; -+ /** -+ * @plane_hdr_mult_property: -+ */ -+ struct drm_property *plane_hdr_mult_property; -+ /** -+ * @shaper_lut_property: Plane property to set pre-blending shaper LUT -+ * that converts color content before 3D LUT. -+ */ -+ struct drm_property *plane_shaper_lut_property; -+ /** -+ * @shaper_lut_size_property: Plane property for the size of -+ * pre-blending shaper LUT as supported by the driver (read-only). -+ */ -+ struct drm_property *plane_shaper_lut_size_property; -+ /** -+ * @plane_shaper_tf_property: Plane property to set a predefined -+ * transfer function for pre-blending shaper (before applying 3D LUT) -+ * with or without LUT. -+ */ -+ struct drm_property *plane_shaper_tf_property; -+ /** -+ * @plane_lut3d_property: Plane property for gamma correction using a -+ * 3D LUT (pre-blending). -+ */ -+ struct drm_property *plane_lut3d_property; -+ /** -+ * @plane_degamma_lut_size_property: Plane property to define the max -+ * size of 3D LUT as supported by the driver (read-only). -+ */ -+ struct drm_property *plane_lut3d_size_property; -+ /** -+ * @plane_blend_lut_property: Plane property for output gamma before -+ * blending. Userspace set a blend LUT to convert colors after 3D LUT -+ * conversion. It works as a post-3D LUT 1D LUT, with shaper LUT, they -+ * are sandwiching 3D LUT with two 1D LUT. -+ */ -+ struct drm_property *plane_blend_lut_property; -+ /** -+ * @plane_blend_lut_size_property: Plane property to define the max -+ * size of blend LUT as supported by the driver (read-only). -+ */ -+ struct drm_property *plane_blend_lut_size_property; -+ /** -+ * @plane_blend_tf_property: Plane property to set a predefined -+ * transfer function for pre-blending blend (before applying 3D LUT) -+ * with or without LUT. -+ */ -+ struct drm_property *plane_blend_tf_property; - }; - - #define AMDGPU_MAX_BL_LEVEL 0xFF -diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -index 44f4c7441974..0e6bae54f6fe 100644 ---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c -@@ -5046,7 +5046,9 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev, - * Always set input transfer function, since plane state is refreshed - * every time. - */ -- ret = amdgpu_dm_update_plane_color_mgmt(dm_crtc_state, dc_plane_state); -+ ret = amdgpu_dm_update_plane_color_mgmt(dm_crtc_state, -+ plane_state, -+ dc_plane_state); - if (ret) - return ret; - -@@ -7993,6 +7995,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, - bundle->surface_updates[planes_count].gamma = dc_plane->gamma_correction; - bundle->surface_updates[planes_count].in_transfer_func = dc_plane->in_transfer_func; - bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix; -+ bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult; -+ bundle->surface_updates[planes_count].func_shaper = dc_plane->in_shaper_func; -+ bundle->surface_updates[planes_count].lut3d_func = dc_plane->lut3d_func; -+ bundle->surface_updates[planes_count].blend_tf = dc_plane->blend_tf; - } - - amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state, -@@ -8194,6 +8200,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, - &acrtc_state->stream->csc_color_matrix; - bundle->stream_update.out_transfer_func = - acrtc_state->stream->out_transfer_func; -+ bundle->stream_update.lut3d_func = -+ (struct dc_3dlut *) acrtc_state->stream->lut3d_func; -+ bundle->stream_update.func_shaper = -+ (struct dc_transfer_func *) acrtc_state->stream->func_shaper; - } - - acrtc_state->stream->abm_level = acrtc_state->abm_level; -@@ -9384,7 +9394,12 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, - */ - if (dm_new_crtc_state->base.color_mgmt_changed || - drm_atomic_crtc_needs_modeset(new_crtc_state)) { -- ret = amdgpu_dm_update_crtc_color_mgmt(dm_new_crtc_state); -+ if (!dm_state) { -+ ret = dm_atomic_get_state(state, &dm_state); -+ if (ret) -+ goto fail; -+ } -+ ret = amdgpu_dm_update_crtc_color_mgmt(dm_new_crtc_state, dm_state->context); - if (ret) - goto fail; - } -@@ -9440,6 +9455,9 @@ static bool should_reset_plane(struct drm_atomic_state *state, - if (drm_atomic_crtc_needs_modeset(new_crtc_state)) - return true; - -+ if (new_plane_state->color_mgmt_changed) -+ return true; -+ - /* - * If there are any new primary or overlay planes being added or - * removed then the z-order can potentially change. To ensure -@@ -9945,6 +9963,12 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, - goto fail; - } - -+ ret = amdgpu_dm_verify_lut3d_size(adev, new_crtc_state); -+ if (ret) { -+ drm_dbg_driver(dev, "amdgpu_dm_verify_lut_sizes() failed\n"); -+ goto fail; -+ } -+ - if (!new_crtc_state->enable) - continue; - -diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h -index 2e2413fd73a4..bf4a1d6be99e 100644 ---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h -+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h -@@ -51,6 +51,7 @@ - - #define AMDGPU_DMUB_NOTIFICATION_MAX 5 - -+#define AMDGPU_HDR_MULT_DEFAULT (0x100000000LL) - /* - #include "include/amdgpu_dal_power_if.h" - #include "amdgpu_dm_irq.h" -@@ -699,9 +700,78 @@ static inline void amdgpu_dm_set_mst_status(uint8_t *status, - - extern const struct amdgpu_ip_block_version dm_ip_block; - -+enum drm_transfer_function { -+ DRM_TRANSFER_FUNCTION_DEFAULT, -+ DRM_TRANSFER_FUNCTION_SRGB, -+ DRM_TRANSFER_FUNCTION_BT709, -+ DRM_TRANSFER_FUNCTION_PQ, -+ DRM_TRANSFER_FUNCTION_LINEAR, -+ DRM_TRANSFER_FUNCTION_UNITY, -+ DRM_TRANSFER_FUNCTION_HLG, -+ DRM_TRANSFER_FUNCTION_GAMMA22, -+ DRM_TRANSFER_FUNCTION_GAMMA24, -+ DRM_TRANSFER_FUNCTION_GAMMA26, -+ DRM_TRANSFER_FUNCTION_MAX, -+}; -+ - struct dm_plane_state { - struct drm_plane_state base; - struct dc_plane_state *dc_state; -+ -+ /* Plane color mgmt */ -+ /** -+ * @degamma_lut: -+ * -+ * LUT for converting plane pixel data before going into plane merger. -+ * The blob (if not NULL) is an array of &struct drm_color_lut. -+ */ -+ struct drm_property_blob *degamma_lut; -+ /** -+ * @degamma_tf: -+ * -+ * Predefined transfer function to tell DC driver the input space to -+ * linearize. -+ */ -+ enum drm_transfer_function degamma_tf; -+ /** -+ * @hdr_mult: -+ * -+ * Multiplier to 'gain' the plane. When PQ is decoded using the fixed -+ * func transfer function to the internal FP16 fb, 1.0 -> 80 nits (on -+ * AMD at least). When sRGB is decoded, 1.0 -> 1.0, obviously. -+ * Therefore, 1.0 multiplier = 80 nits for SDR content. So if you -+ * want, 203 nits for SDR content, pass in (203.0 / 80.0). Format is -+ * S31.32 sign-magnitude. -+ */ -+ __u64 hdr_mult; -+ /** -+ * @shaper_lut: shaper lookup table blob. The blob (if not NULL) is an -+ * array of &struct drm_color_lut. -+ */ -+ struct drm_property_blob *shaper_lut; -+ /** -+ * @shaper_tf: -+ * -+ * Predefined transfer function to delinearize color space. -+ */ -+ enum drm_transfer_function shaper_tf; -+ /** -+ * @lut3d: 3D lookup table blob. The blob (if not NULL) is an array of -+ * &struct drm_color_lut. -+ */ -+ struct drm_property_blob *lut3d; -+ /** -+ * @blend_lut: blend lut lookup table blob. The blob (if not NULL) is an -+ * array of &struct drm_color_lut. -+ */ -+ struct drm_property_blob *blend_lut; -+ /** -+ * @blend_tf: -+ * -+ * Pre-defined transfer function for converting plane pixel data before -+ * applying blend LUT. -+ */ -+ enum drm_transfer_function blend_tf; - }; - - struct dm_crtc_state { -@@ -726,6 +796,36 @@ struct dm_crtc_state { - struct dc_info_packet vrr_infopacket; - - int abm_level; -+ -+ /* AMD driver-private CRTC color management -+ * -+ * DRM provides CRTC degamma/ctm/gamma color mgmt features, but AMD HW -+ * has a larger set of post-blending color calibration. Here, DC MPC -+ * color caps are wired up to DM CRTC state: -+ */ -+ /** -+ * @shaper_lut: -+ * -+ * Post-blending 1D Lookup table used to de-linearize pixel data for 3D -+ * LUT. The blob (if not NULL) is an array of &struct drm_color_lut. -+ */ -+ struct drm_property_blob *shaper_lut; -+ /** -+ * @lut3d: -+ * -+ * Post-blending 3D Lookup table for converting pixel data. When -+ * supported by HW (DCN 3+), it is positioned just before post-blending -+ * regamma and always assumes a preceding shaper LUT. The blob (if not -+ * NULL) is an array of &struct drm_color_lut. -+ */ -+ struct drm_property_blob *lut3d; -+ /** -+ * @regamma_tf: -+ * -+ * Pre-defined transfer function for converting internal FB -> wire -+ * encoding. -+ */ -+ enum drm_transfer_function regamma_tf; - }; - - #define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base) -@@ -787,14 +887,22 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, - - void amdgpu_dm_trigger_timing_sync(struct drm_device *dev); - -+/* 3D LUT max size is 17x17x17 */ -+#define MAX_COLOR_3DLUT_ENTRIES 4913 -+#define MAX_COLOR_3DLUT_BITDEPTH 12 -+int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev, -+ const struct drm_crtc_state *crtc_state); -+/* 1D LUT size */ - #define MAX_COLOR_LUT_ENTRIES 4096 - /* Legacy gamm LUT users such as X doesn't like large LUT sizes */ - #define MAX_COLOR_LEGACY_LUT_ENTRIES 256 - - void amdgpu_dm_init_color_mod(void); - int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state); --int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc); -+int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc, -+ struct dc_state *ctx); - int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, -+ struct drm_plane_state *plane_state, - struct dc_plane_state *dc_plane_state); - - void amdgpu_dm_update_connector_after_detect( -diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c -index a4cb23d059bd..714f07bb9c9c 100644 ---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c -+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c -@@ -72,6 +72,7 @@ - */ - - #define MAX_DRM_LUT_VALUE 0xFFFF -+#define SDR_WHITE_LEVEL_INIT_VALUE 80 - - /** - * amdgpu_dm_init_color_mod - Initialize the color module. -@@ -182,7 +183,6 @@ static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut, - static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, - struct fixed31_32 *matrix) - { -- int64_t val; - int i; - - /* -@@ -201,12 +201,7 @@ static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, - } - - /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */ -- val = ctm->matrix[i - (i / 4)]; -- /* If negative, convert to 2's complement. */ -- if (val & (1ULL << 63)) -- val = -(val & ~(1ULL << 63)); -- -- matrix[i].value = val; -+ matrix[i] = dc_fixpt_from_s3132(ctm->matrix[i - (i / 4)]); - } - } - -@@ -268,16 +263,18 @@ static int __set_output_tf(struct dc_transfer_func *func, - struct calculate_buffer cal_buffer = {0}; - bool res; - -- ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES); -- - cal_buffer.buffer_index = -1; - -- gamma = dc_create_gamma(); -- if (!gamma) -- return -ENOMEM; -+ if (lut_size) { -+ ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES); - -- gamma->num_entries = lut_size; -- __drm_lut_to_dc_gamma(lut, gamma, false); -+ gamma = dc_create_gamma(); -+ if (!gamma) -+ return -ENOMEM; -+ -+ gamma->num_entries = lut_size; -+ __drm_lut_to_dc_gamma(lut, gamma, false); -+ } - - if (func->tf == TRANSFER_FUNCTION_LINEAR) { - /* -@@ -285,27 +282,63 @@ static int __set_output_tf(struct dc_transfer_func *func, - * on top of a linear input. But degamma params can be used - * instead to simulate this. - */ -- gamma->type = GAMMA_CUSTOM; -+ if (gamma) -+ gamma->type = GAMMA_CUSTOM; - res = mod_color_calculate_degamma_params(NULL, func, -- gamma, true); -+ gamma, gamma != NULL); - } else { - /* - * Assume sRGB. The actual mapping will depend on whether the - * input was legacy or not. - */ -- gamma->type = GAMMA_CS_TFM_1D; -- res = mod_color_calculate_regamma_params(func, gamma, false, -+ if (gamma) -+ gamma->type = GAMMA_CS_TFM_1D; -+ res = mod_color_calculate_regamma_params(func, gamma, gamma != NULL, - has_rom, NULL, &cal_buffer); - } - -- dc_gamma_release(&gamma); -+ if (gamma) -+ dc_gamma_release(&gamma); - - return res ? 0 : -ENOMEM; - } - -+static int amdgpu_dm_set_atomic_regamma(struct dc_stream_state *stream, -+ const struct drm_color_lut *regamma_lut, -+ uint32_t regamma_size, bool has_rom, -+ enum dc_transfer_func_predefined tf) -+{ -+ struct dc_transfer_func *out_tf = stream->out_transfer_func; -+ int ret = 0; -+ -+ if (regamma_size || tf != TRANSFER_FUNCTION_LINEAR) { -+ /* CRTC RGM goes into RGM LUT. -+ * -+ * Note: there is no implicit sRGB regamma here. We are using -+ * degamma calculation from color module to calculate the curve -+ * from a linear base. -+ */ -+ out_tf->type = TF_TYPE_DISTRIBUTED_POINTS; -+ out_tf->tf = tf; -+ out_tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; -+ -+ ret = __set_output_tf(out_tf, regamma_lut, regamma_size, has_rom); -+ } else { -+ /* -+ * No CRTC RGM means we can just put the block into bypass -+ * since we don't have any plane level adjustments using it. -+ */ -+ out_tf->type = TF_TYPE_BYPASS; -+ out_tf->tf = TRANSFER_FUNCTION_LINEAR; -+ } -+ -+ return ret; -+} -+ - /** - * __set_input_tf - calculates the input transfer function based on expected - * input space. -+ * @caps: dc color capabilities - * @func: transfer function - * @lut: lookup table that defines the color space - * @lut_size: size of respective lut. -@@ -313,27 +346,288 @@ static int __set_output_tf(struct dc_transfer_func *func, - * Returns: - * 0 in case of success. -ENOMEM if fails. - */ --static int __set_input_tf(struct dc_transfer_func *func, -+static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *func, - const struct drm_color_lut *lut, uint32_t lut_size) - { - struct dc_gamma *gamma = NULL; - bool res; - -- gamma = dc_create_gamma(); -- if (!gamma) -- return -ENOMEM; -+ if (lut_size) { -+ gamma = dc_create_gamma(); -+ if (!gamma) -+ return -ENOMEM; - -- gamma->type = GAMMA_CUSTOM; -- gamma->num_entries = lut_size; -+ gamma->type = GAMMA_CUSTOM; -+ gamma->num_entries = lut_size; - -- __drm_lut_to_dc_gamma(lut, gamma, false); -+ __drm_lut_to_dc_gamma(lut, gamma, false); -+ } - -- res = mod_color_calculate_degamma_params(NULL, func, gamma, true); -- dc_gamma_release(&gamma); -+ res = mod_color_calculate_degamma_params(caps, func, gamma, gamma != NULL); -+ -+ if (gamma) -+ dc_gamma_release(&gamma); - - return res ? 0 : -ENOMEM; - } - -+static enum dc_transfer_func_predefined drm_tf_to_dc_tf(enum drm_transfer_function drm_tf) -+{ -+ switch (drm_tf) -+ { -+ default: -+ case DRM_TRANSFER_FUNCTION_DEFAULT: return TRANSFER_FUNCTION_LINEAR; -+ case DRM_TRANSFER_FUNCTION_SRGB: return TRANSFER_FUNCTION_SRGB; -+ case DRM_TRANSFER_FUNCTION_BT709: return TRANSFER_FUNCTION_BT709; -+ case DRM_TRANSFER_FUNCTION_PQ: return TRANSFER_FUNCTION_PQ; -+ case DRM_TRANSFER_FUNCTION_LINEAR: return TRANSFER_FUNCTION_LINEAR; -+ case DRM_TRANSFER_FUNCTION_UNITY: return TRANSFER_FUNCTION_UNITY; -+ case DRM_TRANSFER_FUNCTION_HLG: return TRANSFER_FUNCTION_HLG; -+ case DRM_TRANSFER_FUNCTION_GAMMA22: return TRANSFER_FUNCTION_GAMMA22; -+ case DRM_TRANSFER_FUNCTION_GAMMA24: return TRANSFER_FUNCTION_GAMMA24; -+ case DRM_TRANSFER_FUNCTION_GAMMA26: return TRANSFER_FUNCTION_GAMMA26; -+ } -+} -+ -+static void __to_dc_lut3d_color(struct dc_rgb *rgb, -+ const struct drm_color_lut lut, -+ int bit_precision) -+{ -+ rgb->red = drm_color_lut_extract(lut.red, bit_precision); -+ rgb->green = drm_color_lut_extract(lut.green, bit_precision); -+ rgb->blue = drm_color_lut_extract(lut.blue, bit_precision); -+} -+ -+static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut, -+ uint32_t lut3d_size, -+ struct tetrahedral_params *params, -+ bool use_tetrahedral_9, -+ int bit_depth) -+{ -+ struct dc_rgb *lut0; -+ struct dc_rgb *lut1; -+ struct dc_rgb *lut2; -+ struct dc_rgb *lut3; -+ int lut_i, i; -+ -+ -+ if (use_tetrahedral_9) { -+ lut0 = params->tetrahedral_9.lut0; -+ lut1 = params->tetrahedral_9.lut1; -+ lut2 = params->tetrahedral_9.lut2; -+ lut3 = params->tetrahedral_9.lut3; -+ } else { -+ lut0 = params->tetrahedral_17.lut0; -+ lut1 = params->tetrahedral_17.lut1; -+ lut2 = params->tetrahedral_17.lut2; -+ lut3 = params->tetrahedral_17.lut3; -+ } -+ -+ for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) { -+ /* We should consider the 3dlut RGB values are distributed -+ * along four arrays lut0-3 where the first sizes 1229 and the -+ * other 1228. The bit depth supported for 3dlut channel is -+ * 12-bit, but DC also supports 10-bit. -+ * -+ * TODO: improve color pipeline API to enable the userspace set -+ * bit depth and 3D LUT size/stride, as specified by VA-API. -+ */ -+ __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth); -+ __to_dc_lut3d_color(&lut1[lut_i], lut[i + 1], bit_depth); -+ __to_dc_lut3d_color(&lut2[lut_i], lut[i + 2], bit_depth); -+ __to_dc_lut3d_color(&lut3[lut_i], lut[i + 3], bit_depth); -+ } -+ /* lut0 has 1229 points (lut_size/4 + 1) */ -+ __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth); -+} -+ -+/* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream -+ * @drm_lut3d: DRM CRTC (user) 3D LUT -+ * @drm_lut3d_size: size of 3D LUT -+ * @lut3d: DC 3D LUT -+ * -+ * Map DRM CRTC 3D LUT to DC 3D LUT and all necessary bits to program it -+ * on DCN MPC accordingly. -+ */ -+static void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut, -+ uint32_t drm_lut3d_size, -+ struct dc_3dlut *lut) -+{ -+ if (!drm_lut3d_size) { -+ lut->state.bits.initialized = 0; -+ } else { -+ /* Stride and bit depth are not programmable by API yet. -+ * Therefore, only supports 17x17x17 3D LUT (12-bit). -+ */ -+ lut->lut_3d.use_tetrahedral_9 = false; -+ lut->lut_3d.use_12bits = true; -+ lut->state.bits.initialized = 1; -+ __drm_3dlut_to_dc_3dlut(drm_lut, drm_lut3d_size, &lut->lut_3d, -+ lut->lut_3d.use_tetrahedral_9, -+ MAX_COLOR_3DLUT_BITDEPTH); -+ } -+} -+ -+static int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut, -+ bool has_rom, -+ enum dc_transfer_func_predefined tf, -+ uint32_t shaper_size, -+ struct dc_transfer_func *func_shaper) -+{ -+ int ret = 0; -+ -+ if (shaper_size || tf != TRANSFER_FUNCTION_LINEAR) { -+ /* If DRM shaper LUT is set, we assume a linear color space -+ * (linearized by DRM degamma 1D LUT or not) -+ */ -+ func_shaper->type = TF_TYPE_DISTRIBUTED_POINTS; -+ func_shaper->tf = tf; -+ func_shaper->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; -+ -+ ret = __set_output_tf(func_shaper, shaper_lut, shaper_size, has_rom); -+ } else { -+ func_shaper->type = TF_TYPE_BYPASS; -+ func_shaper->tf = TRANSFER_FUNCTION_LINEAR; -+ } -+ -+ return ret; -+} -+ -+static int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blend_lut, -+ bool has_rom, -+ enum dc_transfer_func_predefined tf, -+ uint32_t blend_size, -+ struct dc_transfer_func *func_blend) -+{ -+ int ret = 0; -+ -+ if (blend_size || tf != TRANSFER_FUNCTION_LINEAR) { -+ /* DRM plane gamma LUT or TF means we are linearizing color -+ * space before blending (similar to degamma programming). As -+ * we don't have hardcoded curve support, or we use AMD color -+ * module to fill the parameters that will be translated to HW -+ * points. -+ */ -+ func_blend->type = TF_TYPE_DISTRIBUTED_POINTS; -+ func_blend->tf = tf; -+ func_blend->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; -+ -+ ret = __set_input_tf(NULL, func_blend, blend_lut, blend_size); -+ } else { -+ func_blend->type = TF_TYPE_BYPASS; -+ func_blend->tf = TRANSFER_FUNCTION_LINEAR; -+ } -+ -+ return ret; -+} -+ -+/* amdgpu_dm_atomic_shaper_lut3d - set DRM CRTC shaper LUT and 3D LUT to DC -+ * interface -+ * @dc: Display Core control structure -+ * @ctx: new DC state information -+ * @stream: DC stream state to set shaper LUT and 3D LUT -+ * @drm_shaper_lut: DRM CRTC (user) shaper LUT -+ * @drm_shaper_size: size of shaper LUT -+ * @drm_lut3d: DRM CRTC (user) 3D LUT -+ * @drm_lut3d_size: size of 3D LUT -+ * -+ * Returns: -+ * 0 on success. -+ */ -+static int amdgpu_dm_atomic_shaper_lut3d(struct dc *dc, -+ struct dc_state *ctx, -+ struct dc_stream_state *stream, -+ const struct drm_color_lut *drm_shaper_lut, -+ uint32_t drm_shaper_size, -+ bool has_rom, -+ enum dc_transfer_func_predefined tf, -+ const struct drm_color_lut *drm_lut3d, -+ uint32_t drm_lut3d_size) -+{ -+ struct dc_3dlut *lut3d_func; -+ struct dc_transfer_func *func_shaper; -+ bool acquire = drm_shaper_size || drm_lut3d_size; -+ -+ lut3d_func = (struct dc_3dlut *)stream->lut3d_func; -+ func_shaper = (struct dc_transfer_func *)stream->func_shaper; -+ -+ ASSERT((lut3d_func && func_shaper) || (!lut3d_func && !func_shaper)); -+ if ((acquire && !lut3d_func && !func_shaper) || -+ (!acquire && lut3d_func && func_shaper)) -+ { -+ if (!dc_acquire_release_mpc_3dlut_for_ctx(dc, acquire, ctx, stream, -+ &lut3d_func, &func_shaper)) -+ return DC_ERROR_UNEXPECTED; -+ } -+ -+ stream->func_shaper = func_shaper; -+ stream->lut3d_func = lut3d_func; -+ -+ if (!acquire) -+ return 0; -+ -+ amdgpu_dm_atomic_lut3d(drm_lut3d, drm_lut3d_size, lut3d_func); -+ -+ return amdgpu_dm_atomic_shaper_lut(drm_shaper_lut, has_rom, tf, -+ drm_shaper_size, func_shaper); -+} -+ -+/** -+ * amdgpu_dm_lut3d_size - get expected size according to hw color caps -+ * @adev: amdgpu device -+ * @lut_size: default size -+ * -+ * Return: -+ * lut_size if DC 3D LUT is supported, zero otherwise. -+ */ -+static uint32_t amdgpu_dm_get_lut3d_size(struct amdgpu_device *adev, -+ uint32_t lut_size) -+{ -+ return adev->dm.dc->caps.color.mpc.num_3dluts ? lut_size : 0; -+} -+ -+/** -+ * amdgpu_dm_verify_lut3d_size - verifies if 3D LUT is supported and if DRM 3D -+ * LUT matches the hw supported size -+ * @adev: amdgpu device -+ * @crtc_state: the DRM CRTC state -+ * -+ * Verifies if post-blending (MPC) 3D LUT is supported by the HW (DCN 3.0 or -+ * newer) and if the DRM 3D LUT matches the supported size. -+ * -+ * Returns: -+ * 0 on success. -EINVAL if lut size are invalid. -+ */ -+int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev, -+ const struct drm_crtc_state *crtc_state) -+{ -+ struct dm_crtc_state *acrtc_state = to_dm_crtc_state(crtc_state); -+ const struct drm_color_lut *shaper = NULL, *lut3d = NULL; -+ uint32_t exp_size, size; -+ -+ /* shaper LUT is only available if 3D LUT color caps*/ -+ exp_size = amdgpu_dm_get_lut3d_size(adev, MAX_COLOR_LUT_ENTRIES); -+ shaper = __extract_blob_lut(acrtc_state->shaper_lut, &size); -+ -+ if (shaper && size != exp_size) { -+ drm_dbg(&adev->ddev, -+ "Invalid Shaper LUT size. Should be %u but got %u.\n", -+ exp_size, size); -+ return -EINVAL; -+ } -+ -+ exp_size = amdgpu_dm_get_lut3d_size(adev, MAX_COLOR_3DLUT_ENTRIES); -+ lut3d = __extract_blob_lut(acrtc_state->lut3d, &size); -+ -+ if (lut3d && size != exp_size) { -+ drm_dbg(&adev->ddev, "Invalid 3D LUT size. Should be %u but got %u.\n", -+ exp_size, size); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ - /** - * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes - * @crtc_state: the DRM CRTC state -@@ -373,6 +667,7 @@ int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state) - /** - * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream. - * @crtc: amdgpu_dm crtc state -+ * @ctx: new DC state information - * - * With no plane level color management properties we're free to use any - * of the HW blocks as long as the CRTC CTM always comes before the -@@ -392,7 +687,8 @@ int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state) - * Returns: - * 0 on success. Error code if setup fails. - */ --int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc) -+int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc, -+ struct dc_state *ctx) - { - struct dc_stream_state *stream = crtc->stream; - struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev); -@@ -401,9 +697,20 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc) - const struct drm_color_lut *degamma_lut, *regamma_lut; - uint32_t degamma_size, regamma_size; - bool has_regamma, has_degamma; -+ enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_LINEAR; - bool is_legacy; -+ const struct drm_color_lut *shaper_lut, *lut3d; -+ uint32_t shaper_size, lut3d_size; - int r; - -+ r = amdgpu_dm_verify_lut3d_size(adev, &crtc->base); -+ if (r) -+ return r; -+ -+ lut3d = __extract_blob_lut(crtc->lut3d, &lut3d_size); -+ shaper_lut = __extract_blob_lut(crtc->shaper_lut, &shaper_size); -+ tf = drm_tf_to_dc_tf(crtc->regamma_tf); -+ - r = amdgpu_dm_verify_lut_sizes(&crtc->base); - if (r) - return r; -@@ -440,26 +747,41 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc) - stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; - stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB; - -+ /* Note: although we pass has_rom as parameter here, we never -+ * actually use ROM because the color module only takes the ROM -+ * path if transfer_func->type == PREDEFINED. -+ * -+ * See more in mod_color_calculate_regamma_params() -+ */ - r = __set_legacy_tf(stream->out_transfer_func, regamma_lut, - regamma_size, has_rom); - if (r) - return r; -- } else if (has_regamma) { -- /* If atomic regamma, CRTC RGM goes into RGM LUT. */ -- stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; -- stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; -+ } else { -+ /* We are not exposing CRTC 3D LUT properties yet, so DC 3D LUT -+ * programming is expected to be set to bypass mode, since -+ * there is no user-blob. -+ */ -+ lut3d_size = lut3d != NULL ? lut3d_size : 0; -+ shaper_size = shaper_lut != NULL ? shaper_size : 0; -+ r = amdgpu_dm_atomic_shaper_lut3d(adev->dm.dc, ctx, stream, -+ shaper_lut, shaper_size, -+ has_rom, tf, -+ lut3d, lut3d_size); -+ if (r) { -+ drm_dbg(&adev->ddev, "Failed on shaper/3D LUTs setup\n"); -+ return r; -+ } - -- r = __set_output_tf(stream->out_transfer_func, regamma_lut, -- regamma_size, has_rom); -+ /* Note: OGAM is disabled if 3D LUT is successfully programmed. -+ * See params and set_output_gamma in -+ * dcn30_set_output_transfer_func() -+ */ -+ regamma_size = has_regamma ? regamma_size : 0; -+ r = amdgpu_dm_set_atomic_regamma(stream, regamma_lut, -+ regamma_size, has_rom, tf); - if (r) - return r; -- } else { -- /* -- * No CRTC RGM means we can just put the block into bypass -- * since we don't have any plane level adjustments using it. -- */ -- stream->out_transfer_func->type = TF_TYPE_BYPASS; -- stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; - } - - /* -@@ -495,20 +817,10 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc) - return 0; - } - --/** -- * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane. -- * @crtc: amdgpu_dm crtc state -- * @dc_plane_state: target DC surface -- * -- * Update the underlying dc_stream_state's input transfer function (ITF) in -- * preparation for hardware commit. The transfer function used depends on -- * the preparation done on the stream for color management. -- * -- * Returns: -- * 0 on success. -ENOMEM if mem allocation fails. -- */ --int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, -- struct dc_plane_state *dc_plane_state) -+static int -+map_crtc_degamma_to_dc_plane(struct dm_crtc_state *crtc, -+ struct dc_plane_state *dc_plane_state, -+ struct dc_color_caps *caps) - { - const struct drm_color_lut *degamma_lut; - enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; -@@ -531,8 +843,7 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, - °amma_size); - ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES); - -- dc_plane_state->in_transfer_func->type = -- TF_TYPE_DISTRIBUTED_POINTS; -+ dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; - - /* - * This case isn't fully correct, but also fairly -@@ -564,11 +875,11 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, - dc_plane_state->in_transfer_func->tf = - TRANSFER_FUNCTION_LINEAR; - -- r = __set_input_tf(dc_plane_state->in_transfer_func, -+ r = __set_input_tf(caps, dc_plane_state->in_transfer_func, - degamma_lut, degamma_size); - if (r) - return r; -- } else if (crtc->cm_is_degamma_srgb) { -+ } else { - /* - * For legacy gamma support we need the regamma input - * in linear space. Assume that the input is sRGB. -@@ -577,14 +888,183 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, - dc_plane_state->in_transfer_func->tf = tf; - - if (tf != TRANSFER_FUNCTION_SRGB && -- !mod_color_calculate_degamma_params(NULL, -- dc_plane_state->in_transfer_func, NULL, false)) -+ !mod_color_calculate_degamma_params(caps, -+ dc_plane_state->in_transfer_func, -+ NULL, false)) -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static int -+__set_dm_plane_degamma(struct drm_plane_state *plane_state, -+ struct dc_plane_state *dc_plane_state, -+ struct dc_color_caps *color_caps) -+{ -+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); -+ const struct drm_color_lut *degamma_lut; -+ enum drm_transfer_function drm_tf = DRM_TRANSFER_FUNCTION_DEFAULT; -+ uint32_t degamma_size; -+ bool has_degamma_lut; -+ int ret; -+ -+ if (dc_plane_state->ctx && dc_plane_state->ctx->dc) -+ color_caps = &dc_plane_state->ctx->dc->caps.color; -+ -+ degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut, -+ °amma_size); -+ -+ has_degamma_lut = degamma_lut && -+ !__is_lut_linear(degamma_lut, degamma_size); -+ -+ drm_tf = dm_plane_state->degamma_tf; -+ -+ /* If we don't have plane degamma LUT nor TF to set on DC, we have -+ * nothing to do here, return. -+ */ -+ if (!has_degamma_lut && drm_tf == DRM_TRANSFER_FUNCTION_DEFAULT) -+ return -EINVAL; -+ -+ dc_plane_state->in_transfer_func->tf = drm_tf_to_dc_tf(drm_tf); -+ -+ if (has_degamma_lut) { -+ ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES); -+ -+ dc_plane_state->in_transfer_func->type = -+ TF_TYPE_DISTRIBUTED_POINTS; -+ -+ ret = __set_input_tf(color_caps, dc_plane_state->in_transfer_func, -+ degamma_lut, degamma_size); -+ if (ret) -+ return ret; -+ } else { -+ dc_plane_state->in_transfer_func->type = -+ TF_TYPE_PREDEFINED; -+ -+ if (!mod_color_calculate_degamma_params(NULL, -+ dc_plane_state->in_transfer_func, NULL, false)) - return -ENOMEM; -- } else { -- /* ...Otherwise we can just bypass the DGM block. */ -- dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; -- dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; -+ } -+ return 0; -+} -+ -+static int -+amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, -+ struct dc_plane_state *dc_plane_state, -+ struct dc_color_caps *color_caps) -+{ -+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); -+ enum drm_transfer_function shaper_tf = DRM_TRANSFER_FUNCTION_DEFAULT; -+ enum drm_transfer_function blend_tf = DRM_TRANSFER_FUNCTION_DEFAULT; -+ const struct drm_color_lut *shaper_lut, *lut3d, *blend_lut; -+ uint32_t lut3d_size, shaper_size, blend_size; -+ int ret; -+ -+ /* We have nothing to do here, return */ -+ if (!plane_state->color_mgmt_changed) -+ return 0; -+ -+ dc_plane_state->hdr_mult = dc_fixpt_from_s3132(dm_plane_state->hdr_mult); -+ -+ shaper_tf = dm_plane_state->shaper_tf; -+ shaper_lut = __extract_blob_lut(dm_plane_state->shaper_lut, &shaper_size); -+ lut3d = __extract_blob_lut(dm_plane_state->lut3d, &lut3d_size); -+ lut3d_size = lut3d != NULL ? lut3d_size : 0; -+ shaper_size = shaper_lut != NULL ? shaper_size : 0; -+ -+ amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, dc_plane_state->lut3d_func); -+ ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, false, -+ drm_tf_to_dc_tf(shaper_tf), -+ shaper_size, dc_plane_state->in_shaper_func); -+ if (ret) { -+ drm_dbg_kms(plane_state->plane->dev, -+ "setting plane %d shaper/3d lut failed.\n", -+ plane_state->plane->index); -+ -+ return ret; -+ } -+ -+ blend_tf = dm_plane_state->blend_tf; -+ blend_lut = __extract_blob_lut(dm_plane_state->blend_lut, &blend_size); -+ blend_size = blend_lut != NULL ? blend_size : 0; -+ -+ ret = amdgpu_dm_atomic_blend_lut(blend_lut, false, -+ drm_tf_to_dc_tf(blend_tf), -+ blend_size, dc_plane_state->blend_tf); -+ if (ret) { -+ drm_dbg_kms(plane_state->plane->dev, -+ "setting plane %d gamma lut failed.\n", -+ plane_state->plane->index); -+ -+ return ret; - } - - return 0; - } -+ -+/** -+ * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane. -+ * @crtc: amdgpu_dm crtc state -+ * @plane_state: DRM plane state -+ * @dc_plane_state: target DC surface -+ * -+ * Update the underlying dc_stream_state's input transfer function (ITF) in -+ * preparation for hardware commit. The transfer function used depends on -+ * the preparation done on the stream for color management. -+ * -+ * Returns: -+ * 0 on success. -ENOMEM if mem allocation fails. -+ */ -+int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, -+ struct drm_plane_state *plane_state, -+ struct dc_plane_state *dc_plane_state) -+{ -+ struct dc_color_caps *color_caps = NULL; -+ bool has_crtc_cm_degamma; -+ int ret; -+ -+ if (dc_plane_state->ctx && dc_plane_state->ctx->dc) -+ color_caps = &dc_plane_state->ctx->dc->caps.color; -+ -+ /* Initially, we can just bypass the DGM block. */ -+ dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; -+ dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; -+ -+ /* After, we start to update values according to color props */ -+ has_crtc_cm_degamma = (crtc->cm_has_degamma || crtc->cm_is_degamma_srgb); -+ -+ ret = __set_dm_plane_degamma(plane_state, dc_plane_state, color_caps); -+ if (ret == -ENOMEM) -+ return ret; -+ -+ /* We only have one degamma block available (pre-blending) for the -+ * whole color correction pipeline, so that we can't actually perform -+ * plane and CRTC degamma at the same time. Explicitly reject atomic -+ * updates when userspace sets both plane and CRTC degamma properties. -+ */ -+ if (has_crtc_cm_degamma && ret != -EINVAL){ -+ drm_dbg_kms(crtc->base.crtc->dev, -+ "doesn't support plane and CRTC degamma at the same time\n"); -+ return -EINVAL; -+ } -+ -+ /* If we are here, it means we don't have plane degamma settings, check -+ * if we have CRTC degamma waiting for mapping to pre-blending degamma -+ * block -+ */ -+ if (has_crtc_cm_degamma) { -+ /* AMD HW doesn't have post-blending degamma caps. When DRM -+ * CRTC atomic degamma is set, we maps it to DPP degamma block -+ * (pre-blending) or, on legacy gamma, we use DPP degamma to -+ * linearize (implicit degamma) from sRGB/BT709 according to -+ * the input space. -+ */ -+ ret = map_crtc_degamma_to_dc_plane(crtc, dc_plane_state, color_caps); -+ if (ret) -+ return ret; -+ } -+ -+ return amdgpu_dm_plane_set_color_properties(plane_state, -+ dc_plane_state, color_caps); -+} -diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c -index 440fc0869a34..4a725aeef3e8 100644 ---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c -+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c -@@ -219,7 +219,6 @@ static void dm_crtc_destroy_state(struct drm_crtc *crtc, - if (cur->stream) - dc_stream_release(cur->stream); - -- - __drm_atomic_helper_crtc_destroy_state(state); - - -@@ -253,6 +252,7 @@ static struct drm_crtc_state *dm_crtc_duplicate_state(struct drm_crtc *crtc) - state->freesync_config = cur->freesync_config; - state->cm_has_degamma = cur->cm_has_degamma; - state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb; -+ state->regamma_tf = cur->regamma_tf; - state->crc_skip_count = cur->crc_skip_count; - state->mpo_requested = cur->mpo_requested; - /* TODO Duplicate dc_stream after objects are stream object is flattened */ -@@ -289,6 +289,69 @@ static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc) - } - #endif - -+#ifdef AMD_PRIVATE_COLOR -+/** -+ * drm_crtc_additional_color_mgmt - enable additional color properties -+ * @crtc: DRM CRTC -+ * -+ * This function lets the driver enable the 3D LUT color correction property -+ * on a CRTC. This includes shaper LUT, 3D LUT and regamma TF. The shaper -+ * LUT and 3D LUT property is only attached if its size is not 0. -+ */ -+static void -+dm_crtc_additional_color_mgmt(struct drm_crtc *crtc) -+{ -+ struct amdgpu_device *adev = drm_to_adev(crtc->dev); -+ -+ if(adev->dm.dc->caps.color.mpc.ogam_ram) -+ drm_object_attach_property(&crtc->base, -+ adev->mode_info.regamma_tf_property, -+ DRM_TRANSFER_FUNCTION_DEFAULT); -+} -+ -+static int -+amdgpu_dm_atomic_crtc_set_property(struct drm_crtc *crtc, -+ struct drm_crtc_state *state, -+ struct drm_property *property, -+ uint64_t val) -+{ -+ struct amdgpu_device *adev = drm_to_adev(crtc->dev); -+ struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state); -+ -+ if (property == adev->mode_info.regamma_tf_property) { -+ if (acrtc_state->regamma_tf != val) { -+ acrtc_state->regamma_tf = val; -+ acrtc_state->base.color_mgmt_changed |= 1; -+ } -+ } else { -+ drm_dbg_atomic(crtc->dev, -+ "[CRTC:%d:%s] unknown property [PROP:%d:%s]]\n", -+ crtc->base.id, crtc->name, -+ property->base.id, property->name); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+amdgpu_dm_atomic_crtc_get_property(struct drm_crtc *crtc, -+ const struct drm_crtc_state *state, -+ struct drm_property *property, -+ uint64_t *val) -+{ -+ struct amdgpu_device *adev = drm_to_adev(crtc->dev); -+ struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state); -+ -+ if (property == adev->mode_info.regamma_tf_property) -+ *val = acrtc_state->regamma_tf; -+ else -+ return -EINVAL; -+ -+ return 0; -+} -+#endif -+ - /* Implemented only the options currently available for the driver */ - static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { - .reset = dm_crtc_reset_state, -@@ -307,6 +370,10 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { - #if defined(CONFIG_DEBUG_FS) - .late_register = amdgpu_dm_crtc_late_register, - #endif -+#ifdef AMD_PRIVATE_COLOR -+ .atomic_set_property = amdgpu_dm_atomic_crtc_set_property, -+ .atomic_get_property = amdgpu_dm_atomic_crtc_get_property, -+#endif - }; - - static void dm_crtc_helper_disable(struct drm_crtc *crtc) -@@ -470,6 +537,9 @@ int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm, - - drm_mode_crtc_set_gamma_size(&acrtc->base, MAX_COLOR_LEGACY_LUT_ENTRIES); - -+#ifdef AMD_PRIVATE_COLOR -+ dm_crtc_additional_color_mgmt(&acrtc->base); -+#endif - return 0; - - fail: -diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c -index 322668973747..ea13b49fa021 100644 ---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c -+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c -@@ -1317,8 +1317,14 @@ static void dm_drm_plane_reset(struct drm_plane *plane) - amdgpu_state = kzalloc(sizeof(*amdgpu_state), GFP_KERNEL); - WARN_ON(amdgpu_state == NULL); - -- if (amdgpu_state) -- __drm_atomic_helper_plane_reset(plane, &amdgpu_state->base); -+ if (!amdgpu_state) -+ return; -+ -+ __drm_atomic_helper_plane_reset(plane, &amdgpu_state->base); -+ amdgpu_state->degamma_tf = DRM_TRANSFER_FUNCTION_DEFAULT; -+ amdgpu_state->hdr_mult = AMDGPU_HDR_MULT_DEFAULT; -+ amdgpu_state->shaper_tf = DRM_TRANSFER_FUNCTION_DEFAULT; -+ amdgpu_state->blend_tf = DRM_TRANSFER_FUNCTION_DEFAULT; - } - - static struct drm_plane_state * -@@ -1338,6 +1344,20 @@ dm_drm_plane_duplicate_state(struct drm_plane *plane) - dc_plane_state_retain(dm_plane_state->dc_state); - } - -+ if (dm_plane_state->degamma_lut) -+ drm_property_blob_get(dm_plane_state->degamma_lut); -+ if (dm_plane_state->shaper_lut) -+ drm_property_blob_get(dm_plane_state->shaper_lut); -+ if (dm_plane_state->lut3d) -+ drm_property_blob_get(dm_plane_state->lut3d); -+ if (dm_plane_state->blend_lut) -+ drm_property_blob_get(dm_plane_state->blend_lut); -+ -+ dm_plane_state->degamma_tf = old_dm_plane_state->degamma_tf; -+ dm_plane_state->hdr_mult = old_dm_plane_state->hdr_mult; -+ dm_plane_state->shaper_tf = old_dm_plane_state->shaper_tf; -+ dm_plane_state->blend_tf = old_dm_plane_state->blend_tf; -+ - return &dm_plane_state->base; - } - -@@ -1405,12 +1425,194 @@ static void dm_drm_plane_destroy_state(struct drm_plane *plane, - { - struct dm_plane_state *dm_plane_state = to_dm_plane_state(state); - -+ if (dm_plane_state->degamma_lut) -+ drm_property_blob_put(dm_plane_state->degamma_lut); -+ if (dm_plane_state->lut3d) -+ drm_property_blob_put(dm_plane_state->lut3d); -+ if (dm_plane_state->shaper_lut) -+ drm_property_blob_put(dm_plane_state->shaper_lut); -+ if (dm_plane_state->blend_lut) -+ drm_property_blob_put(dm_plane_state->blend_lut); -+ - if (dm_plane_state->dc_state) - dc_plane_state_release(dm_plane_state->dc_state); - - drm_atomic_helper_plane_destroy_state(plane, state); - } - -+static const struct drm_prop_enum_list drm_transfer_function_enum_list[] = { -+ { DRM_TRANSFER_FUNCTION_DEFAULT, "Default" }, -+ { DRM_TRANSFER_FUNCTION_SRGB, "sRGB" }, -+ { DRM_TRANSFER_FUNCTION_BT709, "BT.709" }, -+ { DRM_TRANSFER_FUNCTION_PQ, "PQ (Perceptual Quantizer)" }, -+ { DRM_TRANSFER_FUNCTION_LINEAR, "Linear" }, -+ { DRM_TRANSFER_FUNCTION_UNITY, "Unity" }, -+ { DRM_TRANSFER_FUNCTION_HLG, "HLG (Hybrid Log Gamma)" }, -+ { DRM_TRANSFER_FUNCTION_GAMMA22, "Gamma 2.2" }, -+ { DRM_TRANSFER_FUNCTION_GAMMA24, "Gamma 2.4" }, -+ { DRM_TRANSFER_FUNCTION_GAMMA26, "Gamma 2.6" }, -+}; -+ -+#ifdef AMD_PRIVATE_COLOR -+static void -+dm_atomic_plane_attach_color_mgmt_properties(struct amdgpu_display_manager *dm, -+ struct drm_plane *plane) -+{ -+ if (dm->dc->caps.color.dpp.dgam_ram || dm->dc->caps.color.dpp.gamma_corr ) { -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_degamma_lut_property, 0); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_degamma_lut_size_property, -+ MAX_COLOR_LUT_ENTRIES); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_degamma_tf_property, -+ DRM_TRANSFER_FUNCTION_DEFAULT); -+ } -+ /* HDR MULT is always available */ -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_hdr_mult_property, -+ AMDGPU_HDR_MULT_DEFAULT); -+ -+ if (dm->dc->caps.color.dpp.hw_3d_lut) { -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_shaper_lut_property, 0); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_shaper_lut_size_property, -+ MAX_COLOR_LUT_ENTRIES); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_shaper_tf_property, -+ DRM_TRANSFER_FUNCTION_DEFAULT); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_lut3d_property, 0); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_lut3d_size_property, -+ MAX_COLOR_3DLUT_ENTRIES); -+ } -+ -+ if (dm->dc->caps.color.dpp.ogam_ram) { -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_blend_lut_property, 0); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_blend_lut_size_property, -+ MAX_COLOR_LUT_ENTRIES); -+ drm_object_attach_property(&plane->base, -+ dm->adev->mode_info.plane_blend_tf_property, -+ DRM_TRANSFER_FUNCTION_DEFAULT); -+ } -+} -+ -+static int -+dm_atomic_plane_set_property(struct drm_plane *plane, -+ struct drm_plane_state *state, -+ struct drm_property *property, -+ uint64_t val) -+{ -+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(state); -+ struct amdgpu_device *adev = drm_to_adev(plane->dev); -+ bool replaced = false; -+ int ret; -+ -+ if (property == adev->mode_info.plane_degamma_lut_property) { -+ ret = drm_property_replace_blob_from_id(plane->dev, -+ &dm_plane_state->degamma_lut, -+ val, -+ -1, sizeof(struct drm_color_lut), -+ &replaced); -+ dm_plane_state->base.color_mgmt_changed |= replaced; -+ return ret; -+ } else if (property == adev->mode_info.plane_degamma_tf_property) { -+ if (dm_plane_state->degamma_tf != val) { -+ dm_plane_state->degamma_tf = val; -+ dm_plane_state->base.color_mgmt_changed = 1; -+ } -+ } else if (property == adev->mode_info.plane_hdr_mult_property) { -+ if (dm_plane_state->hdr_mult != val) { -+ dm_plane_state->hdr_mult = val; -+ dm_plane_state->base.color_mgmt_changed = 1; -+ } -+ } else if (property == adev->mode_info.plane_shaper_lut_property) { -+ ret = drm_property_replace_blob_from_id(plane->dev, -+ &dm_plane_state->shaper_lut, -+ val, -1, -+ sizeof(struct drm_color_lut), -+ &replaced); -+ dm_plane_state->base.color_mgmt_changed |= replaced; -+ return ret; -+ } else if (property == adev->mode_info.plane_shaper_tf_property) { -+ if (dm_plane_state->shaper_tf != val) { -+ dm_plane_state->shaper_tf = val; -+ dm_plane_state->base.color_mgmt_changed = 1; -+ } -+ } else if (property == adev->mode_info.plane_lut3d_property) { -+ ret = drm_property_replace_blob_from_id(plane->dev, -+ &dm_plane_state->lut3d, -+ val, -1, -+ sizeof(struct drm_color_lut), -+ &replaced); -+ dm_plane_state->base.color_mgmt_changed |= replaced; -+ return ret; -+ } else if (property == adev->mode_info.plane_blend_lut_property) { -+ ret = drm_property_replace_blob_from_id(plane->dev, -+ &dm_plane_state->blend_lut, -+ val, -1, -+ sizeof(struct drm_color_lut), -+ &replaced); -+ dm_plane_state->base.color_mgmt_changed |= replaced; -+ return ret; -+ } else if (property == adev->mode_info.plane_blend_tf_property) { -+ if (dm_plane_state->blend_tf != val) { -+ dm_plane_state->blend_tf = val; -+ dm_plane_state->base.color_mgmt_changed = 1; -+ } -+ } else { -+ drm_dbg_atomic(plane->dev, -+ "[PLANE:%d:%s] unknown property [PROP:%d:%s]]\n", -+ plane->base.id, plane->name, -+ property->base.id, property->name); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+dm_atomic_plane_get_property(struct drm_plane *plane, -+ const struct drm_plane_state *state, -+ struct drm_property *property, -+ uint64_t *val) -+{ -+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(state); -+ struct amdgpu_device *adev = drm_to_adev(plane->dev); -+ -+ if (property == adev->mode_info.plane_degamma_lut_property) { -+ *val = (dm_plane_state->degamma_lut) ? -+ dm_plane_state->degamma_lut->base.id : 0; -+ } else if (property == adev->mode_info.plane_degamma_tf_property) { -+ *val = dm_plane_state->degamma_tf; -+ } else if (property == adev->mode_info.plane_hdr_mult_property) { -+ *val = dm_plane_state->hdr_mult; -+ } else if (property == adev->mode_info.plane_shaper_lut_property) { -+ *val = (dm_plane_state->shaper_lut) ? -+ dm_plane_state->shaper_lut->base.id : 0; -+ } else if (property == adev->mode_info.plane_shaper_tf_property) { -+ *val = dm_plane_state->shaper_tf; -+ } else if (property == adev->mode_info.plane_lut3d_property) { -+ *val = (dm_plane_state->lut3d) ? -+ dm_plane_state->lut3d->base.id : 0; -+ } else if (property == adev->mode_info.plane_blend_lut_property) { -+ *val = (dm_plane_state->blend_lut) ? -+ dm_plane_state->blend_lut->base.id : 0; -+ } else if (property == adev->mode_info.plane_blend_tf_property) { -+ *val = dm_plane_state->blend_tf; -+ -+ } else { -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+#endif -+ - static const struct drm_plane_funcs dm_plane_funcs = { - .update_plane = drm_atomic_helper_update_plane, - .disable_plane = drm_atomic_helper_disable_plane, -@@ -1419,6 +1621,10 @@ static const struct drm_plane_funcs dm_plane_funcs = { - .atomic_duplicate_state = dm_drm_plane_duplicate_state, - .atomic_destroy_state = dm_drm_plane_destroy_state, - .format_mod_supported = dm_plane_format_mod_supported, -+#ifdef AMD_PRIVATE_COLOR -+ .atomic_set_property = dm_atomic_plane_set_property, -+ .atomic_get_property = dm_atomic_plane_get_property, -+#endif - }; - - int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, -@@ -1489,6 +1695,9 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, - - drm_plane_helper_add(plane, &dm_plane_helper_funcs); - -+#ifdef AMD_PRIVATE_COLOR -+ dm_atomic_plane_attach_color_mgmt_properties(dm, plane); -+#endif - /* Create (reset) the plane state */ - if (plane->funcs->reset) - plane->funcs->reset(plane); -diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c -index 6eace83c9c6f..35a8b8ca87de 100644 ---- a/drivers/gpu/drm/amd/display/dc/core/dc.c -+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c -@@ -2126,6 +2126,45 @@ bool dc_acquire_release_mpc_3dlut( - return ret; - } - -+bool -+dc_acquire_release_mpc_3dlut_for_ctx(struct dc *dc, -+ bool acquire, -+ struct dc_state *state, -+ struct dc_stream_state *stream, -+ struct dc_3dlut **lut, -+ struct dc_transfer_func **shaper) -+{ -+ int pipe_idx; -+ bool ret = false; -+ bool found_pipe_idx = false; -+ const struct resource_pool *pool = dc->res_pool; -+ struct resource_context *res_ctx = &state->res_ctx; -+ int mpcc_id = 0; -+ -+ if (pool && res_ctx) { -+ if (acquire) { -+ /*find pipe idx for the given stream*/ -+ for (pipe_idx = 0; pipe_idx < pool->pipe_count; pipe_idx++) { -+ if (res_ctx->pipe_ctx[pipe_idx].stream == stream) { -+ found_pipe_idx = true; -+ mpcc_id = res_ctx->pipe_ctx[pipe_idx].plane_res.hubp->inst; -+ break; -+ } -+ } -+ } else -+ found_pipe_idx = true;/*for release pipe_idx is not required*/ -+ -+ if (found_pipe_idx) { -+ if (acquire && pool->funcs->acquire_post_bldn_3dlut) -+ ret = pool->funcs->acquire_post_bldn_3dlut(res_ctx, pool, mpcc_id, lut, shaper); -+ else if (!acquire && pool->funcs->release_post_bldn_3dlut) -+ ret = pool->funcs->release_post_bldn_3dlut(res_ctx, pool, lut, shaper); -+ } -+ } -+ return ret; -+} -+ -+ - static bool is_flip_pending_in_pipes(struct dc *dc, struct dc_state *context) - { - int i; -@@ -2606,7 +2645,7 @@ static enum surface_update_type check_update_surfaces_for_stream( - stream_update->integer_scaling_update) - su_flags->bits.scaling = 1; - -- if (stream_update->out_transfer_func) -+ if (stream_update->out_transfer_func || stream_update->lut3d_func) - su_flags->bits.out_tf = 1; - - if (stream_update->abm_level) -@@ -2955,6 +2994,14 @@ static void copy_stream_update_to_stream(struct dc *dc, - sizeof(struct dc_transfer_func_distributed_points)); - } - -+ if (update->func_shaper && -+ stream->func_shaper != update->func_shaper) -+ stream->func_shaper = update->func_shaper; -+ -+ if (update->lut3d_func && -+ stream->lut3d_func != update->lut3d_func) -+ stream->lut3d_func = update->lut3d_func; -+ - if (update->hdr_static_metadata) - stream->hdr_static_metadata = *update->hdr_static_metadata; - -diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h -index 4d93ca9c627b..2fd65f84dc5d 100644 ---- a/drivers/gpu/drm/amd/display/dc/dc.h -+++ b/drivers/gpu/drm/amd/display/dc/dc.h -@@ -1348,6 +1348,14 @@ bool dc_acquire_release_mpc_3dlut( - struct dc_3dlut **lut, - struct dc_transfer_func **shaper); - -+bool -+dc_acquire_release_mpc_3dlut_for_ctx(struct dc *dc, -+ bool acquire, -+ struct dc_state *state, -+ struct dc_stream_state *stream, -+ struct dc_3dlut **lut, -+ struct dc_transfer_func **shaper); -+ - void dc_resource_state_copy_construct( - const struct dc_state *src_ctx, - struct dc_state *dst_ctx); -diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c -index 7a00fe525dfb..efa6cee649d0 100644 ---- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c -+++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c -@@ -346,20 +346,37 @@ bool cm_helper_translate_curve_to_hw_format( - * segment is from 2^-10 to 2^1 - * There are less than 256 points, for optimization - */ -- seg_distr[0] = 3; -- seg_distr[1] = 4; -- seg_distr[2] = 4; -- seg_distr[3] = 4; -- seg_distr[4] = 4; -- seg_distr[5] = 4; -- seg_distr[6] = 4; -- seg_distr[7] = 4; -- seg_distr[8] = 4; -- seg_distr[9] = 4; -- seg_distr[10] = 1; -- -- region_start = -10; -- region_end = 1; -+ if (output_tf->tf == TRANSFER_FUNCTION_LINEAR) { -+ seg_distr[0] = 0; /* 2 */ -+ seg_distr[1] = 1; /* 4 */ -+ seg_distr[2] = 2; /* 4 */ -+ seg_distr[3] = 3; /* 8 */ -+ seg_distr[4] = 4; /* 16 */ -+ seg_distr[5] = 5; /* 32 */ -+ seg_distr[6] = 6; /* 64 */ -+ seg_distr[7] = 7; /* 128 */ -+ -+ region_start = -8; -+ region_end = 1; -+ } else { -+ seg_distr[0] = 3; /* 8 */ -+ seg_distr[1] = 4; /* 16 */ -+ seg_distr[2] = 4; -+ seg_distr[3] = 4; -+ seg_distr[4] = 4; -+ seg_distr[5] = 4; -+ seg_distr[6] = 4; -+ seg_distr[7] = 4; -+ seg_distr[8] = 4; -+ seg_distr[9] = 4; -+ seg_distr[10] = 1; /* 2 */ -+ /* total = 8*16 + 8 + 64 + 2 = */ -+ -+ region_start = -10; -+ region_end = 1; -+ } -+ -+ - } - - for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++) -@@ -372,16 +389,56 @@ bool cm_helper_translate_curve_to_hw_format( - - j = 0; - for (k = 0; k < (region_end - region_start); k++) { -- increment = NUMBER_SW_SEGMENTS / (1 << seg_distr[k]); -+ /* -+ * We're using an ugly-ish hack here. Our HW allows for -+ * 256 segments per region but SW_SEGMENTS is 16. -+ * SW_SEGMENTS has some undocumented relationship to -+ * the number of points in the tf_pts struct, which -+ * is 512, unlike what's suggested TRANSFER_FUNC_POINTS. -+ * -+ * In order to work past this dilemma we'll scale our -+ * increment by (1 << 4) and then do the inverse (1 >> 4) -+ * when accessing the elements in tf_pts. -+ * -+ * TODO: find a better way using SW_SEGMENTS and -+ * TRANSFER_FUNC_POINTS definitions -+ */ -+ increment = (NUMBER_SW_SEGMENTS << 4) / (1 << seg_distr[k]); - start_index = (region_start + k + MAX_LOW_POINT) * - NUMBER_SW_SEGMENTS; -- for (i = start_index; i < start_index + NUMBER_SW_SEGMENTS; -+ for (i = (start_index << 4); i < (start_index << 4) + (NUMBER_SW_SEGMENTS << 4); - i += increment) { -+ struct fixed31_32 in_plus_one, in; -+ struct fixed31_32 value, red_value, green_value, blue_value; -+ uint32_t t = i & 0xf; -+ - if (j == hw_points - 1) - break; -- rgb_resulted[j].red = output_tf->tf_pts.red[i]; -- rgb_resulted[j].green = output_tf->tf_pts.green[i]; -- rgb_resulted[j].blue = output_tf->tf_pts.blue[i]; -+ -+ in_plus_one = output_tf->tf_pts.red[(i >> 4) + 1]; -+ in = output_tf->tf_pts.red[i >> 4]; -+ value = dc_fixpt_sub(in_plus_one, in); -+ value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4); -+ value = dc_fixpt_add(in, value); -+ red_value = value; -+ -+ in_plus_one = output_tf->tf_pts.green[(i >> 4) + 1]; -+ in = output_tf->tf_pts.green[i >> 4]; -+ value = dc_fixpt_sub(in_plus_one, in); -+ value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4); -+ value = dc_fixpt_add(in, value); -+ green_value = value; -+ -+ in_plus_one = output_tf->tf_pts.blue[(i >> 4) + 1]; -+ in = output_tf->tf_pts.blue[i >> 4]; -+ value = dc_fixpt_sub(in_plus_one, in); -+ value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4); -+ value = dc_fixpt_add(in, value); -+ blue_value = value; -+ -+ rgb_resulted[j].red = red_value; -+ rgb_resulted[j].green = green_value; -+ rgb_resulted[j].blue = blue_value; - j++; - } - } -@@ -482,10 +539,18 @@ bool cm_helper_translate_curve_to_hw_format( - rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green); - rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue); - -+ - if (fixpoint == true) { -- rgb->delta_red_reg = dc_fixpt_clamp_u0d10(rgb->delta_red); -- rgb->delta_green_reg = dc_fixpt_clamp_u0d10(rgb->delta_green); -- rgb->delta_blue_reg = dc_fixpt_clamp_u0d10(rgb->delta_blue); -+ uint32_t red_clamp = dc_fixpt_clamp_u0d14(rgb->delta_red); -+ uint32_t green_clamp = dc_fixpt_clamp_u0d14(rgb->delta_green); -+ uint32_t blue_clamp = dc_fixpt_clamp_u0d14(rgb->delta_blue); -+ -+ if (red_clamp >> 10 || green_clamp >> 10 || blue_clamp >> 10) -+ DC_LOG_WARNING("Losing delta precision while programming shaper LUT."); -+ -+ rgb->delta_red_reg = red_clamp & 0x3ff; -+ rgb->delta_green_reg = green_clamp & 0x3ff; -+ rgb->delta_blue_reg = blue_clamp & 0x3ff; - rgb->red_reg = dc_fixpt_clamp_u0d14(rgb->red); - rgb->green_reg = dc_fixpt_clamp_u0d14(rgb->green); - rgb->blue_reg = dc_fixpt_clamp_u0d14(rgb->blue); -diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c -index c38be3c6c234..aad9dfcad37b 100644 ---- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c -+++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c -@@ -1766,8 +1766,9 @@ static void dcn20_program_pipe( - hws->funcs.set_hdr_multiplier(pipe_ctx); - - if (pipe_ctx->update_flags.bits.enable || -- pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || -- pipe_ctx->plane_state->update_flags.bits.gamma_change) -+ pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || -+ pipe_ctx->plane_state->update_flags.bits.gamma_change || -+ pipe_ctx->plane_state->update_flags.bits.lut_3d) - hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); - - /* dcn10_translate_regamma_to_hw_format takes 750us to finish -diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c -index 32121db2851e..fd2428871c8a 100644 ---- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c -+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c -@@ -113,7 +113,6 @@ static bool dcn30_set_mpc_shaper_3dlut(struct pipe_ctx *pipe_ctx, - } - - if (stream->lut3d_func && -- stream->lut3d_func->state.bits.initialized == 1 && - stream->lut3d_func->state.bits.rmu_idx_valid == 1) { - if (stream->lut3d_func->state.bits.rmu_mux_num == 0) - mpcc_id_projected = stream->lut3d_func->state.bits.mpc_rmu0_mux; -@@ -131,8 +130,12 @@ static bool dcn30_set_mpc_shaper_3dlut(struct pipe_ctx *pipe_ctx, - if (acquired_rmu != stream->lut3d_func->state.bits.rmu_mux_num) - BREAK_TO_DEBUGGER(); - -- result = mpc->funcs->program_3dlut(mpc, &stream->lut3d_func->lut_3d, -- stream->lut3d_func->state.bits.rmu_mux_num); -+ if (stream->lut3d_func->state.bits.initialized == 1) -+ result = mpc->funcs->program_3dlut(mpc, &stream->lut3d_func->lut_3d, -+ stream->lut3d_func->state.bits.rmu_mux_num); -+ else -+ result = mpc->funcs->program_3dlut(mpc, NULL, -+ stream->lut3d_func->state.bits.rmu_mux_num); - result = mpc->funcs->program_shaper(mpc, shaper_lut, - stream->lut3d_func->state.bits.rmu_mux_num); - } else { -diff --git a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_resource.c -index 5ac2a272c380..a6d6fcaaca1c 100644 ---- a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_resource.c -+++ b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_resource.c -@@ -1258,6 +1258,30 @@ static struct display_stream_compressor *dcn301_dsc_create( - return &dsc->base; - } - -+static enum dc_status -+dcn301_remove_stream_from_ctx(struct dc *dc, -+ struct dc_state *new_ctx, -+ struct dc_stream_state *dc_stream) -+{ -+ struct dc_3dlut *lut3d_func; -+ struct dc_transfer_func *func_shaper; -+ -+ lut3d_func = (struct dc_3dlut *)dc_stream->lut3d_func; -+ func_shaper = (struct dc_transfer_func *)dc_stream->func_shaper; -+ -+ ASSERT((lut3d_func && func_shaper) || (!lut3d_func && !func_shaper)); -+ if (lut3d_func && func_shaper) -+ { -+ if (!dc_acquire_release_mpc_3dlut_for_ctx(dc, false, new_ctx, dc_stream, -+ &lut3d_func, &func_shaper)) -+ return DC_ERROR_UNEXPECTED; -+ } -+ -+ dc_stream->lut3d_func = lut3d_func; -+ dc_stream->func_shaper = func_shaper; -+ -+ return dcn20_remove_stream_from_ctx(dc, new_ctx, dc_stream); -+} - - static void dcn301_destroy_resource_pool(struct resource_pool **pool) - { -@@ -1406,7 +1430,7 @@ static struct resource_funcs dcn301_res_pool_funcs = { - .acquire_idle_pipe_for_layer = dcn20_acquire_idle_pipe_for_layer, - .add_stream_to_ctx = dcn30_add_stream_to_ctx, - .add_dsc_to_stream_resource = dcn20_add_dsc_to_stream_resource, -- .remove_stream_from_ctx = dcn20_remove_stream_from_ctx, -+ .remove_stream_from_ctx = dcn301_remove_stream_from_ctx, - .populate_dml_writeback_from_context = dcn30_populate_dml_writeback_from_context, - .set_mcif_arb_params = dcn30_set_mcif_arb_params, - .find_first_free_match_stream_enc_for_link = dcn10_find_first_free_match_stream_enc_for_link, -diff --git a/drivers/gpu/drm/amd/display/include/fixed31_32.h b/drivers/gpu/drm/amd/display/include/fixed31_32.h -index ece97ae0e826..f4cc7f97329f 100644 ---- a/drivers/gpu/drm/amd/display/include/fixed31_32.h -+++ b/drivers/gpu/drm/amd/display/include/fixed31_32.h -@@ -69,6 +69,18 @@ static const struct fixed31_32 dc_fixpt_epsilon = { 1LL }; - static const struct fixed31_32 dc_fixpt_half = { 0x80000000LL }; - static const struct fixed31_32 dc_fixpt_one = { 0x100000000LL }; - -+static inline struct fixed31_32 dc_fixpt_from_s3132(__u64 x) -+{ -+ struct fixed31_32 val; -+ -+ /* If negative, convert to 2's complement. */ -+ if (x & (1ULL << 63)) -+ x = -(x & ~(1ULL << 63)); -+ -+ val.value = x; -+ return val; -+} -+ - /* - * @brief - * Initialization routines -diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c -index dc01c43f6193..d72c22dcf685 100644 ---- a/drivers/gpu/drm/arm/malidp_crtc.c -+++ b/drivers/gpu/drm/arm/malidp_crtc.c -@@ -221,7 +221,7 @@ static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc, - - /* - * The size of the ctm is checked in -- * drm_atomic_replace_property_blob_from_id. -+ * drm_property_replace_blob_from_id. - */ - ctm = (struct drm_color_ctm *)state->ctm->data; - for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) { -diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c -index 88fcc6bbc8b7..956362f9d57c 100644 ---- a/drivers/gpu/drm/drm_atomic.c -+++ b/drivers/gpu/drm/drm_atomic.c -@@ -733,6 +733,7 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, - drm_get_color_encoding_name(state->color_encoding)); - drm_printf(p, "\tcolor-range=%s\n", - drm_get_color_range_name(state->color_range)); -+ drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); - - if (plane->funcs->atomic_print_state) - plane->funcs->atomic_print_state(p, state); -diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c -index 784e63d70a42..25bb0859fda7 100644 ---- a/drivers/gpu/drm/drm_atomic_state_helper.c -+++ b/drivers/gpu/drm/drm_atomic_state_helper.c -@@ -338,6 +338,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, - state->fence = NULL; - state->commit = NULL; - state->fb_damage_clips = NULL; -+ state->color_mgmt_changed = false; - } - EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); - -diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c -index d867e7f9f2cd..a6a9ee5086dd 100644 ---- a/drivers/gpu/drm/drm_atomic_uapi.c -+++ b/drivers/gpu/drm/drm_atomic_uapi.c -@@ -362,39 +362,6 @@ static s32 __user *get_out_fence_for_connector(struct drm_atomic_state *state, - return fence_ptr; - } - --static int --drm_atomic_replace_property_blob_from_id(struct drm_device *dev, -- struct drm_property_blob **blob, -- uint64_t blob_id, -- ssize_t expected_size, -- ssize_t expected_elem_size, -- bool *replaced) --{ -- struct drm_property_blob *new_blob = NULL; -- -- if (blob_id != 0) { -- new_blob = drm_property_lookup_blob(dev, blob_id); -- if (new_blob == NULL) -- return -EINVAL; -- -- if (expected_size > 0 && -- new_blob->length != expected_size) { -- drm_property_blob_put(new_blob); -- return -EINVAL; -- } -- if (expected_elem_size > 0 && -- new_blob->length % expected_elem_size != 0) { -- drm_property_blob_put(new_blob); -- return -EINVAL; -- } -- } -- -- *replaced |= drm_property_replace_blob(blob, new_blob); -- drm_property_blob_put(new_blob); -- -- return 0; --} -- - static int drm_atomic_crtc_set_property(struct drm_crtc *crtc, - struct drm_crtc_state *state, struct drm_property *property, - uint64_t val) -@@ -415,7 +382,7 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc, - } else if (property == config->prop_vrr_enabled) { - state->vrr_enabled = val; - } else if (property == config->degamma_lut_property) { -- ret = drm_atomic_replace_property_blob_from_id(dev, -+ ret = drm_property_replace_blob_from_id(dev, - &state->degamma_lut, - val, - -1, sizeof(struct drm_color_lut), -@@ -423,7 +390,7 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc, - state->color_mgmt_changed |= replaced; - return ret; - } else if (property == config->ctm_property) { -- ret = drm_atomic_replace_property_blob_from_id(dev, -+ ret = drm_property_replace_blob_from_id(dev, - &state->ctm, - val, - sizeof(struct drm_color_ctm), -1, -@@ -431,7 +398,7 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc, - state->color_mgmt_changed |= replaced; - return ret; - } else if (property == config->gamma_lut_property) { -- ret = drm_atomic_replace_property_blob_from_id(dev, -+ ret = drm_property_replace_blob_from_id(dev, - &state->gamma_lut, - val, - -1, sizeof(struct drm_color_lut), -@@ -563,7 +530,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, - } else if (property == plane->color_range_property) { - state->color_range = val; - } else if (property == config->prop_fb_damage_clips) { -- ret = drm_atomic_replace_property_blob_from_id(dev, -+ ret = drm_property_replace_blob_from_id(dev, - &state->fb_damage_clips, - val, - -1, -@@ -729,7 +696,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, - if (state->link_status != DRM_LINK_STATUS_GOOD) - state->link_status = val; - } else if (property == config->hdr_output_metadata_property) { -- ret = drm_atomic_replace_property_blob_from_id(dev, -+ ret = drm_property_replace_blob_from_id(dev, - &state->hdr_output_metadata, - val, - sizeof(struct hdr_output_metadata), -1, -diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c -index dfec479830e4..f72ef6493340 100644 ---- a/drivers/gpu/drm/drm_property.c -+++ b/drivers/gpu/drm/drm_property.c -@@ -751,6 +751,55 @@ bool drm_property_replace_blob(struct drm_property_blob **blob, - } - EXPORT_SYMBOL(drm_property_replace_blob); - -+/** -+ * drm_property_replace_blob_from_id - replace a blob property taking a reference -+ * @dev: DRM device -+ * @blob: a pointer to the member blob to be replaced -+ * @blob_id: the id of the new blob to replace with -+ * @expected_size: expected size of the blob property -+ * @expected_elem_size: expected size of an element in the blob property -+ * @replaced: if the blob was in fact replaced -+ * -+ * Look up the new blob from id, take its reference, check expected sizes of -+ * the blob and its element and replace the old blob by the new one. Advertise -+ * if the replacement operation was successful. -+ * -+ * Return: true if the blob was in fact replaced. -EINVAL if the new blob was -+ * not found or sizes don't match. -+ */ -+int drm_property_replace_blob_from_id(struct drm_device *dev, -+ struct drm_property_blob **blob, -+ uint64_t blob_id, -+ ssize_t expected_size, -+ ssize_t expected_elem_size, -+ bool *replaced) -+{ -+ struct drm_property_blob *new_blob = NULL; -+ -+ if (blob_id != 0) { -+ new_blob = drm_property_lookup_blob(dev, blob_id); -+ if (new_blob == NULL) -+ return -EINVAL; -+ -+ if (expected_size > 0 && -+ new_blob->length != expected_size) { -+ drm_property_blob_put(new_blob); -+ return -EINVAL; -+ } -+ if (expected_elem_size > 0 && -+ new_blob->length % expected_elem_size != 0) { -+ drm_property_blob_put(new_blob); -+ return -EINVAL; -+ } -+ } -+ -+ *replaced |= drm_property_replace_blob(blob, new_blob); -+ drm_property_blob_put(new_blob); -+ -+ return 0; -+} -+EXPORT_SYMBOL(drm_property_replace_blob_from_id); -+ - int drm_mode_getblob_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) - { -diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h -index 912f1e415685..08d7a7f0188f 100644 ---- a/include/drm/drm_mode_object.h -+++ b/include/drm/drm_mode_object.h -@@ -60,7 +60,7 @@ struct drm_mode_object { - void (*free_cb)(struct kref *kref); - }; - --#define DRM_OBJECT_MAX_PROPERTY 24 -+#define DRM_OBJECT_MAX_PROPERTY 64 - /** - * struct drm_object_properties - property tracking for &drm_mode_object - */ -diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h -index 51291983ea44..52c3287da0da 100644 ---- a/include/drm/drm_plane.h -+++ b/include/drm/drm_plane.h -@@ -237,6 +237,13 @@ struct drm_plane_state { - - /** @state: backpointer to global drm_atomic_state */ - struct drm_atomic_state *state; -+ -+ /** -+ * @color_mgmt_changed: Color management properties have changed. Used -+ * by the atomic helpers and drivers to steer the atomic commit control -+ * flow. -+ */ -+ bool color_mgmt_changed : 1; - }; - - static inline struct drm_rect -diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h -index 65bc9710a470..082f29156b3e 100644 ---- a/include/drm/drm_property.h -+++ b/include/drm/drm_property.h -@@ -279,6 +279,12 @@ struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, - const void *data); - struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, - uint32_t id); -+int drm_property_replace_blob_from_id(struct drm_device *dev, -+ struct drm_property_blob **blob, -+ uint64_t blob_id, -+ ssize_t expected_size, -+ ssize_t expected_elem_size, -+ bool *replaced); - int drm_property_replace_global_blob(struct drm_device *dev, - struct drm_property_blob **replace, - size_t length, --- -2.41.0 - -From 3f03c9100fee5841528bc4cc5cbabe55fd2cd48a Mon Sep 17 00:00:00 2001 -From: Peter Jung -Date: Wed, 19 Jul 2023 18:50:00 +0200 -Subject: [PATCH 5/8] ksm - -Signed-off-by: Peter Jung ---- - arch/alpha/kernel/syscalls/syscall.tbl | 3 + - arch/arm/tools/syscall.tbl | 3 + - arch/arm64/include/asm/unistd.h | 2 +- - arch/arm64/include/asm/unistd32.h | 6 + - arch/ia64/kernel/syscalls/syscall.tbl | 3 + - arch/m68k/kernel/syscalls/syscall.tbl | 3 + - arch/microblaze/kernel/syscalls/syscall.tbl | 3 + - arch/mips/kernel/syscalls/syscall_n32.tbl | 3 + - arch/mips/kernel/syscalls/syscall_n64.tbl | 3 + - arch/mips/kernel/syscalls/syscall_o32.tbl | 3 + - arch/parisc/kernel/syscalls/syscall.tbl | 3 + - arch/powerpc/kernel/syscalls/syscall.tbl | 3 + - arch/s390/kernel/syscalls/syscall.tbl | 3 + - arch/sh/kernel/syscalls/syscall.tbl | 3 + - arch/sparc/kernel/syscalls/syscall.tbl | 3 + - arch/x86/entry/syscalls/syscall_32.tbl | 3 + - arch/x86/entry/syscalls/syscall_64.tbl | 3 + - arch/xtensa/kernel/syscalls/syscall.tbl | 3 + - include/linux/syscalls.h | 3 + - include/uapi/asm-generic/unistd.h | 11 +- - kernel/sys.c | 147 ++++++++++++++++++++ - kernel/sys_ni.c | 3 + - 22 files changed, 218 insertions(+), 2 deletions(-) + Documentation/admin-guide/mm/ksm.rst | 25 ++- + arch/alpha/kernel/syscalls/syscall.tbl | 3 + + arch/arm/tools/syscall.tbl | 3 + + arch/arm64/include/asm/unistd.h | 2 +- + arch/arm64/include/asm/unistd32.h | 6 + + arch/ia64/kernel/syscalls/syscall.tbl | 3 + + arch/m68k/kernel/syscalls/syscall.tbl | 3 + + arch/microblaze/kernel/syscalls/syscall.tbl | 3 + + arch/mips/kernel/syscalls/syscall_n32.tbl | 3 + + arch/mips/kernel/syscalls/syscall_n64.tbl | 3 + + arch/mips/kernel/syscalls/syscall_o32.tbl | 3 + + arch/parisc/kernel/syscalls/syscall.tbl | 3 + + arch/powerpc/kernel/syscalls/syscall.tbl | 3 + + arch/s390/kernel/syscalls/syscall.tbl | 3 + + arch/sh/kernel/syscalls/syscall.tbl | 3 + + arch/sparc/kernel/syscalls/syscall.tbl | 3 + + arch/x86/entry/syscalls/syscall_32.tbl | 3 + + arch/x86/entry/syscalls/syscall_64.tbl | 3 + + arch/xtensa/kernel/syscalls/syscall.tbl | 3 + + fs/proc/base.c | 1 + + include/linux/ksm.h | 20 +++ + include/linux/mm_types.h | 9 +- + include/linux/syscalls.h | 3 + + include/uapi/asm-generic/unistd.h | 11 +- + kernel/sys.c | 147 ++++++++++++++++++ + kernel/sys_ni.c | 3 + + mm/khugepaged.c | 2 + + mm/ksm.c | 28 +++- + mm/memory.c | 5 +- + .../selftests/mm/ksm_functional_tests.c | 98 +++++++++++- + 30 files changed, 390 insertions(+), 18 deletions(-) +diff --git a/Documentation/admin-guide/mm/ksm.rst b/Documentation/admin-guide/mm/ksm.rst +index 7626392fe82c..5c5be7bd84b8 100644 +--- a/Documentation/admin-guide/mm/ksm.rst ++++ b/Documentation/admin-guide/mm/ksm.rst +@@ -173,6 +173,13 @@ stable_node_chains + the number of KSM pages that hit the ``max_page_sharing`` limit + stable_node_dups + number of duplicated KSM pages ++ksm_zero_pages ++ how many zero pages that are still mapped into processes were mapped by ++ KSM when deduplicating. ++ ++When ``use_zero_pages`` is/was enabled, the sum of ``pages_sharing`` + ++``ksm_zero_pages`` represents the actual number of pages saved by KSM. ++if ``use_zero_pages`` has never been enabled, ``ksm_zero_pages`` is 0. + + A high ratio of ``pages_sharing`` to ``pages_shared`` indicates good + sharing, but a high ratio of ``pages_unshared`` to ``pages_sharing`` +@@ -196,21 +203,25 @@ several times, which are unprofitable memory consumed. + 1) How to determine whether KSM save memory or consume memory in system-wide + range? Here is a simple approximate calculation for reference:: + +- general_profit =~ pages_sharing * sizeof(page) - (all_rmap_items) * ++ general_profit =~ ksm_saved_pages * sizeof(page) - (all_rmap_items) * + sizeof(rmap_item); + +- where all_rmap_items can be easily obtained by summing ``pages_sharing``, +- ``pages_shared``, ``pages_unshared`` and ``pages_volatile``. ++ where ksm_saved_pages equals to the sum of ``pages_sharing`` + ++ ``ksm_zero_pages`` of the system, and all_rmap_items can be easily ++ obtained by summing ``pages_sharing``, ``pages_shared``, ``pages_unshared`` ++ and ``pages_volatile``. + + 2) The KSM profit inner a single process can be similarly obtained by the + following approximate calculation:: + +- process_profit =~ ksm_merging_pages * sizeof(page) - ++ process_profit =~ ksm_saved_pages * sizeof(page) - + ksm_rmap_items * sizeof(rmap_item). + +- where ksm_merging_pages is shown under the directory ``/proc//``, +- and ksm_rmap_items is shown in ``/proc//ksm_stat``. The process profit +- is also shown in ``/proc//ksm_stat`` as ksm_process_profit. ++ where ksm_saved_pages equals to the sum of ``ksm_merging_pages`` and ++ ``ksm_zero_pages``, both of which are shown under the directory ++ ``/proc//ksm_stat``, and ksm_rmap_items is also shown in ++ ``/proc//ksm_stat``. The process profit is also shown in ++ ``/proc//ksm_stat`` as ksm_process_profit. + + From the perspective of application, a high ratio of ``ksm_rmap_items`` to + ``ksm_merging_pages`` means a bad madvise-applied policy, so developers or diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl -index 8ebacf37a8cf..9b8afad2ba08 100644 +index 1f13995d00d7..4a5bc2a91fa7 100644 --- a/arch/alpha/kernel/syscalls/syscall.tbl +++ b/arch/alpha/kernel/syscalls/syscall.tbl -@@ -490,3 +490,6 @@ - 558 common process_mrelease sys_process_mrelease +@@ -491,3 +491,6 @@ 559 common futex_waitv sys_futex_waitv 560 common set_mempolicy_home_node sys_ni_syscall -+561 common process_ksm_enable sys_process_ksm_enable -+562 common process_ksm_disable sys_process_ksm_disable -+563 common process_ksm_status sys_process_ksm_status + 561 common cachestat sys_cachestat ++562 common process_ksm_enable sys_process_ksm_enable ++563 common process_ksm_disable sys_process_ksm_disable ++564 common process_ksm_status sys_process_ksm_status diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl -index ac964612d8b0..b8a2f2689caa 100644 +index 8ebed8a13874..d616dcc060df 100644 --- a/arch/arm/tools/syscall.tbl +++ b/arch/arm/tools/syscall.tbl -@@ -464,3 +464,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -465,3 +465,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h -index 037feba03a51..6a28fb91b85d 100644 +index 64a514f90131..63a8a9c4abc1 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -39,7 +39,7 @@ #define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE + 5) #define __ARM_NR_COMPAT_END (__ARM_NR_COMPAT_BASE + 0x800) --#define __NR_compat_syscalls 451 -+#define __NR_compat_syscalls 454 +-#define __NR_compat_syscalls 452 ++#define __NR_compat_syscalls 455 #endif #define __ARCH_WANT_SYS_CLONE diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h -index 604a2053d006..72e380b95461 100644 +index d952a28463e0..c99c8260489b 100644 --- a/arch/arm64/include/asm/unistd32.h +++ b/arch/arm64/include/asm/unistd32.h -@@ -907,6 +907,12 @@ __SYSCALL(__NR_process_mrelease, sys_process_mrelease) - __SYSCALL(__NR_futex_waitv, sys_futex_waitv) - #define __NR_set_mempolicy_home_node 450 +@@ -909,6 +909,12 @@ __SYSCALL(__NR_futex_waitv, sys_futex_waitv) __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) -+#define __NR_process_ksm_enable 451 + #define __NR_cachestat 451 + __SYSCALL(__NR_cachestat, sys_cachestat) ++#define __NR_process_ksm_enable 452 +__SYSCALL(__NR_process_ksm_enable, sys_process_ksm_enable) -+#define __NR_process_ksm_disable 452 ++#define __NR_process_ksm_disable 453 +__SYSCALL(__NR_process_ksm_disable, sys_process_ksm_disable) -+#define __NR_process_ksm_status 453 ++#define __NR_process_ksm_status 454 +__SYSCALL(__NR_process_ksm_status, sys_process_ksm_status) /* * Please add new compat syscalls above this comment and update diff --git a/arch/ia64/kernel/syscalls/syscall.tbl b/arch/ia64/kernel/syscalls/syscall.tbl -index 72c929d9902b..aa698c590a1e 100644 +index f8c74ffeeefb..735157909c6f 100644 --- a/arch/ia64/kernel/syscalls/syscall.tbl +++ b/arch/ia64/kernel/syscalls/syscall.tbl -@@ -371,3 +371,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -372,3 +372,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl -index b1f3940bc298..4e9d10dfd079 100644 +index 4f504783371f..25b22d311f10 100644 --- a/arch/m68k/kernel/syscalls/syscall.tbl +++ b/arch/m68k/kernel/syscalls/syscall.tbl -@@ -450,3 +450,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -451,3 +451,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl -index 820145e47350..f89d989f9058 100644 +index 858d22bf275c..e548c182a33e 100644 --- a/arch/microblaze/kernel/syscalls/syscall.tbl +++ b/arch/microblaze/kernel/syscalls/syscall.tbl -@@ -456,3 +456,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -457,3 +457,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl -index 253ff994ed2e..fbd19a4195b9 100644 +index 1976317d4e8b..fed21167be44 100644 --- a/arch/mips/kernel/syscalls/syscall_n32.tbl +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl -@@ -389,3 +389,6 @@ - 448 n32 process_mrelease sys_process_mrelease +@@ -390,3 +390,6 @@ 449 n32 futex_waitv sys_futex_waitv 450 n32 set_mempolicy_home_node sys_set_mempolicy_home_node -+451 n32 process_ksm_enable sys_process_ksm_enable -+452 n32 process_ksm_disable sys_process_ksm_disable -+453 n32 process_ksm_status sys_process_ksm_status + 451 n32 cachestat sys_cachestat ++452 n32 process_ksm_enable sys_process_ksm_enable ++453 n32 process_ksm_disable sys_process_ksm_disable ++454 n32 process_ksm_status sys_process_ksm_status diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl -index 3f1886ad9d80..32694bd4a2fe 100644 +index cfda2511badf..b27ae871f676 100644 --- a/arch/mips/kernel/syscalls/syscall_n64.tbl +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl -@@ -365,3 +365,6 @@ - 448 n64 process_mrelease sys_process_mrelease +@@ -366,3 +366,6 @@ 449 n64 futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 n64 process_ksm_enable sys_process_ksm_enable -+452 n64 process_ksm_disable sys_process_ksm_disable -+453 n64 process_ksm_status sys_process_ksm_status + 451 n64 cachestat sys_cachestat ++452 n64 process_ksm_enable sys_process_ksm_enable ++453 n64 process_ksm_disable sys_process_ksm_disable ++454 n64 process_ksm_status sys_process_ksm_status diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl -index 8f243e35a7b2..6463565ed41b 100644 +index 7692234c3768..59f298413c29 100644 --- a/arch/mips/kernel/syscalls/syscall_o32.tbl +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl -@@ -438,3 +438,6 @@ - 448 o32 process_mrelease sys_process_mrelease +@@ -439,3 +439,6 @@ 449 o32 futex_waitv sys_futex_waitv 450 o32 set_mempolicy_home_node sys_set_mempolicy_home_node -+451 o32 process_ksm_enable sys_process_ksm_enable -+452 o32 process_ksm_disable sys_process_ksm_disable -+453 o32 process_ksm_status sys_process_ksm_status + 451 o32 cachestat sys_cachestat ++452 o32 process_ksm_enable sys_process_ksm_enable ++453 o32 process_ksm_disable sys_process_ksm_disable ++454 o32 process_ksm_status sys_process_ksm_status diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl -index 0e42fceb2d5e..7862be4cea70 100644 +index a0a9145b6dd4..494b59d1185f 100644 --- a/arch/parisc/kernel/syscalls/syscall.tbl +++ b/arch/parisc/kernel/syscalls/syscall.tbl -@@ -448,3 +448,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -450,3 +450,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl -index a0be127475b1..8d986c713b51 100644 +index 8c0b08b7a80e..499d7b233a43 100644 --- a/arch/powerpc/kernel/syscalls/syscall.tbl +++ b/arch/powerpc/kernel/syscalls/syscall.tbl -@@ -537,3 +537,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -538,3 +538,6 @@ 449 common futex_waitv sys_futex_waitv 450 nospu set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl -index b68f47541169..388f208236d9 100644 +index a6935af2235c..97b36ce15155 100644 --- a/arch/s390/kernel/syscalls/syscall.tbl +++ b/arch/s390/kernel/syscalls/syscall.tbl -@@ -453,3 +453,6 @@ - 448 common process_mrelease sys_process_mrelease sys_process_mrelease +@@ -454,3 +454,6 @@ 449 common futex_waitv sys_futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status sys_process_ksm_status diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl -index 2de85c977f54..df431a526a78 100644 +index 97377e8c5025..bd3827e1fc8d 100644 --- a/arch/sh/kernel/syscalls/syscall.tbl +++ b/arch/sh/kernel/syscalls/syscall.tbl -@@ -453,3 +453,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -454,3 +454,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl -index 4398cc6fb68d..f757d9623ff3 100644 +index faa835f3c54a..c05e62a0ca02 100644 --- a/arch/sparc/kernel/syscalls/syscall.tbl +++ b/arch/sparc/kernel/syscalls/syscall.tbl -@@ -496,3 +496,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -497,3 +497,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl -index 320480a8db4f..f18baa583fb4 100644 +index bc0a3c941b35..c79bd2dd758d 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl -@@ -455,3 +455,6 @@ - 448 i386 process_mrelease sys_process_mrelease +@@ -456,3 +456,6 @@ 449 i386 futex_waitv sys_futex_waitv 450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node -+451 i386 process_ksm_enable sys_process_ksm_enable -+452 i386 process_ksm_disable sys_process_ksm_disable -+453 i386 process_ksm_status sys_process_ksm_status + 451 i386 cachestat sys_cachestat ++452 i386 process_ksm_enable sys_process_ksm_enable ++453 i386 process_ksm_disable sys_process_ksm_disable ++454 i386 process_ksm_status sys_process_ksm_status diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl -index c84d12608cd2..03187452c6ee 100644 +index 227538b0ce80..e146a70cc299 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl -@@ -372,6 +372,9 @@ - 448 common process_mrelease sys_process_mrelease +@@ -373,6 +373,9 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status # # Due to a historical design error, certain syscalls are numbered differently diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl -index 52c94ab5c205..4002ab783337 100644 +index 2b69c3c035b6..b7bf81a3ba13 100644 --- a/arch/xtensa/kernel/syscalls/syscall.tbl +++ b/arch/xtensa/kernel/syscalls/syscall.tbl -@@ -421,3 +421,6 @@ - 448 common process_mrelease sys_process_mrelease +@@ -422,3 +422,6 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node -+451 common process_ksm_enable sys_process_ksm_enable -+452 common process_ksm_disable sys_process_ksm_disable -+453 common process_ksm_status sys_process_ksm_status + 451 common cachestat sys_cachestat ++452 common process_ksm_enable sys_process_ksm_enable ++453 common process_ksm_disable sys_process_ksm_disable ++454 common process_ksm_status sys_process_ksm_status +diff --git a/fs/proc/base.c b/fs/proc/base.c +index 05452c3b9872..eb2e498e3b8d 100644 +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -3207,6 +3207,7 @@ static int proc_pid_ksm_stat(struct seq_file *m, struct pid_namespace *ns, + mm = get_task_mm(task); + if (mm) { + seq_printf(m, "ksm_rmap_items %lu\n", mm->ksm_rmap_items); ++ seq_printf(m, "ksm_zero_pages %lu\n", mm->ksm_zero_pages); + seq_printf(m, "ksm_merging_pages %lu\n", mm->ksm_merging_pages); + seq_printf(m, "ksm_process_profit %ld\n", ksm_process_profit(mm)); + mmput(mm); +diff --git a/include/linux/ksm.h b/include/linux/ksm.h +index 899a314bc487..c2dd786a30e1 100644 +--- a/include/linux/ksm.h ++++ b/include/linux/ksm.h +@@ -26,6 +26,22 @@ int ksm_disable(struct mm_struct *mm); + + int __ksm_enter(struct mm_struct *mm); + void __ksm_exit(struct mm_struct *mm); ++/* ++ * To identify zeropages that were mapped by KSM, we reuse the dirty bit ++ * in the PTE. If the PTE is dirty, the zeropage was mapped by KSM when ++ * deduplicating memory. ++ */ ++#define is_ksm_zero_pte(pte) (is_zero_pfn(pte_pfn(pte)) && pte_dirty(pte)) ++ ++extern unsigned long ksm_zero_pages; ++ ++static inline void ksm_might_unmap_zero_page(struct mm_struct *mm, pte_t pte) ++{ ++ if (is_ksm_zero_pte(pte)) { ++ ksm_zero_pages--; ++ mm->ksm_zero_pages--; ++ } ++} + + static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) + { +@@ -95,6 +111,10 @@ static inline void ksm_exit(struct mm_struct *mm) + { + } + ++static inline void ksm_might_unmap_zero_page(struct mm_struct *mm, pte_t pte) ++{ ++} ++ + #ifdef CONFIG_MEMORY_FAILURE + static inline void collect_procs_ksm(struct page *page, + struct list_head *to_kill, int force_early) +diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h +index de10fc797c8e..1cc93fc7d9b5 100644 +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -784,7 +784,7 @@ struct mm_struct { + #ifdef CONFIG_KSM + /* + * Represent how many pages of this process are involved in KSM +- * merging. ++ * merging (not including ksm_zero_pages). + */ + unsigned long ksm_merging_pages; + /* +@@ -792,7 +792,12 @@ struct mm_struct { + * including merged and not merged. + */ + unsigned long ksm_rmap_items; +-#endif ++ /* ++ * Represent how many empty pages are merged with kernel zero ++ * pages when enabling KSM use_zero_pages. ++ */ ++ unsigned long ksm_zero_pages; ++#endif /* CONFIG_KSM */ + #ifdef CONFIG_LRU_GEN + struct { + /* this mm_struct is on lru_gen_mm_list */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h -index 33a0ee3bcb2e..9f5c1a070767 100644 +index 03e3d0121d5e..16597dea90f4 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h -@@ -919,6 +919,9 @@ asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior); +@@ -813,6 +813,9 @@ asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior); asmlinkage long sys_process_madvise(int pidfd, const struct iovec __user *vec, size_t vlen, int behavior, unsigned int flags); asmlinkage long sys_process_mrelease(int pidfd, unsigned int flags); @@ -13744,33 +12587,33 @@ index 33a0ee3bcb2e..9f5c1a070767 100644 unsigned long prot, unsigned long pgoff, unsigned long flags); diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h -index 45fa180cc56a..98731e18ad84 100644 +index fd6c1cb585db..11d0fc82c437 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h -@@ -886,8 +886,17 @@ __SYSCALL(__NR_futex_waitv, sys_futex_waitv) - #define __NR_set_mempolicy_home_node 450 - __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) +@@ -820,8 +820,17 @@ __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) + #define __NR_cachestat 451 + __SYSCALL(__NR_cachestat, sys_cachestat) -+#define __NR_process_ksm_enable 451 ++#define __NR_process_ksm_enable 452 +__SYSCALL(__NR_process_ksm_enable, sys_process_ksm_enable) + -+#define __NR_process_ksm_disable 452 ++#define __NR_process_ksm_disable 453 +__SYSCALL(__NR_process_ksm_disable, sys_process_ksm_disable) + -+#define __NR_process_ksm_status 453 ++#define __NR_process_ksm_status 454 +__SYSCALL(__NR_process_ksm_status, sys_process_ksm_status) + #undef __NR_syscalls --#define __NR_syscalls 451 -+#define __NR_syscalls 454 +-#define __NR_syscalls 452 ++#define __NR_syscalls 455 /* * 32 bit systems traditionally used different diff --git a/kernel/sys.c b/kernel/sys.c -index 339fee3eff6a..6af3987e735c 100644 +index 05f838929e72..9df683365a37 100644 --- a/kernel/sys.c +++ b/kernel/sys.c -@@ -2715,6 +2715,153 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, +@@ -2727,6 +2727,153 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, return error; } @@ -13925,10 +12768,10 @@ index 339fee3eff6a..6af3987e735c 100644 struct getcpu_cache __user *, unused) { diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c -index 860b2dcf3ac4..96fe36a6d0f5 100644 +index 781de7cc6a4e..49a35d35d0f9 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c -@@ -292,6 +292,9 @@ COND_SYSCALL(mincore); +@@ -184,6 +184,9 @@ COND_SYSCALL(mincore); COND_SYSCALL(madvise); COND_SYSCALL(process_madvise); COND_SYSCALL(process_mrelease); @@ -13938,1495 +12781,315 @@ index 860b2dcf3ac4..96fe36a6d0f5 100644 COND_SYSCALL(remap_file_pages); COND_SYSCALL(mbind); COND_SYSCALL(get_mempolicy); --- -2.41.0 - -From fa79cac5b93e4f85435d80b748f1a1049e23a938 Mon Sep 17 00:00:00 2001 -From: Peter Jung -Date: Wed, 19 Jul 2023 18:50:17 +0200 -Subject: [PATCH 6/8] kvm-lru - -Signed-off-by: Peter Jung ---- - Documentation/admin-guide/mm/multigen_lru.rst | 6 +- - arch/arm64/include/asm/kvm_host.h | 6 + - arch/arm64/include/asm/kvm_pgtable.h | 55 +++++++ - arch/arm64/kvm/arm.c | 1 + - arch/arm64/kvm/hyp/pgtable.c | 61 +------- - arch/arm64/kvm/mmu.c | 53 ++++++- - arch/powerpc/include/asm/kvm_host.h | 8 + - arch/powerpc/include/asm/kvm_ppc.h | 1 + - arch/powerpc/kvm/book3s.c | 6 + - arch/powerpc/kvm/book3s.h | 1 + - arch/powerpc/kvm/book3s_64_mmu_radix.c | 65 +++++++- - arch/powerpc/kvm/book3s_hv.c | 5 + - arch/x86/include/asm/kvm_host.h | 13 ++ - arch/x86/kvm/mmu.h | 6 - - arch/x86/kvm/mmu/spte.h | 1 - - arch/x86/kvm/mmu/tdp_mmu.c | 34 +++++ - include/linux/kvm_host.h | 22 +++ - include/linux/mmu_notifier.h | 79 ++++++---- - include/linux/mmzone.h | 6 +- - include/trace/events/kvm.h | 15 -- - mm/mmu_notifier.c | 48 ++---- - mm/rmap.c | 8 +- - mm/vmscan.c | 139 ++++++++++++++++-- - virt/kvm/kvm_main.c | 115 +++++++++------ - 24 files changed, 546 insertions(+), 208 deletions(-) - -diff --git a/Documentation/admin-guide/mm/multigen_lru.rst b/Documentation/admin-guide/mm/multigen_lru.rst -index 33e068830497..0ae2a6d4d94c 100644 ---- a/Documentation/admin-guide/mm/multigen_lru.rst -+++ b/Documentation/admin-guide/mm/multigen_lru.rst -@@ -48,6 +48,10 @@ Values Components - verified on x86 varieties other than Intel and AMD. If it is - disabled, the multi-gen LRU will suffer a negligible - performance degradation. -+0x0008 Clearing the accessed bit in KVM page table entries in large -+ batches, when KVM MMU sets it (e.g., on x86_64). This can -+ improve the performance of guests when the host is under memory -+ pressure. - [yYnN] Apply to all the components above. - ====== =============================================================== - -@@ -56,7 +60,7 @@ E.g., - - echo y >/sys/kernel/mm/lru_gen/enabled - cat /sys/kernel/mm/lru_gen/enabled -- 0x0007 -+ 0x000f - echo 5 >/sys/kernel/mm/lru_gen/enabled - cat /sys/kernel/mm/lru_gen/enabled - 0x0005 -diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h -index 9787503ff43f..598e927cb233 100644 ---- a/arch/arm64/include/asm/kvm_host.h -+++ b/arch/arm64/include/asm/kvm_host.h -@@ -1120,4 +1120,10 @@ static inline void kvm_hyp_reserve(void) { } - void kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu); - bool kvm_arm_vcpu_stopped(struct kvm_vcpu *vcpu); - -+#define kvm_arch_has_test_clear_young kvm_arch_has_test_clear_young -+static inline bool kvm_arch_has_test_clear_young(void) -+{ -+ return cpu_has_hw_af() && !is_protected_kvm_enabled(); -+} -+ - #endif /* __ARM64_KVM_HOST_H__ */ -diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h -index 93bd0975b15f..ad7cae0d8f3a 100644 ---- a/arch/arm64/include/asm/kvm_pgtable.h -+++ b/arch/arm64/include/asm/kvm_pgtable.h -@@ -44,6 +44,49 @@ typedef u64 kvm_pte_t; - - #define KVM_PHYS_INVALID (-1ULL) - -+#define KVM_PTE_TYPE BIT(1) -+#define KVM_PTE_TYPE_BLOCK 0 -+#define KVM_PTE_TYPE_PAGE 1 -+#define KVM_PTE_TYPE_TABLE 1 -+ -+#define KVM_PTE_LEAF_ATTR_LO GENMASK(11, 2) -+ -+#define KVM_PTE_LEAF_ATTR_LO_S1_ATTRIDX GENMASK(4, 2) -+#define KVM_PTE_LEAF_ATTR_LO_S1_AP GENMASK(7, 6) -+#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RO 3 -+#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RW 1 -+#define KVM_PTE_LEAF_ATTR_LO_S1_SH GENMASK(9, 8) -+#define KVM_PTE_LEAF_ATTR_LO_S1_SH_IS 3 -+#define KVM_PTE_LEAF_ATTR_LO_S1_AF BIT(10) -+ -+#define KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR GENMASK(5, 2) -+#define KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R BIT(6) -+#define KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W BIT(7) -+#define KVM_PTE_LEAF_ATTR_LO_S2_SH GENMASK(9, 8) -+#define KVM_PTE_LEAF_ATTR_LO_S2_SH_IS 3 -+#define KVM_PTE_LEAF_ATTR_LO_S2_AF BIT(10) -+ -+#define KVM_PTE_LEAF_ATTR_HI GENMASK(63, 51) -+ -+#define KVM_PTE_LEAF_ATTR_HI_SW GENMASK(58, 55) -+ -+#define KVM_PTE_LEAF_ATTR_HI_S1_XN BIT(54) -+ -+#define KVM_PTE_LEAF_ATTR_HI_S2_XN BIT(54) -+ -+#define KVM_PTE_LEAF_ATTR_S2_PERMS (KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R | \ -+ KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W | \ -+ KVM_PTE_LEAF_ATTR_HI_S2_XN) -+ -+#define KVM_INVALID_PTE_OWNER_MASK GENMASK(9, 2) -+#define KVM_MAX_OWNER_ID 1 -+ -+/* -+ * Used to indicate a pte for which a 'break-before-make' sequence is in -+ * progress. -+ */ -+#define KVM_INVALID_PTE_LOCKED BIT(10) -+ - static inline bool kvm_pte_valid(kvm_pte_t pte) - { - return pte & KVM_PTE_VALID; -@@ -110,6 +153,7 @@ static inline bool kvm_level_supports_block_mapping(u32 level) - * @put_page: Decrement the refcount on a page. When the - * refcount reaches 0 the page is automatically - * freed. -+ * @put_page_rcu: RCU variant of the above. - * @page_count: Return the refcount of a page. - * @phys_to_virt: Convert a physical address into a virtual - * address mapped in the current context. -@@ -127,6 +171,7 @@ struct kvm_pgtable_mm_ops { - void (*free_removed_table)(void *addr, u32 level); - void (*get_page)(void *addr); - void (*put_page)(void *addr); -+ void (*put_page_rcu)(void *addr); - int (*page_count)(void *addr); - void* (*phys_to_virt)(phys_addr_t phys); - phys_addr_t (*virt_to_phys)(void *addr); -@@ -224,6 +269,16 @@ static inline bool kvm_pgtable_walk_shared(const struct kvm_pgtable_visit_ctx *c - return ctx->flags & KVM_PGTABLE_WALK_SHARED; - } - -+static inline bool stage2_try_set_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_t new) -+{ -+ if (!kvm_pgtable_walk_shared(ctx)) { -+ WRITE_ONCE(*ctx->ptep, new); -+ return true; -+ } -+ -+ return cmpxchg(ctx->ptep, ctx->old, new) == ctx->old; -+} -+ - /** - * struct kvm_pgtable_walker - Hook into a page-table walk. - * @cb: Callback function to invoke during the walk. -diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c -index 14391826241c..ee93271035d9 100644 ---- a/arch/arm64/kvm/arm.c -+++ b/arch/arm64/kvm/arm.c -@@ -191,6 +191,7 @@ vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) - */ - void kvm_arch_destroy_vm(struct kvm *kvm) - { -+ kvm_free_stage2_pgd(&kvm->arch.mmu); - bitmap_free(kvm->arch.pmu_filter); - free_cpumask_var(kvm->arch.supported_cpus); - -diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c -index 37bd64e912ca..0ee3d5b49f27 100644 ---- a/arch/arm64/kvm/hyp/pgtable.c -+++ b/arch/arm64/kvm/hyp/pgtable.c -@@ -12,49 +12,6 @@ - #include - - --#define KVM_PTE_TYPE BIT(1) --#define KVM_PTE_TYPE_BLOCK 0 --#define KVM_PTE_TYPE_PAGE 1 --#define KVM_PTE_TYPE_TABLE 1 -- --#define KVM_PTE_LEAF_ATTR_LO GENMASK(11, 2) -- --#define KVM_PTE_LEAF_ATTR_LO_S1_ATTRIDX GENMASK(4, 2) --#define KVM_PTE_LEAF_ATTR_LO_S1_AP GENMASK(7, 6) --#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RO 3 --#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RW 1 --#define KVM_PTE_LEAF_ATTR_LO_S1_SH GENMASK(9, 8) --#define KVM_PTE_LEAF_ATTR_LO_S1_SH_IS 3 --#define KVM_PTE_LEAF_ATTR_LO_S1_AF BIT(10) -- --#define KVM_PTE_LEAF_ATTR_LO_S2_MEMATTR GENMASK(5, 2) --#define KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R BIT(6) --#define KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W BIT(7) --#define KVM_PTE_LEAF_ATTR_LO_S2_SH GENMASK(9, 8) --#define KVM_PTE_LEAF_ATTR_LO_S2_SH_IS 3 --#define KVM_PTE_LEAF_ATTR_LO_S2_AF BIT(10) -- --#define KVM_PTE_LEAF_ATTR_HI GENMASK(63, 51) -- --#define KVM_PTE_LEAF_ATTR_HI_SW GENMASK(58, 55) -- --#define KVM_PTE_LEAF_ATTR_HI_S1_XN BIT(54) -- --#define KVM_PTE_LEAF_ATTR_HI_S2_XN BIT(54) -- --#define KVM_PTE_LEAF_ATTR_S2_PERMS (KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R | \ -- KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W | \ -- KVM_PTE_LEAF_ATTR_HI_S2_XN) -- --#define KVM_INVALID_PTE_OWNER_MASK GENMASK(9, 2) --#define KVM_MAX_OWNER_ID 1 -- --/* -- * Used to indicate a pte for which a 'break-before-make' sequence is in -- * progress. -- */ --#define KVM_INVALID_PTE_LOCKED BIT(10) -- - struct kvm_pgtable_walk_data { - struct kvm_pgtable_walker *walker; - -@@ -722,16 +679,6 @@ static bool stage2_pte_is_locked(kvm_pte_t pte) - return !kvm_pte_valid(pte) && (pte & KVM_INVALID_PTE_LOCKED); - } - --static bool stage2_try_set_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_t new) --{ -- if (!kvm_pgtable_walk_shared(ctx)) { -- WRITE_ONCE(*ctx->ptep, new); -- return true; -- } -- -- return cmpxchg(ctx->ptep, ctx->old, new) == ctx->old; --} -- - /** - * stage2_try_break_pte() - Invalidates a pte according to the - * 'break-before-make' requirements of the -@@ -1061,8 +1008,12 @@ static int stage2_unmap_walker(const struct kvm_pgtable_visit_ctx *ctx, - mm_ops->dcache_clean_inval_poc(kvm_pte_follow(ctx->old, mm_ops), - kvm_granule_size(ctx->level)); - -- if (childp) -- mm_ops->put_page(childp); -+ if (childp) { -+ if (mm_ops->put_page_rcu) -+ mm_ops->put_page_rcu(childp); -+ else -+ mm_ops->put_page(childp); -+ } - - return 0; - } -diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c -index 3b9d4d24c361..26a8d955b49c 100644 ---- a/arch/arm64/kvm/mmu.c -+++ b/arch/arm64/kvm/mmu.c -@@ -172,6 +172,21 @@ static int kvm_host_page_count(void *addr) - return page_count(virt_to_page(addr)); - } - -+static void kvm_s2_rcu_put_page(struct rcu_head *head) -+{ -+ put_page(container_of(head, struct page, rcu_head)); -+} -+ -+static void kvm_s2_put_page_rcu(void *addr) -+{ -+ struct page *page = virt_to_page(addr); -+ -+ if (kvm_host_page_count(addr) == 1) -+ kvm_account_pgtable_pages(addr, -1); -+ -+ call_rcu(&page->rcu_head, kvm_s2_rcu_put_page); -+} -+ - static phys_addr_t kvm_host_pa(void *addr) - { - return __pa(addr); -@@ -704,6 +719,7 @@ static struct kvm_pgtable_mm_ops kvm_s2_mm_ops = { - .free_removed_table = stage2_free_removed_table, - .get_page = kvm_host_get_page, - .put_page = kvm_s2_put_page, -+ .put_page_rcu = kvm_s2_put_page_rcu, - .page_count = kvm_host_page_count, - .phys_to_virt = kvm_host_va, - .virt_to_phys = kvm_host_pa, -@@ -1662,6 +1678,42 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) - range->start << PAGE_SHIFT); - } - -+static int stage2_test_clear_young(const struct kvm_pgtable_visit_ctx *ctx, -+ enum kvm_pgtable_walk_flags flags) -+{ -+ kvm_pte_t new = ctx->old & ~KVM_PTE_LEAF_ATTR_LO_S2_AF; -+ -+ VM_WARN_ON_ONCE(!page_count(virt_to_page(ctx->ptep))); -+ -+ if (!kvm_pte_valid(new)) -+ return 0; -+ -+ if (new == ctx->old) -+ return 0; -+ -+ if (kvm_should_clear_young(ctx->arg, ctx->addr / PAGE_SIZE)) -+ stage2_try_set_pte(ctx, new); -+ -+ return 0; -+} -+ -+bool kvm_arch_test_clear_young(struct kvm *kvm, struct kvm_gfn_range *range) -+{ -+ u64 start = range->start * PAGE_SIZE; -+ u64 end = range->end * PAGE_SIZE; -+ struct kvm_pgtable_walker walker = { -+ .cb = stage2_test_clear_young, -+ .arg = range, -+ .flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_SHARED, -+ }; -+ -+ BUILD_BUG_ON(is_hyp_code()); -+ -+ kvm_pgtable_walk(kvm->arch.mmu.pgt, start, end - start, &walker); -+ -+ return false; -+} -+ - phys_addr_t kvm_mmu_get_httbr(void) - { - return __pa(hyp_pgtable->pgd); -@@ -1877,7 +1929,6 @@ void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) - - void kvm_arch_flush_shadow_all(struct kvm *kvm) - { -- kvm_free_stage2_pgd(&kvm->arch.mmu); - } - - void kvm_arch_flush_shadow_memslot(struct kvm *kvm, -diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h -index 14ee0dece853..75c260ea8a9e 100644 ---- a/arch/powerpc/include/asm/kvm_host.h -+++ b/arch/powerpc/include/asm/kvm_host.h -@@ -883,4 +883,12 @@ static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {} - static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {} - static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {} - -+#define kvm_arch_has_test_clear_young kvm_arch_has_test_clear_young -+static inline bool kvm_arch_has_test_clear_young(void) -+{ -+ return IS_ENABLED(CONFIG_KVM_BOOK3S_HV_POSSIBLE) && -+ cpu_has_feature(CPU_FTR_HVMODE) && cpu_has_feature(CPU_FTR_ARCH_300) && -+ radix_enabled(); -+} -+ - #endif /* __POWERPC_KVM_HOST_H__ */ -diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h -index 79a9c0bb8bba..ff1af6a7b44f 100644 ---- a/arch/powerpc/include/asm/kvm_ppc.h -+++ b/arch/powerpc/include/asm/kvm_ppc.h -@@ -287,6 +287,7 @@ struct kvmppc_ops { - bool (*unmap_gfn_range)(struct kvm *kvm, struct kvm_gfn_range *range); - bool (*age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range); - bool (*test_age_gfn)(struct kvm *kvm, struct kvm_gfn_range *range); -+ bool (*test_clear_young)(struct kvm *kvm, struct kvm_gfn_range *range); - bool (*set_spte_gfn)(struct kvm *kvm, struct kvm_gfn_range *range); - void (*free_memslot)(struct kvm_memory_slot *slot); - int (*init_vm)(struct kvm *kvm); -diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c -index 686d8d9eda3e..37bf40b0c4ff 100644 ---- a/arch/powerpc/kvm/book3s.c -+++ b/arch/powerpc/kvm/book3s.c -@@ -899,6 +899,12 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) - return kvm->arch.kvm_ops->test_age_gfn(kvm, range); - } - -+bool kvm_arch_test_clear_young(struct kvm *kvm, struct kvm_gfn_range *range) -+{ -+ return !kvm->arch.kvm_ops->test_clear_young || -+ kvm->arch.kvm_ops->test_clear_young(kvm, range); -+} -+ - bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) - { - return kvm->arch.kvm_ops->set_spte_gfn(kvm, range); -diff --git a/arch/powerpc/kvm/book3s.h b/arch/powerpc/kvm/book3s.h -index 58391b4b32ed..fa2659e21ccc 100644 ---- a/arch/powerpc/kvm/book3s.h -+++ b/arch/powerpc/kvm/book3s.h -@@ -12,6 +12,7 @@ extern void kvmppc_core_flush_memslot_hv(struct kvm *kvm, - extern bool kvm_unmap_gfn_range_hv(struct kvm *kvm, struct kvm_gfn_range *range); - extern bool kvm_age_gfn_hv(struct kvm *kvm, struct kvm_gfn_range *range); - extern bool kvm_test_age_gfn_hv(struct kvm *kvm, struct kvm_gfn_range *range); -+extern bool kvm_test_clear_young_hv(struct kvm *kvm, struct kvm_gfn_range *range); - extern bool kvm_set_spte_gfn_hv(struct kvm *kvm, struct kvm_gfn_range *range); - - extern int kvmppc_mmu_init_pr(struct kvm_vcpu *vcpu); -diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c -index 461307b89c3a..0a392e9a100a 100644 ---- a/arch/powerpc/kvm/book3s_64_mmu_radix.c -+++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c -@@ -1088,6 +1088,65 @@ bool kvm_test_age_radix(struct kvm *kvm, struct kvm_memory_slot *memslot, - return ref; - } - -+bool kvm_test_clear_young_hv(struct kvm *kvm, struct kvm_gfn_range *range) -+{ -+ bool err; -+ gfn_t gfn = range->start; -+ -+ rcu_read_lock(); -+ -+ err = !kvm_is_radix(kvm); -+ if (err) -+ goto unlock; -+ -+ /* -+ * Case 1: This function kvmppc_switch_mmu_to_hpt() -+ * -+ * rcu_read_lock() -+ * Test kvm_is_radix() kvm->arch.radix = 0 -+ * Use kvm->arch.pgtable synchronize_rcu() -+ * rcu_read_unlock() -+ * kvmppc_free_radix() -+ * -+ * -+ * Case 2: This function kvmppc_switch_mmu_to_radix() -+ * -+ * kvmppc_init_vm_radix() -+ * smp_wmb() -+ * Test kvm_is_radix() kvm->arch.radix = 1 -+ * smp_rmb() -+ * Use kvm->arch.pgtable -+ */ -+ smp_rmb(); -+ -+ while (gfn < range->end) { -+ pte_t *ptep; -+ pte_t old, new; -+ unsigned int shift; -+ -+ ptep = find_kvm_secondary_pte_unlocked(kvm, gfn * PAGE_SIZE, &shift); -+ if (!ptep) -+ goto next; -+ -+ VM_WARN_ON_ONCE(!page_count(virt_to_page(ptep))); -+ -+ old = READ_ONCE(*ptep); -+ if (!pte_present(old) || !pte_young(old)) -+ goto next; -+ -+ new = pte_mkold(old); -+ -+ if (kvm_should_clear_young(range, gfn)) -+ pte_xchg(ptep, old, new); -+next: -+ gfn += shift ? BIT(shift - PAGE_SHIFT) : 1; -+ } -+unlock: -+ rcu_read_unlock(); -+ -+ return err; -+} -+ - /* Returns the number of PAGE_SIZE pages that are dirty */ - static int kvm_radix_test_clear_dirty(struct kvm *kvm, - struct kvm_memory_slot *memslot, int pagenum) -@@ -1469,13 +1528,15 @@ int kvmppc_radix_init(void) - { - unsigned long size = sizeof(void *) << RADIX_PTE_INDEX_SIZE; - -- kvm_pte_cache = kmem_cache_create("kvm-pte", size, size, 0, pte_ctor); -+ kvm_pte_cache = kmem_cache_create("kvm-pte", size, size, -+ SLAB_TYPESAFE_BY_RCU, pte_ctor); - if (!kvm_pte_cache) - return -ENOMEM; - - size = sizeof(void *) << RADIX_PMD_INDEX_SIZE; - -- kvm_pmd_cache = kmem_cache_create("kvm-pmd", size, size, 0, pmd_ctor); -+ kvm_pmd_cache = kmem_cache_create("kvm-pmd", size, size, -+ SLAB_TYPESAFE_BY_RCU, pmd_ctor); - if (!kvm_pmd_cache) { - kmem_cache_destroy(kvm_pte_cache); - return -ENOMEM; -diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c -index 130bafdb1430..20a81ec9fde8 100644 ---- a/arch/powerpc/kvm/book3s_hv.c -+++ b/arch/powerpc/kvm/book3s_hv.c -@@ -5262,6 +5262,8 @@ int kvmppc_switch_mmu_to_hpt(struct kvm *kvm) - spin_lock(&kvm->mmu_lock); - kvm->arch.radix = 0; - spin_unlock(&kvm->mmu_lock); -+ /* see the comments in kvm_test_clear_young_hv() */ -+ synchronize_rcu(); - kvmppc_free_radix(kvm); - - lpcr = LPCR_VPM1; -@@ -5286,6 +5288,8 @@ int kvmppc_switch_mmu_to_radix(struct kvm *kvm) - if (err) - return err; - kvmppc_rmap_reset(kvm); -+ /* see the comments in kvm_test_clear_young_hv() */ -+ smp_wmb(); - /* Mutual exclusion with kvm_unmap_gfn_range etc. */ - spin_lock(&kvm->mmu_lock); - kvm->arch.radix = 1; -@@ -6185,6 +6189,7 @@ static struct kvmppc_ops kvm_ops_hv = { - .unmap_gfn_range = kvm_unmap_gfn_range_hv, - .age_gfn = kvm_age_gfn_hv, - .test_age_gfn = kvm_test_age_gfn_hv, -+ .test_clear_young = kvm_test_clear_young_hv, - .set_spte_gfn = kvm_set_spte_gfn_hv, - .free_memslot = kvmppc_core_free_memslot_hv, - .init_vm = kvmppc_core_init_vm_hv, -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index fb9d1f2d6136..d6dfdebe3d94 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -1772,6 +1772,7 @@ struct kvm_arch_async_pf { - - extern u32 __read_mostly kvm_nr_uret_msrs; - extern u64 __read_mostly host_efer; -+extern u64 __read_mostly shadow_accessed_mask; - extern bool __read_mostly allow_smaller_maxphyaddr; - extern bool __read_mostly enable_apicv; - extern struct kvm_x86_ops kvm_x86_ops; -@@ -1855,6 +1856,11 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, - bool mask); - - extern bool tdp_enabled; -+#ifdef CONFIG_X86_64 -+extern bool tdp_mmu_enabled; -+#else -+#define tdp_mmu_enabled false -+#endif - - u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); - -@@ -2217,4 +2223,11 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages); - */ - #define KVM_EXIT_HYPERCALL_MBZ GENMASK_ULL(31, 1) - -+#define kvm_arch_has_test_clear_young kvm_arch_has_test_clear_young -+static inline bool kvm_arch_has_test_clear_young(void) -+{ -+ return IS_ENABLED(CONFIG_X86_64) && -+ (!IS_REACHABLE(CONFIG_KVM) || (tdp_mmu_enabled && shadow_accessed_mask)); -+} -+ - #endif /* _ASM_X86_KVM_HOST_H */ -diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h -index 92d5a1924fc1..84aedb2671ef 100644 ---- a/arch/x86/kvm/mmu.h -+++ b/arch/x86/kvm/mmu.h -@@ -253,12 +253,6 @@ static inline bool kvm_shadow_root_allocated(struct kvm *kvm) - return smp_load_acquire(&kvm->arch.shadow_root_allocated); - } - --#ifdef CONFIG_X86_64 --extern bool tdp_mmu_enabled; --#else --#define tdp_mmu_enabled false --#endif -- - static inline bool kvm_memslots_have_rmaps(struct kvm *kvm) - { - return !tdp_mmu_enabled || kvm_shadow_root_allocated(kvm); -diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h -index 1279db2eab44..a82c4fa1c47b 100644 ---- a/arch/x86/kvm/mmu/spte.h -+++ b/arch/x86/kvm/mmu/spte.h -@@ -153,7 +153,6 @@ extern u64 __read_mostly shadow_mmu_writable_mask; - extern u64 __read_mostly shadow_nx_mask; - extern u64 __read_mostly shadow_x_mask; /* mutual exclusive with nx_mask */ - extern u64 __read_mostly shadow_user_mask; --extern u64 __read_mostly shadow_accessed_mask; - extern u64 __read_mostly shadow_dirty_mask; - extern u64 __read_mostly shadow_mmio_value; - extern u64 __read_mostly shadow_mmio_mask; -diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c -index 08340219c35a..6875a819e007 100644 ---- a/arch/x86/kvm/mmu/tdp_mmu.c -+++ b/arch/x86/kvm/mmu/tdp_mmu.c -@@ -1232,6 +1232,40 @@ bool kvm_tdp_mmu_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) - return kvm_tdp_mmu_handle_gfn(kvm, range, test_age_gfn); - } - -+bool kvm_arch_test_clear_young(struct kvm *kvm, struct kvm_gfn_range *range) -+{ -+ struct kvm_mmu_page *root; -+ int offset = ffs(shadow_accessed_mask) - 1; -+ -+ if (kvm_shadow_root_allocated(kvm)) -+ return true; -+ -+ rcu_read_lock(); -+ -+ list_for_each_entry_rcu(root, &kvm->arch.tdp_mmu_roots, link) { -+ struct tdp_iter iter; -+ -+ if (kvm_mmu_page_as_id(root) != range->slot->as_id) -+ continue; -+ -+ tdp_root_for_each_leaf_pte(iter, root, range->start, range->end) { -+ u64 *sptep = rcu_dereference(iter.sptep); -+ -+ VM_WARN_ON_ONCE(!page_count(virt_to_page(sptep))); -+ -+ if (!(iter.old_spte & shadow_accessed_mask)) -+ continue; -+ -+ if (kvm_should_clear_young(range, iter.gfn)) -+ clear_bit(offset, (unsigned long *)sptep); -+ } -+ } -+ -+ rcu_read_unlock(); -+ -+ return false; -+} -+ - static bool set_spte_gfn(struct kvm *kvm, struct tdp_iter *iter, - struct kvm_gfn_range *range) - { -diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h -index 0e571e973bc2..374262545f96 100644 ---- a/include/linux/kvm_host.h -+++ b/include/linux/kvm_host.h -@@ -258,6 +258,7 @@ int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu); - #ifdef KVM_ARCH_WANT_MMU_NOTIFIER - struct kvm_gfn_range { - struct kvm_memory_slot *slot; -+ void *args; - gfn_t start; - gfn_t end; - pte_t pte; -@@ -267,6 +268,27 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range); - bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range); - bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range); - bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range); -+bool kvm_should_clear_young(struct kvm_gfn_range *range, gfn_t gfn); -+bool kvm_arch_test_clear_young(struct kvm *kvm, struct kvm_gfn_range *range); -+#endif -+ -+/* -+ * Architectures that implement kvm_arch_test_clear_young() should override -+ * kvm_arch_has_test_clear_young(). -+ * -+ * kvm_arch_has_test_clear_young() is allowed to return false positive, i.e., it -+ * can return true if kvm_arch_test_clear_young() is supported but disabled due -+ * to some runtime constraint. In this case, kvm_arch_test_clear_young() should -+ * return true; otherwise, it should return false. -+ * -+ * For each young KVM PTE, kvm_arch_test_clear_young() should call -+ * kvm_should_clear_young() to decide whether to clear the accessed bit. -+ */ -+#ifndef kvm_arch_has_test_clear_young -+static inline bool kvm_arch_has_test_clear_young(void) -+{ -+ return false; -+} - #endif - - enum { -diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h -index 64a3e051c3c4..748e5bddb05f 100644 ---- a/include/linux/mmu_notifier.h -+++ b/include/linux/mmu_notifier.h -@@ -60,6 +60,8 @@ enum mmu_notifier_event { - }; - - #define MMU_NOTIFIER_RANGE_BLOCKABLE (1 << 0) -+#define MMU_NOTIFIER_RANGE_LOCKLESS (1 << 1) -+#define MMU_NOTIFIER_RANGE_YOUNG (1 << 2) - - struct mmu_notifier_ops { - /* -@@ -102,25 +104,9 @@ struct mmu_notifier_ops { - unsigned long start, - unsigned long end); - -- /* -- * clear_young is a lightweight version of clear_flush_young. Like the -- * latter, it is supposed to test-and-clear the young/accessed bitflag -- * in the secondary pte, but it may omit flushing the secondary tlb. -- */ -- int (*clear_young)(struct mmu_notifier *subscription, -- struct mm_struct *mm, -- unsigned long start, -- unsigned long end); -- -- /* -- * test_young is called to check the young/accessed bitflag in -- * the secondary pte. This is used to know if the page is -- * frequently used without actually clearing the flag or tearing -- * down the secondary mapping on the page. -- */ -- int (*test_young)(struct mmu_notifier *subscription, -- struct mm_struct *mm, -- unsigned long address); -+ int (*test_clear_young)(struct mmu_notifier *mn, struct mm_struct *mm, -+ unsigned long start, unsigned long end, -+ bool clear, unsigned long *bitmap); - - /* - * change_pte is called in cases that pte mapping to page is changed: -@@ -387,11 +373,9 @@ extern void __mmu_notifier_release(struct mm_struct *mm); - extern int __mmu_notifier_clear_flush_young(struct mm_struct *mm, - unsigned long start, - unsigned long end); --extern int __mmu_notifier_clear_young(struct mm_struct *mm, -- unsigned long start, -- unsigned long end); --extern int __mmu_notifier_test_young(struct mm_struct *mm, -- unsigned long address); -+extern int __mmu_notifier_test_clear_young(struct mm_struct *mm, -+ unsigned long start, unsigned long end, -+ bool clear, unsigned long *bitmap); - extern void __mmu_notifier_change_pte(struct mm_struct *mm, - unsigned long address, pte_t pte); - extern int __mmu_notifier_invalidate_range_start(struct mmu_notifier_range *r); -@@ -428,7 +412,7 @@ static inline int mmu_notifier_clear_young(struct mm_struct *mm, - unsigned long end) - { - if (mm_has_notifiers(mm)) -- return __mmu_notifier_clear_young(mm, start, end); -+ return __mmu_notifier_test_clear_young(mm, start, end, true, NULL); - return 0; - } - -@@ -436,7 +420,36 @@ static inline int mmu_notifier_test_young(struct mm_struct *mm, - unsigned long address) - { - if (mm_has_notifiers(mm)) -- return __mmu_notifier_test_young(mm, address); -+ return __mmu_notifier_test_clear_young(mm, address, address + 1, false, NULL); -+ return 0; -+} -+ -+/* -+ * mmu_notifier_test_clear_young() returns nonzero if any of the KVM PTEs within -+ * a given range was young: MMU_NOTIFIER_RANGE_LOCKLESS if the fast path was -+ * successful, MMU_NOTIFIER_RANGE_YOUNG otherwise. -+ * -+ * The last parameter to the function is a bitmap and only the fast path -+ * supports it: if it is NULL, the function falls back to the slow path if the -+ * fast path was unsuccessful; otherwise, the function bails out. -+ * -+ * The bitmap has the following specifications: -+ * 1. The number of bits should be at least (end-start)/PAGE_SIZE. -+ * 2. The offset of each bit should be relative to the end, i.e., the offset -+ * corresponding to addr should be (end-addr)/PAGE_SIZE-1. This is convenient -+ * for batching while forward looping. -+ * -+ * When testing, this function sets the corresponding bit in the bitmap for each -+ * young KVM PTE. When clearing, this function clears the accessed bit for each -+ * young KVM PTE whose corresponding bit in the bitmap is set. -+ */ -+static inline int mmu_notifier_test_clear_young(struct mm_struct *mm, -+ unsigned long start, unsigned long end, -+ bool clear, unsigned long *bitmap) -+{ -+ if (mm_has_notifiers(mm)) -+ return __mmu_notifier_test_clear_young(mm, start, end, clear, bitmap); -+ - return 0; - } - -@@ -684,12 +697,26 @@ static inline int mmu_notifier_clear_flush_young(struct mm_struct *mm, - return 0; - } - -+static inline int mmu_notifier_clear_young(struct mm_struct *mm, -+ unsigned long start, -+ unsigned long end) -+{ -+ return 0; -+} -+ - static inline int mmu_notifier_test_young(struct mm_struct *mm, - unsigned long address) - { - return 0; - } - -+static inline int mmu_notifier_test_clear_young(struct mm_struct *mm, -+ unsigned long start, unsigned long end, -+ bool clear, unsigned long *bitmap) -+{ -+ return 0; -+} -+ - static inline void mmu_notifier_change_pte(struct mm_struct *mm, - unsigned long address, pte_t pte) - { -diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h -index a4889c9d4055..9d0f5f8cab23 100644 ---- a/include/linux/mmzone.h -+++ b/include/linux/mmzone.h -@@ -369,6 +369,7 @@ enum { - LRU_GEN_CORE, - LRU_GEN_MM_WALK, - LRU_GEN_NONLEAF_YOUNG, -+ LRU_GEN_KVM_MMU_WALK, - NR_LRU_GEN_CAPS - }; - -@@ -471,7 +472,7 @@ struct lru_gen_mm_walk { - }; - - void lru_gen_init_lruvec(struct lruvec *lruvec); --void lru_gen_look_around(struct page_vma_mapped_walk *pvmw); -+bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw); - - #ifdef CONFIG_MEMCG - -@@ -559,8 +560,9 @@ static inline void lru_gen_init_lruvec(struct lruvec *lruvec) - { - } - --static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) -+static inline bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw) - { -+ return false; - } - - #ifdef CONFIG_MEMCG -diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h -index 3bd31ea23fee..46c347e56e60 100644 ---- a/include/trace/events/kvm.h -+++ b/include/trace/events/kvm.h -@@ -489,21 +489,6 @@ TRACE_EVENT(kvm_age_hva, - __entry->start, __entry->end) - ); - --TRACE_EVENT(kvm_test_age_hva, -- TP_PROTO(unsigned long hva), -- TP_ARGS(hva), -- -- TP_STRUCT__entry( -- __field( unsigned long, hva ) -- ), -- -- TP_fast_assign( -- __entry->hva = hva; -- ), -- -- TP_printk("mmu notifier test age hva: %#016lx", __entry->hva) --); -- - #endif /* _TRACE_KVM_MAIN_H */ - - /* This part must be outside protection */ -diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c -index 50c0dde1354f..c7e9747c9920 100644 ---- a/mm/mmu_notifier.c -+++ b/mm/mmu_notifier.c -@@ -382,44 +382,26 @@ int __mmu_notifier_clear_flush_young(struct mm_struct *mm, - return young; - } - --int __mmu_notifier_clear_young(struct mm_struct *mm, -- unsigned long start, -- unsigned long end) -+int __mmu_notifier_test_clear_young(struct mm_struct *mm, -+ unsigned long start, unsigned long end, -+ bool clear, unsigned long *bitmap) - { -- struct mmu_notifier *subscription; -- int young = 0, id; -+ int idx; -+ struct mmu_notifier *mn; -+ int young = 0; - -- id = srcu_read_lock(&srcu); -- hlist_for_each_entry_rcu(subscription, -- &mm->notifier_subscriptions->list, hlist, -- srcu_read_lock_held(&srcu)) { -- if (subscription->ops->clear_young) -- young |= subscription->ops->clear_young(subscription, -- mm, start, end); -- } -- srcu_read_unlock(&srcu, id); -+ idx = srcu_read_lock(&srcu); - -- return young; --} -+ hlist_for_each_entry_srcu(mn, &mm->notifier_subscriptions->list, hlist, -+ srcu_read_lock_held(&srcu)) { -+ if (mn->ops->test_clear_young) -+ young |= mn->ops->test_clear_young(mn, mm, start, end, clear, bitmap); - --int __mmu_notifier_test_young(struct mm_struct *mm, -- unsigned long address) --{ -- struct mmu_notifier *subscription; -- int young = 0, id; -- -- id = srcu_read_lock(&srcu); -- hlist_for_each_entry_rcu(subscription, -- &mm->notifier_subscriptions->list, hlist, -- srcu_read_lock_held(&srcu)) { -- if (subscription->ops->test_young) { -- young = subscription->ops->test_young(subscription, mm, -- address); -- if (young) -- break; -- } -+ if (young && !clear) -+ break; - } -- srcu_read_unlock(&srcu, id); -+ -+ srcu_read_unlock(&srcu, idx); - - return young; - } -diff --git a/mm/rmap.c b/mm/rmap.c -index 19392e090bec..51eae5411fa7 100644 ---- a/mm/rmap.c -+++ b/mm/rmap.c -@@ -825,12 +825,10 @@ static bool folio_referenced_one(struct folio *folio, - return false; /* To break the loop */ - } - -- if (pvmw.pte) { -- if (lru_gen_enabled() && pte_young(*pvmw.pte)) { -- lru_gen_look_around(&pvmw); -+ if (lru_gen_enabled() && pvmw.pte) { -+ if (lru_gen_look_around(&pvmw)) - referenced++; -- } -- -+ } else if (pvmw.pte) { - if (ptep_clear_flush_young_notify(vma, address, - pvmw.pte)) - referenced++; -diff --git a/mm/vmscan.c b/mm/vmscan.c -index 6aa2d0a0b1d6..d6802821d8f7 100644 ---- a/mm/vmscan.c -+++ b/mm/vmscan.c -@@ -57,6 +57,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -3222,6 +3223,20 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS); - #define get_cap(cap) static_branch_unlikely(&lru_gen_caps[cap]) - #endif - -+#if IS_ENABLED(CONFIG_KVM) -+#include -+ -+static bool should_walk_kvm_mmu(void) -+{ -+ return kvm_arch_has_test_clear_young() && get_cap(LRU_GEN_KVM_MMU_WALK); -+} -+#else -+static bool should_walk_kvm_mmu(void) -+{ -+ return false; -+} -+#endif -+ - /****************************************************************************** - * shorthand helpers - ******************************************************************************/ -@@ -3960,6 +3975,55 @@ static struct folio *get_pfn_folio(unsigned long pfn, struct mem_cgroup *memcg, - return folio; - } - -+static bool test_spte_young(struct mm_struct *mm, unsigned long addr, unsigned long end, -+ unsigned long *bitmap, unsigned long *last) -+{ -+ if (!should_walk_kvm_mmu()) -+ return false; -+ -+ if (*last > addr) -+ goto done; -+ -+ *last = end - addr > MIN_LRU_BATCH * PAGE_SIZE ? -+ addr + MIN_LRU_BATCH * PAGE_SIZE - 1 : end - 1; -+ bitmap_zero(bitmap, MIN_LRU_BATCH); -+ -+ mmu_notifier_test_clear_young(mm, addr, *last + 1, false, bitmap); -+done: -+ return test_bit((*last - addr) / PAGE_SIZE, bitmap); -+} -+ -+static void clear_spte_young(struct mm_struct *mm, unsigned long addr, -+ unsigned long *bitmap, unsigned long *last) -+{ -+ int i; -+ unsigned long start, end = *last + 1; -+ -+ if (addr + PAGE_SIZE != end) -+ return; -+ -+ i = find_last_bit(bitmap, MIN_LRU_BATCH); -+ if (i == MIN_LRU_BATCH) -+ return; -+ -+ start = end - (i + 1) * PAGE_SIZE; -+ -+ i = find_first_bit(bitmap, MIN_LRU_BATCH); -+ -+ end -= i * PAGE_SIZE; -+ -+ mmu_notifier_test_clear_young(mm, start, end, true, bitmap); -+} -+ -+static void skip_spte_young(struct mm_struct *mm, unsigned long addr, -+ unsigned long *bitmap, unsigned long *last) -+{ -+ if (*last > addr) -+ __clear_bit((*last - addr) / PAGE_SIZE, bitmap); -+ -+ clear_spte_young(mm, addr, bitmap, last); -+} -+ - static bool suitable_to_scan(int total, int young) - { - int n = clamp_t(int, cache_line_size() / sizeof(pte_t), 2, 8); -@@ -3975,6 +4039,8 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end, - pte_t *pte; - spinlock_t *ptl; - unsigned long addr; -+ DECLARE_BITMAP(bitmap, MIN_LRU_BATCH); -+ unsigned long last = 0; - int total = 0; - int young = 0; - struct lru_gen_mm_walk *walk = args->private; -@@ -3993,6 +4059,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end, - pte = pte_offset_map(pmd, start & PMD_MASK); - restart: - for (i = pte_index(start), addr = start; addr != end; i++, addr += PAGE_SIZE) { -+ bool ret; - unsigned long pfn; - struct folio *folio; - -@@ -4000,20 +4067,27 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end, - walk->mm_stats[MM_LEAF_TOTAL]++; - - pfn = get_pte_pfn(pte[i], args->vma, addr); -- if (pfn == -1) -+ if (pfn == -1) { -+ skip_spte_young(args->vma->vm_mm, addr, bitmap, &last); - continue; -+ } - -- if (!pte_young(pte[i])) { -+ ret = test_spte_young(args->vma->vm_mm, addr, end, bitmap, &last); -+ if (!ret && !pte_young(pte[i])) { -+ skip_spte_young(args->vma->vm_mm, addr, bitmap, &last); - walk->mm_stats[MM_LEAF_OLD]++; - continue; - } - - folio = get_pfn_folio(pfn, memcg, pgdat, walk->can_swap); -- if (!folio) -+ if (!folio) { -+ skip_spte_young(args->vma->vm_mm, addr, bitmap, &last); - continue; -+ } - -- if (!ptep_test_and_clear_young(args->vma, addr, pte + i)) -- VM_WARN_ON_ONCE(true); -+ clear_spte_young(args->vma->vm_mm, addr, bitmap, &last); -+ if (pte_young(pte[i])) -+ ptep_test_and_clear_young(args->vma, addr, pte + i); - - young++; - walk->mm_stats[MM_LEAF_YOUNG]++; -@@ -4611,6 +4685,23 @@ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) - * rmap/PT walk feedback - ******************************************************************************/ - -+static bool should_look_around(struct vm_area_struct *vma, unsigned long addr, -+ pte_t *pte, int *young) -+{ -+ int ret = mmu_notifier_clear_young(vma->vm_mm, addr, addr + PAGE_SIZE); -+ -+ if (pte_young(*pte)) { -+ ptep_test_and_clear_young(vma, addr, pte); -+ *young = true; -+ return true; -+ } -+ -+ if (ret) -+ *young = true; -+ -+ return ret & MMU_NOTIFIER_RANGE_LOCKLESS; -+} -+ - /* - * This function exploits spatial locality when shrink_folio_list() walks the - * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. If -@@ -4618,12 +4709,14 @@ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) - * the PTE table to the Bloom filter. This forms a feedback loop between the - * eviction and the aging. - */ --void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) -+bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw) - { - int i; - unsigned long start; - unsigned long end; - struct lru_gen_mm_walk *walk; -+ DECLARE_BITMAP(bitmap, MIN_LRU_BATCH); -+ unsigned long last = 0; - int young = 0; - pte_t *pte = pvmw->pte; - unsigned long addr = pvmw->address; -@@ -4637,8 +4730,11 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) - lockdep_assert_held(pvmw->ptl); - VM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio); - -+ if (!should_look_around(pvmw->vma, addr, pte, &young)) -+ return young; -+ - if (spin_is_contended(pvmw->ptl)) -- return; -+ return young; - - /* avoid taking the LRU lock under the PTL when possible */ - walk = current->reclaim_state ? current->reclaim_state->mm_walk : NULL; -@@ -4646,6 +4742,9 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) - start = max(addr & PMD_MASK, pvmw->vma->vm_start); - end = min(addr | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1; - -+ if (end - start == PAGE_SIZE) -+ return young; -+ - if (end - start > MIN_LRU_BATCH * PAGE_SIZE) { - if (addr - start < MIN_LRU_BATCH * PAGE_SIZE / 2) - end = start + MIN_LRU_BATCH * PAGE_SIZE; -@@ -4659,28 +4758,37 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) - - /* folio_update_gen() requires stable folio_memcg() */ - if (!mem_cgroup_trylock_pages(memcg)) -- return; -+ return young; - - arch_enter_lazy_mmu_mode(); - - pte -= (addr - start) / PAGE_SIZE; - - for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) { -+ bool ret; - unsigned long pfn; - - pfn = get_pte_pfn(pte[i], pvmw->vma, addr); -- if (pfn == -1) -+ if (pfn == -1) { -+ skip_spte_young(pvmw->vma->vm_mm, addr, bitmap, &last); - continue; -+ } - -- if (!pte_young(pte[i])) -+ ret = test_spte_young(pvmw->vma->vm_mm, addr, end, bitmap, &last); -+ if (!ret && !pte_young(pte[i])) { -+ skip_spte_young(pvmw->vma->vm_mm, addr, bitmap, &last); - continue; -+ } - - folio = get_pfn_folio(pfn, memcg, pgdat, !walk || walk->can_swap); -- if (!folio) -+ if (!folio) { -+ skip_spte_young(pvmw->vma->vm_mm, addr, bitmap, &last); - continue; -+ } - -- if (!ptep_test_and_clear_young(pvmw->vma, addr, pte + i)) -- VM_WARN_ON_ONCE(true); -+ clear_spte_young(pvmw->vma->vm_mm, addr, bitmap, &last); -+ if (pte_young(pte[i])) -+ ptep_test_and_clear_young(pvmw->vma, addr, pte + i); - - young++; - -@@ -4710,6 +4818,8 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) - /* feedback from rmap walkers to page table walkers */ - if (suitable_to_scan(i, young)) - update_bloom_filter(lruvec, max_seq, pvmw->pmd); -+ -+ return young; - } - - /****************************************************************************** -@@ -5727,6 +5837,9 @@ static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, c - if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG)) - caps |= BIT(LRU_GEN_NONLEAF_YOUNG); - -+ if (should_walk_kvm_mmu()) -+ caps |= BIT(LRU_GEN_KVM_MMU_WALK); -+ - return sysfs_emit(buf, "0x%04x\n", caps); - } - -diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c -index 65f94f592ff8..9db05880b6b9 100644 ---- a/virt/kvm/kvm_main.c -+++ b/virt/kvm/kvm_main.c -@@ -541,6 +541,7 @@ typedef void (*on_lock_fn_t)(struct kvm *kvm, unsigned long start, - typedef void (*on_unlock_fn_t)(struct kvm *kvm); - - struct kvm_hva_range { -+ void *args; - unsigned long start; - unsigned long end; - pte_t pte; -@@ -549,6 +550,7 @@ struct kvm_hva_range { - on_unlock_fn_t on_unlock; - bool flush_on_ret; - bool may_block; -+ bool lockless; - }; - - /* -@@ -602,6 +604,8 @@ static __always_inline int __kvm_handle_hva_range(struct kvm *kvm, - hva_end = min(range->end, slot->userspace_addr + - (slot->npages << PAGE_SHIFT)); - -+ gfn_range.args = range->args; -+ - /* - * To optimize for the likely case where the address - * range is covered by zero or one memslots, don't -@@ -619,7 +623,7 @@ static __always_inline int __kvm_handle_hva_range(struct kvm *kvm, - gfn_range.end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1, slot); - gfn_range.slot = slot; - -- if (!locked) { -+ if (!range->lockless && !locked) { - locked = true; - KVM_MMU_LOCK(kvm); - if (!IS_KVM_NULL_FN(range->on_lock)) -@@ -628,6 +632,9 @@ static __always_inline int __kvm_handle_hva_range(struct kvm *kvm, - break; +diff --git a/mm/khugepaged.c b/mm/khugepaged.c +index 78c8d5d8b628..4b8b8673d5d9 100644 +--- a/mm/khugepaged.c ++++ b/mm/khugepaged.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -709,6 +710,7 @@ static void __collapse_huge_page_copy_succeeded(pte_t *pte, + spin_lock(ptl); + ptep_clear(vma->vm_mm, address, _pte); + spin_unlock(ptl); ++ ksm_might_unmap_zero_page(vma->vm_mm, pteval); } - ret |= range->handler(kvm, &gfn_range); + } else { + src_page = pte_page(pteval); +diff --git a/mm/ksm.c b/mm/ksm.c +index ba266359da55..97a9627116fa 100644 +--- a/mm/ksm.c ++++ b/mm/ksm.c +@@ -278,6 +278,9 @@ static unsigned int zero_checksum __read_mostly; + /* Whether to merge empty (zeroed) pages with actual zero pages */ + static bool ksm_use_zero_pages __read_mostly; + ++/* The number of zero pages which is placed by KSM */ ++unsigned long ksm_zero_pages; + -+ if (range->lockless && ret) -+ break; - } + #ifdef CONFIG_NUMA + /* Zeroed when merging across nodes is not allowed */ + static unsigned int ksm_merge_across_nodes = 1; +@@ -448,7 +451,8 @@ static int break_ksm_pmd_entry(pmd_t *pmd, unsigned long addr, unsigned long nex + if (is_migration_entry(entry)) + page = pfn_swap_entry_to_page(entry); } +- ret = page && PageKsm(page); ++ /* return 1 if the page is an normal ksm page or KSM-placed zero page */ ++ ret = (page && PageKsm(page)) || is_ksm_zero_pte(*pte); + pte_unmap_unlock(pte, ptl); + return ret; + } +@@ -1222,8 +1226,14 @@ static int replace_page(struct vm_area_struct *vma, struct page *page, + page_add_anon_rmap(kpage, vma, addr, RMAP_NONE); + newpte = mk_pte(kpage, vma->vm_page_prot); + } else { +- newpte = pte_mkspecial(pfn_pte(page_to_pfn(kpage), +- vma->vm_page_prot)); ++ /* ++ * Use pte_mkdirty to mark the zero page mapped by KSM, and then ++ * we can easily track all KSM-placed zero pages by checking if ++ * the dirty bit in zero page's PTE is set. ++ */ ++ newpte = pte_mkdirty(pte_mkspecial(pfn_pte(page_to_pfn(kpage), vma->vm_page_prot))); ++ ksm_zero_pages++; ++ mm->ksm_zero_pages++; + /* + * We're replacing an anonymous page with a zero page, which is + * not anonymous. We need to do proper accounting otherwise we +@@ -3082,7 +3092,7 @@ static void wait_while_offlining(void) + #ifdef CONFIG_PROC_FS + long ksm_process_profit(struct mm_struct *mm) + { +- return mm->ksm_merging_pages * PAGE_SIZE - ++ return (long)(mm->ksm_merging_pages + mm->ksm_zero_pages) * PAGE_SIZE - + mm->ksm_rmap_items * sizeof(struct ksm_rmap_item); + } + #endif /* CONFIG_PROC_FS */ +@@ -3351,12 +3361,19 @@ static ssize_t pages_volatile_show(struct kobject *kobj, + } + KSM_ATTR_RO(pages_volatile); -@@ -667,26 +674,6 @@ static __always_inline int kvm_handle_hva_range(struct mmu_notifier *mn, - return __kvm_handle_hva_range(kvm, &range); ++static ssize_t ksm_zero_pages_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sysfs_emit(buf, "%ld\n", ksm_zero_pages); ++} ++KSM_ATTR_RO(ksm_zero_pages); ++ + static ssize_t general_profit_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) + { + long general_profit; + +- general_profit = ksm_pages_sharing * PAGE_SIZE - ++ general_profit = (ksm_pages_sharing + ksm_zero_pages) * PAGE_SIZE - + ksm_rmap_items * sizeof(struct ksm_rmap_item); + + return sysfs_emit(buf, "%ld\n", general_profit); +@@ -3418,6 +3435,7 @@ static struct attribute *ksm_attrs[] = { + &pages_sharing_attr.attr, + &pages_unshared_attr.attr, + &pages_volatile_attr.attr, ++ &ksm_zero_pages_attr.attr, + &full_scans_attr.attr, + #ifdef CONFIG_NUMA + &merge_across_nodes_attr.attr, +diff --git a/mm/memory.c b/mm/memory.c +index 01f39e8144ef..0dc2f193c4d6 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -1433,8 +1433,10 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, + tlb_remove_tlb_entry(tlb, pte, addr); + zap_install_uffd_wp_if_needed(vma, addr, pte, details, + ptent); +- if (unlikely(!page)) ++ if (unlikely(!page)) { ++ ksm_might_unmap_zero_page(mm, ptent); + continue; ++ } + + delay_rmap = 0; + if (!PageAnon(page)) { +@@ -3128,6 +3130,7 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf) + inc_mm_counter(mm, MM_ANONPAGES); + } + } else { ++ ksm_might_unmap_zero_page(mm, vmf->orig_pte); + inc_mm_counter(mm, MM_ANONPAGES); + } + flush_cache_page(vma, vmf->address, pte_pfn(vmf->orig_pte)); +diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c +index 26853badae70..0de9d33cd565 100644 +--- a/tools/testing/selftests/mm/ksm_functional_tests.c ++++ b/tools/testing/selftests/mm/ksm_functional_tests.c +@@ -29,6 +29,8 @@ + + static int ksm_fd; + static int ksm_full_scans_fd; ++static int proc_self_ksm_stat_fd; ++static int ksm_use_zero_pages_fd; + static int pagemap_fd; + static size_t pagesize; + +@@ -59,6 +61,33 @@ static bool range_maps_duplicates(char *addr, unsigned long size) + return false; } --static __always_inline int kvm_handle_hva_range_no_flush(struct mmu_notifier *mn, -- unsigned long start, -- unsigned long end, -- hva_handler_t handler) --{ -- struct kvm *kvm = mmu_notifier_to_kvm(mn); -- const struct kvm_hva_range range = { -- .start = start, -- .end = end, -- .pte = __pte(0), -- .handler = handler, -- .on_lock = (void *)kvm_null_fn, -- .on_unlock = (void *)kvm_null_fn, -- .flush_on_ret = false, -- .may_block = false, -- }; -- -- return __kvm_handle_hva_range(kvm, &range); --} -- - static bool kvm_change_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) - { - /* -@@ -865,37 +852,70 @@ static int kvm_mmu_notifier_clear_flush_young(struct mmu_notifier *mn, - return kvm_handle_hva_range(mn, start, end, __pte(0), kvm_age_gfn); - } - --static int kvm_mmu_notifier_clear_young(struct mmu_notifier *mn, -- struct mm_struct *mm, -- unsigned long start, -- unsigned long end) -+struct test_clear_young_args { -+ unsigned long *bitmap; -+ unsigned long end; -+ bool clear; -+ bool young; -+}; ++static long get_my_ksm_zero_pages(void) ++{ ++ char buf[200]; ++ char *substr_ksm_zero; ++ size_t value_pos; ++ ssize_t read_size; ++ unsigned long my_ksm_zero_pages; + -+bool kvm_should_clear_young(struct kvm_gfn_range *range, gfn_t gfn) - { -- trace_kvm_age_hva(start, end); -+ struct test_clear_young_args *args = range->args; - -- /* -- * Even though we do not flush TLB, this will still adversely -- * affect performance on pre-Haswell Intel EPT, where there is -- * no EPT Access Bit to clear so that we have to tear down EPT -- * tables instead. If we find this unacceptable, we can always -- * add a parameter to kvm_age_hva so that it effectively doesn't -- * do anything on clear_young. -- * -- * Also note that currently we never issue secondary TLB flushes -- * from clear_young, leaving this job up to the regular system -- * cadence. If we find this inaccurate, we might come up with a -- * more sophisticated heuristic later. -- */ -- return kvm_handle_hva_range_no_flush(mn, start, end, kvm_age_gfn); -+ VM_WARN_ON_ONCE(gfn < range->start || gfn >= range->end); -+ -+ args->young = true; -+ -+ if (args->bitmap) { -+ int offset = hva_to_gfn_memslot(args->end - 1, range->slot) - gfn; -+ -+ if (args->clear) -+ return test_bit(offset, args->bitmap); -+ -+ __set_bit(offset, args->bitmap); -+ } -+ -+ return args->clear; - } - --static int kvm_mmu_notifier_test_young(struct mmu_notifier *mn, -- struct mm_struct *mm, -- unsigned long address) -+static int kvm_mmu_notifier_test_clear_young(struct mmu_notifier *mn, struct mm_struct *mm, -+ unsigned long start, unsigned long end, -+ bool clear, unsigned long *bitmap) - { -- trace_kvm_test_age_hva(address); -+ struct kvm *kvm = mmu_notifier_to_kvm(mn); -+ struct kvm_hva_range range = { -+ .start = start, -+ .end = end, -+ .on_lock = (void *)kvm_null_fn, -+ .on_unlock = (void *)kvm_null_fn, -+ }; -+ -+ trace_kvm_age_hva(start, end); -+ -+ if (kvm_arch_has_test_clear_young()) { -+ struct test_clear_young_args args = { -+ .bitmap = bitmap, -+ .end = end, -+ .clear = clear, -+ }; -+ -+ range.args = &args; -+ range.lockless = true; -+ range.handler = kvm_arch_test_clear_young; -+ -+ if (!__kvm_handle_hva_range(kvm, &range)) -+ return args.young ? MMU_NOTIFIER_RANGE_LOCKLESS : 0; -+ } -+ -+ if (bitmap) ++ if (!proc_self_ksm_stat_fd) + return 0; + -+ range.args = NULL; -+ range.lockless = false; -+ range.handler = clear ? kvm_age_gfn : kvm_test_age_gfn; - -- return kvm_handle_hva_range_no_flush(mn, address, address + 1, -- kvm_test_age_gfn); -+ return __kvm_handle_hva_range(kvm, &range) ? MMU_NOTIFIER_RANGE_YOUNG : 0; ++ read_size = pread(proc_self_ksm_stat_fd, buf, sizeof(buf) - 1, 0); ++ if (read_size < 0) ++ return -errno; ++ ++ buf[read_size] = 0; ++ ++ substr_ksm_zero = strstr(buf, "ksm_zero_pages"); ++ if (!substr_ksm_zero) ++ return 0; ++ ++ value_pos = strcspn(substr_ksm_zero, "0123456789"); ++ my_ksm_zero_pages = strtol(substr_ksm_zero + value_pos, NULL, 10); ++ ++ return my_ksm_zero_pages; ++} ++ + static long ksm_get_full_scans(void) + { + char buf[10]; +@@ -159,6 +188,70 @@ static void test_unmerge(void) + munmap(map, size); } - static void kvm_mmu_notifier_release(struct mmu_notifier *mn, -@@ -914,8 +934,7 @@ static const struct mmu_notifier_ops kvm_mmu_notifier_ops = { - .invalidate_range_start = kvm_mmu_notifier_invalidate_range_start, - .invalidate_range_end = kvm_mmu_notifier_invalidate_range_end, - .clear_flush_young = kvm_mmu_notifier_clear_flush_young, -- .clear_young = kvm_mmu_notifier_clear_young, -- .test_young = kvm_mmu_notifier_test_young, -+ .test_clear_young = kvm_mmu_notifier_test_clear_young, - .change_pte = kvm_mmu_notifier_change_pte, - .release = kvm_mmu_notifier_release, - }; ++static void test_unmerge_zero_pages(void) ++{ ++ const unsigned int size = 2 * MiB; ++ char *map; ++ unsigned int offs; ++ unsigned long pages_expected; ++ ++ ksft_print_msg("[RUN] %s\n", __func__); ++ ++ if (proc_self_ksm_stat_fd < 0) { ++ ksft_test_result_skip("open(\"/proc/self/ksm_stat\") failed\n"); ++ return; ++ } ++ if (ksm_use_zero_pages_fd < 0) { ++ ksft_test_result_skip("open \"/sys/kernel/mm/ksm/use_zero_pages\" failed\n"); ++ return; ++ } ++ if (write(ksm_use_zero_pages_fd, "1", 1) != 1) { ++ ksft_test_result_skip("write \"/sys/kernel/mm/ksm/use_zero_pages\" failed\n"); ++ return; ++ } ++ ++ /* Let KSM deduplicate zero pages. */ ++ map = mmap_and_merge_range(0x00, size, false); ++ if (map == MAP_FAILED) ++ return; ++ ++ /* Check if ksm_zero_pages is updated correctly after KSM merging */ ++ pages_expected = size / pagesize; ++ if (pages_expected != get_my_ksm_zero_pages()) { ++ ksft_test_result_fail("'ksm_zero_pages' updated after merging\n"); ++ goto unmap; ++ } ++ ++ /* Try to unmerge half of the region */ ++ if (madvise(map, size / 2, MADV_UNMERGEABLE)) { ++ ksft_test_result_fail("MADV_UNMERGEABLE failed\n"); ++ goto unmap; ++ } ++ ++ /* Check if ksm_zero_pages is updated correctly after unmerging */ ++ pages_expected /= 2; ++ if (pages_expected != get_my_ksm_zero_pages()) { ++ ksft_test_result_fail("'ksm_zero_pages' updated after unmerging\n"); ++ goto unmap; ++ } ++ ++ /* Trigger unmerging of the other half by writing to the pages. */ ++ for (offs = size / 2; offs < size; offs += pagesize) ++ *((unsigned int *)&map[offs]) = offs; ++ ++ /* Now we should have no zeropages remaining. */ ++ if (get_my_ksm_zero_pages()) { ++ ksft_test_result_fail("'ksm_zero_pages' updated after write fault\n"); ++ goto unmap; ++ } ++ ++ /* Check if ksm zero pages are really unmerged */ ++ ksft_test_result(!range_maps_duplicates(map, size), ++ "KSM zero pages were unmerged\n"); ++unmap: ++ munmap(map, size); ++} ++ + static void test_unmerge_discarded(void) + { + const unsigned int size = 2 * MiB; +@@ -358,7 +451,7 @@ static void test_prctl_unmerge(void) + + int main(int argc, char **argv) + { +- unsigned int tests = 5; ++ unsigned int tests = 6; + int err; + + #ifdef __NR_userfaultfd +@@ -379,8 +472,11 @@ int main(int argc, char **argv) + pagemap_fd = open("/proc/self/pagemap", O_RDONLY); + if (pagemap_fd < 0) + ksft_exit_skip("open(\"/proc/self/pagemap\") failed\n"); ++ proc_self_ksm_stat_fd = open("/proc/self/ksm_stat", O_RDONLY); ++ ksm_use_zero_pages_fd = open("/sys/kernel/mm/ksm/use_zero_pages", O_RDWR); + + test_unmerge(); ++ test_unmerge_zero_pages(); + test_unmerge_discarded(); + #ifdef __NR_userfaultfd + test_unmerge_uffd_wp(); -- 2.41.0 -From f002178b1b3fe685dc55b8772ae140e09ad4d894 Mon Sep 17 00:00:00 2001 +From 31b1d9be3d434ee82ffccc53fabc5a4326db96c7 Mon Sep 17 00:00:00 2001 From: Peter Jung -Date: Wed, 19 Jul 2023 18:51:24 +0200 -Subject: [PATCH 7/8] sched +Date: Mon, 10 Jul 2023 17:10:36 +0200 +Subject: [PATCH 6/7] sched Signed-off-by: Peter Jung --- - arch/x86/kernel/itmt.c | 23 +- - arch/x86/kernel/smpboot.c | 7 +- - include/linux/cgroup-defs.h | 2 + - include/linux/sched.h | 2 + - include/linux/sched/sd_flags.h | 5 +- - include/linux/sched/task.h | 38 ++- - kernel/cgroup/cgroup.c | 46 ++++ - kernel/fork.c | 8 + - kernel/sched/core.c | 116 +++++++++- - kernel/sched/deadline.c | 7 - - kernel/sched/debug.c | 3 +- - kernel/sched/fair.c | 409 ++++++++++++++++++++++++--------- - kernel/sched/features.h | 1 + - kernel/sched/psi.c | 21 +- - kernel/sched/sched.h | 32 ++- - kernel/sched/topology.c | 21 +- - kernel/softirq.c | 2 +- - 17 files changed, 568 insertions(+), 175 deletions(-) + arch/x86/kernel/smpboot.c | 11 +-- + include/linux/cgroup-defs.h | 2 + + include/linux/sched.h | 2 + + include/linux/sched/task.h | 38 +++++++- + kernel/cgroup/cgroup.c | 34 +++++++ + kernel/fork.c | 8 ++ + kernel/sched/core.c | 57 ++++++++++++ + kernel/sched/debug.c | 1 + + kernel/sched/fair.c | 177 +++++++++++++++++++++++++++++++++--- + kernel/sched/psi.c | 2 +- + kernel/sched/sched.h | 3 + + kernel/sched/topology.c | 14 ++- + kernel/softirq.c | 2 +- + 13 files changed, 325 insertions(+), 26 deletions(-) -diff --git a/arch/x86/kernel/itmt.c b/arch/x86/kernel/itmt.c -index 670eb08b972a..ee4fe8cdb857 100644 ---- a/arch/x86/kernel/itmt.c -+++ b/arch/x86/kernel/itmt.c -@@ -165,32 +165,19 @@ int arch_asym_cpu_priority(int cpu) - - /** - * sched_set_itmt_core_prio() - Set CPU priority based on ITMT -- * @prio: Priority of cpu core -- * @core_cpu: The cpu number associated with the core -+ * @prio: Priority of @cpu -+ * @cpu: The CPU number - * - * The pstate driver will find out the max boost frequency - * and call this function to set a priority proportional -- * to the max boost frequency. CPU with higher boost -+ * to the max boost frequency. CPUs with higher boost - * frequency will receive higher priority. - * - * No need to rebuild sched domain after updating - * the CPU priorities. The sched domains have no - * dependency on CPU priorities. - */ --void sched_set_itmt_core_prio(int prio, int core_cpu) -+void sched_set_itmt_core_prio(int prio, int cpu) - { -- int cpu, i = 1; -- -- for_each_cpu(cpu, topology_sibling_cpumask(core_cpu)) { -- int smt_prio; -- -- /* -- * Ensure that the siblings are moved to the end -- * of the priority chain and only used when -- * all other high priority cpus are out of capacity. -- */ -- smt_prio = prio * smp_num_siblings / (i * i); -- per_cpu(sched_core_priority, cpu) = smt_prio; -- i++; -- } -+ per_cpu(sched_core_priority, cpu) = prio; - } diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c -index 483df0427678..a81f2b0dbbad 100644 +index e1aa2cd7734b..4c314475cc13 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c -@@ -571,7 +571,7 @@ static int x86_core_flags(void) - #ifdef CONFIG_SCHED_SMT - static int x86_smt_flags(void) - { -- return cpu_smt_flags() | x86_sched_itmt_flags(); -+ return cpu_smt_flags(); - } +@@ -632,14 +632,9 @@ static void __init build_sched_topology(void) + }; #endif #ifdef CONFIG_SCHED_CLUSTER -@@ -599,10 +599,13 @@ static struct sched_domain_topology_level x86_hybrid_topology[] = { - #ifdef CONFIG_SCHED_SMT - { cpu_smt_mask, x86_smt_flags, SD_INIT_NAME(SMT) }, +- /* +- * For now, skip the cluster domain on Hybrid. +- */ +- if (!cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) { +- x86_topology[i++] = (struct sched_domain_topology_level){ +- cpu_clustergroup_mask, x86_cluster_flags, SD_INIT_NAME(CLS) +- }; +- } ++ x86_topology[i++] = (struct sched_domain_topology_level){ ++ cpu_clustergroup_mask, x86_cluster_flags, SD_INIT_NAME(CLS) ++ }; #endif -+#ifdef CONFIG_SCHED_CLUSTER -+ { cpu_clustergroup_mask, x86_cluster_flags, SD_INIT_NAME(CLS) }, -+#endif #ifdef CONFIG_SCHED_MC - { cpu_coregroup_mask, x86_core_flags, SD_INIT_NAME(MC) }, - #endif -- { cpu_cpu_mask, SD_INIT_NAME(DIE) }, -+ { cpu_cpu_mask, x86_sched_itmt_flags, SD_INIT_NAME(DIE) }, - { NULL, }, - }; - + x86_topology[i++] = (struct sched_domain_topology_level){ diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 8a0d5466c7be..ae20dbb885d6 100644 --- a/include/linux/cgroup-defs.h @@ -15441,10 +13104,10 @@ index 8a0d5466c7be..ae20dbb885d6 100644 int (*can_attach)(struct cgroup_taskset *tset); void (*cancel_attach)(struct cgroup_taskset *tset); diff --git a/include/linux/sched.h b/include/linux/sched.h -index eed5d65b8d1f..8473324705ca 100644 +index 609bde814cb0..efc9f4bdc4ca 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -2437,9 +2437,11 @@ extern void sched_core_free(struct task_struct *tsk); +@@ -2433,9 +2433,11 @@ extern void sched_core_free(struct task_struct *tsk); extern void sched_core_fork(struct task_struct *p); extern int sched_core_share_pid(unsigned int cmd, pid_t pid, enum pid_type type, unsigned long uaddr); @@ -15456,26 +13119,8 @@ index eed5d65b8d1f..8473324705ca 100644 #endif extern void sched_set_stop_task(int cpu, struct task_struct *stop); -diff --git a/include/linux/sched/sd_flags.h b/include/linux/sched/sd_flags.h -index 57bde66d95f7..fad77b5172e2 100644 ---- a/include/linux/sched/sd_flags.h -+++ b/include/linux/sched/sd_flags.h -@@ -132,12 +132,9 @@ SD_FLAG(SD_SERIALIZE, SDF_SHARED_PARENT | SDF_NEEDS_GROUPS) - /* - * Place busy tasks earlier in the domain - * -- * SHARED_CHILD: Usually set on the SMT level. Technically could be set further -- * up, but currently assumed to be set from the base domain -- * upwards (see update_top_cache_domain()). - * NEEDS_GROUPS: Load balancing flag. - */ --SD_FLAG(SD_ASYM_PACKING, SDF_SHARED_CHILD | SDF_NEEDS_GROUPS) -+SD_FLAG(SD_ASYM_PACKING, SDF_NEEDS_GROUPS) - - /* - * Prefer to place tasks in a sibling domain diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h -index e0f5ac90a228..b53909027771 100644 +index dd35ce28bb90..a23af225c898 100644 --- a/include/linux/sched/task.h +++ b/include/linux/sched/task.h @@ -118,11 +118,47 @@ static inline struct task_struct *get_task_struct(struct task_struct *t) @@ -15526,12 +13171,12 @@ index e0f5ac90a228..b53909027771 100644 + call_rcu(&t->rcu, __put_task_struct_rcu_cb); } - static inline void put_task_struct_many(struct task_struct *t, int nr) + DEFINE_FREE(put_task, struct task_struct *, if (_T) put_task_struct(_T)) diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c -index 4d42f0cbc11e..b782ae876c84 100644 +index bfe3cd8ccf36..4e3ee13217ce 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c -@@ -3740,6 +3740,36 @@ static int cpu_stat_show(struct seq_file *seq, void *v) +@@ -3685,6 +3685,36 @@ static int cpu_stat_show(struct seq_file *seq, void *v) return ret; } @@ -15568,22 +13213,7 @@ index 4d42f0cbc11e..b782ae876c84 100644 #ifdef CONFIG_PSI static int cgroup_io_pressure_show(struct seq_file *seq, void *v) { -@@ -3891,6 +3921,14 @@ static __poll_t cgroup_pressure_poll(struct kernfs_open_file *of, - return psi_trigger_poll(&ctx->psi.trigger, of->file, pt); - } - -+static int cgroup_pressure_open(struct kernfs_open_file *of) -+{ -+ if (of->file->f_mode & FMODE_WRITE && !capable(CAP_SYS_RESOURCE)) -+ return -EPERM; -+ -+ return 0; -+} -+ - static void cgroup_pressure_release(struct kernfs_open_file *of) - { - struct cgroup_file_ctx *ctx = of->priv; -@@ -5282,6 +5320,10 @@ static struct cftype cgroup_base_files[] = { +@@ -5235,6 +5265,10 @@ static struct cftype cgroup_base_files[] = { .name = "cpu.stat", .seq_show = cpu_stat_show, }, @@ -15594,43 +13224,11 @@ index 4d42f0cbc11e..b782ae876c84 100644 { } /* terminate */ }; -@@ -5290,6 +5332,7 @@ static struct cftype cgroup_psi_files[] = { - { - .name = "io.pressure", - .file_offset = offsetof(struct cgroup, psi_files[PSI_IO]), -+ .open = cgroup_pressure_open, - .seq_show = cgroup_io_pressure_show, - .write = cgroup_io_pressure_write, - .poll = cgroup_pressure_poll, -@@ -5298,6 +5341,7 @@ static struct cftype cgroup_psi_files[] = { - { - .name = "memory.pressure", - .file_offset = offsetof(struct cgroup, psi_files[PSI_MEM]), -+ .open = cgroup_pressure_open, - .seq_show = cgroup_memory_pressure_show, - .write = cgroup_memory_pressure_write, - .poll = cgroup_pressure_poll, -@@ -5306,6 +5350,7 @@ static struct cftype cgroup_psi_files[] = { - { - .name = "cpu.pressure", - .file_offset = offsetof(struct cgroup, psi_files[PSI_CPU]), -+ .open = cgroup_pressure_open, - .seq_show = cgroup_cpu_pressure_show, - .write = cgroup_cpu_pressure_write, - .poll = cgroup_pressure_poll, -@@ -5315,6 +5360,7 @@ static struct cftype cgroup_psi_files[] = { - { - .name = "irq.pressure", - .file_offset = offsetof(struct cgroup, psi_files[PSI_IRQ]), -+ .open = cgroup_pressure_open, - .seq_show = cgroup_irq_pressure_show, - .write = cgroup_irq_pressure_write, - .poll = cgroup_pressure_poll, diff --git a/kernel/fork.c b/kernel/fork.c -index f405763e06ae..47a1967b6a55 100644 +index 95ca80492a37..36fb0b711541 100644 --- a/kernel/fork.c +++ b/kernel/fork.c -@@ -993,6 +993,14 @@ void __put_task_struct(struct task_struct *tsk) +@@ -989,6 +989,14 @@ void __put_task_struct(struct task_struct *tsk) } EXPORT_SYMBOL_GPL(__put_task_struct); @@ -15646,119 +13244,10 @@ index f405763e06ae..47a1967b6a55 100644 /* diff --git a/kernel/sched/core.c b/kernel/sched/core.c -index a68d1276bab0..1b971c69d3a2 100644 +index c52c2eba7c73..83e36547af17 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c -@@ -3341,6 +3341,39 @@ int migrate_swap(struct task_struct *cur, struct task_struct *p, - } - #endif /* CONFIG_NUMA_BALANCING */ - -+static __always_inline -+int __task_state_match(struct task_struct *p, unsigned int state) -+{ -+ if (READ_ONCE(p->__state) & state) -+ return 1; -+ -+#ifdef CONFIG_PREEMPT_RT -+ if (READ_ONCE(p->saved_state) & state) -+ return -1; -+#endif -+ return 0; -+} -+ -+static __always_inline -+int task_state_match(struct task_struct *p, unsigned int state) -+{ -+#ifdef CONFIG_PREEMPT_RT -+ int match; -+ -+ /* -+ * Serialize against current_save_and_set_rtlock_wait_state() and -+ * current_restore_rtlock_saved_state(). -+ */ -+ raw_spin_lock_irq(&p->pi_lock); -+ match = __task_state_match(p, state); -+ raw_spin_unlock_irq(&p->pi_lock); -+ -+ return match; -+#else -+ return __task_state_match(p, state); -+#endif -+} -+ - /* - * wait_task_inactive - wait for a thread to unschedule. - * -@@ -3359,7 +3392,7 @@ int migrate_swap(struct task_struct *cur, struct task_struct *p, - */ - unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state) - { -- int running, queued; -+ int running, queued, match; - struct rq_flags rf; - unsigned long ncsw; - struct rq *rq; -@@ -3385,7 +3418,7 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state - * is actually now running somewhere else! - */ - while (task_on_cpu(rq, p)) { -- if (!(READ_ONCE(p->__state) & match_state)) -+ if (!task_state_match(p, match_state)) - return 0; - cpu_relax(); - } -@@ -3400,8 +3433,15 @@ unsigned long wait_task_inactive(struct task_struct *p, unsigned int match_state - running = task_on_cpu(rq, p); - queued = task_on_rq_queued(p); - ncsw = 0; -- if (READ_ONCE(p->__state) & match_state) -+ if ((match = __task_state_match(p, match_state))) { -+ /* -+ * When matching on p->saved_state, consider this task -+ * still queued so it will wait. -+ */ -+ if (match < 0) -+ queued = 1; - ncsw = p->nvcsw | LONG_MIN; /* sets MSB */ -+ } - task_rq_unlock(rq, p, &rf); - - /* -@@ -4003,15 +4043,14 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags) - static __always_inline - bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success) - { -+ int match; -+ - if (IS_ENABLED(CONFIG_DEBUG_PREEMPT)) { - WARN_ON_ONCE((state & TASK_RTLOCK_WAIT) && - state != TASK_RTLOCK_WAIT); - } - -- if (READ_ONCE(p->__state) & state) { -- *success = 1; -- return true; -- } -+ *success = !!(match = __task_state_match(p, state)); - - #ifdef CONFIG_PREEMPT_RT - /* -@@ -4027,12 +4066,10 @@ bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success) - * p::saved_state to TASK_RUNNING so any further tests will - * not result in false positives vs. @success - */ -- if (p->saved_state & state) { -+ if (match < 0) - p->saved_state = TASK_RUNNING; -- *success = 1; -- } - #endif -- return false; -+ return match > 0; - } - - /* -@@ -7342,6 +7379,19 @@ struct task_struct *idle_task(int cpu) +@@ -7383,6 +7383,19 @@ struct task_struct *idle_task(int cpu) return cpu_rq(cpu)->idle; } @@ -15778,7 +13267,7 @@ index a68d1276bab0..1b971c69d3a2 100644 #ifdef CONFIG_SMP /* * This function computes an effective utilization for the given CPU, to be -@@ -11103,6 +11153,27 @@ static int cpu_cfs_stat_show(struct seq_file *sf, void *v) +@@ -11139,6 +11152,27 @@ static int cpu_cfs_stat_show(struct seq_file *sf, void *v) return 0; } @@ -15806,7 +13295,7 @@ index a68d1276bab0..1b971c69d3a2 100644 #endif /* CONFIG_CFS_BANDWIDTH */ #endif /* CONFIG_FAIR_GROUP_SCHED */ -@@ -11179,6 +11250,10 @@ static struct cftype cpu_legacy_files[] = { +@@ -11215,6 +11249,10 @@ static struct cftype cpu_legacy_files[] = { .name = "stat", .seq_show = cpu_cfs_stat_show, }, @@ -15817,7 +13306,7 @@ index a68d1276bab0..1b971c69d3a2 100644 #endif #ifdef CONFIG_RT_GROUP_SCHED { -@@ -11235,6 +11310,24 @@ static int cpu_extra_stat_show(struct seq_file *sf, +@@ -11271,6 +11309,24 @@ static int cpu_extra_stat_show(struct seq_file *sf, return 0; } @@ -15842,7 +13331,7 @@ index a68d1276bab0..1b971c69d3a2 100644 #ifdef CONFIG_FAIR_GROUP_SCHED static u64 cpu_weight_read_u64(struct cgroup_subsys_state *css, struct cftype *cft) -@@ -11413,6 +11506,7 @@ struct cgroup_subsys cpu_cgrp_subsys = { +@@ -11449,6 +11505,7 @@ struct cgroup_subsys cpu_cgrp_subsys = { .css_released = cpu_cgroup_css_released, .css_free = cpu_cgroup_css_free, .css_extra_stat_show = cpu_extra_stat_show, @@ -15850,26 +13339,8 @@ index a68d1276bab0..1b971c69d3a2 100644 #ifdef CONFIG_RT_GROUP_SCHED .can_attach = cpu_cgroup_can_attach, #endif -diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c -index 5a9a4b81c972..f827067ad03b 100644 ---- a/kernel/sched/deadline.c -+++ b/kernel/sched/deadline.c -@@ -489,13 +489,6 @@ static inline int is_leftmost(struct task_struct *p, struct dl_rq *dl_rq) - - static void init_dl_rq_bw_ratio(struct dl_rq *dl_rq); - --void init_dl_bandwidth(struct dl_bandwidth *dl_b, u64 period, u64 runtime) --{ -- raw_spin_lock_init(&dl_b->dl_runtime_lock); -- dl_b->dl_period = period; -- dl_b->dl_runtime = runtime; --} -- - void init_dl_bw(struct dl_bw *dl_b) - { - raw_spin_lock_init(&dl_b->lock); diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c -index 0b2340a79b65..aeeba46a096b 100644 +index 066ff1c8ae4e..aeeba46a096b 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -427,6 +427,7 @@ static void register_sd(struct sched_domain *sd, struct dentry *parent) @@ -15880,67 +13351,10 @@ index 0b2340a79b65..aeeba46a096b 100644 } void update_sched_domain_debugfs(void) -@@ -777,7 +778,7 @@ static void print_cpu(struct seq_file *m, int cpu) - #define P(x) \ - do { \ - if (sizeof(rq->x) == 4) \ -- SEQ_printf(m, " .%-30s: %ld\n", #x, (long)(rq->x)); \ -+ SEQ_printf(m, " .%-30s: %d\n", #x, (int)(rq->x)); \ - else \ - SEQ_printf(m, " .%-30s: %Ld\n", #x, (long long)(rq->x));\ - } while (0) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c -index 6742b1e1a359..a6205f9e6cb5 100644 +index b097a9f4d817..4039ff46fcb3 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c -@@ -1082,6 +1082,23 @@ update_stats_curr_start(struct cfs_rq *cfs_rq, struct sched_entity *se) - * Scheduling class queueing methods: - */ - -+static inline bool is_core_idle(int cpu) -+{ -+#ifdef CONFIG_SCHED_SMT -+ int sibling; -+ -+ for_each_cpu(sibling, cpu_smt_mask(cpu)) { -+ if (cpu == sibling) -+ continue; -+ -+ if (!idle_cpu(sibling)) -+ return false; -+ } -+#endif -+ -+ return true; -+} -+ - #ifdef CONFIG_NUMA - #define NUMA_IMBALANCE_MIN 2 - -@@ -1718,23 +1735,6 @@ struct numa_stats { - int idle_cpu; - }; - --static inline bool is_core_idle(int cpu) --{ --#ifdef CONFIG_SCHED_SMT -- int sibling; -- -- for_each_cpu(sibling, cpu_smt_mask(cpu)) { -- if (cpu == sibling) -- continue; -- -- if (!idle_cpu(sibling)) -- return false; -- } --#endif -- -- return true; --} -- - struct task_numa_env { - struct task_struct *p; - @@ -4805,6 +4805,7 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int initial) } @@ -16021,58 +13435,17 @@ index 6742b1e1a359..a6205f9e6cb5 100644 list_del_rcu(&cfs_rq->throttled_list); raw_spin_unlock(&cfs_b->lock); -@@ -7063,6 +7094,37 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool - return idle_cpu; - } +@@ -7307,9 +7338,6 @@ cpu_util(int cpu, struct task_struct *p, int dst_cpu, int boost) -+/* -+ * For the multiple-LLC per node case, make sure to try the other LLC's if the -+ * local LLC comes up empty. -+ */ -+static int -+select_idle_node(struct task_struct *p, struct sched_domain *sd, int target) -+{ -+ struct sched_domain *parent = sd->parent; -+ struct sched_group *sg; -+ -+ /* Make sure to not cross nodes. */ -+ if (!parent || parent->flags & SD_NUMA) -+ return -1; -+ -+ sg = parent->groups; -+ do { -+ int cpu = cpumask_first(sched_group_span(sg)); -+ -+ if (!cpus_share_cache(cpu, target)) { -+ int i = select_idle_cpu(p, per_cpu(sd_llc, cpu), -+ test_idle_cores(cpu), cpu); -+ if ((unsigned)i < nr_cpumask_bits) -+ return i; -+ } -+ -+ sg = sg->next; -+ } while (sg != parent->groups); -+ -+ return -1; -+} -+ - /* - * Scan the asym_capacity domain for idle CPUs; pick the first idle one on which - * the task fits. If no CPU is big enough, but there are idle ones, try to -@@ -7235,6 +7297,12 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target) - if ((unsigned)i < nr_cpumask_bits) - return i; + util_est = READ_ONCE(cfs_rq->avg.util_est.enqueued); -+ if (sched_feat(SIS_NODE)) { -+ i = select_idle_node(p, sd, target); -+ if ((unsigned)i < nr_cpumask_bits) -+ return i; -+ } -+ - return target; - } - -@@ -8376,6 +8444,11 @@ enum group_type { +- if (boost) +- util_est = max(util_est, runnable); +- + /* + * During wake-up @p isn't enqueued yet and doesn't contribute + * to any cpu_rq(cpu)->cfs.avg.util_est.enqueued. +@@ -8433,6 +8461,11 @@ enum group_type { * more powerful CPU. */ group_misfit_task, @@ -16084,7 +13457,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 /* * SD_ASYM_PACKING only: One local CPU with higher capacity is available, * and the task should be migrated to it instead of running on the -@@ -9084,6 +9157,7 @@ struct sg_lb_stats { +@@ -9141,6 +9174,7 @@ struct sg_lb_stats { unsigned int group_weight; enum group_type group_type; unsigned int group_asym_packing; /* Tasks should be moved to preferred CPU */ @@ -16092,7 +13465,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 unsigned long group_misfit_task_load; /* A CPU has a task too big for its capacity */ #ifdef CONFIG_NUMA_BALANCING unsigned int nr_numa_running; -@@ -9357,6 +9431,9 @@ group_type group_classify(unsigned int imbalance_pct, +@@ -9414,6 +9448,9 @@ group_type group_classify(unsigned int imbalance_pct, if (sgs->group_asym_packing) return group_asym_packing; @@ -16102,175 +13475,48 @@ index 6742b1e1a359..a6205f9e6cb5 100644 if (sgs->group_misfit_task_load) return group_misfit_task; -@@ -9367,98 +9444,128 @@ group_type group_classify(unsigned int imbalance_pct, +@@ -9483,6 +9520,71 @@ sched_asym(struct lb_env *env, struct sd_lb_stats *sds, struct sg_lb_stats *sgs + return sched_asym_prefer(env->dst_cpu, group->asym_prefer_cpu); } - /** -- * asym_smt_can_pull_tasks - Check whether the load balancing CPU can pull tasks -- * @dst_cpu: Destination CPU of the load balancing -+ * sched_use_asym_prio - Check whether asym_packing priority must be used -+ * @sd: The scheduling domain of the load balancing -+ * @cpu: A CPU -+ * -+ * Always use CPU priority when balancing load between SMT siblings. When -+ * balancing load between cores, it is not sufficient that @cpu is idle. Only -+ * use CPU priority if the whole core is idle. -+ * -+ * Returns: True if the priority of @cpu must be followed. False otherwise. -+ */ -+static bool sched_use_asym_prio(struct sched_domain *sd, int cpu) -+{ -+ if (!sched_smt_active()) -+ return true; -+ -+ return sd->flags & SD_SHARE_CPUCAPACITY || is_core_idle(cpu); -+} -+ -+/** -+ * sched_asym - Check if the destination CPU can do asym_packing load balance -+ * @env: The load balancing environment - * @sds: Load-balancing data with statistics of the local group - * @sgs: Load-balancing statistics of the candidate busiest group -- * @sg: The candidate busiest group -- * -- * Check the state of the SMT siblings of both @sds::local and @sg and decide -- * if @dst_cpu can pull tasks. -+ * @group: The candidate busiest group - * -- * If @dst_cpu does not have SMT siblings, it can pull tasks if two or more of -- * the SMT siblings of @sg are busy. If only one CPU in @sg is busy, pull tasks -- * only if @dst_cpu has higher priority. -+ * @env::dst_cpu can do asym_packing if it has higher priority than the -+ * preferred CPU of @group. - * -- * If both @dst_cpu and @sg have SMT siblings, and @sg has exactly one more -- * busy CPU than @sds::local, let @dst_cpu pull tasks if it has higher priority. -- * Bigger imbalances in the number of busy CPUs will be dealt with in -- * update_sd_pick_busiest(). -+ * SMT is a special case. If we are balancing load between cores, @env::dst_cpu -+ * can do asym_packing balance only if all its SMT siblings are idle. Also, it -+ * can only do it if @group is an SMT group and has exactly on busy CPU. Larger -+ * imbalances in the number of CPUS are dealt with in find_busiest_group(). - * -- * If @sg does not have SMT siblings, only pull tasks if all of the SMT siblings -- * of @dst_cpu are idle and @sg has lower priority. -+ * If we are balancing load within an SMT core, or at DIE domain level, always -+ * proceed. - * -- * Return: true if @dst_cpu can pull tasks, false otherwise. -+ * Return: true if @env::dst_cpu can do with asym_packing load balance. False -+ * otherwise. - */ --static bool asym_smt_can_pull_tasks(int dst_cpu, struct sd_lb_stats *sds, -- struct sg_lb_stats *sgs, -- struct sched_group *sg) -+static inline bool -+sched_asym(struct lb_env *env, struct sd_lb_stats *sds, struct sg_lb_stats *sgs, -+ struct sched_group *group) - { --#ifdef CONFIG_SCHED_SMT -- bool local_is_smt, sg_is_smt; -- int sg_busy_cpus; -- -- local_is_smt = sds->local->flags & SD_SHARE_CPUCAPACITY; -- sg_is_smt = sg->flags & SD_SHARE_CPUCAPACITY; -- -- sg_busy_cpus = sgs->group_weight - sgs->idle_cpus; -- -- if (!local_is_smt) { -- /* -- * If we are here, @dst_cpu is idle and does not have SMT -- * siblings. Pull tasks if candidate group has two or more -- * busy CPUs. -- */ -- if (sg_busy_cpus >= 2) /* implies sg_is_smt */ -- return true; -+ /* Ensure that the whole local core is idle, if applicable. */ -+ if (!sched_use_asym_prio(env->sd, env->dst_cpu)) -+ return false; - -- /* -- * @dst_cpu does not have SMT siblings. @sg may have SMT -- * siblings and only one is busy. In such case, @dst_cpu -- * can help if it has higher priority and is idle (i.e., -- * it has no running tasks). -- */ -- return sched_asym_prefer(dst_cpu, sg->asym_prefer_cpu); -+ /* -+ * CPU priorities does not make sense for SMT cores with more than one -+ * busy sibling. -+ */ -+ if (group->flags & SD_SHARE_CPUCAPACITY) { -+ if (sgs->group_weight - sgs->idle_cpus != 1) -+ return false; - } - -- /* @dst_cpu has SMT siblings. */ -+ return sched_asym_prefer(env->dst_cpu, group->asym_prefer_cpu); -+} - -- if (sg_is_smt) { -- int local_busy_cpus = sds->local->group_weight - -- sds->local_stat.idle_cpus; -- int busy_cpus_delta = sg_busy_cpus - local_busy_cpus; +/* One group has more than one SMT CPU while the other group does not */ +static inline bool smt_vs_nonsmt_groups(struct sched_group *sg1, + struct sched_group *sg2) +{ + if (!sg1 || !sg2) + return false; - -- if (busy_cpus_delta == 1) -- return sched_asym_prefer(dst_cpu, sg->asym_prefer_cpu); ++ + return (sg1->flags & SD_SHARE_CPUCAPACITY) != + (sg2->flags & SD_SHARE_CPUCAPACITY); +} - ++ +static inline bool smt_balance(struct lb_env *env, struct sg_lb_stats *sgs, + struct sched_group *group) +{ + if (env->idle == CPU_NOT_IDLE) - return false; -- } - - /* -- * @sg does not have SMT siblings. Ensure that @sds::local does not end -- * up with more than one busy SMT sibling and only pull tasks if there -- * are not busy CPUs (i.e., no CPU has running tasks). ++ return false; ++ ++ /* + * For SMT source group, it is better to move a task + * to a CPU that doesn't have multiple tasks sharing its CPU capacity. + * Note that if a group has a single SMT, SD_SHARE_CPUCAPACITY + * will not be on. - */ -- if (!sds->local_stat.sum_nr_running) -- return sched_asym_prefer(dst_cpu, sg->asym_prefer_cpu); ++ */ + if (group->flags & SD_SHARE_CPUCAPACITY && + sgs->sum_h_nr_running > 1) + return true; - - return false; --#else -- /* Always return false so that callers deal with non-SMT cases. */ -- return false; --#endif - } - --static inline bool --sched_asym(struct lb_env *env, struct sd_lb_stats *sds, struct sg_lb_stats *sgs, -- struct sched_group *group) ++ ++ return false; ++} ++ +static inline long sibling_imbalance(struct lb_env *env, + struct sd_lb_stats *sds, + struct sg_lb_stats *busiest, + struct sg_lb_stats *local) - { -- /* Only do SMT checks if either local or candidate have SMT siblings */ -- if ((sds->local->flags & SD_SHARE_CPUCAPACITY) || -- (group->flags & SD_SHARE_CPUCAPACITY)) -- return asym_smt_can_pull_tasks(env->dst_cpu, sds, sgs, group); ++{ + int ncores_busiest, ncores_local; + long imbalance; - -- return sched_asym_prefer(env->dst_cpu, group->asym_prefer_cpu); ++ + if (env->idle == CPU_NOT_IDLE || !busiest->sum_nr_running) + return 0; + @@ -16296,10 +13542,12 @@ index 6742b1e1a359..a6205f9e6cb5 100644 + imbalance = 2; + + return imbalance; - } - ++} ++ static inline bool -@@ -9553,6 +9660,10 @@ static inline void update_sg_lb_stats(struct lb_env *env, + sched_reduced_capacity(struct rq *rq, struct sched_domain *sd) + { +@@ -9575,6 +9677,10 @@ static inline void update_sg_lb_stats(struct lb_env *env, sgs->group_asym_packing = 1; } @@ -16310,7 +13558,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 sgs->group_type = group_classify(env->sd->imbalance_pct, group, sgs); /* Computing avg_load makes sense only when group is overloaded */ -@@ -9637,6 +9748,7 @@ static bool update_sd_pick_busiest(struct lb_env *env, +@@ -9659,6 +9765,7 @@ static bool update_sd_pick_busiest(struct lb_env *env, return false; break; @@ -16318,28 +13566,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 case group_fully_busy: /* * Select the fully busy group with highest avg_load. In -@@ -9646,13 +9758,37 @@ static bool update_sd_pick_busiest(struct lb_env *env, - * contention when accessing shared HW resources. - * - * XXX for now avg_load is not computed and always 0 so we -- * select the 1st one. -+ * select the 1st one, except if @sg is composed of SMT -+ * siblings. - */ -- if (sgs->avg_load <= busiest->avg_load) -+ -+ if (sgs->avg_load < busiest->avg_load) - return false; -+ -+ if (sgs->avg_load == busiest->avg_load) { -+ /* -+ * SMT sched groups need more help than non-SMT groups. -+ * If @sg happens to also be SMT, either choice is good. -+ */ -+ if (sds->busiest->flags & SD_SHARE_CPUCAPACITY) -+ return false; -+ } -+ +@@ -9687,6 +9794,18 @@ static bool update_sd_pick_busiest(struct lb_env *env, break; case group_has_spare: @@ -16358,7 +13585,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 /* * Select not overloaded group with lowest number of idle cpus * and highest number of running tasks. We could also compare -@@ -9849,6 +9985,7 @@ static bool update_pick_idlest(struct sched_group *idlest, +@@ -9883,6 +10002,7 @@ static bool update_pick_idlest(struct sched_group *idlest, case group_imbalanced: case group_asym_packing: @@ -16366,7 +13593,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 /* Those types are not used in the slow wakeup path */ return false; -@@ -9980,6 +10117,7 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p, int this_cpu) +@@ -10014,6 +10134,7 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p, int this_cpu) case group_imbalanced: case group_asym_packing: @@ -16374,31 +13601,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 /* Those type are not used in the slow wakeup path */ return NULL; -@@ -10124,7 +10262,6 @@ static void update_idle_cpu_scan(struct lb_env *env, - - static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sds) - { -- struct sched_domain *child = env->sd->child; - struct sched_group *sg = env->sd->groups; - struct sg_lb_stats *local = &sds->local_stat; - struct sg_lb_stats tmp_sgs; -@@ -10165,8 +10302,13 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd - sg = sg->next; - } while (sg != env->sd->groups); - -- /* Tag domain that child domain prefers tasks go to siblings first */ -- sds->prefer_sibling = child && child->flags & SD_PREFER_SIBLING; -+ /* -+ * Indicate that the child domain of the busiest group prefers tasks -+ * go to a child's sibling domains first. NB the flags of a sched group -+ * are those of the child domain. -+ */ -+ if (sds->busiest) -+ sds->prefer_sibling = !!(sds->busiest->flags & SD_PREFER_SIBLING); - - - if (env->sd->flags & SD_NUMA) -@@ -10230,6 +10372,13 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s +@@ -10268,6 +10389,13 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s return; } @@ -16412,7 +13615,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 if (busiest->group_type == group_imbalanced) { /* * In the group_imb case we cannot rely on group-wide averages -@@ -10277,14 +10426,12 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s +@@ -10315,14 +10443,12 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s } if (busiest->group_weight == 1 || sds->prefer_sibling) { @@ -16428,15 +13631,9 @@ index 6742b1e1a359..a6205f9e6cb5 100644 } else { /* -@@ -10476,22 +10623,32 @@ static struct sched_group *find_busiest_group(struct lb_env *env) - goto out_balanced; - } - -- /* Try to move all excess tasks to child's sibling domain */ -+ /* -+ * Try to move all excess tasks to a sibling domain of the busiest -+ * group's child domain. -+ */ +@@ -10519,20 +10645,27 @@ static struct sched_group *find_busiest_group(struct lb_env *env) + * group's child domain. + */ if (sds.prefer_sibling && local->group_type == group_has_spare && - busiest->sum_nr_running > local->sum_nr_running + 1) + sibling_imbalance(env, &sds, busiest, local) > 1) @@ -16465,7 +13662,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 /* * If the busiest group is not overloaded * and there is no imbalance between this and busiest -@@ -10502,12 +10659,14 @@ static struct sched_group *find_busiest_group(struct lb_env *env) +@@ -10543,12 +10676,14 @@ static struct sched_group *find_busiest_group(struct lb_env *env) * there is more than 1 CPU per group. */ goto out_balanced; @@ -16481,48 +13678,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 } force_balance: -@@ -10578,8 +10737,15 @@ static struct rq *find_busiest_queue(struct lb_env *env, - nr_running == 1) - continue; - -- /* Make sure we only pull tasks from a CPU of lower priority */ -+ /* -+ * Make sure we only pull tasks from a CPU of lower priority -+ * when balancing between SMT siblings. -+ * -+ * If balancing between cores, let lower priority CPUs help -+ * SMT cores with more than one busy sibling. -+ */ - if ((env->sd->flags & SD_ASYM_PACKING) && -+ sched_use_asym_prio(env->sd, i) && - sched_asym_prefer(i, env->dst_cpu) && - nr_running == 1) - continue; -@@ -10668,12 +10834,19 @@ static inline bool - asym_active_balance(struct lb_env *env) - { - /* -- * ASYM_PACKING needs to force migrate tasks from busy but -- * lower priority CPUs in order to pack all tasks in the -- * highest priority CPUs. -+ * ASYM_PACKING needs to force migrate tasks from busy but lower -+ * priority CPUs in order to pack all tasks in the highest priority -+ * CPUs. When done between cores, do it only if the whole core if the -+ * whole core is idle. -+ * -+ * If @env::src_cpu is an SMT core with busy siblings, let -+ * the lower priority @env::dst_cpu help it. Do not follow -+ * CPU priority. - */ - return env->idle != CPU_NOT_IDLE && (env->sd->flags & SD_ASYM_PACKING) && -- sched_asym_prefer(env->dst_cpu, env->src_cpu); -+ sched_use_asym_prio(env->sd, env->dst_cpu) && -+ (sched_asym_prefer(env->dst_cpu, env->src_cpu) || -+ !sched_use_asym_prio(env->sd, env->src_cpu)); - } - - static inline bool -@@ -10727,7 +10900,7 @@ static int active_load_balance_cpu_stop(void *data); +@@ -10782,7 +10917,7 @@ static int active_load_balance_cpu_stop(void *data); static int should_we_balance(struct lb_env *env) { struct sched_group *sg = env->sd->groups; @@ -16531,7 +13687,7 @@ index 6742b1e1a359..a6205f9e6cb5 100644 /* * Ensure the balancing environment is consistent; can happen -@@ -10754,10 +10927,24 @@ static int should_we_balance(struct lb_env *env) +@@ -10809,10 +10944,24 @@ static int should_we_balance(struct lb_env *env) if (!idle_cpu(cpu)) continue; @@ -16556,44 +13712,8 @@ index 6742b1e1a359..a6205f9e6cb5 100644 /* Are we the first CPU of this group ? */ return group_balance_cpu(sg) == env->dst_cpu; } -@@ -10780,7 +10967,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, - .sd = sd, - .dst_cpu = this_cpu, - .dst_rq = this_rq, -- .dst_grpmask = sched_group_span(sd->groups), -+ .dst_grpmask = group_balance_mask(sd->groups), - .idle = idle, - .loop_break = SCHED_NR_MIGRATE_BREAK, - .cpus = cpus, -@@ -11407,9 +11594,13 @@ static void nohz_balancer_kick(struct rq *rq) - * When ASYM_PACKING; see if there's a more preferred CPU - * currently idle; in which case, kick the ILB to move tasks - * around. -+ * -+ * When balancing betwen cores, all the SMT siblings of the -+ * preferred CPU must be idle. - */ - for_each_cpu_and(i, sched_domain_span(sd), nohz.idle_cpus_mask) { -- if (sched_asym_prefer(i, cpu)) { -+ if (sched_use_asym_prio(sd, i) && -+ sched_asym_prefer(i, cpu)) { - flags = NOHZ_STATS_KICK | NOHZ_BALANCE_KICK; - goto unlock; - } -diff --git a/kernel/sched/features.h b/kernel/sched/features.h -index ee7f23c76bd3..9e390eb82e38 100644 ---- a/kernel/sched/features.h -+++ b/kernel/sched/features.h -@@ -62,6 +62,7 @@ SCHED_FEAT(TTWU_QUEUE, true) - */ - SCHED_FEAT(SIS_PROP, false) - SCHED_FEAT(SIS_UTIL, true) -+SCHED_FEAT(SIS_NODE, true) - - /* - * Issue a WARN when we do multiple update_rq_clock() calls diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c -index e072f6b31bf3..2ccb0b2ebd78 100644 +index 81fca77397f6..2ccb0b2ebd78 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -140,7 +140,7 @@ @@ -16605,64 +13725,11 @@ index e072f6b31bf3..2ccb0b2ebd78 100644 #ifdef CONFIG_PSI_DEFAULT_DISABLED static bool psi_enable; -@@ -160,7 +160,6 @@ __setup("psi=", setup_psi); - #define EXP_300s 2034 /* 1/exp(2s/300s) */ - - /* PSI trigger definitions */ --#define WINDOW_MIN_US 500000 /* Min window size is 500ms */ - #define WINDOW_MAX_US 10000000 /* Max window size is 10s */ - #define UPDATES_PER_WINDOW 10 /* 10 updates per window */ - -@@ -1305,8 +1304,7 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, - if (state >= PSI_NONIDLE) - return ERR_PTR(-EINVAL); - -- if (window_us < WINDOW_MIN_US || -- window_us > WINDOW_MAX_US) -+ if (window_us == 0 || window_us > WINDOW_MAX_US) - return ERR_PTR(-EINVAL); - - /* -@@ -1409,11 +1407,16 @@ void psi_trigger_destroy(struct psi_trigger *t) - group->rtpoll_nr_triggers[t->state]--; - if (!group->rtpoll_nr_triggers[t->state]) - group->rtpoll_states &= ~(1 << t->state); -- /* reset min update period for the remaining triggers */ -- list_for_each_entry(tmp, &group->rtpoll_triggers, node) -- period = min(period, div_u64(tmp->win.size, -- UPDATES_PER_WINDOW)); -- group->rtpoll_min_period = period; -+ /* -+ * Reset min update period for the remaining triggers -+ * iff the destroying trigger had the min window size. -+ */ -+ if (group->rtpoll_min_period == div_u64(t->win.size, UPDATES_PER_WINDOW)) { -+ list_for_each_entry(tmp, &group->rtpoll_triggers, node) -+ period = min(period, div_u64(tmp->win.size, -+ UPDATES_PER_WINDOW)); -+ group->rtpoll_min_period = period; -+ } - /* Destroy rtpoll_task when the last trigger is destroyed */ - if (group->rtpoll_states == 0) { - group->rtpoll_until = 0; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h -index 81ac605b9cd5..a6e814eb84cd 100644 +index e93e006a942b..9baeb1a2dfdd 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h -@@ -286,12 +286,6 @@ struct rt_bandwidth { - - void __dl_clear_params(struct task_struct *p); - --struct dl_bandwidth { -- raw_spinlock_t dl_runtime_lock; -- u64 dl_runtime; -- u64 dl_period; --}; -- - static inline int dl_bandwidth_enabled(void) - { - return sysctl_sched_rt_runtime >= 0; -@@ -642,6 +636,8 @@ struct cfs_rq { +@@ -636,6 +636,8 @@ struct cfs_rq { u64 throttled_clock; u64 throttled_clock_pelt; u64 throttled_clock_pelt_time; @@ -16671,50 +13738,7 @@ index 81ac605b9cd5..a6e814eb84cd 100644 int throttled; int throttle_count; struct list_head throttled_list; -@@ -1794,6 +1790,13 @@ queue_balance_callback(struct rq *rq, - for (__sd = rcu_dereference_check_sched_domain(cpu_rq(cpu)->sd); \ - __sd; __sd = __sd->parent) - -+/* A mask of all the SD flags that have the SDF_SHARED_CHILD metaflag */ -+#define SD_FLAG(name, mflags) (name * !!((mflags) & SDF_SHARED_CHILD)) | -+static const unsigned int SD_SHARED_CHILD_MASK = -+#include -+0; -+#undef SD_FLAG -+ - /** - * highest_flag_domain - Return highest sched_domain containing flag. - * @cpu: The CPU whose highest level of sched domain is to -@@ -1801,16 +1804,25 @@ queue_balance_callback(struct rq *rq, - * @flag: The flag to check for the highest sched_domain - * for the given CPU. - * -- * Returns the highest sched_domain of a CPU which contains the given flag. -+ * Returns the highest sched_domain of a CPU which contains @flag. If @flag has -+ * the SDF_SHARED_CHILD metaflag, all the children domains also have @flag. - */ - static inline struct sched_domain *highest_flag_domain(int cpu, int flag) - { - struct sched_domain *sd, *hsd = NULL; - - for_each_domain(cpu, sd) { -- if (!(sd->flags & flag)) -+ if (sd->flags & flag) { -+ hsd = sd; -+ continue; -+ } -+ -+ /* -+ * Stop the search if @flag is known to be shared at lower -+ * levels. It will not be found further up. -+ */ -+ if (flag & SD_SHARED_CHILD_MASK) - break; -- hsd = sd; - } - - return hsd; -@@ -1866,6 +1878,7 @@ struct sched_group { +@@ -1882,6 +1884,7 @@ struct sched_group { atomic_t ref; unsigned int group_weight; @@ -16722,34 +13746,11 @@ index 81ac605b9cd5..a6e814eb84cd 100644 struct sched_group_capacity *sgc; int asym_prefer_cpu; /* CPU of highest priority in group */ int flags; -@@ -2400,7 +2413,6 @@ extern struct rt_bandwidth def_rt_bandwidth; - extern void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime); - extern bool sched_rt_bandwidth_account(struct rt_rq *rt_rq); - --extern void init_dl_bandwidth(struct dl_bandwidth *dl_b, u64 period, u64 runtime); - extern void init_dl_task_timer(struct sched_dl_entity *dl_se); - extern void init_dl_inactive_task_timer(struct sched_dl_entity *dl_se); - diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c -index 6682535e37c8..8c7c0b64e615 100644 +index d3a3b2646ec4..4bbe1631d950 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c -@@ -719,8 +719,13 @@ cpu_attach_domain(struct sched_domain *sd, struct root_domain *rd, int cpu) - - if (sd_parent_degenerate(tmp, parent)) { - tmp->parent = parent->parent; -- if (parent->parent) -+ -+ if (parent->parent) { - parent->parent->child = tmp; -+ if (tmp->flags & SD_SHARE_CPUCAPACITY) -+ parent->parent->groups->flags |= SD_SHARE_CPUCAPACITY; -+ } -+ - /* - * Transfer SD_PREFER_SIBLING down in case of a - * degenerate parent; the spans match for this -@@ -1270,14 +1275,26 @@ build_sched_groups(struct sched_domain *sd, int cpu) +@@ -1275,14 +1275,26 @@ build_sched_groups(struct sched_domain *sd, int cpu) static void init_sched_groups_capacity(int cpu, struct sched_domain *sd) { struct sched_group *sg = sd->groups; @@ -16778,10 +13779,10 @@ index 6682535e37c8..8c7c0b64e615 100644 goto next; diff --git a/kernel/softirq.c b/kernel/softirq.c -index 1b725510dd0f..a5758661875c 100644 +index 807b34ccd797..210cf5f8d92c 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c -@@ -630,7 +630,7 @@ static inline void tick_irq_exit(void) +@@ -612,7 +612,7 @@ static inline void tick_irq_exit(void) int cpu = smp_processor_id(); /* Make sure that timer wheel updates are propagated */ @@ -16793,16 +13794,16 @@ index 1b725510dd0f..a5758661875c 100644 -- 2.41.0 -From 691757954b7447086955a7d38e525c4be37c9a67 Mon Sep 17 00:00:00 2001 +From 06e0e78e6ce4cea4215ba00474d011f49a3ff8f5 Mon Sep 17 00:00:00 2001 From: Peter Jung -Date: Wed, 19 Jul 2023 18:52:42 +0200 -Subject: [PATCH 8/8] zstd +Date: Mon, 10 Jul 2023 17:11:55 +0200 +Subject: [PATCH 7/7] zstd Signed-off-by: Peter Jung --- include/linux/zstd.h | 2 +- include/linux/zstd_errors.h | 23 +- - include/linux/zstd_lib.h | 703 +++++-- + include/linux/zstd_lib.h | 697 +++++-- lib/zstd/Makefile | 2 +- lib/zstd/common/allocations.h | 56 + lib/zstd/common/bits.h | 149 ++ @@ -16820,7 +13821,7 @@ Signed-off-by: Peter Jung lib/zstd/common/mem.h | 2 +- lib/zstd/common/portability_macros.h | 26 +- lib/zstd/common/zstd_common.c | 38 +- - lib/zstd/common/zstd_deps.h | 2 +- + lib/zstd/common/zstd_deps.h | 20 +- lib/zstd/common/zstd_internal.h | 99 +- lib/zstd/compress/clevels.h | 3 +- lib/zstd/compress/fse_compress.c | 59 +- @@ -16850,15 +13851,15 @@ Signed-off-by: Peter Jung lib/zstd/decompress/huf_decompress.c | 731 ++++--- lib/zstd/decompress/zstd_ddict.c | 9 +- lib/zstd/decompress/zstd_ddict.h | 3 +- - lib/zstd/decompress/zstd_decompress.c | 269 ++- + lib/zstd/decompress/zstd_decompress.c | 261 ++- lib/zstd/decompress/zstd_decompress_block.c | 283 ++- lib/zstd/decompress/zstd_decompress_block.h | 8 +- .../decompress/zstd_decompress_internal.h | 7 +- lib/zstd/decompress_sources.h | 2 +- lib/zstd/zstd_common_module.c | 5 +- lib/zstd/zstd_compress_module.c | 2 +- - lib/zstd/zstd_decompress_module.c | 4 +- - 58 files changed, 4731 insertions(+), 2611 deletions(-) + lib/zstd/zstd_decompress_module.c | 2 +- + 58 files changed, 4751 insertions(+), 2593 deletions(-) create mode 100644 lib/zstd/common/allocations.h create mode 100644 lib/zstd/common/bits.h @@ -16941,7 +13942,7 @@ index 58b6dd45a969..6d5cf55f0bf3 100644 } ZSTD_ErrorCode; diff --git a/include/linux/zstd_lib.h b/include/linux/zstd_lib.h -index 79d55465d5c1..738fe8ea4ead 100644 +index 79d55465d5c1..8b4ffe649df5 100644 --- a/include/linux/zstd_lib.h +++ b/include/linux/zstd_lib.h @@ -1,5 +1,6 @@ @@ -17777,7 +14778,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /*! -@@ -2330,27 +2595,185 @@ ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const vo +@@ -2330,8 +2595,8 @@ ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const vo * ZSTD_DCtx_refDDict(zds, ddict); * * note : ddict is referenced, it must outlive decompression session @@ -17786,16 +14787,17 @@ index 79d55465d5c1..738fe8ea4ead 100644 +ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_refDDict, see zstd.h for detailed instructions") ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); --/*! -- * This function is deprecated, and is equivalent to: -- * -- * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); -- * -- * re-use decompression parameters from previous init; saves dictionary loading + /*! +@@ -2340,17 +2605,185 @@ ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const Z + * ZSTD_DCtx_reset(zds, ZSTD_reset_session_only); + * + * re-use decompression parameters from previous init; saves dictionary loading - * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x -- */ --ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); -+ + */ ++ZSTD_DEPRECATED("use ZSTD_DCtx_reset, see zstd.h for detailed instructions") + ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); + + +/* ********************* BLOCK-LEVEL SEQUENCE PRODUCER API ********************* + * + * *** OVERVIEW *** @@ -17956,20 +14958,20 @@ index 79d55465d5c1..738fe8ea4ead 100644 + void* sequenceProducerState, + ZSTD_sequenceProducer_F* sequenceProducer +); - - ++ ++ /* ******************************************************************* -* Buffer-less and synchronous inner streaming functions +* Buffer-less and synchronous inner streaming functions (DEPRECATED) - * --* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. --* But it's also a complex one, with several restrictions, documented below. --* Prefer normal streaming API for an easier experience. ++* +* This API is deprecated, and will be removed in a future version. +* It allows streaming (de)compression with user allocated buffers. +* However, it is hard to use, and not as well tested as the rest of +* our API. -+* + * +-* This is an advanced API, giving full control over buffer management, for users which need direct control over memory. +-* But it's also a complex one, with several restrictions, documented below. +-* Prefer normal streaming API for an easier experience. +* Please use the normal streaming API instead: ZSTD_compressStream2, +* and ZSTD_decompressStream. +* If there is functionality that you need, but it doesn't provide, @@ -17977,7 +14979,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 ********************************************************************* */ /* -@@ -2362,7 +2785,6 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); +@@ -2362,7 +2795,6 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); Start by initializing a context. Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression. @@ -17985,7 +14987,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 Then, consume your input using ZSTD_compressContinue(). There are some important considerations to keep in mind when using this advanced function : -@@ -2384,18 +2806,28 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); +@@ -2384,18 +2816,28 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); */ /*===== Buffer-less streaming compression functions =====*/ @@ -18015,7 +15017,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ /* Buffer-less streaming decompression (synchronous mode) -@@ -2408,8 +2840,8 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ +@@ -2408,8 +2850,8 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough. Data fragment must be large enough to ensure successful decoding. `ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough. @@ -18026,7 +15028,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 errorCode, which can be tested using ZSTD_isError(). It fills a ZSTD_frameHeader structure with important information to correctly decode the frame, -@@ -2428,7 +2860,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ +@@ -2428,7 +2870,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ The most memory efficient way is to use a round buffer of sufficient size. Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(), @@ -18035,7 +15037,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one, up to the moment there is not enough room left in the buffer to guarantee decoding another full block, which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`. -@@ -2448,7 +2880,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ +@@ -2448,7 +2890,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue(). ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail. @@ -18044,7 +15046,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item. It can also be an error code, which can be tested with ZSTD_isError(). -@@ -2471,27 +2903,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ +@@ -2471,27 +2913,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_ */ /*===== Buffer-less streaming decompression functions =====*/ @@ -18072,7 +15074,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /*< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */ ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx); -@@ -2502,6 +2914,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); +@@ -2502,6 +2924,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx); ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); /* misc */ @@ -18080,7 +15082,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx); typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e; ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); -@@ -2509,11 +2922,23 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); +@@ -2509,11 +2932,23 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); @@ -18107,7 +15109,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 Block functions produce and decode raw zstd blocks, without frame metadata. Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes). But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes. -@@ -2524,7 +2949,6 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); +@@ -2524,7 +2959,6 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); - It is necessary to init context before starting + compression : any ZSTD_compressBegin*() variant, including with dictionary + decompression : any ZSTD_decompressBegin*() variant, including with dictionary @@ -18115,7 +15117,7 @@ index 79d55465d5c1..738fe8ea4ead 100644 - Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB + If input is larger than a block size, it's necessary to split input data into multiple blocks + For inputs larger than a single block, consider using regular ZSTD_compress() instead. -@@ -2541,11 +2965,14 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); +@@ -2541,11 +2975,14 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx); */ /*===== Raw zstd block functions =====*/ @@ -19499,7 +16501,7 @@ index 3d7e35b309b5..44b95b25344a 100644 - } -} diff --git a/lib/zstd/common/zstd_deps.h b/lib/zstd/common/zstd_deps.h -index f06df065dec0..670c5fa2a952 100644 +index 2c34e8a33a1c..670c5fa2a952 100644 --- a/lib/zstd/common/zstd_deps.h +++ b/lib/zstd/common/zstd_deps.h @@ -1,6 +1,6 @@ @@ -19510,6 +16512,28 @@ index f06df065dec0..670c5fa2a952 100644 * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the +@@ -105,3 +105,21 @@ static uint64_t ZSTD_div64(uint64_t dividend, uint32_t divisor) { + + #endif /* ZSTD_DEPS_IO */ + #endif /* ZSTD_DEPS_NEED_IO */ ++ ++/* ++ * Only requested when MSAN is enabled. ++ * Need: ++ * intptr_t ++ */ ++#ifdef ZSTD_DEPS_NEED_STDINT ++#ifndef ZSTD_DEPS_STDINT ++#define ZSTD_DEPS_STDINT ++ ++/* ++ * The Linux Kernel doesn't provide intptr_t, only uintptr_t, which ++ * is an unsigned long. ++ */ ++typedef long intptr_t; ++ ++#endif /* ZSTD_DEPS_STDINT */ ++#endif /* ZSTD_DEPS_NEED_STDINT */ diff --git a/lib/zstd/common/zstd_internal.h b/lib/zstd/common/zstd_internal.h index 93305d9b41bb..7f023e4d4774 100644 --- a/lib/zstd/common/zstd_internal.h @@ -29333,7 +26357,7 @@ index 8c1a79d666f8..de459a0dacd1 100644 * * This source code is licensed under both the BSD-style license (found in the diff --git a/lib/zstd/decompress/zstd_decompress.c b/lib/zstd/decompress/zstd_decompress.c -index 6b3177c94711..5e2a3ef03732 100644 +index 6b3177c94711..03dbdf39109f 100644 --- a/lib/zstd/decompress/zstd_decompress.c +++ b/lib/zstd/decompress/zstd_decompress.c @@ -1,5 +1,6 @@ @@ -29712,7 +26736,7 @@ index 6b3177c94711..5e2a3ef03732 100644 } /* ZSTD_initDStream_usingDDict() : -@@ -1589,20 +1664,12 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds) +@@ -1589,6 +1664,7 @@ size_t ZSTD_initDStream(ZSTD_DStream* zds) * this function cannot fail */ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) { @@ -29720,21 +26744,15 @@ index 6b3177c94711..5e2a3ef03732 100644 FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) , ""); FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) , ""); return ZSTD_startingInputLength(dctx->format); - } - --/* ZSTD_resetDStream() : -- * return : expected size, aka ZSTD_startingInputLength(). -- * this function cannot fail */ --size_t ZSTD_resetDStream(ZSTD_DStream* dctx) --{ -- FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only), ""); -- return ZSTD_startingInputLength(dctx->format); --} -- - - size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict) +@@ -1599,6 +1675,7 @@ size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict) + * this function cannot fail */ + size_t ZSTD_resetDStream(ZSTD_DStream* dctx) { -@@ -1670,6 +1737,11 @@ ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam) ++ DEBUGLOG(4, "ZSTD_resetDStream"); + FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only), ""); + return ZSTD_startingInputLength(dctx->format); + } +@@ -1670,6 +1747,11 @@ ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam) bounds.lowerBound = (int)ZSTD_rmd_refSingleDDict; bounds.upperBound = (int)ZSTD_rmd_refMultipleDDicts; return bounds; @@ -29746,7 +26764,7 @@ index 6b3177c94711..5e2a3ef03732 100644 default:; } bounds.error = ERROR(parameter_unsupported); -@@ -1710,6 +1782,9 @@ size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value +@@ -1710,6 +1792,9 @@ size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value case ZSTD_d_refMultipleDDicts: *value = (int)dctx->refMultipleDDicts; return 0; @@ -29756,7 +26774,7 @@ index 6b3177c94711..5e2a3ef03732 100644 default:; } RETURN_ERROR(parameter_unsupported, ""); -@@ -1743,6 +1818,10 @@ size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value +@@ -1743,6 +1828,10 @@ size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value } dctx->refMultipleDDicts = (ZSTD_refMultipleDDicts_e)value; return 0; @@ -29767,7 +26785,7 @@ index 6b3177c94711..5e2a3ef03732 100644 default:; } RETURN_ERROR(parameter_unsupported, ""); -@@ -1918,7 +1997,6 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -1918,7 +2007,6 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if (zds->refMultipleDDicts && zds->ddictSet) { ZSTD_DCtx_selectFrameDDict(zds); } @@ -29775,7 +26793,7 @@ index 6b3177c94711..5e2a3ef03732 100644 if (ZSTD_isError(hSize)) { return hSize; /* error */ } -@@ -1932,6 +2010,11 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -1932,6 +2020,11 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB zds->lhSize += remainingInput; } input->pos = input->size; @@ -29787,7 +26805,7 @@ index 6b3177c94711..5e2a3ef03732 100644 return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ } assert(ip != NULL); -@@ -1949,8 +2032,9 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -1949,8 +2042,9 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, (size_t)(oend-op), istart, cSize, ZSTD_getDDict(zds)); if (ZSTD_isError(decompressedSize)) return decompressedSize; DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") @@ -29798,7 +26816,7 @@ index 6b3177c94711..5e2a3ef03732 100644 zds->expected = 0; zds->streamStage = zdss_init; someMoreWork = 0; -@@ -2034,6 +2118,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -2034,6 +2128,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */ FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, ip, neededInSize), ""); @@ -29806,7 +26824,7 @@ index 6b3177c94711..5e2a3ef03732 100644 ip += neededInSize; /* Function modifies the stage so we must break */ break; -@@ -2048,7 +2133,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -2048,7 +2143,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB int const isSkipFrame = ZSTD_isSkipFrame(zds); size_t loadedSize; /* At this point we shouldn't be decompressing a block that we can stream. */ @@ -29815,7 +26833,7 @@ index 6b3177c94711..5e2a3ef03732 100644 if (isSkipFrame) { loadedSize = MIN(toLoad, (size_t)(iend-ip)); } else { -@@ -2057,8 +2142,11 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -2057,8 +2152,11 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB "should never happen"); loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, (size_t)(iend-ip)); } @@ -29829,7 +26847,7 @@ index 6b3177c94711..5e2a3ef03732 100644 if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */ /* decode loaded input */ -@@ -2068,14 +2156,17 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -2068,14 +2166,17 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB break; } case zdss_flush: @@ -29850,7 +26868,7 @@ index 6b3177c94711..5e2a3ef03732 100644 DEBUGLOG(5, "restart filling outBuff from beginning (left:%i, needed:%u)", (int)(zds->outBuffSize - zds->outStart), (U32)zds->fParams.blockSizeMax); -@@ -2089,7 +2180,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -2089,7 +2190,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB default: assert(0); /* impossible */ @@ -29859,7 +26877,7 @@ index 6b3177c94711..5e2a3ef03732 100644 } } /* result */ -@@ -2102,8 +2193,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB +@@ -2102,8 +2203,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB if ((ip==istart) && (op==ostart)) { /* no forward progress */ zds->noForwardProgress ++; if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) { @@ -29870,7 +26888,7 @@ index 6b3177c94711..5e2a3ef03732 100644 assert(0); } } else { -@@ -2140,11 +2231,17 @@ size_t ZSTD_decompressStream_simpleArgs ( +@@ -2140,11 +2241,17 @@ size_t ZSTD_decompressStream_simpleArgs ( void* dst, size_t dstCapacity, size_t* dstPos, const void* src, size_t srcSize, size_t* srcPos) { @@ -30537,7 +27555,7 @@ index 04e1b5c01d9b..8ecf43226af2 100644 * * This source code is licensed under both the BSD-style license (found in the diff --git a/lib/zstd/zstd_decompress_module.c b/lib/zstd/zstd_decompress_module.c -index f4ed952ed485..7d31518e9d5a 100644 +index f4ed952ed485..eb1c49e69722 100644 --- a/lib/zstd/zstd_decompress_module.c +++ b/lib/zstd/zstd_decompress_module.c @@ -1,6 +1,6 @@ @@ -30548,14 +27566,5 @@ index f4ed952ed485..7d31518e9d5a 100644 * All rights reserved. * * This source code is licensed under both the BSD-style license (found in the -@@ -77,7 +77,7 @@ EXPORT_SYMBOL(zstd_init_dstream); - - size_t zstd_reset_dstream(zstd_dstream *dstream) - { -- return ZSTD_resetDStream(dstream); -+ return ZSTD_DCtx_reset(dstream, ZSTD_reset_session_only); - } - EXPORT_SYMBOL(zstd_reset_dstream); - -- 2.41.0 diff --git a/patches/0002-eevdf.patch b/patches/0002-eevdf.patch index 8fd8ec9..590e6a9 100644 --- a/patches/0002-eevdf.patch +++ b/patches/0002-eevdf.patch @@ -1,7 +1,7 @@ -From 5c15cb285591295dbbe5da9d7d957fa36e49db0b Mon Sep 17 00:00:00 2001 +From 0af97bb369de3bfe15d724e9bb0e3c971c6f9f20 Mon Sep 17 00:00:00 2001 From: Peter Jung -Date: Wed, 19 Jul 2023 18:55:28 +0200 -Subject: [PATCH] EEVDF +Date: Mon, 10 Jul 2023 17:12:45 +0200 +Subject: [PATCH] EEVDF-cachy Signed-off-by: Peter Jung --- @@ -20,7 +20,7 @@ Signed-off-by: Peter Jung 12 files changed, 733 insertions(+), 658 deletions(-) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst -index e592a93644739..c826ab4e2e1a1 100644 +index 4ef890191196..3a8d3e1e5591 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -1121,6 +1121,16 @@ All time durations are in microseconds. @@ -41,7 +41,7 @@ index e592a93644739..c826ab4e2e1a1 100644 Memory diff --git a/include/linux/rbtree_augmented.h b/include/linux/rbtree_augmented.h -index 7ee7ed5de7227..6dbc5a1bf6a8c 100644 +index 7ee7ed5de722..6dbc5a1bf6a8 100644 --- a/include/linux/rbtree_augmented.h +++ b/include/linux/rbtree_augmented.h @@ -60,6 +60,32 @@ rb_insert_augmented_cached(struct rb_node *node, @@ -78,10 +78,10 @@ index 7ee7ed5de7227..6dbc5a1bf6a8c 100644 * Template for declaring augmented rbtree callbacks (generic case) * diff --git a/include/linux/sched.h b/include/linux/sched.h -index 8473324705caa..88c3e7ba8992e 100644 +index efc9f4bdc4ca..e99a9aa6a972 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -550,13 +550,18 @@ struct sched_entity { +@@ -549,13 +549,18 @@ struct sched_entity { /* For load-balancing: */ struct load_weight load; struct rb_node run_node; @@ -101,7 +101,7 @@ index 8473324705caa..88c3e7ba8992e 100644 u64 nr_migrations; -@@ -786,6 +791,7 @@ struct task_struct { +@@ -785,6 +790,7 @@ struct task_struct { int static_prio; int normal_prio; unsigned int rt_priority; @@ -110,7 +110,7 @@ index 8473324705caa..88c3e7ba8992e 100644 struct sched_entity se; struct sched_rt_entity rt; diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h -index 3bac0a8ceab26..b2e932c25be62 100644 +index 3bac0a8ceab2..b2e932c25be6 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -132,6 +132,7 @@ struct clone_args { @@ -131,7 +131,7 @@ index 3bac0a8ceab26..b2e932c25be62 100644 #endif /* _UAPI_LINUX_SCHED_H */ diff --git a/include/uapi/linux/sched/types.h b/include/uapi/linux/sched/types.h -index f2c4589d4dbfe..db1e8199e8c80 100644 +index f2c4589d4dbf..db1e8199e8c8 100644 --- a/include/uapi/linux/sched/types.h +++ b/include/uapi/linux/sched/types.h @@ -10,6 +10,7 @@ struct sched_param { @@ -175,7 +175,7 @@ index f2c4589d4dbfe..db1e8199e8c80 100644 #endif /* _UAPI_LINUX_SCHED_TYPES_H */ diff --git a/init/init_task.c b/init/init_task.c -index ff6c4b9bfe6b1..511cbcf3510dc 100644 +index ff6c4b9bfe6b..511cbcf3510d 100644 --- a/init/init_task.c +++ b/init/init_task.c @@ -78,6 +78,7 @@ struct task_struct init_task @@ -196,7 +196,7 @@ index ff6c4b9bfe6b1..511cbcf3510dc 100644 .rt = { .run_list = LIST_HEAD_INIT(init_task.rt.run_list), diff --git a/kernel/sched/core.c b/kernel/sched/core.c -index 1b971c69d3a2a..df2f22a9729cb 100644 +index 83e36547af17..8a541fe2d462 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1305,6 +1305,12 @@ static void set_load_weight(struct task_struct *p, bool update_load) @@ -212,7 +212,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 #ifdef CONFIG_UCLAMP_TASK /* * Serializes updates of utilization clamp values -@@ -4500,8 +4506,11 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) +@@ -4501,8 +4507,11 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) p->se.prev_sum_exec_runtime = 0; p->se.nr_migrations = 0; p->se.vruntime = 0; @@ -224,7 +224,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 #ifdef CONFIG_FAIR_GROUP_SCHED p->se.cfs_rq = NULL; #endif -@@ -4753,6 +4762,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) +@@ -4754,6 +4763,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) p->prio = p->normal_prio = p->static_prio; set_load_weight(p, false); @@ -232,7 +232,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 /* * We don't need the reset flag anymore after the fork. It has -@@ -7525,7 +7535,7 @@ static struct task_struct *find_process_by_pid(pid_t pid) +@@ -7529,7 +7539,7 @@ static struct task_struct *find_process_by_pid(pid_t pid) #define SETPARAM_POLICY -1 static void __setscheduler_params(struct task_struct *p, @@ -241,7 +241,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 { int policy = attr->sched_policy; -@@ -7549,6 +7559,13 @@ static void __setscheduler_params(struct task_struct *p, +@@ -7553,6 +7563,13 @@ static void __setscheduler_params(struct task_struct *p, set_load_weight(p, true); } @@ -255,7 +255,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 /* * Check the target process has a UID that matches the current process's: */ -@@ -7682,6 +7699,13 @@ static int __sched_setscheduler(struct task_struct *p, +@@ -7687,6 +7704,13 @@ static int __sched_setscheduler(struct task_struct *p, return retval; } @@ -269,7 +269,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 /* Update task specific "requested" clamps */ if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) { retval = uclamp_validate(p, attr); -@@ -7723,6 +7747,9 @@ static int __sched_setscheduler(struct task_struct *p, +@@ -7734,6 +7758,9 @@ static int __sched_setscheduler(struct task_struct *p, goto change; if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP) goto change; @@ -279,7 +279,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 p->sched_reset_on_fork = reset_on_fork; retval = 0; -@@ -7811,6 +7838,7 @@ static int __sched_setscheduler(struct task_struct *p, +@@ -7822,6 +7849,7 @@ static int __sched_setscheduler(struct task_struct *p, __setscheduler_params(p, attr); __setscheduler_prio(p, newprio); } @@ -287,7 +287,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 __setscheduler_uclamp(p, attr); if (queued) { -@@ -8021,6 +8049,9 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a +@@ -8033,6 +8061,9 @@ static int sched_copy_attr(struct sched_attr __user *uattr, struct sched_attr *a size < SCHED_ATTR_SIZE_VER1) return -EINVAL; @@ -297,7 +297,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 /* * XXX: Do we want to be lenient like existing syscalls; or do we want * to be strict and return an error on out-of-bounds values? -@@ -8258,6 +8289,8 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, +@@ -8270,6 +8301,8 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, get_params(p, &kattr); kattr.sched_flags &= SCHED_FLAG_ALL; @@ -306,7 +306,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 #ifdef CONFIG_UCLAMP_TASK /* * This could race with another potential updater, but this is fine -@@ -11215,6 +11248,25 @@ static int cpu_idle_write_s64(struct cgroup_subsys_state *css, +@@ -11214,6 +11247,25 @@ static int cpu_idle_write_s64(struct cgroup_subsys_state *css, { return sched_group_set_idle(css_tg(css), idle); } @@ -332,7 +332,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 #endif static struct cftype cpu_legacy_files[] = { -@@ -11229,6 +11281,11 @@ static struct cftype cpu_legacy_files[] = { +@@ -11228,6 +11280,11 @@ static struct cftype cpu_legacy_files[] = { .read_s64 = cpu_idle_read_s64, .write_s64 = cpu_idle_write_s64, }, @@ -344,7 +344,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 #endif #ifdef CONFIG_CFS_BANDWIDTH { -@@ -11468,6 +11525,12 @@ static struct cftype cpu_files[] = { +@@ -11467,6 +11524,12 @@ static struct cftype cpu_files[] = { .read_s64 = cpu_idle_read_s64, .write_s64 = cpu_idle_write_s64, }, @@ -358,7 +358,7 @@ index 1b971c69d3a2a..df2f22a9729cb 100644 #ifdef CONFIG_CFS_BANDWIDTH { diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c -index aeeba46a096b9..5c743bcb340d2 100644 +index aeeba46a096b..5c743bcb340d 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -347,10 +347,7 @@ static __init int sched_init_debug(void) @@ -462,7 +462,7 @@ index aeeba46a096b9..5c743bcb340d2 100644 P(dl.runtime); P(dl.deadline); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c -index a6205f9e6cb59..15167f12b9cf3 100644 +index 4039ff46fcb3..0fbb8fb24a50 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -47,6 +47,7 @@ @@ -1706,7 +1706,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 #ifdef CONFIG_SMP static int sched_idle_cpu(int cpu) { -@@ -7827,18 +7908,6 @@ static void migrate_task_rq_fair(struct task_struct *p, int new_cpu) +@@ -7844,18 +7925,6 @@ static void migrate_task_rq_fair(struct task_struct *p, int new_cpu) { struct sched_entity *se = &p->se; @@ -1725,7 +1725,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 if (!task_on_rq_migrating(p)) { remove_entity_load_avg(se); -@@ -7876,66 +7945,6 @@ balance_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) +@@ -7893,66 +7962,6 @@ balance_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) } #endif /* CONFIG_SMP */ @@ -1792,7 +1792,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 static void set_next_buddy(struct sched_entity *se) { for_each_sched_entity(se) { -@@ -7947,12 +7956,6 @@ static void set_next_buddy(struct sched_entity *se) +@@ -7964,12 +7973,6 @@ static void set_next_buddy(struct sched_entity *se) } } @@ -1805,7 +1805,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 /* * Preempt the current task with a newly woken task if needed: */ -@@ -7961,7 +7964,6 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ +@@ -7978,7 +7981,6 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ struct task_struct *curr = rq->curr; struct sched_entity *se = &curr->se, *pse = &p->se; struct cfs_rq *cfs_rq = task_cfs_rq(curr); @@ -1813,7 +1813,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 int next_buddy_marked = 0; int cse_is_idle, pse_is_idle; -@@ -7977,7 +7979,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ +@@ -7994,7 +7996,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ if (unlikely(throttled_hierarchy(cfs_rq_of(pse)))) return; @@ -1822,7 +1822,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 set_next_buddy(pse); next_buddy_marked = 1; } -@@ -8022,35 +8024,19 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ +@@ -8039,35 +8041,19 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ if (cse_is_idle != pse_is_idle) return; @@ -1865,7 +1865,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 } #ifdef CONFIG_SMP -@@ -8251,8 +8237,6 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev) +@@ -8268,8 +8254,6 @@ static void put_prev_task_fair(struct rq *rq, struct task_struct *prev) /* * sched_yield() is very simple @@ -1874,7 +1874,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 */ static void yield_task_fair(struct rq *rq) { -@@ -8268,21 +8252,19 @@ static void yield_task_fair(struct rq *rq) +@@ -8285,21 +8269,19 @@ static void yield_task_fair(struct rq *rq) clear_buddies(cfs_rq, se); @@ -1908,7 +1908,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 } static bool yield_to_task_fair(struct rq *rq, struct task_struct *p) -@@ -8530,8 +8512,7 @@ static int task_hot(struct task_struct *p, struct lb_env *env) +@@ -8547,8 +8529,7 @@ static int task_hot(struct task_struct *p, struct lb_env *env) * Buddy candidates are cache hot: */ if (sched_feat(CACHE_HOT_BUDDY) && env->dst_rq->nr_running && @@ -1918,7 +1918,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 return 1; if (sysctl_sched_migration_cost == -1) -@@ -12157,8 +12138,8 @@ static void rq_offline_fair(struct rq *rq) +@@ -12174,8 +12155,8 @@ static void rq_offline_fair(struct rq *rq) static inline bool __entity_slice_used(struct sched_entity *se, int min_nr_tasks) { @@ -1928,7 +1928,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 return (rtime * min_nr_tasks > slice); } -@@ -12314,8 +12295,8 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) +@@ -12331,8 +12312,8 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) */ static void task_fork_fair(struct task_struct *p) { @@ -1938,7 +1938,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 struct rq *rq = this_rq(); struct rq_flags rf; -@@ -12324,22 +12305,9 @@ static void task_fork_fair(struct task_struct *p) +@@ -12341,22 +12322,9 @@ static void task_fork_fair(struct task_struct *p) cfs_rq = task_cfs_rq(current); curr = cfs_rq->curr; @@ -1963,7 +1963,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 rq_unlock(rq, &rf); } -@@ -12368,34 +12336,6 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) +@@ -12385,34 +12353,6 @@ prio_changed_fair(struct rq *rq, struct task_struct *p, int oldprio) check_preempt_curr(rq, p, 0); } @@ -1998,7 +1998,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 #ifdef CONFIG_FAIR_GROUP_SCHED /* * Propagate the changes of the sched_entity across the tg tree to make it -@@ -12466,16 +12406,6 @@ static void attach_entity_cfs_rq(struct sched_entity *se) +@@ -12483,16 +12423,6 @@ static void attach_entity_cfs_rq(struct sched_entity *se) static void detach_task_cfs_rq(struct task_struct *p) { struct sched_entity *se = &p->se; @@ -2015,7 +2015,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 detach_entity_cfs_rq(se); } -@@ -12483,12 +12413,8 @@ static void detach_task_cfs_rq(struct task_struct *p) +@@ -12500,12 +12430,8 @@ static void detach_task_cfs_rq(struct task_struct *p) static void attach_task_cfs_rq(struct task_struct *p) { struct sched_entity *se = &p->se; @@ -2028,7 +2028,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 } static void switched_from_fair(struct rq *rq, struct task_struct *p) -@@ -12599,6 +12525,7 @@ int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent) +@@ -12616,6 +12542,7 @@ int alloc_fair_sched_group(struct task_group *tg, struct task_group *parent) goto err; tg->shares = NICE_0_LOAD; @@ -2036,7 +2036,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 init_cfs_bandwidth(tg_cfs_bandwidth(tg)); -@@ -12697,6 +12624,9 @@ void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq, +@@ -12714,6 +12641,9 @@ void init_tg_cfs_entry(struct task_group *tg, struct cfs_rq *cfs_rq, } se->my_q = cfs_rq; @@ -2046,7 +2046,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 /* guarantee group entities always have weight */ update_load_set(&se->load, NICE_0_LOAD); se->parent = parent; -@@ -12827,6 +12757,29 @@ int sched_group_set_idle(struct task_group *tg, long idle) +@@ -12844,6 +12774,29 @@ int sched_group_set_idle(struct task_group *tg, long idle) return 0; } @@ -2076,7 +2076,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 #else /* CONFIG_FAIR_GROUP_SCHED */ void free_fair_sched_group(struct task_group *tg) { } -@@ -12853,7 +12806,7 @@ static unsigned int get_rr_interval_fair(struct rq *rq, struct task_struct *task +@@ -12870,7 +12823,7 @@ static unsigned int get_rr_interval_fair(struct rq *rq, struct task_struct *task * idle runqueue: */ if (rq->cfs.load.weight) @@ -2086,7 +2086,7 @@ index a6205f9e6cb59..15167f12b9cf3 100644 return rr_interval; } diff --git a/kernel/sched/features.h b/kernel/sched/features.h -index 9e390eb82e384..ca95044a74791 100644 +index ee7f23c76bd3..7d65b40299d9 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -1,16 +1,12 @@ @@ -2125,7 +2125,7 @@ index 9e390eb82e384..ca95044a74791 100644 /* * Consider buddies to be cache hot, decreases the likeliness of a * cache buddy being migrated away, increases cache locality. -@@ -99,6 +88,3 @@ SCHED_FEAT(UTIL_EST, true) +@@ -98,6 +87,3 @@ SCHED_FEAT(UTIL_EST, true) SCHED_FEAT(UTIL_EST_FASTUP, true) SCHED_FEAT(LATENCY_WARN, false) @@ -2133,7 +2133,7 @@ index 9e390eb82e384..ca95044a74791 100644 -SCHED_FEAT(ALT_PERIOD, true) -SCHED_FEAT(BASE_SLICE, true) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h -index a6e814eb84cd8..abf5a48b509c6 100644 +index 9baeb1a2dfdd..4236c4c893aa 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -372,6 +372,8 @@ struct task_group { @@ -2174,7 +2174,7 @@ index a6e814eb84cd8..abf5a48b509c6 100644 #ifdef CONFIG_SCHED_DEBUG unsigned int nr_spread_over; -@@ -2192,6 +2198,7 @@ extern const u32 sched_prio_to_wmult[40]; +@@ -2198,6 +2204,7 @@ extern const u32 sched_prio_to_wmult[40]; #else #define ENQUEUE_MIGRATED 0x00 #endif @@ -2182,7 +2182,7 @@ index a6e814eb84cd8..abf5a48b509c6 100644 #define RETRY_TASK ((void *)-1UL) -@@ -2496,11 +2503,9 @@ extern void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags); +@@ -2502,11 +2509,9 @@ extern void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags); extern const_debug unsigned int sysctl_sched_nr_migrate; extern const_debug unsigned int sysctl_sched_migration_cost; @@ -2196,7 +2196,7 @@ index a6e814eb84cd8..abf5a48b509c6 100644 extern int sysctl_resched_latency_warn_ms; extern int sysctl_resched_latency_warn_once; -@@ -2513,6 +2518,8 @@ extern unsigned int sysctl_numa_balancing_scan_size; +@@ -2519,6 +2524,8 @@ extern unsigned int sysctl_numa_balancing_scan_size; extern unsigned int sysctl_numa_balancing_hot_threshold; #endif @@ -2205,7 +2205,7 @@ index a6e814eb84cd8..abf5a48b509c6 100644 #ifdef CONFIG_SCHED_HRTICK /* -@@ -3521,4 +3528,7 @@ static inline void task_tick_mm_cid(struct rq *rq, struct task_struct *curr) { } +@@ -3483,4 +3490,7 @@ static inline void task_tick_mm_cid(struct rq *rq, struct task_struct *curr) { } static inline void init_sched_mm_cid(struct task_struct *t) { } #endif @@ -2214,7 +2214,7 @@ index a6e814eb84cd8..abf5a48b509c6 100644 + #endif /* _KERNEL_SCHED_SCHED_H */ diff --git a/tools/include/uapi/linux/sched.h b/tools/include/uapi/linux/sched.h -index 3bac0a8ceab26..b2e932c25be62 100644 +index 3bac0a8ceab2..b2e932c25be6 100644 --- a/tools/include/uapi/linux/sched.h +++ b/tools/include/uapi/linux/sched.h @@ -132,6 +132,7 @@ struct clone_args { diff --git a/patches/0002-eevdfbore.patch b/patches/0002-eevdfbore.patch index cf8bbf6..6d53439 100644 --- a/patches/0002-eevdfbore.patch +++ b/patches/0002-eevdfbore.patch @@ -1,6 +1,6 @@ -From 06140f2f7a609e07d9fc7d1c79343772ead98dbd Mon Sep 17 00:00:00 2001 +From e6e251fb3f3927c18ac4f2a22a43c6c198133d19 Mon Sep 17 00:00:00 2001 From: Piotr Gorski -Date: Sun, 23 Jul 2023 09:44:46 +0200 +Date: Sun, 23 Jul 2023 09:46:42 +0200 Subject: [PATCH] bore-eevdf Signed-off-by: Piotr Gorski @@ -15,10 +15,10 @@ Signed-off-by: Piotr Gorski 7 files changed, 351 insertions(+), 8 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h -index 88c3e7ba8..6b4c553ae 100644 +index e99a9aa6a..14a1ce058 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h -@@ -560,6 +560,12 @@ struct sched_entity { +@@ -559,6 +559,12 @@ struct sched_entity { u64 sum_exec_runtime; u64 prev_sum_exec_runtime; u64 vruntime; @@ -31,7 +31,7 @@ index 88c3e7ba8..6b4c553ae 100644 s64 vlag; u64 slice; -@@ -991,6 +997,10 @@ struct task_struct { +@@ -990,6 +996,10 @@ struct task_struct { struct list_head children; struct list_head sibling; struct task_struct *group_leader; @@ -43,7 +43,7 @@ index 88c3e7ba8..6b4c553ae 100644 /* * 'ptraced' is the list of tasks this task is using ptrace() on. diff --git a/init/Kconfig b/init/Kconfig -index b6d38eccc..e90546df3 100644 +index 71755cc8e..c697be79e 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1277,6 +1277,26 @@ config CHECKPOINT_RESTORE @@ -74,10 +74,10 @@ index b6d38eccc..e90546df3 100644 bool "Automatic process group scheduling" select CGROUPS diff --git a/kernel/sched/core.c b/kernel/sched/core.c -index df2f22a97..df8b76e2c 100644 +index 8a541fe2d..13969a3a3 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c -@@ -4490,6 +4490,112 @@ int wake_up_state(struct task_struct *p, unsigned int state) +@@ -4491,6 +4491,112 @@ int wake_up_state(struct task_struct *p, unsigned int state) return try_to_wake_up(p, state, 0); } @@ -190,7 +190,7 @@ index df2f22a97..df8b76e2c 100644 /* * Perform scheduler related setup for a newly forked process p. * p is forked by current. -@@ -4506,6 +4612,9 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) +@@ -4507,6 +4613,9 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) p->se.prev_sum_exec_runtime = 0; p->se.nr_migrations = 0; p->se.vruntime = 0; @@ -200,7 +200,7 @@ index df2f22a97..df8b76e2c 100644 p->se.vlag = 0; INIT_LIST_HEAD(&p->se.group_node); -@@ -4827,6 +4936,9 @@ void sched_cgroup_fork(struct task_struct *p, struct kernel_clone_args *kargs) +@@ -4828,6 +4937,9 @@ void sched_cgroup_fork(struct task_struct *p, struct kernel_clone_args *kargs) void sched_post_fork(struct task_struct *p) { @@ -210,7 +210,7 @@ index df2f22a97..df8b76e2c 100644 uclamp_post_fork(p); } -@@ -9968,6 +10080,11 @@ void __init sched_init(void) +@@ -9967,6 +10079,11 @@ void __init sched_init(void) BUG_ON(&dl_sched_class != &stop_sched_class + 1); #endif @@ -245,7 +245,7 @@ index 5c743bcb3..755ef4c8d 100644 SEQ_printf(m, " %d %d", task_node(p), task_numa_group_id(p)); #endif diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c -index 15167f12b..51f1b7a67 100644 +index d6042543c..e52c14232 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -19,6 +19,9 @@ @@ -557,7 +557,7 @@ index 15167f12b..51f1b7a67 100644 cfs_rq = cfs_rq_of(se); dequeue_entity(cfs_rq, se, flags); -@@ -8030,7 +8214,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ +@@ -8047,7 +8231,7 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_ /* * XXX pick_eevdf(cfs_rq) != se ? */ @@ -566,7 +566,7 @@ index 15167f12b..51f1b7a67 100644 goto preempt; return; -@@ -8243,6 +8427,9 @@ static void yield_task_fair(struct rq *rq) +@@ -8260,6 +8444,9 @@ static void yield_task_fair(struct rq *rq) struct task_struct *curr = rq->curr; struct cfs_rq *cfs_rq = task_cfs_rq(curr); struct sched_entity *se = &curr->se; @@ -577,7 +577,7 @@ index 15167f12b..51f1b7a67 100644 /* * Are we the only task in the tree? diff --git a/kernel/sched/features.h b/kernel/sched/features.h -index ca95044a7..a7d34d1b2 100644 +index 7d65b4029..bd274f7c7 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -13,7 +13,11 @@ SCHED_FEAT(PLACE_DEADLINE_INITIAL, true) @@ -593,10 +593,10 @@ index ca95044a7..a7d34d1b2 100644 /* * Consider buddies to be cache hot, decreases the likeliness of a diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h -index abf5a48b5..a9f9e80a1 100644 +index 4236c4c89..714cc6ad9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h -@@ -2504,6 +2504,7 @@ extern const_debug unsigned int sysctl_sched_nr_migrate; +@@ -2510,6 +2510,7 @@ extern const_debug unsigned int sysctl_sched_nr_migrate; extern const_debug unsigned int sysctl_sched_migration_cost; extern unsigned int sysctl_sched_base_slice; diff --git a/patches/0006-r8169-fix.patch b/patches/0006-r8169-fix.patch deleted file mode 100644 index a1c77a8..0000000 --- a/patches/0006-r8169-fix.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 2ab19de62d67e403105ba860971e5ff0d511ad15 Mon Sep 17 00:00:00 2001 -From: Heiner Kallweit -Date: Mon, 6 Mar 2023 22:28:06 +0100 -Subject: [PATCH] r8169: remove ASPM restrictions now that ASPM is disabled - during NAPI poll -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Now that ASPM is disabled during NAPI poll, we can remove all ASPM -restrictions. This allows for higher power savings if the network -isn't fully loaded. - -Reviewed-by: Simon Horman -Tested-by: Kai-Heng Feng -Tested-by: Holger Hoffstätte -Signed-off-by: Heiner Kallweit -Signed-off-by: David S. Miller ---- - drivers/net/ethernet/realtek/r8169_main.c | 27 +---------------------- - 1 file changed, 1 insertion(+), 26 deletions(-) - -diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c -index 2897b9bf29af67..6563e4c6a13606 100644 ---- a/drivers/net/ethernet/realtek/r8169_main.c -+++ b/drivers/net/ethernet/realtek/r8169_main.c -@@ -620,7 +620,6 @@ struct rtl8169_private { - int cfg9346_usage_count; - - unsigned supports_gmii:1; -- unsigned aspm_manageable:1; - dma_addr_t counters_phys_addr; - struct rtl8169_counters *counters; - struct rtl8169_tc_offsets tc_offset; -@@ -2744,8 +2743,7 @@ static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) - if (tp->mac_version < RTL_GIGA_MAC_VER_32) - return; - -- /* Don't enable ASPM in the chip if OS can't control ASPM */ -- if (enable && tp->aspm_manageable) { -+ if (enable) { - rtl_mod_config5(tp, 0, ASPM_en); - rtl_mod_config2(tp, 0, ClkReqEn); - -@@ -5221,16 +5219,6 @@ static void rtl_init_mac_address(struct rtl8169_private *tp) - rtl_rar_set(tp, mac_addr); - } - --/* register is set if system vendor successfully tested ASPM 1.2 */ --static bool rtl_aspm_is_safe(struct rtl8169_private *tp) --{ -- if (tp->mac_version >= RTL_GIGA_MAC_VER_61 && -- r8168_mac_ocp_read(tp, 0xc0b2) & 0xf) -- return true; -- -- return false; --} -- - static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) - { - struct rtl8169_private *tp; -@@ -5302,19 +5290,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) - - tp->mac_version = chipset; - -- /* Disable ASPM L1 as that cause random device stop working -- * problems as well as full system hangs for some PCIe devices users. -- * Chips from RTL8168h partially have issues with L1.2, but seem -- * to work fine with L1 and L1.1. -- */ -- if (rtl_aspm_is_safe(tp)) -- rc = 0; -- else if (tp->mac_version >= RTL_GIGA_MAC_VER_46) -- rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1_2); -- else -- rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); -- tp->aspm_manageable = !rc; -- - tp->dash_type = rtl_check_dash(tp); - - tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK; \ No newline at end of file diff --git a/scripts/patch.sh b/scripts/patch.sh index 0b223dc..0cf2285 100755 --- a/scripts/patch.sh +++ b/scripts/patch.sh @@ -15,6 +15,4 @@ patch -Np1 < "../patches/0002-eevdfbore.patch" # Allow setting custom pollrates for usb devices patch -Np1 < "../patches/0004-Allow-to-set-custom-USB-pollrate-for-specific-device.patch" # Allow pre polaris cards to use the amdgpu kernel module -patch -Np1 < "../patches/0005-amdgpu-si-cik-default.patch" -# Fix a dumb upstream change that breaks the r8169 ethernet adapter -patch -RNp1 < "../patches/0006-r8169-fix.patch" \ No newline at end of file +patch -Np1 < "../patches/0005-amdgpu-si-cik-default.patch" \ No newline at end of file diff --git a/scripts/source.sh b/scripts/source.sh index 18017e8..b0658da 100755 --- a/scripts/source.sh +++ b/scripts/source.sh @@ -2,7 +2,7 @@ echo "Pika Kernel - Getting source" -wget -nv https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.4.6.tar.gz -tar -xf ./linux-6.4.6.tar.gz +wget -nv https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/snapshot/linux-6.5-rc3.tar.gz +tar -xf ./linux-6.5-rc3.tar.gz -cd linux-6.4.6 +cd linux-6.5-rc3