4922049993
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEZH8oZUiU471FcZm+ONu9yGCSaT4FAmPCczkACgkQONu9yGCS aT4tnRAAsW8h/ohmhP+O2lQ9Ekw6s9VB6KB4aJzLhQXqIZlrzk2DP3CiLxQ7DkFc AcHwFYq+sERo8O7dK6pbCW0zNvLUpbK2wJhwMHujJfSUFboXX85NR6u90U67pBKS p+yVkDSx8LNc7c676xQ7ey5rO1K2fQQ266gexjI9WOkjIFOfplVkZ7tkvt51VwAD mNvOQsZdCE6xs+T3t9YMOtAx3wW8vl1wW3QDWCHznQwOJiMEjfNEOUY/+xELnnWz DVONWPHTFNKQHZwIuVUFZdNuORq5WXoIaMZdaEFkhuOtRMnQ9l+wi8iMxX8zkgBn Ji/dPu3GdAZsJU4/rXHkY2AeZV1oJc3NLYmevoRZirTqMQCqyM+blif7Rf34kBi7 6jlGPWOjyMNe58shfHmjWTl/u4pMDoYOnm5XO+1oL+2Xg8QWCucqAlfPFB3uMh6n aL4ymzo5QRy1+tx8v7o1NOjnGWydvYn3O4fwJVkYTFaJZZr9EI7gpTmEBw/gwfuf 4TH6BC++Ai/8vgKUhpdnWuTrjomWalPTcPBgQYG4gD7ak2TM1rmgMaCl/THUe36R zPC8m2sIXKeI4zGi8TeqTgaRvFSKJnuEmVo5OPkG98fZkjekCzWHp0q1+PG2Ecy8 Mu2/AOnwb1aSfOJh2Qajoke/Wed0U6qszop8C/jPRh8D1uTmRbI= =sOTj -----END PGP SIGNATURE----- Merge 5.10.163 into android12-5.10-lts Changes in 5.10.163 usb: musb: remove extra check in musb_gadget_vbus_draw arm64: dts: qcom: ipq6018-cp01-c1: use BLSPI1 pins arm64: dts: qcom: msm8996: fix GPU OPP table ARM: dts: qcom: apq8064: fix coresight compatible arm64: dts: qcom: sdm630: fix UART1 pin bias arm64: dts: qcom: sdm845-cheza: fix AP suspend pin bias arm64: dts: qcom: msm8916: Drop MSS fallback compatible objtool, kcsan: Add volatile read/write instrumentation to whitelist ARM: dts: stm32: Drop stm32mp15xc.dtsi from Avenger96 ARM: dts: stm32: Fix AV96 WLAN regulator gpio property drivers: soc: ti: knav_qmss_queue: Mark knav_acc_firmwares as static soc: qcom: llcc: make irq truly optional soc: qcom: apr: make code more reuseable soc: qcom: apr: Add check for idr_alloc and of_property_read_string_index arm: dts: spear600: Fix clcd interrupt soc: ti: knav_qmss_queue: Use pm_runtime_resume_and_get instead of pm_runtime_get_sync soc: ti: knav_qmss_queue: Fix PM disable depth imbalance in knav_queue_probe soc: ti: smartreflex: Fix PM disable depth imbalance in omap_sr_probe perf: arm_dsu: Fix hotplug callback leak in dsu_pmu_init() perf/smmuv3: Fix hotplug callback leak in arm_smmu_pmu_init() arm64: dts: ti: k3-am65-main: Drop dma-coherent in crypto node arm64: dts: ti: k3-j721e-main: Drop dma-coherent in crypto node arm64: dts: mt2712e: Fix unit_address_vs_reg warning for oscillators arm64: dts: mt2712e: Fix unit address for pinctrl node arm64: dts: mt2712-evb: Fix vproc fixed regulators unit names arm64: dts: mt2712-evb: Fix usb vbus regulators unit names arm64: dts: mediatek: pumpkin-common: Fix devicetree warnings arm64: dts: mediatek: mt6797: Fix 26M oscillator unit name ARM: dts: dove: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-370: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-xp: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-375: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-38x: Fix assigned-addresses for every PCIe Root Port ARM: dts: armada-39x: Fix assigned-addresses for every PCIe Root Port ARM: dts: turris-omnia: Add ethernet aliases ARM: dts: turris-omnia: Add switch port 6 node arm64: dts: armada-3720-turris-mox: Add missing interrupt for RTC pstore/ram: Fix error return code in ramoops_probe() ARM: mmp: fix timer_read delay pstore: Avoid kcore oops by vmap()ing with VM_IOREMAP tpm/tpm_ftpm_tee: Fix error handling in ftpm_mod_init() tpm/tpm_crb: Fix error message in __crb_relinquish_locality() sched/fair: Cleanup task_util and capacity type sched/uclamp: Fix relationship between uclamp and migration margin cpuidle: dt: Return the correct numbers of parsed idle states alpha: fix syscall entry in !AUDUT_SYSCALL case PM: hibernate: Fix mistake in kerneldoc comment fs: don't audit the capability check in simple_xattr_list() cpufreq: qcom-hw: Fix memory leak in qcom_cpufreq_hw_read_lut() selftests/ftrace: event_triggers: wait longer for test_event_enable perf: Fix possible memleak in pmu_dev_alloc() lib/debugobjects: fix stat count and optimize debug_objects_mem_init platform/x86: huawei-wmi: fix return value calculation timerqueue: Use rb_entry_safe() in timerqueue_getnext() proc: fixup uptime selftest lib/fonts: fix undefined behavior in bit shift for get_default_font ocfs2: fix memory leak in ocfs2_stack_glue_init() MIPS: vpe-mt: fix possible memory leak while module exiting MIPS: vpe-cmp: fix possible memory leak while module exiting selftests/efivarfs: Add checking of the test return value PNP: fix name memory leak in pnp_alloc_dev() perf/x86/intel/uncore: Fix reference count leak in hswep_has_limit_sbox() perf/x86/intel/uncore: Fix reference count leak in snr_uncore_mmio_map() perf/x86/intel/uncore: Fix reference count leak in __uncore_imc_init_box() platform/chrome: cros_usbpd_notify: Fix error handling in cros_usbpd_notify_init() irqchip: gic-pm: Use pm_runtime_resume_and_get() in gic_probe() EDAC/i10nm: fix refcount leak in pci_get_dev_wrapper() nfsd: don't call nfsd_file_put from client states seqfile display genirq/irqdesc: Don't try to remove non-existing sysfs files cpufreq: amd_freq_sensitivity: Add missing pci_dev_put() libfs: add DEFINE_SIMPLE_ATTRIBUTE_SIGNED for signed value lib/notifier-error-inject: fix error when writing -errno to debugfs file docs: fault-injection: fix non-working usage of negative values debugfs: fix error when writing negative value to atomic_t debugfs file ocfs2: ocfs2_mount_volume does cleanup job before return error ocfs2: rewrite error handling of ocfs2_fill_super ocfs2: fix memory leak in ocfs2_mount_volume() rapidio: fix possible name leaks when rio_add_device() fails rapidio: rio: fix possible name leak in rio_register_mport() clocksource/drivers/sh_cmt: Make sure channel clock supply is enabled clocksource/drivers/sh_cmt: Access registers according to spec futex: Move to kernel/futex/ futex: Resend potentially swallowed owner death notification cpu/hotplug: Make target_store() a nop when target == state clocksource/drivers/timer-ti-dm: Fix missing clk_disable_unprepare in dmtimer_systimer_init_clock() ACPICA: Fix use-after-free in acpi_ut_copy_ipackage_to_ipackage() uprobes/x86: Allow to probe a NOP instruction with 0x66 prefix x86/xen: Fix memory leak in xen_smp_intr_init{_pv}() x86/xen: Fix memory leak in xen_init_lock_cpu() xen/privcmd: Fix a possible warning in privcmd_ioctl_mmap_resource() PM: runtime: Improve path in rpm_idle() when no callback PM: runtime: Do not call __rpm_callback() from rpm_idle() platform/x86: mxm-wmi: fix memleak in mxm_wmi_call_mx[ds|mx]() platform/x86: intel_scu_ipc: fix possible name leak in __intel_scu_ipc_register() MIPS: BCM63xx: Add check for NULL for clk in clk_enable MIPS: OCTEON: warn only once if deprecated link status is being used fs: sysv: Fix sysv_nblocks() returns wrong value rapidio: fix possible UAF when kfifo_alloc() fails eventfd: change int to __u64 in eventfd_signal() ifndef CONFIG_EVENTFD relay: fix type mismatch when allocating memory in relay_create_buf() hfs: Fix OOB Write in hfs_asc2mac rapidio: devices: fix missing put_device in mport_cdev_open wifi: ath9k: hif_usb: fix memory leak of urbs in ath9k_hif_usb_dealloc_tx_urbs() wifi: ath9k: hif_usb: Fix use-after-free in ath9k_hif_usb_reg_in_cb() wifi: rtl8xxxu: Fix reading the vendor of combo chips drm/bridge: adv7533: remove dynamic lane switching from adv7533 bridge libbpf: Fix use-after-free in btf_dump_name_dups libbpf: Fix null-pointer dereference in find_prog_by_sec_insn() pata_ipx4xx_cf: Fix unsigned comparison with less than zero media: coda: jpeg: Add check for kmalloc media: i2c: ad5820: Fix error path venus: pm_helpers: Fix error check in vcodec_domains_get() media: exynos4-is: Use v4l2_async_notifier_add_fwnode_remote_subdev media: exynos4-is: don't rely on the v4l2_async_subdev internals can: kvaser_usb: do not increase tx statistics when sending error message frames can: kvaser_usb: kvaser_usb_leaf: Get capabilities from device can: kvaser_usb: kvaser_usb_leaf: Rename {leaf,usbcan}_cmd_error_event to {leaf,usbcan}_cmd_can_error_event can: kvaser_usb: kvaser_usb_leaf: Handle CMD_ERROR_EVENT can: kvaser_usb_leaf: Set Warning state even without bus errors can: kvaser_usb_leaf: Fix improved state not being reported can: kvaser_usb_leaf: Fix wrong CAN state after stopping can: kvaser_usb_leaf: Fix bogus restart events can: kvaser_usb: Add struct kvaser_usb_busparams can: kvaser_usb: Compare requested bittiming parameters with actual parameters in do_set_{,data}_bittiming drm/rockchip: lvds: fix PM usage counter unbalance in poweron clk: renesas: r9a06g032: Repair grave increment error spi: Update reference to struct spi_controller drm/panel/panel-sitronix-st7701: Remove panel on DSI attach failure ima: Fix fall-through warnings for Clang ima: Handle -ESTALE returned by ima_filter_rule_match() drm/msm/hdmi: switch to drm_bridge_connector drm/msm/hdmi: drop unused GPIO support bpf: Fix slot type check in check_stack_write_var_off media: vivid: fix compose size exceed boundary media: platform: exynos4-is: fix return value check in fimc_md_probe() bpf: propagate precision in ALU/ALU64 operations bpf: Check the other end of slot_type for STACK_SPILL bpf: propagate precision across all frames, not just the last one clk: qcom: gcc-sm8250: Use retention mode for USB GDSCs mtd: Fix device name leak when register device failed in add_mtd_device() Input: joystick - fix Kconfig warning for JOYSTICK_ADC wifi: rsi: Fix handling of 802.3 EAPOL frames sent via control port media: camss: Clean up received buffers on failed start of streaming net, proc: Provide PROC_FS=n fallback for proc_create_net_single_write() rxrpc: Fix ack.bufferSize to be 0 when generating an ack drm/radeon: Add the missed acpi_put_table() to fix memory leak drm/mediatek: Modify dpi power on/off sequence. ASoC: pxa: fix null-pointer dereference in filter() regulator: core: fix unbalanced of node refcount in regulator_dev_lookup() amdgpu/pm: prevent array underflow in vega20_odn_edit_dpm_table() drm/fourcc: Add packed 10bit YUV 4:2:0 format drm/fourcc: Fix vsub/hsub for Q410 and Q401 integrity: Fix memory leakage in keyring allocation error path ima: Fix misuse of dereference of pointer in template_desc_init_fields() wifi: ath10k: Fix return value in ath10k_pci_init() mtd: lpddr2_nvm: Fix possible null-ptr-deref Input: elants_i2c - properly handle the reset GPIO when power is off media: vidtv: Fix use-after-free in vidtv_bridge_dvb_init() media: solo6x10: fix possible memory leak in solo_sysfs_init() media: platform: exynos4-is: Fix error handling in fimc_md_init() media: videobuf-dma-contig: use dma_mmap_coherent inet: add READ_ONCE(sk->sk_bound_dev_if) in inet_csk_bind_conflict() bpf: Move skb->len == 0 checks into __bpf_redirect HID: hid-sensor-custom: set fixed size for custom attributes ALSA: pcm: fix undefined behavior in bit shift for SNDRV_PCM_RATE_KNOT ALSA: seq: fix undefined behavior in bit shift for SNDRV_SEQ_FILTER_USE_EVENT regulator: core: use kfree_const() to free space conditionally clk: rockchip: Fix memory leak in rockchip_clk_register_pll() drm/amdgpu: fix pci device refcount leak bonding: fix link recovery in mode 2 when updelay is nonzero mtd: maps: pxa2xx-flash: fix memory leak in probe drbd: fix an invalid memory access caused by incorrect use of list iterator ASoC: qcom: Add checks for devm_kcalloc media: vimc: Fix wrong function called when vimc_init() fails media: imon: fix a race condition in send_packet() clk: imx: replace osc_hdmi with dummy pinctrl: pinconf-generic: add missing of_node_put() media: dvb-core: Fix ignored return value in dvb_register_frontend() media: dvb-usb: az6027: fix null-ptr-deref in az6027_i2c_xfer() media: s5p-mfc: Add variant data for MFC v7 hardware for Exynos 3250 SoC drm/tegra: Add missing clk_disable_unprepare() in tegra_dc_probe() ASoC: dt-bindings: wcd9335: fix reset line polarity in example ASoC: mediatek: mtk-btcvsd: Add checks for write and read of mtk_btcvsd_snd NFSv4.2: Clear FATTR4_WORD2_SECURITY_LABEL when done decoding NFSv4.2: Fix a memory stomp in decode_attr_security_label NFSv4.2: Fix initialisation of struct nfs4_label NFSv4: Fix a deadlock between nfs4_open_recover_helper() and delegreturn NFS: Fix an Oops in nfs_d_automount() ALSA: asihpi: fix missing pci_disable_device() wifi: iwlwifi: mvm: fix double free on tx path. ASoC: mediatek: mt8173: Fix debugfs registration for components ASoC: mediatek: mt8173: Enable IRQ when pdata is ready drm/amd/pm/smu11: BACO is supported when it's in BACO state drm/radeon: Fix PCI device refcount leak in radeon_atrm_get_bios() drm/amdgpu: Fix PCI device refcount leak in amdgpu_atrm_get_bios() ASoC: pcm512x: Fix PM disable depth imbalance in pcm512x_probe netfilter: conntrack: set icmpv6 redirects as RELATED bpf, sockmap: Fix repeated calls to sock_put() when msg has more_data bpf, sockmap: Fix data loss caused by using apply_bytes on ingress redirect bonding: uninitialized variable in bond_miimon_inspect() spi: spidev: mask SPI_CS_HIGH in SPI_IOC_RD_MODE wifi: mac80211: fix memory leak in ieee80211_if_add() wifi: cfg80211: Fix not unregister reg_pdev when load_builtin_regdb_keys() fails wifi: mt76: fix coverity overrun-call in mt76_get_txpower() regulator: core: fix module refcount leak in set_supply() clk: qcom: clk-krait: fix wrong div2 functions hsr: Add a rcu-read lock to hsr_forward_skb(). net: hsr: generate supervision frame without HSR/PRP tag hsr: Disable netpoll. hsr: Synchronize sending frames to have always incremented outgoing seq nr. hsr: Synchronize sequence number updates. configfs: fix possible memory leak in configfs_create_dir() regulator: core: fix resource leak in regulator_register() hwmon: (jc42) Convert register access and caching to regmap/regcache hwmon: (jc42) Restore the min/max/critical temperatures on resume bpf, sockmap: fix race in sock_map_free() ALSA: pcm: Set missing stop_operating flag at undoing trigger start media: saa7164: fix missing pci_disable_device() ALSA: mts64: fix possible null-ptr-defer in snd_mts64_interrupt xprtrdma: Fix regbuf data not freed in rpcrdma_req_create() SUNRPC: Fix missing release socket in rpc_sockname() NFSv4.x: Fail client initialisation if state manager thread can't run mmc: alcor: fix return value check of mmc_add_host() mmc: moxart: fix return value check of mmc_add_host() mmc: mxcmmc: fix return value check of mmc_add_host() mmc: pxamci: fix return value check of mmc_add_host() mmc: rtsx_usb_sdmmc: fix return value check of mmc_add_host() mmc: toshsd: fix return value check of mmc_add_host() mmc: vub300: fix return value check of mmc_add_host() mmc: wmt-sdmmc: fix return value check of mmc_add_host() mmc: atmel-mci: fix return value check of mmc_add_host() mmc: omap_hsmmc: fix return value check of mmc_add_host() mmc: meson-gx: fix return value check of mmc_add_host() mmc: via-sdmmc: fix return value check of mmc_add_host() mmc: wbsd: fix return value check of mmc_add_host() mmc: mmci: fix return value check of mmc_add_host() media: c8sectpfe: Add of_node_put() when breaking out of loop media: coda: Add check for dcoda_iram_alloc media: coda: Add check for kmalloc clk: samsung: Fix memory leak in _samsung_clk_register_pll() spi: spi-gpio: Don't set MOSI as an input if not 3WIRE mode wifi: rtl8xxxu: Add __packed to struct rtl8723bu_c2h wifi: rtl8xxxu: Fix the channel width reporting wifi: brcmfmac: Fix error return code in brcmf_sdio_download_firmware() blktrace: Fix output non-blktrace event when blk_classic option enabled clk: socfpga: clk-pll: Remove unused variable 'rc' clk: socfpga: use clk_hw_register for a5/c5 clk: socfpga: Fix memory leak in socfpga_gate_init() net: vmw_vsock: vmci: Check memcpy_from_msg() net: defxx: Fix missing err handling in dfx_init() net: stmmac: selftests: fix potential memleak in stmmac_test_arpoffload() drivers: net: qlcnic: Fix potential memory leak in qlcnic_sriov_init() of: overlay: fix null pointer dereferencing in find_dup_cset_node_entry() and find_dup_cset_prop() ethernet: s2io: don't call dev_kfree_skb() under spin_lock_irqsave() net: farsync: Fix kmemleak when rmmods farsync net/tunnel: wait until all sk_user_data reader finish before releasing the sock net: apple: mace: don't call dev_kfree_skb() under spin_lock_irqsave() net: apple: bmac: don't call dev_kfree_skb() under spin_lock_irqsave() net: emaclite: don't call dev_kfree_skb() under spin_lock_irqsave() net: ethernet: dnet: don't call dev_kfree_skb() under spin_lock_irqsave() hamradio: don't call dev_kfree_skb() under spin_lock_irqsave() net: amd: lance: don't call dev_kfree_skb() under spin_lock_irqsave() net: amd-xgbe: Fix logic around active and passive cables net: amd-xgbe: Check only the minimum speed for active/passive cables can: tcan4x5x: Remove invalid write in clear_interrupts net: lan9303: Fix read error execution path ntb_netdev: Use dev_kfree_skb_any() in interrupt context sctp: sysctl: make extra pointers netns aware Bluetooth: btusb: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_qca: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_ll: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_h5: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_bcsp: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: hci_core: don't call kfree_skb() under spin_lock_irqsave() Bluetooth: RFCOMM: don't call kfree_skb() under spin_lock_irqsave() stmmac: fix potential division by 0 apparmor: fix a memleak in multi_transaction_new() apparmor: fix lockdep warning when removing a namespace apparmor: Fix abi check to include v8 abi crypto: sun8i-ss - use dma_addr instead u32 crypto: nitrox - avoid double free on error path in nitrox_sriov_init() scsi: core: Fix a race between scsi_done() and scsi_timeout() apparmor: Use pointer to struct aa_label for lbs_cred PCI: dwc: Fix n_fts[] array overrun RDMA/core: Fix order of nldev_exit call PCI: pci-epf-test: Register notifier if only core_init_notifier is enabled f2fs: Fix the race condition of resize flag between resizefs crypto: rockchip - do not do custom power management crypto: rockchip - do not store mode globally crypto: rockchip - add fallback for cipher crypto: rockchip - add fallback for ahash crypto: rockchip - better handle cipher key crypto: rockchip - remove non-aligned handling crypto: rockchip - delete unneeded variable initialization crypto: rockchip - rework by using crypto_engine apparmor: Fix memleak in alloc_ns() f2fs: fix normal discard process RDMA/siw: Fix immediate work request flush to completion queue RDMA/nldev: Return "-EAGAIN" if the cm_id isn't from expected port RDMA/siw: Set defined status for work completion with undefined status scsi: scsi_debug: Fix a warning in resp_write_scat() crypto: ccree - Remove debugfs when platform_driver_register failed crypto: cryptd - Use request context instead of stack for sub-request crypto: hisilicon/qm - add missing pci_dev_put() in q_num_set() RDMA/hns: Repacing 'dseg_len' by macros in fill_ext_sge_inl_data() RDMA/hns: Fix ext_sge num error when post send PCI: Check for alloc failure in pci_request_irq() RDMA/hfi: Decrease PCI device reference count in error path crypto: ccree - Make cc_debugfs_global_fini() available for module init function RDMA/hns: fix memory leak in hns_roce_alloc_mr() RDMA/rxe: Fix NULL-ptr-deref in rxe_qp_do_cleanup() when socket create failed scsi: hpsa: Fix possible memory leak in hpsa_init_one() crypto: tcrypt - Fix multibuffer skcipher speed test mem leak padata: Always leave BHs disabled when running ->parallel() padata: Fix list iterator in padata_do_serial() scsi: mpt3sas: Fix possible resource leaks in mpt3sas_transport_port_add() scsi: hpsa: Fix error handling in hpsa_add_sas_host() scsi: hpsa: Fix possible memory leak in hpsa_add_sas_device() scsi: scsi_debug: Fix a warning in resp_verify() scsi: scsi_debug: Fix a warning in resp_report_zones() scsi: fcoe: Fix possible name leak when device_register() fails scsi: scsi_debug: Fix possible name leak in sdebug_add_host_helper() scsi: ipr: Fix WARNING in ipr_init() scsi: fcoe: Fix transport not deattached when fcoe_if_init() fails scsi: snic: Fix possible UAF in snic_tgt_create() RDMA/nldev: Add checks for nla_nest_start() in fill_stat_counter_qps() f2fs: avoid victim selection from previous victim section RDMA/nldev: Fix failure to send large messages crypto: amlogic - Remove kcalloc without check crypto: omap-sham - Use pm_runtime_resume_and_get() in omap_sham_probe() riscv/mm: add arch hook arch_clear_hugepage_flags RDMA/hfi1: Fix error return code in parse_platform_config() RDMA/srp: Fix error return code in srp_parse_options() orangefs: Fix sysfs not cleanup when dev init failed RDMA/hns: Fix PBL page MTR find RDMA/hns: Fix page size cap from firmware crypto: img-hash - Fix variable dereferenced before check 'hdev->req' hwrng: amd - Fix PCI device refcount leak hwrng: geode - Fix PCI device refcount leak IB/IPoIB: Fix queue count inconsistency for PKEY child interfaces drivers: dio: fix possible memory leak in dio_init() serial: tegra: Read DMA status before terminating class: fix possible memory leak in __class_register() vfio: platform: Do not pass return buffer to ACPI _RST method uio: uio_dmem_genirq: Fix missing unlock in irq configuration uio: uio_dmem_genirq: Fix deadlock between irq config and handling usb: fotg210-udc: Fix ages old endianness issues staging: vme_user: Fix possible UAF in tsi148_dma_list_add usb: typec: Check for ops->exit instead of ops->enter in altmode_exit usb: typec: tcpci: fix of node refcount leak in tcpci_register_port() usb: typec: tipd: Fix spurious fwnode_handle_put in error path serial: amba-pl011: avoid SBSA UART accessing DMACR register serial: pl011: Do not clear RX FIFO & RX interrupt in unthrottle. serial: pch: Fix PCI device refcount leak in pch_request_dma() tty: serial: clean up stop-tx part in altera_uart_tx_chars() tty: serial: altera_uart_{r,t}x_chars() need only uart_port serial: altera_uart: fix locking in polling mode serial: sunsab: Fix error handling in sunsab_init() test_firmware: fix memory leak in test_firmware_init() misc: ocxl: fix possible name leak in ocxl_file_register_afu() ocxl: fix pci device refcount leak when calling get_function_0() misc: tifm: fix possible memory leak in tifm_7xx1_switch_media() misc: sgi-gru: fix use-after-free error in gru_set_context_option, gru_fault and gru_handle_user_call_os firmware: raspberrypi: fix possible memory leak in rpi_firmware_probe() cxl: fix possible null-ptr-deref in cxl_guest_init_afu|adapter() cxl: fix possible null-ptr-deref in cxl_pci_init_afu|adapter() iio: temperature: ltc2983: make bulk write buffer DMA-safe genirq: Add IRQF_NO_AUTOEN for request_irq/nmi() iio:imu:adis: Use IRQF_NO_AUTOEN instead of irq request then disable iio: adis: handle devices that cannot unmask the drdy pin iio: adis: stylistic changes iio:imu:adis: Move exports into IIO_ADISLIB namespace iio: adis: add '__adis_enable_irq()' implementation counter: stm32-lptimer-cnt: fix the check on arr and cmp registers update usb: roles: fix of node refcount leak in usb_role_switch_is_parent() usb: gadget: f_hid: optional SETUP/SET_REPORT mode usb: gadget: f_hid: fix f_hidg lifetime vs cdev usb: gadget: f_hid: fix refcount leak on error path drivers: mcb: fix resource leak in mcb_probe() mcb: mcb-parse: fix error handing in chameleon_parse_gdd() chardev: fix error handling in cdev_device_add() i2c: pxa-pci: fix missing pci_disable_device() on error in ce4100_i2c_probe staging: rtl8192u: Fix use after free in ieee80211_rx() staging: rtl8192e: Fix potential use-after-free in rtllib_rx_Monitor() vme: Fix error not catched in fake_init() gpiolib: Get rid of redundant 'else' gpiolib: cdev: fix NULL-pointer dereferences i2c: mux: reg: check return value after calling platform_get_resource() i2c: ismt: Fix an out-of-bounds bug in ismt_access() usb: storage: Add check for kcalloc tracing/hist: Fix issue of losting command info in error_log samples: vfio-mdev: Fix missing pci_disable_device() in mdpy_fb_probe() thermal/drivers/imx8mm_thermal: Validate temperature range fbdev: ssd1307fb: Drop optional dependency fbdev: pm2fb: fix missing pci_disable_device() fbdev: via: Fix error in via_core_init() fbdev: vermilion: decrease reference count in error path fbdev: uvesafb: Fixes an error handling path in uvesafb_probe() HSI: omap_ssi_core: fix unbalanced pm_runtime_disable() HSI: omap_ssi_core: fix possible memory leak in ssi_probe() power: supply: fix residue sysfs file in error handle route of __power_supply_register() perf trace: Return error if a system call doesn't exist perf trace: Use macro RAW_SYSCALL_ARGS_NUM to replace number perf trace: Handle failure when trace point folder is missed perf symbol: correction while adjusting symbol HSI: omap_ssi_core: Fix error handling in ssi_init() power: supply: fix null pointer dereferencing in power_supply_get_battery_info RDMA/siw: Fix pointer cast warning iommu/sun50i: Fix reset release iommu/sun50i: Consider all fault sources for reset iommu/sun50i: Fix R/W permission check iommu/sun50i: Fix flush size phy: usb: s2 WoL wakeup_count not incremented for USB->Eth devices include/uapi/linux/swab: Fix potentially missing __always_inline pwm: tegra: Improve required rate calculation dmaengine: idxd: Fix crc_val field for completion record rtc: rtc-cmos: Do not check ACPI_FADT_LOW_POWER_S0 rtc: cmos: Fix event handler registration ordering issue rtc: cmos: Fix wake alarm breakage rtc: cmos: fix build on non-ACPI platforms rtc: cmos: Call cmos_wake_setup() from cmos_do_probe() rtc: cmos: Call rtc_wake_setup() from cmos_do_probe() rtc: cmos: Eliminate forward declarations of some functions rtc: cmos: Rename ACPI-related functions rtc: cmos: Disable ACPI RTC event on removal rtc: snvs: Allow a time difference on clock register read rtc: pcf85063: Fix reading alarm iommu/amd: Fix pci device refcount leak in ppr_notifier() iommu/fsl_pamu: Fix resource leak in fsl_pamu_probe() macintosh: fix possible memory leak in macio_add_one_device() macintosh/macio-adb: check the return value of ioremap() powerpc/52xx: Fix a resource leak in an error handling path cxl: Fix refcount leak in cxl_calc_capp_routing powerpc/xmon: Enable breakpoints on 8xx powerpc/xmon: Fix -Wswitch-unreachable warning in bpt_cmds powerpc/xive: add missing iounmap() in error path in xive_spapr_populate_irq_data() kbuild: remove unneeded mkdir for external modules_install kbuild: unify modules(_install) for in-tree and external modules kbuild: refactor single builds of *.ko powerpc/perf: callchain validate kernel stack pointer bounds powerpc/83xx/mpc832x_rdb: call platform_device_put() in error case in of_fsl_spi_probe() powerpc/hv-gpci: Fix hv_gpci event list selftests/powerpc: Fix resource leaks iommu/sun50i: Remove IOMMU_DOMAIN_IDENTITY pwm: sifive: Call pwm_sifive_update_clock() while mutex is held remoteproc: sysmon: fix memory leak in qcom_add_sysmon_subdev() remoteproc: qcom_q6v5_pas: disable wakeup on probe fail or remove remoteproc: qcom_q6v5_pas: detach power domains on remove remoteproc: qcom_q6v5_pas: Fix missing of_node_put() in adsp_alloc_memory_region() powerpc/eeh: Drop redundant spinlock initialization powerpc/pseries/eeh: use correct API for error log size netfilter: flowtable: really fix NAT IPv6 offload rtc: st-lpc: Add missing clk_disable_unprepare in st_rtc_probe() rtc: pic32: Move devm_rtc_allocate_device earlier in pic32_rtc_probe() rtc: pcf85063: fix pcf85063_clkout_control NFSD: Remove spurious cb_setup_err tracepoint nfsd: under NFSv4.1, fix double svc_xprt_put on rpc_create failure net: macsec: fix net device access prior to holding a lock mISDN: hfcsusb: don't call dev_kfree_skb/kfree_skb() under spin_lock_irqsave() mISDN: hfcpci: don't call dev_kfree_skb/kfree_skb() under spin_lock_irqsave() mISDN: hfcmulti: don't call dev_kfree_skb/kfree_skb() under spin_lock_irqsave() nfc: pn533: Clear nfc_target before being used r6040: Fix kmemleak in probe and remove net: switch to storing KCOV handle directly in sk_buff net: add inline function skb_csum_is_sctp net: igc: use skb_csum_is_sctp instead of protocol check net: add a helper to avoid issues with HW TX timestamping and SO_TXTIME igc: Enhance Qbv scheduling by using first flag bit igc: Use strict cycles for Qbv scheduling igc: Add checking for basetime less than zero igc: recalculate Qbv end_time by considering cycle time igc: Lift TAPRIO schedule restriction igc: Set Qbv start_time and end_time to end_time if not being configured in GCL rtc: mxc_v2: Add missing clk_disable_unprepare() selftests: devlink: fix the fd redirect in dummy_reporter_test openvswitch: Fix flow lookup to use unmasked key skbuff: Account for tail adjustment during pull operations mailbox: zynq-ipi: fix error handling while device_register() fails net_sched: reject TCF_EM_SIMPLE case for complex ematch module rxrpc: Fix missing unlock in rxrpc_do_sendmsg() myri10ge: Fix an error handling path in myri10ge_probe() net: stream: purge sk_error_queue in sk_stream_kill_queues() rcu: Fix __this_cpu_read() lockdep warning in rcu_force_quiescent_state() arm64: make is_ttbrX_addr() noinstr-safe video: hyperv_fb: Avoid taking busy spinlock on panic path x86/hyperv: Remove unregister syscore call from Hyper-V cleanup binfmt_misc: fix shift-out-of-bounds in check_special_flags fs: jfs: fix shift-out-of-bounds in dbAllocAG udf: Avoid double brelse() in udf_rename() fs: jfs: fix shift-out-of-bounds in dbDiscardAG ACPICA: Fix error code path in acpi_ds_call_control_method() nilfs2: fix shift-out-of-bounds/overflow in nilfs_sb2_bad_offset() nilfs2: fix shift-out-of-bounds due to too large exponent of block size acct: fix potential integer overflow in encode_comp_t() hfs: fix OOB Read in __hfs_brec_find drm/etnaviv: add missing quirks for GC300 brcmfmac: return error when getting invalid max_flowrings from dongle wifi: ath9k: verify the expected usb_endpoints are present wifi: ar5523: Fix use-after-free on ar5523_cmd() timed out ASoC: codecs: rt298: Add quirk for KBL-R RVP platform ipmi: fix memleak when unload ipmi driver drm/amd/display: prevent memory leak qed (gcc13): use u16 for fid to be big enough bpf: make sure skb->len != 0 when redirecting to a tunneling device net: ethernet: ti: Fix return type of netcp_ndo_start_xmit() hamradio: baycom_epp: Fix return type of baycom_send_packet() wifi: brcmfmac: Fix potential shift-out-of-bounds in brcmf_fw_alloc_request() igb: Do not free q_vector unless new one was allocated drm/amdgpu: Fix type of second parameter in trans_msg() callback drm/amdgpu: Fix type of second parameter in odn_edit_dpm_table() callback s390/ctcm: Fix return type of ctc{mp,}m_tx() s390/netiucv: Fix return type of netiucv_tx() s390/lcs: Fix return type of lcs_start_xmit() drm/msm: Use drm_mode_copy() drm/rockchip: Use drm_mode_copy() drm/sti: Use drm_mode_copy() drivers/md/md-bitmap: check the return value of md_bitmap_get_counter() md/raid1: stop mdx_raid1 thread when raid1 array run failed drm/amd/display: fix array index out of bound error in bios parser net: add atomic_long_t to net_device_stats fields mrp: introduce active flags to prevent UAF when applicant uninit ppp: associate skb with a device at tx bpf: Prevent decl_tag from being referenced in func_proto arg ethtool: avoiding integer overflow in ethtool_phys_id() media: dvb-frontends: fix leak of memory fw media: dvbdev: adopts refcnt to avoid UAF media: dvb-usb: fix memory leak in dvb_usb_adapter_init() blk-mq: fix possible memleak when register 'hctx' failed libbpf: Avoid enum forward-declarations in public API in C++ mode regulator: core: fix use_count leakage when handling boot-on mmc: f-sdh30: Add quirks for broken timeout clock capability mmc: renesas_sdhi: better reset from HS400 mode media: si470x: Fix use-after-free in si470x_int_in_callback() clk: st: Fix memory leak in st_of_quadfs_setup() hugetlbfs: fix null-ptr-deref in hugetlbfs_parse_param() drm/fsl-dcu: Fix return type of fsl_dcu_drm_connector_mode_valid() drm/sti: Fix return type of sti_{dvo,hda,hdmi}_connector_mode_valid() orangefs: Fix kmemleak in orangefs_prepare_debugfs_help_string() orangefs: Fix kmemleak in orangefs_{kernel,client}_debug_init() hwmon: (jc42) Fix missing unlock on error in jc42_write() ALSA/ASoC: hda: move/rename snd_hdac_ext_stop_streams to hdac_stream.c ALSA: hda: add snd_hdac_stop_streams() helper ASoC: Intel: Skylake: Fix driver hang during shutdown ASoC: mediatek: mt8173-rt5650-rt5514: fix refcount leak in mt8173_rt5650_rt5514_dev_probe() ASoC: audio-graph-card: fix refcount leak of cpu_ep in __graph_for_each_link() ASoC: rockchip: pdm: Add missing clk_disable_unprepare() in rockchip_pdm_runtime_resume() ASoC: wm8994: Fix potential deadlock ASoC: rockchip: spdif: Add missing clk_disable_unprepare() in rk_spdif_runtime_resume() ASoC: rt5670: Remove unbalanced pm_runtime_put() LoadPin: Ignore the "contents" argument of the LSM hooks pstore: Switch pmsg_lock to an rt_mutex to avoid priority inversion perf debug: Set debug_peo_args and redirect_to_stderr variable to correct values in perf_quiet_option() afs: Fix lost servers_outstanding count pstore: Make sure CONFIG_PSTORE_PMSG selects CONFIG_RT_MUTEXES ima: Simplify ima_lsm_copy_rule ALSA: usb-audio: add the quirk for KT0206 device ALSA: hda/realtek: Add quirk for Lenovo TianYi510Pro-14IOB ALSA: hda/hdmi: Add HP Device 0x8711 to force connect list usb: dwc3: Fix race between dwc3_set_mode and __dwc3_set_mode usb: dwc3: core: defer probe on ulpi_read_id timeout HID: wacom: Ensure bootloader PID is usable in hidraw mode HID: mcp2221: don't connect hidraw reiserfs: Add missing calls to reiserfs_security_free() iio: adc: ad_sigma_delta: do not use internal iio_dev lock iio: adc128s052: add proper .data members in adc128_of_match table regulator: core: fix deadlock on regulator enable gcov: add support for checksum field ovl: fix use inode directly in rcu-walk mode media: dvbdev: fix build warning due to comments media: dvbdev: fix refcnt bug pwm: tegra: Fix 32 bit build usb: dwc3: qcom: Fix memory leak in dwc3_qcom_interconnect_init cifs: fix oops during encryption nvme-pci: fix doorbell buffer value endianness nvme-pci: fix mempool alloc size nvme-pci: fix page size checks ata: ahci: Fix PCS quirk application for suspend nvme: fix the NVME_CMD_EFFECTS_CSE_MASK definition nvmet: don't defer passthrough commands with trivial effects to the workqueue objtool: Fix SEGFAULT powerpc/rtas: avoid device tree lookups in rtas_os_term() powerpc/rtas: avoid scheduling in rtas_os_term() HID: multitouch: fix Asus ExpertBook P2 P2451FA trackpoint HID: plantronics: Additional PIDs for double volume key presses quirk pstore/zone: Use GFP_ATOMIC to allocate zone buffer hfsplus: fix bug causing custom uid and gid being unable to be assigned with mount binfmt: Fix error return code in load_elf_fdpic_binary() ovl: Use ovl mounter's fsuid and fsgid in ovl_link() ALSA: line6: correct midi status byte when receiving data from podxt ALSA: line6: fix stack overflow in line6_midi_transmit pnode: terminate at peers of source md: fix a crash in mempool_free mm, compaction: fix fast_isolate_around() to stay within boundaries f2fs: should put a page when checking the summary info mmc: vub300: fix warning - do not call blocking ops when !TASK_RUNNING tpm: acpi: Call acpi_put_table() to fix memory leak tpm: tpm_crb: Add the missed acpi_put_table() to fix memory leak tpm: tpm_tis: Add the missed acpi_put_table() to fix memory leak SUNRPC: Don't leak netobj memory when gss_read_proxy_verf() fails kcsan: Instrument memcpy/memset/memmove with newer Clang ASoC: Intel/SOF: use set_stream() instead of set_tdm_slots() for HDAudio ASoC/SoundWire: dai: expand 'stream' concept beyond SoundWire net/mlx5e: Fix nullptr in mlx5e_tc_add_fdb_flow() wifi: rtlwifi: remove always-true condition pointed out by GCC 12 wifi: rtlwifi: 8192de: correct checking of IQK reload torture: Exclude "NOHZ tick-stop error" from fatal errors rcu: Prevent lockdep-RCU splats on lock acquisition/release net/af_packet: add VLAN support for AF_PACKET SOCK_RAW GSO net/af_packet: make sure to pull mac header media: stv0288: use explicitly signed char soc: qcom: Select REMAP_MMIO for LLCC driver kest.pl: Fix grub2 menu handling for rebooting ktest.pl minconfig: Unset configs instead of just removing them jbd2: use the correct print format arm64: dts: qcom: sdm845-db845c: correct SPI2 pins drive strength mmc: sdhci-sprd: Disable CLK_AUTO when the clock is less than 400K btrfs: fix resolving backrefs for inline extent followed by prealloc ARM: ux500: do not directly dereference __iomem arm64: dts: qcom: sdm850-lenovo-yoga-c630: correct I2C12 pins drive strength selftests: Use optional USERCFLAGS and USERLDFLAGS PM/devfreq: governor: Add a private governor_data for governor cpufreq: Init completion before kobject_init_and_add() ALSA: patch_realtek: Fix Dell Inspiron Plus 16 ALSA: hda/realtek: Apply dual codec fixup for Dell Latitude laptops dm cache: Fix ABBA deadlock between shrink_slab and dm_cache_metadata_abort dm thin: Fix ABBA deadlock between shrink_slab and dm_pool_abort_metadata dm thin: Use last transaction's pmd->root when commit failed dm thin: resume even if in FAIL mode dm thin: Fix UAF in run_timer_softirq() dm integrity: Fix UAF in dm_integrity_dtr() dm clone: Fix UAF in clone_dtr() dm cache: Fix UAF in destroy() dm cache: set needs_check flag after aborting metadata tracing/hist: Fix out-of-bound write on 'action_data.var_ref_idx' perf/core: Call LSM hook after copying perf_event_attr KVM: nVMX: Inject #GP, not #UD, if "generic" VMXON CR0/CR4 check fails x86/microcode/intel: Do not retry microcode reloading on the APs ftrace/x86: Add back ftrace_expected for ftrace bug reports x86/kprobes: Fix kprobes instruction boudary check with CONFIG_RETHUNK tracing/hist: Fix wrong return value in parse_action_params() tracing: Fix infinite loop in tracing_read_pipe on overflowed print_trace_line staging: media: tegra-video: fix chan->mipi value on error ARM: 9256/1: NWFPE: avoid compiler-generated __aeabi_uldivmod media: dvb-core: Fix double free in dvb_register_device() media: dvb-core: Fix UAF due to refcount races at releasing cifs: fix confusing debug message cifs: fix missing display of three mount options rtc: ds1347: fix value written to century register md/bitmap: Fix bitmap chunk size overflow issues efi: Add iMac Pro 2017 to uefi skip cert quirk wifi: wilc1000: sdio: fix module autoloading ASoC: jz4740-i2s: Handle independent FIFO flush bits ipmi: fix long wait in unload when IPMI disconnect mtd: spi-nor: Check for zero erase size in spi_nor_find_best_erase_type() ima: Fix a potential NULL pointer access in ima_restore_measurement_list ipmi: fix use after free in _ipmi_destroy_user() PCI: Fix pci_device_is_present() for VFs by checking PF PCI/sysfs: Fix double free in error path crypto: n2 - add missing hash statesize driver core: Fix bus_type.match() error handling in __driver_attach() iommu/amd: Fix ivrs_acpihid cmdline parsing code remoteproc: core: Do pm_relax when in RPROC_OFFLINE state parisc: led: Fix potential null-ptr-deref in start_task() device_cgroup: Roll back to original exceptions after copy failure drm/connector: send hotplug uevent on connector cleanup drm/vmwgfx: Validate the box size for the snooped cursor drm/i915/dsi: fix VBT send packet port selection for dual link DSI drm/ingenic: Fix missing platform_driver_unregister() call in ingenic_drm_init() ext4: silence the warning when evicting inode with dioread_nolock ext4: add inode table check in __ext4_get_inode_loc to aovid possible infinite loop ext4: fix use-after-free in ext4_orphan_cleanup ext4: fix undefined behavior in bit shift for ext4_check_flag_values ext4: add EXT4_IGET_BAD flag to prevent unexpected bad inode ext4: add helper to check quota inums ext4: fix bug_on in __es_tree_search caused by bad quota inode ext4: fix reserved cluster accounting in __es_remove_extent() ext4: check and assert if marking an no_delete evicting inode dirty ext4: fix bug_on in __es_tree_search caused by bad boot loader inode ext4: init quota for 'old.inode' in 'ext4_rename' ext4: fix delayed allocation bug in ext4_clu_mapped for bigalloc + inline ext4: fix corruption when online resizing a 1K bigalloc fs ext4: fix error code return to user-space in ext4_get_branch() ext4: avoid BUG_ON when creating xattrs ext4: fix inode leak in ext4_xattr_inode_create() on an error path ext4: initialize quota before expanding inode in setproject ioctl ext4: avoid unaccounted block allocation when expanding inode ext4: allocate extended attribute value in vmalloc area drm/amdgpu: handle polaris10/11 overlap asics (v2) drm/amdgpu: make display pinning more flexible (v2) ARM: renumber bits related to _TIF_WORK_MASK perf/x86/intel/uncore: Generalize I/O stacks to PMON mapping procedure perf/x86/intel/uncore: Clear attr_update properly btrfs: replace strncpy() with strscpy() x86/mce: Get rid of msr_ops x86/MCE/AMD: Clear DFR errors found in THR handler media: s5p-mfc: Fix to handle reference queue during finishing media: s5p-mfc: Clear workbit to handle error condition media: s5p-mfc: Fix in register read and write for H264 perf probe: Use dwarf_attr_integrate as generic DWARF attr accessor perf probe: Fix to get the DW_AT_decl_file and DW_AT_call_file as unsinged data x86/kprobes: Convert to insn_decode() x86/kprobes: Fix optprobe optimization check with CONFIG_RETHUNK staging: media: tegra-video: fix device_node use after free ravb: Fix "failed to switch device to config mode" message during unbind riscv/stacktrace: Fix stack output without ra on the stack top riscv: stacktrace: Fixup ftrace_graph_ret_addr retp argument ext4: goto right label 'failed_mount3a' ext4: correct inconsistent error msg in nojournal mode mm/highmem: Lift memcpy_[to|from]_page to core ext4: use memcpy_to_page() in pagecache_write() fs: ext4: initialize fsdata in pagecache_write() ext4: move functions in super.c ext4: simplify ext4 error translation ext4: fix various seppling typos ext4: fix leaking uninitialized memory in fast-commit journal ext4: use kmemdup() to replace kmalloc + memcpy mbcache: don't reclaim used entries mbcache: add functions to delete entry if unused ext4: remove EA inode entry from mbcache on inode eviction ext4: unindent codeblock in ext4_xattr_block_set() ext4: fix race when reusing xattr blocks mbcache: automatically delete entries from cache on freeing ext4: fix deadlock due to mbcache entry corruption SUNRPC: ensure the matching upcall is in-flight upon downcall bpf: pull before calling skb_postpull_rcsum() drm/panfrost: Fix GEM handle creation ref-counting vmxnet3: correctly report csum_level for encapsulated packet veth: Fix race with AF_XDP exposing old or uninitialized descriptors nfsd: shut down the NFSv4 state objects before the filecache net: hns3: add interrupts re-initialization while doing VF FLR net: sched: fix memory leak in tcindex_set_parms qlcnic: prevent ->dcb use-after-free on qlcnic_dcb_enable() failure nfc: Fix potential resource leaks vhost/vsock: Fix error handling in vhost_vsock_init() vringh: fix range used in iotlb_translate() vhost: fix range used in translate_desc() net/mlx5: Add forgotten cleanup calls into mlx5_init_once() error path net/mlx5: Avoid recovery in probe flows net/mlx5e: IPoIB, Don't allow CQE compression to be turned on by default net/mlx5e: Fix hw mtu initializing at XDP SQ allocation net: amd-xgbe: add missed tasklet_kill net: phy: xgmiitorgmii: Fix refcount leak in xgmiitorgmii_probe RDMA/mlx5: Fix validation of max_rd_atomic caps for DC drm/meson: Reduce the FIFO lines held when AFBC is not used filelock: new helper: vfs_inode_has_locks ceph: switch to vfs_inode_has_locks() to fix file lock bug gpio: sifive: Fix refcount leak in sifive_gpio_probe net: sched: atm: dont intepret cls results when asked to drop net: sched: cbq: dont intepret cls results when asked to drop netfilter: ipset: fix hash:net,port,net hang with /0 subnet netfilter: ipset: Rework long task execution when adding/deleting entries perf tools: Fix resources leak in perf_data__open_dir() drivers/net/bonding/bond_3ad: return when there's no aggregator usb: rndis_host: Secure rndis_query check against int overflow drm/i915: unpin on error in intel_vgpu_shadow_mm_pin() caif: fix memory leak in cfctrl_linkup_request() udf: Fix extension of the last extent in the file ASoC: Intel: bytcr_rt5640: Add quirk for the Advantech MICA-071 tablet nvme: fix multipath crash caused by flush request when blktrace is enabled x86/bugs: Flush IBP in ib_prctl_set() nfsd: fix handling of readdir in v4root vs. mount upcall timeout fbdev: matroxfb: G200eW: Increase max memory from 1 MB to 16 MB riscv: uaccess: fix type of 0 variable on error in get_user() drm/i915/gvt: fix gvt debugfs destroy drm/i915/gvt: fix vgpu debugfs clean in remove ext4: don't allow journal inode to have encrypt flag selftests: set the BUILD variable to absolute path hfs/hfsplus: use WARN_ON for sanity check hfs/hfsplus: avoid WARN_ON() for sanity check, use proper error handling mbcache: Avoid nesting of cache->c_list_lock under bit locks efi: random: combine bootloader provided RNG seed with RNG protocol output io_uring: Fix unsigned 'res' comparison with zero in io_fixup_rw_res() parisc: Align parisc MADV_XXX constants with all other architectures ext4: disable fast-commit of encrypted dir operations ext4: don't set up encryption key during jbd2 transaction fsl_lpuart: Don't enable interrupts too early serial: fixup backport of "serial: Deassert Transmit Enable on probe in driver-specific way" mptcp: mark ops structures as ro_after_init mptcp: remove MPTCP 'ifdef' in TCP SYN cookies mptcp: dedicated request sock for subflow in v6 mptcp: use proper req destructor for IPv6 net: sched: disallow noqueue for qdisc classes net/ulp: prevent ULP without clone op from entering the LISTEN status ALSA: pcm: Move rwsem lock inside snd_ctl_elem_read to prevent UAF ALSA: hda/hdmi: Add a HP device 0x8715 to force connect list ALSA: hda - Enable headset mic on another Dell laptop with ALC3254 Linux 5.10.163 Change-Id: I9026971760be8484f1e1fa607f9f91243cc87785 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
1996 lines
51 KiB
C
1996 lines
51 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/* -*- mode: c; c-basic-offset: 8; -*-
|
|
* vim: noexpandtab sw=8 ts=8 sts=0:
|
|
*
|
|
* dir.c - Operations for configfs directories.
|
|
*
|
|
* Based on sysfs:
|
|
* sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
|
|
*
|
|
* configfs Copyright (C) 2005 Oracle. All rights reserved.
|
|
*/
|
|
|
|
#undef DEBUG
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/fsnotify.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/configfs.h>
|
|
#include "configfs_internal.h"
|
|
|
|
/*
|
|
* Protects mutations of configfs_dirent linkage together with proper i_mutex
|
|
* Also protects mutations of symlinks linkage to target configfs_dirent
|
|
* Mutators of configfs_dirent linkage must *both* have the proper inode locked
|
|
* and configfs_dirent_lock locked, in that order.
|
|
* This allows one to safely traverse configfs_dirent trees and symlinks without
|
|
* having to lock inodes.
|
|
*
|
|
* Protects setting of CONFIGFS_USET_DROPPING: checking the flag
|
|
* unlocked is not reliable unless in detach_groups() called from
|
|
* rmdir()/unregister() and from configfs_attach_group()
|
|
*/
|
|
DEFINE_SPINLOCK(configfs_dirent_lock);
|
|
|
|
/*
|
|
* All of link_obj/unlink_obj/link_group/unlink_group require that
|
|
* subsys->su_mutex is held.
|
|
* But parent configfs_subsystem is NULL when config_item is root.
|
|
* Use this mutex when config_item is root.
|
|
*/
|
|
static DEFINE_MUTEX(configfs_subsystem_mutex);
|
|
|
|
static void configfs_d_iput(struct dentry * dentry,
|
|
struct inode * inode)
|
|
{
|
|
struct configfs_dirent *sd = dentry->d_fsdata;
|
|
|
|
if (sd) {
|
|
/* Coordinate with configfs_readdir */
|
|
spin_lock(&configfs_dirent_lock);
|
|
/*
|
|
* Set sd->s_dentry to null only when this dentry is the one
|
|
* that is going to be killed. Otherwise configfs_d_iput may
|
|
* run just after configfs_attach_attr and set sd->s_dentry to
|
|
* NULL even it's still in use.
|
|
*/
|
|
if (sd->s_dentry == dentry)
|
|
sd->s_dentry = NULL;
|
|
|
|
spin_unlock(&configfs_dirent_lock);
|
|
configfs_put(sd);
|
|
}
|
|
iput(inode);
|
|
}
|
|
|
|
const struct dentry_operations configfs_dentry_ops = {
|
|
.d_iput = configfs_d_iput,
|
|
.d_delete = always_delete_dentry,
|
|
};
|
|
|
|
#ifdef CONFIG_LOCKDEP
|
|
|
|
/*
|
|
* Helpers to make lockdep happy with our recursive locking of default groups'
|
|
* inodes (see configfs_attach_group() and configfs_detach_group()).
|
|
* We put default groups i_mutexes in separate classes according to their depth
|
|
* from the youngest non-default group ancestor.
|
|
*
|
|
* For a non-default group A having default groups A/B, A/C, and A/C/D, default
|
|
* groups A/B and A/C will have their inode's mutex in class
|
|
* default_group_class[0], and default group A/C/D will be in
|
|
* default_group_class[1].
|
|
*
|
|
* The lock classes are declared and assigned in inode.c, according to the
|
|
* s_depth value.
|
|
* The s_depth value is initialized to -1, adjusted to >= 0 when attaching
|
|
* default groups, and reset to -1 when all default groups are attached. During
|
|
* attachment, if configfs_create() sees s_depth > 0, the lock class of the new
|
|
* inode's mutex is set to default_group_class[s_depth - 1].
|
|
*/
|
|
|
|
static void configfs_init_dirent_depth(struct configfs_dirent *sd)
|
|
{
|
|
sd->s_depth = -1;
|
|
}
|
|
|
|
static void configfs_set_dir_dirent_depth(struct configfs_dirent *parent_sd,
|
|
struct configfs_dirent *sd)
|
|
{
|
|
int parent_depth = parent_sd->s_depth;
|
|
|
|
if (parent_depth >= 0)
|
|
sd->s_depth = parent_depth + 1;
|
|
}
|
|
|
|
static void
|
|
configfs_adjust_dir_dirent_depth_before_populate(struct configfs_dirent *sd)
|
|
{
|
|
/*
|
|
* item's i_mutex class is already setup, so s_depth is now only
|
|
* used to set new sub-directories s_depth, which is always done
|
|
* with item's i_mutex locked.
|
|
*/
|
|
/*
|
|
* sd->s_depth == -1 iff we are a non default group.
|
|
* else (we are a default group) sd->s_depth > 0 (see
|
|
* create_dir()).
|
|
*/
|
|
if (sd->s_depth == -1)
|
|
/*
|
|
* We are a non default group and we are going to create
|
|
* default groups.
|
|
*/
|
|
sd->s_depth = 0;
|
|
}
|
|
|
|
static void
|
|
configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
|
|
{
|
|
/* We will not create default groups anymore. */
|
|
sd->s_depth = -1;
|
|
}
|
|
|
|
#else /* CONFIG_LOCKDEP */
|
|
|
|
static void configfs_init_dirent_depth(struct configfs_dirent *sd)
|
|
{
|
|
}
|
|
|
|
static void configfs_set_dir_dirent_depth(struct configfs_dirent *parent_sd,
|
|
struct configfs_dirent *sd)
|
|
{
|
|
}
|
|
|
|
static void
|
|
configfs_adjust_dir_dirent_depth_before_populate(struct configfs_dirent *sd)
|
|
{
|
|
}
|
|
|
|
static void
|
|
configfs_adjust_dir_dirent_depth_after_populate(struct configfs_dirent *sd)
|
|
{
|
|
}
|
|
|
|
#endif /* CONFIG_LOCKDEP */
|
|
|
|
static struct configfs_fragment *new_fragment(void)
|
|
{
|
|
struct configfs_fragment *p;
|
|
|
|
p = kmalloc(sizeof(struct configfs_fragment), GFP_KERNEL);
|
|
if (p) {
|
|
atomic_set(&p->frag_count, 1);
|
|
init_rwsem(&p->frag_sem);
|
|
p->frag_dead = false;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void put_fragment(struct configfs_fragment *frag)
|
|
{
|
|
if (frag && atomic_dec_and_test(&frag->frag_count))
|
|
kfree(frag);
|
|
}
|
|
|
|
struct configfs_fragment *get_fragment(struct configfs_fragment *frag)
|
|
{
|
|
if (likely(frag))
|
|
atomic_inc(&frag->frag_count);
|
|
return frag;
|
|
}
|
|
|
|
/*
|
|
* Allocates a new configfs_dirent and links it to the parent configfs_dirent
|
|
*/
|
|
static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *parent_sd,
|
|
void *element, int type,
|
|
struct configfs_fragment *frag)
|
|
{
|
|
struct configfs_dirent * sd;
|
|
|
|
sd = kmem_cache_zalloc(configfs_dir_cachep, GFP_KERNEL);
|
|
if (!sd)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
atomic_set(&sd->s_count, 1);
|
|
INIT_LIST_HEAD(&sd->s_children);
|
|
sd->s_element = element;
|
|
sd->s_type = type;
|
|
configfs_init_dirent_depth(sd);
|
|
spin_lock(&configfs_dirent_lock);
|
|
if (parent_sd->s_type & CONFIGFS_USET_DROPPING) {
|
|
spin_unlock(&configfs_dirent_lock);
|
|
kmem_cache_free(configfs_dir_cachep, sd);
|
|
return ERR_PTR(-ENOENT);
|
|
}
|
|
sd->s_frag = get_fragment(frag);
|
|
list_add(&sd->s_sibling, &parent_sd->s_children);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
|
|
return sd;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Return -EEXIST if there is already a configfs element with the same
|
|
* name for the same parent.
|
|
*
|
|
* called with parent inode's i_mutex held
|
|
*/
|
|
static int configfs_dirent_exists(struct configfs_dirent *parent_sd,
|
|
const unsigned char *new)
|
|
{
|
|
struct configfs_dirent * sd;
|
|
|
|
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
|
|
if (sd->s_element) {
|
|
const unsigned char *existing = configfs_get_name(sd);
|
|
if (strcmp(existing, new))
|
|
continue;
|
|
else
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int configfs_make_dirent(struct configfs_dirent * parent_sd,
|
|
struct dentry * dentry, void * element,
|
|
umode_t mode, int type, struct configfs_fragment *frag)
|
|
{
|
|
struct configfs_dirent * sd;
|
|
|
|
sd = configfs_new_dirent(parent_sd, element, type, frag);
|
|
if (IS_ERR(sd))
|
|
return PTR_ERR(sd);
|
|
|
|
sd->s_mode = mode;
|
|
sd->s_dentry = dentry;
|
|
if (dentry)
|
|
dentry->d_fsdata = configfs_get(sd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void configfs_remove_dirent(struct dentry *dentry)
|
|
{
|
|
struct configfs_dirent *sd = dentry->d_fsdata;
|
|
|
|
if (!sd)
|
|
return;
|
|
spin_lock(&configfs_dirent_lock);
|
|
list_del_init(&sd->s_sibling);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
configfs_put(sd);
|
|
}
|
|
|
|
/**
|
|
* configfs_create_dir - create a directory for an config_item.
|
|
* @item: config_itemwe're creating directory for.
|
|
* @dentry: config_item's dentry.
|
|
*
|
|
* Note: user-created entries won't be allowed under this new directory
|
|
* until it is validated by configfs_dir_set_ready()
|
|
*/
|
|
|
|
static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
|
|
struct configfs_fragment *frag)
|
|
{
|
|
int error;
|
|
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
|
|
struct dentry *p = dentry->d_parent;
|
|
struct inode *inode;
|
|
|
|
BUG_ON(!item);
|
|
|
|
error = configfs_dirent_exists(p->d_fsdata, dentry->d_name.name);
|
|
if (unlikely(error))
|
|
return error;
|
|
|
|
error = configfs_make_dirent(p->d_fsdata, dentry, item, mode,
|
|
CONFIGFS_DIR | CONFIGFS_USET_CREATING,
|
|
frag);
|
|
if (unlikely(error))
|
|
return error;
|
|
|
|
configfs_set_dir_dirent_depth(p->d_fsdata, dentry->d_fsdata);
|
|
inode = configfs_create(dentry, mode);
|
|
if (IS_ERR(inode))
|
|
goto out_remove;
|
|
|
|
inode->i_op = &configfs_dir_inode_operations;
|
|
inode->i_fop = &configfs_dir_operations;
|
|
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
|
inc_nlink(inode);
|
|
d_instantiate(dentry, inode);
|
|
/* already hashed */
|
|
dget(dentry); /* pin directory dentries in core */
|
|
inc_nlink(d_inode(p));
|
|
item->ci_dentry = dentry;
|
|
return 0;
|
|
|
|
out_remove:
|
|
configfs_put(dentry->d_fsdata);
|
|
configfs_remove_dirent(dentry);
|
|
return PTR_ERR(inode);
|
|
}
|
|
|
|
/*
|
|
* Allow userspace to create new entries under a new directory created with
|
|
* configfs_create_dir(), and under all of its chidlren directories recursively.
|
|
* @sd configfs_dirent of the new directory to validate
|
|
*
|
|
* Caller must hold configfs_dirent_lock.
|
|
*/
|
|
static void configfs_dir_set_ready(struct configfs_dirent *sd)
|
|
{
|
|
struct configfs_dirent *child_sd;
|
|
|
|
sd->s_type &= ~CONFIGFS_USET_CREATING;
|
|
list_for_each_entry(child_sd, &sd->s_children, s_sibling)
|
|
if (child_sd->s_type & CONFIGFS_USET_CREATING)
|
|
configfs_dir_set_ready(child_sd);
|
|
}
|
|
|
|
/*
|
|
* Check that a directory does not belong to a directory hierarchy being
|
|
* attached and not validated yet.
|
|
* @sd configfs_dirent of the directory to check
|
|
*
|
|
* @return non-zero iff the directory was validated
|
|
*
|
|
* Note: takes configfs_dirent_lock, so the result may change from false to true
|
|
* in two consecutive calls, but never from true to false.
|
|
*/
|
|
int configfs_dirent_is_ready(struct configfs_dirent *sd)
|
|
{
|
|
int ret;
|
|
|
|
spin_lock(&configfs_dirent_lock);
|
|
ret = !(sd->s_type & CONFIGFS_USET_CREATING);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int configfs_create_link(struct configfs_dirent *target, struct dentry *parent,
|
|
struct dentry *dentry, char *body)
|
|
{
|
|
int err = 0;
|
|
umode_t mode = S_IFLNK | S_IRWXUGO;
|
|
struct configfs_dirent *p = parent->d_fsdata;
|
|
struct inode *inode;
|
|
|
|
err = configfs_make_dirent(p, dentry, target, mode, CONFIGFS_ITEM_LINK,
|
|
p->s_frag);
|
|
if (err)
|
|
return err;
|
|
|
|
inode = configfs_create(dentry, mode);
|
|
if (IS_ERR(inode))
|
|
goto out_remove;
|
|
|
|
inode->i_link = body;
|
|
inode->i_op = &configfs_symlink_inode_operations;
|
|
d_instantiate(dentry, inode);
|
|
dget(dentry); /* pin link dentries in core */
|
|
return 0;
|
|
|
|
out_remove:
|
|
configfs_put(dentry->d_fsdata);
|
|
configfs_remove_dirent(dentry);
|
|
return PTR_ERR(inode);
|
|
}
|
|
|
|
static void remove_dir(struct dentry * d)
|
|
{
|
|
struct dentry * parent = dget(d->d_parent);
|
|
|
|
configfs_remove_dirent(d);
|
|
|
|
if (d_really_is_positive(d))
|
|
simple_rmdir(d_inode(parent),d);
|
|
|
|
pr_debug(" o %pd removing done (%d)\n", d, d_count(d));
|
|
|
|
dput(parent);
|
|
}
|
|
|
|
/**
|
|
* configfs_remove_dir - remove an config_item's directory.
|
|
* @item: config_item we're removing.
|
|
*
|
|
* The only thing special about this is that we remove any files in
|
|
* the directory before we remove the directory, and we've inlined
|
|
* what used to be configfs_rmdir() below, instead of calling separately.
|
|
*
|
|
* Caller holds the mutex of the item's inode
|
|
*/
|
|
|
|
static void configfs_remove_dir(struct config_item * item)
|
|
{
|
|
struct dentry * dentry = dget(item->ci_dentry);
|
|
|
|
if (!dentry)
|
|
return;
|
|
|
|
remove_dir(dentry);
|
|
/**
|
|
* Drop reference from dget() on entrance.
|
|
*/
|
|
dput(dentry);
|
|
}
|
|
|
|
|
|
/* attaches attribute's configfs_dirent to the dentry corresponding to the
|
|
* attribute file
|
|
*/
|
|
static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * dentry)
|
|
{
|
|
struct configfs_attribute * attr = sd->s_element;
|
|
struct inode *inode;
|
|
|
|
spin_lock(&configfs_dirent_lock);
|
|
dentry->d_fsdata = configfs_get(sd);
|
|
sd->s_dentry = dentry;
|
|
spin_unlock(&configfs_dirent_lock);
|
|
|
|
inode = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG);
|
|
if (IS_ERR(inode)) {
|
|
configfs_put(sd);
|
|
return PTR_ERR(inode);
|
|
}
|
|
if (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) {
|
|
inode->i_size = 0;
|
|
inode->i_fop = &configfs_bin_file_operations;
|
|
} else {
|
|
inode->i_size = PAGE_SIZE;
|
|
inode->i_fop = &configfs_file_operations;
|
|
}
|
|
d_add(dentry, inode);
|
|
return 0;
|
|
}
|
|
|
|
static struct dentry * configfs_lookup(struct inode *dir,
|
|
struct dentry *dentry,
|
|
unsigned int flags)
|
|
{
|
|
struct configfs_dirent * parent_sd = dentry->d_parent->d_fsdata;
|
|
struct configfs_dirent * sd;
|
|
int found = 0;
|
|
int err;
|
|
|
|
/*
|
|
* Fake invisibility if dir belongs to a group/default groups hierarchy
|
|
* being attached
|
|
*
|
|
* This forbids userspace to read/write attributes of items which may
|
|
* not complete their initialization, since the dentries of the
|
|
* attributes won't be instantiated.
|
|
*/
|
|
err = -ENOENT;
|
|
if (!configfs_dirent_is_ready(parent_sd))
|
|
goto out;
|
|
|
|
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
|
|
if (sd->s_type & CONFIGFS_NOT_PINNED) {
|
|
const unsigned char * name = configfs_get_name(sd);
|
|
|
|
if (strcmp(name, dentry->d_name.name))
|
|
continue;
|
|
|
|
found = 1;
|
|
err = configfs_attach_attr(sd, dentry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
/*
|
|
* If it doesn't exist and it isn't a NOT_PINNED item,
|
|
* it must be negative.
|
|
*/
|
|
if (dentry->d_name.len > NAME_MAX)
|
|
return ERR_PTR(-ENAMETOOLONG);
|
|
d_add(dentry, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
out:
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
/*
|
|
* Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are
|
|
* attributes and are removed by rmdir(). We recurse, setting
|
|
* CONFIGFS_USET_DROPPING on all children that are candidates for
|
|
* default detach.
|
|
* If there is an error, the caller will reset the flags via
|
|
* configfs_detach_rollback().
|
|
*/
|
|
static int configfs_detach_prep(struct dentry *dentry, struct dentry **wait)
|
|
{
|
|
struct configfs_dirent *parent_sd = dentry->d_fsdata;
|
|
struct configfs_dirent *sd;
|
|
int ret;
|
|
|
|
/* Mark that we're trying to drop the group */
|
|
parent_sd->s_type |= CONFIGFS_USET_DROPPING;
|
|
|
|
ret = -EBUSY;
|
|
if (parent_sd->s_links)
|
|
goto out;
|
|
|
|
ret = 0;
|
|
list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
|
|
if (!sd->s_element ||
|
|
(sd->s_type & CONFIGFS_NOT_PINNED))
|
|
continue;
|
|
if (sd->s_type & CONFIGFS_USET_DEFAULT) {
|
|
/* Abort if racing with mkdir() */
|
|
if (sd->s_type & CONFIGFS_USET_IN_MKDIR) {
|
|
if (wait)
|
|
*wait= dget(sd->s_dentry);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/*
|
|
* Yup, recursive. If there's a problem, blame
|
|
* deep nesting of default_groups
|
|
*/
|
|
ret = configfs_detach_prep(sd->s_dentry, wait);
|
|
if (!ret)
|
|
continue;
|
|
} else
|
|
ret = -ENOTEMPTY;
|
|
|
|
break;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Walk the tree, resetting CONFIGFS_USET_DROPPING wherever it was
|
|
* set.
|
|
*/
|
|
static void configfs_detach_rollback(struct dentry *dentry)
|
|
{
|
|
struct configfs_dirent *parent_sd = dentry->d_fsdata;
|
|
struct configfs_dirent *sd;
|
|
|
|
parent_sd->s_type &= ~CONFIGFS_USET_DROPPING;
|
|
|
|
list_for_each_entry(sd, &parent_sd->s_children, s_sibling)
|
|
if (sd->s_type & CONFIGFS_USET_DEFAULT)
|
|
configfs_detach_rollback(sd->s_dentry);
|
|
}
|
|
|
|
static void detach_attrs(struct config_item * item)
|
|
{
|
|
struct dentry * dentry = dget(item->ci_dentry);
|
|
struct configfs_dirent * parent_sd;
|
|
struct configfs_dirent * sd, * tmp;
|
|
|
|
if (!dentry)
|
|
return;
|
|
|
|
pr_debug("configfs %s: dropping attrs for dir\n",
|
|
dentry->d_name.name);
|
|
|
|
parent_sd = dentry->d_fsdata;
|
|
list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
|
|
if (!sd->s_element || !(sd->s_type & CONFIGFS_NOT_PINNED))
|
|
continue;
|
|
spin_lock(&configfs_dirent_lock);
|
|
list_del_init(&sd->s_sibling);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
configfs_drop_dentry(sd, dentry);
|
|
configfs_put(sd);
|
|
}
|
|
|
|
/**
|
|
* Drop reference from dget() on entrance.
|
|
*/
|
|
dput(dentry);
|
|
}
|
|
|
|
static int populate_attrs(struct config_item *item)
|
|
{
|
|
const struct config_item_type *t = item->ci_type;
|
|
struct configfs_attribute *attr;
|
|
struct configfs_bin_attribute *bin_attr;
|
|
int error = 0;
|
|
int i;
|
|
|
|
if (!t)
|
|
return -EINVAL;
|
|
if (t->ct_attrs) {
|
|
for (i = 0; (attr = t->ct_attrs[i]) != NULL; i++) {
|
|
if ((error = configfs_create_file(item, attr)))
|
|
break;
|
|
}
|
|
}
|
|
if (t->ct_bin_attrs) {
|
|
for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) {
|
|
error = configfs_create_bin_file(item, bin_attr);
|
|
if (error)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (error)
|
|
detach_attrs(item);
|
|
|
|
return error;
|
|
}
|
|
|
|
static int configfs_attach_group(struct config_item *parent_item,
|
|
struct config_item *item,
|
|
struct dentry *dentry,
|
|
struct configfs_fragment *frag);
|
|
static void configfs_detach_group(struct config_item *item);
|
|
|
|
static void detach_groups(struct config_group *group)
|
|
{
|
|
struct dentry * dentry = dget(group->cg_item.ci_dentry);
|
|
struct dentry *child;
|
|
struct configfs_dirent *parent_sd;
|
|
struct configfs_dirent *sd, *tmp;
|
|
|
|
if (!dentry)
|
|
return;
|
|
|
|
parent_sd = dentry->d_fsdata;
|
|
list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
|
|
if (!sd->s_element ||
|
|
!(sd->s_type & CONFIGFS_USET_DEFAULT))
|
|
continue;
|
|
|
|
child = sd->s_dentry;
|
|
|
|
inode_lock(d_inode(child));
|
|
|
|
configfs_detach_group(sd->s_element);
|
|
d_inode(child)->i_flags |= S_DEAD;
|
|
dont_mount(child);
|
|
|
|
inode_unlock(d_inode(child));
|
|
|
|
d_delete(child);
|
|
dput(child);
|
|
}
|
|
|
|
/**
|
|
* Drop reference from dget() on entrance.
|
|
*/
|
|
dput(dentry);
|
|
}
|
|
|
|
/*
|
|
* This fakes mkdir(2) on a default_groups[] entry. It
|
|
* creates a dentry, attachs it, and then does fixup
|
|
* on the sd->s_type.
|
|
*
|
|
* We could, perhaps, tweak our parent's ->mkdir for a minute and
|
|
* try using vfs_mkdir. Just a thought.
|
|
*/
|
|
static int create_default_group(struct config_group *parent_group,
|
|
struct config_group *group,
|
|
struct configfs_fragment *frag)
|
|
{
|
|
int ret;
|
|
struct configfs_dirent *sd;
|
|
/* We trust the caller holds a reference to parent */
|
|
struct dentry *child, *parent = parent_group->cg_item.ci_dentry;
|
|
|
|
if (!group->cg_item.ci_name)
|
|
group->cg_item.ci_name = group->cg_item.ci_namebuf;
|
|
|
|
ret = -ENOMEM;
|
|
child = d_alloc_name(parent, group->cg_item.ci_name);
|
|
if (child) {
|
|
d_add(child, NULL);
|
|
|
|
ret = configfs_attach_group(&parent_group->cg_item,
|
|
&group->cg_item, child, frag);
|
|
if (!ret) {
|
|
sd = child->d_fsdata;
|
|
sd->s_type |= CONFIGFS_USET_DEFAULT;
|
|
} else {
|
|
BUG_ON(d_inode(child));
|
|
d_drop(child);
|
|
dput(child);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int populate_groups(struct config_group *group,
|
|
struct configfs_fragment *frag)
|
|
{
|
|
struct config_group *new_group;
|
|
int ret = 0;
|
|
|
|
list_for_each_entry(new_group, &group->default_groups, group_entry) {
|
|
ret = create_default_group(group, new_group, frag);
|
|
if (ret) {
|
|
detach_groups(group);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void configfs_remove_default_groups(struct config_group *group)
|
|
{
|
|
struct config_group *g, *n;
|
|
|
|
list_for_each_entry_safe(g, n, &group->default_groups, group_entry) {
|
|
list_del(&g->group_entry);
|
|
config_item_put(&g->cg_item);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(configfs_remove_default_groups);
|
|
|
|
/*
|
|
* All of link_obj/unlink_obj/link_group/unlink_group require that
|
|
* subsys->su_mutex is held.
|
|
*/
|
|
|
|
static void unlink_obj(struct config_item *item)
|
|
{
|
|
struct config_group *group;
|
|
|
|
group = item->ci_group;
|
|
if (group) {
|
|
list_del_init(&item->ci_entry);
|
|
|
|
item->ci_group = NULL;
|
|
item->ci_parent = NULL;
|
|
|
|
/* Drop the reference for ci_entry */
|
|
config_item_put(item);
|
|
|
|
/* Drop the reference for ci_parent */
|
|
config_group_put(group);
|
|
}
|
|
}
|
|
|
|
static void link_obj(struct config_item *parent_item, struct config_item *item)
|
|
{
|
|
/*
|
|
* Parent seems redundant with group, but it makes certain
|
|
* traversals much nicer.
|
|
*/
|
|
item->ci_parent = parent_item;
|
|
|
|
/*
|
|
* We hold a reference on the parent for the child's ci_parent
|
|
* link.
|
|
*/
|
|
item->ci_group = config_group_get(to_config_group(parent_item));
|
|
list_add_tail(&item->ci_entry, &item->ci_group->cg_children);
|
|
|
|
/*
|
|
* We hold a reference on the child for ci_entry on the parent's
|
|
* cg_children
|
|
*/
|
|
config_item_get(item);
|
|
}
|
|
|
|
static void unlink_group(struct config_group *group)
|
|
{
|
|
struct config_group *new_group;
|
|
|
|
list_for_each_entry(new_group, &group->default_groups, group_entry)
|
|
unlink_group(new_group);
|
|
|
|
group->cg_subsys = NULL;
|
|
unlink_obj(&group->cg_item);
|
|
}
|
|
|
|
static void link_group(struct config_group *parent_group, struct config_group *group)
|
|
{
|
|
struct config_group *new_group;
|
|
struct configfs_subsystem *subsys = NULL; /* gcc is a turd */
|
|
|
|
link_obj(&parent_group->cg_item, &group->cg_item);
|
|
|
|
if (parent_group->cg_subsys)
|
|
subsys = parent_group->cg_subsys;
|
|
else if (configfs_is_root(&parent_group->cg_item))
|
|
subsys = to_configfs_subsystem(group);
|
|
else
|
|
BUG();
|
|
group->cg_subsys = subsys;
|
|
|
|
list_for_each_entry(new_group, &group->default_groups, group_entry)
|
|
link_group(group, new_group);
|
|
}
|
|
|
|
/*
|
|
* The goal is that configfs_attach_item() (and
|
|
* configfs_attach_group()) can be called from either the VFS or this
|
|
* module. That is, they assume that the items have been created,
|
|
* the dentry allocated, and the dcache is all ready to go.
|
|
*
|
|
* If they fail, they must clean up after themselves as if they
|
|
* had never been called. The caller (VFS or local function) will
|
|
* handle cleaning up the dcache bits.
|
|
*
|
|
* configfs_detach_group() and configfs_detach_item() behave similarly on
|
|
* the way out. They assume that the proper semaphores are held, they
|
|
* clean up the configfs items, and they expect their callers will
|
|
* handle the dcache bits.
|
|
*/
|
|
static int configfs_attach_item(struct config_item *parent_item,
|
|
struct config_item *item,
|
|
struct dentry *dentry,
|
|
struct configfs_fragment *frag)
|
|
{
|
|
int ret;
|
|
|
|
ret = configfs_create_dir(item, dentry, frag);
|
|
if (!ret) {
|
|
ret = populate_attrs(item);
|
|
if (ret) {
|
|
/*
|
|
* We are going to remove an inode and its dentry but
|
|
* the VFS may already have hit and used them. Thus,
|
|
* we must lock them as rmdir() would.
|
|
*/
|
|
inode_lock(d_inode(dentry));
|
|
configfs_remove_dir(item);
|
|
d_inode(dentry)->i_flags |= S_DEAD;
|
|
dont_mount(dentry);
|
|
inode_unlock(d_inode(dentry));
|
|
d_delete(dentry);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Caller holds the mutex of the item's inode */
|
|
static void configfs_detach_item(struct config_item *item)
|
|
{
|
|
detach_attrs(item);
|
|
configfs_remove_dir(item);
|
|
}
|
|
|
|
static int configfs_attach_group(struct config_item *parent_item,
|
|
struct config_item *item,
|
|
struct dentry *dentry,
|
|
struct configfs_fragment *frag)
|
|
{
|
|
int ret;
|
|
struct configfs_dirent *sd;
|
|
|
|
ret = configfs_attach_item(parent_item, item, dentry, frag);
|
|
if (!ret) {
|
|
sd = dentry->d_fsdata;
|
|
sd->s_type |= CONFIGFS_USET_DIR;
|
|
|
|
/*
|
|
* FYI, we're faking mkdir in populate_groups()
|
|
* We must lock the group's inode to avoid races with the VFS
|
|
* which can already hit the inode and try to add/remove entries
|
|
* under it.
|
|
*
|
|
* We must also lock the inode to remove it safely in case of
|
|
* error, as rmdir() would.
|
|
*/
|
|
inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
|
|
configfs_adjust_dir_dirent_depth_before_populate(sd);
|
|
ret = populate_groups(to_config_group(item), frag);
|
|
if (ret) {
|
|
configfs_detach_item(item);
|
|
d_inode(dentry)->i_flags |= S_DEAD;
|
|
dont_mount(dentry);
|
|
}
|
|
configfs_adjust_dir_dirent_depth_after_populate(sd);
|
|
inode_unlock(d_inode(dentry));
|
|
if (ret)
|
|
d_delete(dentry);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Caller holds the mutex of the group's inode */
|
|
static void configfs_detach_group(struct config_item *item)
|
|
{
|
|
detach_groups(to_config_group(item));
|
|
configfs_detach_item(item);
|
|
}
|
|
|
|
/*
|
|
* After the item has been detached from the filesystem view, we are
|
|
* ready to tear it out of the hierarchy. Notify the client before
|
|
* we do that so they can perform any cleanup that requires
|
|
* navigating the hierarchy. A client does not need to provide this
|
|
* callback. The subsystem semaphore MUST be held by the caller, and
|
|
* references must be valid for both items. It also assumes the
|
|
* caller has validated ci_type.
|
|
*/
|
|
static void client_disconnect_notify(struct config_item *parent_item,
|
|
struct config_item *item)
|
|
{
|
|
const struct config_item_type *type;
|
|
|
|
type = parent_item->ci_type;
|
|
BUG_ON(!type);
|
|
|
|
if (type->ct_group_ops && type->ct_group_ops->disconnect_notify)
|
|
type->ct_group_ops->disconnect_notify(to_config_group(parent_item),
|
|
item);
|
|
}
|
|
|
|
/*
|
|
* Drop the initial reference from make_item()/make_group()
|
|
* This function assumes that reference is held on item
|
|
* and that item holds a valid reference to the parent. Also, it
|
|
* assumes the caller has validated ci_type.
|
|
*/
|
|
static void client_drop_item(struct config_item *parent_item,
|
|
struct config_item *item)
|
|
{
|
|
const struct config_item_type *type;
|
|
|
|
type = parent_item->ci_type;
|
|
BUG_ON(!type);
|
|
|
|
/*
|
|
* If ->drop_item() exists, it is responsible for the
|
|
* config_item_put().
|
|
*/
|
|
if (type->ct_group_ops && type->ct_group_ops->drop_item)
|
|
type->ct_group_ops->drop_item(to_config_group(parent_item),
|
|
item);
|
|
else
|
|
config_item_put(item);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static void configfs_dump_one(struct configfs_dirent *sd, int level)
|
|
{
|
|
pr_info("%*s\"%s\":\n", level, " ", configfs_get_name(sd));
|
|
|
|
#define type_print(_type) if (sd->s_type & _type) pr_info("%*s %s\n", level, " ", #_type);
|
|
type_print(CONFIGFS_ROOT);
|
|
type_print(CONFIGFS_DIR);
|
|
type_print(CONFIGFS_ITEM_ATTR);
|
|
type_print(CONFIGFS_ITEM_LINK);
|
|
type_print(CONFIGFS_USET_DIR);
|
|
type_print(CONFIGFS_USET_DEFAULT);
|
|
type_print(CONFIGFS_USET_DROPPING);
|
|
#undef type_print
|
|
}
|
|
|
|
static int configfs_dump(struct configfs_dirent *sd, int level)
|
|
{
|
|
struct configfs_dirent *child_sd;
|
|
int ret = 0;
|
|
|
|
configfs_dump_one(sd, level);
|
|
|
|
if (!(sd->s_type & (CONFIGFS_DIR|CONFIGFS_ROOT)))
|
|
return 0;
|
|
|
|
list_for_each_entry(child_sd, &sd->s_children, s_sibling) {
|
|
ret = configfs_dump(child_sd, level + 2);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* configfs_depend_item() and configfs_undepend_item()
|
|
*
|
|
* WARNING: Do not call these from a configfs callback!
|
|
*
|
|
* This describes these functions and their helpers.
|
|
*
|
|
* Allow another kernel system to depend on a config_item. If this
|
|
* happens, the item cannot go away until the dependent can live without
|
|
* it. The idea is to give client modules as simple an interface as
|
|
* possible. When a system asks them to depend on an item, they just
|
|
* call configfs_depend_item(). If the item is live and the client
|
|
* driver is in good shape, we'll happily do the work for them.
|
|
*
|
|
* Why is the locking complex? Because configfs uses the VFS to handle
|
|
* all locking, but this function is called outside the normal
|
|
* VFS->configfs path. So it must take VFS locks to prevent the
|
|
* VFS->configfs stuff (configfs_mkdir(), configfs_rmdir(), etc). This is
|
|
* why you can't call these functions underneath configfs callbacks.
|
|
*
|
|
* Note, btw, that this can be called at *any* time, even when a configfs
|
|
* subsystem isn't registered, or when configfs is loading or unloading.
|
|
* Just like configfs_register_subsystem(). So we take the same
|
|
* precautions. We pin the filesystem. We lock configfs_dirent_lock.
|
|
* If we can find the target item in the
|
|
* configfs tree, it must be part of the subsystem tree as well, so we
|
|
* do not need the subsystem semaphore. Holding configfs_dirent_lock helps
|
|
* locking out mkdir() and rmdir(), who might be racing us.
|
|
*/
|
|
|
|
/*
|
|
* configfs_depend_prep()
|
|
*
|
|
* Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are
|
|
* attributes. This is similar but not the same to configfs_detach_prep().
|
|
* Note that configfs_detach_prep() expects the parent to be locked when it
|
|
* is called, but we lock the parent *inside* configfs_depend_prep(). We
|
|
* do that so we can unlock it if we find nothing.
|
|
*
|
|
* Here we do a depth-first search of the dentry hierarchy looking for
|
|
* our object.
|
|
* We deliberately ignore items tagged as dropping since they are virtually
|
|
* dead, as well as items in the middle of attachment since they virtually
|
|
* do not exist yet. This completes the locking out of racing mkdir() and
|
|
* rmdir().
|
|
* Note: subdirectories in the middle of attachment start with s_type =
|
|
* CONFIGFS_DIR|CONFIGFS_USET_CREATING set by create_dir(). When
|
|
* CONFIGFS_USET_CREATING is set, we ignore the item. The actual set of
|
|
* s_type is in configfs_new_dirent(), which has configfs_dirent_lock.
|
|
*
|
|
* If the target is not found, -ENOENT is bubbled up.
|
|
*
|
|
* This adds a requirement that all config_items be unique!
|
|
*
|
|
* This is recursive. There isn't
|
|
* much on the stack, though, so folks that need this function - be careful
|
|
* about your stack! Patches will be accepted to make it iterative.
|
|
*/
|
|
static int configfs_depend_prep(struct dentry *origin,
|
|
struct config_item *target)
|
|
{
|
|
struct configfs_dirent *child_sd, *sd;
|
|
int ret = 0;
|
|
|
|
BUG_ON(!origin || !origin->d_fsdata);
|
|
sd = origin->d_fsdata;
|
|
|
|
if (sd->s_element == target) /* Boo-yah */
|
|
goto out;
|
|
|
|
list_for_each_entry(child_sd, &sd->s_children, s_sibling) {
|
|
if ((child_sd->s_type & CONFIGFS_DIR) &&
|
|
!(child_sd->s_type & CONFIGFS_USET_DROPPING) &&
|
|
!(child_sd->s_type & CONFIGFS_USET_CREATING)) {
|
|
ret = configfs_depend_prep(child_sd->s_dentry,
|
|
target);
|
|
if (!ret)
|
|
goto out; /* Child path boo-yah */
|
|
}
|
|
}
|
|
|
|
/* We looped all our children and didn't find target */
|
|
ret = -ENOENT;
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int configfs_do_depend_item(struct dentry *subsys_dentry,
|
|
struct config_item *target)
|
|
{
|
|
struct configfs_dirent *p;
|
|
int ret;
|
|
|
|
spin_lock(&configfs_dirent_lock);
|
|
/* Scan the tree, return 0 if found */
|
|
ret = configfs_depend_prep(subsys_dentry, target);
|
|
if (ret)
|
|
goto out_unlock_dirent_lock;
|
|
|
|
/*
|
|
* We are sure that the item is not about to be removed by rmdir(), and
|
|
* not in the middle of attachment by mkdir().
|
|
*/
|
|
p = target->ci_dentry->d_fsdata;
|
|
p->s_dependent_count += 1;
|
|
|
|
out_unlock_dirent_lock:
|
|
spin_unlock(&configfs_dirent_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline struct configfs_dirent *
|
|
configfs_find_subsys_dentry(struct configfs_dirent *root_sd,
|
|
struct config_item *subsys_item)
|
|
{
|
|
struct configfs_dirent *p;
|
|
struct configfs_dirent *ret = NULL;
|
|
|
|
list_for_each_entry(p, &root_sd->s_children, s_sibling) {
|
|
if (p->s_type & CONFIGFS_DIR &&
|
|
p->s_element == subsys_item) {
|
|
ret = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int configfs_depend_item(struct configfs_subsystem *subsys,
|
|
struct config_item *target)
|
|
{
|
|
int ret;
|
|
struct configfs_dirent *subsys_sd;
|
|
struct config_item *s_item = &subsys->su_group.cg_item;
|
|
struct dentry *root;
|
|
|
|
/*
|
|
* Pin the configfs filesystem. This means we can safely access
|
|
* the root of the configfs filesystem.
|
|
*/
|
|
root = configfs_pin_fs();
|
|
if (IS_ERR(root))
|
|
return PTR_ERR(root);
|
|
|
|
/*
|
|
* Next, lock the root directory. We're going to check that the
|
|
* subsystem is really registered, and so we need to lock out
|
|
* configfs_[un]register_subsystem().
|
|
*/
|
|
inode_lock(d_inode(root));
|
|
|
|
subsys_sd = configfs_find_subsys_dentry(root->d_fsdata, s_item);
|
|
if (!subsys_sd) {
|
|
ret = -ENOENT;
|
|
goto out_unlock_fs;
|
|
}
|
|
|
|
/* Ok, now we can trust subsys/s_item */
|
|
ret = configfs_do_depend_item(subsys_sd->s_dentry, target);
|
|
|
|
out_unlock_fs:
|
|
inode_unlock(d_inode(root));
|
|
|
|
/*
|
|
* If we succeeded, the fs is pinned via other methods. If not,
|
|
* we're done with it anyway. So release_fs() is always right.
|
|
*/
|
|
configfs_release_fs();
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(configfs_depend_item);
|
|
|
|
/*
|
|
* Release the dependent linkage. This is much simpler than
|
|
* configfs_depend_item() because we know that the client driver is
|
|
* pinned, thus the subsystem is pinned, and therefore configfs is pinned.
|
|
*/
|
|
void configfs_undepend_item(struct config_item *target)
|
|
{
|
|
struct configfs_dirent *sd;
|
|
|
|
/*
|
|
* Since we can trust everything is pinned, we just need
|
|
* configfs_dirent_lock.
|
|
*/
|
|
spin_lock(&configfs_dirent_lock);
|
|
|
|
sd = target->ci_dentry->d_fsdata;
|
|
BUG_ON(sd->s_dependent_count < 1);
|
|
|
|
sd->s_dependent_count -= 1;
|
|
|
|
/*
|
|
* After this unlock, we cannot trust the item to stay alive!
|
|
* DO NOT REFERENCE item after this unlock.
|
|
*/
|
|
spin_unlock(&configfs_dirent_lock);
|
|
}
|
|
EXPORT_SYMBOL(configfs_undepend_item);
|
|
|
|
/*
|
|
* caller_subsys is a caller's subsystem not target's. This is used to
|
|
* determine if we should lock root and check subsys or not. When we are
|
|
* in the same subsystem as our target there is no need to do locking as
|
|
* we know that subsys is valid and is not unregistered during this function
|
|
* as we are called from callback of one of his children and VFS holds a lock
|
|
* on some inode. Otherwise we have to lock our root to ensure that target's
|
|
* subsystem it is not unregistered during this function.
|
|
*/
|
|
int configfs_depend_item_unlocked(struct configfs_subsystem *caller_subsys,
|
|
struct config_item *target)
|
|
{
|
|
struct configfs_subsystem *target_subsys;
|
|
struct config_group *root, *parent;
|
|
struct configfs_dirent *subsys_sd;
|
|
int ret = -ENOENT;
|
|
|
|
/* Disallow this function for configfs root */
|
|
if (configfs_is_root(target))
|
|
return -EINVAL;
|
|
|
|
parent = target->ci_group;
|
|
/*
|
|
* This may happen when someone is trying to depend root
|
|
* directory of some subsystem
|
|
*/
|
|
if (configfs_is_root(&parent->cg_item)) {
|
|
target_subsys = to_configfs_subsystem(to_config_group(target));
|
|
root = parent;
|
|
} else {
|
|
target_subsys = parent->cg_subsys;
|
|
/* Find a cofnigfs root as we may need it for locking */
|
|
for (root = parent; !configfs_is_root(&root->cg_item);
|
|
root = root->cg_item.ci_group)
|
|
;
|
|
}
|
|
|
|
if (target_subsys != caller_subsys) {
|
|
/*
|
|
* We are in other configfs subsystem, so we have to do
|
|
* additional locking to prevent other subsystem from being
|
|
* unregistered
|
|
*/
|
|
inode_lock(d_inode(root->cg_item.ci_dentry));
|
|
|
|
/*
|
|
* As we are trying to depend item from other subsystem
|
|
* we have to check if this subsystem is still registered
|
|
*/
|
|
subsys_sd = configfs_find_subsys_dentry(
|
|
root->cg_item.ci_dentry->d_fsdata,
|
|
&target_subsys->su_group.cg_item);
|
|
if (!subsys_sd)
|
|
goto out_root_unlock;
|
|
} else {
|
|
subsys_sd = target_subsys->su_group.cg_item.ci_dentry->d_fsdata;
|
|
}
|
|
|
|
/* Now we can execute core of depend item */
|
|
ret = configfs_do_depend_item(subsys_sd->s_dentry, target);
|
|
|
|
if (target_subsys != caller_subsys)
|
|
out_root_unlock:
|
|
/*
|
|
* We were called from subsystem other than our target so we
|
|
* took some locks so now it's time to release them
|
|
*/
|
|
inode_unlock(d_inode(root->cg_item.ci_dentry));
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(configfs_depend_item_unlocked);
|
|
|
|
static int configfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|
{
|
|
int ret = 0;
|
|
int module_got = 0;
|
|
struct config_group *group = NULL;
|
|
struct config_item *item = NULL;
|
|
struct config_item *parent_item;
|
|
struct configfs_subsystem *subsys;
|
|
struct configfs_dirent *sd;
|
|
const struct config_item_type *type;
|
|
struct module *subsys_owner = NULL, *new_item_owner = NULL;
|
|
struct configfs_fragment *frag;
|
|
char *name;
|
|
|
|
sd = dentry->d_parent->d_fsdata;
|
|
|
|
/*
|
|
* Fake invisibility if dir belongs to a group/default groups hierarchy
|
|
* being attached
|
|
*/
|
|
if (!configfs_dirent_is_ready(sd)) {
|
|
ret = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
if (!(sd->s_type & CONFIGFS_USET_DIR)) {
|
|
ret = -EPERM;
|
|
goto out;
|
|
}
|
|
|
|
frag = new_fragment();
|
|
if (!frag) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/* Get a working ref for the duration of this function */
|
|
parent_item = configfs_get_config_item(dentry->d_parent);
|
|
type = parent_item->ci_type;
|
|
subsys = to_config_group(parent_item)->cg_subsys;
|
|
BUG_ON(!subsys);
|
|
|
|
if (!type || !type->ct_group_ops ||
|
|
(!type->ct_group_ops->make_group &&
|
|
!type->ct_group_ops->make_item)) {
|
|
ret = -EPERM; /* Lack-of-mkdir returns -EPERM */
|
|
goto out_put;
|
|
}
|
|
|
|
/*
|
|
* The subsystem may belong to a different module than the item
|
|
* being created. We don't want to safely pin the new item but
|
|
* fail to pin the subsystem it sits under.
|
|
*/
|
|
if (!subsys->su_group.cg_item.ci_type) {
|
|
ret = -EINVAL;
|
|
goto out_put;
|
|
}
|
|
subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
|
|
if (!try_module_get(subsys_owner)) {
|
|
ret = -EINVAL;
|
|
goto out_put;
|
|
}
|
|
|
|
name = kmalloc(dentry->d_name.len + 1, GFP_KERNEL);
|
|
if (!name) {
|
|
ret = -ENOMEM;
|
|
goto out_subsys_put;
|
|
}
|
|
|
|
snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name);
|
|
|
|
mutex_lock(&subsys->su_mutex);
|
|
if (type->ct_group_ops->make_group) {
|
|
group = type->ct_group_ops->make_group(to_config_group(parent_item), name);
|
|
if (!group)
|
|
group = ERR_PTR(-ENOMEM);
|
|
if (!IS_ERR(group)) {
|
|
link_group(to_config_group(parent_item), group);
|
|
item = &group->cg_item;
|
|
} else
|
|
ret = PTR_ERR(group);
|
|
} else {
|
|
item = type->ct_group_ops->make_item(to_config_group(parent_item), name);
|
|
if (!item)
|
|
item = ERR_PTR(-ENOMEM);
|
|
if (!IS_ERR(item))
|
|
link_obj(parent_item, item);
|
|
else
|
|
ret = PTR_ERR(item);
|
|
}
|
|
mutex_unlock(&subsys->su_mutex);
|
|
|
|
kfree(name);
|
|
if (ret) {
|
|
/*
|
|
* If ret != 0, then link_obj() was never called.
|
|
* There are no extra references to clean up.
|
|
*/
|
|
goto out_subsys_put;
|
|
}
|
|
|
|
/*
|
|
* link_obj() has been called (via link_group() for groups).
|
|
* From here on out, errors must clean that up.
|
|
*/
|
|
|
|
type = item->ci_type;
|
|
if (!type) {
|
|
ret = -EINVAL;
|
|
goto out_unlink;
|
|
}
|
|
|
|
new_item_owner = type->ct_owner;
|
|
if (!try_module_get(new_item_owner)) {
|
|
ret = -EINVAL;
|
|
goto out_unlink;
|
|
}
|
|
|
|
/*
|
|
* I hate doing it this way, but if there is
|
|
* an error, module_put() probably should
|
|
* happen after any cleanup.
|
|
*/
|
|
module_got = 1;
|
|
|
|
/*
|
|
* Make racing rmdir() fail if it did not tag parent with
|
|
* CONFIGFS_USET_DROPPING
|
|
* Note: if CONFIGFS_USET_DROPPING is already set, attach_group() will
|
|
* fail and let rmdir() terminate correctly
|
|
*/
|
|
spin_lock(&configfs_dirent_lock);
|
|
/* This will make configfs_detach_prep() fail */
|
|
sd->s_type |= CONFIGFS_USET_IN_MKDIR;
|
|
spin_unlock(&configfs_dirent_lock);
|
|
|
|
if (group)
|
|
ret = configfs_attach_group(parent_item, item, dentry, frag);
|
|
else
|
|
ret = configfs_attach_item(parent_item, item, dentry, frag);
|
|
|
|
/* inherit uid/gid from process creating the directory */
|
|
if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID) ||
|
|
!gid_eq(current_fsgid(), GLOBAL_ROOT_GID)) {
|
|
struct iattr ia = {
|
|
.ia_uid = current_fsuid(),
|
|
.ia_gid = current_fsgid(),
|
|
.ia_valid = ATTR_UID | ATTR_GID,
|
|
};
|
|
struct inode *inode = d_inode(dentry);
|
|
inode->i_uid = ia.ia_uid;
|
|
inode->i_gid = ia.ia_gid;
|
|
/* the above manual assignments skip the permission checks */
|
|
configfs_setattr(dentry, &ia);
|
|
}
|
|
|
|
spin_lock(&configfs_dirent_lock);
|
|
sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
|
|
if (!ret)
|
|
configfs_dir_set_ready(dentry->d_fsdata);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
|
|
out_unlink:
|
|
if (ret) {
|
|
/* Tear down everything we built up */
|
|
mutex_lock(&subsys->su_mutex);
|
|
|
|
client_disconnect_notify(parent_item, item);
|
|
if (group)
|
|
unlink_group(group);
|
|
else
|
|
unlink_obj(item);
|
|
client_drop_item(parent_item, item);
|
|
|
|
mutex_unlock(&subsys->su_mutex);
|
|
|
|
if (module_got)
|
|
module_put(new_item_owner);
|
|
}
|
|
|
|
out_subsys_put:
|
|
if (ret)
|
|
module_put(subsys_owner);
|
|
|
|
out_put:
|
|
/*
|
|
* link_obj()/link_group() took a reference from child->parent,
|
|
* so the parent is safely pinned. We can drop our working
|
|
* reference.
|
|
*/
|
|
config_item_put(parent_item);
|
|
put_fragment(frag);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|
{
|
|
struct config_item *parent_item;
|
|
struct config_item *item;
|
|
struct configfs_subsystem *subsys;
|
|
struct configfs_dirent *sd;
|
|
struct configfs_fragment *frag;
|
|
struct module *subsys_owner = NULL, *dead_item_owner = NULL;
|
|
int ret;
|
|
|
|
sd = dentry->d_fsdata;
|
|
if (sd->s_type & CONFIGFS_USET_DEFAULT)
|
|
return -EPERM;
|
|
|
|
/* Get a working ref until we have the child */
|
|
parent_item = configfs_get_config_item(dentry->d_parent);
|
|
subsys = to_config_group(parent_item)->cg_subsys;
|
|
BUG_ON(!subsys);
|
|
|
|
if (!parent_item->ci_type) {
|
|
config_item_put(parent_item);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* configfs_mkdir() shouldn't have allowed this */
|
|
BUG_ON(!subsys->su_group.cg_item.ci_type);
|
|
subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
|
|
|
|
/*
|
|
* Ensure that no racing symlink() will make detach_prep() fail while
|
|
* the new link is temporarily attached
|
|
*/
|
|
do {
|
|
struct dentry *wait;
|
|
|
|
mutex_lock(&configfs_symlink_mutex);
|
|
spin_lock(&configfs_dirent_lock);
|
|
/*
|
|
* Here's where we check for dependents. We're protected by
|
|
* configfs_dirent_lock.
|
|
* If no dependent, atomically tag the item as dropping.
|
|
*/
|
|
ret = sd->s_dependent_count ? -EBUSY : 0;
|
|
if (!ret) {
|
|
ret = configfs_detach_prep(dentry, &wait);
|
|
if (ret)
|
|
configfs_detach_rollback(dentry);
|
|
}
|
|
spin_unlock(&configfs_dirent_lock);
|
|
mutex_unlock(&configfs_symlink_mutex);
|
|
|
|
if (ret) {
|
|
if (ret != -EAGAIN) {
|
|
config_item_put(parent_item);
|
|
return ret;
|
|
}
|
|
|
|
/* Wait until the racing operation terminates */
|
|
inode_lock(d_inode(wait));
|
|
inode_unlock(d_inode(wait));
|
|
dput(wait);
|
|
}
|
|
} while (ret == -EAGAIN);
|
|
|
|
frag = sd->s_frag;
|
|
if (down_write_killable(&frag->frag_sem)) {
|
|
spin_lock(&configfs_dirent_lock);
|
|
configfs_detach_rollback(dentry);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
config_item_put(parent_item);
|
|
return -EINTR;
|
|
}
|
|
frag->frag_dead = true;
|
|
up_write(&frag->frag_sem);
|
|
|
|
/* Get a working ref for the duration of this function */
|
|
item = configfs_get_config_item(dentry);
|
|
|
|
/* Drop reference from above, item already holds one. */
|
|
config_item_put(parent_item);
|
|
|
|
if (item->ci_type)
|
|
dead_item_owner = item->ci_type->ct_owner;
|
|
|
|
if (sd->s_type & CONFIGFS_USET_DIR) {
|
|
configfs_detach_group(item);
|
|
|
|
mutex_lock(&subsys->su_mutex);
|
|
client_disconnect_notify(parent_item, item);
|
|
unlink_group(to_config_group(item));
|
|
} else {
|
|
configfs_detach_item(item);
|
|
|
|
mutex_lock(&subsys->su_mutex);
|
|
client_disconnect_notify(parent_item, item);
|
|
unlink_obj(item);
|
|
}
|
|
|
|
client_drop_item(parent_item, item);
|
|
mutex_unlock(&subsys->su_mutex);
|
|
|
|
/* Drop our reference from above */
|
|
config_item_put(item);
|
|
|
|
module_put(dead_item_owner);
|
|
module_put(subsys_owner);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct inode_operations configfs_dir_inode_operations = {
|
|
.mkdir = configfs_mkdir,
|
|
.rmdir = configfs_rmdir,
|
|
.symlink = configfs_symlink,
|
|
.unlink = configfs_unlink,
|
|
.lookup = configfs_lookup,
|
|
.setattr = configfs_setattr,
|
|
};
|
|
|
|
const struct inode_operations configfs_root_inode_operations = {
|
|
.lookup = configfs_lookup,
|
|
.setattr = configfs_setattr,
|
|
};
|
|
|
|
static int configfs_dir_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct dentry * dentry = file->f_path.dentry;
|
|
struct configfs_dirent * parent_sd = dentry->d_fsdata;
|
|
int err;
|
|
|
|
inode_lock(d_inode(dentry));
|
|
/*
|
|
* Fake invisibility if dir belongs to a group/default groups hierarchy
|
|
* being attached
|
|
*/
|
|
err = -ENOENT;
|
|
if (configfs_dirent_is_ready(parent_sd)) {
|
|
file->private_data = configfs_new_dirent(parent_sd, NULL, 0, NULL);
|
|
if (IS_ERR(file->private_data))
|
|
err = PTR_ERR(file->private_data);
|
|
else
|
|
err = 0;
|
|
}
|
|
inode_unlock(d_inode(dentry));
|
|
|
|
return err;
|
|
}
|
|
|
|
static int configfs_dir_close(struct inode *inode, struct file *file)
|
|
{
|
|
struct dentry * dentry = file->f_path.dentry;
|
|
struct configfs_dirent * cursor = file->private_data;
|
|
|
|
inode_lock(d_inode(dentry));
|
|
spin_lock(&configfs_dirent_lock);
|
|
list_del_init(&cursor->s_sibling);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
inode_unlock(d_inode(dentry));
|
|
|
|
release_configfs_dirent(cursor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Relationship between s_mode and the DT_xxx types */
|
|
static inline unsigned char dt_type(struct configfs_dirent *sd)
|
|
{
|
|
return (sd->s_mode >> 12) & 15;
|
|
}
|
|
|
|
static int configfs_readdir(struct file *file, struct dir_context *ctx)
|
|
{
|
|
struct dentry *dentry = file->f_path.dentry;
|
|
struct super_block *sb = dentry->d_sb;
|
|
struct configfs_dirent * parent_sd = dentry->d_fsdata;
|
|
struct configfs_dirent *cursor = file->private_data;
|
|
struct list_head *p, *q = &cursor->s_sibling;
|
|
ino_t ino = 0;
|
|
|
|
if (!dir_emit_dots(file, ctx))
|
|
return 0;
|
|
spin_lock(&configfs_dirent_lock);
|
|
if (ctx->pos == 2)
|
|
list_move(q, &parent_sd->s_children);
|
|
for (p = q->next; p != &parent_sd->s_children; p = p->next) {
|
|
struct configfs_dirent *next;
|
|
const char *name;
|
|
int len;
|
|
struct inode *inode = NULL;
|
|
|
|
next = list_entry(p, struct configfs_dirent, s_sibling);
|
|
if (!next->s_element)
|
|
continue;
|
|
|
|
/*
|
|
* We'll have a dentry and an inode for
|
|
* PINNED items and for open attribute
|
|
* files. We lock here to prevent a race
|
|
* with configfs_d_iput() clearing
|
|
* s_dentry before calling iput().
|
|
*
|
|
* Why do we go to the trouble? If
|
|
* someone has an attribute file open,
|
|
* the inode number should match until
|
|
* they close it. Beyond that, we don't
|
|
* care.
|
|
*/
|
|
dentry = next->s_dentry;
|
|
if (dentry)
|
|
inode = d_inode(dentry);
|
|
if (inode)
|
|
ino = inode->i_ino;
|
|
spin_unlock(&configfs_dirent_lock);
|
|
if (!inode)
|
|
ino = iunique(sb, 2);
|
|
|
|
name = configfs_get_name(next);
|
|
len = strlen(name);
|
|
|
|
if (!dir_emit(ctx, name, len, ino, dt_type(next)))
|
|
return 0;
|
|
|
|
spin_lock(&configfs_dirent_lock);
|
|
list_move(q, p);
|
|
p = q;
|
|
ctx->pos++;
|
|
}
|
|
spin_unlock(&configfs_dirent_lock);
|
|
return 0;
|
|
}
|
|
|
|
static loff_t configfs_dir_lseek(struct file *file, loff_t offset, int whence)
|
|
{
|
|
struct dentry * dentry = file->f_path.dentry;
|
|
|
|
switch (whence) {
|
|
case 1:
|
|
offset += file->f_pos;
|
|
fallthrough;
|
|
case 0:
|
|
if (offset >= 0)
|
|
break;
|
|
fallthrough;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
if (offset != file->f_pos) {
|
|
file->f_pos = offset;
|
|
if (file->f_pos >= 2) {
|
|
struct configfs_dirent *sd = dentry->d_fsdata;
|
|
struct configfs_dirent *cursor = file->private_data;
|
|
struct list_head *p;
|
|
loff_t n = file->f_pos - 2;
|
|
|
|
spin_lock(&configfs_dirent_lock);
|
|
list_del(&cursor->s_sibling);
|
|
p = sd->s_children.next;
|
|
while (n && p != &sd->s_children) {
|
|
struct configfs_dirent *next;
|
|
next = list_entry(p, struct configfs_dirent,
|
|
s_sibling);
|
|
if (next->s_element)
|
|
n--;
|
|
p = p->next;
|
|
}
|
|
list_add_tail(&cursor->s_sibling, p);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
}
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
const struct file_operations configfs_dir_operations = {
|
|
.open = configfs_dir_open,
|
|
.release = configfs_dir_close,
|
|
.llseek = configfs_dir_lseek,
|
|
.read = generic_read_dir,
|
|
.iterate_shared = configfs_readdir,
|
|
};
|
|
|
|
/**
|
|
* configfs_register_group - creates a parent-child relation between two groups
|
|
* @parent_group: parent group
|
|
* @group: child group
|
|
*
|
|
* link groups, creates dentry for the child and attaches it to the
|
|
* parent dentry.
|
|
*
|
|
* Return: 0 on success, negative errno code on error
|
|
*/
|
|
int configfs_register_group(struct config_group *parent_group,
|
|
struct config_group *group)
|
|
{
|
|
struct configfs_subsystem *subsys = parent_group->cg_subsys;
|
|
struct dentry *parent;
|
|
struct configfs_fragment *frag;
|
|
int ret;
|
|
|
|
frag = new_fragment();
|
|
if (!frag)
|
|
return -ENOMEM;
|
|
|
|
mutex_lock(&subsys->su_mutex);
|
|
link_group(parent_group, group);
|
|
mutex_unlock(&subsys->su_mutex);
|
|
|
|
parent = parent_group->cg_item.ci_dentry;
|
|
|
|
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
|
|
ret = create_default_group(parent_group, group, frag);
|
|
if (ret)
|
|
goto err_out;
|
|
|
|
spin_lock(&configfs_dirent_lock);
|
|
configfs_dir_set_ready(group->cg_item.ci_dentry->d_fsdata);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
inode_unlock(d_inode(parent));
|
|
put_fragment(frag);
|
|
return 0;
|
|
err_out:
|
|
inode_unlock(d_inode(parent));
|
|
mutex_lock(&subsys->su_mutex);
|
|
unlink_group(group);
|
|
mutex_unlock(&subsys->su_mutex);
|
|
put_fragment(frag);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(configfs_register_group);
|
|
|
|
/**
|
|
* configfs_unregister_group() - unregisters a child group from its parent
|
|
* @group: parent group to be unregistered
|
|
*
|
|
* Undoes configfs_register_group()
|
|
*/
|
|
void configfs_unregister_group(struct config_group *group)
|
|
{
|
|
struct configfs_subsystem *subsys = group->cg_subsys;
|
|
struct dentry *dentry = group->cg_item.ci_dentry;
|
|
struct dentry *parent = group->cg_item.ci_parent->ci_dentry;
|
|
struct configfs_dirent *sd = dentry->d_fsdata;
|
|
struct configfs_fragment *frag = sd->s_frag;
|
|
|
|
down_write(&frag->frag_sem);
|
|
frag->frag_dead = true;
|
|
up_write(&frag->frag_sem);
|
|
|
|
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
|
|
spin_lock(&configfs_dirent_lock);
|
|
configfs_detach_prep(dentry, NULL);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
|
|
configfs_detach_group(&group->cg_item);
|
|
d_inode(dentry)->i_flags |= S_DEAD;
|
|
dont_mount(dentry);
|
|
d_drop(dentry);
|
|
fsnotify_rmdir(d_inode(parent), dentry);
|
|
inode_unlock(d_inode(parent));
|
|
|
|
dput(dentry);
|
|
|
|
mutex_lock(&subsys->su_mutex);
|
|
unlink_group(group);
|
|
mutex_unlock(&subsys->su_mutex);
|
|
}
|
|
EXPORT_SYMBOL(configfs_unregister_group);
|
|
|
|
/**
|
|
* configfs_register_default_group() - allocates and registers a child group
|
|
* @parent_group: parent group
|
|
* @name: child group name
|
|
* @item_type: child item type description
|
|
*
|
|
* boilerplate to allocate and register a child group with its parent. We need
|
|
* kzalloc'ed memory because child's default_group is initially empty.
|
|
*
|
|
* Return: allocated config group or ERR_PTR() on error
|
|
*/
|
|
struct config_group *
|
|
configfs_register_default_group(struct config_group *parent_group,
|
|
const char *name,
|
|
const struct config_item_type *item_type)
|
|
{
|
|
int ret;
|
|
struct config_group *group;
|
|
|
|
group = kzalloc(sizeof(*group), GFP_KERNEL);
|
|
if (!group)
|
|
return ERR_PTR(-ENOMEM);
|
|
config_group_init_type_name(group, name, item_type);
|
|
|
|
ret = configfs_register_group(parent_group, group);
|
|
if (ret) {
|
|
kfree(group);
|
|
return ERR_PTR(ret);
|
|
}
|
|
return group;
|
|
}
|
|
EXPORT_SYMBOL(configfs_register_default_group);
|
|
|
|
/**
|
|
* configfs_unregister_default_group() - unregisters and frees a child group
|
|
* @group: the group to act on
|
|
*/
|
|
void configfs_unregister_default_group(struct config_group *group)
|
|
{
|
|
configfs_unregister_group(group);
|
|
kfree(group);
|
|
}
|
|
EXPORT_SYMBOL(configfs_unregister_default_group);
|
|
|
|
int configfs_register_subsystem(struct configfs_subsystem *subsys)
|
|
{
|
|
int err;
|
|
struct config_group *group = &subsys->su_group;
|
|
struct dentry *dentry;
|
|
struct dentry *root;
|
|
struct configfs_dirent *sd;
|
|
struct configfs_fragment *frag;
|
|
|
|
frag = new_fragment();
|
|
if (!frag)
|
|
return -ENOMEM;
|
|
|
|
root = configfs_pin_fs();
|
|
if (IS_ERR(root)) {
|
|
put_fragment(frag);
|
|
return PTR_ERR(root);
|
|
}
|
|
|
|
if (!group->cg_item.ci_name)
|
|
group->cg_item.ci_name = group->cg_item.ci_namebuf;
|
|
|
|
sd = root->d_fsdata;
|
|
mutex_lock(&configfs_subsystem_mutex);
|
|
link_group(to_config_group(sd->s_element), group);
|
|
mutex_unlock(&configfs_subsystem_mutex);
|
|
|
|
inode_lock_nested(d_inode(root), I_MUTEX_PARENT);
|
|
|
|
err = -ENOMEM;
|
|
dentry = d_alloc_name(root, group->cg_item.ci_name);
|
|
if (dentry) {
|
|
d_add(dentry, NULL);
|
|
|
|
err = configfs_attach_group(sd->s_element, &group->cg_item,
|
|
dentry, frag);
|
|
if (err) {
|
|
BUG_ON(d_inode(dentry));
|
|
d_drop(dentry);
|
|
dput(dentry);
|
|
} else {
|
|
spin_lock(&configfs_dirent_lock);
|
|
configfs_dir_set_ready(dentry->d_fsdata);
|
|
spin_unlock(&configfs_dirent_lock);
|
|
}
|
|
}
|
|
|
|
inode_unlock(d_inode(root));
|
|
|
|
if (err) {
|
|
mutex_lock(&configfs_subsystem_mutex);
|
|
unlink_group(group);
|
|
mutex_unlock(&configfs_subsystem_mutex);
|
|
configfs_release_fs();
|
|
}
|
|
put_fragment(frag);
|
|
|
|
return err;
|
|
}
|
|
|
|
void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
|
|
{
|
|
struct config_group *group = &subsys->su_group;
|
|
struct dentry *dentry = group->cg_item.ci_dentry;
|
|
struct dentry *root = dentry->d_sb->s_root;
|
|
struct configfs_dirent *sd = dentry->d_fsdata;
|
|
struct configfs_fragment *frag = sd->s_frag;
|
|
|
|
if (dentry->d_parent != root) {
|
|
pr_err("Tried to unregister non-subsystem!\n");
|
|
return;
|
|
}
|
|
|
|
down_write(&frag->frag_sem);
|
|
frag->frag_dead = true;
|
|
up_write(&frag->frag_sem);
|
|
|
|
inode_lock_nested(d_inode(root),
|
|
I_MUTEX_PARENT);
|
|
inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
|
|
mutex_lock(&configfs_symlink_mutex);
|
|
spin_lock(&configfs_dirent_lock);
|
|
if (configfs_detach_prep(dentry, NULL)) {
|
|
pr_err("Tried to unregister non-empty subsystem!\n");
|
|
}
|
|
spin_unlock(&configfs_dirent_lock);
|
|
mutex_unlock(&configfs_symlink_mutex);
|
|
configfs_detach_group(&group->cg_item);
|
|
d_inode(dentry)->i_flags |= S_DEAD;
|
|
dont_mount(dentry);
|
|
inode_unlock(d_inode(dentry));
|
|
|
|
d_drop(dentry);
|
|
fsnotify_rmdir(d_inode(root), dentry);
|
|
|
|
inode_unlock(d_inode(root));
|
|
|
|
dput(dentry);
|
|
|
|
mutex_lock(&configfs_subsystem_mutex);
|
|
unlink_group(group);
|
|
mutex_unlock(&configfs_subsystem_mutex);
|
|
configfs_release_fs();
|
|
}
|
|
|
|
EXPORT_SYMBOL(configfs_register_subsystem);
|
|
EXPORT_SYMBOL(configfs_unregister_subsystem);
|