0e8e989142
Changes in 5.10.121
binfmt_flat: do not stop relocating GOT entries prematurely on riscv
parisc/stifb: Implement fb_is_primary_device()
riscv: Initialize thread pointer before calling C functions
riscv: Fix irq_work when SMP is disabled
ALSA: hda/realtek: Enable 4-speaker output for Dell XPS 15 9520 laptop
ALSA: hda/realtek - Fix microphone noise on ASUS TUF B550M-PLUS
ALSA: usb-audio: Cancel pending work at closing a MIDI substream
USB: serial: option: add Quectel BG95 modem
USB: new quirk for Dell Gen 2 devices
usb: dwc3: gadget: Move null pinter check to proper place
usb: core: hcd: Add support for deferring roothub registration
cifs: when extending a file with falloc we should make files not-sparse
xhci: Allow host runtime PM as default for Intel Alder Lake N xHCI
Fonts: Make font size unsigned in font_desc
parisc/stifb: Keep track of hardware path of graphics card
x86/MCE/AMD: Fix memory leak when threshold_create_bank() fails
perf/x86/intel: Fix event constraints for ICL
ptrace/um: Replace PT_DTRACE with TIF_SINGLESTEP
ptrace/xtensa: Replace PT_SINGLESTEP with TIF_SINGLESTEP
ptrace: Reimplement PTRACE_KILL by always sending SIGKILL
btrfs: add "0x" prefix for unsupported optional features
btrfs: repair super block num_devices automatically
iommu/vt-d: Add RPLS to quirk list to skip TE disabling
drm/virtio: fix NULL pointer dereference in virtio_gpu_conn_get_modes
mwifiex: add mutex lock for call in mwifiex_dfs_chan_sw_work_queue
b43legacy: Fix assigning negative value to unsigned variable
b43: Fix assigning negative value to unsigned variable
ipw2x00: Fix potential NULL dereference in libipw_xmit()
ipv6: fix locking issues with loops over idev->addr_list
fbcon: Consistently protect deferred_takeover with console_lock()
x86/platform/uv: Update TSC sync state for UV5
ACPICA: Avoid cache flush inside virtual machines
drm/komeda: return early if drm_universal_plane_init() fails.
rcu-tasks: Fix race in schedule and flush work
rcu: Make TASKS_RUDE_RCU select IRQ_WORK
sfc: ef10: Fix assigning negative value to unsigned variable
ALSA: jack: Access input_dev under mutex
spi: spi-rspi: Remove setting {src,dst}_{addr,addr_width} based on DMA direction
tools/power turbostat: fix ICX DRAM power numbers
drm/amd/pm: fix double free in si_parse_power_table()
ath9k: fix QCA9561 PA bias level
media: venus: hfi: avoid null dereference in deinit
media: pci: cx23885: Fix the error handling in cx23885_initdev()
media: cx25821: Fix the warning when removing the module
md/bitmap: don't set sb values if can't pass sanity check
mmc: jz4740: Apply DMA engine limits to maximum segment size
drivers: mmc: sdhci_am654: Add the quirk to set TESTCD bit
scsi: megaraid: Fix error check return value of register_chrdev()
scsi: ufs: Use pm_runtime_resume_and_get() instead of pm_runtime_get_sync()
scsi: lpfc: Fix resource leak in lpfc_sli4_send_seq_to_ulp()
ath11k: disable spectral scan during spectral deinit
ASoC: Intel: bytcr_rt5640: Add quirk for the HP Pro Tablet 408
drm/plane: Move range check for format_count earlier
drm/amd/pm: fix the compile warning
ath10k: skip ath10k_halt during suspend for driver state RESTARTING
arm64: compat: Do not treat syscall number as ESR_ELx for a bad syscall
drm: msm: fix error check return value of irq_of_parse_and_map()
ipv6: Don't send rs packets to the interface of ARPHRD_TUNNEL
net/mlx5: fs, delete the FTE when there are no rules attached to it
ASoC: dapm: Don't fold register value changes into notifications
mlxsw: spectrum_dcb: Do not warn about priority changes
mlxsw: Treat LLDP packets as control
drm/amdgpu/ucode: Remove firmware load type check in amdgpu_ucode_free_bo
HID: bigben: fix slab-out-of-bounds Write in bigben_probe
ASoC: tscs454: Add endianness flag in snd_soc_component_driver
net: remove two BUG() from skb_checksum_help()
s390/preempt: disable __preempt_count_add() optimization for PROFILE_ALL_BRANCHES
perf/amd/ibs: Cascade pmu init functions' return value
spi: stm32-qspi: Fix wait_cmd timeout in APM mode
dma-debug: change allocation mode from GFP_NOWAIT to GFP_ATIOMIC
ACPI: PM: Block ASUS B1400CEAE from suspend to idle by default
ipmi:ssif: Check for NULL msg when handling events and messages
ipmi: Fix pr_fmt to avoid compilation issues
rtlwifi: Use pr_warn instead of WARN_ONCE
media: rga: fix possible memory leak in rga_probe
media: coda: limit frame interval enumeration to supported encoder frame sizes
media: imon: reorganize serialization
media: cec-adap.c: fix is_configuring state
openrisc: start CPU timer early in boot
nvme-pci: fix a NULL pointer dereference in nvme_alloc_admin_tags
ASoC: rt5645: Fix errorenous cleanup order
nbd: Fix hung on disconnect request if socket is closed before
net: phy: micrel: Allow probing without .driver_data
media: exynos4-is: Fix compile warning
ASoC: max98357a: remove dependency on GPIOLIB
ASoC: rt1015p: remove dependency on GPIOLIB
can: mcp251xfd: silence clang's -Wunaligned-access warning
x86/microcode: Add explicit CPU vendor dependency
m68k: atari: Make Atari ROM port I/O write macros return void
rxrpc: Return an error to sendmsg if call failed
rxrpc, afs: Fix selection of abort codes
eth: tg3: silence the GCC 12 array-bounds warning
selftests/bpf: fix btf_dump/btf_dump due to recent clang change
gfs2: use i_lock spin_lock for inode qadata
IB/rdmavt: add missing locks in rvt_ruc_loopback
ARM: dts: ox820: align interrupt controller node name with dtschema
ARM: dts: s5pv210: align DMA channels with dtschema
arm64: dts: qcom: msm8994: Fix BLSP[12]_DMA channels count
PM / devfreq: rk3399_dmc: Disable edev on remove()
crypto: ccree - use fine grained DMA mapping dir
soc: ti: ti_sci_pm_domains: Check for null return of devm_kcalloc
fs: jfs: fix possible NULL pointer dereference in dbFree()
ARM: OMAP1: clock: Fix UART rate reporting algorithm
powerpc/fadump: Fix fadump to work with a different endian capture kernel
fat: add ratelimit to fat*_ent_bread()
pinctrl: renesas: rzn1: Fix possible null-ptr-deref in sh_pfc_map_resources()
ARM: versatile: Add missing of_node_put in dcscb_init
ARM: dts: exynos: add atmel,24c128 fallback to Samsung EEPROM
ARM: hisi: Add missing of_node_put after of_find_compatible_node
PCI: Avoid pci_dev_lock() AB/BA deadlock with sriov_numvfs_store()
tracing: incorrect isolate_mote_t cast in mm_vmscan_lru_isolate
powerpc/powernv/vas: Assign real address to rx_fifo in vas_rx_win_attr
powerpc/xics: fix refcount leak in icp_opal_init()
powerpc/powernv: fix missing of_node_put in uv_init()
macintosh/via-pmu: Fix build failure when CONFIG_INPUT is disabled
powerpc/iommu: Add missing of_node_put in iommu_init_early_dart
RDMA/hfi1: Prevent panic when SDMA is disabled
drm: fix EDID struct for old ARM OABI format
dt-bindings: display: sitronix, st7735r: Fix backlight in example
ath11k: acquire ab->base_lock in unassign when finding the peer by addr
ath9k: fix ar9003_get_eepmisc
drm/edid: fix invalid EDID extension block filtering
drm/bridge: adv7511: clean up CEC adapter when probe fails
spi: qcom-qspi: Add minItems to interconnect-names
ASoC: mediatek: Fix error handling in mt8173_max98090_dev_probe
ASoC: mediatek: Fix missing of_node_put in mt2701_wm8960_machine_probe
x86/delay: Fix the wrong asm constraint in delay_loop()
drm/ingenic: Reset pixclock rate when parent clock rate changes
drm/mediatek: Fix mtk_cec_mask()
drm/vc4: hvs: Reset muxes at probe time
drm/vc4: txp: Don't set TXP_VSTART_AT_EOF
drm/vc4: txp: Force alpha to be 0xff if it's disabled
libbpf: Don't error out on CO-RE relos for overriden weak subprogs
bpf: Fix excessive memory allocation in stack_map_alloc()
nl80211: show SSID for P2P_GO interfaces
drm/komeda: Fix an undefined behavior bug in komeda_plane_add()
drm: mali-dp: potential dereference of null pointer
spi: spi-ti-qspi: Fix return value handling of wait_for_completion_timeout
scftorture: Fix distribution of short handler delays
net: dsa: mt7530: 1G can also support 1000BASE-X link mode
NFC: NULL out the dev->rfkill to prevent UAF
efi: Add missing prototype for efi_capsule_setup_info
target: remove an incorrect unmap zeroes data deduction
drbd: fix duplicate array initializer
EDAC/dmc520: Don't print an error for each unconfigured interrupt line
mtd: rawnand: denali: Use managed device resources
HID: hid-led: fix maximum brightness for Dream Cheeky
HID: elan: Fix potential double free in elan_input_configured
drm/bridge: Fix error handling in analogix_dp_probe
sched/fair: Fix cfs_rq_clock_pelt() for throttled cfs_rq
spi: img-spfi: Fix pm_runtime_get_sync() error checking
cpufreq: Fix possible race in cpufreq online error path
ath9k_htc: fix potential out of bounds access with invalid rxstatus->rs_keyix
media: hantro: Empty encoder capture buffers by default
drm/panel: simple: Add missing bus flags for Innolux G070Y2-L01
ALSA: pcm: Check for null pointer of pointer substream before dereferencing it
inotify: show inotify mask flags in proc fdinfo
fsnotify: fix wrong lockdep annotations
of: overlay: do not break notify on NOTIFY_{OK|STOP}
drm/msm/dpu: adjust display_v_end for eDP and DP
scsi: ufs: qcom: Fix ufs_qcom_resume()
scsi: ufs: core: Exclude UECxx from SFR dump list
selftests/resctrl: Fix null pointer dereference on open failed
libbpf: Fix logic for finding matching program for CO-RE relocation
mtd: spi-nor: core: Check written SR value in spi_nor_write_16bit_sr_and_check()
x86/pm: Fix false positive kmemleak report in msr_build_context()
mtd: rawnand: cadence: fix possible null-ptr-deref in cadence_nand_dt_probe()
x86/speculation: Add missing prototype for unpriv_ebpf_notify()
ASoC: rk3328: fix disabling mclk on pclk probe failure
perf tools: Add missing headers needed by util/data.h
drm/msm/disp/dpu1: set vbif hw config to NULL to avoid use after memory free during pm runtime resume
drm/msm/dp: stop event kernel thread when DP unbind
drm/msm/dp: fix error check return value of irq_of_parse_and_map()
drm/msm/dsi: fix error checks and return values for DSI xmit functions
drm/msm/hdmi: check return value after calling platform_get_resource_byname()
drm/msm/hdmi: fix error check return value of irq_of_parse_and_map()
drm/msm: add missing include to msm_drv.c
drm/panel: panel-simple: Fix proper bpc for AM-1280800N3TZQW-T00H
drm/rockchip: vop: fix possible null-ptr-deref in vop_bind()
perf tools: Use Python devtools for version autodetection rather than runtime
virtio_blk: fix the discard_granularity and discard_alignment queue limits
x86: Fix return value of __setup handlers
irqchip/exiu: Fix acknowledgment of edge triggered interrupts
irqchip/aspeed-i2c-ic: Fix irq_of_parse_and_map() return value
irqchip/aspeed-scu-ic: Fix irq_of_parse_and_map() return value
x86/mm: Cleanup the control_va_addr_alignment() __setup handler
arm64: fix types in copy_highpage()
regulator: core: Fix enable_count imbalance with EXCLUSIVE_GET
drm/msm/dp: fix event thread stuck in wait_event after kthread_stop()
drm/msm/mdp5: Return error code in mdp5_pipe_release when deadlock is detected
drm/msm/mdp5: Return error code in mdp5_mixer_release when deadlock is detected
drm/msm: return an error pointer in msm_gem_prime_get_sg_table()
media: uvcvideo: Fix missing check to determine if element is found in list
iomap: iomap_write_failed fix
spi: spi-fsl-qspi: check return value after calling platform_get_resource_byname()
Revert "cpufreq: Fix possible race in cpufreq online error path"
regulator: qcom_smd: Fix up PM8950 regulator configuration
perf/amd/ibs: Use interrupt regs ip for stack unwinding
ath11k: Don't check arvif->is_started before sending management frames
ASoC: fsl: Fix refcount leak in imx_sgtl5000_probe
ASoC: mxs-saif: Fix refcount leak in mxs_saif_probe
regulator: pfuze100: Fix refcount leak in pfuze_parse_regulators_dt
ASoC: samsung: Use dev_err_probe() helper
ASoC: samsung: Fix refcount leak in aries_audio_probe
kselftest/cgroup: fix test_stress.sh to use OUTPUT dir
scripts/faddr2line: Fix overlapping text section failures
media: aspeed: Fix an error handling path in aspeed_video_probe()
media: exynos4-is: Fix PM disable depth imbalance in fimc_is_probe
media: st-delta: Fix PM disable depth imbalance in delta_probe
media: exynos4-is: Change clk_disable to clk_disable_unprepare
media: pvrusb2: fix array-index-out-of-bounds in pvr2_i2c_core_init
media: vsp1: Fix offset calculation for plane cropping
Bluetooth: fix dangling sco_conn and use-after-free in sco_sock_timeout
Bluetooth: Interleave with allowlist scan
Bluetooth: L2CAP: Rudimentary typo fixes
Bluetooth: LL privacy allow RPA
Bluetooth: use inclusive language in HCI role comments
Bluetooth: use inclusive language when filtering devices
Bluetooth: use hdev lock for accept_list and reject_list in conn req
nvme: set dma alignment to dword
m68k: math-emu: Fix dependencies of math emulation support
lsm,selinux: pass flowi_common instead of flowi to the LSM hooks
sctp: read sk->sk_bound_dev_if once in sctp_rcv()
net: hinic: add missing destroy_workqueue in hinic_pf_to_mgmt_init
ASoC: ti: j721e-evm: Fix refcount leak in j721e_soc_probe_*
media: ov7670: remove ov7670_power_off from ov7670_remove
media: staging: media: rkvdec: Make use of the helper function devm_platform_ioremap_resource()
media: rkvdec: h264: Fix dpb_valid implementation
media: rkvdec: h264: Fix bit depth wrap in pps packet
ext4: reject the 'commit' option on ext2 filesystems
drm/msm/a6xx: Fix refcount leak in a6xx_gpu_init
drm: msm: fix possible memory leak in mdp5_crtc_cursor_set()
x86/sev: Annotate stack change in the #VC handler
drm/msm/dpu: handle pm_runtime_get_sync() errors in bind path
drm/i915: Fix CFI violation with show_dynamic_id()
thermal/drivers/bcm2711: Don't clamp temperature at zero
thermal/drivers/broadcom: Fix potential NULL dereference in sr_thermal_probe
thermal/drivers/core: Use a char pointer for the cooling device name
thermal/core: Fix memory leak in __thermal_cooling_device_register()
thermal/drivers/imx_sc_thermal: Fix refcount leak in imx_sc_thermal_probe
ASoC: wm2000: fix missing clk_disable_unprepare() on error in wm2000_anc_transition()
NFC: hci: fix sleep in atomic context bugs in nfc_hci_hcp_message_tx
ASoC: max98090: Move check for invalid values before casting in max98090_put_enab_tlv()
net: stmmac: selftests: Use kcalloc() instead of kzalloc()
net: stmmac: fix out-of-bounds access in a selftest
hv_netvsc: Fix potential dereference of NULL pointer
rxrpc: Fix listen() setting the bar too high for the prealloc rings
rxrpc: Don't try to resend the request if we're receiving the reply
rxrpc: Fix overlapping ACK accounting
rxrpc: Don't let ack.previousPacket regress
rxrpc: Fix decision on when to generate an IDLE ACK
net: huawei: hinic: Use devm_kcalloc() instead of devm_kzalloc()
hinic: Avoid some over memory allocation
net/smc: postpone sk_refcnt increment in connect()
arm64: dts: rockchip: Move drive-impedance-ohm to emmc phy on rk3399
memory: samsung: exynos5422-dmc: Avoid some over memory allocation
ARM: dts: suniv: F1C100: fix watchdog compatible
soc: qcom: smp2p: Fix missing of_node_put() in smp2p_parse_ipc
soc: qcom: smsm: Fix missing of_node_put() in smsm_parse_ipc
PCI: cadence: Fix find_first_zero_bit() limit
PCI: rockchip: Fix find_first_zero_bit() limit
PCI: dwc: Fix setting error return on MSI DMA mapping failure
ARM: dts: ci4x10: Adapt to changes in imx6qdl.dtsi regarding fec clocks
soc: qcom: llcc: Add MODULE_DEVICE_TABLE()
KVM: nVMX: Leave most VM-Exit info fields unmodified on failed VM-Entry
KVM: nVMX: Clear IDT vectoring on nested VM-Exit for double/triple fault
platform/chrome: cros_ec: fix error handling in cros_ec_register()
ARM: dts: imx6dl-colibri: Fix I2C pinmuxing
platform/chrome: Re-introduce cros_ec_cmd_xfer and use it for ioctls
can: xilinx_can: mark bit timing constants as const
ARM: dts: stm32: Fix PHY post-reset delay on Avenger96
ARM: dts: bcm2835-rpi-zero-w: Fix GPIO line name for Wifi/BT
ARM: dts: bcm2837-rpi-cm3-io3: Fix GPIO line names for SMPS I2C
ARM: dts: bcm2837-rpi-3-b-plus: Fix GPIO line name of power LED
ARM: dts: bcm2835-rpi-b: Fix GPIO line names
misc: ocxl: fix possible double free in ocxl_file_register_afu
crypto: marvell/cesa - ECB does not IV
gpiolib: of: Introduce hook for missing gpio-ranges
pinctrl: bcm2835: implement hook for missing gpio-ranges
arm: mediatek: select arch timer for mt7629
powerpc/fadump: fix PT_LOAD segment for boot memory area
mfd: ipaq-micro: Fix error check return value of platform_get_irq()
scsi: fcoe: Fix Wstringop-overflow warnings in fcoe_wwn_from_mac()
firmware: arm_scmi: Fix list protocols enumeration in the base protocol
nvdimm: Fix firmware activation deadlock scenarios
nvdimm: Allow overwrite in the presence of disabled dimms
pinctrl: mvebu: Fix irq_of_parse_and_map() return value
drivers/base/node.c: fix compaction sysfs file leak
dax: fix cache flush on PMD-mapped pages
drivers/base/memory: fix an unlikely reference counting issue in __add_memory_block()
powerpc/8xx: export 'cpm_setbrg' for modules
pinctrl: renesas: core: Fix possible null-ptr-deref in sh_pfc_map_resources()
powerpc/idle: Fix return value of __setup() handler
powerpc/4xx/cpm: Fix return value of __setup() handler
ASoC: atmel-pdmic: Remove endianness flag on pdmic component
ASoC: atmel-classd: Remove endianness flag on class d component
proc: fix dentry/inode overinstantiating under /proc/${pid}/net
ipc/mqueue: use get_tree_nodev() in mqueue_get_tree()
PCI: imx6: Fix PERST# start-up sequence
tty: fix deadlock caused by calling printk() under tty_port->lock
crypto: sun8i-ss - rework handling of IV
crypto: sun8i-ss - handle zero sized sg
crypto: cryptd - Protect per-CPU resource by disabling BH.
Input: sparcspkr - fix refcount leak in bbc_beep_probe
PCI/AER: Clear MULTI_ERR_COR/UNCOR_RCV bits
hwrng: omap3-rom - fix using wrong clk_disable() in omap_rom_rng_runtime_resume()
powerpc/64: Only WARN if __pa()/__va() called with bad addresses
powerpc/perf: Fix the threshold compare group constraint for power9
macintosh: via-pmu and via-cuda need RTC_LIB
powerpc/fsl_rio: Fix refcount leak in fsl_rio_setup
mfd: davinci_voicecodec: Fix possible null-ptr-deref davinci_vc_probe()
mailbox: forward the hrtimer if not queued and under a lock
RDMA/hfi1: Prevent use of lock before it is initialized
Input: stmfts - do not leave device disabled in stmfts_input_open
OPP: call of_node_put() on error path in _bandwidth_supported()
f2fs: fix dereference of stale list iterator after loop body
iommu/mediatek: Add list_del in mtk_iommu_remove
i2c: at91: use dma safe buffers
cpufreq: mediatek: add missing platform_driver_unregister() on error in mtk_cpufreq_driver_init
cpufreq: mediatek: Use module_init and add module_exit
cpufreq: mediatek: Unregister platform device on exit
MIPS: Loongson: Use hwmon_device_register_with_groups() to register hwmon
i2c: at91: Initialize dma_buf in at91_twi_xfer()
dmaengine: idxd: Fix the error handling path in idxd_cdev_register()
NFS: Do not report EINTR/ERESTARTSYS as mapping errors
NFS: fsync() should report filesystem errors over EINTR/ERESTARTSYS
NFS: Do not report flush errors in nfs_write_end()
NFS: Don't report errors from nfs_pageio_complete() more than once
NFSv4/pNFS: Do not fail I/O when we fail to allocate the pNFS layout
video: fbdev: clcdfb: Fix refcount leak in clcdfb_of_vram_setup
dmaengine: stm32-mdma: remove GISR1 register
dmaengine: stm32-mdma: rework interrupt handler
dmaengine: stm32-mdma: fix chan initialization in stm32_mdma_irq_handler()
iommu/amd: Increase timeout waiting for GA log enablement
i2c: npcm: Fix timeout calculation
i2c: npcm: Correct register access width
i2c: npcm: Handle spurious interrupts
i2c: rcar: fix PM ref counts in probe error paths
perf c2c: Use stdio interface if slang is not supported
perf jevents: Fix event syntax error caused by ExtSel
f2fs: fix to avoid f2fs_bug_on() in dec_valid_node_count()
f2fs: fix to do sanity check on block address in f2fs_do_zero_range()
f2fs: fix to clear dirty inode in f2fs_evict_inode()
f2fs: fix deadloop in foreground GC
f2fs: don't need inode lock for system hidden quota
f2fs: fix to do sanity check on total_data_blocks
f2fs: fix fallocate to use file_modified to update permissions consistently
f2fs: fix to do sanity check for inline inode
wifi: mac80211: fix use-after-free in chanctx code
iwlwifi: mvm: fix assert 1F04 upon reconfig
fs-writeback: writeback_sb_inodes:Recalculate 'wrote' according skipped pages
efi: Do not import certificates from UEFI Secure Boot for T2 Macs
bfq: Split shared queues on move between cgroups
bfq: Update cgroup information before merging bio
bfq: Track whether bfq_group is still online
ext4: fix use-after-free in ext4_rename_dir_prepare
ext4: fix warning in ext4_handle_inode_extension
ext4: fix bug_on in ext4_writepages
ext4: filter out EXT4_FC_REPLAY from on-disk superblock field s_state
ext4: fix bug_on in __es_tree_search
ext4: verify dir block before splitting it
ext4: avoid cycles in directory h-tree
ACPI: property: Release subnode properties with data nodes
tracing: Fix potential double free in create_var_ref()
PCI/PM: Fix bridge_d3_blacklist[] Elo i2 overwrite of Gigabyte X299
PCI: qcom: Fix runtime PM imbalance on probe errors
PCI: qcom: Fix unbalanced PHY init on probe errors
mm, compaction: fast_find_migrateblock() should return pfn in the target zone
s390/perf: obtain sie_block from the right address
dlm: fix plock invalid read
dlm: fix missing lkb refcount handling
ocfs2: dlmfs: fix error handling of user_dlm_destroy_lock
scsi: dc395x: Fix a missing check on list iterator
scsi: ufs: qcom: Add a readl() to make sure ref_clk gets enabled
drm/amdgpu/cs: make commands with 0 chunks illegal behaviour.
drm/etnaviv: check for reaped mapping in etnaviv_iommu_unmap_gem
drm/nouveau/clk: Fix an incorrect NULL check on list iterator
drm/nouveau/kms/nv50-: atom: fix an incorrect NULL check on list iterator
drm/bridge: analogix_dp: Grab runtime PM reference for DP-AUX
drm/i915/dsi: fix VBT send packet port selection for ICL+
md: fix an incorrect NULL check in does_sb_need_changing
md: fix an incorrect NULL check in md_reload_sb
mtd: cfi_cmdset_0002: Move and rename chip_check/chip_ready/chip_good_for_write
mtd: cfi_cmdset_0002: Use chip_ready() for write on S29GL064N
media: coda: Fix reported H264 profile
media: coda: Add more H264 levels for CODA960
ima: remove the IMA_TEMPLATE Kconfig option
Kconfig: Add option for asm goto w/ tied outputs to workaround clang-13 bug
RDMA/hfi1: Fix potential integer multiplication overflow errors
csky: patch_text: Fixup last cpu should be master
irqchip/armada-370-xp: Do not touch Performance Counter Overflow on A375, A38x, A39x
irqchip: irq-xtensa-mx: fix initial IRQ affinity
cfg80211: declare MODULE_FIRMWARE for regulatory.db
mac80211: upgrade passive scan to active scan on DFS channels after beacon rx
um: chan_user: Fix winch_tramp() return value
um: Fix out-of-bounds read in LDT setup
kexec_file: drop weak attribute from arch_kexec_apply_relocations[_add]
ftrace: Clean up hash direct_functions on register failures
iommu/msm: Fix an incorrect NULL check on list iterator
nodemask.h: fix compilation error with GCC12
hugetlb: fix huge_pmd_unshare address update
xtensa/simdisk: fix proc_read_simdisk()
rtl818x: Prevent using not initialized queues
ASoC: rt5514: Fix event generation for "DSP Voice Wake Up" control
carl9170: tx: fix an incorrect use of list iterator
stm: ltdc: fix two incorrect NULL checks on list iterator
bcache: improve multithreaded bch_btree_check()
bcache: improve multithreaded bch_sectors_dirty_init()
bcache: remove incremental dirty sector counting for bch_sectors_dirty_init()
bcache: avoid journal no-space deadlock by reserving 1 journal bucket
serial: pch: don't overwrite xmit->buf[0] by x_char
tilcdc: tilcdc_external: fix an incorrect NULL check on list iterator
gma500: fix an incorrect NULL check on list iterator
arm64: dts: qcom: ipq8074: fix the sleep clock frequency
phy: qcom-qmp: fix struct clk leak on probe errors
ARM: dts: s5pv210: Remove spi-cs-high on panel in Aries
ARM: pxa: maybe fix gpio lookup tables
SMB3: EBADF/EIO errors in rename/open caused by race condition in smb2_compound_op
docs/conf.py: Cope with removal of language=None in Sphinx 5.0.0
dt-bindings: gpio: altera: correct interrupt-cells
vdpasim: allow to enable a vq repeatedly
blk-iolatency: Fix inflight count imbalances and IO hangs on offline
coresight: core: Fix coresight device probe failure issue
phy: qcom-qmp: fix reset-controller leak on probe errors
net: ipa: fix page free in ipa_endpoint_trans_release()
net: ipa: fix page free in ipa_endpoint_replenish_one()
xfs: set inode size after creating symlink
xfs: sync lazy sb accounting on quiesce of read-only mounts
xfs: fix chown leaking delalloc quota blocks when fssetxattr fails
xfs: fix incorrect root dquot corruption error when switching group/project quota types
xfs: restore shutdown check in mapped write fault path
xfs: force log and push AIL to clear pinned inodes when aborting mount
xfs: consider shutdown in bmapbt cursor delete assert
xfs: assert in xfs_btree_del_cursor should take into account error
kseltest/cgroup: Make test_stress.sh work if run interactively
thermal/core: fix a UAF bug in __thermal_cooling_device_register()
thermal/core: Fix memory leak in the error path
bfq: Avoid merging queues with different parents
bfq: Drop pointless unlock-lock pair
bfq: Remove pointless bfq_init_rq() calls
bfq: Get rid of __bio_blkcg() usage
bfq: Make sure bfqg for which we are queueing requests is online
block: fix bio_clone_blkg_association() to associate with proper blkcg_gq
Revert "random: use static branch for crng_ready()"
RDMA/rxe: Generate a completion for unsupported/invalid opcode
MIPS: IP27: Remove incorrect `cpu_has_fpu' override
MIPS: IP30: Remove incorrect `cpu_has_fpu' override
ext4: only allow test_dummy_encryption when supported
md: bcache: check the return value of kzalloc() in detached_dev_do_request()
Linux 5.10.121
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I52dd11dc43acfa0ebddd2b6e277c823b96b07327
1063 lines
26 KiB
C
1063 lines
26 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
//#define DEBUG
|
|
#include <linux/spinlock.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/hdreg.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/virtio.h>
|
|
#include <linux/virtio_blk.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/string_helpers.h>
|
|
#include <linux/idr.h>
|
|
#include <linux/blk-mq.h>
|
|
#include <linux/blk-mq-virtio.h>
|
|
#include <linux/numa.h>
|
|
#include <uapi/linux/virtio_ring.h>
|
|
|
|
#define PART_BITS 4
|
|
#define VQ_NAME_LEN 16
|
|
#define MAX_DISCARD_SEGMENTS 256u
|
|
|
|
static int major;
|
|
static DEFINE_IDA(vd_index_ida);
|
|
|
|
static struct workqueue_struct *virtblk_wq;
|
|
|
|
struct virtio_blk_vq {
|
|
struct virtqueue *vq;
|
|
spinlock_t lock;
|
|
char name[VQ_NAME_LEN];
|
|
} ____cacheline_aligned_in_smp;
|
|
|
|
struct virtio_blk {
|
|
/*
|
|
* This mutex must be held by anything that may run after
|
|
* virtblk_remove() sets vblk->vdev to NULL.
|
|
*
|
|
* blk-mq, virtqueue processing, and sysfs attribute code paths are
|
|
* shut down before vblk->vdev is set to NULL and therefore do not need
|
|
* to hold this mutex.
|
|
*/
|
|
struct mutex vdev_mutex;
|
|
struct virtio_device *vdev;
|
|
|
|
/* The disk structure for the kernel. */
|
|
struct gendisk *disk;
|
|
|
|
/* Block layer tags. */
|
|
struct blk_mq_tag_set tag_set;
|
|
|
|
/* Process context for config space updates */
|
|
struct work_struct config_work;
|
|
|
|
/*
|
|
* Tracks references from block_device_operations open/release and
|
|
* virtio_driver probe/remove so this object can be freed once no
|
|
* longer in use.
|
|
*/
|
|
refcount_t refs;
|
|
|
|
/* What host tells us, plus 2 for header & tailer. */
|
|
unsigned int sg_elems;
|
|
|
|
/* Ida index - used to track minor number allocations. */
|
|
int index;
|
|
|
|
/* num of vqs */
|
|
int num_vqs;
|
|
struct virtio_blk_vq *vqs;
|
|
};
|
|
|
|
struct virtblk_req {
|
|
struct virtio_blk_outhdr out_hdr;
|
|
u8 status;
|
|
struct scatterlist sg[];
|
|
};
|
|
|
|
static inline blk_status_t virtblk_result(struct virtblk_req *vbr)
|
|
{
|
|
switch (vbr->status) {
|
|
case VIRTIO_BLK_S_OK:
|
|
return BLK_STS_OK;
|
|
case VIRTIO_BLK_S_UNSUPP:
|
|
return BLK_STS_NOTSUPP;
|
|
default:
|
|
return BLK_STS_IOERR;
|
|
}
|
|
}
|
|
|
|
static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr,
|
|
struct scatterlist *data_sg, bool have_data)
|
|
{
|
|
struct scatterlist hdr, status, *sgs[3];
|
|
unsigned int num_out = 0, num_in = 0;
|
|
|
|
sg_init_one(&hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
|
|
sgs[num_out++] = &hdr;
|
|
|
|
if (have_data) {
|
|
if (vbr->out_hdr.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
|
|
sgs[num_out++] = data_sg;
|
|
else
|
|
sgs[num_out + num_in++] = data_sg;
|
|
}
|
|
|
|
sg_init_one(&status, &vbr->status, sizeof(vbr->status));
|
|
sgs[num_out + num_in++] = &status;
|
|
|
|
return virtqueue_add_sgs(vq, sgs, num_out, num_in, vbr, GFP_ATOMIC);
|
|
}
|
|
|
|
static int virtblk_setup_discard_write_zeroes(struct request *req, bool unmap)
|
|
{
|
|
unsigned short segments = blk_rq_nr_discard_segments(req);
|
|
unsigned short n = 0;
|
|
struct virtio_blk_discard_write_zeroes *range;
|
|
struct bio *bio;
|
|
u32 flags = 0;
|
|
|
|
if (unmap)
|
|
flags |= VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP;
|
|
|
|
range = kmalloc_array(segments, sizeof(*range), GFP_ATOMIC);
|
|
if (!range)
|
|
return -ENOMEM;
|
|
|
|
/*
|
|
* Single max discard segment means multi-range discard isn't
|
|
* supported, and block layer only runs contiguity merge like
|
|
* normal RW request. So we can't reply on bio for retrieving
|
|
* each range info.
|
|
*/
|
|
if (queue_max_discard_segments(req->q) == 1) {
|
|
range[0].flags = cpu_to_le32(flags);
|
|
range[0].num_sectors = cpu_to_le32(blk_rq_sectors(req));
|
|
range[0].sector = cpu_to_le64(blk_rq_pos(req));
|
|
n = 1;
|
|
} else {
|
|
__rq_for_each_bio(bio, req) {
|
|
u64 sector = bio->bi_iter.bi_sector;
|
|
u32 num_sectors = bio->bi_iter.bi_size >> SECTOR_SHIFT;
|
|
|
|
range[n].flags = cpu_to_le32(flags);
|
|
range[n].num_sectors = cpu_to_le32(num_sectors);
|
|
range[n].sector = cpu_to_le64(sector);
|
|
n++;
|
|
}
|
|
}
|
|
|
|
WARN_ON_ONCE(n != segments);
|
|
|
|
req->special_vec.bv_page = virt_to_page(range);
|
|
req->special_vec.bv_offset = offset_in_page(range);
|
|
req->special_vec.bv_len = sizeof(*range) * segments;
|
|
req->rq_flags |= RQF_SPECIAL_PAYLOAD;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void virtblk_request_done(struct request *req)
|
|
{
|
|
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
|
|
|
|
if (req->rq_flags & RQF_SPECIAL_PAYLOAD) {
|
|
kfree(page_address(req->special_vec.bv_page) +
|
|
req->special_vec.bv_offset);
|
|
}
|
|
|
|
blk_mq_end_request(req, virtblk_result(vbr));
|
|
}
|
|
|
|
static void virtblk_done(struct virtqueue *vq)
|
|
{
|
|
struct virtio_blk *vblk = vq->vdev->priv;
|
|
bool req_done = false;
|
|
int qid = vq->index;
|
|
struct virtblk_req *vbr;
|
|
unsigned long flags;
|
|
unsigned int len;
|
|
|
|
spin_lock_irqsave(&vblk->vqs[qid].lock, flags);
|
|
do {
|
|
virtqueue_disable_cb(vq);
|
|
while ((vbr = virtqueue_get_buf(vblk->vqs[qid].vq, &len)) != NULL) {
|
|
struct request *req = blk_mq_rq_from_pdu(vbr);
|
|
|
|
if (likely(!blk_should_fake_timeout(req->q)))
|
|
blk_mq_complete_request(req);
|
|
req_done = true;
|
|
}
|
|
if (unlikely(virtqueue_is_broken(vq)))
|
|
break;
|
|
} while (!virtqueue_enable_cb(vq));
|
|
|
|
/* In case queue is stopped waiting for more buffers. */
|
|
if (req_done)
|
|
blk_mq_start_stopped_hw_queues(vblk->disk->queue, true);
|
|
spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags);
|
|
}
|
|
|
|
static void virtio_commit_rqs(struct blk_mq_hw_ctx *hctx)
|
|
{
|
|
struct virtio_blk *vblk = hctx->queue->queuedata;
|
|
struct virtio_blk_vq *vq = &vblk->vqs[hctx->queue_num];
|
|
bool kick;
|
|
|
|
spin_lock_irq(&vq->lock);
|
|
kick = virtqueue_kick_prepare(vq->vq);
|
|
spin_unlock_irq(&vq->lock);
|
|
|
|
if (kick)
|
|
virtqueue_notify(vq->vq);
|
|
}
|
|
|
|
static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
|
|
const struct blk_mq_queue_data *bd)
|
|
{
|
|
struct virtio_blk *vblk = hctx->queue->queuedata;
|
|
struct request *req = bd->rq;
|
|
struct virtblk_req *vbr = blk_mq_rq_to_pdu(req);
|
|
unsigned long flags;
|
|
unsigned int num;
|
|
int qid = hctx->queue_num;
|
|
int err;
|
|
bool notify = false;
|
|
bool unmap = false;
|
|
u32 type;
|
|
|
|
switch (req_op(req)) {
|
|
case REQ_OP_READ:
|
|
case REQ_OP_WRITE:
|
|
type = 0;
|
|
break;
|
|
case REQ_OP_FLUSH:
|
|
type = VIRTIO_BLK_T_FLUSH;
|
|
break;
|
|
case REQ_OP_DISCARD:
|
|
type = VIRTIO_BLK_T_DISCARD;
|
|
break;
|
|
case REQ_OP_WRITE_ZEROES:
|
|
type = VIRTIO_BLK_T_WRITE_ZEROES;
|
|
unmap = !(req->cmd_flags & REQ_NOUNMAP);
|
|
break;
|
|
case REQ_OP_DRV_IN:
|
|
type = VIRTIO_BLK_T_GET_ID;
|
|
break;
|
|
default:
|
|
WARN_ON_ONCE(1);
|
|
return BLK_STS_IOERR;
|
|
}
|
|
|
|
BUG_ON(type != VIRTIO_BLK_T_DISCARD &&
|
|
type != VIRTIO_BLK_T_WRITE_ZEROES &&
|
|
(req->nr_phys_segments + 2 > vblk->sg_elems));
|
|
|
|
vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, type);
|
|
vbr->out_hdr.sector = type ?
|
|
0 : cpu_to_virtio64(vblk->vdev, blk_rq_pos(req));
|
|
vbr->out_hdr.ioprio = cpu_to_virtio32(vblk->vdev, req_get_ioprio(req));
|
|
|
|
blk_mq_start_request(req);
|
|
|
|
if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES) {
|
|
err = virtblk_setup_discard_write_zeroes(req, unmap);
|
|
if (err)
|
|
return BLK_STS_RESOURCE;
|
|
}
|
|
|
|
num = blk_rq_map_sg(hctx->queue, req, vbr->sg);
|
|
if (num) {
|
|
if (rq_data_dir(req) == WRITE)
|
|
vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_OUT);
|
|
else
|
|
vbr->out_hdr.type |= cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_IN);
|
|
}
|
|
|
|
spin_lock_irqsave(&vblk->vqs[qid].lock, flags);
|
|
err = virtblk_add_req(vblk->vqs[qid].vq, vbr, vbr->sg, num);
|
|
if (err) {
|
|
virtqueue_kick(vblk->vqs[qid].vq);
|
|
/* Don't stop the queue if -ENOMEM: we may have failed to
|
|
* bounce the buffer due to global resource outage.
|
|
*/
|
|
if (err == -ENOSPC)
|
|
blk_mq_stop_hw_queue(hctx);
|
|
spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags);
|
|
switch (err) {
|
|
case -ENOSPC:
|
|
return BLK_STS_DEV_RESOURCE;
|
|
case -ENOMEM:
|
|
return BLK_STS_RESOURCE;
|
|
default:
|
|
return BLK_STS_IOERR;
|
|
}
|
|
}
|
|
|
|
if (bd->last && virtqueue_kick_prepare(vblk->vqs[qid].vq))
|
|
notify = true;
|
|
spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags);
|
|
|
|
if (notify)
|
|
virtqueue_notify(vblk->vqs[qid].vq);
|
|
return BLK_STS_OK;
|
|
}
|
|
|
|
/* return id (s/n) string for *disk to *id_str
|
|
*/
|
|
static int virtblk_get_id(struct gendisk *disk, char *id_str)
|
|
{
|
|
struct virtio_blk *vblk = disk->private_data;
|
|
struct request_queue *q = vblk->disk->queue;
|
|
struct request *req;
|
|
int err;
|
|
|
|
req = blk_get_request(q, REQ_OP_DRV_IN, 0);
|
|
if (IS_ERR(req))
|
|
return PTR_ERR(req);
|
|
|
|
err = blk_rq_map_kern(q, req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
|
|
if (err)
|
|
goto out;
|
|
|
|
blk_execute_rq(vblk->disk->queue, vblk->disk, req, false);
|
|
err = blk_status_to_errno(virtblk_result(blk_mq_rq_to_pdu(req)));
|
|
out:
|
|
blk_put_request(req);
|
|
return err;
|
|
}
|
|
|
|
static void virtblk_get(struct virtio_blk *vblk)
|
|
{
|
|
refcount_inc(&vblk->refs);
|
|
}
|
|
|
|
static void virtblk_put(struct virtio_blk *vblk)
|
|
{
|
|
if (refcount_dec_and_test(&vblk->refs)) {
|
|
ida_simple_remove(&vd_index_ida, vblk->index);
|
|
mutex_destroy(&vblk->vdev_mutex);
|
|
kfree(vblk);
|
|
}
|
|
}
|
|
|
|
static int virtblk_open(struct block_device *bd, fmode_t mode)
|
|
{
|
|
struct virtio_blk *vblk = bd->bd_disk->private_data;
|
|
int ret = 0;
|
|
|
|
mutex_lock(&vblk->vdev_mutex);
|
|
|
|
if (vblk->vdev)
|
|
virtblk_get(vblk);
|
|
else
|
|
ret = -ENXIO;
|
|
|
|
mutex_unlock(&vblk->vdev_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static void virtblk_release(struct gendisk *disk, fmode_t mode)
|
|
{
|
|
struct virtio_blk *vblk = disk->private_data;
|
|
|
|
virtblk_put(vblk);
|
|
}
|
|
|
|
/* We provide getgeo only to please some old bootloader/partitioning tools */
|
|
static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
|
|
{
|
|
struct virtio_blk *vblk = bd->bd_disk->private_data;
|
|
int ret = 0;
|
|
|
|
mutex_lock(&vblk->vdev_mutex);
|
|
|
|
if (!vblk->vdev) {
|
|
ret = -ENXIO;
|
|
goto out;
|
|
}
|
|
|
|
/* see if the host passed in geometry config */
|
|
if (virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_GEOMETRY)) {
|
|
virtio_cread(vblk->vdev, struct virtio_blk_config,
|
|
geometry.cylinders, &geo->cylinders);
|
|
virtio_cread(vblk->vdev, struct virtio_blk_config,
|
|
geometry.heads, &geo->heads);
|
|
virtio_cread(vblk->vdev, struct virtio_blk_config,
|
|
geometry.sectors, &geo->sectors);
|
|
} else {
|
|
/* some standard values, similar to sd */
|
|
geo->heads = 1 << 6;
|
|
geo->sectors = 1 << 5;
|
|
geo->cylinders = get_capacity(bd->bd_disk) >> 11;
|
|
}
|
|
out:
|
|
mutex_unlock(&vblk->vdev_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static const struct block_device_operations virtblk_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = virtblk_open,
|
|
.release = virtblk_release,
|
|
.getgeo = virtblk_getgeo,
|
|
};
|
|
|
|
static int index_to_minor(int index)
|
|
{
|
|
return index << PART_BITS;
|
|
}
|
|
|
|
static int minor_to_index(int minor)
|
|
{
|
|
return minor >> PART_BITS;
|
|
}
|
|
|
|
static ssize_t serial_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct gendisk *disk = dev_to_disk(dev);
|
|
int err;
|
|
|
|
/* sysfs gives us a PAGE_SIZE buffer */
|
|
BUILD_BUG_ON(PAGE_SIZE < VIRTIO_BLK_ID_BYTES);
|
|
|
|
buf[VIRTIO_BLK_ID_BYTES] = '\0';
|
|
err = virtblk_get_id(disk, buf);
|
|
if (!err)
|
|
return strlen(buf);
|
|
|
|
if (err == -EIO) /* Unsupported? Make it empty. */
|
|
return 0;
|
|
|
|
return err;
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(serial);
|
|
|
|
/* The queue's logical block size must be set before calling this */
|
|
static void virtblk_update_capacity(struct virtio_blk *vblk, bool resize)
|
|
{
|
|
struct virtio_device *vdev = vblk->vdev;
|
|
struct request_queue *q = vblk->disk->queue;
|
|
char cap_str_2[10], cap_str_10[10];
|
|
unsigned long long nblocks;
|
|
u64 capacity;
|
|
|
|
/* Host must always specify the capacity. */
|
|
virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity);
|
|
|
|
/* If capacity is too big, truncate with warning. */
|
|
if ((sector_t)capacity != capacity) {
|
|
dev_warn(&vdev->dev, "Capacity %llu too large: truncating\n",
|
|
(unsigned long long)capacity);
|
|
capacity = (sector_t)-1;
|
|
}
|
|
|
|
nblocks = DIV_ROUND_UP_ULL(capacity, queue_logical_block_size(q) >> 9);
|
|
|
|
string_get_size(nblocks, queue_logical_block_size(q),
|
|
STRING_UNITS_2, cap_str_2, sizeof(cap_str_2));
|
|
string_get_size(nblocks, queue_logical_block_size(q),
|
|
STRING_UNITS_10, cap_str_10, sizeof(cap_str_10));
|
|
|
|
dev_notice(&vdev->dev,
|
|
"[%s] %s%llu %d-byte logical blocks (%s/%s)\n",
|
|
vblk->disk->disk_name,
|
|
resize ? "new size: " : "",
|
|
nblocks,
|
|
queue_logical_block_size(q),
|
|
cap_str_10,
|
|
cap_str_2);
|
|
|
|
set_capacity_revalidate_and_notify(vblk->disk, capacity, true);
|
|
}
|
|
|
|
static void virtblk_config_changed_work(struct work_struct *work)
|
|
{
|
|
struct virtio_blk *vblk =
|
|
container_of(work, struct virtio_blk, config_work);
|
|
|
|
virtblk_update_capacity(vblk, true);
|
|
}
|
|
|
|
static void virtblk_config_changed(struct virtio_device *vdev)
|
|
{
|
|
struct virtio_blk *vblk = vdev->priv;
|
|
|
|
queue_work(virtblk_wq, &vblk->config_work);
|
|
}
|
|
|
|
static int init_vq(struct virtio_blk *vblk)
|
|
{
|
|
int err;
|
|
int i;
|
|
vq_callback_t **callbacks;
|
|
const char **names;
|
|
struct virtqueue **vqs;
|
|
unsigned short num_vqs;
|
|
struct virtio_device *vdev = vblk->vdev;
|
|
struct irq_affinity desc = { 0, };
|
|
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_MQ,
|
|
struct virtio_blk_config, num_queues,
|
|
&num_vqs);
|
|
if (err)
|
|
num_vqs = 1;
|
|
|
|
num_vqs = min_t(unsigned int, nr_cpu_ids, num_vqs);
|
|
|
|
vblk->vqs = kmalloc_array(num_vqs, sizeof(*vblk->vqs), GFP_KERNEL);
|
|
if (!vblk->vqs)
|
|
return -ENOMEM;
|
|
|
|
names = kmalloc_array(num_vqs, sizeof(*names), GFP_KERNEL);
|
|
callbacks = kmalloc_array(num_vqs, sizeof(*callbacks), GFP_KERNEL);
|
|
vqs = kmalloc_array(num_vqs, sizeof(*vqs), GFP_KERNEL);
|
|
if (!names || !callbacks || !vqs) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < num_vqs; i++) {
|
|
callbacks[i] = virtblk_done;
|
|
snprintf(vblk->vqs[i].name, VQ_NAME_LEN, "req.%d", i);
|
|
names[i] = vblk->vqs[i].name;
|
|
}
|
|
|
|
/* Discover virtqueues and write information to configuration. */
|
|
err = virtio_find_vqs(vdev, num_vqs, vqs, callbacks, names, &desc);
|
|
if (err)
|
|
goto out;
|
|
|
|
for (i = 0; i < num_vqs; i++) {
|
|
spin_lock_init(&vblk->vqs[i].lock);
|
|
vblk->vqs[i].vq = vqs[i];
|
|
}
|
|
vblk->num_vqs = num_vqs;
|
|
|
|
out:
|
|
kfree(vqs);
|
|
kfree(callbacks);
|
|
kfree(names);
|
|
if (err)
|
|
kfree(vblk->vqs);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Legacy naming scheme used for virtio devices. We are stuck with it for
|
|
* virtio blk but don't ever use it for any new driver.
|
|
*/
|
|
static int virtblk_name_format(char *prefix, int index, char *buf, int buflen)
|
|
{
|
|
const int base = 'z' - 'a' + 1;
|
|
char *begin = buf + strlen(prefix);
|
|
char *end = buf + buflen;
|
|
char *p;
|
|
int unit;
|
|
|
|
p = end - 1;
|
|
*p = '\0';
|
|
unit = base;
|
|
do {
|
|
if (p == begin)
|
|
return -EINVAL;
|
|
*--p = 'a' + (index % unit);
|
|
index = (index / unit) - 1;
|
|
} while (index >= 0);
|
|
|
|
memmove(begin, p, end - p);
|
|
memcpy(buf, prefix, strlen(prefix));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtblk_get_cache_mode(struct virtio_device *vdev)
|
|
{
|
|
u8 writeback;
|
|
int err;
|
|
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE,
|
|
struct virtio_blk_config, wce,
|
|
&writeback);
|
|
|
|
/*
|
|
* If WCE is not configurable and flush is not available,
|
|
* assume no writeback cache is in use.
|
|
*/
|
|
if (err)
|
|
writeback = virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH);
|
|
|
|
return writeback;
|
|
}
|
|
|
|
static void virtblk_update_cache_mode(struct virtio_device *vdev)
|
|
{
|
|
u8 writeback = virtblk_get_cache_mode(vdev);
|
|
struct virtio_blk *vblk = vdev->priv;
|
|
|
|
blk_queue_write_cache(vblk->disk->queue, writeback, false);
|
|
revalidate_disk_size(vblk->disk, true);
|
|
}
|
|
|
|
static const char *const virtblk_cache_types[] = {
|
|
"write through", "write back"
|
|
};
|
|
|
|
static ssize_t
|
|
cache_type_store(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct gendisk *disk = dev_to_disk(dev);
|
|
struct virtio_blk *vblk = disk->private_data;
|
|
struct virtio_device *vdev = vblk->vdev;
|
|
int i;
|
|
|
|
BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE));
|
|
i = sysfs_match_string(virtblk_cache_types, buf);
|
|
if (i < 0)
|
|
return i;
|
|
|
|
virtio_cwrite8(vdev, offsetof(struct virtio_blk_config, wce), i);
|
|
virtblk_update_cache_mode(vdev);
|
|
return count;
|
|
}
|
|
|
|
static ssize_t
|
|
cache_type_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct gendisk *disk = dev_to_disk(dev);
|
|
struct virtio_blk *vblk = disk->private_data;
|
|
u8 writeback = virtblk_get_cache_mode(vblk->vdev);
|
|
|
|
BUG_ON(writeback >= ARRAY_SIZE(virtblk_cache_types));
|
|
return snprintf(buf, 40, "%s\n", virtblk_cache_types[writeback]);
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(cache_type);
|
|
|
|
static struct attribute *virtblk_attrs[] = {
|
|
&dev_attr_serial.attr,
|
|
&dev_attr_cache_type.attr,
|
|
NULL,
|
|
};
|
|
|
|
static umode_t virtblk_attrs_are_visible(struct kobject *kobj,
|
|
struct attribute *a, int n)
|
|
{
|
|
struct device *dev = kobj_to_dev(kobj);
|
|
struct gendisk *disk = dev_to_disk(dev);
|
|
struct virtio_blk *vblk = disk->private_data;
|
|
struct virtio_device *vdev = vblk->vdev;
|
|
|
|
if (a == &dev_attr_cache_type.attr &&
|
|
!virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE))
|
|
return S_IRUGO;
|
|
|
|
return a->mode;
|
|
}
|
|
|
|
static const struct attribute_group virtblk_attr_group = {
|
|
.attrs = virtblk_attrs,
|
|
.is_visible = virtblk_attrs_are_visible,
|
|
};
|
|
|
|
static const struct attribute_group *virtblk_attr_groups[] = {
|
|
&virtblk_attr_group,
|
|
NULL,
|
|
};
|
|
|
|
static int virtblk_init_request(struct blk_mq_tag_set *set, struct request *rq,
|
|
unsigned int hctx_idx, unsigned int numa_node)
|
|
{
|
|
struct virtio_blk *vblk = set->driver_data;
|
|
struct virtblk_req *vbr = blk_mq_rq_to_pdu(rq);
|
|
|
|
sg_init_table(vbr->sg, vblk->sg_elems);
|
|
return 0;
|
|
}
|
|
|
|
static int virtblk_map_queues(struct blk_mq_tag_set *set)
|
|
{
|
|
struct virtio_blk *vblk = set->driver_data;
|
|
|
|
return blk_mq_virtio_map_queues(&set->map[HCTX_TYPE_DEFAULT],
|
|
vblk->vdev, 0);
|
|
}
|
|
|
|
static const struct blk_mq_ops virtio_mq_ops = {
|
|
.queue_rq = virtio_queue_rq,
|
|
.commit_rqs = virtio_commit_rqs,
|
|
.complete = virtblk_request_done,
|
|
.init_request = virtblk_init_request,
|
|
.map_queues = virtblk_map_queues,
|
|
};
|
|
|
|
static unsigned int virtblk_queue_depth;
|
|
module_param_named(queue_depth, virtblk_queue_depth, uint, 0444);
|
|
|
|
static int virtblk_probe(struct virtio_device *vdev)
|
|
{
|
|
struct virtio_blk *vblk;
|
|
struct request_queue *q;
|
|
int err, index;
|
|
|
|
u32 v, blk_size, max_size, sg_elems, opt_io_size;
|
|
u16 min_io_size;
|
|
u8 physical_block_exp, alignment_offset;
|
|
|
|
if (!vdev->config->get) {
|
|
dev_err(&vdev->dev, "%s failure: config access disabled\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = ida_simple_get(&vd_index_ida, 0, minor_to_index(1 << MINORBITS),
|
|
GFP_KERNEL);
|
|
if (err < 0)
|
|
goto out;
|
|
index = err;
|
|
|
|
/* We need to know how many segments before we allocate. */
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SEG_MAX,
|
|
struct virtio_blk_config, seg_max,
|
|
&sg_elems);
|
|
|
|
/* We need at least one SG element, whatever they say. */
|
|
if (err || !sg_elems)
|
|
sg_elems = 1;
|
|
|
|
/* We need an extra sg elements at head and tail. */
|
|
sg_elems += 2;
|
|
vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL);
|
|
if (!vblk) {
|
|
err = -ENOMEM;
|
|
goto out_free_index;
|
|
}
|
|
|
|
/* This reference is dropped in virtblk_remove(). */
|
|
refcount_set(&vblk->refs, 1);
|
|
mutex_init(&vblk->vdev_mutex);
|
|
|
|
vblk->vdev = vdev;
|
|
vblk->sg_elems = sg_elems;
|
|
|
|
INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
|
|
|
|
err = init_vq(vblk);
|
|
if (err)
|
|
goto out_free_vblk;
|
|
|
|
/* FIXME: How many partitions? How long is a piece of string? */
|
|
vblk->disk = alloc_disk(1 << PART_BITS);
|
|
if (!vblk->disk) {
|
|
err = -ENOMEM;
|
|
goto out_free_vq;
|
|
}
|
|
|
|
/* Default queue sizing is to fill the ring. */
|
|
if (!virtblk_queue_depth) {
|
|
virtblk_queue_depth = vblk->vqs[0].vq->num_free;
|
|
/* ... but without indirect descs, we use 2 descs per req */
|
|
if (!virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC))
|
|
virtblk_queue_depth /= 2;
|
|
}
|
|
|
|
memset(&vblk->tag_set, 0, sizeof(vblk->tag_set));
|
|
vblk->tag_set.ops = &virtio_mq_ops;
|
|
vblk->tag_set.queue_depth = virtblk_queue_depth;
|
|
vblk->tag_set.numa_node = NUMA_NO_NODE;
|
|
vblk->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
|
|
vblk->tag_set.cmd_size =
|
|
sizeof(struct virtblk_req) +
|
|
sizeof(struct scatterlist) * sg_elems;
|
|
vblk->tag_set.driver_data = vblk;
|
|
vblk->tag_set.nr_hw_queues = vblk->num_vqs;
|
|
|
|
err = blk_mq_alloc_tag_set(&vblk->tag_set);
|
|
if (err)
|
|
goto out_put_disk;
|
|
|
|
q = blk_mq_init_queue(&vblk->tag_set);
|
|
if (IS_ERR(q)) {
|
|
err = -ENOMEM;
|
|
goto out_free_tags;
|
|
}
|
|
vblk->disk->queue = q;
|
|
|
|
q->queuedata = vblk;
|
|
|
|
virtblk_name_format("vd", index, vblk->disk->disk_name, DISK_NAME_LEN);
|
|
|
|
vblk->disk->major = major;
|
|
vblk->disk->first_minor = index_to_minor(index);
|
|
vblk->disk->private_data = vblk;
|
|
vblk->disk->fops = &virtblk_fops;
|
|
vblk->disk->flags |= GENHD_FL_EXT_DEVT;
|
|
vblk->index = index;
|
|
|
|
/* configure queue flush support */
|
|
virtblk_update_cache_mode(vdev);
|
|
|
|
/* If disk is read-only in the host, the guest should obey */
|
|
if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO))
|
|
set_disk_ro(vblk->disk, 1);
|
|
|
|
/* We can handle whatever the host told us to handle. */
|
|
blk_queue_max_segments(q, vblk->sg_elems-2);
|
|
|
|
/* No real sector limit. */
|
|
blk_queue_max_hw_sectors(q, -1U);
|
|
|
|
max_size = virtio_max_dma_size(vdev);
|
|
|
|
/* Host can optionally specify maximum segment size and number of
|
|
* segments. */
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,
|
|
struct virtio_blk_config, size_max, &v);
|
|
if (!err)
|
|
max_size = min(max_size, v);
|
|
|
|
blk_queue_max_segment_size(q, max_size);
|
|
|
|
/* Host can optionally specify the block size of the device */
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_BLK_SIZE,
|
|
struct virtio_blk_config, blk_size,
|
|
&blk_size);
|
|
if (!err) {
|
|
err = blk_validate_block_size(blk_size);
|
|
if (err) {
|
|
dev_err(&vdev->dev,
|
|
"virtio_blk: invalid block size: 0x%x\n",
|
|
blk_size);
|
|
goto out_cleanup_disk;
|
|
}
|
|
|
|
blk_queue_logical_block_size(q, blk_size);
|
|
} else
|
|
blk_size = queue_logical_block_size(q);
|
|
|
|
/* Use topology information if available */
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
|
|
struct virtio_blk_config, physical_block_exp,
|
|
&physical_block_exp);
|
|
if (!err && physical_block_exp)
|
|
blk_queue_physical_block_size(q,
|
|
blk_size * (1 << physical_block_exp));
|
|
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
|
|
struct virtio_blk_config, alignment_offset,
|
|
&alignment_offset);
|
|
if (!err && alignment_offset)
|
|
blk_queue_alignment_offset(q, blk_size * alignment_offset);
|
|
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
|
|
struct virtio_blk_config, min_io_size,
|
|
&min_io_size);
|
|
if (!err && min_io_size)
|
|
blk_queue_io_min(q, blk_size * min_io_size);
|
|
|
|
err = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,
|
|
struct virtio_blk_config, opt_io_size,
|
|
&opt_io_size);
|
|
if (!err && opt_io_size)
|
|
blk_queue_io_opt(q, blk_size * opt_io_size);
|
|
|
|
if (virtio_has_feature(vdev, VIRTIO_BLK_F_DISCARD)) {
|
|
virtio_cread(vdev, struct virtio_blk_config,
|
|
discard_sector_alignment, &v);
|
|
if (v)
|
|
q->limits.discard_granularity = v << SECTOR_SHIFT;
|
|
else
|
|
q->limits.discard_granularity = blk_size;
|
|
|
|
virtio_cread(vdev, struct virtio_blk_config,
|
|
max_discard_sectors, &v);
|
|
blk_queue_max_discard_sectors(q, v ? v : UINT_MAX);
|
|
|
|
virtio_cread(vdev, struct virtio_blk_config, max_discard_seg,
|
|
&v);
|
|
|
|
/*
|
|
* max_discard_seg == 0 is out of spec but we always
|
|
* handled it.
|
|
*/
|
|
if (!v)
|
|
v = sg_elems - 2;
|
|
blk_queue_max_discard_segments(q,
|
|
min(v, MAX_DISCARD_SEGMENTS));
|
|
|
|
blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
|
|
}
|
|
|
|
if (virtio_has_feature(vdev, VIRTIO_BLK_F_WRITE_ZEROES)) {
|
|
virtio_cread(vdev, struct virtio_blk_config,
|
|
max_write_zeroes_sectors, &v);
|
|
blk_queue_max_write_zeroes_sectors(q, v ? v : UINT_MAX);
|
|
}
|
|
|
|
virtblk_update_capacity(vblk, false);
|
|
virtio_device_ready(vdev);
|
|
|
|
device_add_disk(&vdev->dev, vblk->disk, virtblk_attr_groups);
|
|
return 0;
|
|
|
|
out_cleanup_disk:
|
|
blk_cleanup_queue(vblk->disk->queue);
|
|
out_free_tags:
|
|
blk_mq_free_tag_set(&vblk->tag_set);
|
|
out_put_disk:
|
|
put_disk(vblk->disk);
|
|
out_free_vq:
|
|
vdev->config->del_vqs(vdev);
|
|
kfree(vblk->vqs);
|
|
out_free_vblk:
|
|
kfree(vblk);
|
|
out_free_index:
|
|
ida_simple_remove(&vd_index_ida, index);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static void virtblk_remove(struct virtio_device *vdev)
|
|
{
|
|
struct virtio_blk *vblk = vdev->priv;
|
|
|
|
/* Make sure no work handler is accessing the device. */
|
|
flush_work(&vblk->config_work);
|
|
|
|
del_gendisk(vblk->disk);
|
|
blk_cleanup_queue(vblk->disk->queue);
|
|
|
|
blk_mq_free_tag_set(&vblk->tag_set);
|
|
|
|
mutex_lock(&vblk->vdev_mutex);
|
|
|
|
/* Stop all the virtqueues. */
|
|
vdev->config->reset(vdev);
|
|
|
|
/* Virtqueues are stopped, nothing can use vblk->vdev anymore. */
|
|
vblk->vdev = NULL;
|
|
|
|
put_disk(vblk->disk);
|
|
vdev->config->del_vqs(vdev);
|
|
kfree(vblk->vqs);
|
|
|
|
mutex_unlock(&vblk->vdev_mutex);
|
|
|
|
virtblk_put(vblk);
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int virtblk_freeze(struct virtio_device *vdev)
|
|
{
|
|
struct virtio_blk *vblk = vdev->priv;
|
|
|
|
/* Ensure we don't receive any more interrupts */
|
|
vdev->config->reset(vdev);
|
|
|
|
/* Make sure no work handler is accessing the device. */
|
|
flush_work(&vblk->config_work);
|
|
|
|
blk_mq_quiesce_queue(vblk->disk->queue);
|
|
|
|
vdev->config->del_vqs(vdev);
|
|
kfree(vblk->vqs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtblk_restore(struct virtio_device *vdev)
|
|
{
|
|
struct virtio_blk *vblk = vdev->priv;
|
|
int ret;
|
|
|
|
ret = init_vq(vdev->priv);
|
|
if (ret)
|
|
return ret;
|
|
|
|
virtio_device_ready(vdev);
|
|
|
|
blk_mq_unquiesce_queue(vblk->disk->queue);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct virtio_device_id id_table[] = {
|
|
{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
|
|
{ 0 },
|
|
};
|
|
|
|
static unsigned int features_legacy[] = {
|
|
VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY,
|
|
VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
|
|
VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,
|
|
VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES,
|
|
}
|
|
;
|
|
static unsigned int features[] = {
|
|
VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY,
|
|
VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
|
|
VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,
|
|
VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES,
|
|
};
|
|
|
|
static struct virtio_driver virtio_blk = {
|
|
.feature_table = features,
|
|
.feature_table_size = ARRAY_SIZE(features),
|
|
.feature_table_legacy = features_legacy,
|
|
.feature_table_size_legacy = ARRAY_SIZE(features_legacy),
|
|
.driver.name = KBUILD_MODNAME,
|
|
.driver.owner = THIS_MODULE,
|
|
.id_table = id_table,
|
|
.probe = virtblk_probe,
|
|
.remove = virtblk_remove,
|
|
.config_changed = virtblk_config_changed,
|
|
#ifdef CONFIG_PM_SLEEP
|
|
.freeze = virtblk_freeze,
|
|
.restore = virtblk_restore,
|
|
#endif
|
|
};
|
|
|
|
static int __init init(void)
|
|
{
|
|
int error;
|
|
|
|
virtblk_wq = alloc_workqueue("virtio-blk", 0, 0);
|
|
if (!virtblk_wq)
|
|
return -ENOMEM;
|
|
|
|
major = register_blkdev(0, "virtblk");
|
|
if (major < 0) {
|
|
error = major;
|
|
goto out_destroy_workqueue;
|
|
}
|
|
|
|
error = register_virtio_driver(&virtio_blk);
|
|
if (error)
|
|
goto out_unregister_blkdev;
|
|
return 0;
|
|
|
|
out_unregister_blkdev:
|
|
unregister_blkdev(major, "virtblk");
|
|
out_destroy_workqueue:
|
|
destroy_workqueue(virtblk_wq);
|
|
return error;
|
|
}
|
|
|
|
static void __exit fini(void)
|
|
{
|
|
unregister_virtio_driver(&virtio_blk);
|
|
unregister_blkdev(major, "virtblk");
|
|
destroy_workqueue(virtblk_wq);
|
|
}
|
|
module_init(init);
|
|
module_exit(fini);
|
|
|
|
MODULE_DEVICE_TABLE(virtio, id_table);
|
|
MODULE_DESCRIPTION("Virtio block driver");
|
|
MODULE_LICENSE("GPL");
|