Merge 4.19.294 into android-4.19-stable
Linux 4.19.294
Revert "ARM: ep93xx: fix missing-prototype warnings"
Revert "MIPS: Alchemy: fix dbdma2"
Merge 4.19.293 into android-4.19-stable
Linux 4.19.293
dma-buf/sw_sync: Avoid recursive lock during fence signal
* clk: Fix undefined reference to `clk_rate_exclusive_{get,put}'
include/linux/clk.h
* scsi: core: raid_class: Remove raid_component_add()
include/linux/raid_class.h
scsi: snic: Fix double free in snic_tgt_create()
irqchip/mips-gic: Don't touch vl_map if a local interrupt is not routable
* rtnetlink: Reject negative ifindexes in RTM_NEWLINK
net/core/rtnetlink.c
* netfilter: nf_queue: fix socket leak
net/netfilter/nf_queue.c
* sched/rt: pick_next_rt_entity(): check list_entry
kernel/sched/rt.c
* mmc: block: Fix in_flight[issue_type] value error
drivers/mmc/core/block.c
x86/fpu: Set X86_FEATURE_OSXSAVE feature after enabling OSXSAVE in CR4
PCI: acpiphp: Use pci_assign_unassigned_bridge_resources() only for non-root bus
media: vcodec: Fix potential array out-of-bounds in encoder queue_setup
* lib/clz_ctz.c: Fix __clzdi2() and __ctzdi2() for 32-bit kernels
lib/clz_ctz.c
batman-adv: Fix batadv_v_ogm_aggr_send memory leak
batman-adv: Fix TT global entry leak when client roamed back
batman-adv: Do not get eth header before batadv_check_management_packet
batman-adv: Don't increase MTU when set by user
batman-adv: Trigger events for auto adjusted MTU
nfsd: Fix race to FREE_STATEID and cl_revoked
ibmveth: Use dcbf rather than dcbfl
ipvs: fix racy memcpy in proc_do_sync_threshold
ipvs: Improve robustness to the ipvs sysctl
* bonding: fix macvlan over alb bond support
drivers/net/bonding/bond_alb.c
include/net/bonding.h
* net: remove bond_slave_has_mac_rcu()
include/net/bonding.h
* net/sched: fix a qdisc modification with ambiguous command request
net/sched/sch_api.c
igb: Avoid starting unnecessary workqueues
dccp: annotate data-races in dccp_poll()
* sock: annotate data-races around prot->memory_pressure
include/net/sock.h
net/sctp/socket.c
* tracing: Fix memleak due to race between current_tracer and trace
kernel/trace/trace.c
drm/amd/display: check TG is non-null before checking if enabled
drm/amd/display: do not wait for mpc idle if tg is disabled
* regmap: Account for register length in SMBus I/O limits
drivers/base/regmap/regmap-i2c.c
dm integrity: reduce vmalloc space footprint on 32-bit architectures
dm integrity: increase RECALC_SECTORS to improve recalculate speed
powerpc: Fail build if using recordmcount with binutils v2.37
powerpc: remove leftover code of old GCC version checks
powerpc/32: add stack protector support
fbdev: fix potential OOB read in fast_imageblit()
fbdev: Fix sys_imageblit() for arbitrary image widths
fbdev: Improve performance of sys_imageblit()
tty: serial: fsl_lpuart: add earlycon for imx8ulp platform
Revert "tty: serial: fsl_lpuart: drop earlycon entry for i.MX8QXP"
MIPS: cpu-features: Use boot_cpu_type for CPU type based features
MIPS: cpu-features: Enable octeon_cache by cpu_type
fs: dlm: fix mismatch of plock results from userspace
fs: dlm: use dlm_plock_info for do_unlock_close
fs: dlm: change plock interrupted message to debug again
fs: dlm: add pid to debug log
dlm: replace usage of found with dedicated list iterator variable
dlm: improve plock logging if interrupted
PCI: acpiphp: Reassign resources on bridge if necessary
net: phy: broadcom: stub c45 read/write for 54810
* net: xfrm: Amend XFRMA_SEC_CTX nla_policy structure
net/xfrm/xfrm_user.c
* net: fix the RTO timer retransmitting skb every 1ms if linear option is enabled
net/ipv4/tcp_timer.c
virtio-net: set queues after driver_ok
* af_unix: Fix null-ptr-deref in unix_stream_sendpage().
net/unix/af_unix.c
* netfilter: set default timeout to 3 secs for sctp shutdown send and recv state
net/netfilter/nf_conntrack_proto_sctp.c
test_firmware: prevent race conditions by a correct implementation of locking
mmc: wbsd: fix double mmc_free_host() in wbsd_init()
cifs: Release folio lock on fscache read hit.
* ALSA: usb-audio: Add support for Mythware XA001AU capture and playback interfaces.
sound/usb/quirks-table.h
serial: 8250: Fix oops for port->pm on uart_change_pm()
ASoC: meson: axg-tdm-formatter: fix channel slot allocation
ASoC: rt5665: add missed regulator_bulk_disable
* net: do not allow gso_size to be set to GSO_BY_FRAGS
include/linux/virtio_net.h
* sock: Fix misuse of sk_under_memory_pressure()
include/net/sock.h
net/core/sock.c
i40e: fix misleading debug logs
team: Fix incorrect deletion of ETH_P_8021AD protocol vid from slaves
netfilter: nft_dynset: disallow object maps
selftests: mirror_gre_changes: Tighten up the TTL test match
* xfrm: add NULL check in xfrm_update_ae_params
net/xfrm/xfrm_user.c
* ip_vti: fix potential slab-use-after-free in decode_session6
net/ipv4/ip_vti.c
* ip6_vti: fix slab-use-after-free in decode_session6
net/ipv6/ip6_vti.c
* xfrm: fix slab-use-after-free in decode_session6
net/xfrm/xfrm_interface_core.c
* xfrm: interface: rename xfrm_interface.c to xfrm_interface_core.c
net/xfrm/Makefile
* net: af_key: fix sadb_x_filter validation
net/key/af_key.c
* net: xfrm: Fix xfrm_address_filter OOB read
net/xfrm/xfrm_user.c
btrfs: fix BUG_ON condition in btrfs_cancel_balance
powerpc/rtas_flash: allow user copy to flash block cache objects
fbdev: mmp: fix value check in mmphw_probe()
virtio-mmio: don't break lifecycle of vm_dev
virtio-mmio: Use to_virtio_mmio_device() to simply code
virtio-mmio: convert to devm_platform_ioremap_resource
nfsd: Remove incorrect check in nfsd4_validate_stateid
nfsd4: kill warnings on testing stateids with mismatched clientids
block: fix signed int overflow in Amiga partition support
mmc: sunxi: fix deferred probing
mmc: bcm2835: fix deferred probing
* mmc: Remove dev_err() usage after platform_get_irq()
drivers/mmc/host/sdhci-msm.c
mmc: tmio: move tmio_mmc_set_clock() to platform hook
mmc: tmio: replace tmio_mmc_clk_stop() calls with tmio_mmc_set_clock()
mmc: meson-gx: remove redundant mmc_request_done() call from irq context
mmc: meson-gx: remove useless lock
* USB: dwc3: qcom: fix NULL-deref on suspend
drivers/usb/dwc3/dwc3-qcom.c
* usb: dwc3: qcom: Add helper functions to enable,disable wake irqs
drivers/usb/dwc3/dwc3-qcom.c
irqchip/mips-gic: Use raw spinlock for gic_lock
irqchip/mips-gic: Get rid of the reliance on irq_cpu_online()
x86/topology: Fix erroneous smp_num_siblings on Intel Hybrid platforms
powerpc/64s/radix: Fix soft dirty tracking
powerpc: Move page table dump files in a dedicated subdirectory
powerpc/mm: dump block address translation on book3s/32
powerpc/mm: dump segment registers on book3s/32
powerpc/mm: Move pgtable_t into platform headers
powerpc/mm: move platform specific mmu-xxx.h in platform directories
iio: addac: stx104: Fix race condition when converting analog-to-digital
iio: addac: stx104: Fix race condition for stx104_write_raw()
iio: adc: stx104: Implement and utilize register structures
iio: adc: stx104: Utilize iomap interface
* iio: add addac subdirectory
drivers/iio/Kconfig
drivers/iio/Makefile
drivers/iio/addac/Kconfig
drivers/iio/addac/Makefile
* IMA: allow/fix UML builds
security/integrity/ima/Kconfig
drm/amdgpu: Fix potential fence use-after-free v2
* Bluetooth: L2CAP: Fix use-after-free
net/bluetooth/l2cap_core.c
pcmcia: rsrc_nonstatic: Fix memory leak in nonstatic_release_resource_db()
gfs2: Fix possible data races in gfs2_show_options()
media: platform: mediatek: vpu: fix NULL ptr dereference
* media: v4l2-mem2mem: add lock to protect parameter num_rdy
include/media/v4l2-mem2mem.h
FS: JFS: Check for read-only mounted filesystem in txBegin
FS: JFS: Fix null-ptr-deref Read in txBegin
MIPS: dec: prom: Address -Warray-bounds warning
fs: jfs: Fix UBSAN: array-index-out-of-bounds in dbAllocDmapLev
udf: Fix uninitialized array access for some pathnames
* HID: add quirk for 03f0:464a HP Elite Presenter Mouse
drivers/hid/hid-ids.h
drivers/hid/hid-quirks.c
* quota: fix warning in dqgrab()
fs/quota/dquot.c
* quota: Properly disable quotas when add_dquot_ref() fails
fs/quota/dquot.c
ALSA: emu10k1: roll up loops in DSP setup code for Audigy
drm/radeon: Fix integer overflow in radeon_cs_parser_init
selftests: forwarding: tc_flower: Relax success criterion
* lib/mpi: Eliminate unused umul_ppmm definitions for MIPS
lib/mpi/longlong.h
Merge 4.19.292 into android-4.19-stable
* Revert "posix-timers: Ensure timer ID search-loop limit is valid"
include/linux/sched/signal.h
kernel/time/posix-timers.c
Merge 4.19.291 into android-4.19-stable
Merge 4.19.290 into android-4.19-stable
UPSTREAM: media: usb: siano: Fix warning due to null work_func_t function pointer
* UPSTREAM: Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_ready_cb
net/bluetooth/l2cap_sock.c
UPSTREAM: net/sched: cls_route: No longer copy tcf_result on update to avoid use-after-free
* UPSTREAM: net/sched: cls_u32: No longer copy tcf_result on update to avoid use-after-free
net/sched/cls_u32.c
Linux 4.19.292
* sch_netem: fix issues in netem_change() vs get_dist_table()
net/sched/sch_netem.c
alpha: remove __init annotation from exported page_is_ram()
scsi: core: Fix possible memory leak if device_add() fails
scsi: snic: Fix possible memory leak if device_add() fails
scsi: 53c700: Check that command slot is not NULL
scsi: storvsc: Fix handling of virtual Fibre Channel timeouts
* scsi: core: Fix legacy /proc parsing buffer overflow
drivers/scsi/scsi_proc.c
* netfilter: nf_tables: report use refcount overflow
include/net/netfilter/nf_tables.h
* netfilter: nf_tables: bogus EBUSY when deleting flowtable after flush
include/net/netfilter/nf_tables.h
btrfs: don't stop integrity writeback too early
ibmvnic: Handle DMA unmapping of login buffs in release functions
* wifi: cfg80211: fix sband iftype data lookup for AP_VLAN
include/net/cfg80211.h
IB/hfi1: Fix possible panic during hotplug remove
* drivers: net: prevent tun_build_skb() to exceed the packet size limit
drivers/net/tun.c
dccp: fix data-race around dp->dccps_mss_cache
* bonding: Fix incorrect deletion of ETH_P_8021AD protocol vid from slaves
drivers/net/bonding/bond_main.c
* net/packet: annotate data-races around tp->status
net/packet/af_packet.c
mISDN: Update parameter type of dsp_cmx_send()
drm/nouveau/disp: Revert a NULL check inside nouveau_connector_get_modes
x86: Move gds_ucode_mitigated() declaration to header
x86/mm: Fix VDSO and VVAR placement on 5-level paging machines
x86/cpu/amd: Enable Zenbleed fix for AMD Custom APU 0405
* usb: dwc3: Properly handle processing of pending events
drivers/usb/dwc3/gadget.c
usb-storage: alauda: Fix uninit-value in alauda_check_media()
* binder: fix memory leak in binder_init()
drivers/android/binder.c
drivers/android/binder_alloc.c
drivers/android/binder_alloc.h
iio: cros_ec: Fix the allocation size for cros_ec_command
nilfs2: fix use-after-free of nilfs_root in dirtying inodes via iput
radix tree test suite: fix incorrect allocation size for pthreads
drm/nouveau/gr: enable memory loads on helper invocation on all channels
dmaengine: pl330: Return DMA_PAUSED when transaction is paused
* ipv6: adjust ndisc_is_useropt() to also return true for PIO
net/ipv6/ndisc.c
mmc: moxart: read scr register without changing byte order
sparc: fix up arch_cpu_finalize_init() build breakage.
* UPSTREAM: net/sched: cls_fw: Fix improper refcount update leads to use-after-free
net/sched/cls_fw.c
Linux 4.19.291
* drm/edid: fix objtool warning in drm_cvt_modes()
drivers/gpu/drm/drm_edid.c
arm64: dts: stratix10: fix incorrect I2C property for SCL signal
* drivers core: Use sysfs_emit and sysfs_emit_at for show(device *...) functions
drivers/base/arch_topology.c
drivers/base/cacheinfo.c
drivers/base/core.c
drivers/base/cpu.c
drivers/base/firmware_loader/fallback.c
drivers/base/platform.c
drivers/base/power/sysfs.c
drivers/base/soc.c
ARM: dts: nxp/imx6sll: fix wrong property name in usbphy node
ARM: dts: imx6sll: fixup of operating points
ARM: dts: imx: add usb alias
ARM: dts: imx6sll: Make ssi node name same as other platforms
* PM: sleep: wakeirq: fix wake irq arming
drivers/base/power/power.h
drivers/base/power/wakeirq.c
* PM / wakeirq: support enabling wake-up irq after runtime_suspend called
drivers/base/power/power.h
drivers/base/power/runtime.c
drivers/base/power/wakeirq.c
include/linux/pm_wakeirq.h
powerpc/mm/altmap: Fix altmap boundary check
mtd: rawnand: omap_elm: Fix incorrect type in assignment
test_firmware: return ENOMEM instead of ENOSPC on failed memory allocation
test_firmware: fix a memory leak with reqs buffer
ext2: Drop fragment support
* net: usbnet: Fix WARNING in usbnet_start_xmit/usb_submit_urb
drivers/net/usb/usbnet.c
* Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_ready_cb
net/bluetooth/l2cap_sock.c
fs/sysv: Null check to prevent null-ptr-deref bug
* USB: zaurus: Add ID for A-300/B-500/C-700
drivers/net/usb/cdc_ether.c
drivers/net/usb/zaurus.c
libceph: fix potential hang in ceph_osdc_notify()
scsi: zfcp: Defer fc_rport blocking until after ADISC response
* tcp_metrics: fix data-race in tcpm_suck_dst() vs fastopen
net/ipv4/tcp_metrics.c
* tcp_metrics: annotate data-races around tm->tcpm_net
net/ipv4/tcp_metrics.c
* tcp_metrics: annotate data-races around tm->tcpm_vals[]
net/ipv4/tcp_metrics.c
* tcp_metrics: annotate data-races around tm->tcpm_lock
net/ipv4/tcp_metrics.c
* tcp_metrics: annotate data-races around tm->tcpm_stamp
net/ipv4/tcp_metrics.c
* tcp_metrics: fix addr_same() helper
net/ipv4/tcp_metrics.c
ip6mr: Fix skb_under_panic in ip6mr_cache_report()
net/sched: cls_route: No longer copy tcf_result on update to avoid use-after-free
* net/sched: cls_u32: No longer copy tcf_result on update to avoid use-after-free
net/sched/cls_u32.c
* net: add missing data-race annotation for sk_ll_usec
net/core/sock.c
* net: add missing data-race annotations around sk->sk_peek_off
net/core/sock.c
net/unix/af_unix.c
* net: sched: cls_u32: Fix match key mis-addressing
net/sched/cls_u32.c
perf test uprobe_from_different_cu: Skip if there is no gcc
net/mlx5e: fix return value check in mlx5e_ipsec_remove_trailer()
KVM: s390: fix sthyi error handling
* word-at-a-time: use the same return type for has_zero regardless of endianness
include/asm-generic/word-at-a-time.h
* loop: Select I/O scheduler 'none' from inside add_disk()
drivers/block/loop.c
* perf: Fix function pointer case
kernel/events/core.c
* net/sched: cls_u32: Fix reference counter leak leading to overflow
net/sched/cls_u32.c
ASoC: cs42l51: fix driver to properly autoload with automatic module loading
net/sched: sch_qfq: account for stab overhead in qfq_enqueue
* net/sched: cls_fw: Fix improper refcount update leads to use-after-free
net/sched/cls_fw.c
drm/client: Fix memory leak in drm_client_target_cloned
dm cache policy smq: ensure IO doesn't prevent cleaner policy progress
ASoC: wm8904: Fill the cache for WM8904_ADC_TEST_0 register
s390/dasd: fix hanging device after quiesce/resume
virtio-net: fix race between set queues and probe
serial: 8250_dw: Preserve original value of DLF register
* serial: 8250_dw: split Synopsys DesignWare 8250 common functions
drivers/tty/serial/8250/Kconfig
irq-bcm6345-l1: Do not assume a fixed block to cpu mapping
tpm_tis: Explicitly check for error code
btrfs: check for commit error at btrfs_attach_transaction_barrier()
hwmon: (nct7802) Fix for temp6 (PECI1) processed even if PECI1 disabled
staging: ks7010: potential buffer overflow in ks_wlan_set_encode_ext()
Documentation: security-bugs.rst: clarify CVE handling
Documentation: security-bugs.rst: update preferences when dealing with the linux-distros group
usb: xhci-mtk: set the dma max_seg_size
* USB: quirks: add quirk for Focusrite Scarlett
drivers/usb/core/quirks.c
usb: ohci-at91: Fix the unhandle interrupt when resume
* usb: dwc3: don't reset device side if dwc3 was configured as host-only
drivers/usb/dwc3/core.c
usb: dwc3: pci: skip BYT GPIO lookup table for hardwired phy
* Revert "usb: dwc3: core: Enable AutoRetry feature in the controller"
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
can: gs_usb: gs_can_close(): add missing set of CAN state to CAN_STATE_STOPPED
USB: serial: simple: sort driver entries
USB: serial: simple: add Kaufmann RKS+CAN VCP
USB: serial: option: add Quectel EC200A module support
USB: serial: option: support Quectel EM060K_128
* tracing: Fix warning in trace_buffered_event_disable()
kernel/trace/trace_events.c
* ring-buffer: Fix wrong stat of cpu_buffer->read
kernel/trace/ring_buffer.c
ata: pata_ns87415: mark ns87560_tf_read static
dm raid: fix missing reconfig_mutex unlock in raid_ctr() error paths
* block: Fix a source code comment in include/uapi/linux/blkzoned.h
include/uapi/linux/blkzoned.h
ASoC: fsl_spdif: Silence output on stop
drm/msm: Fix IS_ERR_OR_NULL() vs NULL check in a5xx_submit_in_rb()
RDMA/mlx4: Make check for invalid flags stricter
benet: fix return value check in be_lancer_xmit_workarounds()
net/sched: mqprio: Add length check for TCA_MQPRIO_{MAX/MIN}_RATE64
net/sched: mqprio: add extack to mqprio_parse_nlattr()
net/sched: mqprio: refactor nlattr parsing to a separate function
platform/x86: msi-laptop: Fix rfkill out-of-sync on MSI Wind U100
team: reset team's flags when down link is P2P device
* bonding: reset bond's flags when down link is P2P device
drivers/net/bonding/bond_main.c
* tcp: Reduce chance of collisions in inet6_hashfn().
include/net/ipv6.h
* ipv6 addrconf: fix bug where deleting a mngtmpaddr can create a new temporary address
net/ipv6/addrconf.c
ethernet: atheros: fix return value check in atl1e_tso_csum()
phy: hisilicon: Fix an out of bounds check in hisi_inno_phy_probe()
i40e: Fix an NULL vs IS_ERR() bug for debugfs_create_dir()
* ext4: fix to check return value of freeze_bdev() in ext4_shutdown()
fs/ext4/ioctl.c
scsi: qla2xxx: Array index may go out of bound
scsi: qla2xxx: Fix inconsistent format argument type in qla_os.c
ftrace: Fix possible warning on checking all pages used in ftrace_process_locs()
ftrace: Store the order of pages allocated in ftrace_page
ftrace: Check if pages were allocated before calling free_pages()
* ftrace: Add information on number of page groups allocated
kernel/trace/trace.c
kernel/trace/trace.h
fs: dlm: interrupt posix locks only when process is killed
dlm: rearrange async condition return
dlm: cleanup plock_op vs plock_xop
PCI/ASPM: Avoid link retraining race
PCI/ASPM: Factor out pcie_wait_for_retrain()
PCI/ASPM: Return 0 or -ETIMEDOUT from pcie_retrain_link()
PCI: Rework pcie_retrain_link() wait loop
* ext4: Fix reusing stale buffer heads from last failed mounting
fs/ext4/super.c
* ext4: rename journal_dev to s_journal_dev inside ext4_sb_info
fs/ext4/ext4.h
fs/ext4/fsmap.c
fs/ext4/super.c
btrfs: fix extent buffer leak after tree mod log failure at split_node()
bcache: Fix __bch_btree_node_alloc to make the failure behavior consistent
bcache: remove 'int n' from parameter list of bch_bucket_alloc_set()
bcache: use MAX_CACHES_PER_SET instead of magic number 8 in __bch_bucket_alloc_set
gpio: tps68470: Make tps68470_gpio_output() always set the initial value
tracing/histograms: Return an error if we fail to add histogram to hist_vars list
* tcp: annotate data-races around fastopenq.max_qlen
include/linux/tcp.h
net/ipv4/tcp.c
net/ipv4/tcp_fastopen.c
* tcp: annotate data-races around tp->notsent_lowat
include/net/tcp.h
net/ipv4/tcp.c
* tcp: annotate data-races around rskq_defer_accept
net/ipv4/tcp.c
* tcp: annotate data-races around tp->linger2
net/ipv4/tcp.c
* net: Replace the limit of TCP_LINGER2 with TCP_FIN_TIMEOUT_MAX
include/net/tcp.h
net/ipv4/tcp.c
netfilter: nf_tables: can't schedule in nft_chain_validate
netfilter: nf_tables: fix spurious set element insertion failure
* llc: Don't drop packet from non-root netns.
net/llc/llc_input.c
fbdev: au1200fb: Fix missing IRQ check in au1200fb_drv_probe
* Revert "tcp: avoid the lookup process failing to get sk in ehash table"
net/ipv4/inet_hashtables.c
net/ipv4/inet_timewait_sock.c
net:ipv6: check return value of pskb_trim()
net: ethernet: ti: cpsw_ale: Fix cpsw_ale_get_field()/cpsw_ale_set_field()
pinctrl: amd: Use amd_pinconf_set() for all config options
fbdev: imxfb: warn about invalid left/right margin
spi: bcm63xx: fix max prepend length
igb: Fix igb_down hung on surprise removal
wifi: iwlwifi: mvm: avoid baid size integer overflow
* wifi: wext-core: Fix -Wstringop-overflow warning in ioctl_standard_iw_point()
net/wireless/wext-core.c
* bpf: Address KCSAN report on bpf_lru_list
kernel/bpf/bpf_lru_list.c
kernel/bpf/bpf_lru_list.h
* sched/fair: Don't balance task to its current running CPU
kernel/sched/fair.c
* posix-timers: Ensure timer ID search-loop limit is valid
include/linux/sched/signal.h
kernel/time/posix-timers.c
md/raid10: prevent soft lockup while flush writes
md: fix data corruption for raid456 when reshape restart while grow up
nbd: Add the maximum limit of allocated index in nbd_dev_add
debugobjects: Recheck debug_objects_enabled before reporting
* ext4: correct inline offset when handling xattrs in inode body
fs/ext4/xattr.c
can: bcm: Fix UAF in bcm_proc_show()
* fuse: revalidate: don't invalidate if interrupted
fs/fuse/dir.c
perf probe: Add test for regression introduced by switch to die_get_decl_file()
tracing/histograms: Add histograms to hist_vars if they have referenced variables
* drm/atomic: Fix potential use-after-free in nonblocking commits
drivers/gpu/drm/drm_atomic.c
scsi: qla2xxx: Pointer may be dereferenced
scsi: qla2xxx: Check valid rport returned by fc_bsg_to_rport()
scsi: qla2xxx: Fix potential NULL pointer dereference
scsi: qla2xxx: Wait for io return on terminate rport
xtensa: ISS: fix call to split_if_spec
* ring-buffer: Fix deadloop issue on reading trace_pipe
kernel/trace/ring_buffer.c
tty: serial: samsung_tty: Fix a memory leak in s3c24xx_serial_getclk() when iterating clk
tty: serial: samsung_tty: Fix a memory leak in s3c24xx_serial_getclk() in case of error
* Revert "8250: add support for ASIX devices with a FIFO bug"
include/linux/serial_8250.h
meson saradc: fix clock divider mask length
ceph: don't let check_caps skip sending responses for revoke msgs
hwrng: imx-rngc - fix the timeout for init and self check
serial: atmel: don't enable IRQs prematurely
fs: dlm: return positive pid value for F_GETLK
md/raid0: add discard support for the 'original' layout
misc: pci_endpoint_test: Re-init completion for every test
misc: pci_endpoint_test: Free IRQs before removing the device
PCI: rockchip: Use u32 variable to access 32-bit registers
PCI: rockchip: Fix legacy IRQ generation for RK3399 PCIe endpoint core
PCI: rockchip: Add poll and timeout to wait for PHY PLLs to be locked
PCI: rockchip: Write PCI Device ID to correct register
PCI: rockchip: Assert PCI Configuration Enable bit after probe
PCI: qcom: Disable write access to read only registers for IP v2.3.3
* PCI: Add function 1 DMA alias quirk for Marvell 88SE9235
drivers/pci/quirks.c
* PCI/PM: Avoid putting EloPOS E2/S2/H2 PCIe Ports in D3cold
drivers/pci/pci.c
jfs: jfs_dmap: Validate db_l2nbperpage while mounting
* ext4: only update i_reserved_data_blocks on successful block allocation
fs/ext4/indirect.c
fs/ext4/inode.c
* ext4: fix wrong unit use in ext4_mb_clear_bb
fs/ext4/mballoc.c
perf intel-pt: Fix CYC timestamps after standalone CBR
SUNRPC: Fix UAF in svc_tcp_listen_data_ready()
net: bcmgenet: Ensure MDIO unregistration has clocks enabled
tpm: tpm_vtpm_proxy: fix a race condition in /dev/vtpmx creation
pinctrl: amd: Only use special debounce behavior for GPIO 0
pinctrl: amd: Detect internal GPIO0 debounce handling
pinctrl: amd: Fix mistake in handling clearing pins at startup
* net/sched: make psched_mtu() RTNL-less safe
include/net/pkt_sched.h
wifi: airo: avoid uninitialized warning in airo_get_rate()
* ipv6/addrconf: fix a potential refcount underflow for idev
net/ipv6/addrconf.c
NTB: ntb_tool: Add check for devm_kcalloc
NTB: ntb_transport: fix possible memory leak while device_register() fails
ntb: intel: Fix error handling in intel_ntb_pci_driver_init()
NTB: amd: Fix error handling in amd_ntb_pci_driver_init()
ntb: idt: Fix error handling in idt_pci_driver_init()
* udp6: fix udp6_ehashfn() typo
net/ipv6/udp.c
* icmp6: Fix null-ptr-deref of ip6_null_entry->rt6i_idev in icmp6_dev().
net/ipv6/icmp.c
* vrf: Increment Icmp6InMsgs on the original netdev
include/net/addrconf.h
net/ipv6/icmp.c
net/ipv6/reassembly.c
net: mvneta: fix txq_map in case of txq_number==1
* workqueue: clean up WORK_* constant types, clarify masking
include/linux/workqueue.h
kernel/workqueue.c
net: lan743x: Don't sleep in atomic context
netfilter: nf_tables: prevent OOB access in nft_byteorder_eval
* netfilter: conntrack: Avoid nf_ct_helper_hash uses after free
net/netfilter/nf_conntrack_helper.c
netfilter: nf_tables: fix scheduling-while-atomic splat
netfilter: nf_tables: unbind non-anonymous set if rule construction fails
* netfilter: nf_tables: reject unbound anonymous set before commit phase
include/net/netfilter/nf_tables.h
* netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain
include/net/netfilter/nf_tables.h
netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE
* netfilter: nf_tables: use net_generic infra for transaction data
include/net/netfilter/nf_tables.h
include/net/netns/nftables.h
* netfilter: add helper function to set up the nfnetlink header and use it
include/linux/netfilter/nfnetlink.h
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue.c
netfilter: nftables: add helper function to set the base sequence number
netfilter: nf_tables: add rescheduling points during loop detection walks
netfilter: nf_tables: fix nat hook table deletion
spi: spi-fsl-spi: allow changing bits_per_word while CS is still active
spi: spi-fsl-spi: relax message sanity checking a little
spi: spi-fsl-spi: remove always-true conditional in fsl_spi_do_one_msg
ARM: orion5x: fix d2net gpio initialization
btrfs: fix race when deleting quota root from the dirty cow roots list
jffs2: reduce stack usage in jffs2_build_xattr_subsystem()
* integrity: Fix possible multiple allocation in integrity_inode_get()
security/integrity/iint.c
bcache: Remove unnecessary NULL point check in node allocations
mmc: core: disable TRIM on Micron MTFC4GACAJCN-1M
mmc: core: disable TRIM on Kingston EMMC04G-M627
NFSD: add encoding of op_recall flag for write delegation
* ALSA: jack: Fix mutex call in snd_jack_report()
sound/core/jack.c
i2c: xiic: Don't try to handle more interrupt events after error
i2c: xiic: Defer xiic_wakeup() and __xiic_start_xfer() in xiic_process()
sh: dma: Fix DMA channel offset calculation
net/sched: act_pedit: Add size check for TCA_PEDIT_PARMS_EX
* tcp: annotate data races in __tcp_oow_rate_limited()
net/ipv4/tcp_input.c
* net: bridge: keep ports without IFF_UNICAST_FLT in BR_PROMISC mode
net/bridge/br_if.c
powerpc: allow PPC_EARLY_DEBUG_CPM only when SERIAL_CPM=y
* f2fs: fix error path handling in truncate_dnode()
fs/f2fs/node.c
mailbox: ti-msgmgr: Fill non-message tx data fields with 0x0
spi: bcm-qspi: return error if neither hif_mspi nor mspi is available
Add MODULE_FIRMWARE() for FIRMWARE_TG357766.
* sctp: fix potential deadlock on &net->sctp.addr_wq_lock
net/sctp/socket.c
rtc: st-lpc: Release some resources in st_rtc_probe() in case of error
mfd: stmpe: Only disable the regulators if they are enabled
mfd: intel-lpss: Add missing check for platform_get_resource
KVM: s390: fix KVM_S390_GET_CMMA_BITS for GFNs in memslot holes
mfd: rt5033: Drop rt5033-battery sub-device
usb: phy: phy-tahvo: fix memory leak in tahvo_usb_probe()
* extcon: Fix kernel doc of property capability fields to avoid warnings
drivers/extcon/extcon.c
* extcon: Fix kernel doc of property fields to avoid warnings
drivers/extcon/extcon.c
media: usb: siano: Fix warning due to null work_func_t function pointer
* media: videodev2.h: Fix struct v4l2_input tuner index comment
include/uapi/linux/videodev2.h
media: usb: Check az6007_read() return value
sh: j2: Use ioremap() to translate device tree address into kernel memory
w1: fix loop in w1_fini()
* block: change all __u32 annotations to __be32 in affs_hardblocks.h
include/uapi/linux/affs_hardblocks.h
USB: serial: option: add LARA-R6 01B PIDs
ARC: define ASM_NL and __ALIGN(_STR) outside #ifdef __ASSEMBLY__ guard
ARCv2: entry: rewrite to enable use of double load/stores LDD/STD
ARCv2: entry: avoid a branch
ARCv2: entry: push out the Z flag unclobber from common EXCEPTION_PROLOGUE
ARCv2: entry: comments about hardware auto-save on taken interrupts
* modpost: fix section mismatch message for R_ARM_{PC24,CALL,JUMP24}
scripts/mod/modpost.c
* modpost: fix section mismatch message for R_ARM_ABS32
scripts/mod/modpost.c
crypto: nx - fix build warnings when DEBUG_FS is not enabled
hwrng: virtio - Fix race on data_avail and actual data
hwrng: virtio - always add a pending request
hwrng: virtio - don't waste entropy
hwrng: virtio - don't wait on cleanup
hwrng: virtio - add an internal buffer
pinctrl: at91-pio4: check return value of devm_kasprintf()
perf dwarf-aux: Fix off-by-one in die_get_varname()
pinctrl: cherryview: Return correct value if pin in push-pull mode
* PCI: Add pci_clear_master() stub for non-CONFIG_PCI
include/linux/pci.h
scsi: 3w-xxxx: Add error handling for initialization failure in tw_probe()
ALSA: ac97: Fix possible NULL dereference in snd_ac97_mixer
drm/radeon: fix possible division-by-zero errors
fbdev: omapfb: lcd_mipid: Fix an error handling path in mipid_spi_probe()
arm64: dts: renesas: ulcb-kf: Remove flow control for SCIF1
IB/hfi1: Fix sdma.h tx->num_descs off-by-one errors
* soc/fsl/qe: fix usb.c build errors
drivers/soc/fsl/qe/Kconfig
ASoC: es8316: Increment max value for ALC Capture Target Volume control
ARM: ep93xx: fix missing-prototype warnings
drm/panel: simple: fix active size for Ampire AM-480272H3TMQW-T01H
Input: adxl34x - do not hardcode interrupt trigger type
ARM: dts: BCM5301X: Drop "clock-names" from the SPI node
Input: drv260x - sleep between polling GO bit
radeon: avoid double free in ci_dpm_init()
* netlink: Add __sock_i_ino() for __netlink_diag_dump().
include/net/sock.h
net/core/sock.c
ipvlan: Fix return value of ipvlan_queue_xmit()
netfilter: nf_conntrack_sip: fix the ct_sip_parse_numerical_param() return value.
* lib/ts_bm: reset initial match offset for every block of text
lib/ts_bm.c
gtp: Fix use-after-free in __gtp_encap_destroy().
* netlink: do not hard code device address lenth in fdb dumps
net/core/rtnetlink.c
* netlink: fix potential deadlock in netlink_set_err()
net/netlink/af_netlink.c
wifi: ath9k: convert msecs to jiffies where needed
wifi: ath9k: Fix possible stall on ath9k_txq_list_has_key()
memstick r592: make memstick_debug_get_tpc_name() static
kexec: fix a memory leak in crash_shrink_memory()
watchdog/perf: more properly prevent false positives with turbo modes
* watchdog/perf: define dummy watchdog_update_hrtimer_threshold() on correct config
include/linux/nmi.h
wifi: rsi: Do not set MMC_PM_KEEP_POWER in shutdown
wifi: ath9k: don't allow to overwrite ENDPOINT0 attributes
wifi: ray_cs: Fix an error handling path in ray_probe()
wifi: ray_cs: Drop useless status variable in parse_addr()
wifi: ray_cs: Utilize strnlen() in parse_addr()
wifi: wl3501_cs: Fix an error handling path in wl3501_probe()
wl3501_cs: use eth_hw_addr_set()
* net: create netdev->dev_addr assignment helpers
include/linux/etherdevice.h
include/linux/netdevice.h
wl3501_cs: Fix misspelling and provide missing documentation
wl3501_cs: Remove unnecessary NULL check
wl3501_cs: Fix a bunch of formatting issues related to function docs
wifi: atmel: Fix an error handling path in atmel_probe()
wifi: orinoco: Fix an error handling path in orinoco_cs_probe()
wifi: orinoco: Fix an error handling path in spectrum_cs_probe()
nfc: llcp: fix possible use of uninitialized variable in nfc_llcp_send_connect()
* nfc: constify several pointers to u8, char and sk_buff
include/net/nfc/nfc.h
wifi: mwifiex: Fix the size of a memory allocation in mwifiex_ret_802_11_scan()
samples/bpf: Fix buffer overflow in tcp_basertt
wifi: ath9k: avoid referencing uninit memory in ath9k_wmi_ctrl_rx
wifi: ath9k: fix AR9003 mac hardware hang check register offset calculation
evm: Complete description of evm_inode_setattr()
ARM: 9303/1: kprobes: avoid missing-declaration warnings
* PM: domains: fix integer overflow issues in genpd_parse_state()
drivers/base/power/domain.c
clocksource/drivers/cadence-ttc: Fix memory leak in ttc_timer_probe
clocksource/drivers/cadence-ttc: Use ttc driver as platform driver
* clocksource/drivers: Unify the names to timer-* format
drivers/clocksource/Makefile
irqchip/jcore-aic: Fix missing allocation of IRQ descriptors
irqchip/jcore-aic: Kill use of irq_create_strict_mappings()
md/raid10: fix io loss while replacement replace rdev
md/raid10: fix wrong setting of max_corr_read_errors
md/raid10: fix overflow of md/safe_mode_delay
md/raid10: check slab-out-of-bounds in md_bitmap_get_counter
* treewide: Remove uninitialized_var() usage
drivers/clk/clk-gate.c
drivers/gpu/drm/drm_edid.c
drivers/md/dm-io.c
drivers/md/dm-ioctl.c
drivers/md/dm-snap-persistent.c
drivers/md/dm-table.c
fs/fat/dir.c
fs/fuse/control.c
fs/fuse/file.c
fs/overlayfs/copy_up.c
kernel/async.c
kernel/audit.c
kernel/events/core.c
kernel/events/uprobes.c
kernel/exit.c
kernel/futex.c
kernel/trace/ring_buffer.c
lib/radix-tree.c
mm/memcontrol.c
mm/percpu.c
mm/slub.c
mm/swap.c
net/ipv4/netfilter/nf_socket_ipv4.c
net/ipv6/ip6_flowlabel.c
net/ipv6/netfilter/nf_socket_ipv6.c
net/netfilter/nf_conntrack_ftp.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue.c
net/sched/cls_flow.c
sound/core/control_compat.c
sound/usb/endpoint.c
drm/amdgpu: Validate VM ioctl flags.
scripts/tags.sh: Resolve gtags empty index generation
* drm/edid: Fix uninitialized variable in drm_cvt_modes()
drivers/gpu/drm/drm_edid.c
fbdev: imsttfb: Fix use after free bug in imsttfb_probe
video: imsttfb: check for ioremap() failures
x86/smp: Use dedicated cache-line for mwait_play_dead()
gfs2: Don't deref jdesc in evict
Linux 4.19.290
x86: fix backwards merge of GDS/SRSO bit
xen/netback: Fix buffer overrun triggered by unusual packet
Documentation/x86: Fix backwards on/off logic about YMM support
x86/xen: Fix secondary processors' FPU initialization
KVM: Add GDS_NO support to KVM
x86/speculation: Add Kconfig option for GDS
x86/speculation: Add force option to GDS mitigation
* x86/speculation: Add Gather Data Sampling mitigation
drivers/base/cpu.c
x86/fpu: Move FPU initialization into arch_cpu_finalize_init()
x86/fpu: Mark init functions __init
x86/fpu: Remove cpuinfo argument from init functions
* init, x86: Move mem_encrypt_init() into arch_cpu_finalize_init()
init/main.c
* init: Invoke arch_cpu_finalize_init() earlier
init/main.c
* init: Remove check_bugs() leftovers
init/main.c
um/cpu: Switch to arch_cpu_finalize_init()
sparc/cpu: Switch to arch_cpu_finalize_init()
sh/cpu: Switch to arch_cpu_finalize_init()
mips/cpu: Switch to arch_cpu_finalize_init()
m68k/cpu: Switch to arch_cpu_finalize_init()
ia64/cpu: Switch to arch_cpu_finalize_init()
ARM: cpu: Switch to arch_cpu_finalize_init()
x86/cpu: Switch to arch_cpu_finalize_init()
* init: Provide arch_cpu_finalize_init()
arch/Kconfig
include/linux/cpu.h
init/main.c
Merge 4.19.289 into android-4.19-stable
Linux 4.19.289
x86/cpu/amd: Add a Zenbleed fix
x86/cpu/amd: Move the errata checking functionality up
x86/microcode/AMD: Load late on both threads too
Merge 4.19.288 into android-4.19-stable
Linux 4.19.288
i2c: imx-lpi2c: fix type char overflow issue when calculating the clock cycle
x86/apic: Fix kernel panic when booting with intremap=off and x2apic_phys
drm/radeon: fix race condition UAF in radeon_gem_set_domain_ioctl
drm/exynos: fix race condition UAF in exynos_g2d_exec_ioctl
drm/exynos: vidi: fix a wrong error return
ASoC: nau8824: Add quirk to active-high jack-detect
s390/cio: unregister device when the only path is gone
usb: gadget: udc: fix NULL dereference in remove()
nfcsim.c: Fix error checking for debugfs_create_dir
media: cec: core: don't set last_initiator if tx in progress
* arm64: Add missing Set/Way CMO encodings
arch/arm64/include/asm/sysreg.h
* HID: wacom: Add error check to wacom_parse_and_register()
drivers/hid/wacom_sys.c
scsi: target: iscsi: Prevent login threads from racing between each other
* sch_netem: acquire qdisc lock in netem_change()
net/sched/sch_netem.c
netfilter: nfnetlink_osf: fix module autoload
netfilter: nf_tables: disallow element updates of bound anonymous sets
be2net: Extend xmit workaround to BE3 chip
mmc: usdhi60rol0: fix deferred probing
mmc: sdhci-acpi: fix deferred probing
mmc: omap_hsmmc: fix deferred probing
mmc: omap: fix deferred probing
mmc: mvsdio: fix deferred probing
mmc: mvsdio: convert to devm_platform_ioremap_resource
mmc: mtk-sd: fix deferred probing
net: qca_spi: Avoid high load if QCA7000 is not available
xfrm: Linearize the skb after offloading if needed.
ieee802154: hwsim: Fix possible memory leaks
* rcu: Upgrade rcu_swap_protected() to rcu_replace_pointer()
include/linux/rcupdate.h
nilfs2: prevent general protection fault in nilfs_clear_dirty_page()
* cgroup: Do not corrupt task iteration when rebinding subsystem
kernel/cgroup/cgroup.c
PCI: hv: Fix a race condition bug in hv_pci_query_relations()
Drivers: hv: vmbus: Fix vmbus_wait_for_unload() to scan present CPUs
nilfs2: fix buffer corruption due to concurrent device reads
ipmi: move message error checking to avoid deadlock
* ipmi: Make the smi watcher be disabled immediately when not needed
include/linux/ipmi_smi.h
x86/purgatory: remove PGO flags
nilfs2: reject devices with insufficient block count
serial: lantiq: add missing interrupt ack
serial: lantiq: Do not swap register read/writes
serial: lantiq: Use readl/writel instead of ltq_r32/ltq_w32
serial: lantiq: Change ltq_w32_mask to asc_update_bits
Merge 4.19.287 into android-4.19-stable
Linux 4.19.287
* mmc: block: ensure error propagation for non-blk
drivers/mmc/core/block.c
powerpc: Fix defconfig choice logic when cross compiling
drm/nouveau/kms: Fix NULL pointer dereference in nouveau_connector_detect_depth
* neighbour: delete neigh_lookup_nodev as not used
include/net/neighbour.h
net/core/neighbour.c
* net: Remove unused inline function dst_hold_and_use()
include/net/dst.h
* neighbour: Remove unused inline function neigh_key_eq16()
include/net/neighbour.h
selftests/ptp: Fix timestamp printf format for PTP_SYS_OFFSET
* net: tipc: resize nlattr array to correct size
net/tipc/bearer.c
net: lapbether: only support ethernet devices
drm/nouveau: add nv_encoder pointer check for NULL
drm/nouveau/kms: Don't change EDID when it hasn't actually changed
drm/nouveau/dp: check for NULL nv_connector->native_mode
igb: fix nvm.ops.read() error handling
* sctp: fix an error code in sctp_sf_eat_auth()
net/sctp/sm_statefuns.c
IB/isert: Fix incorrect release of isert connection
IB/isert: Fix possible list corruption in CMA handler
IB/isert: Fix dead lock in ib_isert
IB/uverbs: Fix to consider event queue closing also upon non-blocking mode
RDMA/rxe: Fix the use-before-initialization error of resp_pkts
RDMA/rxe: Removed unused name from rxe_task struct
RDMA/rxe: Remove the unused variable obj
* ping6: Fix send to link-local addresses with VRF.
net/ipv6/ping.c
* netfilter: nfnetlink: skip error delivery on batch in case of ENOMEM
net/netfilter/nfnetlink.c
* usb: gadget: f_ncm: Fix NTP-32 support
drivers/usb/gadget/function/f_ncm.c
* usb: gadget: f_ncm: Add OS descriptor support
drivers/usb/gadget/function/f_ncm.c
drivers/usb/gadget/function/u_ncm.h
* usb: dwc3: gadget: Reset num TRBs before giving back the request
drivers/usb/dwc3/gadget.c
USB: serial: option: add Quectel EM061KGL series
* Remove DECnet support from kernel
include/linux/netdevice.h
include/linux/netfilter.h
include/linux/netfilter_defs.h
include/net/netns/netfilter.h
include/uapi/linux/netlink.h
net/Kconfig
net/Makefile
net/core/dev.c
net/core/neighbour.c
net/netfilter/core.c
net: usb: qmi_wwan: add support for Compal RXM-G1
RDMA/uverbs: Restrict usage of privileged QKEYs
nouveau: fix client work fence deletion race
powerpc/purgatory: remove PGO flags
kexec: support purgatories with .text.hot sections
nilfs2: fix possible out-of-bounds segment allocation in resize ioctl
nilfs2: fix incomplete buffer cleanup in nilfs_btnode_abort_change_key()
nios2: dts: Fix tse_mac "max-frame-size" property
ocfs2: check new file size on fallocate call
ocfs2: fix use-after-free when unmounting read-only filesystem
xen/blkfront: Only check REQ_FUA for writes
mips: Move initrd_start check after initrd address sanitisation.
MIPS: Alchemy: fix dbdma2
parisc: Improve cache flushing for PCXL in arch_sync_dma_for_cpu()
* power: supply: Fix logic checking if system is running from battery
drivers/power/supply/power_supply_core.c
irqchip/meson-gpio: Mark OF related data as maybe unused
* regulator: Fix error checking for debugfs_create_dir
drivers/regulator/core.c
* power: supply: Ratelimit no data debug output
drivers/power/supply/power_supply_sysfs.c
ARM: dts: vexpress: add missing cache properties
power: supply: bq27xxx: Use mod_delayed_work() instead of cancel() + schedule()
power: supply: ab8500: Fix external_power_changed race
Merge "Merge 4.19.286 into android-4.19-stable" into android-4.19-stable
* Revert "tcp: deny tcp_disconnect() when threads are waiting"
include/net/sock.h
net/ipv4/af_inet.c
net/ipv4/inet_connection_sock.c
net/ipv4/tcp.c
Merge "Merge 4.19.285 into android-4.19-stable" into android-4.19-stable
Merge 4.19.286 into android-4.19-stable
* Revert "tcp: deny tcp_disconnect() when threads are waiting"
include/net/sock.h
net/ipv4/af_inet.c
net/ipv4/inet_connection_sock.c
net/ipv4/tcp.c
* ANDROID: GKI: update ABI xml for incrementalfs.ko
android/abi_gki_aarch64.xml
Merge 4.19.285 into android-4.19-stable
Linux 4.19.286
Revert "staging: rtl8192e: Replace macro RTL_PCI_DEVICE with PCI_DEVICE"
btrfs: unset reloc control if transaction commit fails in prepare_to_relocate()
btrfs: check return value of btrfs_commit_transaction in relocation
* ext4: only check dquot_initialize_needed() when debugging
fs/ext4/xattr.c
i2c: sprd: Delete i2c adapter in .remove's error path
pinctrl: meson-axg: add missing GPIOA_18 gpio group
* Bluetooth: Fix use-after-free in hci_remove_ltk/hci_remove_irk
net/bluetooth/hci_core.c
ceph: fix use-after-free bug for inodes when flushing capsnaps
drm/amdgpu: fix xclk freq on CHIP_STONEY
Input: psmouse - fix OOB access in Elantech protocol
* Input: xpad - delete a Razer DeathAdder mouse VID/PID entry
drivers/input/joystick/xpad.c
batman-adv: Broken sync while rescheduling delayed work
* lib: cpu_rmap: Fix potential use-after-free in irq_cpu_rmap_release()
lib/cpu_rmap.c
* net: sched: fix possible refcount leak in tc_chain_tmplt_add()
net/sched/cls_api.c
* net: sched: move rtm_tca_policy declaration to include file
include/net/pkt_sched.h
net/sched/cls_api.c
* rfs: annotate lockless accesses to RFS sock flow table
include/linux/netdevice.h
net/core/dev.c
* rfs: annotate lockless accesses to sk->sk_rxhash
include/net/sock.h
* Bluetooth: L2CAP: Add missing checks for invalid DCID
net/bluetooth/l2cap_core.c
* Bluetooth: Fix l2cap_disconnect_req deadlock
net/bluetooth/l2cap_core.c
net: dsa: lan9303: allow vid != 0 in port_fdb_{add|del} methods
spi: qup: Request DMA before enabling clocks
i40e: fix build warnings in i40e_alloc.h
i40iw: fix build warning in i40iw_manage_apbvt()
* UPSTREAM: net: cdc_ncm: Deal with too low values of dwNtbOutMaxSize
drivers/net/usb/cdc_ncm.c
* UPSTREAM: cdc_ncm: Fix the build warning
drivers/net/usb/cdc_ncm.c
* UPSTREAM: cdc_ncm: Implement the 32-bit version of NCM Transfer Block
drivers/net/usb/cdc_ncm.c
include/linux/usb/cdc_ncm.h
* Revert "tcp: reduce POLLOUT events caused by TCP_NOTSENT_LOWAT"
include/net/sock.h
include/net/tcp.h
net/core/stream.c
* Revert "tcp: return EPOLLOUT from tcp_poll only when notsent_bytes is half the limit"
net/ipv4/tcp.c
* Revert "tcp: factor out __tcp_close() helper"
include/net/tcp.h
net/ipv4/tcp.c
* Revert "tcp: add annotations around sk->sk_shutdown accesses"
net/ipv4/af_inet.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
* ANDROID: fix abi break in 4.19.284 for cpuhotplug.h
include/linux/cpuhotplug.h
Merge "Merge 4.19.284 into android-4.19-stable" into android-4.19-stable
UPSTREAM: mailbox: mailbox-test: fix a locking issue in mbox_test_message_write()
UPSTREAM: mailbox: mailbox-test: Fix potential double-free in mbox_test_message_write()
Linux 4.19.285
wifi: rtlwifi: 8192de: correct checking of IQK reload
* scsi: dpt_i2o: Do not process completions with invalid addresses
drivers/scsi/Kconfig
scsi: dpt_i2o: Remove broken pass-through ioctl (I2OUSERCMD)
* regmap: Account for register length when chunking
drivers/base/regmap/regmap.c
fbcon: Fix null-ptr-deref in soft_cursor
* ext4: add lockdep annotations for i_data_sem for ea_inode's
fs/ext4/ext4.h
fs/ext4/xattr.c
* selinux: don't use make's grouped targets feature yet
security/selinux/Makefile
tty: serial: fsl_lpuart: use UARTCTRL_TXINV to send break instead of UARTCTRL_SBK
mmc: vub300: fix invalid response handling
rsi: Remove unnecessary boolean condition
regulator: da905{2,5}: Remove unnecessary array check
hwmon: (scmi) Remove redundant pointer check
wifi: rtlwifi: remove always-true condition pointed out by GCC 12
lib/dynamic_debug.c: use address-of operator on section symbols
* kernel/extable.c: use address-of operator on section symbols
kernel/extable.c
eth: sun: cassini: remove dead code
* gcc-12: disable '-Wdangling-pointer' warning for now
Makefile
ACPI: thermal: drop an always true check
x86/boot: Wrap literal addresses in absolute_pointer()
ata: libata-scsi: Use correct device no in ata_find_dev()
scsi: stex: Fix gcc 13 warnings
* usb: gadget: f_fs: Add unbind event before functionfs_unbind
drivers/usb/gadget/function/f_fs.c
net: usb: qmi_wwan: Set DTR quirk for BroadMobi BM818
* iio: dac: build ad5758 driver when AD5758 is selected
drivers/iio/dac/Makefile
iio: dac: mcp4725: Fix i2c_master_send() return value handling
* HID: wacom: avoid integer overflow in wacom_intuos_inout()
drivers/hid/wacom_wac.c
* HID: google: add jewel USB id
drivers/hid/hid-ids.h
iio: adc: mxs-lradc: fix the order of two cleanup operations
mailbox: mailbox-test: fix a locking issue in mbox_test_message_write()
atm: hide unused procfs functions
ALSA: oss: avoid missing-prototype warnings
* netfilter: conntrack: define variables exp_nat_nla_policy and any_addr with CONFIG_NF_NAT
net/netfilter/nf_conntrack_netlink.c
wifi: b43: fix incorrect __packed annotation
* scsi: core: Decrease scsi_device's iorequest_cnt if dispatch failed
drivers/scsi/scsi_lib.c
* arm64/mm: mark private VM_FAULT_X defines as vm_fault_t
arch/arm64/mm/fault.c
ARM: dts: stm32: add pin map for CAN controller on stm32f7
wifi: rtl8xxxu: fix authentication timeout due to incorrect RCR value
media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221
media: dvb-core: Fix kernel WARNING for blocking operation in wait_event*()
* media: dvb-core: Fix use-after-free due on race condition at dvb_net
include/media/dvb_net.h
media: mn88443x: fix !CONFIG_OF error by drop of_match_ptr from ID table
media: ttusb-dec: fix memory leak in ttusb_dec_exit_dvb()
media: dvb_ca_en50221: fix a size write bug
media: netup_unidvb: fix irq init by register it at the end of probe
media: dvb-usb: dw2102: fix uninit-value in su3000_read_mac_address
media: dvb-usb: digitv: fix null-ptr-deref in digitv_i2c_xfer()
media: dvb-usb-v2: rtl28xxu: fix null-ptr-deref in rtl28xxu_i2c_xfer
media: dvb-usb-v2: ce6230: fix null-ptr-deref in ce6230_i2c_master_xfer()
media: dvb-usb-v2: ec168: fix null-ptr-deref in ec168_i2c_xfer()
media: dvb-usb: az6027: fix three null-ptr-deref in az6027_i2c_xfer()
* media: dvb_demux: fix a bug for the continuity counter
drivers/media/dvb-core/dvb_demux.c
ASoC: ssm2602: Add workaround for playback distortions
* xfrm: Check if_id in inbound policy/secpath match
net/xfrm/xfrm_policy.c
ASoC: dwc: limit the number of overrun messages
nbd: Fix debugfs_create_dir error checking
fbdev: stifb: Fix info entry in sti_struct on error path
fbdev: modedb: Add 1920x1080 at 60 Hz video mode
media: rcar-vin: Select correct interrupt mode for V4L2_FIELD_ALTERNATE
ARM: 9295/1: unwind:fix unwind abort for uleb128 case
mailbox: mailbox-test: Fix potential double-free in mbox_test_message_write()
watchdog: menz069_wdt: fix watchdog initialisation
net: dsa: mv88e6xxx: Increase wait after reset deactivation
net/sched: flower: fix possible OOB write in fl_set_geneve_opt()
* udp6: Fix race condition in udp6_sendmsg & connect
net/core/sock.c
* net/netlink: fix NETLINK_LIST_MEMBERSHIPS length report
net/netlink/af_netlink.c
* ocfs2/dlm: move BITS_TO_BYTES() to bitops.h for wider use
include/linux/bitops.h
* net: sched: fix NULL pointer dereference in mq_attach
net/sched/sch_api.c
* net/sched: Prohibit regrafting ingress or clsact Qdiscs
net/sched/sch_api.c
* net/sched: Reserve TC_H_INGRESS (TC_H_CLSACT) for ingress (clsact) Qdiscs
net/sched/sch_api.c
net/sched/sch_ingress.c
* net/sched: sch_clsact: Only create under TC_H_CLSACT
net/sched/sch_ingress.c
* net/sched: sch_ingress: Only create under TC_H_INGRESS
net/sched/sch_ingress.c
* tcp: Return user_mss for TCP_MAXSEG in CLOSE/LISTEN state if user_mss set
net/ipv4/tcp.c
* tcp: deny tcp_disconnect() when threads are waiting
include/net/sock.h
net/ipv4/af_inet.c
net/ipv4/inet_connection_sock.c
net/ipv4/tcp.c
* af_packet: do not use READ_ONCE() in packet_bind()
net/packet/af_packet.c
amd-xgbe: fix the false linkup in xgbe_phy_status
* af_packet: Fix data-races of pkt_sk(sk)->num.
net/packet/af_packet.c
netrom: fix info-leak in nr_write_internal()
net/mlx5: fw_tracer, Fix event handling
dmaengine: pl330: rename _start to prevent build error
* netfilter: ctnetlink: Support offloaded conntrack entry deletion
net/netfilter/nf_conntrack_netlink.c
* ipv{4,6}/raw: fix output xfrm lookup wrt protocol
include/net/ip.h
include/uapi/linux/in.h
net/ipv4/ip_sockglue.c
net/ipv4/raw.c
net/ipv6/raw.c
* bluetooth: Add cmd validity checks at the start of hci_sock_ioctl()
net/bluetooth/hci_sock.c
* cdc_ncm: Fix the build warning
drivers/net/usb/cdc_ncm.c
power: supply: bq24190: Call power_supply_changed() after updating input current
* power: supply: core: Refactor power_supply_set_input_current_limit_from_supplier()
drivers/power/supply/power_supply_core.c
include/linux/power_supply.h
power: supply: bq27xxx: After charger plug in/out wait 0.5s for things to stabilize
* net: cdc_ncm: Deal with too low values of dwNtbOutMaxSize
drivers/net/usb/cdc_ncm.c
* cdc_ncm: Implement the 32-bit version of NCM Transfer Block
drivers/net/usb/cdc_ncm.c
include/linux/usb/cdc_ncm.h
Merge 4.19.284 into android-4.19-stable
UPSTREAM: efi: rt-wrapper: Add missing include
* BACKPORT: arm64: efi: Execute runtime services from a dedicated stack
arch/arm64/include/asm/efi.h
* Revert "uapi/linux/const.h: prefer ISO-friendly __typeof__"
include/uapi/linux/const.h
Merge "Merge 4.19.283 into android-4.19-stable" into android-4.19-stable
Linux 4.19.284
* drivers: depend on HAS_IOMEM for devm_platform_ioremap_resource()
drivers/base/platform.c
3c589_cs: Fix an error handling path in tc589_probe()
forcedeth: Fix an error handling path in nv_probe()
* ASoC: Intel: Skylake: Fix declaration of enum skl_ch_cfg
include/uapi/sound/skl-tplg-interface.h
x86/show_trace_log_lvl: Ensure stack pointer is aligned, again
xen/pvcalls-back: fix double frees with pvcalls_new_active_socket()
* coresight: Fix signedness bug in tmc_etr_buf_insert_barrier_packet()
drivers/hwtracing/coresight/coresight-tmc-etr.c
power: supply: sbs-charger: Fix INHIBITED bit for Status reg
* power: supply: bq27xxx: Fix poll_interval handling and races on remove
include/linux/power/bq27xxx_battery.h
power: supply: bq27xxx: Fix I2C IRQ race on remove
power: supply: bq27xxx: Fix bq27xxx_battery_update() race condition
* power: supply: leds: Fix blink to LED on transition
drivers/power/supply/power_supply_leds.c
* ipv6: Fix out-of-bounds access in ipv6_find_tlv()
net/ipv6/exthdrs_core.c
* bpf: Fix mask generation for 32-bit narrow loads of 64-bit fields
kernel/bpf/verifier.c
* net: fix skb leak in __skb_tstamp_tx()
net/core/skbuff.c
media: radio-shark: Add endpoint checks
USB: sisusbvga: Add endpoint checks
* USB: core: Add routines for endpoint checks in old drivers
drivers/usb/core/usb.c
include/linux/usb.h
* udplite: Fix NULL pointer dereference in __sk_mem_raise_allocated().
net/ipv4/udplite.c
net/ipv6/udplite.c
ALSA: hda/realtek - Fix inverted bass GPIO pin on Acer 8951G
ALSA: hda/realtek - Fixed one of HP ALC671 platform Headset Mic supported
parisc: Fix flush_dcache_page() for usage from irq context
selftests/memfd: Fix unknown type name build failure
x86/mm: Avoid incomplete Global INVLPG flushes
btrfs: use nofs when cleaning up aborted transactions
parisc: Allow to reboot machine after system halt
m68k: Move signal frame following exception on 68020/030
ALSA: hda/ca0132: add quirk for EVGA X299 DARK
spi: fsl-cpm: Use 16 bit mode for large transfers with even size
spi: fsl-spi: Re-organise transfer bits_per_word adaptation
spi: spi-fsl-spi: automatically adapt bits-per-word in cpu mode
s390/qdio: fix do_sqbs() inline assembly constraint
s390/qdio: get rid of register asm
vc_screen: reload load of struct vc_data pointer in vcs_write() to avoid UAF
vc_screen: rewrite vcs_size to accept vc, not inode
* usb: gadget: u_ether: Fix host MAC address case
drivers/usb/gadget/function/u_ether.c
* usb: gadget: u_ether: Convert prints to device prints
drivers/usb/gadget/function/u_ether.c
* lib/string_helpers: Introduce string_upper() and string_lower() helpers
include/linux/string_helpers.h
ALSA: hda/realtek: Add a quirk for HP EliteDesk 805
ALSA: hda/realtek - ALC897 headset MIC no sound
ALSA: hda/realtek - Add headset Mic support for Lenovo ALC897 platform
ALSA: hda/realtek: Fix the mic type detection issue for ASUS G551JW
ALSA: hda/realtek - The front Mic on a HP machine doesn't work
ALSA: hda/realtek - Enable the headset of Acer N50-600 with ALC662
ALSA: hda/realtek - Enable headset mic of Acer X2660G with ALC662
ALSA: hda/realtek - Add Headset Mic supported for HP cPC
ALSA: hda/realtek - More constifications
Add Acer Aspire Ethos 8951G model quirk
* HID: wacom: Force pen out of prox if no events have been received in a while
drivers/hid/wacom.h
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c
netfilter: nf_tables: do not allow RULE_ID to refer to another chain
netfilter: nf_tables: validate NFTA_SET_ELEM_OBJREF based on NFT_SET_OBJECT flag
netfilter: nf_tables: stricter validation of element data
* netfilter: nf_tables: allow up to 64 bytes in the set element data area
include/net/netfilter/nf_tables.h
netfilter: nf_tables: add nft_setelem_parse_key()
netfilter: nf_tables: validate registers coming from userspace.
* netfilter: nftables: statify nft_parse_register()
include/net/netfilter/nf_tables.h
* netfilter: nftables: add nft_parse_register_store() and use it
include/net/netfilter/nf_tables.h
include/net/netfilter/nf_tables_core.h
include/net/netfilter/nft_fib.h
* netfilter: nftables: add nft_parse_register_load() and use it
include/net/netfilter/nf_tables.h
include/net/netfilter/nf_tables_core.h
include/net/netfilter/nft_masq.h
include/net/netfilter/nft_redir.h
nilfs2: fix use-after-free bug of nilfs_root in nilfs_evict_inode()
tpm/tpm_tis: Disable interrupts for more Lenovo devices
ceph: force updating the msg pointer in non-split case
serial: Add support for Advantech PCI-1611U card
* statfs: enforce statfs[64] structure initialization
fs/statfs.c
ALSA: hda: Add NVIDIA codec IDs a3 through a7 to patch table
ALSA: hda: Fix Oops by 9.1 surround channel names
usb: typec: altmodes/displayport: fix pin_assignment_show
* usb-storage: fix deadlock when a scsi command timeouts more than once
drivers/usb/storage/scsiglue.c
vlan: fix a potential uninit-value in vlan_dev_hard_start_xmit()
igb: fix bit_shift to be in [1..8] range
cassini: Fix a memory leak in the error handling path of cas_init_one()
net: bcmgenet: Restore phy_stop() depending upon suspend/close
net: bcmgenet: Remove phy_stop() from bcmgenet_netif_stop()
net: nsh: Use correct mac_offset to unwind gso skb in nsh_gso_segment()
drm/exynos: fix g2d_open/close helper function definitions
media: netup_unidvb: fix use-after-free at del_timer()
erspan: get the proto with the md version for collect_md
* ip_gre, ip6_gre: Fix race condition on o_seqno in collect_md mode
include/net/ip6_tunnel.h
include/net/ip_tunnels.h
ip6_gre: Make o_seqno start from 0 in native mode
ip6_gre: Fix skb_under_panic in __gre6_xmit()
serial: arc_uart: fix of_iomap leak in `arc_serial_probe`
* drivers: provide devm_platform_ioremap_resource()
drivers/base/platform.c
include/linux/platform_device.h
vsock: avoid to close connected socket after the timeout
net: fec: Better handle pm_runtime_get() failing in .remove()
* af_key: Reject optional tunnel/BEET mode templates in outbound policies
net/key/af_key.c
cpupower: Make TSC read per CPU for Mperf monitor
btrfs: fix space cache inconsistency after error loading it from disk
btrfs: replace calls to btrfs_find_free_ino with btrfs_find_free_objectid
mfd: dln2: Fix memory leak in dln2_probe()
phy: st: miphy28lp: use _poll_timeout functions for waits
* Input: xpad - add constants for GIP interface numbers
drivers/input/joystick/xpad.c
clk: tegra20: fix gcc-7 constant overflow warning
recordmcount: Fix memory leaks in the uwrite function
* sched: Fix KCSAN noinstr violation
include/linux/sched/task_stack.h
mcb-pci: Reallocate memory region to avoid memory overlapping
serial: 8250: Reinit port->pm on port specific driver unbind
usb: typec: tcpm: fix multiple times discover svids error
* HID: wacom: generic: Set battery quirk only when we see battery data
drivers/hid/wacom_wac.c
spi: spi-imx: fix MX51_ECSPI_* macros when cs > 3
HID: logitech-hidpp: Reconcile USB and Unifying serials
HID: logitech-hidpp: Don't use the USB serial for USB devices
staging: rtl8192e: Replace macro RTL_PCI_DEVICE with PCI_DEVICE
* Bluetooth: L2CAP: fix "bad unlock balance" in l2cap_disconnect_rsp
net/bluetooth/l2cap_core.c
wifi: iwlwifi: dvm: Fix memcpy: detected field-spanning write backtrace
* f2fs: fix to drop all dirty pages during umount() if cp_error is set
fs/f2fs/checkpoint.c
fs/f2fs/data.c
* ext4: Fix best extent lstart adjustment logic in ext4_mb_new_inode_pa()
fs/ext4/mballoc.c
* ext4: set goal start correctly in ext4_mb_normalize_request
fs/ext4/mballoc.c
gfs2: Fix inode height consistency check
scsi: message: mptlan: Fix use after free bug in mptlan_remove() due to race condition
* lib: cpu_rmap: Avoid use after free on rmap->obj array entries
lib/cpu_rmap.c
* net: Catch invalid index in XPS mapping
net/core/dev.c
net: pasemi: Fix return type of pasemi_mac_start_tx()
ext2: Check block size validity during mount
wifi: brcmfmac: cfg80211: Pass the PMK in binary instead of hex
ACPICA: ACPICA: check null return of ACPI_ALLOCATE_ZEROED in acpi_db_display_objects
ACPICA: Avoid undefined behavior: applying zero offset to null pointer
drm/tegra: Avoid potential 32-bit integer overflow
ACPI: EC: Fix oops when removing custom query handlers
* firmware: arm_sdei: Fix sleep from invalid context BUG
include/linux/cpuhotplug.h
memstick: r592: Fix UAF bug in r592_remove due to race condition
* regmap: cache: Return error in cache sync operations for REGCACHE_NONE
drivers/base/regmap/regcache.c
drm/amd/display: Use DC_LOG_DC in the trasform pixel function
fs: hfsplus: remove WARN_ON() from hfsplus_cat_{read,write}_inode()
* af_unix: Fix data races around sk->sk_shutdown.
net/unix/af_unix.c
* af_unix: Fix a data race of sk->sk_receive_queue->qlen.
net/unix/af_unix.c
* net: datagram: fix data-races in datagram_poll()
net/core/datagram.c
ipvlan:Fix out-of-bounds caused by unclear skb->cb
* tcp: add annotations around sk->sk_shutdown accesses
net/ipv4/af_inet.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
* tcp: factor out __tcp_close() helper
include/net/tcp.h
net/ipv4/tcp.c
* tcp: return EPOLLOUT from tcp_poll only when notsent_bytes is half the limit
net/ipv4/tcp.c
* tcp: reduce POLLOUT events caused by TCP_NOTSENT_LOWAT
include/net/sock.h
include/net/tcp.h
net/core/stream.c
* net: annotate sk->sk_err write from do_recvmmsg()
net/socket.c
* netlink: annotate accesses to nlk->cb_running
net/netlink/af_netlink.c
* net: Fix load-tearing on sk->sk_stamp in sock_recv_cmsgs().
include/net/sock.h
* UPSTREAM: ext4: avoid a potential slab-out-of-bounds in ext4_group_desc_csum
fs/ext4/super.c
Merge 4.19.283 into android-4.19-stable
* UPSTREAM: ext4: fix invalid free tracking in ext4_xattr_move_to_block()
fs/ext4/xattr.c
Linux 4.19.283
* mm/page_alloc: fix potential deadlock on zonelist_update_seq seqlock
mm/page_alloc.c
* printk: declare printk_deferred_{enter,safe}() in include/linux/printk.h
include/linux/printk.h
PCI: pciehp: Fix AB-BA deadlock between reset_lock and device_lock
PCI: pciehp: Use down_read/write_nested(reset_lock) to fix lockdep errors
drbd: correctly submit flush bio on barrier
serial: 8250: Fix serial8250_tx_empty() race with DMA Tx
* tty: Prevent writing chars during tcsetattr TCSADRAIN/FLUSH
drivers/tty/tty_io.c
drivers/tty/tty_ioctl.c
include/linux/tty.h
* ext4: fix invalid free tracking in ext4_xattr_move_to_block()
fs/ext4/xattr.c
* ext4: remove a BUG_ON in ext4_mb_release_group_pa()
fs/ext4/mballoc.c
* ext4: bail out of ext4_xattr_ibody_get() fails for any reason
fs/ext4/inline.c
* ext4: add bounds checking in get_max_inline_xattr_value_size()
fs/ext4/inline.c
* ext4: improve error recovery code paths in __ext4_remount()
fs/ext4/super.c
* ext4: avoid a potential slab-out-of-bounds in ext4_group_desc_csum
fs/ext4/super.c
* ext4: fix WARNING in mb_find_extent
fs/ext4/balloc.c
* HID: wacom: Set a default resolution for older tablets
drivers/hid/wacom_wac.c
drm/panel: otm8009a: Set backlight parent to panel device
ARM: dts: s5pv210: correct MIPI CSIS clock name
ARM: dts: exynos: fix WM8960 clock name in Itop Elite
sh: nmi_debug: fix return value of __setup handler
sh: init: use OF_EARLY_FLATTREE for early init
sh: math-emu: fix macro redefined warning
platform/x86: touchscreen_dmi: Add info for the Dexp Ursus KX210i
cifs: fix pcchunk length type in smb2_copychunk_range
btrfs: print-tree: parent bytenr must be aligned to sector size
btrfs: fix btrfs_prev_leaf() to not return the same key twice
perf symbols: Fix return incorrect build_id size in elf_read_build_id()
perf map: Delete two variable initialisations before null pointer checks in sort__sym_from_cmp()
perf vendor events power9: Remove UTF-8 characters from JSON files
virtio_net: suppress cpu stall when free_unused_bufs
virtio_net: split free_unused_bufs()
ALSA: caiaq: input: Add error handling for unsupported input methods in `snd_usb_caiaq_input_init`
drm/amdgpu: add a missing lock for AMDGPU_SCHED
* drm/amdgpu: Add command to override the context priority.
include/uapi/drm/amdgpu_drm.h
drm/amdgpu: Put enable gfx off feature to a delay thread
drm/amdgpu: Add amdgpu_gfx_off_ctrl function
* af_packet: Don't send zero-byte data in packet_sendmsg_spkt().
net/packet/af_packet.c
rxrpc: Fix hard call timeout units
* net/sched: act_mirred: Add carrier check
net/sched/act_mirred.c
* writeback: fix call of incorrect macro
fs/fs-writeback.c
net: dsa: mv88e6xxx: add mv88e6321 rsvd2cpu
net: dsa: mv88e6xxx: Add missing watchdog ops for 6320 family
* sit: update dev->needed_headroom in ipip6_tunnel_bind_dev()
net/ipv6/sit.c
relayfs: fix out-of-bounds access in relay_file_read
kernel/relay.c: fix read_pos error when multiple readers
* dm verity: fix error handling for check_at_most_once on FEC
drivers/md/dm-verity-target.c
* dm verity: skip redundant verity_handle_err() on I/O errors
drivers/md/dm-verity-target.c
ipmi: fix SSIF not responding under certain cond.
ipmi_ssif: Rename idle state and check
* ipmi: Fix how the lower layers are told to watch for messages
include/linux/ipmi_smi.h
ipmi: Fix SSIF flag requests
* tick/nohz: Fix cpu_is_hotpluggable() by checking with nohz subsystem
drivers/base/cpu.c
include/linux/tick.h
kernel/time/tick-sched.c
* nohz: Add TICK_DEP_BIT_RCU
include/linux/tick.h
include/trace/events/timer.h
kernel/time/tick-sched.c
* netfilter: nf_tables: deactivate anonymous set from preparation phase
include/net/netfilter/nf_tables.h
debugobject: Ensure pool refill (again)
perf auxtrace: Fix address filter entire kernel size
* dm ioctl: fix nested locking in table_clear() to remove deadlock concern
drivers/md/dm-ioctl.c
dm flakey: fix a crash with invalid table line
dm integrity: call kmem_cache_destroy() in dm_integrity_init() error path
s390/dasd: fix hanging blockdevice after request requeue
* btrfs: scrub: reject unsupported scrub flags
include/uapi/linux/btrfs.h
clk: rockchip: rk3399: allow clk_cifout to force clk_cifout_src to reparent
wifi: rtl8xxxu: RTL8192EU always needs full init
md/raid10: fix null-ptr-deref in raid10_sync_request
nilfs2: fix infinite loop in nilfs_mdt_get_block()
nilfs2: do not write dirty data after degenerating to read-only
parisc: Fix argument pointer in real64_call_asm()
dmaengine: at_xdmac: do not enable all cyclic channels
phy: tegra: xusb: Add missing tegra_xusb_port_unregister for usb2_port and ulpi_port
pwm: mtk-disp: Disable shadow registers before setting backlight values
pwm: mtk-disp: Adjust the clocks to avoid them mismatch
pwm: mtk-disp: Don't check the return code of pwmchip_remove()
openrisc: Properly store r31 to pt_regs on unhandled exceptions
RDMA/mlx5: Use correct device num_ports when modify DC
* SUNRPC: remove the maximum number of retries in call_bind_status
include/linux/sunrpc/sched.h
NFSv4.1: Always send a RECLAIM_COMPLETE after establishing lease
IB/hfi1: Fix SDMA mmu_rb_node not being evicted in LRU order
* clk: add missing of_node_put() in "assigned-clocks" property parsing
drivers/clk/clk-conf.c
power: supply: generic-adc-battery: fix unit scaling
RDMA/mlx4: Prevent shift wrapping in set_user_sq_size()
RDMA/rdmavt: Delete unnecessary NULL check
* perf/core: Fix hardlockup failure caused by perf throttle
kernel/events/core.c
powerpc/rtas: use memmove for potentially overlapping buffer copy
* macintosh: via-pmu-led: requires ATA to be set
drivers/macintosh/Kconfig
powerpc/sysdev/tsi108: fix resource printk format warnings
powerpc/wii: fix resource printk format warnings
powerpc/mpc512x: fix resource printk format warning
macintosh/windfarm_smu_sat: Add missing of_node_put()
* spmi: Add a check for remove callback when removing a SPMI driver
drivers/spmi/spmi.c
staging: rtl8192e: Fix W_DISABLE# does not work after stop/start
serial: 8250: Add missing wakeup event reporting
tty: serial: fsl_lpuart: adjust buffer length to the intended size
usb: chipidea: fix missing goto in `ci_hdrc_probe`
sh: sq: Fix incorrect element size for allocating bitmap buffer
* uapi/linux/const.h: prefer ISO-friendly __typeof__
include/uapi/linux/const.h
spi: cadence-quadspi: fix suspend-resume implementations
mtd: spi-nor: cadence-quadspi: Handle probe deferral while requesting DMA channel
mtd: spi-nor: cadence-quadspi: Don't initialize rx_dma_complete on failure
mtd: spi-nor: cadence-quadspi: Make driver independent of flash geometry
ia64: salinfo: placate defined-but-not-used warning
ia64: mm/contig: fix section mismatch warning/error
* of: Fix modalias string generation
drivers/of/device.c
vmci_host: fix a race condition in vmci_host_poll() causing GPF
spi: fsl-spi: Fix CPM/QE mode Litte Endian
spi: qup: Don't skip cleanup in remove's error path
spi: qup: fix PM reference leak in spi_qup_remove()
* linux/vt_buffer.h: allow either builtin or modular for macros
include/linux/vt_buffer.h
usb: gadget: udc: renesas_usb3: Fix use after free bug in renesas_usb3_remove due to race condition
fpga: bridge: fix kernel-doc parameter description
usb: host: xhci-rcar: remove leftover quirk handling
* pstore: Revert pmsg_lock back to a normal mutex
fs/pstore/pmsg.c
* tcp/udp: Fix memleaks of sk and zerocopy skbs with TX timestamp.
net/core/skbuff.c
net: amd: Fix link leak when verifying config failed
* netlink: Use copy_to_user() for optval in netlink_getsockopt().
net/netlink/af_netlink.c
Revert "Bluetooth: btsdio: fix use after free bug in btsdio_remove due to unfinished work"
* ipv4: Fix potential uninit variable access bug in __ip_make_skb()
net/ipv4/ip_output.c
* netfilter: nf_tables: don't write table validation state without mutex
include/linux/netfilter/nfnetlink.h
net/netfilter/nfnetlink.c
ixgbe: Enable setting RSS table to default values
ixgbe: Allow flow hash to be set via ethtool
wifi: iwlwifi: mvm: check firmware response size
wifi: iwlwifi: make the loop for card preparation effective
md/raid10: fix memleak of md thread
md: update the optimal I/O size on reshape
md/raid10: fix memleak for 'conf->bio_split'
md/raid10: fix leak of 'r10bio->remaining' for recovery
* crypto: drbg - Only fail when jent is unavailable in FIPS mode
crypto/drbg.c
* crypto: drbg - make drbg_prepare_hrng() handle jent instantiation errors
crypto/drbg.c
bpftool: Fix bug for long instructions in program CFG dumps
wifi: rtlwifi: fix incorrect error codes in rtl_debugfs_set_write_reg()
wifi: rtlwifi: fix incorrect error codes in rtl_debugfs_set_write_rfreg()
rtlwifi: Replace RT_TRACE with rtl_dbg
rtlwifi: Start changing RT_TRACE into rtl_dbg
rtlwifi: rtl_pci: Fix memory leak when hardware init fails
scsi: megaraid: Fix mega_cmd_done() CMDID_INT_CMDS
scsi: target: iscsit: Fix TAS handling during conn cleanup
* net/packet: convert po->auxdata to an atomic flag
net/packet/af_packet.c
net/packet/internal.h
* net/packet: convert po->origdev to an atomic flag
net/packet/af_packet.c
net/packet/internal.h
vlan: partially enable SIOCSHWTSTAMP in container
* scm: fix MSG_CTRUNC setting condition for SO_PASSSEC
include/net/scm.h
tools: bpftool: Remove invalid \' json escape
wifi: ath6kl: reduce WARN to dev_dbg() in callback
wifi: ath5k: fix an off by one check in ath5k_eeprom_read_freq_list()
wifi: ath9k: hif_usb: fix memory leak of remain_skbs
wifi: ath6kl: minor fix for allocation size
debugobject: Prevent init race with static objects
debugobjects: Move printk out of db->lock critical sections
debugobjects: Add percpu free pools
* arm64: kgdb: Set PSTATE.SS to 1 to re-enable single-step
arch/arm64/include/asm/debug-monitors.h
arch/arm64/kernel/debug-monitors.c
x86/ioapic: Don't return 0 from arch_dynirq_lower_bound()
media: rc: gpio-ir-recv: Fix support for wake-up
media: rcar_fdp1: Fix refcount leak in probe and remove function
media: rcar_fdp1: Fix the correct variable assignments
media: saa7134: fix use after free bug in saa7134_finidev due to race condition
media: dm1105: Fix use after free bug in dm1105_remove due to race condition
x86/apic: Fix atomic update of offset in reserve_eilvt_offset()
drm/msm/adreno: drop bogus pm_runtime_set_active()
drm/msm/adreno: Defer enabling runpm until hw_init()
* firmware: qcom_scm: Clear download bit during reboot
drivers/firmware/qcom_scm.c
media: av7110: prevent underflow in write_ts_to_decoder()
* media: uapi: add MEDIA_BUS_FMT_METADATA_FIXED media bus format.
include/uapi/linux/media-bus-format.h
media: bdisp: Add missing check for create_workqueue
ARM: dts: qcom: ipq4019: Fix the PCI I/O port range
EDAC/skx: Fix overflows on the DRAM row address mapping arrays
EDAC, skx: Move debugfs node under EDAC's hierarchy
* drm/probe-helper: Cancel previous job before starting new one
drivers/gpu/drm/drm_probe_helper.c
drm/vgem: add missing mutex_destroy
drm/rockchip: Drop unbalanced obj unref
* selinux: ensure av_permissions.h is built when needed
security/selinux/Makefile
* selinux: fix Makefile dependencies of flask.h
security/selinux/Makefile
ubifs: Free memory for tmpfile name
ubi: Fix return value overwrite issue in try_write_vid_and_data()
ubifs: Fix memleak when insert_old_idx() failed
Revert "ubifs: dirty_cow_znode: Fix memleak in error handling path"
i2c: omap: Fix standard mode false ACK readings
KVM: nVMX: Emulate NOPs in L2, and PAUSE if it's not intercepted
reiserfs: Add security prefix to xattr name in reiserfs_security_write()
* ring-buffer: Sync IRQ works before buffer destruction
kernel/trace/ring_buffer.c
pwm: meson: Fix axg ao mux parents
MIPS: fw: Allow firmware to pass a empty env
* xhci: fix debugfs register accesses while suspended
drivers/usb/host/xhci-debugfs.c
* debugfs: regset32: Add Runtime PM support
fs/debugfs/file.c
include/linux/debugfs.h
staging: iio: resolver: ads1210: fix config mode
perf sched: Cast PTHREAD_STACK_MIN to int as it may turn into sysconf(__SC_THREAD_STACK_MIN_VALUE)
* USB: dwc3: fix runtime pm imbalance on unbind
drivers/usb/dwc3/core.c
stmmac: debugfs entry name is not be changed when udev rename device name.
ASoC: Intel: bytcr_rt5640: Add quirk for the Acer Iconia One 7 B1-750
iio: adc: palmas_gpadc: fix NULL dereference on rmmod
USB: serial: option: add UNISOC vendor and TOZED LT70C product
* bluetooth: Perform careful capability checks in hci_sock_ioctl()
net/bluetooth/hci_sock.c
wifi: brcmfmac: slab-out-of-bounds read in brcmf_get_assoc_ies()
* ANDROID: incremental fs: Evict inodes before freeing mount data
fs/incfs/main.c
fs/incfs/vfs.c
* Revert "Revert "mm/rmap: Fix anon_vma->degree ambiguity leading to double-reuse""
android/abi_gki_aarch64.xml
include/linux/rmap.h
mm/rmap.c
Bug: 299241959
Change-Id: Ib8c4ff87b1b0b720abce0f5fcdf1a51f01a472a9
Signed-off-by: Wilson Sung <wilsonsung@google.com>
Signed-off-by: ChangYan Lee <changyan@google.com>
3135 lines
77 KiB
C
3135 lines
77 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/pagemap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/module.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/falloc.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/fs.h>
|
|
|
|
static const struct file_operations fuse_direct_io_file_operations;
|
|
|
|
static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
|
int opcode, struct fuse_open_out *outargp)
|
|
{
|
|
struct fuse_open_in inarg;
|
|
FUSE_ARGS(args);
|
|
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
|
|
if (!fc->atomic_o_trunc)
|
|
inarg.flags &= ~O_TRUNC;
|
|
args.in.h.opcode = opcode;
|
|
args.in.h.nodeid = nodeid;
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
args.out.numargs = 1;
|
|
args.out.args[0].size = sizeof(*outargp);
|
|
args.out.args[0].value = outargp;
|
|
|
|
return fuse_simple_request(fc, &args);
|
|
}
|
|
|
|
struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
|
|
{
|
|
struct fuse_file *ff;
|
|
|
|
ff = kzalloc(sizeof(struct fuse_file), GFP_KERNEL);
|
|
if (unlikely(!ff))
|
|
return NULL;
|
|
|
|
ff->fc = fc;
|
|
ff->reserved_req = fuse_request_alloc(0);
|
|
if (unlikely(!ff->reserved_req)) {
|
|
kfree(ff);
|
|
return NULL;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&ff->write_entry);
|
|
refcount_set(&ff->count, 1);
|
|
RB_CLEAR_NODE(&ff->polled_node);
|
|
init_waitqueue_head(&ff->poll_wait);
|
|
|
|
spin_lock(&fc->lock);
|
|
ff->kh = ++fc->khctr;
|
|
spin_unlock(&fc->lock);
|
|
|
|
return ff;
|
|
}
|
|
|
|
void fuse_file_free(struct fuse_file *ff)
|
|
{
|
|
fuse_request_free(ff->reserved_req);
|
|
kfree(ff);
|
|
}
|
|
|
|
static struct fuse_file *fuse_file_get(struct fuse_file *ff)
|
|
{
|
|
refcount_inc(&ff->count);
|
|
return ff;
|
|
}
|
|
|
|
static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
|
|
{
|
|
iput(req->misc.release.inode);
|
|
}
|
|
|
|
static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir)
|
|
{
|
|
if (refcount_dec_and_test(&ff->count)) {
|
|
struct fuse_req *req = ff->reserved_req;
|
|
|
|
if (ff->fc->no_open && !isdir) {
|
|
/*
|
|
* Drop the release request when client does not
|
|
* implement 'open'
|
|
*/
|
|
__clear_bit(FR_BACKGROUND, &req->flags);
|
|
iput(req->misc.release.inode);
|
|
fuse_put_request(ff->fc, req);
|
|
} else if (sync) {
|
|
__set_bit(FR_FORCE, &req->flags);
|
|
__clear_bit(FR_BACKGROUND, &req->flags);
|
|
fuse_request_send(ff->fc, req);
|
|
iput(req->misc.release.inode);
|
|
fuse_put_request(ff->fc, req);
|
|
} else {
|
|
req->end = fuse_release_end;
|
|
__set_bit(FR_BACKGROUND, &req->flags);
|
|
fuse_request_send_background(ff->fc, req);
|
|
}
|
|
kfree(ff);
|
|
}
|
|
}
|
|
|
|
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
|
bool isdir)
|
|
{
|
|
struct fuse_file *ff;
|
|
int opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN;
|
|
|
|
ff = fuse_file_alloc(fc);
|
|
if (!ff)
|
|
return -ENOMEM;
|
|
|
|
ff->fh = 0;
|
|
ff->open_flags = FOPEN_KEEP_CACHE; /* Default for no-open */
|
|
if (!fc->no_open || isdir) {
|
|
struct fuse_open_out outarg;
|
|
int err;
|
|
|
|
err = fuse_send_open(fc, nodeid, file, opcode, &outarg);
|
|
if (!err) {
|
|
ff->fh = outarg.fh;
|
|
ff->open_flags = outarg.open_flags;
|
|
fuse_passthrough_setup(fc, ff, &outarg);
|
|
} else if (err != -ENOSYS || isdir) {
|
|
fuse_file_free(ff);
|
|
return err;
|
|
} else {
|
|
fc->no_open = 1;
|
|
}
|
|
}
|
|
|
|
if (isdir)
|
|
ff->open_flags &= ~FOPEN_DIRECT_IO;
|
|
|
|
ff->nodeid = nodeid;
|
|
file->private_data = ff;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_do_open);
|
|
|
|
static void fuse_link_write_file(struct file *file)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
struct fuse_file *ff = file->private_data;
|
|
/*
|
|
* file may be written through mmap, so chain it onto the
|
|
* inodes's write_file list
|
|
*/
|
|
spin_lock(&fc->lock);
|
|
if (list_empty(&ff->write_entry))
|
|
list_add(&ff->write_entry, &fi->write_files);
|
|
spin_unlock(&fc->lock);
|
|
}
|
|
|
|
void fuse_finish_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
|
|
if (ff->open_flags & FOPEN_DIRECT_IO)
|
|
file->f_op = &fuse_direct_io_file_operations;
|
|
if (ff->open_flags & FOPEN_STREAM)
|
|
stream_open(inode, file);
|
|
else if (ff->open_flags & FOPEN_NONSEEKABLE)
|
|
nonseekable_open(inode, file);
|
|
|
|
if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) {
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
|
|
spin_lock(&fc->lock);
|
|
fi->attr_version = ++fc->attr_version;
|
|
i_size_write(inode, 0);
|
|
spin_unlock(&fc->lock);
|
|
truncate_pagecache(inode, 0);
|
|
fuse_invalidate_attr(inode);
|
|
if (fc->writeback_cache)
|
|
file_update_time(file);
|
|
} else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
|
|
invalidate_inode_pages2(inode->i_mapping);
|
|
}
|
|
|
|
if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
|
|
fuse_link_write_file(file);
|
|
}
|
|
|
|
int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
int err;
|
|
bool is_wb_truncate = (file->f_flags & O_TRUNC) &&
|
|
fc->atomic_o_trunc &&
|
|
fc->writeback_cache;
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
err = generic_file_open(inode, file);
|
|
if (err)
|
|
return err;
|
|
|
|
if (is_wb_truncate) {
|
|
inode_lock(inode);
|
|
fuse_set_nowrite(inode);
|
|
}
|
|
|
|
err = fuse_do_open(fc, get_node_id(inode), file, isdir);
|
|
|
|
if (!err)
|
|
fuse_finish_open(inode, file);
|
|
|
|
if (is_wb_truncate) {
|
|
fuse_release_nowrite(inode);
|
|
inode_unlock(inode);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode)
|
|
{
|
|
struct fuse_conn *fc = ff->fc;
|
|
struct fuse_req *req = ff->reserved_req;
|
|
struct fuse_release_in *inarg = &req->misc.release.in;
|
|
|
|
spin_lock(&fc->lock);
|
|
list_del(&ff->write_entry);
|
|
if (!RB_EMPTY_NODE(&ff->polled_node))
|
|
rb_erase(&ff->polled_node, &fc->polled_files);
|
|
spin_unlock(&fc->lock);
|
|
|
|
wake_up_interruptible_all(&ff->poll_wait);
|
|
|
|
inarg->fh = ff->fh;
|
|
inarg->flags = flags;
|
|
req->in.h.opcode = opcode;
|
|
req->in.h.nodeid = ff->nodeid;
|
|
req->in.numargs = 1;
|
|
req->in.args[0].size = sizeof(struct fuse_release_in);
|
|
req->in.args[0].value = inarg;
|
|
}
|
|
|
|
void fuse_release_common(struct file *file, bool isdir)
|
|
{
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_req *req = ff->reserved_req;
|
|
int opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE;
|
|
|
|
fuse_passthrough_release(&ff->passthrough);
|
|
|
|
fuse_prepare_release(ff, file->f_flags, opcode);
|
|
|
|
if (ff->flock) {
|
|
struct fuse_release_in *inarg = &req->misc.release.in;
|
|
inarg->release_flags |= FUSE_RELEASE_FLOCK_UNLOCK;
|
|
inarg->lock_owner = fuse_lock_owner_id(ff->fc,
|
|
(fl_owner_t) file);
|
|
}
|
|
/* Hold inode until release is finished */
|
|
req->misc.release.inode = igrab(file_inode(file));
|
|
|
|
/*
|
|
* Normally this will send the RELEASE request, however if
|
|
* some asynchronous READ or WRITE requests are outstanding,
|
|
* the sending will be delayed.
|
|
*
|
|
* Make the release synchronous if this is a fuseblk mount,
|
|
* synchronous RELEASE is allowed (and desirable) in this case
|
|
* because the server can be trusted not to screw up.
|
|
*/
|
|
fuse_file_put(ff, ff->fc->destroy_req != NULL, isdir);
|
|
}
|
|
|
|
static int fuse_open(struct inode *inode, struct file *file)
|
|
{
|
|
return fuse_open_common(inode, file, false);
|
|
}
|
|
|
|
static int fuse_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
|
|
/* see fuse_vma_close() for !writeback_cache case */
|
|
if (fc->writeback_cache)
|
|
write_inode_now(inode, 1);
|
|
|
|
fuse_release_common(file, false);
|
|
|
|
/* return value is ignored by VFS */
|
|
return 0;
|
|
}
|
|
|
|
void fuse_sync_release(struct fuse_file *ff, int flags)
|
|
{
|
|
WARN_ON(refcount_read(&ff->count) > 1);
|
|
fuse_prepare_release(ff, flags, FUSE_RELEASE);
|
|
/*
|
|
* iput(NULL) is a no-op and since the refcount is 1 and everything's
|
|
* synchronous, we are fine with not doing igrab() here"
|
|
*/
|
|
fuse_file_put(ff, true, false);
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_sync_release);
|
|
|
|
/*
|
|
* Scramble the ID space with XTEA, so that the value of the files_struct
|
|
* pointer is not exposed to userspace.
|
|
*/
|
|
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id)
|
|
{
|
|
u32 *k = fc->scramble_key;
|
|
u64 v = (unsigned long) id;
|
|
u32 v0 = v;
|
|
u32 v1 = v >> 32;
|
|
u32 sum = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]);
|
|
sum += 0x9E3779B9;
|
|
v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[sum>>11 & 3]);
|
|
}
|
|
|
|
return (u64) v0 + ((u64) v1 << 32);
|
|
}
|
|
|
|
/*
|
|
* Check if any page in a range is under writeback
|
|
*
|
|
* This is currently done by walking the list of writepage requests
|
|
* for the inode, which can be pretty inefficient.
|
|
*/
|
|
static bool fuse_range_is_writeback(struct inode *inode, pgoff_t idx_from,
|
|
pgoff_t idx_to)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
struct fuse_req *req;
|
|
bool found = false;
|
|
|
|
spin_lock(&fc->lock);
|
|
list_for_each_entry(req, &fi->writepages, writepages_entry) {
|
|
pgoff_t curr_index;
|
|
|
|
BUG_ON(req->inode != inode);
|
|
curr_index = req->misc.write.in.offset >> PAGE_SHIFT;
|
|
if (idx_from < curr_index + req->num_pages &&
|
|
curr_index <= idx_to) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock(&fc->lock);
|
|
|
|
return found;
|
|
}
|
|
|
|
static inline bool fuse_page_is_writeback(struct inode *inode, pgoff_t index)
|
|
{
|
|
return fuse_range_is_writeback(inode, index, index);
|
|
}
|
|
|
|
/*
|
|
* Wait for page writeback to be completed.
|
|
*
|
|
* Since fuse doesn't rely on the VM writeback tracking, this has to
|
|
* use some other means.
|
|
*/
|
|
static int fuse_wait_on_page_writeback(struct inode *inode, pgoff_t index)
|
|
{
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
|
|
wait_event(fi->page_waitq, !fuse_page_is_writeback(inode, index));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Wait for all pending writepages on the inode to finish.
|
|
*
|
|
* This is currently done by blocking further writes with FUSE_NOWRITE
|
|
* and waiting for all sent writes to complete.
|
|
*
|
|
* This must be called under i_mutex, otherwise the FUSE_NOWRITE usage
|
|
* could conflict with truncation.
|
|
*/
|
|
static void fuse_sync_writes(struct inode *inode)
|
|
{
|
|
fuse_set_nowrite(inode);
|
|
fuse_release_nowrite(inode);
|
|
}
|
|
|
|
static int fuse_flush(struct file *file, fl_owner_t id)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_req *req;
|
|
struct fuse_flush_in inarg;
|
|
int err;
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
if (fc->no_flush)
|
|
return 0;
|
|
|
|
err = write_inode_now(inode, 1);
|
|
if (err)
|
|
return err;
|
|
|
|
inode_lock(inode);
|
|
fuse_sync_writes(inode);
|
|
inode_unlock(inode);
|
|
|
|
err = filemap_check_errors(file->f_mapping);
|
|
if (err)
|
|
return err;
|
|
|
|
req = fuse_get_req_nofail_nopages(fc, file);
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
inarg.fh = ff->fh;
|
|
inarg.lock_owner = fuse_lock_owner_id(fc, id);
|
|
req->in.h.opcode = FUSE_FLUSH;
|
|
req->in.h.nodeid = get_node_id(inode);
|
|
req->in.numargs = 1;
|
|
req->in.args[0].size = sizeof(inarg);
|
|
req->in.args[0].value = &inarg;
|
|
__set_bit(FR_FORCE, &req->flags);
|
|
fuse_request_send(fc, req);
|
|
err = req->out.h.error;
|
|
fuse_put_request(fc, req);
|
|
if (err == -ENOSYS) {
|
|
fc->no_flush = 1;
|
|
err = 0;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
|
|
int datasync, int isdir)
|
|
{
|
|
struct inode *inode = file->f_mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_file *ff = file->private_data;
|
|
FUSE_ARGS(args);
|
|
struct fuse_fsync_in inarg;
|
|
int err;
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
inode_lock(inode);
|
|
|
|
/*
|
|
* Start writeback against all dirty pages of the inode, then
|
|
* wait for all outstanding writes, before sending the FSYNC
|
|
* request.
|
|
*/
|
|
err = file_write_and_wait_range(file, start, end);
|
|
if (err)
|
|
goto out;
|
|
|
|
fuse_sync_writes(inode);
|
|
|
|
/*
|
|
* Due to implementation of fuse writeback
|
|
* file_write_and_wait_range() does not catch errors.
|
|
* We have to do this directly after fuse_sync_writes()
|
|
*/
|
|
err = file_check_and_advance_wb_err(file);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = sync_inode_metadata(inode, 1);
|
|
if (err)
|
|
goto out;
|
|
|
|
if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
|
|
goto out;
|
|
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
inarg.fh = ff->fh;
|
|
inarg.fsync_flags = datasync ? 1 : 0;
|
|
args.in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC;
|
|
args.in.h.nodeid = get_node_id(inode);
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
err = fuse_simple_request(fc, &args);
|
|
if (err == -ENOSYS) {
|
|
if (isdir)
|
|
fc->no_fsyncdir = 1;
|
|
else
|
|
fc->no_fsync = 1;
|
|
err = 0;
|
|
}
|
|
out:
|
|
inode_unlock(inode);
|
|
return err;
|
|
}
|
|
|
|
static int fuse_fsync(struct file *file, loff_t start, loff_t end,
|
|
int datasync)
|
|
{
|
|
return fuse_fsync_common(file, start, end, datasync, 0);
|
|
}
|
|
|
|
void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
|
|
size_t count, int opcode)
|
|
{
|
|
struct fuse_read_in *inarg = &req->misc.read.in;
|
|
struct fuse_file *ff = file->private_data;
|
|
|
|
inarg->fh = ff->fh;
|
|
inarg->offset = pos;
|
|
inarg->size = count;
|
|
inarg->flags = file->f_flags;
|
|
req->in.h.opcode = opcode;
|
|
req->in.h.nodeid = ff->nodeid;
|
|
req->in.numargs = 1;
|
|
req->in.args[0].size = sizeof(struct fuse_read_in);
|
|
req->in.args[0].value = inarg;
|
|
req->out.argvar = 1;
|
|
req->out.numargs = 1;
|
|
req->out.args[0].size = count;
|
|
}
|
|
|
|
static void fuse_release_user_pages(struct fuse_req *req, bool should_dirty)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < req->num_pages; i++) {
|
|
struct page *page = req->pages[i];
|
|
if (should_dirty)
|
|
set_page_dirty_lock(page);
|
|
put_page(page);
|
|
}
|
|
}
|
|
|
|
static void fuse_io_release(struct kref *kref)
|
|
{
|
|
kfree(container_of(kref, struct fuse_io_priv, refcnt));
|
|
}
|
|
|
|
static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io)
|
|
{
|
|
if (io->err)
|
|
return io->err;
|
|
|
|
if (io->bytes >= 0 && io->write)
|
|
return -EIO;
|
|
|
|
return io->bytes < 0 ? io->size : io->bytes;
|
|
}
|
|
|
|
/**
|
|
* In case of short read, the caller sets 'pos' to the position of
|
|
* actual end of fuse request in IO request. Otherwise, if bytes_requested
|
|
* == bytes_transferred or rw == WRITE, the caller sets 'pos' to -1.
|
|
*
|
|
* An example:
|
|
* User requested DIO read of 64K. It was splitted into two 32K fuse requests,
|
|
* both submitted asynchronously. The first of them was ACKed by userspace as
|
|
* fully completed (req->out.args[0].size == 32K) resulting in pos == -1. The
|
|
* second request was ACKed as short, e.g. only 1K was read, resulting in
|
|
* pos == 33K.
|
|
*
|
|
* Thus, when all fuse requests are completed, the minimal non-negative 'pos'
|
|
* will be equal to the length of the longest contiguous fragment of
|
|
* transferred data starting from the beginning of IO request.
|
|
*/
|
|
static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
|
|
{
|
|
int left;
|
|
|
|
spin_lock(&io->lock);
|
|
if (err)
|
|
io->err = io->err ? : err;
|
|
else if (pos >= 0 && (io->bytes < 0 || pos < io->bytes))
|
|
io->bytes = pos;
|
|
|
|
left = --io->reqs;
|
|
if (!left && io->blocking)
|
|
complete(io->done);
|
|
spin_unlock(&io->lock);
|
|
|
|
if (!left && !io->blocking) {
|
|
ssize_t res = fuse_get_res_by_io(io);
|
|
|
|
if (res >= 0) {
|
|
struct inode *inode = file_inode(io->iocb->ki_filp);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
|
|
spin_lock(&fc->lock);
|
|
fi->attr_version = ++fc->attr_version;
|
|
spin_unlock(&fc->lock);
|
|
}
|
|
|
|
io->iocb->ki_complete(io->iocb, res, 0);
|
|
}
|
|
|
|
kref_put(&io->refcnt, fuse_io_release);
|
|
}
|
|
|
|
static void fuse_aio_complete_req(struct fuse_conn *fc, struct fuse_req *req)
|
|
{
|
|
struct fuse_io_priv *io = req->io;
|
|
ssize_t pos = -1;
|
|
|
|
fuse_release_user_pages(req, io->should_dirty);
|
|
|
|
if (io->write) {
|
|
if (req->misc.write.in.size != req->misc.write.out.size)
|
|
pos = req->misc.write.in.offset - io->offset +
|
|
req->misc.write.out.size;
|
|
} else {
|
|
if (req->misc.read.in.size != req->out.args[0].size)
|
|
pos = req->misc.read.in.offset - io->offset +
|
|
req->out.args[0].size;
|
|
}
|
|
|
|
fuse_aio_complete(io, req->out.h.error, pos);
|
|
}
|
|
|
|
static size_t fuse_async_req_send(struct fuse_conn *fc, struct fuse_req *req,
|
|
size_t num_bytes, struct fuse_io_priv *io)
|
|
{
|
|
spin_lock(&io->lock);
|
|
kref_get(&io->refcnt);
|
|
io->size += num_bytes;
|
|
io->reqs++;
|
|
spin_unlock(&io->lock);
|
|
|
|
req->io = io;
|
|
req->end = fuse_aio_complete_req;
|
|
|
|
__fuse_get_request(req);
|
|
fuse_request_send_background(fc, req);
|
|
|
|
return num_bytes;
|
|
}
|
|
|
|
static size_t fuse_send_read(struct fuse_req *req, struct fuse_io_priv *io,
|
|
loff_t pos, size_t count, fl_owner_t owner)
|
|
{
|
|
struct file *file = io->iocb->ki_filp;
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_conn *fc = ff->fc;
|
|
|
|
fuse_read_fill(req, file, pos, count, FUSE_READ);
|
|
if (owner != NULL) {
|
|
struct fuse_read_in *inarg = &req->misc.read.in;
|
|
|
|
inarg->read_flags |= FUSE_READ_LOCKOWNER;
|
|
inarg->lock_owner = fuse_lock_owner_id(fc, owner);
|
|
}
|
|
|
|
if (io->async)
|
|
return fuse_async_req_send(fc, req, count, io);
|
|
|
|
fuse_request_send(fc, req);
|
|
return req->out.args[0].size;
|
|
}
|
|
|
|
static void fuse_read_update_size(struct inode *inode, loff_t size,
|
|
u64 attr_ver)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
|
|
spin_lock(&fc->lock);
|
|
if (attr_ver == fi->attr_version && size < inode->i_size &&
|
|
!test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
|
|
fi->attr_version = ++fc->attr_version;
|
|
i_size_write(inode, size);
|
|
}
|
|
spin_unlock(&fc->lock);
|
|
}
|
|
|
|
static void fuse_short_read(struct fuse_req *req, struct inode *inode,
|
|
u64 attr_ver)
|
|
{
|
|
size_t num_read = req->out.args[0].size;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
|
|
if (fc->writeback_cache) {
|
|
/*
|
|
* A hole in a file. Some data after the hole are in page cache,
|
|
* but have not reached the client fs yet. So, the hole is not
|
|
* present there.
|
|
*/
|
|
int i;
|
|
int start_idx = num_read >> PAGE_SHIFT;
|
|
size_t off = num_read & (PAGE_SIZE - 1);
|
|
|
|
for (i = start_idx; i < req->num_pages; i++) {
|
|
zero_user_segment(req->pages[i], off, PAGE_SIZE);
|
|
off = 0;
|
|
}
|
|
} else {
|
|
loff_t pos = page_offset(req->pages[0]) + num_read;
|
|
fuse_read_update_size(inode, pos, attr_ver);
|
|
}
|
|
}
|
|
|
|
static int fuse_do_readpage(struct file *file, struct page *page)
|
|
{
|
|
struct kiocb iocb;
|
|
struct fuse_io_priv io;
|
|
struct inode *inode = page->mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_req *req;
|
|
size_t num_read;
|
|
loff_t pos = page_offset(page);
|
|
size_t count = PAGE_SIZE;
|
|
u64 attr_ver;
|
|
int err;
|
|
|
|
/*
|
|
* Page writeback can extend beyond the lifetime of the
|
|
* page-cache page, so make sure we read a properly synced
|
|
* page.
|
|
*/
|
|
fuse_wait_on_page_writeback(inode, page->index);
|
|
|
|
req = fuse_get_req(fc, 1);
|
|
if (IS_ERR(req))
|
|
return PTR_ERR(req);
|
|
|
|
attr_ver = fuse_get_attr_version(fc);
|
|
|
|
req->out.page_zeroing = 1;
|
|
req->out.argpages = 1;
|
|
req->num_pages = 1;
|
|
req->pages[0] = page;
|
|
req->page_descs[0].length = count;
|
|
init_sync_kiocb(&iocb, file);
|
|
io = (struct fuse_io_priv) FUSE_IO_PRIV_SYNC(&iocb);
|
|
num_read = fuse_send_read(req, &io, pos, count, NULL);
|
|
err = req->out.h.error;
|
|
|
|
if (!err) {
|
|
/*
|
|
* Short read means EOF. If file size is larger, truncate it
|
|
*/
|
|
if (num_read < count)
|
|
fuse_short_read(req, inode, attr_ver);
|
|
|
|
SetPageUptodate(page);
|
|
}
|
|
|
|
fuse_put_request(fc, req);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int fuse_readpage(struct file *file, struct page *page)
|
|
{
|
|
struct inode *inode = page->mapping->host;
|
|
int err;
|
|
|
|
err = -EIO;
|
|
if (fuse_is_bad(inode))
|
|
goto out;
|
|
|
|
err = fuse_do_readpage(file, page);
|
|
fuse_invalidate_atime(inode);
|
|
out:
|
|
unlock_page(page);
|
|
return err;
|
|
}
|
|
|
|
static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
|
|
{
|
|
int i;
|
|
size_t count = req->misc.read.in.size;
|
|
size_t num_read = req->out.args[0].size;
|
|
struct address_space *mapping = NULL;
|
|
|
|
for (i = 0; mapping == NULL && i < req->num_pages; i++)
|
|
mapping = req->pages[i]->mapping;
|
|
|
|
if (mapping) {
|
|
struct inode *inode = mapping->host;
|
|
|
|
/*
|
|
* Short read means EOF. If file size is larger, truncate it
|
|
*/
|
|
if (!req->out.h.error && num_read < count)
|
|
fuse_short_read(req, inode, req->misc.read.attr_ver);
|
|
|
|
fuse_invalidate_atime(inode);
|
|
}
|
|
|
|
for (i = 0; i < req->num_pages; i++) {
|
|
struct page *page = req->pages[i];
|
|
if (!req->out.h.error)
|
|
SetPageUptodate(page);
|
|
else
|
|
SetPageError(page);
|
|
unlock_page(page);
|
|
put_page(page);
|
|
}
|
|
if (req->ff)
|
|
fuse_file_put(req->ff, false, false);
|
|
}
|
|
|
|
static void fuse_send_readpages(struct fuse_req *req, struct file *file)
|
|
{
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_conn *fc = ff->fc;
|
|
loff_t pos = page_offset(req->pages[0]);
|
|
size_t count = req->num_pages << PAGE_SHIFT;
|
|
|
|
req->out.argpages = 1;
|
|
req->out.page_zeroing = 1;
|
|
req->out.page_replace = 1;
|
|
fuse_read_fill(req, file, pos, count, FUSE_READ);
|
|
req->misc.read.attr_ver = fuse_get_attr_version(fc);
|
|
if (fc->async_read) {
|
|
req->ff = fuse_file_get(ff);
|
|
req->end = fuse_readpages_end;
|
|
fuse_request_send_background(fc, req);
|
|
} else {
|
|
fuse_request_send(fc, req);
|
|
fuse_readpages_end(fc, req);
|
|
fuse_put_request(fc, req);
|
|
}
|
|
}
|
|
|
|
struct fuse_fill_data {
|
|
struct fuse_req *req;
|
|
struct file *file;
|
|
struct inode *inode;
|
|
unsigned nr_pages;
|
|
};
|
|
|
|
static int fuse_readpages_fill(void *_data, struct page *page)
|
|
{
|
|
struct fuse_fill_data *data = _data;
|
|
struct fuse_req *req = data->req;
|
|
struct inode *inode = data->inode;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
|
|
fuse_wait_on_page_writeback(inode, page->index);
|
|
|
|
if (req->num_pages &&
|
|
(req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
|
|
(req->num_pages + 1) * PAGE_SIZE > fc->max_read ||
|
|
req->pages[req->num_pages - 1]->index + 1 != page->index)) {
|
|
int nr_alloc = min_t(unsigned, data->nr_pages,
|
|
FUSE_MAX_PAGES_PER_REQ);
|
|
fuse_send_readpages(req, data->file);
|
|
if (fc->async_read)
|
|
req = fuse_get_req_for_background(fc, nr_alloc);
|
|
else
|
|
req = fuse_get_req(fc, nr_alloc);
|
|
|
|
data->req = req;
|
|
if (IS_ERR(req)) {
|
|
unlock_page(page);
|
|
return PTR_ERR(req);
|
|
}
|
|
}
|
|
|
|
if (WARN_ON(req->num_pages >= req->max_pages)) {
|
|
unlock_page(page);
|
|
fuse_put_request(fc, req);
|
|
return -EIO;
|
|
}
|
|
|
|
get_page(page);
|
|
req->pages[req->num_pages] = page;
|
|
req->page_descs[req->num_pages].length = PAGE_SIZE;
|
|
req->num_pages++;
|
|
data->nr_pages--;
|
|
return 0;
|
|
}
|
|
|
|
static int fuse_readpages(struct file *file, struct address_space *mapping,
|
|
struct list_head *pages, unsigned nr_pages)
|
|
{
|
|
struct inode *inode = mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_fill_data data;
|
|
int err;
|
|
int nr_alloc = min_t(unsigned, nr_pages, FUSE_MAX_PAGES_PER_REQ);
|
|
|
|
err = -EIO;
|
|
if (fuse_is_bad(inode))
|
|
goto out;
|
|
|
|
data.file = file;
|
|
data.inode = inode;
|
|
if (fc->async_read)
|
|
data.req = fuse_get_req_for_background(fc, nr_alloc);
|
|
else
|
|
data.req = fuse_get_req(fc, nr_alloc);
|
|
data.nr_pages = nr_pages;
|
|
err = PTR_ERR(data.req);
|
|
if (IS_ERR(data.req))
|
|
goto out;
|
|
|
|
err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
|
|
if (!err) {
|
|
if (data.req->num_pages)
|
|
fuse_send_readpages(data.req, file);
|
|
else
|
|
fuse_put_request(fc, data.req);
|
|
}
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
|
{
|
|
struct inode *inode = iocb->ki_filp->f_mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_file *ff = iocb->ki_filp->private_data;
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
/*
|
|
* In auto invalidate mode, always update attributes on read.
|
|
* Otherwise, only update if we attempt to read past EOF (to ensure
|
|
* i_size is up to date).
|
|
*/
|
|
if (fc->auto_inval_data ||
|
|
(iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) {
|
|
int err;
|
|
err = fuse_update_attributes(inode, iocb->ki_filp);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (ff->passthrough.filp)
|
|
return fuse_passthrough_read_iter(iocb, to);
|
|
return generic_file_read_iter(iocb, to);
|
|
}
|
|
|
|
static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff,
|
|
loff_t pos, size_t count)
|
|
{
|
|
struct fuse_write_in *inarg = &req->misc.write.in;
|
|
struct fuse_write_out *outarg = &req->misc.write.out;
|
|
|
|
inarg->fh = ff->fh;
|
|
inarg->offset = pos;
|
|
inarg->size = count;
|
|
req->in.h.opcode = FUSE_WRITE;
|
|
req->in.h.nodeid = ff->nodeid;
|
|
req->in.numargs = 2;
|
|
if (ff->fc->minor < 9)
|
|
req->in.args[0].size = FUSE_COMPAT_WRITE_IN_SIZE;
|
|
else
|
|
req->in.args[0].size = sizeof(struct fuse_write_in);
|
|
req->in.args[0].value = inarg;
|
|
req->in.args[1].size = count;
|
|
req->out.numargs = 1;
|
|
req->out.args[0].size = sizeof(struct fuse_write_out);
|
|
req->out.args[0].value = outarg;
|
|
}
|
|
|
|
static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io,
|
|
loff_t pos, size_t count, fl_owner_t owner)
|
|
{
|
|
struct kiocb *iocb = io->iocb;
|
|
struct file *file = iocb->ki_filp;
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_conn *fc = ff->fc;
|
|
struct fuse_write_in *inarg = &req->misc.write.in;
|
|
|
|
fuse_write_fill(req, ff, pos, count);
|
|
inarg->flags = file->f_flags;
|
|
if (iocb->ki_flags & IOCB_DSYNC)
|
|
inarg->flags |= O_DSYNC;
|
|
if (iocb->ki_flags & IOCB_SYNC)
|
|
inarg->flags |= O_SYNC;
|
|
if (owner != NULL) {
|
|
inarg->write_flags |= FUSE_WRITE_LOCKOWNER;
|
|
inarg->lock_owner = fuse_lock_owner_id(fc, owner);
|
|
}
|
|
|
|
if (io->async)
|
|
return fuse_async_req_send(fc, req, count, io);
|
|
|
|
fuse_request_send(fc, req);
|
|
return req->misc.write.out.size;
|
|
}
|
|
|
|
bool fuse_write_update_size(struct inode *inode, loff_t pos)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
bool ret = false;
|
|
|
|
spin_lock(&fc->lock);
|
|
fi->attr_version = ++fc->attr_version;
|
|
if (pos > inode->i_size) {
|
|
i_size_write(inode, pos);
|
|
ret = true;
|
|
}
|
|
spin_unlock(&fc->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static size_t fuse_send_write_pages(struct fuse_req *req, struct kiocb *iocb,
|
|
struct inode *inode, loff_t pos,
|
|
size_t count)
|
|
{
|
|
size_t res;
|
|
unsigned offset;
|
|
unsigned i;
|
|
struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb);
|
|
|
|
for (i = 0; i < req->num_pages; i++)
|
|
fuse_wait_on_page_writeback(inode, req->pages[i]->index);
|
|
|
|
res = fuse_send_write(req, &io, pos, count, NULL);
|
|
|
|
offset = req->page_descs[0].offset;
|
|
count = res;
|
|
for (i = 0; i < req->num_pages; i++) {
|
|
struct page *page = req->pages[i];
|
|
|
|
if (!req->out.h.error && !offset && count >= PAGE_SIZE)
|
|
SetPageUptodate(page);
|
|
|
|
if (count > PAGE_SIZE - offset)
|
|
count -= PAGE_SIZE - offset;
|
|
else
|
|
count = 0;
|
|
offset = 0;
|
|
|
|
unlock_page(page);
|
|
put_page(page);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static ssize_t fuse_fill_write_pages(struct fuse_req *req,
|
|
struct address_space *mapping,
|
|
struct iov_iter *ii, loff_t pos)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(mapping->host);
|
|
unsigned offset = pos & (PAGE_SIZE - 1);
|
|
size_t count = 0;
|
|
int err;
|
|
|
|
req->in.argpages = 1;
|
|
req->page_descs[0].offset = offset;
|
|
|
|
do {
|
|
size_t tmp;
|
|
struct page *page;
|
|
pgoff_t index = pos >> PAGE_SHIFT;
|
|
size_t bytes = min_t(size_t, PAGE_SIZE - offset,
|
|
iov_iter_count(ii));
|
|
|
|
bytes = min_t(size_t, bytes, fc->max_write - count);
|
|
|
|
again:
|
|
err = -EFAULT;
|
|
if (iov_iter_fault_in_readable(ii, bytes))
|
|
break;
|
|
|
|
err = -ENOMEM;
|
|
page = grab_cache_page_write_begin(mapping, index, 0);
|
|
if (!page)
|
|
break;
|
|
|
|
if (mapping_writably_mapped(mapping))
|
|
flush_dcache_page(page);
|
|
|
|
tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes);
|
|
flush_dcache_page(page);
|
|
|
|
iov_iter_advance(ii, tmp);
|
|
if (!tmp) {
|
|
unlock_page(page);
|
|
put_page(page);
|
|
bytes = min(bytes, iov_iter_single_seg_count(ii));
|
|
goto again;
|
|
}
|
|
|
|
err = 0;
|
|
req->pages[req->num_pages] = page;
|
|
req->page_descs[req->num_pages].length = tmp;
|
|
req->num_pages++;
|
|
|
|
count += tmp;
|
|
pos += tmp;
|
|
offset += tmp;
|
|
if (offset == PAGE_SIZE)
|
|
offset = 0;
|
|
|
|
if (!fc->big_writes)
|
|
break;
|
|
} while (iov_iter_count(ii) && count < fc->max_write &&
|
|
req->num_pages < req->max_pages && offset == 0);
|
|
|
|
return count > 0 ? count : err;
|
|
}
|
|
|
|
static inline unsigned fuse_wr_pages(loff_t pos, size_t len)
|
|
{
|
|
return min_t(unsigned,
|
|
((pos + len - 1) >> PAGE_SHIFT) -
|
|
(pos >> PAGE_SHIFT) + 1,
|
|
FUSE_MAX_PAGES_PER_REQ);
|
|
}
|
|
|
|
static ssize_t fuse_perform_write(struct kiocb *iocb,
|
|
struct address_space *mapping,
|
|
struct iov_iter *ii, loff_t pos)
|
|
{
|
|
struct inode *inode = mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
int err = 0;
|
|
ssize_t res = 0;
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
if (inode->i_size < pos + iov_iter_count(ii))
|
|
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
|
|
|
|
do {
|
|
struct fuse_req *req;
|
|
ssize_t count;
|
|
unsigned nr_pages = fuse_wr_pages(pos, iov_iter_count(ii));
|
|
|
|
req = fuse_get_req(fc, nr_pages);
|
|
if (IS_ERR(req)) {
|
|
err = PTR_ERR(req);
|
|
break;
|
|
}
|
|
|
|
count = fuse_fill_write_pages(req, mapping, ii, pos);
|
|
if (count <= 0) {
|
|
err = count;
|
|
} else {
|
|
size_t num_written;
|
|
|
|
num_written = fuse_send_write_pages(req, iocb, inode,
|
|
pos, count);
|
|
err = req->out.h.error;
|
|
if (!err) {
|
|
res += num_written;
|
|
pos += num_written;
|
|
|
|
/* break out of the loop on short write */
|
|
if (num_written != count)
|
|
err = -EIO;
|
|
}
|
|
}
|
|
fuse_put_request(fc, req);
|
|
} while (!err && iov_iter_count(ii));
|
|
|
|
if (res > 0)
|
|
fuse_write_update_size(inode, pos);
|
|
|
|
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
|
|
fuse_invalidate_attr(inode);
|
|
|
|
return res > 0 ? res : err;
|
|
}
|
|
|
|
static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|
{
|
|
struct file *file = iocb->ki_filp;
|
|
struct address_space *mapping = file->f_mapping;
|
|
ssize_t written = 0;
|
|
ssize_t written_buffered = 0;
|
|
struct inode *inode = mapping->host;
|
|
ssize_t err;
|
|
loff_t endbyte = 0;
|
|
struct fuse_file *ff = file->private_data;
|
|
|
|
if (ff->passthrough.filp)
|
|
return fuse_passthrough_write_iter(iocb, from);
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
if (get_fuse_conn(inode)->writeback_cache) {
|
|
/* Update size (EOF optimization) and mode (SUID clearing) */
|
|
err = fuse_update_attributes(mapping->host, file);
|
|
if (err)
|
|
return err;
|
|
|
|
return generic_file_write_iter(iocb, from);
|
|
}
|
|
|
|
inode_lock(inode);
|
|
|
|
/* We can write back this queue in page reclaim */
|
|
current->backing_dev_info = inode_to_bdi(inode);
|
|
|
|
err = generic_write_checks(iocb, from);
|
|
if (err <= 0)
|
|
goto out;
|
|
|
|
err = file_remove_privs(file);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = file_update_time(file);
|
|
if (err)
|
|
goto out;
|
|
|
|
if (iocb->ki_flags & IOCB_DIRECT) {
|
|
loff_t pos = iocb->ki_pos;
|
|
written = generic_file_direct_write(iocb, from);
|
|
if (written < 0 || !iov_iter_count(from))
|
|
goto out;
|
|
|
|
pos += written;
|
|
|
|
written_buffered = fuse_perform_write(iocb, mapping, from, pos);
|
|
if (written_buffered < 0) {
|
|
err = written_buffered;
|
|
goto out;
|
|
}
|
|
endbyte = pos + written_buffered - 1;
|
|
|
|
err = filemap_write_and_wait_range(file->f_mapping, pos,
|
|
endbyte);
|
|
if (err)
|
|
goto out;
|
|
|
|
invalidate_mapping_pages(file->f_mapping,
|
|
pos >> PAGE_SHIFT,
|
|
endbyte >> PAGE_SHIFT);
|
|
|
|
written += written_buffered;
|
|
iocb->ki_pos = pos + written_buffered;
|
|
} else {
|
|
written = fuse_perform_write(iocb, mapping, from, iocb->ki_pos);
|
|
if (written >= 0)
|
|
iocb->ki_pos += written;
|
|
}
|
|
out:
|
|
current->backing_dev_info = NULL;
|
|
inode_unlock(inode);
|
|
if (written > 0)
|
|
written = generic_write_sync(iocb, written);
|
|
|
|
return written ? written : err;
|
|
}
|
|
|
|
static inline void fuse_page_descs_length_init(struct fuse_req *req,
|
|
unsigned index, unsigned nr_pages)
|
|
{
|
|
int i;
|
|
|
|
for (i = index; i < index + nr_pages; i++)
|
|
req->page_descs[i].length = PAGE_SIZE -
|
|
req->page_descs[i].offset;
|
|
}
|
|
|
|
static inline unsigned long fuse_get_user_addr(const struct iov_iter *ii)
|
|
{
|
|
return (unsigned long)ii->iov->iov_base + ii->iov_offset;
|
|
}
|
|
|
|
static inline size_t fuse_get_frag_size(const struct iov_iter *ii,
|
|
size_t max_size)
|
|
{
|
|
return min(iov_iter_single_seg_count(ii), max_size);
|
|
}
|
|
|
|
static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
|
|
size_t *nbytesp, int write)
|
|
{
|
|
size_t nbytes = 0; /* # bytes already packed in req */
|
|
ssize_t ret = 0;
|
|
|
|
/* Special case for kernel I/O: can copy directly into the buffer */
|
|
if (ii->type & ITER_KVEC) {
|
|
unsigned long user_addr = fuse_get_user_addr(ii);
|
|
size_t frag_size = fuse_get_frag_size(ii, *nbytesp);
|
|
|
|
if (write)
|
|
req->in.args[1].value = (void *) user_addr;
|
|
else
|
|
req->out.args[0].value = (void *) user_addr;
|
|
|
|
iov_iter_advance(ii, frag_size);
|
|
*nbytesp = frag_size;
|
|
return 0;
|
|
}
|
|
|
|
while (nbytes < *nbytesp && req->num_pages < req->max_pages) {
|
|
unsigned npages;
|
|
size_t start;
|
|
ret = iov_iter_get_pages(ii, &req->pages[req->num_pages],
|
|
*nbytesp - nbytes,
|
|
req->max_pages - req->num_pages,
|
|
&start);
|
|
if (ret < 0)
|
|
break;
|
|
|
|
iov_iter_advance(ii, ret);
|
|
nbytes += ret;
|
|
|
|
ret += start;
|
|
npages = (ret + PAGE_SIZE - 1) / PAGE_SIZE;
|
|
|
|
req->page_descs[req->num_pages].offset = start;
|
|
fuse_page_descs_length_init(req, req->num_pages, npages);
|
|
|
|
req->num_pages += npages;
|
|
req->page_descs[req->num_pages - 1].length -=
|
|
(PAGE_SIZE - ret) & (PAGE_SIZE - 1);
|
|
}
|
|
|
|
req->user_pages = true;
|
|
if (write)
|
|
req->in.argpages = 1;
|
|
else
|
|
req->out.argpages = 1;
|
|
|
|
*nbytesp = nbytes;
|
|
|
|
return ret < 0 ? ret : 0;
|
|
}
|
|
|
|
static inline int fuse_iter_npages(const struct iov_iter *ii_p)
|
|
{
|
|
return iov_iter_npages(ii_p, FUSE_MAX_PAGES_PER_REQ);
|
|
}
|
|
|
|
ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
|
|
loff_t *ppos, int flags)
|
|
{
|
|
int write = flags & FUSE_DIO_WRITE;
|
|
int cuse = flags & FUSE_DIO_CUSE;
|
|
struct file *file = io->iocb->ki_filp;
|
|
struct inode *inode = file->f_mapping->host;
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_conn *fc = ff->fc;
|
|
size_t nmax = write ? fc->max_write : fc->max_read;
|
|
loff_t pos = *ppos;
|
|
size_t count = iov_iter_count(iter);
|
|
pgoff_t idx_from = pos >> PAGE_SHIFT;
|
|
pgoff_t idx_to = (pos + count - 1) >> PAGE_SHIFT;
|
|
ssize_t res = 0;
|
|
struct fuse_req *req;
|
|
int err = 0;
|
|
|
|
if (io->async)
|
|
req = fuse_get_req_for_background(fc, fuse_iter_npages(iter));
|
|
else
|
|
req = fuse_get_req(fc, fuse_iter_npages(iter));
|
|
if (IS_ERR(req))
|
|
return PTR_ERR(req);
|
|
|
|
if (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) {
|
|
if (!write)
|
|
inode_lock(inode);
|
|
fuse_sync_writes(inode);
|
|
if (!write)
|
|
inode_unlock(inode);
|
|
}
|
|
|
|
io->should_dirty = !write && iter_is_iovec(iter);
|
|
while (count) {
|
|
size_t nres;
|
|
fl_owner_t owner = current->files;
|
|
size_t nbytes = min(count, nmax);
|
|
err = fuse_get_user_pages(req, iter, &nbytes, write);
|
|
if (err && !nbytes)
|
|
break;
|
|
|
|
if (write)
|
|
nres = fuse_send_write(req, io, pos, nbytes, owner);
|
|
else
|
|
nres = fuse_send_read(req, io, pos, nbytes, owner);
|
|
|
|
if (!io->async)
|
|
fuse_release_user_pages(req, io->should_dirty);
|
|
if (req->out.h.error) {
|
|
err = req->out.h.error;
|
|
break;
|
|
} else if (nres > nbytes) {
|
|
res = 0;
|
|
err = -EIO;
|
|
break;
|
|
}
|
|
count -= nres;
|
|
res += nres;
|
|
pos += nres;
|
|
if (nres != nbytes)
|
|
break;
|
|
if (count) {
|
|
fuse_put_request(fc, req);
|
|
if (io->async)
|
|
req = fuse_get_req_for_background(fc,
|
|
fuse_iter_npages(iter));
|
|
else
|
|
req = fuse_get_req(fc, fuse_iter_npages(iter));
|
|
if (IS_ERR(req))
|
|
break;
|
|
}
|
|
}
|
|
if (!IS_ERR(req))
|
|
fuse_put_request(fc, req);
|
|
if (res > 0)
|
|
*ppos = pos;
|
|
|
|
return res > 0 ? res : err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_direct_io);
|
|
|
|
static ssize_t __fuse_direct_read(struct fuse_io_priv *io,
|
|
struct iov_iter *iter,
|
|
loff_t *ppos)
|
|
{
|
|
ssize_t res;
|
|
struct inode *inode = file_inode(io->iocb->ki_filp);
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
res = fuse_direct_io(io, iter, ppos, 0);
|
|
|
|
fuse_invalidate_attr(inode);
|
|
|
|
return res;
|
|
}
|
|
|
|
static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
|
{
|
|
struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb);
|
|
return __fuse_direct_read(&io, to, &iocb->ki_pos);
|
|
}
|
|
|
|
static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|
{
|
|
struct inode *inode = file_inode(iocb->ki_filp);
|
|
struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb);
|
|
ssize_t res;
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
/* Don't allow parallel writes to the same file */
|
|
inode_lock(inode);
|
|
res = generic_write_checks(iocb, from);
|
|
if (res > 0)
|
|
res = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE);
|
|
fuse_invalidate_attr(inode);
|
|
if (res > 0)
|
|
fuse_write_update_size(inode, iocb->ki_pos);
|
|
inode_unlock(inode);
|
|
|
|
return res;
|
|
}
|
|
|
|
static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < req->num_pages; i++)
|
|
__free_page(req->pages[i]);
|
|
|
|
if (req->ff)
|
|
fuse_file_put(req->ff, false, false);
|
|
}
|
|
|
|
static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req)
|
|
{
|
|
struct inode *inode = req->inode;
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
struct backing_dev_info *bdi = inode_to_bdi(inode);
|
|
int i;
|
|
|
|
list_del(&req->writepages_entry);
|
|
for (i = 0; i < req->num_pages; i++) {
|
|
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
|
|
dec_node_page_state(req->pages[i], NR_WRITEBACK_TEMP);
|
|
wb_writeout_inc(&bdi->wb);
|
|
}
|
|
wake_up(&fi->page_waitq);
|
|
}
|
|
|
|
/* Called under fc->lock, may release and reacquire it */
|
|
static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req,
|
|
loff_t size)
|
|
__releases(fc->lock)
|
|
__acquires(fc->lock)
|
|
{
|
|
struct fuse_inode *fi = get_fuse_inode(req->inode);
|
|
struct fuse_write_in *inarg = &req->misc.write.in;
|
|
__u64 data_size = req->num_pages * PAGE_SIZE;
|
|
|
|
if (!fc->connected)
|
|
goto out_free;
|
|
|
|
if (inarg->offset + data_size <= size) {
|
|
inarg->size = data_size;
|
|
} else if (inarg->offset < size) {
|
|
inarg->size = size - inarg->offset;
|
|
} else {
|
|
/* Got truncated off completely */
|
|
goto out_free;
|
|
}
|
|
|
|
req->in.args[1].size = inarg->size;
|
|
fi->writectr++;
|
|
fuse_request_send_background_locked(fc, req);
|
|
return;
|
|
|
|
out_free:
|
|
fuse_writepage_finish(fc, req);
|
|
spin_unlock(&fc->lock);
|
|
fuse_writepage_free(fc, req);
|
|
fuse_put_request(fc, req);
|
|
spin_lock(&fc->lock);
|
|
}
|
|
|
|
/*
|
|
* If fi->writectr is positive (no truncate or fsync going on) send
|
|
* all queued writepage requests.
|
|
*
|
|
* Called with fc->lock
|
|
*/
|
|
void fuse_flush_writepages(struct inode *inode)
|
|
__releases(fc->lock)
|
|
__acquires(fc->lock)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
loff_t crop = i_size_read(inode);
|
|
struct fuse_req *req;
|
|
|
|
while (fi->writectr >= 0 && !list_empty(&fi->queued_writes)) {
|
|
req = list_entry(fi->queued_writes.next, struct fuse_req, list);
|
|
list_del_init(&req->list);
|
|
fuse_send_writepage(fc, req, crop);
|
|
}
|
|
}
|
|
|
|
static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req)
|
|
{
|
|
struct inode *inode = req->inode;
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
|
|
mapping_set_error(inode->i_mapping, req->out.h.error);
|
|
spin_lock(&fc->lock);
|
|
while (req->misc.write.next) {
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_write_in *inarg = &req->misc.write.in;
|
|
struct fuse_req *next = req->misc.write.next;
|
|
req->misc.write.next = next->misc.write.next;
|
|
next->misc.write.next = NULL;
|
|
next->ff = fuse_file_get(req->ff);
|
|
list_add(&next->writepages_entry, &fi->writepages);
|
|
|
|
/*
|
|
* Skip fuse_flush_writepages() to make it easy to crop requests
|
|
* based on primary request size.
|
|
*
|
|
* 1st case (trivial): there are no concurrent activities using
|
|
* fuse_set/release_nowrite. Then we're on safe side because
|
|
* fuse_flush_writepages() would call fuse_send_writepage()
|
|
* anyway.
|
|
*
|
|
* 2nd case: someone called fuse_set_nowrite and it is waiting
|
|
* now for completion of all in-flight requests. This happens
|
|
* rarely and no more than once per page, so this should be
|
|
* okay.
|
|
*
|
|
* 3rd case: someone (e.g. fuse_do_setattr()) is in the middle
|
|
* of fuse_set_nowrite..fuse_release_nowrite section. The fact
|
|
* that fuse_set_nowrite returned implies that all in-flight
|
|
* requests were completed along with all of their secondary
|
|
* requests. Further primary requests are blocked by negative
|
|
* writectr. Hence there cannot be any in-flight requests and
|
|
* no invocations of fuse_writepage_end() while we're in
|
|
* fuse_set_nowrite..fuse_release_nowrite section.
|
|
*/
|
|
fuse_send_writepage(fc, next, inarg->offset + inarg->size);
|
|
}
|
|
fi->writectr--;
|
|
fuse_writepage_finish(fc, req);
|
|
spin_unlock(&fc->lock);
|
|
fuse_writepage_free(fc, req);
|
|
}
|
|
|
|
static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc,
|
|
struct fuse_inode *fi)
|
|
{
|
|
struct fuse_file *ff = NULL;
|
|
|
|
spin_lock(&fc->lock);
|
|
if (!list_empty(&fi->write_files)) {
|
|
ff = list_entry(fi->write_files.next, struct fuse_file,
|
|
write_entry);
|
|
fuse_file_get(ff);
|
|
}
|
|
spin_unlock(&fc->lock);
|
|
|
|
return ff;
|
|
}
|
|
|
|
static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,
|
|
struct fuse_inode *fi)
|
|
{
|
|
struct fuse_file *ff = __fuse_write_file_get(fc, fi);
|
|
WARN_ON(!ff);
|
|
return ff;
|
|
}
|
|
|
|
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
struct fuse_file *ff;
|
|
int err;
|
|
|
|
/*
|
|
* Inode is always written before the last reference is dropped and
|
|
* hence this should not be reached from reclaim.
|
|
*
|
|
* Writing back the inode from reclaim can deadlock if the request
|
|
* processing itself needs an allocation. Allocations triggering
|
|
* reclaim while serving a request can't be prevented, because it can
|
|
* involve any number of unrelated userspace processes.
|
|
*/
|
|
WARN_ON(wbc->for_reclaim);
|
|
|
|
ff = __fuse_write_file_get(fc, fi);
|
|
err = fuse_flush_times(inode, ff);
|
|
if (ff)
|
|
fuse_file_put(ff, false, false);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int fuse_writepage_locked(struct page *page)
|
|
{
|
|
struct address_space *mapping = page->mapping;
|
|
struct inode *inode = mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
struct fuse_req *req;
|
|
struct page *tmp_page;
|
|
int error = -ENOMEM;
|
|
|
|
set_page_writeback(page);
|
|
|
|
req = fuse_request_alloc_nofs(1);
|
|
if (!req)
|
|
goto err;
|
|
|
|
/* writeback always goes to bg_queue */
|
|
__set_bit(FR_BACKGROUND, &req->flags);
|
|
tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
|
if (!tmp_page)
|
|
goto err_free;
|
|
|
|
error = -EIO;
|
|
req->ff = fuse_write_file_get(fc, fi);
|
|
if (!req->ff)
|
|
goto err_nofile;
|
|
|
|
fuse_write_fill(req, req->ff, page_offset(page), 0);
|
|
|
|
copy_highpage(tmp_page, page);
|
|
req->misc.write.in.write_flags |= FUSE_WRITE_CACHE;
|
|
req->misc.write.next = NULL;
|
|
req->in.argpages = 1;
|
|
req->num_pages = 1;
|
|
req->pages[0] = tmp_page;
|
|
req->page_descs[0].offset = 0;
|
|
req->page_descs[0].length = PAGE_SIZE;
|
|
req->end = fuse_writepage_end;
|
|
req->inode = inode;
|
|
|
|
inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
|
|
inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP);
|
|
|
|
spin_lock(&fc->lock);
|
|
list_add(&req->writepages_entry, &fi->writepages);
|
|
list_add_tail(&req->list, &fi->queued_writes);
|
|
fuse_flush_writepages(inode);
|
|
spin_unlock(&fc->lock);
|
|
|
|
end_page_writeback(page);
|
|
|
|
return 0;
|
|
|
|
err_nofile:
|
|
__free_page(tmp_page);
|
|
err_free:
|
|
fuse_request_free(req);
|
|
err:
|
|
mapping_set_error(page->mapping, error);
|
|
end_page_writeback(page);
|
|
return error;
|
|
}
|
|
|
|
static int fuse_writepage(struct page *page, struct writeback_control *wbc)
|
|
{
|
|
int err;
|
|
|
|
if (fuse_page_is_writeback(page->mapping->host, page->index)) {
|
|
/*
|
|
* ->writepages() should be called for sync() and friends. We
|
|
* should only get here on direct reclaim and then we are
|
|
* allowed to skip a page which is already in flight
|
|
*/
|
|
WARN_ON(wbc->sync_mode == WB_SYNC_ALL);
|
|
|
|
redirty_page_for_writepage(wbc, page);
|
|
unlock_page(page);
|
|
return 0;
|
|
}
|
|
|
|
err = fuse_writepage_locked(page);
|
|
unlock_page(page);
|
|
|
|
return err;
|
|
}
|
|
|
|
struct fuse_fill_wb_data {
|
|
struct fuse_req *req;
|
|
struct fuse_file *ff;
|
|
struct inode *inode;
|
|
struct page **orig_pages;
|
|
};
|
|
|
|
static void fuse_writepages_send(struct fuse_fill_wb_data *data)
|
|
{
|
|
struct fuse_req *req = data->req;
|
|
struct inode *inode = data->inode;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
int num_pages = req->num_pages;
|
|
int i;
|
|
|
|
req->ff = fuse_file_get(data->ff);
|
|
spin_lock(&fc->lock);
|
|
list_add_tail(&req->list, &fi->queued_writes);
|
|
fuse_flush_writepages(inode);
|
|
spin_unlock(&fc->lock);
|
|
|
|
for (i = 0; i < num_pages; i++)
|
|
end_page_writeback(data->orig_pages[i]);
|
|
}
|
|
|
|
static bool fuse_writepage_in_flight(struct fuse_req *new_req,
|
|
struct page *page)
|
|
{
|
|
struct fuse_conn *fc = get_fuse_conn(new_req->inode);
|
|
struct fuse_inode *fi = get_fuse_inode(new_req->inode);
|
|
struct fuse_req *tmp;
|
|
struct fuse_req *old_req;
|
|
bool found = false;
|
|
pgoff_t curr_index;
|
|
|
|
BUG_ON(new_req->num_pages != 0);
|
|
|
|
spin_lock(&fc->lock);
|
|
list_del(&new_req->writepages_entry);
|
|
list_for_each_entry(old_req, &fi->writepages, writepages_entry) {
|
|
BUG_ON(old_req->inode != new_req->inode);
|
|
curr_index = old_req->misc.write.in.offset >> PAGE_SHIFT;
|
|
if (curr_index <= page->index &&
|
|
page->index < curr_index + old_req->num_pages) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
list_add(&new_req->writepages_entry, &fi->writepages);
|
|
goto out_unlock;
|
|
}
|
|
|
|
new_req->num_pages = 1;
|
|
for (tmp = old_req; tmp != NULL; tmp = tmp->misc.write.next) {
|
|
BUG_ON(tmp->inode != new_req->inode);
|
|
curr_index = tmp->misc.write.in.offset >> PAGE_SHIFT;
|
|
if (tmp->num_pages == 1 &&
|
|
curr_index == page->index) {
|
|
old_req = tmp;
|
|
}
|
|
}
|
|
|
|
if (old_req->num_pages == 1 && test_bit(FR_PENDING, &old_req->flags)) {
|
|
struct backing_dev_info *bdi = inode_to_bdi(page->mapping->host);
|
|
|
|
copy_highpage(old_req->pages[0], page);
|
|
spin_unlock(&fc->lock);
|
|
|
|
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
|
|
dec_node_page_state(new_req->pages[0], NR_WRITEBACK_TEMP);
|
|
wb_writeout_inc(&bdi->wb);
|
|
fuse_writepage_free(fc, new_req);
|
|
fuse_request_free(new_req);
|
|
goto out;
|
|
} else {
|
|
new_req->misc.write.next = old_req->misc.write.next;
|
|
old_req->misc.write.next = new_req;
|
|
}
|
|
out_unlock:
|
|
spin_unlock(&fc->lock);
|
|
out:
|
|
return found;
|
|
}
|
|
|
|
static int fuse_writepages_fill(struct page *page,
|
|
struct writeback_control *wbc, void *_data)
|
|
{
|
|
struct fuse_fill_wb_data *data = _data;
|
|
struct fuse_req *req = data->req;
|
|
struct inode *inode = data->inode;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct page *tmp_page;
|
|
bool is_writeback;
|
|
int err;
|
|
|
|
if (!data->ff) {
|
|
err = -EIO;
|
|
data->ff = fuse_write_file_get(fc, get_fuse_inode(inode));
|
|
if (!data->ff)
|
|
goto out_unlock;
|
|
}
|
|
|
|
/*
|
|
* Being under writeback is unlikely but possible. For example direct
|
|
* read to an mmaped fuse file will set the page dirty twice; once when
|
|
* the pages are faulted with get_user_pages(), and then after the read
|
|
* completed.
|
|
*/
|
|
is_writeback = fuse_page_is_writeback(inode, page->index);
|
|
|
|
if (req && req->num_pages &&
|
|
(is_writeback || req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
|
|
(req->num_pages + 1) * PAGE_SIZE > fc->max_write ||
|
|
data->orig_pages[req->num_pages - 1]->index + 1 != page->index)) {
|
|
fuse_writepages_send(data);
|
|
data->req = NULL;
|
|
}
|
|
err = -ENOMEM;
|
|
tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
|
if (!tmp_page)
|
|
goto out_unlock;
|
|
|
|
/*
|
|
* The page must not be redirtied until the writeout is completed
|
|
* (i.e. userspace has sent a reply to the write request). Otherwise
|
|
* there could be more than one temporary page instance for each real
|
|
* page.
|
|
*
|
|
* This is ensured by holding the page lock in page_mkwrite() while
|
|
* checking fuse_page_is_writeback(). We already hold the page lock
|
|
* since clear_page_dirty_for_io() and keep it held until we add the
|
|
* request to the fi->writepages list and increment req->num_pages.
|
|
* After this fuse_page_is_writeback() will indicate that the page is
|
|
* under writeback, so we can release the page lock.
|
|
*/
|
|
if (data->req == NULL) {
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
|
|
err = -ENOMEM;
|
|
req = fuse_request_alloc_nofs(FUSE_MAX_PAGES_PER_REQ);
|
|
if (!req) {
|
|
__free_page(tmp_page);
|
|
goto out_unlock;
|
|
}
|
|
|
|
fuse_write_fill(req, data->ff, page_offset(page), 0);
|
|
req->misc.write.in.write_flags |= FUSE_WRITE_CACHE;
|
|
req->misc.write.next = NULL;
|
|
req->in.argpages = 1;
|
|
__set_bit(FR_BACKGROUND, &req->flags);
|
|
req->num_pages = 0;
|
|
req->end = fuse_writepage_end;
|
|
req->inode = inode;
|
|
|
|
spin_lock(&fc->lock);
|
|
list_add(&req->writepages_entry, &fi->writepages);
|
|
spin_unlock(&fc->lock);
|
|
|
|
data->req = req;
|
|
}
|
|
set_page_writeback(page);
|
|
|
|
copy_highpage(tmp_page, page);
|
|
req->pages[req->num_pages] = tmp_page;
|
|
req->page_descs[req->num_pages].offset = 0;
|
|
req->page_descs[req->num_pages].length = PAGE_SIZE;
|
|
|
|
inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
|
|
inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP);
|
|
|
|
err = 0;
|
|
if (is_writeback && fuse_writepage_in_flight(req, page)) {
|
|
end_page_writeback(page);
|
|
data->req = NULL;
|
|
goto out_unlock;
|
|
}
|
|
data->orig_pages[req->num_pages] = page;
|
|
|
|
/*
|
|
* Protected by fc->lock against concurrent access by
|
|
* fuse_page_is_writeback().
|
|
*/
|
|
spin_lock(&fc->lock);
|
|
req->num_pages++;
|
|
spin_unlock(&fc->lock);
|
|
|
|
out_unlock:
|
|
unlock_page(page);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int fuse_writepages(struct address_space *mapping,
|
|
struct writeback_control *wbc)
|
|
{
|
|
struct inode *inode = mapping->host;
|
|
struct fuse_fill_wb_data data;
|
|
int err;
|
|
|
|
err = -EIO;
|
|
if (fuse_is_bad(inode))
|
|
goto out;
|
|
|
|
data.inode = inode;
|
|
data.req = NULL;
|
|
data.ff = NULL;
|
|
|
|
err = -ENOMEM;
|
|
data.orig_pages = kcalloc(FUSE_MAX_PAGES_PER_REQ,
|
|
sizeof(struct page *),
|
|
GFP_NOFS);
|
|
if (!data.orig_pages)
|
|
goto out;
|
|
|
|
err = write_cache_pages(mapping, wbc, fuse_writepages_fill, &data);
|
|
if (data.req) {
|
|
/* Ignore errors if we can write at least one page */
|
|
BUG_ON(!data.req->num_pages);
|
|
fuse_writepages_send(&data);
|
|
err = 0;
|
|
}
|
|
if (data.ff)
|
|
fuse_file_put(data.ff, false, false);
|
|
|
|
kfree(data.orig_pages);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* It's worthy to make sure that space is reserved on disk for the write,
|
|
* but how to implement it without killing performance need more thinking.
|
|
*/
|
|
static int fuse_write_begin(struct file *file, struct address_space *mapping,
|
|
loff_t pos, unsigned len, unsigned flags,
|
|
struct page **pagep, void **fsdata)
|
|
{
|
|
pgoff_t index = pos >> PAGE_SHIFT;
|
|
struct fuse_conn *fc = get_fuse_conn(file_inode(file));
|
|
struct page *page;
|
|
loff_t fsize;
|
|
int err = -ENOMEM;
|
|
|
|
WARN_ON(!fc->writeback_cache);
|
|
|
|
page = grab_cache_page_write_begin(mapping, index, flags);
|
|
if (!page)
|
|
goto error;
|
|
|
|
fuse_wait_on_page_writeback(mapping->host, page->index);
|
|
|
|
if (PageUptodate(page) || len == PAGE_SIZE)
|
|
goto success;
|
|
/*
|
|
* Check if the start this page comes after the end of file, in which
|
|
* case the readpage can be optimized away.
|
|
*/
|
|
fsize = i_size_read(mapping->host);
|
|
if (fsize <= (pos & PAGE_MASK)) {
|
|
size_t off = pos & ~PAGE_MASK;
|
|
if (off)
|
|
zero_user_segment(page, 0, off);
|
|
goto success;
|
|
}
|
|
err = fuse_do_readpage(file, page);
|
|
if (err)
|
|
goto cleanup;
|
|
success:
|
|
*pagep = page;
|
|
return 0;
|
|
|
|
cleanup:
|
|
unlock_page(page);
|
|
put_page(page);
|
|
error:
|
|
return err;
|
|
}
|
|
|
|
static int fuse_write_end(struct file *file, struct address_space *mapping,
|
|
loff_t pos, unsigned len, unsigned copied,
|
|
struct page *page, void *fsdata)
|
|
{
|
|
struct inode *inode = page->mapping->host;
|
|
|
|
/* Haven't copied anything? Skip zeroing, size extending, dirtying. */
|
|
if (!copied)
|
|
goto unlock;
|
|
|
|
if (!PageUptodate(page)) {
|
|
/* Zero any unwritten bytes at the end of the page */
|
|
size_t endoff = (pos + copied) & ~PAGE_MASK;
|
|
if (endoff)
|
|
zero_user_segment(page, endoff, PAGE_SIZE);
|
|
SetPageUptodate(page);
|
|
}
|
|
|
|
fuse_write_update_size(inode, pos + copied);
|
|
set_page_dirty(page);
|
|
|
|
unlock:
|
|
unlock_page(page);
|
|
put_page(page);
|
|
|
|
return copied;
|
|
}
|
|
|
|
static int fuse_launder_page(struct page *page)
|
|
{
|
|
int err = 0;
|
|
if (clear_page_dirty_for_io(page)) {
|
|
struct inode *inode = page->mapping->host;
|
|
err = fuse_writepage_locked(page);
|
|
if (!err)
|
|
fuse_wait_on_page_writeback(inode, page->index);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Write back dirty pages now, because there may not be any suitable
|
|
* open files later
|
|
*/
|
|
static void fuse_vma_close(struct vm_area_struct *vma)
|
|
{
|
|
filemap_write_and_wait(vma->vm_file->f_mapping);
|
|
}
|
|
|
|
/*
|
|
* Wait for writeback against this page to complete before allowing it
|
|
* to be marked dirty again, and hence written back again, possibly
|
|
* before the previous writepage completed.
|
|
*
|
|
* Block here, instead of in ->writepage(), so that the userspace fs
|
|
* can only block processes actually operating on the filesystem.
|
|
*
|
|
* Otherwise unprivileged userspace fs would be able to block
|
|
* unrelated:
|
|
*
|
|
* - page migration
|
|
* - sync(2)
|
|
* - try_to_free_pages() with order > PAGE_ALLOC_COSTLY_ORDER
|
|
*/
|
|
static vm_fault_t fuse_page_mkwrite(struct vm_fault *vmf)
|
|
{
|
|
struct page *page = vmf->page;
|
|
struct inode *inode = file_inode(vmf->vma->vm_file);
|
|
|
|
file_update_time(vmf->vma->vm_file);
|
|
lock_page(page);
|
|
if (page->mapping != inode->i_mapping) {
|
|
unlock_page(page);
|
|
return VM_FAULT_NOPAGE;
|
|
}
|
|
|
|
fuse_wait_on_page_writeback(inode, page->index);
|
|
return VM_FAULT_LOCKED;
|
|
}
|
|
|
|
static const struct vm_operations_struct fuse_file_vm_ops = {
|
|
.close = fuse_vma_close,
|
|
.fault = filemap_fault,
|
|
.map_pages = filemap_map_pages,
|
|
.page_mkwrite = fuse_page_mkwrite,
|
|
};
|
|
|
|
static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
|
|
{
|
|
struct fuse_file *ff = file->private_data;
|
|
|
|
if (ff->passthrough.filp)
|
|
return fuse_passthrough_mmap(file, vma);
|
|
|
|
if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
|
|
fuse_link_write_file(file);
|
|
|
|
file_accessed(file);
|
|
vma->vm_ops = &fuse_file_vm_ops;
|
|
return 0;
|
|
}
|
|
|
|
static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma)
|
|
{
|
|
/* Can't provide the coherency needed for MAP_SHARED */
|
|
if (vma->vm_flags & VM_MAYSHARE)
|
|
return -ENODEV;
|
|
|
|
invalidate_inode_pages2(file->f_mapping);
|
|
|
|
return generic_file_mmap(file, vma);
|
|
}
|
|
|
|
static int convert_fuse_file_lock(struct fuse_conn *fc,
|
|
const struct fuse_file_lock *ffl,
|
|
struct file_lock *fl)
|
|
{
|
|
switch (ffl->type) {
|
|
case F_UNLCK:
|
|
break;
|
|
|
|
case F_RDLCK:
|
|
case F_WRLCK:
|
|
if (ffl->start > OFFSET_MAX || ffl->end > OFFSET_MAX ||
|
|
ffl->end < ffl->start)
|
|
return -EIO;
|
|
|
|
fl->fl_start = ffl->start;
|
|
fl->fl_end = ffl->end;
|
|
|
|
/*
|
|
* Convert pid into init's pid namespace. The locks API will
|
|
* translate it into the caller's pid namespace.
|
|
*/
|
|
rcu_read_lock();
|
|
fl->fl_pid = pid_nr_ns(find_pid_ns(ffl->pid, fc->pid_ns), &init_pid_ns);
|
|
rcu_read_unlock();
|
|
break;
|
|
|
|
default:
|
|
return -EIO;
|
|
}
|
|
fl->fl_type = ffl->type;
|
|
return 0;
|
|
}
|
|
|
|
static void fuse_lk_fill(struct fuse_args *args, struct file *file,
|
|
const struct file_lock *fl, int opcode, pid_t pid,
|
|
int flock, struct fuse_lk_in *inarg)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_file *ff = file->private_data;
|
|
|
|
memset(inarg, 0, sizeof(*inarg));
|
|
inarg->fh = ff->fh;
|
|
inarg->owner = fuse_lock_owner_id(fc, fl->fl_owner);
|
|
inarg->lk.start = fl->fl_start;
|
|
inarg->lk.end = fl->fl_end;
|
|
inarg->lk.type = fl->fl_type;
|
|
inarg->lk.pid = pid;
|
|
if (flock)
|
|
inarg->lk_flags |= FUSE_LK_FLOCK;
|
|
args->in.h.opcode = opcode;
|
|
args->in.h.nodeid = get_node_id(inode);
|
|
args->in.numargs = 1;
|
|
args->in.args[0].size = sizeof(*inarg);
|
|
args->in.args[0].value = inarg;
|
|
}
|
|
|
|
static int fuse_getlk(struct file *file, struct file_lock *fl)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
FUSE_ARGS(args);
|
|
struct fuse_lk_in inarg;
|
|
struct fuse_lk_out outarg;
|
|
int err;
|
|
|
|
fuse_lk_fill(&args, file, fl, FUSE_GETLK, 0, 0, &inarg);
|
|
args.out.numargs = 1;
|
|
args.out.args[0].size = sizeof(outarg);
|
|
args.out.args[0].value = &outarg;
|
|
err = fuse_simple_request(fc, &args);
|
|
if (!err)
|
|
err = convert_fuse_file_lock(fc, &outarg.lk, fl);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
FUSE_ARGS(args);
|
|
struct fuse_lk_in inarg;
|
|
int opcode = (fl->fl_flags & FL_SLEEP) ? FUSE_SETLKW : FUSE_SETLK;
|
|
struct pid *pid = fl->fl_type != F_UNLCK ? task_tgid(current) : NULL;
|
|
pid_t pid_nr = pid_nr_ns(pid, fc->pid_ns);
|
|
int err;
|
|
|
|
if (fl->fl_lmops && fl->fl_lmops->lm_grant) {
|
|
/* NLM needs asynchronous locks, which we don't support yet */
|
|
return -ENOLCK;
|
|
}
|
|
|
|
/* Unlock on close is handled by the flush method */
|
|
if ((fl->fl_flags & FL_CLOSE_POSIX) == FL_CLOSE_POSIX)
|
|
return 0;
|
|
|
|
fuse_lk_fill(&args, file, fl, opcode, pid_nr, flock, &inarg);
|
|
err = fuse_simple_request(fc, &args);
|
|
|
|
/* locking is restartable */
|
|
if (err == -EINTR)
|
|
err = -ERESTARTSYS;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
int err;
|
|
|
|
if (cmd == F_CANCELLK) {
|
|
err = 0;
|
|
} else if (cmd == F_GETLK) {
|
|
if (fc->no_lock) {
|
|
posix_test_lock(file, fl);
|
|
err = 0;
|
|
} else
|
|
err = fuse_getlk(file, fl);
|
|
} else {
|
|
if (fc->no_lock)
|
|
err = posix_lock_file(file, fl, NULL);
|
|
else
|
|
err = fuse_setlk(file, fl, 0);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
int err;
|
|
|
|
if (fc->no_flock) {
|
|
err = locks_lock_file_wait(file, fl);
|
|
} else {
|
|
struct fuse_file *ff = file->private_data;
|
|
|
|
/* emulate flock with POSIX locks */
|
|
ff->flock = true;
|
|
err = fuse_setlk(file, fl, 1);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
|
|
{
|
|
struct inode *inode = mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
FUSE_ARGS(args);
|
|
struct fuse_bmap_in inarg;
|
|
struct fuse_bmap_out outarg;
|
|
int err;
|
|
|
|
if (!inode->i_sb->s_bdev || fc->no_bmap)
|
|
return 0;
|
|
|
|
memset(&inarg, 0, sizeof(inarg));
|
|
inarg.block = block;
|
|
inarg.blocksize = inode->i_sb->s_blocksize;
|
|
args.in.h.opcode = FUSE_BMAP;
|
|
args.in.h.nodeid = get_node_id(inode);
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
args.out.numargs = 1;
|
|
args.out.args[0].size = sizeof(outarg);
|
|
args.out.args[0].value = &outarg;
|
|
err = fuse_simple_request(fc, &args);
|
|
if (err == -ENOSYS)
|
|
fc->no_bmap = 1;
|
|
|
|
return err ? 0 : outarg.block;
|
|
}
|
|
|
|
static loff_t fuse_lseek(struct file *file, loff_t offset, int whence)
|
|
{
|
|
struct inode *inode = file->f_mapping->host;
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
struct fuse_file *ff = file->private_data;
|
|
FUSE_ARGS(args);
|
|
struct fuse_lseek_in inarg = {
|
|
.fh = ff->fh,
|
|
.offset = offset,
|
|
.whence = whence
|
|
};
|
|
struct fuse_lseek_out outarg;
|
|
int err;
|
|
|
|
if (fc->no_lseek)
|
|
goto fallback;
|
|
|
|
args.in.h.opcode = FUSE_LSEEK;
|
|
args.in.h.nodeid = ff->nodeid;
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
args.out.numargs = 1;
|
|
args.out.args[0].size = sizeof(outarg);
|
|
args.out.args[0].value = &outarg;
|
|
err = fuse_simple_request(fc, &args);
|
|
if (err) {
|
|
if (err == -ENOSYS) {
|
|
fc->no_lseek = 1;
|
|
goto fallback;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes);
|
|
|
|
fallback:
|
|
err = fuse_update_attributes(inode, file);
|
|
if (!err)
|
|
return generic_file_llseek(file, offset, whence);
|
|
else
|
|
return err;
|
|
}
|
|
|
|
static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence)
|
|
{
|
|
loff_t retval;
|
|
struct inode *inode = file_inode(file);
|
|
|
|
switch (whence) {
|
|
case SEEK_SET:
|
|
case SEEK_CUR:
|
|
/* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */
|
|
retval = generic_file_llseek(file, offset, whence);
|
|
break;
|
|
case SEEK_END:
|
|
inode_lock(inode);
|
|
retval = fuse_update_attributes(inode, file);
|
|
if (!retval)
|
|
retval = generic_file_llseek(file, offset, whence);
|
|
inode_unlock(inode);
|
|
break;
|
|
case SEEK_HOLE:
|
|
case SEEK_DATA:
|
|
inode_lock(inode);
|
|
retval = fuse_lseek(file, offset, whence);
|
|
inode_unlock(inode);
|
|
break;
|
|
default:
|
|
retval = -EINVAL;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* CUSE servers compiled on 32bit broke on 64bit kernels because the
|
|
* ABI was defined to be 'struct iovec' which is different on 32bit
|
|
* and 64bit. Fortunately we can determine which structure the server
|
|
* used from the size of the reply.
|
|
*/
|
|
static int fuse_copy_ioctl_iovec_old(struct iovec *dst, void *src,
|
|
size_t transferred, unsigned count,
|
|
bool is_compat)
|
|
{
|
|
#ifdef CONFIG_COMPAT
|
|
if (count * sizeof(struct compat_iovec) == transferred) {
|
|
struct compat_iovec *ciov = src;
|
|
unsigned i;
|
|
|
|
/*
|
|
* With this interface a 32bit server cannot support
|
|
* non-compat (i.e. ones coming from 64bit apps) ioctl
|
|
* requests
|
|
*/
|
|
if (!is_compat)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
dst[i].iov_base = compat_ptr(ciov[i].iov_base);
|
|
dst[i].iov_len = ciov[i].iov_len;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (count * sizeof(struct iovec) != transferred)
|
|
return -EIO;
|
|
|
|
memcpy(dst, src, transferred);
|
|
return 0;
|
|
}
|
|
|
|
/* Make sure iov_length() won't overflow */
|
|
static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count)
|
|
{
|
|
size_t n;
|
|
u32 max = FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT;
|
|
|
|
for (n = 0; n < count; n++, iov++) {
|
|
if (iov->iov_len > (size_t) max)
|
|
return -ENOMEM;
|
|
max -= iov->iov_len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int fuse_copy_ioctl_iovec(struct fuse_conn *fc, struct iovec *dst,
|
|
void *src, size_t transferred, unsigned count,
|
|
bool is_compat)
|
|
{
|
|
unsigned i;
|
|
struct fuse_ioctl_iovec *fiov = src;
|
|
|
|
if (fc->minor < 16) {
|
|
return fuse_copy_ioctl_iovec_old(dst, src, transferred,
|
|
count, is_compat);
|
|
}
|
|
|
|
if (count * sizeof(struct fuse_ioctl_iovec) != transferred)
|
|
return -EIO;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
/* Did the server supply an inappropriate value? */
|
|
if (fiov[i].base != (unsigned long) fiov[i].base ||
|
|
fiov[i].len != (unsigned long) fiov[i].len)
|
|
return -EIO;
|
|
|
|
dst[i].iov_base = (void __user *) (unsigned long) fiov[i].base;
|
|
dst[i].iov_len = (size_t) fiov[i].len;
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
if (is_compat &&
|
|
(ptr_to_compat(dst[i].iov_base) != fiov[i].base ||
|
|
(compat_size_t) dst[i].iov_len != fiov[i].len))
|
|
return -EIO;
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* For ioctls, there is no generic way to determine how much memory
|
|
* needs to be read and/or written. Furthermore, ioctls are allowed
|
|
* to dereference the passed pointer, so the parameter requires deep
|
|
* copying but FUSE has no idea whatsoever about what to copy in or
|
|
* out.
|
|
*
|
|
* This is solved by allowing FUSE server to retry ioctl with
|
|
* necessary in/out iovecs. Let's assume the ioctl implementation
|
|
* needs to read in the following structure.
|
|
*
|
|
* struct a {
|
|
* char *buf;
|
|
* size_t buflen;
|
|
* }
|
|
*
|
|
* On the first callout to FUSE server, inarg->in_size and
|
|
* inarg->out_size will be NULL; then, the server completes the ioctl
|
|
* with FUSE_IOCTL_RETRY set in out->flags, out->in_iovs set to 1 and
|
|
* the actual iov array to
|
|
*
|
|
* { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) } }
|
|
*
|
|
* which tells FUSE to copy in the requested area and retry the ioctl.
|
|
* On the second round, the server has access to the structure and
|
|
* from that it can tell what to look for next, so on the invocation,
|
|
* it sets FUSE_IOCTL_RETRY, out->in_iovs to 2 and iov array to
|
|
*
|
|
* { { .iov_base = inarg.arg, .iov_len = sizeof(struct a) },
|
|
* { .iov_base = a.buf, .iov_len = a.buflen } }
|
|
*
|
|
* FUSE will copy both struct a and the pointed buffer from the
|
|
* process doing the ioctl and retry ioctl with both struct a and the
|
|
* buffer.
|
|
*
|
|
* This time, FUSE server has everything it needs and completes ioctl
|
|
* without FUSE_IOCTL_RETRY which finishes the ioctl call.
|
|
*
|
|
* Copying data out works the same way.
|
|
*
|
|
* Note that if FUSE_IOCTL_UNRESTRICTED is clear, the kernel
|
|
* automatically initializes in and out iovs by decoding @cmd with
|
|
* _IOC_* macros and the server is not allowed to request RETRY. This
|
|
* limits ioctl data transfers to well-formed ioctls and is the forced
|
|
* behavior for all FUSE servers.
|
|
*/
|
|
long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
|
|
unsigned int flags)
|
|
{
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_conn *fc = ff->fc;
|
|
struct fuse_ioctl_in inarg = {
|
|
.fh = ff->fh,
|
|
.cmd = cmd,
|
|
.arg = arg,
|
|
.flags = flags
|
|
};
|
|
struct fuse_ioctl_out outarg;
|
|
struct fuse_req *req = NULL;
|
|
struct page **pages = NULL;
|
|
struct iovec *iov_page = NULL;
|
|
struct iovec *in_iov = NULL, *out_iov = NULL;
|
|
unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages;
|
|
size_t in_size, out_size, transferred, c;
|
|
int err, i;
|
|
struct iov_iter ii;
|
|
|
|
#if BITS_PER_LONG == 32
|
|
inarg.flags |= FUSE_IOCTL_32BIT;
|
|
#else
|
|
if (flags & FUSE_IOCTL_COMPAT)
|
|
inarg.flags |= FUSE_IOCTL_32BIT;
|
|
#endif
|
|
|
|
/* assume all the iovs returned by client always fits in a page */
|
|
BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE);
|
|
|
|
err = -ENOMEM;
|
|
pages = kcalloc(FUSE_MAX_PAGES_PER_REQ, sizeof(pages[0]), GFP_KERNEL);
|
|
iov_page = (struct iovec *) __get_free_page(GFP_KERNEL);
|
|
if (!pages || !iov_page)
|
|
goto out;
|
|
|
|
/*
|
|
* If restricted, initialize IO parameters as encoded in @cmd.
|
|
* RETRY from server is not allowed.
|
|
*/
|
|
if (!(flags & FUSE_IOCTL_UNRESTRICTED)) {
|
|
struct iovec *iov = iov_page;
|
|
|
|
iov->iov_base = (void __user *)arg;
|
|
|
|
switch (cmd) {
|
|
case FS_IOC_GETFLAGS:
|
|
case FS_IOC_SETFLAGS:
|
|
iov->iov_len = sizeof(int);
|
|
break;
|
|
default:
|
|
iov->iov_len = _IOC_SIZE(cmd);
|
|
break;
|
|
}
|
|
|
|
if (_IOC_DIR(cmd) & _IOC_WRITE) {
|
|
in_iov = iov;
|
|
in_iovs = 1;
|
|
}
|
|
|
|
if (_IOC_DIR(cmd) & _IOC_READ) {
|
|
out_iov = iov;
|
|
out_iovs = 1;
|
|
}
|
|
}
|
|
|
|
retry:
|
|
inarg.in_size = in_size = iov_length(in_iov, in_iovs);
|
|
inarg.out_size = out_size = iov_length(out_iov, out_iovs);
|
|
|
|
/*
|
|
* Out data can be used either for actual out data or iovs,
|
|
* make sure there always is at least one page.
|
|
*/
|
|
out_size = max_t(size_t, out_size, PAGE_SIZE);
|
|
max_pages = DIV_ROUND_UP(max(in_size, out_size), PAGE_SIZE);
|
|
|
|
/* make sure there are enough buffer pages and init request with them */
|
|
err = -ENOMEM;
|
|
if (max_pages > FUSE_MAX_PAGES_PER_REQ)
|
|
goto out;
|
|
while (num_pages < max_pages) {
|
|
pages[num_pages] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
|
|
if (!pages[num_pages])
|
|
goto out;
|
|
num_pages++;
|
|
}
|
|
|
|
req = fuse_get_req(fc, num_pages);
|
|
if (IS_ERR(req)) {
|
|
err = PTR_ERR(req);
|
|
req = NULL;
|
|
goto out;
|
|
}
|
|
memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages);
|
|
req->num_pages = num_pages;
|
|
fuse_page_descs_length_init(req, 0, req->num_pages);
|
|
|
|
/* okay, let's send it to the client */
|
|
req->in.h.opcode = FUSE_IOCTL;
|
|
req->in.h.nodeid = ff->nodeid;
|
|
req->in.numargs = 1;
|
|
req->in.args[0].size = sizeof(inarg);
|
|
req->in.args[0].value = &inarg;
|
|
if (in_size) {
|
|
req->in.numargs++;
|
|
req->in.args[1].size = in_size;
|
|
req->in.argpages = 1;
|
|
|
|
err = -EFAULT;
|
|
iov_iter_init(&ii, WRITE, in_iov, in_iovs, in_size);
|
|
for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= num_pages); i++) {
|
|
c = copy_page_from_iter(pages[i], 0, PAGE_SIZE, &ii);
|
|
if (c != PAGE_SIZE && iov_iter_count(&ii))
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
req->out.numargs = 2;
|
|
req->out.args[0].size = sizeof(outarg);
|
|
req->out.args[0].value = &outarg;
|
|
req->out.args[1].size = out_size;
|
|
req->out.argpages = 1;
|
|
req->out.argvar = 1;
|
|
|
|
fuse_request_send(fc, req);
|
|
err = req->out.h.error;
|
|
transferred = req->out.args[1].size;
|
|
fuse_put_request(fc, req);
|
|
req = NULL;
|
|
if (err)
|
|
goto out;
|
|
|
|
/* did it ask for retry? */
|
|
if (outarg.flags & FUSE_IOCTL_RETRY) {
|
|
void *vaddr;
|
|
|
|
/* no retry if in restricted mode */
|
|
err = -EIO;
|
|
if (!(flags & FUSE_IOCTL_UNRESTRICTED))
|
|
goto out;
|
|
|
|
in_iovs = outarg.in_iovs;
|
|
out_iovs = outarg.out_iovs;
|
|
|
|
/*
|
|
* Make sure things are in boundary, separate checks
|
|
* are to protect against overflow.
|
|
*/
|
|
err = -ENOMEM;
|
|
if (in_iovs > FUSE_IOCTL_MAX_IOV ||
|
|
out_iovs > FUSE_IOCTL_MAX_IOV ||
|
|
in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
|
|
goto out;
|
|
|
|
vaddr = kmap_atomic(pages[0]);
|
|
err = fuse_copy_ioctl_iovec(fc, iov_page, vaddr,
|
|
transferred, in_iovs + out_iovs,
|
|
(flags & FUSE_IOCTL_COMPAT) != 0);
|
|
kunmap_atomic(vaddr);
|
|
if (err)
|
|
goto out;
|
|
|
|
in_iov = iov_page;
|
|
out_iov = in_iov + in_iovs;
|
|
|
|
err = fuse_verify_ioctl_iov(in_iov, in_iovs);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = fuse_verify_ioctl_iov(out_iov, out_iovs);
|
|
if (err)
|
|
goto out;
|
|
|
|
goto retry;
|
|
}
|
|
|
|
err = -EIO;
|
|
if (transferred > inarg.out_size)
|
|
goto out;
|
|
|
|
err = -EFAULT;
|
|
iov_iter_init(&ii, READ, out_iov, out_iovs, transferred);
|
|
for (i = 0; iov_iter_count(&ii) && !WARN_ON(i >= num_pages); i++) {
|
|
c = copy_page_to_iter(pages[i], 0, PAGE_SIZE, &ii);
|
|
if (c != PAGE_SIZE && iov_iter_count(&ii))
|
|
goto out;
|
|
}
|
|
err = 0;
|
|
out:
|
|
if (req)
|
|
fuse_put_request(fc, req);
|
|
free_page((unsigned long) iov_page);
|
|
while (num_pages)
|
|
__free_page(pages[--num_pages]);
|
|
kfree(pages);
|
|
|
|
return err ? err : outarg.result;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_do_ioctl);
|
|
|
|
long fuse_ioctl_common(struct file *file, unsigned int cmd,
|
|
unsigned long arg, unsigned int flags)
|
|
{
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
|
|
|
if (!fuse_allow_current_process(fc))
|
|
return -EACCES;
|
|
|
|
if (fuse_is_bad(inode))
|
|
return -EIO;
|
|
|
|
return fuse_do_ioctl(file, cmd, arg, flags);
|
|
}
|
|
|
|
static long fuse_file_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
return fuse_ioctl_common(file, cmd, arg, 0);
|
|
}
|
|
|
|
static long fuse_file_compat_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
return fuse_ioctl_common(file, cmd, arg, FUSE_IOCTL_COMPAT);
|
|
}
|
|
|
|
/*
|
|
* All files which have been polled are linked to RB tree
|
|
* fuse_conn->polled_files which is indexed by kh. Walk the tree and
|
|
* find the matching one.
|
|
*/
|
|
static struct rb_node **fuse_find_polled_node(struct fuse_conn *fc, u64 kh,
|
|
struct rb_node **parent_out)
|
|
{
|
|
struct rb_node **link = &fc->polled_files.rb_node;
|
|
struct rb_node *last = NULL;
|
|
|
|
while (*link) {
|
|
struct fuse_file *ff;
|
|
|
|
last = *link;
|
|
ff = rb_entry(last, struct fuse_file, polled_node);
|
|
|
|
if (kh < ff->kh)
|
|
link = &last->rb_left;
|
|
else if (kh > ff->kh)
|
|
link = &last->rb_right;
|
|
else
|
|
return link;
|
|
}
|
|
|
|
if (parent_out)
|
|
*parent_out = last;
|
|
return link;
|
|
}
|
|
|
|
/*
|
|
* The file is about to be polled. Make sure it's on the polled_files
|
|
* RB tree. Note that files once added to the polled_files tree are
|
|
* not removed before the file is released. This is because a file
|
|
* polled once is likely to be polled again.
|
|
*/
|
|
static void fuse_register_polled_file(struct fuse_conn *fc,
|
|
struct fuse_file *ff)
|
|
{
|
|
spin_lock(&fc->lock);
|
|
if (RB_EMPTY_NODE(&ff->polled_node)) {
|
|
struct rb_node **link, *parent;
|
|
|
|
link = fuse_find_polled_node(fc, ff->kh, &parent);
|
|
BUG_ON(*link);
|
|
rb_link_node(&ff->polled_node, parent, link);
|
|
rb_insert_color(&ff->polled_node, &fc->polled_files);
|
|
}
|
|
spin_unlock(&fc->lock);
|
|
}
|
|
|
|
__poll_t fuse_file_poll(struct file *file, poll_table *wait)
|
|
{
|
|
struct fuse_file *ff = file->private_data;
|
|
struct fuse_conn *fc = ff->fc;
|
|
struct fuse_poll_in inarg = { .fh = ff->fh, .kh = ff->kh };
|
|
struct fuse_poll_out outarg;
|
|
FUSE_ARGS(args);
|
|
int err;
|
|
|
|
if (fc->no_poll)
|
|
return DEFAULT_POLLMASK;
|
|
|
|
poll_wait(file, &ff->poll_wait, wait);
|
|
inarg.events = mangle_poll(poll_requested_events(wait));
|
|
|
|
/*
|
|
* Ask for notification iff there's someone waiting for it.
|
|
* The client may ignore the flag and always notify.
|
|
*/
|
|
if (waitqueue_active(&ff->poll_wait)) {
|
|
inarg.flags |= FUSE_POLL_SCHEDULE_NOTIFY;
|
|
fuse_register_polled_file(fc, ff);
|
|
}
|
|
|
|
args.in.h.opcode = FUSE_POLL;
|
|
args.in.h.nodeid = ff->nodeid;
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
args.out.numargs = 1;
|
|
args.out.args[0].size = sizeof(outarg);
|
|
args.out.args[0].value = &outarg;
|
|
err = fuse_simple_request(fc, &args);
|
|
|
|
if (!err)
|
|
return demangle_poll(outarg.revents);
|
|
if (err == -ENOSYS) {
|
|
fc->no_poll = 1;
|
|
return DEFAULT_POLLMASK;
|
|
}
|
|
return EPOLLERR;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fuse_file_poll);
|
|
|
|
/*
|
|
* This is called from fuse_handle_notify() on FUSE_NOTIFY_POLL and
|
|
* wakes up the poll waiters.
|
|
*/
|
|
int fuse_notify_poll_wakeup(struct fuse_conn *fc,
|
|
struct fuse_notify_poll_wakeup_out *outarg)
|
|
{
|
|
u64 kh = outarg->kh;
|
|
struct rb_node **link;
|
|
|
|
spin_lock(&fc->lock);
|
|
|
|
link = fuse_find_polled_node(fc, kh, NULL);
|
|
if (*link) {
|
|
struct fuse_file *ff;
|
|
|
|
ff = rb_entry(*link, struct fuse_file, polled_node);
|
|
wake_up_interruptible_sync(&ff->poll_wait);
|
|
}
|
|
|
|
spin_unlock(&fc->lock);
|
|
return 0;
|
|
}
|
|
|
|
static void fuse_do_truncate(struct file *file)
|
|
{
|
|
struct inode *inode = file->f_mapping->host;
|
|
struct iattr attr;
|
|
|
|
attr.ia_valid = ATTR_SIZE;
|
|
attr.ia_size = i_size_read(inode);
|
|
|
|
attr.ia_file = file;
|
|
attr.ia_valid |= ATTR_FILE;
|
|
|
|
fuse_do_setattr(file_dentry(file), &attr, file);
|
|
}
|
|
|
|
static inline loff_t fuse_round_up(loff_t off)
|
|
{
|
|
return round_up(off, FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT);
|
|
}
|
|
|
|
static ssize_t
|
|
fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
|
{
|
|
DECLARE_COMPLETION_ONSTACK(wait);
|
|
ssize_t ret = 0;
|
|
struct file *file = iocb->ki_filp;
|
|
struct fuse_file *ff = file->private_data;
|
|
bool async_dio = ff->fc->async_dio;
|
|
loff_t pos = 0;
|
|
struct inode *inode;
|
|
loff_t i_size;
|
|
size_t count = iov_iter_count(iter);
|
|
loff_t offset = iocb->ki_pos;
|
|
struct fuse_io_priv *io;
|
|
|
|
pos = offset;
|
|
inode = file->f_mapping->host;
|
|
i_size = i_size_read(inode);
|
|
|
|
if ((iov_iter_rw(iter) == READ) && (offset > i_size))
|
|
return 0;
|
|
|
|
/* optimization for short read */
|
|
if (async_dio && iov_iter_rw(iter) != WRITE && offset + count > i_size) {
|
|
if (offset >= i_size)
|
|
return 0;
|
|
iov_iter_truncate(iter, fuse_round_up(i_size - offset));
|
|
count = iov_iter_count(iter);
|
|
}
|
|
|
|
io = kmalloc(sizeof(struct fuse_io_priv), GFP_KERNEL);
|
|
if (!io)
|
|
return -ENOMEM;
|
|
spin_lock_init(&io->lock);
|
|
kref_init(&io->refcnt);
|
|
io->reqs = 1;
|
|
io->bytes = -1;
|
|
io->size = 0;
|
|
io->offset = offset;
|
|
io->write = (iov_iter_rw(iter) == WRITE);
|
|
io->err = 0;
|
|
/*
|
|
* By default, we want to optimize all I/Os with async request
|
|
* submission to the client filesystem if supported.
|
|
*/
|
|
io->async = async_dio;
|
|
io->iocb = iocb;
|
|
io->blocking = is_sync_kiocb(iocb);
|
|
|
|
/*
|
|
* We cannot asynchronously extend the size of a file.
|
|
* In such case the aio will behave exactly like sync io.
|
|
*/
|
|
if ((offset + count > i_size) && iov_iter_rw(iter) == WRITE)
|
|
io->blocking = true;
|
|
|
|
if (io->async && io->blocking) {
|
|
/*
|
|
* Additional reference to keep io around after
|
|
* calling fuse_aio_complete()
|
|
*/
|
|
kref_get(&io->refcnt);
|
|
io->done = &wait;
|
|
}
|
|
|
|
if (iov_iter_rw(iter) == WRITE) {
|
|
ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE);
|
|
fuse_invalidate_attr(inode);
|
|
} else {
|
|
ret = __fuse_direct_read(io, iter, &pos);
|
|
}
|
|
|
|
if (io->async) {
|
|
bool blocking = io->blocking;
|
|
|
|
fuse_aio_complete(io, ret < 0 ? ret : 0, -1);
|
|
|
|
/* we have a non-extending, async request, so return */
|
|
if (!blocking)
|
|
return -EIOCBQUEUED;
|
|
|
|
wait_for_completion(&wait);
|
|
ret = fuse_get_res_by_io(io);
|
|
}
|
|
|
|
kref_put(&io->refcnt, fuse_io_release);
|
|
|
|
if (iov_iter_rw(iter) == WRITE) {
|
|
if (ret > 0)
|
|
fuse_write_update_size(inode, pos);
|
|
else if (ret < 0 && offset + count > i_size)
|
|
fuse_do_truncate(file);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
|
|
loff_t length)
|
|
{
|
|
struct fuse_file *ff = file->private_data;
|
|
struct inode *inode = file_inode(file);
|
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
|
struct fuse_conn *fc = ff->fc;
|
|
FUSE_ARGS(args);
|
|
struct fuse_fallocate_in inarg = {
|
|
.fh = ff->fh,
|
|
.offset = offset,
|
|
.length = length,
|
|
.mode = mode
|
|
};
|
|
int err;
|
|
bool lock_inode = !(mode & FALLOC_FL_KEEP_SIZE) ||
|
|
(mode & FALLOC_FL_PUNCH_HOLE);
|
|
|
|
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
|
|
return -EOPNOTSUPP;
|
|
|
|
if (fc->no_fallocate)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (lock_inode) {
|
|
inode_lock(inode);
|
|
if (mode & FALLOC_FL_PUNCH_HOLE) {
|
|
loff_t endbyte = offset + length - 1;
|
|
err = filemap_write_and_wait_range(inode->i_mapping,
|
|
offset, endbyte);
|
|
if (err)
|
|
goto out;
|
|
|
|
fuse_sync_writes(inode);
|
|
}
|
|
}
|
|
|
|
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
|
offset + length > i_size_read(inode)) {
|
|
err = inode_newsize_ok(inode, offset + length);
|
|
if (err)
|
|
goto out;
|
|
}
|
|
|
|
if (!(mode & FALLOC_FL_KEEP_SIZE))
|
|
set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
|
|
|
|
args.in.h.opcode = FUSE_FALLOCATE;
|
|
args.in.h.nodeid = ff->nodeid;
|
|
args.in.numargs = 1;
|
|
args.in.args[0].size = sizeof(inarg);
|
|
args.in.args[0].value = &inarg;
|
|
err = fuse_simple_request(fc, &args);
|
|
if (err == -ENOSYS) {
|
|
fc->no_fallocate = 1;
|
|
err = -EOPNOTSUPP;
|
|
}
|
|
if (err)
|
|
goto out;
|
|
|
|
/* we could have extended the file */
|
|
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
|
|
bool changed = fuse_write_update_size(inode, offset + length);
|
|
|
|
if (changed && fc->writeback_cache)
|
|
file_update_time(file);
|
|
}
|
|
|
|
if (mode & FALLOC_FL_PUNCH_HOLE)
|
|
truncate_pagecache_range(inode, offset, offset + length - 1);
|
|
|
|
fuse_invalidate_attr(inode);
|
|
|
|
out:
|
|
if (!(mode & FALLOC_FL_KEEP_SIZE))
|
|
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
|
|
|
|
if (lock_inode)
|
|
inode_unlock(inode);
|
|
|
|
fuse_flush_time_update(inode);
|
|
|
|
return err;
|
|
}
|
|
|
|
static const struct file_operations fuse_file_operations = {
|
|
.llseek = fuse_file_llseek,
|
|
.read_iter = fuse_file_read_iter,
|
|
.write_iter = fuse_file_write_iter,
|
|
.mmap = fuse_file_mmap,
|
|
.open = fuse_open,
|
|
.flush = fuse_flush,
|
|
.release = fuse_release,
|
|
.fsync = fuse_fsync,
|
|
.lock = fuse_file_lock,
|
|
.flock = fuse_file_flock,
|
|
.splice_read = generic_file_splice_read,
|
|
.unlocked_ioctl = fuse_file_ioctl,
|
|
.compat_ioctl = fuse_file_compat_ioctl,
|
|
.poll = fuse_file_poll,
|
|
.fallocate = fuse_file_fallocate,
|
|
};
|
|
|
|
static const struct file_operations fuse_direct_io_file_operations = {
|
|
.llseek = fuse_file_llseek,
|
|
.read_iter = fuse_direct_read_iter,
|
|
.write_iter = fuse_direct_write_iter,
|
|
.mmap = fuse_direct_mmap,
|
|
.open = fuse_open,
|
|
.flush = fuse_flush,
|
|
.release = fuse_release,
|
|
.fsync = fuse_fsync,
|
|
.lock = fuse_file_lock,
|
|
.flock = fuse_file_flock,
|
|
.unlocked_ioctl = fuse_file_ioctl,
|
|
.compat_ioctl = fuse_file_compat_ioctl,
|
|
.poll = fuse_file_poll,
|
|
.fallocate = fuse_file_fallocate,
|
|
/* no splice_read */
|
|
};
|
|
|
|
static const struct address_space_operations fuse_file_aops = {
|
|
.readpage = fuse_readpage,
|
|
.writepage = fuse_writepage,
|
|
.writepages = fuse_writepages,
|
|
.launder_page = fuse_launder_page,
|
|
.readpages = fuse_readpages,
|
|
.set_page_dirty = __set_page_dirty_nobuffers,
|
|
.bmap = fuse_bmap,
|
|
.direct_IO = fuse_direct_IO,
|
|
.write_begin = fuse_write_begin,
|
|
.write_end = fuse_write_end,
|
|
};
|
|
|
|
void fuse_init_file_inode(struct inode *inode)
|
|
{
|
|
inode->i_fop = &fuse_file_operations;
|
|
inode->i_data.a_ops = &fuse_file_aops;
|
|
}
|