b9d4c135c7
This merges up to the 5.10.226 LTS release into android12-5.10. Included in here are the following commits: *bfa0f472d5
Revert "udf: Avoid excessive partition lengths" *90336334a0
Revert "bareudp: Fix device stats updates." *bcfc839140
ANDROID: fix up change to pti_clone_pgtable() *ebdacb6176
Revert "perf/aux: Fix AUX buffer serialization" *3c59c9aebf
Revert "clocksource/drivers/timer-of: Remove percpu irq related code" *7d3ca1ed3f
Merge 5.10.226 into android12-5.10-lts |\ | *ceb091e2c4
Linux 5.10.226 | *912736a043
memcg: protect concurrent access to mem_cgroup_idr | *02ee1976ed
net, sunrpc: Remap EPERM in case of connection failure in xs_tcp_setup_socket | *dad75cf2c3
x86/mm: Fix PTI for i386 some more | *1401da1486
rtmutex: Drop rt_mutex::wait_lock before scheduling | *c6bd80f585
mmc: cqhci: Fix checking of CQHCI_HALT state | *b35d3c8181
drm/i915/fence: Mark debug_fence_free() with __maybe_unused | *b8dfa35f00
drm/i915/fence: Mark debug_fence_init_onstack() with __maybe_unused | *50632b877c
nvmet-tcp: fix kernel crash if commands allocation fails | *4c3b21204a
arm64: acpi: Harden get_cpu_for_acpi_id() against missing CPU entry | *ccb95b37e9
arm64: acpi: Move get_cpu_for_acpi_id() to a header | *3658388cd3
ACPI: processor: Fix memory leaks in error paths of processor_add() | *5dac987d1b
ACPI: processor: Return an error if acpi_processor_get_info() fails in processor_add() | *157c0d94b4
nilfs2: protect references to superblock parameters exposed in sysfs | *0630e3d435
nilfs2: replace snprintf in show functions with sysfs_emit | *7882923f1c
perf/aux: Fix AUX buffer serialization | *0f511f2840
uprobes: Use kzalloc to allocate xol area | *0af6b80dac
clocksource/drivers/timer-of: Remove percpu irq related code | *3ded318cf0
clocksource/drivers/imx-tpm: Fix next event not taking effect sometime | *cf6ffb1688
clocksource/drivers/imx-tpm: Fix return -ETIME when delta exceeds INT_MAX | *6c563a2985
VMCI: Fix use-after-free when removing resource in vmci_resource_remove() | *359ea5edc9
Drivers: hv: vmbus: Fix rescind handling in uio_hv_generic | *1d8e020e51
uio_hv_generic: Fix kernel NULL pointer dereference in hv_uio_rescind | *38cd8bde8a
nvmem: Fix return type of devm_nvmem_device_get() in kerneldoc | *3a8154bb4a
binder: fix UAF caused by offsets overwrite | *d0d3edb56e
iio: adc: ad7124: fix chip ID mismatch | *1719ebc8e3
iio: fix scale application in iio_convert_raw_to_processed_unlocked | *f3a54c27ba
iio: buffer-dmaengine: fix releasing dma channel on error | *41cc91e313
staging: iio: frequency: ad9834: Validate frequency parameter value | *d8a61e69f8
NFSv4: Add missing rescheduling points in nfs_client_return_marked_delegations | *6fb7b7f5ba
ata: pata_macio: Use WARN instead of BUG | *d3ff0f98a5
MIPS: cevt-r4k: Don't call get_c0_compare_int if timer irq is installed | *99418ec776
lib/generic-radix-tree.c: Fix rare race in __genradix_ptr_alloc() | *9d1e9f0876
of/irq: Prevent device address out-of-bounds read in interrupt map walk | *5c8906de98
Squashfs: sanity check symbolic link size | *2f14160d9f
usbnet: ipheth: race between ipheth_close and error handling | *51fa08edd8
Input: uinput - reject requests with unreasonable number of slots | *34185de73d
HID: cougar: fix slab-out-of-bounds Read in cougar_report_fixup | *3206e4a4b0
s390/vmlinux.lds.S: Move ro_after_init section behind rodata section | *912bcdc51b
btrfs: initialize location to fix -Wmaybe-uninitialized in btrfs_lookup_dentry() | *3eaad59258
kselftests: dmabuf-heaps: Ensure the driver name is null-terminated | *e6f3008de8
net: dpaa: avoid on-stack arrays of NR_CPUS elements | *e2355d513b
PCI: Add missing bridge lock to pci_bus_lock() | *c60676b81f
btrfs: clean up our handling of refs == 0 in snapshot delete | *ed1b61398c
btrfs: replace BUG_ON with ASSERT in walk_down_proc() | *8780129cbc
smp: Add missing destroy_work_on_stack() call in smp_call_on_cpu() | *9813770f25
wifi: mwifiex: Do not return unused priv in mwifiex_get_priv_by_id() | *fb2257089a
libbpf: Add NULL checks to bpf_object__{prev_map,next_map} | *56cfdeb2c7
hwmon: (w83627ehf) Fix underflows seen when writing limit attributes | *8a1e958e26
hwmon: (nct6775-core) Fix underflows seen when writing limit attributes | *59c1fb9874
hwmon: (lm95234) Fix underflows seen when writing limit attributes | *2a3add62f1
hwmon: (adc128d818) Fix underflows seen when writing limit attributes | *bc1faed19d
pci/hotplug/pnv_php: Fix hotplug driver crash on Powernv | *9b884bdc29
devres: Initialize an uninitialized struct member | *c8944d449f
um: line: always fill *error_out in setup_one_line() | *1434b72a2d
cgroup: Protect css->cgroup write under css_set_lock | *70854bf003
iommu/vt-d: Handle volatile descriptor status read | *8a7ef20bf7
dm init: Handle minors larger than 255 | *583b5d2d43
ASoC: topology: Properly initialize soc_enum values | *43b442c972
net: dsa: vsc73xx: fix possible subblocks range of CAPT block | *19af8a23a1
net: bridge: br_fdb_external_learn_add(): always set EXT_LEARN | *231c235d2f
fou: Fix null-ptr-deref in GRO. | *0ea3f2798d
gro: remove rcu_read_lock/rcu_read_unlock from gro_complete handlers | *77ad44ee33
gro: remove rcu_read_lock/rcu_read_unlock from gro_receive handlers | *bc18f3c806
fou: remove sparse errors | *3c0cedc22c
bareudp: Fix device stats updates. | *32cbafeebf
usbnet: modern method to get random MAC | *594cc1dba0
net: usb: don't write directly to netdev->dev_addr | *98a4cabf87
drivers/net/usb: Remove all strcpy() uses | *acd2985137
igc: Unlock on error in igc_io_resume() | *3efe53eb22
tcp_bpf: fix return value of tcp_bpf_sendmsg() | *ee1c2ecf7b
platform/x86: dell-smbios: Fix error path in dell_smbios_init() | *45c0c747df
svcrdma: Catch another Reply chunk overflow case | *449d70b16b
igb: Fix not clearing TimeSync interrupts for 82580 | *aec92dbebd
can: bcm: Remove proc entry when dev is unregistered. | *ee50abebdc
pcmcia: Use resource_size function on resource object | *9380fe33ab
media: qcom: camss: Add check for v4l2_fwnode_endpoint_parse | *ebbdbbc580
PCI: keystone: Add workaround for Errata #i2037 (AM65x SR 1.0) | *1fa40e0d27
media: vivid: don't set HDMI TX controls if there are no HDMI outputs | *44a595f897
usb: uas: set host status byte on data completion error | *3ab3ee4125
wifi: brcmsmac: advertise MFP_CAPABLE to enable WPA3 | *9e28a1df18
leds: spi-byte: Call of_node_put() on error path | *e73b63f138
media: vivid: fix wrong sizeimage value for mplane | *551966371e
udf: Avoid excessive partition lengths | *66234da64d
netfilter: nf_conncount: fix wrong variable type | *f56089a180
iommu: sun50i: clear bypass register | *1c5bad90e0
af_unix: Remove put_pid()/put_cred() in copy_peercred(). | *ec08e30082
irqchip/armada-370-xp: Do not allow mapping IRQ 0 and 1 | *500e4bf673
smack: unix sockets: fix accept()ed socket label | *414736fcb7
ALSA: hda: Add input value sanity checks to HDMI channel map controls * |70fe52b634
Revert "Merge751777a79a
("nfsd: make svc_stat per-network namespace instead of global") into android12-5.10-lts" * |c3e9a280ba
Merge751777a79a
("nfsd: make svc_stat per-network namespace instead of global") into android12-5.10-lts |\| | *751777a79a
nfsd: make svc_stat per-network namespace instead of global | *f8219c4b80
nfsd: remove nfsd_stats, make th_cnt a global counter | *f2fe1ec906
nfsd: make all of the nfsd stats per-network namespace | *5545496966
nfsd: expose /proc/net/sunrpc/nfsd in net namespaces | *fec6561e75
nfsd: rename NFSD_NET_* to NFSD_STATS_* | *9eb5d44b8f
sunrpc: use the struct net as the svc proc private | *e0fba78ab9
sunrpc: remove ->pg_stats from svc_program | *7f2476914e
sunrpc: pass in the sv_stats struct through svc_create_pooled | *d06254ae7d
nfsd: stop setting ->pg_stats for unused stats | *2197b23eda
sunrpc: don't change ->sv_stats if it doesn't exist | *d47c660e8c
NFSD: Fix frame size warning in svc_export_parse() | *a8aaffc0c1
NFSD: Rewrite synopsis of nfsd_percpu_counters_init() | *c532274202
NFSD: simplify error paths in nfsd_svc() | *ebfce8dd7e
NFSD: Refactor the duplicate reply cache shrinker | *895807268a
NFSD: Replace nfsd_prune_bucket() | *a02f9d6ea3
NFSD: Rename nfsd_reply_cache_alloc() | *73b72f4b3b
NFSD: Refactor nfsd_reply_cache_free_locked() | *3025d489f9
nfsd: move init of percpu reply_cache_stats counters back to nfsd_init_net | *4e18b58b10
nfsd: move reply cache initialization into nfsd startup * |00588cd66d
Revert "hwspinlock: Introduce hwspin_lock_bust()" * |c2345ad899
Revert "bpf, cgroups: Fix cgroup v2 fallback on v1/v2 mixed mode" * |fe709a1a77
Revert "bpf, cgroup: Assign cgroup in cgroup_sk_alloc when called from interrupt" * |b22678f8ef
Mergeddee5b4b6a
("mptcp: pm: avoid possible UaF when selecting endp") into android12-5.10-lts |\| | *ddee5b4b6a
mptcp: pm: avoid possible UaF when selecting endp | *91fb0512a0
mptcp: pr_debug: add missing \n at the end | *7e4c72dbaf
btrfs: fix use-after-free after failure to create a snapshot | *efdde00d4a
nilfs2: fix state management in error path of log writing function | *07e4dc2fe0
nilfs2: fix missing cleanup on rollforward recovery error | *7725152b54
sched: sch_cake: fix bulk flow accounting logic for host fairness | *93ee345ba3
ila: call nf_unregister_net_hooks() sooner | *e3ad85c477
tracing: Avoid possible softlockup in tracing_iter_reset() | *3a49b6b1ca
can: mcp251x: fix deadlock if an interrupt occurs during mcp251x_open | *6949a97f6d
clk: qcom: clk-alpha-pll: Fix the trion pll postdiv set rate API | *f540bc71d5
clk: qcom: clk-alpha-pll: Fix the pll post div mask | *0811d57384
fuse: use unsigned type for getxattr/listxattr size truncation | *9d38c704b4
fuse: update stats for pages in dropped aux writeback list | *4be36d9d18
mmc: sdhci-of-aspeed: fix module autoloading | *2793f42389
mmc: dw_mmc: Fix IDMAC operation with pages bigger than 4K | *b2ead09489
Bluetooth: MGMT: Ignore keys being loaded with invalid type | *029e462bb4
Revert "Bluetooth: MGMT/SMP: Fix address type when using SMP over BREDR/LE" | *cb27399b3d
irqchip/gic-v2m: Fix refcount leak in gicv2m_of_init() | *e0b122a8f6
ata: libata: Fix memory leak for error path in ata_host_alloc() | *0f27b8c07e
ALSA: hda/realtek: Support mute LED on HP Laptop 14-dq2xxx | *2ef683b058
ALSA: hda/realtek: add patch for internal mic in Lenovo V145 | *adc688a505
ALSA: hda/conexant: Add pincfg quirk to enable top speakers on Sirius devices | *8ca21e7a27
ASoC: dapm: Fix UAF for snd_soc_pcm_runtime object | *98c75d7618
sch/netem: fix use after free in netem_dequeue | *06e7be6934
bpf, cgroup: Assign cgroup in cgroup_sk_alloc when called from interrupt | *b140074560
i2c: Use IS_REACHABLE() for substituting empty ACPI functions | *dfc8eb4d7e
ext4: handle redirtying in ext4_bio_write_page() | *5895541d73
udf: Limit file size to 4TB | *17c43211d4
rcu-tasks: Fix show_rcu_tasks_trace_gp_kthread buffer overflow | *842a97b5e4
virtio_net: Fix napi_skb_cache_put warning | *c8e5439b5b
net: set SOCK_RCU_FREE before inserting socket into hashtable | *cf002be3b8
bpf, cgroups: Fix cgroup v2 fallback on v1/v2 mixed mode | *2ac9deb7e0
drm/amd/pm: Fix the null pointer dereference for vega10_hwmgr | *3fd11fe4f2
block: initialize integrity buffer to zero before writing it to media | *0623c9f371
media: uvcvideo: Enforce alignment of frame and interval | *c083c8be6b
drm/amd/display: Skip wbscl_set_scaler_filter if filter is null | *5eb04f9894
block: remove the blk_flush_integrity call in blk_integrity_unregister | *0305a885cc
wifi: cfg80211: make hash table duplicates more survivable | *d24bc270b7
drm/meson: plane: Add error handling | *a948ec9935
smack: tcp: ipv4, fix incorrect labeling | *3f3ef1d9f6
fsnotify: clear PARENT_WATCHED flags lazily | *7e64cabe81
usb: typec: ucsi: Fix null pointer dereference in trace | *bd13c1119a
usbip: Don't submit special requests twice | *c7975f09ae
ionic: fix potential irq name truncation | *e85cf9a5a4
hwspinlock: Introduce hwspin_lock_bust() | *7eb7888021
PCI: al: Check IORESOURCE_BUS existence during probe | *9aa7dd5e31
wifi: iwlwifi: remove fw_running op | *ed7e9ed973
drm/amd/pm: check negtive return for table entries | *614564a5b2
drm/amdgpu: the warning dereferencing obj for nbio_v7_4 | *008933832a
drm/amdgpu/pm: Check input value for CUSTOM profile mode setting on legacy SOCs | *52338a3aa7
apparmor: fix possible NULL pointer dereference | *0842db679d
drm/amdkfd: Reconcile the definition and use of oem_id in struct kfd_topology_device | *310b9d8363
drm/amdgpu: fix mc_data out-of-bounds read warning | *5f09fa5e0a
drm/amdgpu: fix ucode out-of-bounds read warning | *725b728cc0
drm/amdgpu: Fix out-of-bounds read of df_v1_7_channel_number | *c253b87c7c
drm/amdgpu: Fix out-of-bounds write warning | *60097df938
drm/amdgpu/pm: Fix uninitialized variable agc_btc_response | *74c5d8b057
drm/amd/display: Fix Coverity INTEGER_OVERFLOW within dal_gpio_service_create | *9160830546
drm/amd/display: Check msg_id before processing transcation | *7c47dd2e92
drm/amd/display: Check num_valid_sets before accessing reader_wm_sets[] | *2a63c90c7a
drm/amd/display: Add array index check for hdcp ddc access | *754321ed63
drm/amd/display: Stop amdgpu_dm initialize when stream nums greater than 6 | *40c2e8bc11
drm/amd/display: Check gpio_id before used as array index | *e24fa82729
drm/amdgpu: avoid reading vf2pf info size from FB | *1d0c85d0fc
drm/amd/pm: fix uninitialized variable warnings for vega10_hwmgr | *59ac791297
drm/amdgpu: fix uninitialized scalar variable warning | *38e32a0d83
drm/amd/pm: fix the Out-of-bounds read warning | *d592768c17
drm/amd/pm: fix warning using uninitialized value of max_vid_step | *a601129c78
drm/amd/pm: fix uninitialized variable warning for smu8_hwmgr | *774bae3b8d
drm/amdgpu: fix overflowed array index read warning | *28b539bbcc
drm/amdgpu: Fix uninitialized variable warning in amdgpu_afmt_acr | *40d0fedacf
net: usb: qmi_wwan: add MeiG Smart SRM825L | *ff5af3f9b5
dma-debug: avoid deadlock between dma debug vs printk and netconsole | *712921d2ab
i2c: Fix conditional for substituting empty ACPI functions | *0e69cf9b65
ALSA: hda/conexant: Mute speakers at suspend / shutdown | *221ebded43
ALSA: hda/generic: Add a helper to mute speakers at suspend/shutdown | *e78bc7099c
drm: panel-orientation-quirks: Add quirk for OrangePi Neo * |52c4910c65
ANDROID: fix up crc issue for cpuset_cpus_allowed() * |4951c68022
Merge 5.10.225 into android12-5.10-lts |\| | *b57d01c66f
Linux 5.10.225 | *7e8bad2cf3
apparmor: fix policy_unpack_test on big endian systems | *9e96dea7ef
scsi: aacraid: Fix double-free on probe failure | *4538335cc2
usb: core: sysfs: Unmerge @usb3_hardware_lpm_attr_group in remove_power_attributes() | *59579a627a
usb: dwc3: st: add missing depopulate in probe error path | *6aee4c5635
usb: dwc3: st: fix probed platform device ref count on probe error path | *b72da4d89b
usb: dwc3: core: Prevent USB core invalid event buffer address access | *16cc6114c9
usb: dwc3: omap: add missing depopulate in probe error path | *f84d5dccc8
USB: serial: option: add MeiG Smart SRM825L | *612843f842
cdc-acm: Add DISABLE_ECHO quirk for GE HealthCare UI Controller | *f5a5a5a0e9
soc: qcom: cmd-db: Map shared memory as WC, not WB | *8ddaea033d
nfc: pn533: Add poll mod list filling check | *7e5d5c4ae7
net: busy-poll: use ktime_get_ns() instead of local_clock() | *8bbb9e4e0e
gtp: fix a potential NULL pointer dereference | *842a40c727
ethtool: check device is present when getting link settings | *2e8e93dea0
dmaengine: dw: Add memory bus width verification | *9cfe7c53fe
dmaengine: dw: Add peripheral bus width verification | *f8e1c92868
soundwire: stream: fix programming slave ports for non-continous port maps | *acddd7c6b7
ovl: do not fail because of O_NOATIME | *338a3ba30c
net:rds: Fix possible deadlock in rds_message_put | *688325078a
cgroup/cpuset: Prevent UAF in proc_cpuset_show() | *e83405e75d
ata: libata-core: Fix null pointer dereference on error | *f2b6cd1335
Revert "Input: ioc3kbd - convert to platform remove callback returning void" | *777d9c223e
media: uvcvideo: Fix integer overflow calculating timestamp | *f7276cdc19
drm/amdkfd: don't allow mapping the MMIO HDP page with large pages | *0365c9029a
ipc: replace costly bailout check in sysvipc_find_ipc() | *2933b4f8a6
mptcp: sched: check both backup in retrans | *1388df72dc
wifi: mwifiex: duplicate static structs used in driver instances | *4e9436375f
pinctrl: single: fix potential NULL dereference in pcs_get_function() | *d57e6298cc
pinctrl: rockchip: correct RK3328 iomux width flag for GPIO2-B pins | *a45ee4c98d
KVM: arm64: Don't use cbz/adr with external symbols | *df02642c21
drm/amdgpu: Using uninitialized value *size when calling amdgpu_vce_cs_reloc | *239b1cacce
tools: move alignment-related macros to new <linux/align.h> | *05dd9aabd0
Input: MT - limit max slots | *56b82e6ff3
Bluetooth: hci_ldisc: check HCI_UART_PROTO_READY flag in HCIUARTGETPROTO | *93000b2949
nfsd: Don't call freezable_schedule_timeout() after each successful page allocation in svc_alloc_arg(). | *b009444700
ALSA: timer: Relax start tick time check for slave timer elements | *b891438bc3
Revert "drm/amd/display: Validate hw_points_num before using it" | *92915fa734
mmc: dw_mmc: allow biu and ciu clocks to defer | *15818af2f7
KVM: arm64: Make ICC_*SGI*_EL1 undef in the absence of a vGICv3 | *65e79c9437
cxgb4: add forgotten u64 ivlan cast before shift | *d1623e7b43
HID: microsoft: Add rumble support to latest xbox controllers | *8c0a21d37d
HID: wacom: Defer calculation of resolution until resolution_code is known | *fc73103a94
MIPS: Loongson64: Set timer mode in cpu-probe | *7fd3a59268
binfmt_misc: pass binfmt_misc flags to the interpreter | *9df9783bd8
Bluetooth: MGMT: Add error handling to pair_device() | *9b9ba386d7
mmc: mmc_test: Fix NULL dereference on allocation failure | *4370448fca
drm/msm/dp: reset the link phy params before link training | *e54b082752
drm/msm/dpu: don't play tricks with debug macros | *ff6607a477
net: xilinx: axienet: Fix dangling multicast addresses | *2884e73978
net: xilinx: axienet: Always disable promiscuous mode | *cb5880a0de
ipv6: prevent UAF in ip6_send_skb() | *c414000da1
netem: fix return value if duplicate enqueue fails | *050e7274ab
net: dsa: mv88e6xxx: Fix out-of-bound access | *5885217d66
net: dsa: mv88e6xxx: replace ATU violation prints with trace points | *5d8aed3ca6
net: dsa: mv88e6xxx: read FID when handling ATU violations | *544571911b
ice: fix ICE_LAST_OFFSET formula | *5c14483544
bonding: fix xfrm state handling when clearing active slave | *21816b696c
bonding: fix xfrm real_dev null pointer dereference | *81216b9352
bonding: fix null pointer deref in bond_ipsec_offload_ok | *e8c85f2ff3
bonding: fix bond_ipsec_offload_ok return type | *6e630e1d77
ip6_tunnel: Fix broken GRO | *4d42a2257b
netfilter: nft_counter: Synchronize nft_counter_reset() against reader. | *eb06c8d302
kcm: Serialise kcm_sendmsg() for the same socket. | *f4b762cf7e
tc-testing: don't access non-existent variable on exception | *095a1f19d4
Bluetooth: SMP: Fix assumption of Central always being Initiator | *7a4e7a0c6b
Bluetooth: hci_core: Fix LE quote calculation | *ce70b09150
dm suspend: return -ERESTARTSYS instead of -EINTR | *0ba3401777
media: solo6x10: replace max(a, min(b, c)) by clamp(b, a, c) | *d1bd8e0a11
block: use "unsigned long" for blk_validate_block_size(). | *cbb9a969fc
gtp: pull network headers in gtp_dev_xmit() | *5970a540da
hrtimer: Prevent queuing of hrtimer without a function callback | *b09a5ec8de
nvmet-rdma: fix possible bad dereference when freeing rsps | *2143cba143
ext4: set the type of max_zeroout to unsigned int to avoid overflow | *f14cd61826
irqchip/gic-v3-its: Remove BUG_ON in its_vpe_irq_domain_alloc | *9e1c4d0d6a
usb: dwc3: core: Skip setting event buffers for host only controllers | *1b8e318f99
s390/iucv: fix receive buffer virtual vs physical address confusion | *d0414f5436
openrisc: Call setup_memory() earlier in the init sequence | *e5272645a0
NFS: avoid infinite loop in pnfs_update_layout. | *9e0414220b
nvmet-tcp: do not continue for invalid icreq | *5ee7495ac2
net: hns3: add checking for vf id of mailbox | *c7c43a784f
Bluetooth: bnep: Fix out-of-bound access | *bf2f79970b
usb: gadget: fsl: Increase size of name buffer for endpoints | *bf0c603ab4
f2fs: fix to do sanity check in update_sit_entry | *8ec052c544
btrfs: delete pointless BUG_ON check on quota root in btrfs_qgroup_account_extent() | *0c1d7b960f
btrfs: send: handle unexpected data in header buffer in begin_cmd() | *94a7dff229
btrfs: handle invalid root reference found in may_destroy_subvol() | *3dd13074e7
btrfs: change BUG_ON to assertion when checking for delayed_node root | *e21448a49b
powerpc/boot: Only free if realloc() succeeds | *486fb5ebd5
powerpc/boot: Handle allocation failure in simple_realloc() | *05c21f285d
parisc: Use irq_enter_rcu() to fix warning at kernel/context_tracking.c:367 | *4e5464005b
memory: stm32-fmc2-ebi: check regmap_read return value | *25d31baf92
x86: Increase brk randomness entropy for 64-bit systems | *76ec27b709
md: clean up invalid BUG_ON in md_ioctl | *95e49b9258
netlink: hold nlk->cb_mutex longer in __netlink_dump_start() | *316bf51edd
virtiofs: forbid newlines in tags | *be49c4f2a1
drm/lima: set gp bus_stop bit before hard reset | *aa469c3d28
net/sun3_82586: Avoid reading past buffer in debug output | *5fb0cbf84b
scsi: lpfc: Initialize status local variable in lpfc_sli4_repost_sgl_list() | *a441ce39ad
fs: binfmt_elf_efpic: don't use missing interpreter's properties | *e7385510e2
media: pci: cx23885: check cx23885_vdev_init() return | *00d4f971fa
quota: Remove BUG_ON from dqget() | *239c5e988e
ext4: do not trim the group with corrupted block bitmap | *0f6425d90d
nvmet-trace: avoid dereferencing pointer too early | *5380f1b2b9
powerpc/xics: Check return value of kasprintf in icp_native_map_one_cpu | *372928e8be
IB/hfi1: Fix potential deadlock on &irq_src_lock and &dd->uctxt_lock | *7138c59856
wifi: iwlwifi: abort scan when rfkill on but device enabled | *d483de53d4
gfs2: setattr_chown: Add missing initialization | *80456d39f0
scsi: spi: Fix sshdr use | *3663e78fab
media: qcom: venus: fix incorrect return value | *a43edc7abc
binfmt_misc: cleanup on filesystem umount | *c13541c5ef
staging: ks7010: disable bh on tx_dev_lock | *db3b679f66
drm/amd/display: Validate hw_points_num before using it | *cc49ee3433
staging: iio: resolver: ad2s1210: fix use before initialization | *01fa4415c3
media: radio-isa: use dev_name to fill in bus_info | *0f83d77926
s390/smp,mcck: fix early IPI handling | *aeda7043c4
RDMA/rtrs: Fix the problem of variable not initialized fully | *bbb662d0c2
i2c: riic: avoid potential division by zero | *5335c7f8db
wifi: cw1200: Avoid processing an invalid TIM IE | *11b0c7323c
wifi: mac80211: fix BA session teardown race | *5fe7bdbe4f
ssb: Fix division by zero issue in ssb_calc_clock_rate | *dfa894f7ea
ALSA: hda/realtek: Fix noise from speakers on Lenovo IdeaPad 3 15IAU7 | *fc250eca15
net: hns3: fix a deadlock problem when config TC during resetting | *dbdbadec8a
net: hns3: fix wrong use of semaphore up | *e5ceff2196
netfilter: flowtable: initialise extack before use | *50c914b0e6
mptcp: correct MPTCP_SUBFLOW_ATTR_SSN_OFFSET reserved size | *8e8d306f3b
net: dsa: vsc73xx: check busy flag in MDIO operations | *351ad72c50
net: dsa: vsc73xx: use read_poll_timeout instead delay loop | *665a4caa9c
net: dsa: vsc73xx: pass value in phy_write operation | *aa9ce4193c
net: axienet: Fix register defines comment description | *1cece837e3
atm: idt77252: prevent use after free in dequeue_rx() | *4b730a1475
net/mlx5e: Correctly report errors for ethtool rx flows | *8e0e6b15ab
s390/uv: Panic for set and remove shared access UVC errors | *6bcd0f95b8
btrfs: rename bitmap_set_bits() -> btrfs_bitmap_set_bits() | *c10ac31a72
s390/cio: rename bitmap_size() -> idset_bitmap_size() | *e24625310c
drm/amdgpu/jpeg2: properly set atomics vmid field | *ad149f5585
memcg_write_event_control(): fix a user-triggerable oops | *0452e15e7f
drm/amdgpu: Actually check flags for all context ops. | *d88083916f
btrfs: tree-checker: add dev extent item checks | *bbcdda4b0d
selinux: fix potential counting error in avc_add_xperms_decision() | *fe5bf14881
fix bitmap corruption on close_range() with CLOSE_RANGE_UNSHARE | *de7be1940c
bitmap: introduce generic optimized bitmap_size() | *03880af02a
vfs: Don't evict inode under the inode lru traversing context | *ee030e4ffa
dm persistent data: fix memory allocation failure | *63fd38af88
dm resume: don't return EINVAL when signalled | *1b21a791af
arm64: ACPI: NUMA: initialize all values of acpi_early_node_map to NUMA_NO_NODE | *e245a18281
s390/dasd: fix error recovery leading to data corruption on ESE devices | *747bc15457
thunderbolt: Mark XDomain as unplugged when router is removed | *0f0654318e
xhci: Fix Panther point NULL pointer deref at full-speed re-enumeration | *4905e56f7b
ALSA: usb-audio: Support Yamaha P-125 quirk entry | *4690e2171f
fuse: Initialize beyond-EOF page contents before setting uptodate * |39a8a0618d
ANDROID: Fix gki allmodconfig build errors in mptcp * |0c105dabe6
Revert "genirq: Allow the PM device to originate from irq domain" * |e62a1579e0
Revert "genirq: Allow irq_chip registration functions to take a const irq_chip" * |b5df17128a
Revert "irqchip/imx-irqsteer: Constify irq_chip struct" * |6943c015b0
Revert "irqchip/imx-irqsteer: Add runtime PM support" * |3141b23999
Revert "irqchip/imx-irqsteer: Handle runtime power management correctly" * |b84ad15be5
Merge 5.10.224 into android12-5.10-lts |\| | *b2add7c50b
Linux 5.10.224 | *2de18b5cc3
media: Revert "media: dvb-usb: Fix unexpected infinite loop in dvb_usb_read_remote_control()" | *e1ee1c4198
ARM: dts: imx6qdl-kontron-samx6i: fix phy-mode | *80ac0cc9c0
wifi: cfg80211: restrict NL80211_ATTR_TXQ_QUANTUM values | *a563f12430
vhost-vdpa: switch to use vmf_insert_pfn() in the fault handler | *06e9e6ac59
vdpa: Make use of PFN_PHYS/PFN_UP/PFN_DOWN helper macro | *b21ea49e6e
nvme/pci: Add APST quirk for Lenovo N60z laptop | *15469d46ba
exec: Fix ToCToU between perm check and set-uid/gid usage | *d39e0f582b
media: uvcvideo: Use entity get_cur in uvc_ctrl_set | *ec54634f91
arm64: cpufeature: Fix the visibility of compat hwcaps | *fb6675db04
powerpc: Avoid nmi_enter/nmi_exit in real mode interrupt. | *50111a8098
drm/i915/gem: Fix Virtual Memory mapping boundaries calculation | *31c35f9f89
netfilter: nf_tables: prefer nft_chain_validate | *d5f87c1111
netfilter: nf_tables: allow clone callbacks to sleep | *7b17de2a71
netfilter: nf_tables: use timestamp to check for set element timeout | *191fc44395
netfilter: nf_tables: set element extended ACK reporting support | *c52f9e1a9e
PCI/DPC: Fix use-after-free on concurrent DPC and hot-removal | *7e62564d5e
Fix gcc 4.9 build issue in 5.10.y | *329eae03d0
Add gitignore file for samples/fanotify/ subdirectory | *9bdf0624bd
samples: Make fs-monitor depend on libc and headers | *5b9f49cc86
samples: Add fs error monitoring example | *3f84b37abb
mptcp: pm: fix backup support in signal endpoints | *44165604dd
mptcp: export local_address | *9b9a64ef9a
mptcp: mib: count MPJ with backup flag | *96f3c8a850
mptcp: fix NL PM announced address accounting | *1008f2bcbc
mptcp: distinguish rcv vs sent backup flag in requests | *381cad7a08
mptcp: sched: check both directions for backup | *32b133fb78
drm/mgag200: Set DDC timeout in milliseconds | *fd65cf86ca
drm/bridge: analogix_dp: properly handle zero sized AUX transactions | *450b6b22ac
x86/mtrr: Check if fixed MTRRs exist before saving them | *ab8b397d59
padata: Fix possible divide-by-0 panic in padata_mt_helper() | *eb223bf01e
tracing: Fix overflow in get_free_elt() | *ca2ea2dec1
power: supply: axp288_charger: Round constant_charge_voltage writes down | *51e8360d94
power: supply: axp288_charger: Fix constant_charge_voltage writes | *a26bcfeea3
genirq/irqdesc: Honor caller provided affinity in alloc_desc() | *db959cdfe6
irqchip/xilinx: Fix shift out of bounds | *52b138f102
serial: core: check uartclk for zero to avoid divide by zero | *227d455e6c
irqchip/meson-gpio: Convert meson_gpio_irq_controller::lock to 'raw_spinlock_t' | *7dddf560e2
irqchip/meson-gpio: support more than 8 channels gpio irq | *5f1aa8ce64
scsi: mpt3sas: Avoid IOMMU page faults on REPORT ZONES | *8f209716ea
scsi: mpt3sas: Remove scsi_dma_map() error messages | *f3405f4997
ntp: Safeguard against time_constant overflow | *f098e8fc72
driver core: Fix uevent_show() vs driver detach race | *dc335b92e5
ntp: Clamp maxerror and esterror to operating range | *668c6c4a7e
tick/broadcast: Move per CPU pointer access into the atomic section | *005c318981
scsi: ufs: core: Fix hba->last_dme_cmd_tstamp timestamp updating logic | *ef1b208ca8
usb: gadget: u_serial: Set start_delayed during suspend | *7cc9ebcfe5
usb: gadget: core: Check for unset descriptor | *f1205a5aad
USB: serial: debug: do not echo input by default | *4dacdb9720
usb: vhci-hcd: Do not drop references before new references are gained | *d993cb25ef
ALSA: hda/hdmi: Yet more pin fix for HP EliteDesk 800 G4 | *c7c1ca6e25
ALSA: hda: Add HP MP9 G4 Retail System AMS to force connect list | *e7e7d2b180
ALSA: line6: Fix racy access to midibuf | *5291d4f734
drm/client: fix null pointer dereference in drm_client_modeset_probe | *44e11ae8f9
ALSA: usb-audio: Re-add ScratchAmp quirk entries | *c9c11ece5a
spi: spi-fsl-lpspi: Fix scldiv calculation | *c6ba514732
kprobes: Fix to check symbol prefixes correctly | *9ddd5e7835
bpf: kprobe: remove unused declaring of bpf_kprobe_override | *455769ebb6
i2c: smbus: Send alert notifications to all devices if source not found | *56f106d2c4
ASoC: codecs: wsa881x: Correct Soundwire ports mask | *5605992ad4
i2c: smbus: Improve handling of stuck alerts | *706f18a8fa
arm64: errata: Expand speculative SSBS workaround (again) | *f261c5d8d0
arm64: cputype: Add Cortex-A725 definitions | *bdae104b09
arm64: cputype: Add Cortex-X1C definitions | *4a500d4bdc
arm64: errata: Expand speculative SSBS workaround | *bf0d247dfb
arm64: errata: Unify speculative SSBS errata logic | *17ff37fe45
arm64: cputype: Add Cortex-X925 definitions | *77741cdc25
arm64: cputype: Add Cortex-A720 definitions | *b8d683f5b5
arm64: cputype: Add Cortex-X3 definitions | *9f7ba00782
arm64: errata: Add workaround for Arm errata 3194386 and 3312417 | *d8029a49c8
arm64: cputype: Add Neoverse-V3 definitions | *c46b7570c9
arm64: cputype: Add Cortex-X4 definitions | *55920e407a
arm64: Add Neoverse-V2 part | *5b9ae6bb33
arm64: cpufeature: Force HWCAP to be based on the sysreg visible to user-space | *69299a4282
ext4: fix wrong unit use in ext4_mb_find_by_goal | *1d21d41750
sched/cputime: Fix mul_u64_u64_div_u64() precision for cputime | *3b2b169fad
SUNRPC: Fix a race to wake a sync task | *a3e52a4c22
s390/sclp: Prevent release of buffer in I/O | *1a6b4240b0
jbd2: avoid memleak in jbd2_journal_write_metadata_buffer | *e48a901ce6
media: uvcvideo: Fix the bandwdith quirk on USB 3.x | *de305abd36
media: uvcvideo: Ignore empty TS packets | *c1749313f3
drm/amdgpu/pm: Fix the null pointer dereference in apply_state_adjust_rules | *d81c1eeb33
drm/amdgpu: Fix the null pointer dereference to ras_manager | *1d4e65fa62
btrfs: fix bitmap leak when loading free space cache on duplicate entry | *29ce18d767
wifi: nl80211: don't give key data to userspace | *934f815345
udf: prevent integer overflow in udf_bitmap_free_blocks() | *65b982b9af
PCI: Add Edimax Vendor ID to pci_ids.h | *55985e3aa1
selftests/bpf: Fix send_signal test with nested CONFIG_PARAVIRT | *8e665ccc52
ACPI: SBS: manage alarm sysfs attribute through psy core | *85d8fe79a3
ACPI: battery: create alarm sysfs attribute atomically | *64ac0c0235
clocksource/drivers/sh_cmt: Address race condition for clock events | *c384dd4f1f
md/raid5: avoid BUG_ON() while continue reshape after reassembling | *5ccf99545c
md: do not delete safemode_timer in mddev_suspend | *464d242868
rcutorture: Fix rcu_torture_fwd_cb_cr() data race | *adc491f3e7
net: fec: Stop PPS on driver remove | *865948628a
l2tp: fix lockdep splat | *b7b8d9f5e6
net: dsa: bcm_sf2: Fix a possible memory leak in bcm_sf2_mdio_register() | *01150020c0
Bluetooth: l2cap: always unlock channel in l2cap_conless_channel() | *085fb116c4
net: linkwatch: use system_unbound_wq | *e87f52225e
net: usb: qmi_wwan: fix memory leak for not ip packets | *52319d9d2f
sctp: Fix null-ptr-deref in reuseport_add_sock(). | *17a93a8201
sctp: move hlist_node and hashent out of sctp_ep_common | *ba4e59f34c
x86/mm: Fix pti_clone_entry_text() for i386 | *d00c9b4bbc
x86/mm: Fix pti_clone_pgtable() alignment assumption | *75880302cf
irqchip/mbigen: Fix mbigen node address layout | *c476c5c7bb
genirq: Allow irq_chip registration functions to take a const irq_chip | *12fa993433
netfilter: ipset: Add list flush to cancel_gc | *e93fa44f07
mptcp: fix duplicate data handling | *3deac6f686
r8169: don't increment tx_dropped in case of NETDEV_TX_BUSY | *646e9e9071
net: usb: sr9700: fix uninitialized variable use in sr_mdio_read | *8b0a5709ac
ALSA: hda/realtek: Add quirk for Acer Aspire E5-574G | *7b745257ff
ALSA: usb-audio: Correct surround channels in UAC1 channel map | *08775b3d6e
protect the fetch of ->fd[fd] in do_dup2() from mispredictions | *e4b2b0306b
HID: wacom: Modify pen IDs | *b12a67976b
platform/chrome: cros_ec_proto: Lock device when updating MKBP version | *59be4a1677
riscv/mm: Add handling for VM_FAULT_SIGSEGV in mm_fault_error() | *7d72f51951
ipv6: fix ndisc_is_useropt() handling for PIO | *8e97cc828d
net/mlx5e: Add a check for the return value from mlx5_port_set_eth_ptys | *c65f72eec6
net/iucv: fix use after free in iucv_sock_close() | *7c03ab555e
sched: act_ct: take care of padding in struct zones_ht_key | *b17eeed7cd
drm/vmwgfx: Fix overlay when using Screen Targets | *906372e753
drm/nouveau: prime: fix refcount underflow | *6b50462b47
remoteproc: imx_rproc: Skip over memory region when node value is NULL | *5991ef8e7a
remoteproc: imx_rproc: Fix ignoring mapping vdev regions | *a4ed3286a5
remoteproc: imx_rproc: ignore mapping vdev regions | *3a2884a44e
irqchip/imx-irqsteer: Handle runtime power management correctly | *0548b54d0a
irqchip/imx-irqsteer: Add runtime PM support | *06a93b7203
irqchip/imx-irqsteer: Constify irq_chip struct | *652e7b4d73
genirq: Allow the PM device to originate from irq domain | *ef56dcdca8
devres: Fix memory leakage caused by driver API devm_free_percpu() | *81484ab285
driver core: Cast to (void *) with __force for __percpu pointer | *6bb9cc6e25
drivers: soc: xilinx: check return status of get_api_version() | *79ec4cde1d
soc: xilinx: move PM_INIT_FINALIZE to zynqmp_pm_domains driver | *58b07286ae
ext4: check the extent status again before inserting delalloc block | *4b6d9a0fe7
ext4: factor out a common helper to query extent map | *b2591c89a6
sysctl: always initialize i_uid/i_gid | *88f053a1dd
fuse: verify {g,u}id mount options correctly | *997d3c9cbe
fuse: name fs_context consistently | *2fa82af6fd
powerpc/configs: Update defconfig with now user-visible CONFIG_FSL_IFC | *d28869a145
fs: don't allow non-init s_user_ns for filesystems without FS_USERNS_MOUNT | *be23ae6308
nvme-pci: add missing condition check for existence of mapped data | *ce90f30157
nvme: split command copy into a helper | *b59013d264
ceph: fix incorrect kmalloc size of pagevec mempool | *eb1b7575fe
ASoC: Intel: use soc_intel_is_byt_cr() only when IOSF_MBI is reachable | *3ff4316953
lirc: rc_dev_get_from_fd(): fix file leak | *ea72a88810
powerpc: fix a file leak in kvm_vcpu_ioctl_enable_cap() | *347dcb84a4
apparmor: Fix null pointer deref when receiving skb during sock creation | *9460ac3dd1
mISDN: Fix a use after free in hfcmulti_tx() | *dda518dea6
bpf: Fix a segment issue when downgrading gso_size | *5cc4d71dda
net: nexthop: Initialize all fields in dumped nexthops | *dc2a655437
net: stmmac: Correct byte order of perfect_match | *aa38bf7489
tipc: Return non-zero value from tipc_udp_addr2str() on error | *cf791b98fe
netfilter: nft_set_pipapo_avx2: disable softinterrupts | *c8ae5939f4
net: bonding: correctly annotate RCU in bond_should_notify_peers() | *3bf09eab40
ipv4: Fix incorrect source address in Record Route option | *f62a9cc0c2
MIPS: SMP-CPS: Fix address for GCR_ACCESS register for CM3 and later | *257193083e
dma: fix call order in dmam_free_coherent | *641b7a8920
libbpf: Fix no-args func prototype BTF dumping syntax | *ff2387553f
f2fs: fix start segno of large section | *721190921a
um: time-travel: fix time-travel-start option | *538a27c804
jfs: Fix array-index-out-of-bounds in diFree | *1c089efe76
kdb: Use the passed prompt in kdb_position_cursor() | *f0ad62559f
kdb: address -Wformat-security warnings | *65dba3c9ce
kernel: rerun task_work while freezing in get_signal() | *b839175c06
io_uring/io-wq: limit retrying worker initialisation | *5f0a6800b8
nilfs2: handle inconsistent state in nilfs_btnode_create_block() | *9fa8eca259
Bluetooth: btusb: Add Realtek RTL8852BE support ID 0x13d3:0x3591 | *4d3eb40ccd
Bluetooth: btusb: Add RTL8852BE device 0489:e125 to device tables | *1fccae3fd7
rbd: don't assume RBD_LOCK_STATE_LOCKED for exclusive mappings | *52d8d27fd6
rbd: rename RBD_LOCK_STATE_RELEASING and releasing_wait | *76b62f3035
drm/panfrost: Mark simple_ondemand governor as softdep | *77411a2d22
MIPS: Loongson64: env: Hook up Loongsson-2K | *636163de03
MIPS: ip30: ip30-console: Add missing include | *4e8f70d3cc
rbd: don't assume rbd_is_lock_owner() for exclusive mappings | *24933a55bf
selftests/sigaltstack: Fix ppc64 GCC build | *94ee7ff99b
RDMA/iwcm: Fix a use-after-free related to destroying CM IDs | *9667d46f8a
platform: mips: cpu_hwmon: Disable driver on unsupported hardware | *19f108b3d1
watchdog/perf: properly initialize the turbo mode timestamp and rearm counter | *9cba1ec637
rtc: isl1208: Fix return value of nvmem callbacks | *a49321257f
perf/x86/intel/pt: Fix a topa_entry base address calculation | *3b8e1b7d26
perf/x86/intel/pt: Fix topa_entry base length | *a3ab508a48
scsi: qla2xxx: validate nvme_local_port correctly | *57ba756371
scsi: qla2xxx: Complete command early within lock | *b0c39dcbd8
scsi: qla2xxx: Fix flash read failure | *87db8d7b75
scsi: qla2xxx: Fix for possible memory corruption | *e5ed6a26ff
scsi: qla2xxx: During vport delete send async logout explicitly | *2fcd485289
rtc: cmos: Fix return value of nvmem callbacks | *d4d814159f
devres: Fix devm_krealloc() wasting memory | *648d549046
kobject_uevent: Fix OOB access within zap_modalias_env() | *41dd963641
kbuild: Fix '-S -c' in x86 stack protector scripts | *0730ea8502
decompress_bunzip2: fix rare decompression failure | *bed9580165
ubi: eba: properly rollback inside self_check_eba | *ae99754cd8
clk: davinci: da8xx-cfgchip: Initialize clk_init_data before use | *54bc4e8844
f2fs: fix to don't dirty inode for readonly filesystem | *b848b40794
scsi: qla2xxx: Return ENOBUFS if sg_cnt is more than one for ELS cmds | *a44f88f757
dev/parport: fix the array out-of-bounds risk | *388ee7a4d3
binder: fix hang of unregistered readers | *ac2459460c
PCI: rockchip: Use GPIOD_OUT_LOW flag while requesting ep_gpio | *e5bae95306
PCI: hv: Return zero, not garbage, when reading PCI_INTERRUPT_PIN | *af1d27f88e
hwrng: amd - Convert PCIBIOS_* return codes to errnos | *43aab4483d
tools/memory-model: Fix bug in lock.cat | *9d289ce917
KVM: VMX: Split out the non-virtualization part of vmx_interrupt_blocked() | *cdbcb4e9f6
jbd2: make jbd2_journal_get_max_txn_bufs() internal | *6d5223be13
leds: ss4200: Convert PCIBIOS_* return codes to errnos | *35f8c9ac0c
wifi: mwifiex: Fix interface type change | *de2a011a13
ext4: make sure the first directory block is not a hole | *42d4205170
ext4: check dot and dotdot of dx_root before making dir indexed | *3846394785
m68k: amiga: Turn off Warp1260 interrupts during boot | *2199e157a4
udf: Avoid using corrupted block bitmap buffer | *5c59cb8dd9
task_work: Introduce task_work_cancel() again | *1fd27cc6f0
task_work: s/task_work_cancel()/task_work_cancel_func()/ | *973155ca67
apparmor: use kvfree_sensitive to free data->data | *eb46367187
sched/fair: Use all little CPUs for CPU-bound workloads | *9ce89824ff
drm/amd/display: Check for NULL pointer | *748e9ad7c0
scsi: qla2xxx: Fix optrom version displayed in FDMI | *6735d02ead
drm/gma500: fix null pointer dereference in psb_intel_lvds_get_modes | *b6ac46a001
drm/gma500: fix null pointer dereference in cdv_intel_lvds_get_modes | *86f4ca8b3b
ext2: Verify bitmap and itable block numbers before using them | *10f7163bfb
hfs: fix to initialize fields of hfs_inode_info after hfs_alloc_inode() | *4c9d235630
media: venus: fix use after free in vdec_close | *e65cccfae7
char: tpm: Fix possible memory leak in tpm_bios_measurements_open() | *cf0c713c69
sched/fair: set_load_weight() must also call reweight_task() for SCHED_IDLE tasks | *5c5b02d489
ipv6: take care of scope when choosing the src addr | *83e2dfadcb
af_packet: Handle outgoing VLAN packets without hardware offloading | *7e36a3c701
net: netconsole: Disable target before netpoll cleanup | *9ef7190228
tick/broadcast: Make takeover of broadcast hrtimer reliable | *f2c2c4cc5a
dt-bindings: thermal: correct thermal zone node name limit | *14083dc69b
rtc: interface: Add RTC offset to alarm after fix-up | *84ffa27eb0
nilfs2: avoid undefined behavior in nilfs_cnt32_ge macro | *9d6571b1c4
fs/nilfs2: remove some unused macros to tame gcc | *3c6fa67023
fs/proc/task_mmu: indicate PM_FILE for PMD-mapped file THP | *21a15d52bc
pinctrl: freescale: mxs: Fix refcount of child | *d2de7746e5
pinctrl: ti: ti-iodelay: fix possible memory leak when pinctrl_enable() fails | *73303a4a8f
pinctrl: ti: ti-iodelay: Drop if block with always false condition | *15014206f9
pinctrl: single: fix possible memory leak when pinctrl_enable() fails | *8c3bef7ca8
pinctrl: core: fix possible memory leak when pinctrl_enable() fails | *53f2d5bce1
pinctrl: rockchip: update rk3308 iomux routes | *01c0341e98
net: dsa: b53: Limit chip-wide jumbo frame config to CPU ports | *ef6af29942
net: dsa: mv88e6xxx: Limit chip-wide frame size config to CPU ports | *eb4ca1a97e
netfilter: ctnetlink: use helper function to calculate expect ID | *9118c408ee
bnxt_re: Fix imm_data endianness | *edc2dee07a
RDMA/hns: Fix missing pagesize and alignment check in FRMR | *29723ad948
macintosh/therm_windtunnel: fix module unload. | *445ffbccd0
powerpc/xmon: Fix disassembly CPU feature checks | *38a7e4b8bf
MIPS: Octeron: remove source file executable bit | *3009d371a2
Input: elan_i2c - do not leave interrupt disabled on suspend failure | *37a484f771
RDMA/device: Return error earlier if port in not valid | *695d70c60b
mtd: make mtd_test.c a separate module | *ab2114f6ff
ASoC: max98088: Check for clk_prepare_enable() error | *771f129bed
RDMA/rxe: Don't set BTH_ACK_MASK for UC or UD QPs | *506e71b0e1
RDMA/mlx4: Fix truncated output warning in alias_GUID.c | *6bf3cf61f3
RDMA/mlx4: Fix truncated output warning in mad.c | *26b6512d5d
Input: qt1050 - handle CHIP_ID reading error | *2be7e24056
coresight: Fix ref leak when of_coresight_parse_endpoint() fails | *3d1c4bf57d
PCI: Fix resource double counting on remove & rescan | *8105318210
SUNRPC: Fixup gss_status tracepoint error output | *8f1dc3f33f
sparc64: Fix incorrect function signature and add prototype for prom_cif_init | *3d096f2a99
ext4: avoid writing unitialized memory to disk in EA inodes | *91c22df701
SUNRPC: avoid soft lockup when transmitting UDP to reachable server. | *84edcf61bd
xprtrdma: Fix rpcrdma_reqs_reset() | *974294806b
xprtrdma: Rename frwr_release_mr() | *cf9141d2f7
mfd: omap-usb-tll: Use struct_size to allocate tll | *72ac78ec1a
media: venus: flush all buffers in output plane streamoff | *5ed0496e38
ext4: fix infinite loop when replaying fast_commit | *c9106ad5ea
Revert "leds: led-core: Fix refcount leak in of_led_get()" | *4e87f592a4
drm/qxl: Add check for drm_cvt_mode | *cd105977b1
drm/etnaviv: fix DMA direction handling for cached RW buffers | *6ef4f1e981
perf report: Fix condition in sort__sym_cmp() | *09c1583f0e
leds: trigger: Unregister sysfs attributes before calling deactivate() | *3c9071a871
media: renesas: vsp1: Store RPF partition configuration per RPF instance | *3944484005
media: renesas: vsp1: Fix _irqsave and _irq mix | *9459f33175
media: uvcvideo: Override default flags | *115d814d6a
media: uvcvideo: Allow entity-defined get_info and get_cur | *e470e95616
saa7134: Unchecked i2c_transfer function result fixed | *f3968b3d3c
media: imon: Fix race getting ictx->lock | *bcc963f591
media: dvb-usb: Fix unexpected infinite loop in dvb_usb_read_remote_control() | *7aaa368c68
drm/panel: boe-tv101wum-nl6: Check for errors on the NOP in prepare() | *fb20da8338
drm/panel: boe-tv101wum-nl6: If prepare fails, disable GPIO before regulators | *be9d08ff10
xdp: fix invalid wait context of page_pool_destroy() | *96178b12c8
selftests: forwarding: devlink_lib: Wait for udev events after reloading | *859bc76374
bpf: Eliminate remaining "make W=1" warnings in kernel/bpf/btf.o | *6ce46045f9
bna: adjust 'name' buf size of bna_tcb and bna_ccb structures | *28c8fce207
bpf: annotate BTF show functions with __printf | *1ccb1399bd
selftests/bpf: Close fd in error path in drop_on_reuseport | *be53b70fc0
wifi: virt_wifi: don't use strlen() in const context | *f851ff5c6e
gss_krb5: Fix the error handling path for crypto_sync_skcipher_setkey | *05c4488a0e
wifi: virt_wifi: avoid reporting connection success with wrong SSID | *b33dd45086
qed: Improve the stack space of filter_config() | *7f132aca18
perf: Prevent passing zero nr_pages to rb_alloc_aux() | *a2450206c0
perf: Fix perf_aux_size() for greater-than 32-bit size | *a497a6b72b
perf/x86/intel/pt: Fix pt_topa_entry_for_page() address calculation | *d4f4188ecf
netfilter: nf_tables: rise cap on SELinux secmark context | *0d08015bee
ipvs: Avoid unnecessary calls to skb_is_gso_sctp | *2912a0d136
net: fec: Fix FEC_ECR_EN1588 being cleared on link-down | *29254059a1
net: fec: Refactor: #define magic constants | *2e201b3d16
wifi: cfg80211: handle 2x996 RU allocation in cfg80211_calculate_bitrate_he() | *72e470089f
wifi: cfg80211: fix typo in cfg80211_calculate_bitrate_he() | *4055275ca3
wifi: ath11k: fix wrong handling of CCMP256 and GCMP ciphers | *2aa1739334
ath11k: dp: stop rx pktlog before suspend | *dae1ab7040
mlxsw: spectrum_acl: Fix ACL scale regression and firmware errors | *aa98eb0740
mlxsw: spectrum_acl_bloom_filter: Make mlxsw_sp_acl_bf_key_encode() more flexible | *36a9996e02
mlxsw: spectrum_acl_erp: Fix object nesting warning | *22ae17a267
lib: objagg: Fix general protection fault | *ada0c31925
selftests/bpf: Check length of recv in test_sockmap | *249adb30cb
net/smc: set rmb's SG_MAX_SINGLE_ALLOC limitation only when CONFIG_ARCH_NO_SG_CHAIN is defined | *15c2ec7c28
net/smc: Allow SMC-D 1MB DMB allocations | *8d0d50a8b7
net: esp: cleanup esp_output_tail_tcp() in case of unsupported ESPINTCP | *2f5738bdd6
selftests/bpf: Fix prog numbers in test_sockmap | *1302433dc6
wifi: brcmsmac: LCN PHY code is used for BCM4313 2G-only device | *1eb5751e23
firmware: turris-mox-rwtm: Initialize completion before mailbox | *085dc94289
firmware: turris-mox-rwtm: Fix checking return value of wait_for_completion_timeout() | *6f3cb1fd6b
ARM: spitz: fix GPIO assignment for backlight | *7b7d06a310
ARM: pxa: spitz: use gpio descriptors for audio | *3ae2ec97d8
m68k: cmpxchg: Fix return value for default case in __arch_xchg() | *ba1d2ecfcf
x86/xen: Convert comma to semicolon | *4a49ce2d63
m68k: atari: Fix TT bootup freeze / unexpected (SCU) interrupt messages | *e04654f425
arm64: dts: amlogic: gx: correct hdmi clocks | *4745535fce
arm64: dts: mediatek: mt7622: fix "emmc" pinctrl mux | *be5ca40647
arm64: dts: mediatek: mt8183-kukui: Drop bogus output-enable property | *b1e9396ac4
ARM: dts: imx6qdl-kontron-samx6i: fix PCIe reset polarity | *a992c88fbb
ARM: dts: imx6qdl-kontron-samx6i: fix SPI0 chip selects | *c79a7cad41
ARM: dts: imx6qdl-kontron-samx6i: fix board reset | *efd89b5db5
ARM: dts: imx6qdl-kontron-samx6i: fix PHY reset | *bbfa9a71ae
ARM: dts: imx6qdl-kontron-samx6i: move phy reset into phy-node | *31a9a0958b
arm64: dts: rockchip: Increase VOP clk rate on RK3328 | *5cc525351b
soc: qcom: pdr: fix parsing of domains lists | *eab05737ee
soc: qcom: pdr: protect locator_addr with the main mutex | *a584e5d3f7
arm64: dts: qcom: msm8996: specify UFS core_clk frequencies | *eedd9fd986
soc: qcom: rpmh-rsc: Ensure irqs aren't disabled by rpmh_rsc_send_data() callers | *39f4cb508e
arm64: dts: qcom: sdm845: add power-domain to UFS PHY | *d3e6b30c9c
hwmon: (max6697) Fix swapped temp{1,8} critical alarms | *15770a1476
hwmon: (max6697) Fix underflow when writing limit attributes | *ae8bd075a9
pwm: stm32: Always do lazy disabling | *d8571b9a83
hwmon: (adt7475) Fix default duty on fan is disabled | *685976438b
x86/platform/iosf_mbi: Convert PCIBIOS_* return codes to errnos | *010441f083
x86/pci/xen: Fix PCIBIOS_* return code handling | *c995bea85e
x86/pci/intel_mid_pci: Fix PCIBIOS_* return code handling | *e2fdf7b79f
x86/of: Return consistent error type from x86_of_pci_irq_enable() | *97795f23a8
hfsplus: fix to avoid false alarm of circular locking | *c0748b7684
platform/chrome: cros_ec_debugfs: fix wrong EC message version | *3070e81609
EDAC, i10nm: make skx_common.o a separate module | *9bff9479e1
EDAC/skx_common: Add new ADXL components for 2-level memory *767b3cdf4f
Merge branch 'android12-5.10' into android12-5.10-lts Change-Id: I0e05e42a679534cd2d7254df19f21f2d8732df5f Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
2382 lines
53 KiB
C
2382 lines
53 KiB
C
/*
|
|
FUSE: Filesystem in Userspace
|
|
Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
|
|
|
|
This program can be distributed under the terms of the GNU GPL.
|
|
See the file COPYING.
|
|
*/
|
|
|
|
#include "fuse_i.h"
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/file.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/pipe_fs_i.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/splice.h>
|
|
#include <linux/sched.h>
|
|
|
|
#include <trace/hooks/fuse.h>
|
|
|
|
MODULE_ALIAS_MISCDEV(FUSE_MINOR);
|
|
MODULE_ALIAS("devname:fuse");
|
|
|
|
/* Ordinary requests have even IDs, while interrupts IDs are odd */
|
|
#define FUSE_INT_REQ_BIT (1ULL << 0)
|
|
#define FUSE_REQ_ID_STEP (1ULL << 1)
|
|
|
|
static struct kmem_cache *fuse_req_cachep;
|
|
|
|
static struct fuse_dev *fuse_get_dev(struct file *file)
|
|
{
|
|
/*
|
|
* Lockless access is OK, because file->private data is set
|
|
* once during mount and is valid until the file is released.
|
|
*/
|
|
return READ_ONCE(file->private_data);
|
|
}
|
|
|
|
static void fuse_request_init(struct fuse_mount *fm, struct fuse_req *req)
|
|
{
|
|
INIT_LIST_HEAD(&req->list);
|
|
INIT_LIST_HEAD(&req->intr_entry);
|
|
init_waitqueue_head(&req->waitq);
|
|
refcount_set(&req->count, 1);
|
|
__set_bit(FR_PENDING, &req->flags);
|
|
req->fm = fm;
|
|
}
|
|
|
|
static struct fuse_req *fuse_request_alloc(struct fuse_mount *fm, gfp_t flags)
|
|
{
|
|
struct fuse_req *req = kmem_cache_zalloc(fuse_req_cachep, flags);
|
|
if (req)
|
|
fuse_request_init(fm, req);
|
|
|
|
return req;
|
|
}
|
|
|
|
static void fuse_request_free(struct fuse_req *req)
|
|
{
|
|
kmem_cache_free(fuse_req_cachep, req);
|
|
}
|
|
|
|
static void __fuse_get_request(struct fuse_req *req)
|
|
{
|
|
refcount_inc(&req->count);
|
|
}
|
|
|
|
/* Must be called with > 1 refcount */
|
|
static void __fuse_put_request(struct fuse_req *req)
|
|
{
|
|
refcount_dec(&req->count);
|
|
}
|
|
|
|
void fuse_set_initialized(struct fuse_conn *fc)
|
|
{
|
|
/* Make sure stores before this are seen on another CPU */
|
|
smp_wmb();
|
|
fc->initialized = 1;
|
|
}
|
|
|
|
static bool fuse_block_alloc(struct fuse_conn *fc, bool for_background)
|
|
{
|
|
return !fc->initialized || (for_background && fc->blocked);
|
|
}
|
|
|
|
static void fuse_drop_waiting(struct fuse_conn *fc)
|
|
{
|
|
/*
|
|
* lockess check of fc->connected is okay, because atomic_dec_and_test()
|
|
* provides a memory barrier mached with the one in fuse_wait_aborted()
|
|
* to ensure no wake-up is missed.
|
|
*/
|
|
if (atomic_dec_and_test(&fc->num_waiting) &&
|
|
!READ_ONCE(fc->connected)) {
|
|
/* wake up aborters */
|
|
wake_up_all(&fc->blocked_waitq);
|
|
}
|
|
}
|
|
|
|
static void fuse_put_request(struct fuse_req *req);
|
|
|
|
static struct fuse_req *fuse_get_req(struct fuse_mount *fm, bool for_background)
|
|
{
|
|
struct fuse_conn *fc = fm->fc;
|
|
struct fuse_req *req;
|
|
int err;
|
|
atomic_inc(&fc->num_waiting);
|
|
|
|
if (fuse_block_alloc(fc, for_background)) {
|
|
err = -EINTR;
|
|
if (wait_event_killable_exclusive(fc->blocked_waitq,
|
|
!fuse_block_alloc(fc, for_background)))
|
|
goto out;
|
|
}
|
|
/* Matches smp_wmb() in fuse_set_initialized() */
|
|
smp_rmb();
|
|
|
|
err = -ENOTCONN;
|
|
if (!fc->connected)
|
|
goto out;
|
|
|
|
err = -ECONNREFUSED;
|
|
if (fc->conn_error)
|
|
goto out;
|
|
|
|
req = fuse_request_alloc(fm, GFP_KERNEL);
|
|
err = -ENOMEM;
|
|
if (!req) {
|
|
if (for_background)
|
|
wake_up(&fc->blocked_waitq);
|
|
goto out;
|
|
}
|
|
|
|
req->in.h.uid = from_kuid(fc->user_ns, current_fsuid());
|
|
req->in.h.gid = from_kgid(fc->user_ns, current_fsgid());
|
|
req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns);
|
|
|
|
__set_bit(FR_WAITING, &req->flags);
|
|
if (for_background)
|
|
__set_bit(FR_BACKGROUND, &req->flags);
|
|
|
|
if (unlikely(req->in.h.uid == ((uid_t)-1) ||
|
|
req->in.h.gid == ((gid_t)-1))) {
|
|
fuse_put_request(req);
|
|
return ERR_PTR(-EOVERFLOW);
|
|
}
|
|
return req;
|
|
|
|
out:
|
|
fuse_drop_waiting(fc);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
static void fuse_put_request(struct fuse_req *req)
|
|
{
|
|
struct fuse_conn *fc = req->fm->fc;
|
|
|
|
if (refcount_dec_and_test(&req->count)) {
|
|
if (test_bit(FR_BACKGROUND, &req->flags)) {
|
|
/*
|
|
* We get here in the unlikely case that a background
|
|
* request was allocated but not sent
|
|
*/
|
|
spin_lock(&fc->bg_lock);
|
|
if (!fc->blocked)
|
|
wake_up(&fc->blocked_waitq);
|
|
spin_unlock(&fc->bg_lock);
|
|
}
|
|
|
|
if (test_bit(FR_WAITING, &req->flags)) {
|
|
__clear_bit(FR_WAITING, &req->flags);
|
|
fuse_drop_waiting(fc);
|
|
}
|
|
|
|
fuse_request_free(req);
|
|
}
|
|
}
|
|
|
|
unsigned int fuse_len_args(unsigned int numargs, struct fuse_arg *args)
|
|
{
|
|
unsigned nbytes = 0;
|
|
unsigned i;
|
|
|
|
for (i = 0; i < numargs; i++)
|
|
nbytes += args[i].size;
|
|
|
|
return nbytes;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_len_args);
|
|
|
|
u64 fuse_get_unique(struct fuse_iqueue *fiq)
|
|
{
|
|
fiq->reqctr += FUSE_REQ_ID_STEP;
|
|
return fiq->reqctr;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_get_unique);
|
|
|
|
static unsigned int fuse_req_hash(u64 unique)
|
|
{
|
|
return hash_long(unique & ~FUSE_INT_REQ_BIT, FUSE_PQ_HASH_BITS);
|
|
}
|
|
|
|
/**
|
|
* A new request is available, wake fiq->waitq
|
|
*/
|
|
static void fuse_dev_wake_and_unlock(struct fuse_iqueue *fiq, bool sync)
|
|
__releases(fiq->lock)
|
|
{
|
|
if (sync)
|
|
wake_up_sync(&fiq->waitq);
|
|
else
|
|
wake_up(&fiq->waitq);
|
|
kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
|
|
spin_unlock(&fiq->lock);
|
|
}
|
|
|
|
const struct fuse_iqueue_ops fuse_dev_fiq_ops = {
|
|
.wake_forget_and_unlock = fuse_dev_wake_and_unlock,
|
|
.wake_interrupt_and_unlock = fuse_dev_wake_and_unlock,
|
|
.wake_pending_and_unlock = fuse_dev_wake_and_unlock,
|
|
};
|
|
EXPORT_SYMBOL_GPL(fuse_dev_fiq_ops);
|
|
|
|
static void queue_request_and_unlock(struct fuse_iqueue *fiq,
|
|
struct fuse_req *req, bool sync)
|
|
__releases(fiq->lock)
|
|
{
|
|
req->in.h.len = sizeof(struct fuse_in_header) +
|
|
fuse_len_args(req->args->in_numargs,
|
|
(struct fuse_arg *) req->args->in_args);
|
|
list_add_tail(&req->list, &fiq->pending);
|
|
trace_android_vh_queue_request_and_unlock(&fiq->waitq, sync);
|
|
fiq->ops->wake_pending_and_unlock(fiq, sync);
|
|
}
|
|
|
|
void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
|
|
u64 nodeid, u64 nlookup)
|
|
{
|
|
struct fuse_iqueue *fiq = &fc->iq;
|
|
|
|
forget->forget_one.nodeid = nodeid;
|
|
forget->forget_one.nlookup = nlookup;
|
|
|
|
spin_lock(&fiq->lock);
|
|
if (fiq->connected) {
|
|
fiq->forget_list_tail->next = forget;
|
|
fiq->forget_list_tail = forget;
|
|
fiq->ops->wake_forget_and_unlock(fiq, false);
|
|
} else {
|
|
kfree(forget);
|
|
spin_unlock(&fiq->lock);
|
|
}
|
|
}
|
|
|
|
static void flush_bg_queue(struct fuse_conn *fc)
|
|
{
|
|
struct fuse_iqueue *fiq = &fc->iq;
|
|
|
|
while (fc->active_background < fc->max_background &&
|
|
!list_empty(&fc->bg_queue)) {
|
|
struct fuse_req *req;
|
|
|
|
req = list_first_entry(&fc->bg_queue, struct fuse_req, list);
|
|
list_del(&req->list);
|
|
fc->active_background++;
|
|
spin_lock(&fiq->lock);
|
|
req->in.h.unique = fuse_get_unique(fiq);
|
|
queue_request_and_unlock(fiq, req, false);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function is called when a request is finished. Either a reply
|
|
* has arrived or it was aborted (and not yet sent) or some error
|
|
* occurred during communication with userspace, or the device file
|
|
* was closed. The requester thread is woken up (if still waiting),
|
|
* the 'end' callback is called if given, else the reference to the
|
|
* request is released
|
|
*/
|
|
void fuse_request_end(struct fuse_req *req)
|
|
{
|
|
struct fuse_mount *fm = req->fm;
|
|
struct fuse_conn *fc = fm->fc;
|
|
struct fuse_iqueue *fiq = &fc->iq;
|
|
|
|
if (test_and_set_bit(FR_FINISHED, &req->flags))
|
|
goto put_request;
|
|
|
|
/*
|
|
* test_and_set_bit() implies smp_mb() between bit
|
|
* changing and below FR_INTERRUPTED check. Pairs with
|
|
* smp_mb() from queue_interrupt().
|
|
*/
|
|
if (test_bit(FR_INTERRUPTED, &req->flags)) {
|
|
spin_lock(&fiq->lock);
|
|
list_del_init(&req->intr_entry);
|
|
spin_unlock(&fiq->lock);
|
|
}
|
|
WARN_ON(test_bit(FR_PENDING, &req->flags));
|
|
WARN_ON(test_bit(FR_SENT, &req->flags));
|
|
if (test_bit(FR_BACKGROUND, &req->flags)) {
|
|
spin_lock(&fc->bg_lock);
|
|
clear_bit(FR_BACKGROUND, &req->flags);
|
|
if (fc->num_background == fc->max_background) {
|
|
fc->blocked = 0;
|
|
wake_up(&fc->blocked_waitq);
|
|
} else if (!fc->blocked) {
|
|
/*
|
|
* Wake up next waiter, if any. It's okay to use
|
|
* waitqueue_active(), as we've already synced up
|
|
* fc->blocked with waiters with the wake_up() call
|
|
* above.
|
|
*/
|
|
if (waitqueue_active(&fc->blocked_waitq))
|
|
wake_up(&fc->blocked_waitq);
|
|
}
|
|
|
|
if (fc->num_background == fc->congestion_threshold && fm->sb) {
|
|
clear_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
|
|
clear_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
|
|
}
|
|
fc->num_background--;
|
|
fc->active_background--;
|
|
flush_bg_queue(fc);
|
|
spin_unlock(&fc->bg_lock);
|
|
} else {
|
|
/* Wake up waiter sleeping in request_wait_answer() */
|
|
wake_up(&req->waitq);
|
|
trace_android_vh_fuse_request_end(current);
|
|
}
|
|
|
|
if (test_bit(FR_ASYNC, &req->flags))
|
|
req->args->end(fm, req->args, req->out.h.error);
|
|
put_request:
|
|
fuse_put_request(req);
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_request_end);
|
|
|
|
static int queue_interrupt(struct fuse_req *req)
|
|
{
|
|
struct fuse_iqueue *fiq = &req->fm->fc->iq;
|
|
|
|
spin_lock(&fiq->lock);
|
|
/* Check for we've sent request to interrupt this req */
|
|
if (unlikely(!test_bit(FR_INTERRUPTED, &req->flags))) {
|
|
spin_unlock(&fiq->lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (list_empty(&req->intr_entry)) {
|
|
list_add_tail(&req->intr_entry, &fiq->interrupts);
|
|
/*
|
|
* Pairs with smp_mb() implied by test_and_set_bit()
|
|
* from fuse_request_end().
|
|
*/
|
|
smp_mb();
|
|
if (test_bit(FR_FINISHED, &req->flags)) {
|
|
list_del_init(&req->intr_entry);
|
|
spin_unlock(&fiq->lock);
|
|
return 0;
|
|
}
|
|
fiq->ops->wake_interrupt_and_unlock(fiq, false);
|
|
} else {
|
|
spin_unlock(&fiq->lock);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void request_wait_answer(struct fuse_req *req)
|
|
{
|
|
struct fuse_conn *fc = req->fm->fc;
|
|
struct fuse_iqueue *fiq = &fc->iq;
|
|
int err;
|
|
|
|
if (!fc->no_interrupt) {
|
|
/* Any signal may interrupt this */
|
|
err = wait_event_interruptible(req->waitq,
|
|
test_bit(FR_FINISHED, &req->flags));
|
|
if (!err)
|
|
return;
|
|
|
|
set_bit(FR_INTERRUPTED, &req->flags);
|
|
/* matches barrier in fuse_dev_do_read() */
|
|
smp_mb__after_atomic();
|
|
if (test_bit(FR_SENT, &req->flags))
|
|
queue_interrupt(req);
|
|
}
|
|
|
|
if (!test_bit(FR_FORCE, &req->flags)) {
|
|
/* Only fatal signals may interrupt this */
|
|
err = wait_event_killable(req->waitq,
|
|
test_bit(FR_FINISHED, &req->flags));
|
|
if (!err)
|
|
return;
|
|
|
|
spin_lock(&fiq->lock);
|
|
/* Request is not yet in userspace, bail out */
|
|
if (test_bit(FR_PENDING, &req->flags)) {
|
|
list_del(&req->list);
|
|
spin_unlock(&fiq->lock);
|
|
__fuse_put_request(req);
|
|
req->out.h.error = -EINTR;
|
|
return;
|
|
}
|
|
spin_unlock(&fiq->lock);
|
|
}
|
|
|
|
/*
|
|
* Either request is already in userspace, or it was forced.
|
|
* Wait it out.
|
|
*/
|
|
wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags));
|
|
}
|
|
|
|
static void __fuse_request_send(struct fuse_req *req)
|
|
{
|
|
struct fuse_iqueue *fiq = &req->fm->fc->iq;
|
|
|
|
BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
|
|
spin_lock(&fiq->lock);
|
|
if (!fiq->connected) {
|
|
spin_unlock(&fiq->lock);
|
|
req->out.h.error = -ENOTCONN;
|
|
} else {
|
|
req->in.h.unique = fuse_get_unique(fiq);
|
|
/* acquire extra reference, since request is still needed
|
|
after fuse_request_end() */
|
|
__fuse_get_request(req);
|
|
queue_request_and_unlock(fiq, req, true);
|
|
|
|
request_wait_answer(req);
|
|
/* Pairs with smp_wmb() in fuse_request_end() */
|
|
smp_rmb();
|
|
}
|
|
}
|
|
|
|
static void fuse_adjust_compat(struct fuse_conn *fc, struct fuse_args *args)
|
|
{
|
|
if (fc->minor < 4 && args->opcode == FUSE_STATFS)
|
|
args->out_args[0].size = FUSE_COMPAT_STATFS_SIZE;
|
|
|
|
if (fc->minor < 9) {
|
|
switch (args->opcode) {
|
|
case FUSE_LOOKUP:
|
|
case FUSE_CREATE:
|
|
case FUSE_MKNOD:
|
|
case FUSE_MKDIR:
|
|
case FUSE_SYMLINK:
|
|
case FUSE_LINK:
|
|
args->out_args[0].size = FUSE_COMPAT_ENTRY_OUT_SIZE;
|
|
break;
|
|
case FUSE_GETATTR:
|
|
case FUSE_SETATTR:
|
|
args->out_args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE;
|
|
break;
|
|
}
|
|
}
|
|
if (fc->minor < 12) {
|
|
switch (args->opcode) {
|
|
case FUSE_CREATE:
|
|
args->in_args[0].size = sizeof(struct fuse_open_in);
|
|
break;
|
|
case FUSE_MKNOD:
|
|
args->in_args[0].size = FUSE_COMPAT_MKNOD_IN_SIZE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void fuse_force_creds(struct fuse_req *req)
|
|
{
|
|
struct fuse_conn *fc = req->fm->fc;
|
|
|
|
req->in.h.uid = from_kuid_munged(fc->user_ns, current_fsuid());
|
|
req->in.h.gid = from_kgid_munged(fc->user_ns, current_fsgid());
|
|
req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns);
|
|
}
|
|
|
|
static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
|
|
{
|
|
req->in.h.opcode = args->opcode;
|
|
req->in.h.nodeid = args->nodeid;
|
|
req->args = args;
|
|
if (args->end)
|
|
__set_bit(FR_ASYNC, &req->flags);
|
|
}
|
|
|
|
ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args)
|
|
{
|
|
struct fuse_conn *fc = fm->fc;
|
|
struct fuse_req *req;
|
|
ssize_t ret;
|
|
|
|
if (args->force) {
|
|
atomic_inc(&fc->num_waiting);
|
|
req = fuse_request_alloc(fm, GFP_KERNEL | __GFP_NOFAIL);
|
|
|
|
if (!args->nocreds)
|
|
fuse_force_creds(req);
|
|
|
|
__set_bit(FR_WAITING, &req->flags);
|
|
__set_bit(FR_FORCE, &req->flags);
|
|
} else {
|
|
WARN_ON(args->nocreds);
|
|
req = fuse_get_req(fm, false);
|
|
if (IS_ERR(req))
|
|
return PTR_ERR(req);
|
|
}
|
|
|
|
/* Needs to be done after fuse_get_req() so that fc->minor is valid */
|
|
fuse_adjust_compat(fc, args);
|
|
fuse_args_to_req(req, args);
|
|
|
|
if (!args->noreply)
|
|
__set_bit(FR_ISREPLY, &req->flags);
|
|
__fuse_request_send(req);
|
|
ret = req->out.h.error;
|
|
if (!ret && args->out_argvar) {
|
|
BUG_ON(args->out_numargs == 0);
|
|
ret = args->out_args[args->out_numargs - 1].size;
|
|
}
|
|
fuse_put_request(req);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool fuse_request_queue_background(struct fuse_req *req)
|
|
{
|
|
struct fuse_mount *fm = req->fm;
|
|
struct fuse_conn *fc = fm->fc;
|
|
bool queued = false;
|
|
|
|
WARN_ON(!test_bit(FR_BACKGROUND, &req->flags));
|
|
if (!test_bit(FR_WAITING, &req->flags)) {
|
|
__set_bit(FR_WAITING, &req->flags);
|
|
atomic_inc(&fc->num_waiting);
|
|
}
|
|
__set_bit(FR_ISREPLY, &req->flags);
|
|
spin_lock(&fc->bg_lock);
|
|
if (likely(fc->connected)) {
|
|
fc->num_background++;
|
|
if (fc->num_background == fc->max_background)
|
|
fc->blocked = 1;
|
|
if (fc->num_background == fc->congestion_threshold && fm->sb) {
|
|
set_bdi_congested(fm->sb->s_bdi, BLK_RW_SYNC);
|
|
set_bdi_congested(fm->sb->s_bdi, BLK_RW_ASYNC);
|
|
}
|
|
list_add_tail(&req->list, &fc->bg_queue);
|
|
flush_bg_queue(fc);
|
|
queued = true;
|
|
}
|
|
spin_unlock(&fc->bg_lock);
|
|
|
|
return queued;
|
|
}
|
|
|
|
int fuse_simple_background(struct fuse_mount *fm, struct fuse_args *args,
|
|
gfp_t gfp_flags)
|
|
{
|
|
struct fuse_req *req;
|
|
|
|
if (args->force) {
|
|
WARN_ON(!args->nocreds);
|
|
req = fuse_request_alloc(fm, gfp_flags);
|
|
if (!req)
|
|
return -ENOMEM;
|
|
__set_bit(FR_BACKGROUND, &req->flags);
|
|
} else {
|
|
WARN_ON(args->nocreds);
|
|
req = fuse_get_req(fm, true);
|
|
if (IS_ERR(req))
|
|
return PTR_ERR(req);
|
|
}
|
|
|
|
fuse_args_to_req(req, args);
|
|
|
|
if (!fuse_request_queue_background(req)) {
|
|
fuse_put_request(req);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_simple_background);
|
|
|
|
static int fuse_simple_notify_reply(struct fuse_mount *fm,
|
|
struct fuse_args *args, u64 unique)
|
|
{
|
|
struct fuse_req *req;
|
|
struct fuse_iqueue *fiq = &fm->fc->iq;
|
|
int err = 0;
|
|
|
|
req = fuse_get_req(fm, false);
|
|
if (IS_ERR(req))
|
|
return PTR_ERR(req);
|
|
|
|
__clear_bit(FR_ISREPLY, &req->flags);
|
|
req->in.h.unique = unique;
|
|
|
|
fuse_args_to_req(req, args);
|
|
|
|
spin_lock(&fiq->lock);
|
|
if (fiq->connected) {
|
|
queue_request_and_unlock(fiq, req, false);
|
|
} else {
|
|
err = -ENODEV;
|
|
spin_unlock(&fiq->lock);
|
|
fuse_put_request(req);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Lock the request. Up to the next unlock_request() there mustn't be
|
|
* anything that could cause a page-fault. If the request was already
|
|
* aborted bail out.
|
|
*/
|
|
static int lock_request(struct fuse_req *req)
|
|
{
|
|
int err = 0;
|
|
if (req) {
|
|
spin_lock(&req->waitq.lock);
|
|
if (test_bit(FR_ABORTED, &req->flags))
|
|
err = -ENOENT;
|
|
else
|
|
set_bit(FR_LOCKED, &req->flags);
|
|
spin_unlock(&req->waitq.lock);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Unlock request. If it was aborted while locked, caller is responsible
|
|
* for unlocking and ending the request.
|
|
*/
|
|
static int unlock_request(struct fuse_req *req)
|
|
{
|
|
int err = 0;
|
|
if (req) {
|
|
spin_lock(&req->waitq.lock);
|
|
if (test_bit(FR_ABORTED, &req->flags))
|
|
err = -ENOENT;
|
|
else
|
|
clear_bit(FR_LOCKED, &req->flags);
|
|
spin_unlock(&req->waitq.lock);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
struct fuse_copy_state {
|
|
int write;
|
|
struct fuse_req *req;
|
|
struct iov_iter *iter;
|
|
struct pipe_buffer *pipebufs;
|
|
struct pipe_buffer *currbuf;
|
|
struct pipe_inode_info *pipe;
|
|
unsigned long nr_segs;
|
|
struct page *pg;
|
|
unsigned len;
|
|
unsigned offset;
|
|
unsigned move_pages:1;
|
|
};
|
|
|
|
static void fuse_copy_init(struct fuse_copy_state *cs, int write,
|
|
struct iov_iter *iter)
|
|
{
|
|
memset(cs, 0, sizeof(*cs));
|
|
cs->write = write;
|
|
cs->iter = iter;
|
|
}
|
|
|
|
/* Unmap and put previous page of userspace buffer */
|
|
static void fuse_copy_finish(struct fuse_copy_state *cs)
|
|
{
|
|
if (cs->currbuf) {
|
|
struct pipe_buffer *buf = cs->currbuf;
|
|
|
|
if (cs->write)
|
|
buf->len = PAGE_SIZE - cs->len;
|
|
cs->currbuf = NULL;
|
|
} else if (cs->pg) {
|
|
if (cs->write) {
|
|
flush_dcache_page(cs->pg);
|
|
set_page_dirty_lock(cs->pg);
|
|
}
|
|
/*
|
|
* The page could be GUP page(see iov_iter_get_pages in
|
|
* fuse_copy_fill) so use put_user_page to release it.
|
|
*/
|
|
put_user_page(cs->pg);
|
|
}
|
|
cs->pg = NULL;
|
|
}
|
|
|
|
/*
|
|
* Get another pagefull of userspace buffer, and map it to kernel
|
|
* address space, and lock request
|
|
*/
|
|
static int fuse_copy_fill(struct fuse_copy_state *cs)
|
|
{
|
|
struct page *page;
|
|
int err;
|
|
|
|
err = unlock_request(cs->req);
|
|
if (err)
|
|
return err;
|
|
|
|
fuse_copy_finish(cs);
|
|
if (cs->pipebufs) {
|
|
struct pipe_buffer *buf = cs->pipebufs;
|
|
|
|
if (!cs->write) {
|
|
err = pipe_buf_confirm(cs->pipe, buf);
|
|
if (err)
|
|
return err;
|
|
|
|
BUG_ON(!cs->nr_segs);
|
|
cs->currbuf = buf;
|
|
cs->pg = buf->page;
|
|
cs->offset = buf->offset;
|
|
cs->len = buf->len;
|
|
cs->pipebufs++;
|
|
cs->nr_segs--;
|
|
} else {
|
|
if (cs->nr_segs >= cs->pipe->max_usage)
|
|
return -EIO;
|
|
|
|
page = alloc_page(GFP_HIGHUSER);
|
|
if (!page)
|
|
return -ENOMEM;
|
|
|
|
buf->page = page;
|
|
buf->offset = 0;
|
|
buf->len = 0;
|
|
|
|
cs->currbuf = buf;
|
|
cs->pg = page;
|
|
cs->offset = 0;
|
|
cs->len = PAGE_SIZE;
|
|
cs->pipebufs++;
|
|
cs->nr_segs++;
|
|
}
|
|
} else {
|
|
size_t off;
|
|
err = iov_iter_get_pages(cs->iter, &page, PAGE_SIZE, 1, &off);
|
|
if (err < 0)
|
|
return err;
|
|
BUG_ON(!err);
|
|
cs->len = err;
|
|
cs->offset = off;
|
|
cs->pg = page;
|
|
iov_iter_advance(cs->iter, err);
|
|
}
|
|
|
|
return lock_request(cs->req);
|
|
}
|
|
|
|
/* Do as much copy to/from userspace buffer as we can */
|
|
static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
|
|
{
|
|
unsigned ncpy = min(*size, cs->len);
|
|
if (val) {
|
|
void *pgaddr = kmap_atomic(cs->pg);
|
|
void *buf = pgaddr + cs->offset;
|
|
|
|
if (cs->write)
|
|
memcpy(buf, *val, ncpy);
|
|
else
|
|
memcpy(*val, buf, ncpy);
|
|
|
|
kunmap_atomic(pgaddr);
|
|
*val += ncpy;
|
|
}
|
|
*size -= ncpy;
|
|
cs->len -= ncpy;
|
|
cs->offset += ncpy;
|
|
return ncpy;
|
|
}
|
|
|
|
static int fuse_check_page(struct page *page)
|
|
{
|
|
if (page_mapcount(page) ||
|
|
page->mapping != NULL ||
|
|
(page->flags & PAGE_FLAGS_CHECK_AT_PREP &
|
|
~(1 << PG_locked |
|
|
1 << PG_referenced |
|
|
1 << PG_uptodate |
|
|
1 << PG_lru |
|
|
1 << PG_active |
|
|
1 << PG_workingset |
|
|
1 << PG_reclaim |
|
|
1 << PG_waiters))) {
|
|
dump_page(page, "fuse: trying to steal weird page");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
|
|
{
|
|
int err;
|
|
struct page *oldpage = *pagep;
|
|
struct page *newpage;
|
|
struct pipe_buffer *buf = cs->pipebufs;
|
|
|
|
get_page(oldpage);
|
|
err = unlock_request(cs->req);
|
|
if (err)
|
|
goto out_put_old;
|
|
|
|
fuse_copy_finish(cs);
|
|
|
|
err = pipe_buf_confirm(cs->pipe, buf);
|
|
if (err)
|
|
goto out_put_old;
|
|
|
|
BUG_ON(!cs->nr_segs);
|
|
cs->currbuf = buf;
|
|
cs->len = buf->len;
|
|
cs->pipebufs++;
|
|
cs->nr_segs--;
|
|
|
|
if (cs->len != PAGE_SIZE)
|
|
goto out_fallback;
|
|
|
|
if (!pipe_buf_try_steal(cs->pipe, buf))
|
|
goto out_fallback;
|
|
|
|
newpage = buf->page;
|
|
|
|
if (!PageUptodate(newpage))
|
|
SetPageUptodate(newpage);
|
|
|
|
ClearPageMappedToDisk(newpage);
|
|
|
|
if (fuse_check_page(newpage) != 0)
|
|
goto out_fallback_unlock;
|
|
|
|
/*
|
|
* This is a new and locked page, it shouldn't be mapped or
|
|
* have any special flags on it
|
|
*/
|
|
if (WARN_ON(page_mapped(oldpage)))
|
|
goto out_fallback_unlock;
|
|
if (WARN_ON(page_has_private(oldpage)))
|
|
goto out_fallback_unlock;
|
|
if (WARN_ON(PageDirty(oldpage) || PageWriteback(oldpage)))
|
|
goto out_fallback_unlock;
|
|
if (WARN_ON(PageMlocked(oldpage)))
|
|
goto out_fallback_unlock;
|
|
|
|
err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);
|
|
if (err) {
|
|
unlock_page(newpage);
|
|
goto out_put_old;
|
|
}
|
|
|
|
get_page(newpage);
|
|
|
|
if (!(buf->flags & PIPE_BUF_FLAG_LRU))
|
|
lru_cache_add(newpage);
|
|
|
|
/*
|
|
* Release while we have extra ref on stolen page. Otherwise
|
|
* anon_pipe_buf_release() might think the page can be reused.
|
|
*/
|
|
pipe_buf_release(cs->pipe, buf);
|
|
|
|
err = 0;
|
|
spin_lock(&cs->req->waitq.lock);
|
|
if (test_bit(FR_ABORTED, &cs->req->flags))
|
|
err = -ENOENT;
|
|
else
|
|
*pagep = newpage;
|
|
spin_unlock(&cs->req->waitq.lock);
|
|
|
|
if (err) {
|
|
unlock_page(newpage);
|
|
put_page(newpage);
|
|
goto out_put_old;
|
|
}
|
|
|
|
unlock_page(oldpage);
|
|
/* Drop ref for ap->pages[] array */
|
|
put_page(oldpage);
|
|
cs->len = 0;
|
|
|
|
err = 0;
|
|
out_put_old:
|
|
/* Drop ref obtained in this function */
|
|
put_page(oldpage);
|
|
return err;
|
|
|
|
out_fallback_unlock:
|
|
unlock_page(newpage);
|
|
out_fallback:
|
|
cs->pg = buf->page;
|
|
cs->offset = buf->offset;
|
|
|
|
err = lock_request(cs->req);
|
|
if (!err)
|
|
err = 1;
|
|
|
|
goto out_put_old;
|
|
}
|
|
|
|
static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
|
|
unsigned offset, unsigned count)
|
|
{
|
|
struct pipe_buffer *buf;
|
|
int err;
|
|
|
|
if (cs->nr_segs >= cs->pipe->max_usage)
|
|
return -EIO;
|
|
|
|
get_page(page);
|
|
err = unlock_request(cs->req);
|
|
if (err) {
|
|
put_page(page);
|
|
return err;
|
|
}
|
|
|
|
fuse_copy_finish(cs);
|
|
|
|
buf = cs->pipebufs;
|
|
buf->page = page;
|
|
buf->offset = offset;
|
|
buf->len = count;
|
|
|
|
cs->pipebufs++;
|
|
cs->nr_segs++;
|
|
cs->len = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Copy a page in the request to/from the userspace buffer. Must be
|
|
* done atomically
|
|
*/
|
|
static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
|
|
unsigned offset, unsigned count, int zeroing)
|
|
{
|
|
int err;
|
|
struct page *page = *pagep;
|
|
|
|
if (page && zeroing && count < PAGE_SIZE)
|
|
clear_highpage(page);
|
|
|
|
while (count) {
|
|
if (cs->write && cs->pipebufs && page) {
|
|
/*
|
|
* Can't control lifetime of pipe buffers, so always
|
|
* copy user pages.
|
|
*/
|
|
if (cs->req->args->user_pages) {
|
|
err = fuse_copy_fill(cs);
|
|
if (err)
|
|
return err;
|
|
} else {
|
|
return fuse_ref_page(cs, page, offset, count);
|
|
}
|
|
} else if (!cs->len) {
|
|
if (cs->move_pages && page &&
|
|
offset == 0 && count == PAGE_SIZE) {
|
|
err = fuse_try_move_page(cs, pagep);
|
|
if (err <= 0)
|
|
return err;
|
|
} else {
|
|
err = fuse_copy_fill(cs);
|
|
if (err)
|
|
return err;
|
|
}
|
|
}
|
|
if (page) {
|
|
void *mapaddr = kmap_atomic(page);
|
|
void *buf = mapaddr + offset;
|
|
offset += fuse_copy_do(cs, &buf, &count);
|
|
kunmap_atomic(mapaddr);
|
|
} else
|
|
offset += fuse_copy_do(cs, NULL, &count);
|
|
}
|
|
if (page && !cs->write)
|
|
flush_dcache_page(page);
|
|
return 0;
|
|
}
|
|
|
|
/* Copy pages in the request to/from userspace buffer */
|
|
static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
|
|
int zeroing)
|
|
{
|
|
unsigned i;
|
|
struct fuse_req *req = cs->req;
|
|
struct fuse_args_pages *ap = container_of(req->args, typeof(*ap), args);
|
|
|
|
|
|
for (i = 0; i < ap->num_pages && (nbytes || zeroing); i++) {
|
|
int err;
|
|
unsigned int offset = ap->descs[i].offset;
|
|
unsigned int count = min(nbytes, ap->descs[i].length);
|
|
|
|
err = fuse_copy_page(cs, &ap->pages[i], offset, count, zeroing);
|
|
if (err)
|
|
return err;
|
|
|
|
nbytes -= count;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Copy a single argument in the request to/from userspace buffer */
|
|
static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size)
|
|
{
|
|
while (size) {
|
|
if (!cs->len) {
|
|
int err = fuse_copy_fill(cs);
|
|
if (err)
|
|
return err;
|
|
}
|
|
fuse_copy_do(cs, &val, &size);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Copy request arguments to/from userspace buffer */
|
|
static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
|
|
unsigned argpages, struct fuse_arg *args,
|
|
int zeroing)
|
|
{
|
|
int err = 0;
|
|
unsigned i;
|
|
|
|
for (i = 0; !err && i < numargs; i++) {
|
|
struct fuse_arg *arg = &args[i];
|
|
if (i == numargs - 1 && argpages)
|
|
err = fuse_copy_pages(cs, arg->size, zeroing);
|
|
else
|
|
err = fuse_copy_one(cs, arg->value, arg->size);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int forget_pending(struct fuse_iqueue *fiq)
|
|
{
|
|
return fiq->forget_list_head.next != NULL;
|
|
}
|
|
|
|
static int request_pending(struct fuse_iqueue *fiq)
|
|
{
|
|
return !list_empty(&fiq->pending) || !list_empty(&fiq->interrupts) ||
|
|
forget_pending(fiq);
|
|
}
|
|
|
|
/*
|
|
* Transfer an interrupt request to userspace
|
|
*
|
|
* Unlike other requests this is assembled on demand, without a need
|
|
* to allocate a separate fuse_req structure.
|
|
*
|
|
* Called with fiq->lock held, releases it
|
|
*/
|
|
static int fuse_read_interrupt(struct fuse_iqueue *fiq,
|
|
struct fuse_copy_state *cs,
|
|
size_t nbytes, struct fuse_req *req)
|
|
__releases(fiq->lock)
|
|
{
|
|
struct fuse_in_header ih;
|
|
struct fuse_interrupt_in arg;
|
|
unsigned reqsize = sizeof(ih) + sizeof(arg);
|
|
int err;
|
|
|
|
list_del_init(&req->intr_entry);
|
|
memset(&ih, 0, sizeof(ih));
|
|
memset(&arg, 0, sizeof(arg));
|
|
ih.len = reqsize;
|
|
ih.opcode = FUSE_INTERRUPT;
|
|
ih.unique = (req->in.h.unique | FUSE_INT_REQ_BIT);
|
|
arg.unique = req->in.h.unique;
|
|
|
|
spin_unlock(&fiq->lock);
|
|
if (nbytes < reqsize)
|
|
return -EINVAL;
|
|
|
|
err = fuse_copy_one(cs, &ih, sizeof(ih));
|
|
if (!err)
|
|
err = fuse_copy_one(cs, &arg, sizeof(arg));
|
|
fuse_copy_finish(cs);
|
|
|
|
return err ? err : reqsize;
|
|
}
|
|
|
|
struct fuse_forget_link *fuse_dequeue_forget(struct fuse_iqueue *fiq,
|
|
unsigned int max,
|
|
unsigned int *countp)
|
|
{
|
|
struct fuse_forget_link *head = fiq->forget_list_head.next;
|
|
struct fuse_forget_link **newhead = &head;
|
|
unsigned count;
|
|
|
|
for (count = 0; *newhead != NULL && count < max; count++)
|
|
newhead = &(*newhead)->next;
|
|
|
|
fiq->forget_list_head.next = *newhead;
|
|
*newhead = NULL;
|
|
if (fiq->forget_list_head.next == NULL)
|
|
fiq->forget_list_tail = &fiq->forget_list_head;
|
|
|
|
if (countp != NULL)
|
|
*countp = count;
|
|
|
|
return head;
|
|
}
|
|
EXPORT_SYMBOL(fuse_dequeue_forget);
|
|
|
|
static int fuse_read_single_forget(struct fuse_iqueue *fiq,
|
|
struct fuse_copy_state *cs,
|
|
size_t nbytes)
|
|
__releases(fiq->lock)
|
|
{
|
|
int err;
|
|
struct fuse_forget_link *forget = fuse_dequeue_forget(fiq, 1, NULL);
|
|
struct fuse_forget_in arg = {
|
|
.nlookup = forget->forget_one.nlookup,
|
|
};
|
|
struct fuse_in_header ih = {
|
|
.opcode = FUSE_FORGET,
|
|
.nodeid = forget->forget_one.nodeid,
|
|
.unique = fuse_get_unique(fiq),
|
|
.len = sizeof(ih) + sizeof(arg),
|
|
};
|
|
|
|
spin_unlock(&fiq->lock);
|
|
kfree(forget);
|
|
if (nbytes < ih.len)
|
|
return -EINVAL;
|
|
|
|
err = fuse_copy_one(cs, &ih, sizeof(ih));
|
|
if (!err)
|
|
err = fuse_copy_one(cs, &arg, sizeof(arg));
|
|
fuse_copy_finish(cs);
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
return ih.len;
|
|
}
|
|
|
|
static int fuse_read_batch_forget(struct fuse_iqueue *fiq,
|
|
struct fuse_copy_state *cs, size_t nbytes)
|
|
__releases(fiq->lock)
|
|
{
|
|
int err;
|
|
unsigned max_forgets;
|
|
unsigned count;
|
|
struct fuse_forget_link *head;
|
|
struct fuse_batch_forget_in arg = { .count = 0 };
|
|
struct fuse_in_header ih = {
|
|
.opcode = FUSE_BATCH_FORGET,
|
|
.unique = fuse_get_unique(fiq),
|
|
.len = sizeof(ih) + sizeof(arg),
|
|
};
|
|
|
|
if (nbytes < ih.len) {
|
|
spin_unlock(&fiq->lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
max_forgets = (nbytes - ih.len) / sizeof(struct fuse_forget_one);
|
|
head = fuse_dequeue_forget(fiq, max_forgets, &count);
|
|
spin_unlock(&fiq->lock);
|
|
|
|
arg.count = count;
|
|
ih.len += count * sizeof(struct fuse_forget_one);
|
|
err = fuse_copy_one(cs, &ih, sizeof(ih));
|
|
if (!err)
|
|
err = fuse_copy_one(cs, &arg, sizeof(arg));
|
|
|
|
while (head) {
|
|
struct fuse_forget_link *forget = head;
|
|
|
|
if (!err) {
|
|
err = fuse_copy_one(cs, &forget->forget_one,
|
|
sizeof(forget->forget_one));
|
|
}
|
|
head = forget->next;
|
|
kfree(forget);
|
|
}
|
|
|
|
fuse_copy_finish(cs);
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
return ih.len;
|
|
}
|
|
|
|
static int fuse_read_forget(struct fuse_conn *fc, struct fuse_iqueue *fiq,
|
|
struct fuse_copy_state *cs,
|
|
size_t nbytes)
|
|
__releases(fiq->lock)
|
|
{
|
|
if (fc->minor < 16 || fiq->forget_list_head.next->next == NULL)
|
|
return fuse_read_single_forget(fiq, cs, nbytes);
|
|
else
|
|
return fuse_read_batch_forget(fiq, cs, nbytes);
|
|
}
|
|
|
|
/*
|
|
* Read a single request into the userspace filesystem's buffer. This
|
|
* function waits until a request is available, then removes it from
|
|
* the pending list and copies request data to userspace buffer. If
|
|
* no reply is needed (FORGET) or request has been aborted or there
|
|
* was an error during the copying then it's finished by calling
|
|
* fuse_request_end(). Otherwise add it to the processing list, and set
|
|
* the 'sent' flag.
|
|
*/
|
|
static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
|
|
struct fuse_copy_state *cs, size_t nbytes)
|
|
{
|
|
ssize_t err;
|
|
struct fuse_conn *fc = fud->fc;
|
|
struct fuse_iqueue *fiq = &fc->iq;
|
|
struct fuse_pqueue *fpq = &fud->pq;
|
|
struct fuse_req *req;
|
|
struct fuse_args *args;
|
|
unsigned reqsize;
|
|
unsigned int hash;
|
|
|
|
/*
|
|
* Require sane minimum read buffer - that has capacity for fixed part
|
|
* of any request header + negotiated max_write room for data.
|
|
*
|
|
* Historically libfuse reserves 4K for fixed header room, but e.g.
|
|
* GlusterFS reserves only 80 bytes
|
|
*
|
|
* = `sizeof(fuse_in_header) + sizeof(fuse_write_in)`
|
|
*
|
|
* which is the absolute minimum any sane filesystem should be using
|
|
* for header room.
|
|
*/
|
|
if (nbytes < max_t(size_t, FUSE_MIN_READ_BUFFER,
|
|
sizeof(struct fuse_in_header) +
|
|
sizeof(struct fuse_write_in) +
|
|
fc->max_write))
|
|
return -EINVAL;
|
|
|
|
restart:
|
|
for (;;) {
|
|
spin_lock(&fiq->lock);
|
|
if (!fiq->connected || request_pending(fiq))
|
|
break;
|
|
spin_unlock(&fiq->lock);
|
|
|
|
if (file->f_flags & O_NONBLOCK)
|
|
return -EAGAIN;
|
|
err = wait_event_interruptible_exclusive(fiq->waitq,
|
|
!fiq->connected || request_pending(fiq));
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (!fiq->connected) {
|
|
err = fc->aborted ? -ECONNABORTED : -ENODEV;
|
|
goto err_unlock;
|
|
}
|
|
|
|
if (!list_empty(&fiq->interrupts)) {
|
|
req = list_entry(fiq->interrupts.next, struct fuse_req,
|
|
intr_entry);
|
|
return fuse_read_interrupt(fiq, cs, nbytes, req);
|
|
}
|
|
|
|
if (forget_pending(fiq)) {
|
|
if (list_empty(&fiq->pending) || fiq->forget_batch-- > 0)
|
|
return fuse_read_forget(fc, fiq, cs, nbytes);
|
|
|
|
if (fiq->forget_batch <= -8)
|
|
fiq->forget_batch = 16;
|
|
}
|
|
|
|
req = list_entry(fiq->pending.next, struct fuse_req, list);
|
|
clear_bit(FR_PENDING, &req->flags);
|
|
list_del_init(&req->list);
|
|
spin_unlock(&fiq->lock);
|
|
|
|
args = req->args;
|
|
reqsize = req->in.h.len;
|
|
|
|
/* If request is too large, reply with an error and restart the read */
|
|
if (nbytes < reqsize) {
|
|
req->out.h.error = -EIO;
|
|
/* SETXATTR is special, since it may contain too large data */
|
|
if (args->opcode == FUSE_SETXATTR)
|
|
req->out.h.error = -E2BIG;
|
|
fuse_request_end(req);
|
|
goto restart;
|
|
}
|
|
spin_lock(&fpq->lock);
|
|
/*
|
|
* Must not put request on fpq->io queue after having been shut down by
|
|
* fuse_abort_conn()
|
|
*/
|
|
if (!fpq->connected) {
|
|
req->out.h.error = err = -ECONNABORTED;
|
|
goto out_end;
|
|
|
|
}
|
|
list_add(&req->list, &fpq->io);
|
|
spin_unlock(&fpq->lock);
|
|
cs->req = req;
|
|
err = fuse_copy_one(cs, &req->in.h, sizeof(req->in.h));
|
|
if (!err)
|
|
err = fuse_copy_args(cs, args->in_numargs, args->in_pages,
|
|
(struct fuse_arg *) args->in_args, 0);
|
|
fuse_copy_finish(cs);
|
|
spin_lock(&fpq->lock);
|
|
clear_bit(FR_LOCKED, &req->flags);
|
|
if (!fpq->connected) {
|
|
err = fc->aborted ? -ECONNABORTED : -ENODEV;
|
|
goto out_end;
|
|
}
|
|
if (err) {
|
|
req->out.h.error = -EIO;
|
|
goto out_end;
|
|
}
|
|
if (!test_bit(FR_ISREPLY, &req->flags)) {
|
|
err = reqsize;
|
|
goto out_end;
|
|
}
|
|
hash = fuse_req_hash(req->in.h.unique);
|
|
list_move_tail(&req->list, &fpq->processing[hash]);
|
|
__fuse_get_request(req);
|
|
set_bit(FR_SENT, &req->flags);
|
|
spin_unlock(&fpq->lock);
|
|
/* matches barrier in request_wait_answer() */
|
|
smp_mb__after_atomic();
|
|
if (test_bit(FR_INTERRUPTED, &req->flags))
|
|
queue_interrupt(req);
|
|
fuse_put_request(req);
|
|
|
|
return reqsize;
|
|
|
|
out_end:
|
|
if (!test_bit(FR_PRIVATE, &req->flags))
|
|
list_del_init(&req->list);
|
|
spin_unlock(&fpq->lock);
|
|
fuse_request_end(req);
|
|
return err;
|
|
|
|
err_unlock:
|
|
spin_unlock(&fiq->lock);
|
|
return err;
|
|
}
|
|
|
|
static int fuse_dev_open(struct inode *inode, struct file *file)
|
|
{
|
|
/*
|
|
* The fuse device's file's private_data is used to hold
|
|
* the fuse_conn(ection) when it is mounted, and is used to
|
|
* keep track of whether the file has been mounted already.
|
|
*/
|
|
file->private_data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to)
|
|
{
|
|
struct fuse_copy_state cs;
|
|
struct file *file = iocb->ki_filp;
|
|
struct fuse_dev *fud = fuse_get_dev(file);
|
|
|
|
if (!fud)
|
|
return -EPERM;
|
|
|
|
if (!iter_is_iovec(to))
|
|
return -EINVAL;
|
|
|
|
fuse_copy_init(&cs, 1, to);
|
|
|
|
return fuse_dev_do_read(fud, file, &cs, iov_iter_count(to));
|
|
}
|
|
|
|
static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
|
|
struct pipe_inode_info *pipe,
|
|
size_t len, unsigned int flags)
|
|
{
|
|
int total, ret;
|
|
int page_nr = 0;
|
|
struct pipe_buffer *bufs;
|
|
struct fuse_copy_state cs;
|
|
struct fuse_dev *fud = fuse_get_dev(in);
|
|
|
|
if (!fud)
|
|
return -EPERM;
|
|
|
|
bufs = kvmalloc_array(pipe->max_usage, sizeof(struct pipe_buffer),
|
|
GFP_KERNEL);
|
|
if (!bufs)
|
|
return -ENOMEM;
|
|
|
|
fuse_copy_init(&cs, 1, NULL);
|
|
cs.pipebufs = bufs;
|
|
cs.pipe = pipe;
|
|
ret = fuse_dev_do_read(fud, in, &cs, len);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
if (pipe_occupancy(pipe->head, pipe->tail) + cs.nr_segs > pipe->max_usage) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
for (ret = total = 0; page_nr < cs.nr_segs; total += ret) {
|
|
/*
|
|
* Need to be careful about this. Having buf->ops in module
|
|
* code can Oops if the buffer persists after module unload.
|
|
*/
|
|
bufs[page_nr].ops = &nosteal_pipe_buf_ops;
|
|
bufs[page_nr].flags = 0;
|
|
ret = add_to_pipe(pipe, &bufs[page_nr++]);
|
|
if (unlikely(ret < 0))
|
|
break;
|
|
}
|
|
if (total)
|
|
ret = total;
|
|
out:
|
|
for (; page_nr < cs.nr_segs; page_nr++)
|
|
put_page(bufs[page_nr].page);
|
|
|
|
kvfree(bufs);
|
|
return ret;
|
|
}
|
|
|
|
static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size,
|
|
struct fuse_copy_state *cs)
|
|
{
|
|
struct fuse_notify_poll_wakeup_out outarg;
|
|
int err = -EINVAL;
|
|
|
|
if (size != sizeof(outarg))
|
|
goto err;
|
|
|
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
|
if (err)
|
|
goto err;
|
|
|
|
fuse_copy_finish(cs);
|
|
return fuse_notify_poll_wakeup(fc, &outarg);
|
|
|
|
err:
|
|
fuse_copy_finish(cs);
|
|
return err;
|
|
}
|
|
|
|
static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size,
|
|
struct fuse_copy_state *cs)
|
|
{
|
|
struct fuse_notify_inval_inode_out outarg;
|
|
int err = -EINVAL;
|
|
|
|
if (size != sizeof(outarg))
|
|
goto err;
|
|
|
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
|
if (err)
|
|
goto err;
|
|
fuse_copy_finish(cs);
|
|
|
|
down_read(&fc->killsb);
|
|
err = fuse_reverse_inval_inode(fc, outarg.ino,
|
|
outarg.off, outarg.len);
|
|
up_read(&fc->killsb);
|
|
return err;
|
|
|
|
err:
|
|
fuse_copy_finish(cs);
|
|
return err;
|
|
}
|
|
|
|
static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
|
|
struct fuse_copy_state *cs)
|
|
{
|
|
struct fuse_notify_inval_entry_out outarg;
|
|
int err = -ENOMEM;
|
|
char *buf;
|
|
struct qstr name;
|
|
|
|
buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL);
|
|
if (!buf)
|
|
goto err;
|
|
|
|
err = -EINVAL;
|
|
if (size < sizeof(outarg))
|
|
goto err;
|
|
|
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
|
if (err)
|
|
goto err;
|
|
|
|
err = -ENAMETOOLONG;
|
|
if (outarg.namelen > FUSE_NAME_MAX)
|
|
goto err;
|
|
|
|
err = -EINVAL;
|
|
if (size != sizeof(outarg) + outarg.namelen + 1)
|
|
goto err;
|
|
|
|
name.name = buf;
|
|
name.len = outarg.namelen;
|
|
err = fuse_copy_one(cs, buf, outarg.namelen + 1);
|
|
if (err)
|
|
goto err;
|
|
fuse_copy_finish(cs);
|
|
buf[outarg.namelen] = 0;
|
|
|
|
down_read(&fc->killsb);
|
|
err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name);
|
|
up_read(&fc->killsb);
|
|
kfree(buf);
|
|
return err;
|
|
|
|
err:
|
|
kfree(buf);
|
|
fuse_copy_finish(cs);
|
|
return err;
|
|
}
|
|
|
|
static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
|
|
struct fuse_copy_state *cs)
|
|
{
|
|
struct fuse_notify_delete_out outarg;
|
|
int err = -ENOMEM;
|
|
char *buf;
|
|
struct qstr name;
|
|
|
|
buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL);
|
|
if (!buf)
|
|
goto err;
|
|
|
|
err = -EINVAL;
|
|
if (size < sizeof(outarg))
|
|
goto err;
|
|
|
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
|
if (err)
|
|
goto err;
|
|
|
|
err = -ENAMETOOLONG;
|
|
if (outarg.namelen > FUSE_NAME_MAX)
|
|
goto err;
|
|
|
|
err = -EINVAL;
|
|
if (size != sizeof(outarg) + outarg.namelen + 1)
|
|
goto err;
|
|
|
|
name.name = buf;
|
|
name.len = outarg.namelen;
|
|
err = fuse_copy_one(cs, buf, outarg.namelen + 1);
|
|
if (err)
|
|
goto err;
|
|
fuse_copy_finish(cs);
|
|
buf[outarg.namelen] = 0;
|
|
|
|
down_read(&fc->killsb);
|
|
err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name);
|
|
up_read(&fc->killsb);
|
|
kfree(buf);
|
|
return err;
|
|
|
|
err:
|
|
kfree(buf);
|
|
fuse_copy_finish(cs);
|
|
return err;
|
|
}
|
|
|
|
static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
|
|
struct fuse_copy_state *cs)
|
|
{
|
|
struct fuse_notify_store_out outarg;
|
|
struct inode *inode;
|
|
struct address_space *mapping;
|
|
u64 nodeid;
|
|
int err;
|
|
pgoff_t index;
|
|
unsigned int offset;
|
|
unsigned int num;
|
|
loff_t file_size;
|
|
loff_t end;
|
|
|
|
err = -EINVAL;
|
|
if (size < sizeof(outarg))
|
|
goto out_finish;
|
|
|
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
|
if (err)
|
|
goto out_finish;
|
|
|
|
err = -EINVAL;
|
|
if (size - sizeof(outarg) != outarg.size)
|
|
goto out_finish;
|
|
|
|
nodeid = outarg.nodeid;
|
|
|
|
down_read(&fc->killsb);
|
|
|
|
err = -ENOENT;
|
|
inode = fuse_ilookup(fc, nodeid, NULL);
|
|
if (!inode)
|
|
goto out_up_killsb;
|
|
|
|
mapping = inode->i_mapping;
|
|
index = outarg.offset >> PAGE_SHIFT;
|
|
offset = outarg.offset & ~PAGE_MASK;
|
|
file_size = i_size_read(inode);
|
|
end = outarg.offset + outarg.size;
|
|
if (end > file_size) {
|
|
file_size = end;
|
|
fuse_write_update_size(inode, file_size);
|
|
}
|
|
|
|
num = outarg.size;
|
|
while (num) {
|
|
struct page *page;
|
|
unsigned int this_num;
|
|
|
|
err = -ENOMEM;
|
|
page = find_or_create_page(mapping, index,
|
|
mapping_gfp_mask(mapping));
|
|
if (!page)
|
|
goto out_iput;
|
|
|
|
this_num = min_t(unsigned, num, PAGE_SIZE - offset);
|
|
err = fuse_copy_page(cs, &page, offset, this_num, 0);
|
|
if (!PageUptodate(page) && !err && offset == 0 &&
|
|
(this_num == PAGE_SIZE || file_size == end)) {
|
|
zero_user_segment(page, this_num, PAGE_SIZE);
|
|
SetPageUptodate(page);
|
|
}
|
|
unlock_page(page);
|
|
put_page(page);
|
|
|
|
if (err)
|
|
goto out_iput;
|
|
|
|
num -= this_num;
|
|
offset = 0;
|
|
index++;
|
|
}
|
|
|
|
err = 0;
|
|
|
|
out_iput:
|
|
iput(inode);
|
|
out_up_killsb:
|
|
up_read(&fc->killsb);
|
|
out_finish:
|
|
fuse_copy_finish(cs);
|
|
return err;
|
|
}
|
|
|
|
struct fuse_retrieve_args {
|
|
struct fuse_args_pages ap;
|
|
struct fuse_notify_retrieve_in inarg;
|
|
};
|
|
|
|
static void fuse_retrieve_end(struct fuse_mount *fm, struct fuse_args *args,
|
|
int error)
|
|
{
|
|
struct fuse_retrieve_args *ra =
|
|
container_of(args, typeof(*ra), ap.args);
|
|
|
|
release_pages(ra->ap.pages, ra->ap.num_pages);
|
|
kfree(ra);
|
|
}
|
|
|
|
static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
|
|
struct fuse_notify_retrieve_out *outarg)
|
|
{
|
|
int err;
|
|
struct address_space *mapping = inode->i_mapping;
|
|
pgoff_t index;
|
|
loff_t file_size;
|
|
unsigned int num;
|
|
unsigned int offset;
|
|
size_t total_len = 0;
|
|
unsigned int num_pages;
|
|
struct fuse_conn *fc = fm->fc;
|
|
struct fuse_retrieve_args *ra;
|
|
size_t args_size = sizeof(*ra);
|
|
struct fuse_args_pages *ap;
|
|
struct fuse_args *args;
|
|
|
|
offset = outarg->offset & ~PAGE_MASK;
|
|
file_size = i_size_read(inode);
|
|
|
|
num = min(outarg->size, fc->max_write);
|
|
if (outarg->offset > file_size)
|
|
num = 0;
|
|
else if (outarg->offset + num > file_size)
|
|
num = file_size - outarg->offset;
|
|
|
|
num_pages = (num + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
num_pages = min(num_pages, fc->max_pages);
|
|
|
|
args_size += num_pages * (sizeof(ap->pages[0]) + sizeof(ap->descs[0]));
|
|
|
|
ra = kzalloc(args_size, GFP_KERNEL);
|
|
if (!ra)
|
|
return -ENOMEM;
|
|
|
|
ap = &ra->ap;
|
|
ap->pages = (void *) (ra + 1);
|
|
ap->descs = (void *) (ap->pages + num_pages);
|
|
|
|
args = &ap->args;
|
|
args->nodeid = outarg->nodeid;
|
|
args->opcode = FUSE_NOTIFY_REPLY;
|
|
args->in_numargs = 2;
|
|
args->in_pages = true;
|
|
args->end = fuse_retrieve_end;
|
|
|
|
index = outarg->offset >> PAGE_SHIFT;
|
|
|
|
while (num && ap->num_pages < num_pages) {
|
|
struct page *page;
|
|
unsigned int this_num;
|
|
|
|
page = find_get_page(mapping, index);
|
|
if (!page)
|
|
break;
|
|
|
|
this_num = min_t(unsigned, num, PAGE_SIZE - offset);
|
|
ap->pages[ap->num_pages] = page;
|
|
ap->descs[ap->num_pages].offset = offset;
|
|
ap->descs[ap->num_pages].length = this_num;
|
|
ap->num_pages++;
|
|
|
|
offset = 0;
|
|
num -= this_num;
|
|
total_len += this_num;
|
|
index++;
|
|
}
|
|
ra->inarg.offset = outarg->offset;
|
|
ra->inarg.size = total_len;
|
|
args->in_args[0].size = sizeof(ra->inarg);
|
|
args->in_args[0].value = &ra->inarg;
|
|
args->in_args[1].size = total_len;
|
|
|
|
err = fuse_simple_notify_reply(fm, args, outarg->notify_unique);
|
|
if (err)
|
|
fuse_retrieve_end(fm, args, err);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
|
|
struct fuse_copy_state *cs)
|
|
{
|
|
struct fuse_notify_retrieve_out outarg;
|
|
struct fuse_mount *fm;
|
|
struct inode *inode;
|
|
u64 nodeid;
|
|
int err;
|
|
|
|
err = -EINVAL;
|
|
if (size != sizeof(outarg))
|
|
goto copy_finish;
|
|
|
|
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
|
if (err)
|
|
goto copy_finish;
|
|
|
|
fuse_copy_finish(cs);
|
|
|
|
down_read(&fc->killsb);
|
|
err = -ENOENT;
|
|
nodeid = outarg.nodeid;
|
|
|
|
inode = fuse_ilookup(fc, nodeid, &fm);
|
|
if (inode) {
|
|
err = fuse_retrieve(fm, inode, &outarg);
|
|
iput(inode);
|
|
}
|
|
up_read(&fc->killsb);
|
|
|
|
return err;
|
|
|
|
copy_finish:
|
|
fuse_copy_finish(cs);
|
|
return err;
|
|
}
|
|
|
|
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
|
|
unsigned int size, struct fuse_copy_state *cs)
|
|
{
|
|
/* Don't try to move pages (yet) */
|
|
cs->move_pages = 0;
|
|
|
|
switch (code) {
|
|
case FUSE_NOTIFY_POLL:
|
|
return fuse_notify_poll(fc, size, cs);
|
|
|
|
case FUSE_NOTIFY_INVAL_INODE:
|
|
return fuse_notify_inval_inode(fc, size, cs);
|
|
|
|
case FUSE_NOTIFY_INVAL_ENTRY:
|
|
return fuse_notify_inval_entry(fc, size, cs);
|
|
|
|
case FUSE_NOTIFY_STORE:
|
|
return fuse_notify_store(fc, size, cs);
|
|
|
|
case FUSE_NOTIFY_RETRIEVE:
|
|
return fuse_notify_retrieve(fc, size, cs);
|
|
|
|
case FUSE_NOTIFY_DELETE:
|
|
return fuse_notify_delete(fc, size, cs);
|
|
|
|
default:
|
|
fuse_copy_finish(cs);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Look up request on processing list by unique ID */
|
|
static struct fuse_req *request_find(struct fuse_pqueue *fpq, u64 unique)
|
|
{
|
|
unsigned int hash = fuse_req_hash(unique);
|
|
struct fuse_req *req;
|
|
|
|
list_for_each_entry(req, &fpq->processing[hash], list) {
|
|
if (req->in.h.unique == unique)
|
|
return req;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args,
|
|
unsigned nbytes)
|
|
{
|
|
unsigned reqsize = sizeof(struct fuse_out_header);
|
|
|
|
reqsize += fuse_len_args(args->out_numargs, args->out_args);
|
|
|
|
if (reqsize < nbytes || (reqsize > nbytes && !args->out_argvar))
|
|
return -EINVAL;
|
|
else if (reqsize > nbytes) {
|
|
struct fuse_arg *lastarg = &args->out_args[args->out_numargs-1];
|
|
unsigned diffsize = reqsize - nbytes;
|
|
|
|
if (diffsize > lastarg->size)
|
|
return -EINVAL;
|
|
lastarg->size -= diffsize;
|
|
}
|
|
return fuse_copy_args(cs, args->out_numargs, args->out_pages,
|
|
args->out_args, args->page_zeroing);
|
|
}
|
|
|
|
/*
|
|
* Write a single reply to a request. First the header is copied from
|
|
* the write buffer. The request is then searched on the processing
|
|
* list by the unique ID found in the header. If found, then remove
|
|
* it from the list and copy the rest of the buffer to the request.
|
|
* The request is finished by calling fuse_request_end().
|
|
*/
|
|
static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
|
|
struct fuse_copy_state *cs, size_t nbytes)
|
|
{
|
|
int err;
|
|
struct fuse_conn *fc = fud->fc;
|
|
struct fuse_pqueue *fpq = &fud->pq;
|
|
struct fuse_req *req;
|
|
struct fuse_out_header oh;
|
|
|
|
err = -EINVAL;
|
|
if (nbytes < sizeof(struct fuse_out_header))
|
|
goto out;
|
|
|
|
err = fuse_copy_one(cs, &oh, sizeof(oh));
|
|
if (err)
|
|
goto copy_finish;
|
|
|
|
err = -EINVAL;
|
|
if (oh.len != nbytes)
|
|
goto copy_finish;
|
|
|
|
/*
|
|
* Zero oh.unique indicates unsolicited notification message
|
|
* and error contains notification code.
|
|
*/
|
|
if (!oh.unique) {
|
|
err = fuse_notify(fc, oh.error, nbytes - sizeof(oh), cs);
|
|
goto out;
|
|
}
|
|
|
|
err = -EINVAL;
|
|
if (oh.error <= -512 || oh.error > 0)
|
|
goto copy_finish;
|
|
|
|
spin_lock(&fpq->lock);
|
|
req = NULL;
|
|
if (fpq->connected)
|
|
req = request_find(fpq, oh.unique & ~FUSE_INT_REQ_BIT);
|
|
|
|
err = -ENOENT;
|
|
if (!req) {
|
|
spin_unlock(&fpq->lock);
|
|
goto copy_finish;
|
|
}
|
|
|
|
/* Is it an interrupt reply ID? */
|
|
if (oh.unique & FUSE_INT_REQ_BIT) {
|
|
__fuse_get_request(req);
|
|
spin_unlock(&fpq->lock);
|
|
|
|
err = 0;
|
|
if (nbytes != sizeof(struct fuse_out_header))
|
|
err = -EINVAL;
|
|
else if (oh.error == -ENOSYS)
|
|
fc->no_interrupt = 1;
|
|
else if (oh.error == -EAGAIN)
|
|
err = queue_interrupt(req);
|
|
|
|
fuse_put_request(req);
|
|
|
|
goto copy_finish;
|
|
}
|
|
|
|
clear_bit(FR_SENT, &req->flags);
|
|
list_move(&req->list, &fpq->io);
|
|
req->out.h = oh;
|
|
set_bit(FR_LOCKED, &req->flags);
|
|
spin_unlock(&fpq->lock);
|
|
cs->req = req;
|
|
if (!req->args->page_replace)
|
|
cs->move_pages = 0;
|
|
|
|
if (oh.error)
|
|
err = nbytes != sizeof(oh) ? -EINVAL : 0;
|
|
else
|
|
err = copy_out_args(cs, req->args, nbytes);
|
|
fuse_copy_finish(cs);
|
|
|
|
if (!err && req->in.h.opcode == FUSE_CANONICAL_PATH) {
|
|
char *path = (char *)req->args->out_args[0].value;
|
|
|
|
path[req->args->out_args[0].size - 1] = 0;
|
|
req->out.h.error =
|
|
kern_path(path, 0, req->args->canonical_path);
|
|
}
|
|
|
|
spin_lock(&fpq->lock);
|
|
clear_bit(FR_LOCKED, &req->flags);
|
|
if (!fpq->connected)
|
|
err = -ENOENT;
|
|
else if (err)
|
|
req->out.h.error = -EIO;
|
|
if (!test_bit(FR_PRIVATE, &req->flags))
|
|
list_del_init(&req->list);
|
|
spin_unlock(&fpq->lock);
|
|
|
|
fuse_request_end(req);
|
|
out:
|
|
return err ? err : nbytes;
|
|
|
|
copy_finish:
|
|
fuse_copy_finish(cs);
|
|
goto out;
|
|
}
|
|
|
|
static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from)
|
|
{
|
|
struct fuse_copy_state cs;
|
|
struct fuse_dev *fud = fuse_get_dev(iocb->ki_filp);
|
|
|
|
if (!fud)
|
|
return -EPERM;
|
|
|
|
if (!iter_is_iovec(from))
|
|
return -EINVAL;
|
|
|
|
fuse_copy_init(&cs, 0, from);
|
|
|
|
return fuse_dev_do_write(fud, &cs, iov_iter_count(from));
|
|
}
|
|
|
|
static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
|
|
struct file *out, loff_t *ppos,
|
|
size_t len, unsigned int flags)
|
|
{
|
|
unsigned int head, tail, mask, count;
|
|
unsigned nbuf;
|
|
unsigned idx;
|
|
struct pipe_buffer *bufs;
|
|
struct fuse_copy_state cs;
|
|
struct fuse_dev *fud;
|
|
size_t rem;
|
|
ssize_t ret;
|
|
|
|
fud = fuse_get_dev(out);
|
|
if (!fud)
|
|
return -EPERM;
|
|
|
|
pipe_lock(pipe);
|
|
|
|
head = pipe->head;
|
|
tail = pipe->tail;
|
|
mask = pipe->ring_size - 1;
|
|
count = head - tail;
|
|
|
|
bufs = kvmalloc_array(count, sizeof(struct pipe_buffer), GFP_KERNEL);
|
|
if (!bufs) {
|
|
pipe_unlock(pipe);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
nbuf = 0;
|
|
rem = 0;
|
|
for (idx = tail; idx != head && rem < len; idx++)
|
|
rem += pipe->bufs[idx & mask].len;
|
|
|
|
ret = -EINVAL;
|
|
if (rem < len)
|
|
goto out_free;
|
|
|
|
rem = len;
|
|
while (rem) {
|
|
struct pipe_buffer *ibuf;
|
|
struct pipe_buffer *obuf;
|
|
|
|
if (WARN_ON(nbuf >= count || tail == head))
|
|
goto out_free;
|
|
|
|
ibuf = &pipe->bufs[tail & mask];
|
|
obuf = &bufs[nbuf];
|
|
|
|
if (rem >= ibuf->len) {
|
|
*obuf = *ibuf;
|
|
ibuf->ops = NULL;
|
|
tail++;
|
|
pipe->tail = tail;
|
|
} else {
|
|
if (!pipe_buf_get(pipe, ibuf))
|
|
goto out_free;
|
|
|
|
*obuf = *ibuf;
|
|
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
|
|
obuf->len = rem;
|
|
ibuf->offset += obuf->len;
|
|
ibuf->len -= obuf->len;
|
|
}
|
|
nbuf++;
|
|
rem -= obuf->len;
|
|
}
|
|
pipe_unlock(pipe);
|
|
|
|
fuse_copy_init(&cs, 0, NULL);
|
|
cs.pipebufs = bufs;
|
|
cs.nr_segs = nbuf;
|
|
cs.pipe = pipe;
|
|
|
|
if (flags & SPLICE_F_MOVE)
|
|
cs.move_pages = 1;
|
|
|
|
ret = fuse_dev_do_write(fud, &cs, len);
|
|
|
|
pipe_lock(pipe);
|
|
out_free:
|
|
for (idx = 0; idx < nbuf; idx++) {
|
|
struct pipe_buffer *buf = &bufs[idx];
|
|
|
|
if (buf->ops)
|
|
pipe_buf_release(pipe, buf);
|
|
}
|
|
pipe_unlock(pipe);
|
|
|
|
kvfree(bufs);
|
|
return ret;
|
|
}
|
|
|
|
static __poll_t fuse_dev_poll(struct file *file, poll_table *wait)
|
|
{
|
|
__poll_t mask = EPOLLOUT | EPOLLWRNORM;
|
|
struct fuse_iqueue *fiq;
|
|
struct fuse_dev *fud = fuse_get_dev(file);
|
|
|
|
if (!fud)
|
|
return EPOLLERR;
|
|
|
|
fiq = &fud->fc->iq;
|
|
poll_wait(file, &fiq->waitq, wait);
|
|
|
|
spin_lock(&fiq->lock);
|
|
if (!fiq->connected)
|
|
mask = EPOLLERR;
|
|
else if (request_pending(fiq))
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
|
spin_unlock(&fiq->lock);
|
|
|
|
return mask;
|
|
}
|
|
|
|
/* Abort all requests on the given list (pending or processing) */
|
|
static void end_requests(struct list_head *head)
|
|
{
|
|
while (!list_empty(head)) {
|
|
struct fuse_req *req;
|
|
req = list_entry(head->next, struct fuse_req, list);
|
|
req->out.h.error = -ECONNABORTED;
|
|
clear_bit(FR_SENT, &req->flags);
|
|
list_del_init(&req->list);
|
|
fuse_request_end(req);
|
|
}
|
|
}
|
|
|
|
static void end_polls(struct fuse_conn *fc)
|
|
{
|
|
struct rb_node *p;
|
|
|
|
p = rb_first(&fc->polled_files);
|
|
|
|
while (p) {
|
|
struct fuse_file *ff;
|
|
ff = rb_entry(p, struct fuse_file, polled_node);
|
|
wake_up_interruptible_all(&ff->poll_wait);
|
|
|
|
p = rb_next(p);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Abort all requests.
|
|
*
|
|
* Emergency exit in case of a malicious or accidental deadlock, or just a hung
|
|
* filesystem.
|
|
*
|
|
* The same effect is usually achievable through killing the filesystem daemon
|
|
* and all users of the filesystem. The exception is the combination of an
|
|
* asynchronous request and the tricky deadlock (see
|
|
* Documentation/filesystems/fuse.rst).
|
|
*
|
|
* Aborting requests under I/O goes as follows: 1: Separate out unlocked
|
|
* requests, they should be finished off immediately. Locked requests will be
|
|
* finished after unlock; see unlock_request(). 2: Finish off the unlocked
|
|
* requests. It is possible that some request will finish before we can. This
|
|
* is OK, the request will in that case be removed from the list before we touch
|
|
* it.
|
|
*/
|
|
void fuse_abort_conn(struct fuse_conn *fc)
|
|
{
|
|
struct fuse_iqueue *fiq = &fc->iq;
|
|
|
|
spin_lock(&fc->lock);
|
|
if (fc->connected) {
|
|
struct fuse_dev *fud;
|
|
struct fuse_req *req, *next;
|
|
LIST_HEAD(to_end);
|
|
unsigned int i;
|
|
|
|
/* Background queuing checks fc->connected under bg_lock */
|
|
spin_lock(&fc->bg_lock);
|
|
fc->connected = 0;
|
|
spin_unlock(&fc->bg_lock);
|
|
|
|
fuse_set_initialized(fc);
|
|
list_for_each_entry(fud, &fc->devices, entry) {
|
|
struct fuse_pqueue *fpq = &fud->pq;
|
|
|
|
spin_lock(&fpq->lock);
|
|
fpq->connected = 0;
|
|
list_for_each_entry_safe(req, next, &fpq->io, list) {
|
|
req->out.h.error = -ECONNABORTED;
|
|
spin_lock(&req->waitq.lock);
|
|
set_bit(FR_ABORTED, &req->flags);
|
|
if (!test_bit(FR_LOCKED, &req->flags)) {
|
|
set_bit(FR_PRIVATE, &req->flags);
|
|
__fuse_get_request(req);
|
|
list_move(&req->list, &to_end);
|
|
}
|
|
spin_unlock(&req->waitq.lock);
|
|
}
|
|
for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
|
|
list_splice_tail_init(&fpq->processing[i],
|
|
&to_end);
|
|
spin_unlock(&fpq->lock);
|
|
}
|
|
spin_lock(&fc->bg_lock);
|
|
fc->blocked = 0;
|
|
fc->max_background = UINT_MAX;
|
|
flush_bg_queue(fc);
|
|
spin_unlock(&fc->bg_lock);
|
|
|
|
spin_lock(&fiq->lock);
|
|
fiq->connected = 0;
|
|
list_for_each_entry(req, &fiq->pending, list)
|
|
clear_bit(FR_PENDING, &req->flags);
|
|
list_splice_tail_init(&fiq->pending, &to_end);
|
|
while (forget_pending(fiq))
|
|
kfree(fuse_dequeue_forget(fiq, 1, NULL));
|
|
wake_up_all(&fiq->waitq);
|
|
spin_unlock(&fiq->lock);
|
|
kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
|
|
end_polls(fc);
|
|
wake_up_all(&fc->blocked_waitq);
|
|
spin_unlock(&fc->lock);
|
|
|
|
end_requests(&to_end);
|
|
} else {
|
|
spin_unlock(&fc->lock);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_abort_conn);
|
|
|
|
void fuse_wait_aborted(struct fuse_conn *fc)
|
|
{
|
|
/* matches implicit memory barrier in fuse_drop_waiting() */
|
|
smp_mb();
|
|
wait_event(fc->blocked_waitq, atomic_read(&fc->num_waiting) == 0);
|
|
}
|
|
|
|
int fuse_dev_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct fuse_dev *fud = fuse_get_dev(file);
|
|
|
|
if (fud) {
|
|
struct fuse_conn *fc = fud->fc;
|
|
struct fuse_pqueue *fpq = &fud->pq;
|
|
LIST_HEAD(to_end);
|
|
unsigned int i;
|
|
|
|
spin_lock(&fpq->lock);
|
|
WARN_ON(!list_empty(&fpq->io));
|
|
for (i = 0; i < FUSE_PQ_HASH_SIZE; i++)
|
|
list_splice_init(&fpq->processing[i], &to_end);
|
|
spin_unlock(&fpq->lock);
|
|
|
|
end_requests(&to_end);
|
|
|
|
/* Are we the last open device? */
|
|
if (atomic_dec_and_test(&fc->dev_count)) {
|
|
WARN_ON(fc->iq.fasync != NULL);
|
|
fuse_abort_conn(fc);
|
|
}
|
|
fuse_dev_free(fud);
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_dev_release);
|
|
|
|
static int fuse_dev_fasync(int fd, struct file *file, int on)
|
|
{
|
|
struct fuse_dev *fud = fuse_get_dev(file);
|
|
|
|
if (!fud)
|
|
return -EPERM;
|
|
|
|
/* No locking - fasync_helper does its own locking */
|
|
return fasync_helper(fd, file, on, &fud->fc->iq.fasync);
|
|
}
|
|
|
|
static int fuse_device_clone(struct fuse_conn *fc, struct file *new)
|
|
{
|
|
struct fuse_dev *fud;
|
|
|
|
if (new->private_data)
|
|
return -EINVAL;
|
|
|
|
fud = fuse_dev_alloc_install(fc);
|
|
if (!fud)
|
|
return -ENOMEM;
|
|
|
|
new->private_data = fud;
|
|
atomic_inc(&fc->dev_count);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int res;
|
|
int oldfd;
|
|
struct fuse_dev *fud = NULL;
|
|
|
|
switch (cmd) {
|
|
case FUSE_DEV_IOC_CLONE:
|
|
res = -EFAULT;
|
|
if (!get_user(oldfd, (__u32 __user *)arg)) {
|
|
struct file *old = fget(oldfd);
|
|
|
|
res = -EINVAL;
|
|
if (old) {
|
|
/*
|
|
* Check against file->f_op because CUSE
|
|
* uses the same ioctl handler.
|
|
*/
|
|
if (old->f_op == file->f_op &&
|
|
old->f_cred->user_ns ==
|
|
file->f_cred->user_ns)
|
|
fud = fuse_get_dev(old);
|
|
|
|
if (fud) {
|
|
mutex_lock(&fuse_mutex);
|
|
res = fuse_device_clone(fud->fc, file);
|
|
mutex_unlock(&fuse_mutex);
|
|
}
|
|
fput(old);
|
|
}
|
|
}
|
|
break;
|
|
case FUSE_DEV_IOC_PASSTHROUGH_OPEN:
|
|
res = -EFAULT;
|
|
if (!get_user(oldfd, (__u32 __user *)arg)) {
|
|
res = -EINVAL;
|
|
fud = fuse_get_dev(file);
|
|
if (fud)
|
|
res = fuse_passthrough_open(fud, oldfd);
|
|
}
|
|
break;
|
|
default:
|
|
res = -ENOTTY;
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
const struct file_operations fuse_dev_operations = {
|
|
.owner = THIS_MODULE,
|
|
.open = fuse_dev_open,
|
|
.llseek = no_llseek,
|
|
.read_iter = fuse_dev_read,
|
|
.splice_read = fuse_dev_splice_read,
|
|
.write_iter = fuse_dev_write,
|
|
.splice_write = fuse_dev_splice_write,
|
|
.poll = fuse_dev_poll,
|
|
.release = fuse_dev_release,
|
|
.fasync = fuse_dev_fasync,
|
|
.unlocked_ioctl = fuse_dev_ioctl,
|
|
.compat_ioctl = compat_ptr_ioctl,
|
|
};
|
|
EXPORT_SYMBOL_GPL(fuse_dev_operations);
|
|
|
|
static struct miscdevice fuse_miscdevice = {
|
|
.minor = FUSE_MINOR,
|
|
.name = "fuse",
|
|
.fops = &fuse_dev_operations,
|
|
};
|
|
|
|
int __init fuse_dev_init(void)
|
|
{
|
|
int err = -ENOMEM;
|
|
fuse_req_cachep = kmem_cache_create("fuse_request",
|
|
sizeof(struct fuse_req),
|
|
0, 0, NULL);
|
|
if (!fuse_req_cachep)
|
|
goto out;
|
|
|
|
err = misc_register(&fuse_miscdevice);
|
|
if (err)
|
|
goto out_cache_clean;
|
|
|
|
return 0;
|
|
|
|
out_cache_clean:
|
|
kmem_cache_destroy(fuse_req_cachep);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
void fuse_dev_cleanup(void)
|
|
{
|
|
misc_deregister(&fuse_miscdevice);
|
|
kmem_cache_destroy(fuse_req_cachep);
|
|
}
|