android_kernel_xiaomi_sm8450/fs/fuse/dev.c
Greg Kroah-Hartman b9d4c135c7 Merge tag 'android12-5.10.226_r00' into android12-5.10
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 "Merge 751777a79a ("nfsd: make svc_stat per-network namespace instead of global") into android12-5.10-lts"
* | c3e9a280ba Merge 751777a79a ("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 Merge ddee5b4b6a ("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>
2024-10-28 18:51:06 +00:00

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);
}