mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:09:30 +02:00
Merge tag 'for-7.3/block-20260819' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux
Pull block updates from Jens Axboe:
- NVMe updates via Keith:
- Enable Clang context analysis for the nvme host driver, adding
context annotations across core, fabrics, rdma, tcp and pci
- nvmet reservation state exposed through a new namespace-level
debugfs directory, plus ABI documentation for the host sysfs and
target configfs interfaces
- nvme-tcp host memory disclosure fixes on the read path: reject a
read that transferred too few bytes, don't accept C2HData based
on blk_rq_payload_bytes() alone, and fix the R2T case for a read
command
- Parallelize nvme-rdma I/O queue allocation and startup (Surabhi)
- Apple nvme fixes and quirks: page aligned admin queue buffers,
destroy the admin queue on removal, and various DMA/NVMMU
correctness fixes
- A large pile of nvmet and host fixes for out-of-bounds reads,
refcount/resource leaks, and NULL derefs across auth, zns,
passthru, pci-epf, rdma and configfs
- Various other fixes and cleanups
- MD updates via Yu Kuai:
- llbitmap reshape support, the large series wiring exact bitmap
mapping and reshape lifecycle through raid5 and raid10, growing
the page cache in place, and remapping checkpointed bits as
reshape progresses
- raid5 fixes for lockless max_nr_stripes and recovery_offset
accesses, a reshape deadlock with more failed devices than max
degraded, and bitmap batch counter consistency
- Atomic write handling for raid1/raid10, and removal of the
REQ_NOWAIT support from raid1/10/456
- raid5-ppl use-after-free fix in ppl_do_flush()
- A batch of smaller fixes across md core and the bitmap code
- s390/dasd ESE full-track write support and the surrounding
infrastructure, plus enabling CONTEXT_ANALYSIS for s390/block
- RWF_DONTCACHE support for block devices, built on new task-context
bio completion infrastructure, and wiring it up for the iomap and
buffer dropbehind writeback paths
- Async io_uring zone reset all, plus zone management command cleanups
allowing REQ_NOWAIT and tightening conventional zone rejection
- Block integrity refactoring: lift BIP_CHECK_FLAGS to the shared
header, handle nogenerate/noverify properly in fs-integrity, and drop
the blk-integrity.h include from bdev.c
- Split out a new blk_plug.h header
- ublk improvements: add UBLK_F_IO_DESC_SIZE, split request validation
from io_desc init, reject non-power-of-2 zone sizes in SET_PARAMS,
and a series of hardening fixes around map/unmap and auto buf reg
- null_blk cleanups and configfs serialization fixes
- nbd queue freeze removal on the setup paths, and a new
pre_defined_connections module parameter for pre-created devices
- blk-cgroup fixes for the race between policy activation and blkg
destruction, and accounting per-cpu stats over possible CPUs across
blk-stat, iolatency, iocost and kyber
- Various dio fixes: leak on metadata mapping error, validate user
space vectors during extraction, and set dma_alignment from the
backing file for loop and zloop direct I/O
- bio cleanups
- Various other fixes and cleanups all over
* tag 'for-7.3/block-20260819' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: (241 commits)
nbd: add pre_defined_connections module parameter for pre-created devices
nbd: remove queue freeze for newly created nbd from netlink path
nbd: factor out a nbd_genl_foreach_sock
nbd: skip queue freeze when setting size at device startup
nbd: remove queue freeze in nbd_add_socket
nbd: clear queue limits on disconnect
nbd: disallow NBD_SET_SOCK on an active device
nbd: simplify find_fallback() by removing redundant logic
blk-mq: add missing call to srcu_barrier() in blk_mq_free_tag_set()
block: mtip32xx: synchronize ioctls with device removal
ublk: avoid teardown retry loop on xarray allocation failure
null_blk: fix UBSAN shift-out-of-bounds when zone_size is 0 or overflows
block: don't include blk-integrity.h in bdev.c
xfs: avoid double deferrals for RWF_DONTCACHE writes
loop: Fix recently introduced lock inversion
block: set QUEUE_FLAG_DYING unconditionally in blk_mark_disk_dead()
swim3: Add missing MODULE_DESCRIPTION
selftests: ublk: add SET_PARAMS validation test
selftests: ublk: add helper for SET_PARAMS
ublk: reject non-power-of-2 zone sizes in SET_PARAMS
...
This commit is contained in:
@@ -0,0 +1,352 @@
|
||||
What: /config/nvmet/ports/N/addr_adrfam
|
||||
What: /config/nvmet/ports/N/addr_portid
|
||||
What: /config/nvmet/ports/N/addr_traddr
|
||||
What: /config/nvmet/ports/N/addr_trsvcid
|
||||
What: /config/nvmet/ports/N/addr_trtype
|
||||
What: /config/nvmet/ports/N/addr_treq
|
||||
Date: June 2016
|
||||
KernelVersion: 4.8
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
Address attributes for an NVMe-oF target port.
|
||||
|
||||
addr_adrfam: Shows or sets the address family. Accepted
|
||||
values: "pcie", "ipv4", "ipv6", "ib", "fc", "pci", "loop".
|
||||
|
||||
addr_portid: Shows or sets the port identifier (u16).
|
||||
|
||||
addr_traddr: Shows or sets the transport address string.
|
||||
|
||||
addr_trsvcid: Shows or sets the transport service identifier.
|
||||
|
||||
addr_trtype: Shows or sets the transport type. Accepted
|
||||
values: "rdma", "fc", "tcp", "pci", "loop". Also
|
||||
initializes default TSAS values.
|
||||
|
||||
addr_treq: Shows or sets the transport security requirements.
|
||||
Accepted values: "not specified", "required",
|
||||
"not required". For TCP with TLS1.3, "not specified" is
|
||||
rejected.
|
||||
|
||||
All attributes require the port to be disabled before
|
||||
modification.
|
||||
|
||||
What: /config/nvmet/ports/N/referrals/NAME/addr_adrfam
|
||||
What: /config/nvmet/ports/N/referrals/NAME/addr_portid
|
||||
What: /config/nvmet/ports/N/referrals/NAME/addr_traddr
|
||||
What: /config/nvmet/ports/N/referrals/NAME/addr_trsvcid
|
||||
What: /config/nvmet/ports/N/referrals/NAME/addr_trtype
|
||||
What: /config/nvmet/ports/N/referrals/NAME/addr_treq
|
||||
What: /config/nvmet/ports/N/referrals/NAME/enable
|
||||
Date: June 2016
|
||||
KernelVersion: 4.8
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
Address attributes and enable control for a referral entry
|
||||
under a port. The addr_* attributes have the same semantics
|
||||
as the corresponding port-level attributes. The enable
|
||||
attribute shows or sets whether this referral is enabled
|
||||
(boolean).
|
||||
|
||||
What: /config/nvmet/ports/N/param_inline_data_size
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Steve Wise <swise@opengridcomputing.com>
|
||||
Description:
|
||||
Shows or sets the inline data size for this port. Default
|
||||
is -1 which lets the transport choose. The port must be
|
||||
disabled before modification.
|
||||
|
||||
What: /config/nvmet/ports/N/ana_groups/ID/ana_state
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
Shows or sets the ANA (Asymmetric Namespace Access) state
|
||||
for this group on this port. Accepted values: "optimized",
|
||||
"non-optimized", "inaccessible", "persistent-loss",
|
||||
"change". Changes trigger an ANA change event.
|
||||
|
||||
What: /config/nvmet/ports/N/param_pi_enable
|
||||
Date: May 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: Israel Rukshin <israelr@mellanox.com>
|
||||
Description:
|
||||
Shows or sets whether protection information (PI) is
|
||||
enabled/supported for this port. Accepts boolean value.
|
||||
Only available when CONFIG_BLK_DEV_INTEGRITY is enabled.
|
||||
The port must be disabled before modification.
|
||||
|
||||
What: /config/nvmet/ports/N/addr_tsas
|
||||
Date: August 2023
|
||||
KernelVersion: 6.7
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
Shows or sets the transport-specific address subtype. For
|
||||
TCP transport, accepted values: "none", "tls1.3" (requires
|
||||
CONFIG_NVME_TARGET_TCP_TLS). For RDMA transport, shows the
|
||||
QP type: "connected" or "datagram". The port must be
|
||||
disabled before modification.
|
||||
|
||||
What: /config/nvmet/ports/N/param_max_queue_size
|
||||
Date: January 2024
|
||||
KernelVersion: 6.9
|
||||
Contact: Max Gurtovoy <mgurtovoy@nvidia.com>
|
||||
Description:
|
||||
Shows or sets the maximum queue size for this port. Default
|
||||
is -1 which lets the transport choose. The port must be
|
||||
disabled before modification.
|
||||
|
||||
What: /config/nvmet/ports/N/param_mdts
|
||||
Date: April 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Aurelien Aptel <aaptel@nvidia.com>
|
||||
Description:
|
||||
Shows or sets the maximum data transfer size for this port.
|
||||
Default is -1 which lets the transport choose. The port
|
||||
must be disabled before modification.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_allow_any_host
|
||||
Date: June 2016
|
||||
KernelVersion: 4.8
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
Shows or sets whether any host is allowed to connect.
|
||||
Accepts boolean value. Cannot be set to 1 if explicit
|
||||
hosts are linked in the allowed_hosts/ directory.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/device_path
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/device_nguid
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/enable
|
||||
Date: June 2016
|
||||
KernelVersion: 4.8
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
Namespace attributes added with the initial NVMe target.
|
||||
|
||||
device_path: Shows or sets the backend block device path.
|
||||
The namespace must be disabled before modification.
|
||||
|
||||
device_nguid: Shows or sets the NGUID (128-bit identifier).
|
||||
Accepts 32 hex digits with optional "-" or ":" separators.
|
||||
The namespace must be disabled before modification.
|
||||
|
||||
enable: Shows or sets whether this namespace is enabled
|
||||
(boolean).
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/device_uuid
|
||||
Date: June 2017
|
||||
KernelVersion: 4.13
|
||||
Contact: Johannes Thumshirn <jthumshirn@suse.de>
|
||||
Description:
|
||||
Shows or sets the UUID for this namespace. The namespace
|
||||
must be disabled before modification.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_version
|
||||
What: /config/nvmet/subsystems/NAME/attr_serial
|
||||
Date: July 2017
|
||||
KernelVersion: 4.13
|
||||
Contact: Johannes Thumshirn <jthumshirn@suse.de>
|
||||
Description:
|
||||
attr_version: Shows or sets the NVMe version reported by
|
||||
this subsystem. Format: "major.minor" or
|
||||
"major.minor.tertiary". Cannot be changed after the
|
||||
subsystem has been discovered.
|
||||
|
||||
attr_serial: Shows or sets the serial number. Must be a
|
||||
1-20 byte ASCII string (characters 0x20-0x7e). Cannot be
|
||||
changed after the subsystem has been discovered.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/ana_grpid
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
Shows or sets the ANA (Asymmetric Namespace Access) Group
|
||||
ID for this namespace. Must be between 1 and 128. Changing
|
||||
triggers an ANA event notification.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/buffered_io
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
|
||||
Description:
|
||||
Shows or sets whether buffered I/O is used for this
|
||||
namespace. Accepts boolean value. The namespace must be
|
||||
disabled before modification.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/p2pmem
|
||||
Date: October 2018
|
||||
KernelVersion: 4.20
|
||||
Contact: Logan Gunthorpe <logang@deltatee.com>
|
||||
Description:
|
||||
Shows or sets the P2P DMA memory device for this namespace.
|
||||
Accepts a PCI device BDF, "auto", or "none". The namespace
|
||||
must be disabled before modification. Only available when
|
||||
CONFIG_PCI_P2PDMA is enabled.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_cntlid_min
|
||||
What: /config/nvmet/subsystems/NAME/attr_cntlid_max
|
||||
Date: January 2020
|
||||
KernelVersion: 5.7
|
||||
Contact: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
|
||||
Description:
|
||||
attr_cntlid_min: Shows or sets the minimum controller ID
|
||||
(u16). Must be nonzero and not greater than attr_cntlid_max.
|
||||
|
||||
attr_cntlid_max: Shows or sets the maximum controller ID
|
||||
(u16). Must be nonzero and not less than attr_cntlid_min.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_model
|
||||
Date: January 2020
|
||||
KernelVersion: 5.7
|
||||
Contact: Mark Ruijter <MRuijter@onestopsystems.com>
|
||||
Description:
|
||||
Shows or sets the model number for this subsystem. Must
|
||||
be a 1-40 byte ASCII string (characters 0x20-0x7e).
|
||||
Cannot be changed after the subsystem has been discovered.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_pi_enable
|
||||
Date: May 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: Israel Rukshin <israelr@mellanox.com>
|
||||
Description:
|
||||
Shows or sets whether protection information (PI) is
|
||||
enabled/supported for this subsystem. Accepts boolean
|
||||
value. Only available when CONFIG_BLK_DEV_INTEGRITY is
|
||||
enabled.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/revalidate_size
|
||||
Date: May 2020
|
||||
KernelVersion: 5.8
|
||||
Contact: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
|
||||
Description:
|
||||
Write-only. Writing 1 triggers namespace size revalidation.
|
||||
If the size has changed, a namespace changed AEN is sent.
|
||||
The namespace must be enabled.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/passthru/device_path
|
||||
What: /config/nvmet/subsystems/NAME/passthru/enable
|
||||
Date: July 2020
|
||||
KernelVersion: 5.9
|
||||
Contact: Logan Gunthorpe <logang@deltatee.com>
|
||||
Description:
|
||||
Passthrough mode attributes.
|
||||
|
||||
device_path: Shows or sets the NVMe controller character
|
||||
device path (e.g., /dev/nvme0). Cannot be changed while
|
||||
the passthrough controller is active.
|
||||
|
||||
enable: Shows or sets whether passthrough mode is enabled
|
||||
(boolean).
|
||||
|
||||
Only available when CONFIG_NVME_TARGET_PASSTHRU is enabled.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/passthru/admin_timeout
|
||||
What: /config/nvmet/subsystems/NAME/passthru/io_timeout
|
||||
Date: November 2020
|
||||
KernelVersion: 5.11
|
||||
Contact: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
|
||||
Description:
|
||||
admin_timeout: Shows or sets the admin command timeout for
|
||||
passthrough mode, in jiffies.
|
||||
|
||||
io_timeout: Shows or sets the I/O command timeout for
|
||||
passthrough mode, in jiffies.
|
||||
|
||||
Only available when CONFIG_NVME_TARGET_PASSTHRU is enabled.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/passthru/clear_ids
|
||||
Date: June 2022
|
||||
KernelVersion: 5.19
|
||||
Contact: Alan Adamson <alan.adamson@oracle.com>
|
||||
Description:
|
||||
Shows or sets whether to clear identify data IDs in
|
||||
passthrough mode. Only available when
|
||||
CONFIG_NVME_TARGET_PASSTHRU is enabled.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_qid_max
|
||||
Date: August 2022
|
||||
KernelVersion: 6.1
|
||||
Contact: Daniel Wagner <dwagner@suse.de>
|
||||
Description:
|
||||
Shows or sets the maximum queue ID (number of I/O queues,
|
||||
u16). Must be between 1 and 128. Changing this value
|
||||
forces reconnection of all connected controllers.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_ieee_oui
|
||||
Date: November 2022
|
||||
KernelVersion: 6.2
|
||||
Contact: Aleksandr Miloserdov <a.miloserdov@yadro.com>
|
||||
Description:
|
||||
Shows or sets the IEEE OUI for this subsystem. Displayed
|
||||
in "0x%06x" format. Must be a 24-bit value. Cannot be
|
||||
changed after the subsystem has been discovered.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_firmware
|
||||
Date: November 2022
|
||||
KernelVersion: 6.2
|
||||
Contact: Aleksandr Miloserdov <a.miloserdov@yadro.com>
|
||||
Description:
|
||||
Shows or sets the firmware revision string for this
|
||||
subsystem. Must be a 1-8 byte ASCII string (characters
|
||||
0x20-0x7e). Cannot be changed after the subsystem has
|
||||
been discovered.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/namespaces/NSID/resv_enable
|
||||
Date: November 2024
|
||||
KernelVersion: 6.13
|
||||
Contact: Guixin Liu <kanie@linux.alibaba.com>
|
||||
Description:
|
||||
Shows or sets whether persistent reservation support is
|
||||
enabled for this namespace. Accepts boolean value. The
|
||||
namespace must be disabled before modification.
|
||||
|
||||
What: /config/nvmet/subsystems/NAME/attr_vendor_id
|
||||
What: /config/nvmet/subsystems/NAME/attr_subsys_vendor_id
|
||||
Date: January 2025
|
||||
KernelVersion: 6.14
|
||||
Contact: Damien Le Moal <dlemoal@kernel.org>
|
||||
Description:
|
||||
attr_vendor_id: Shows or sets the PCI vendor ID reported
|
||||
by this subsystem. Displayed in "0x%x" format.
|
||||
|
||||
attr_subsys_vendor_id: Shows or sets the PCI subsystem
|
||||
vendor ID. Displayed in "0x%x" format.
|
||||
|
||||
What: /config/nvmet/hosts/HOSTNQN/dhchap_key
|
||||
What: /config/nvmet/hosts/HOSTNQN/dhchap_ctrl_key
|
||||
What: /config/nvmet/hosts/HOSTNQN/dhchap_hash
|
||||
What: /config/nvmet/hosts/HOSTNQN/dhchap_dhgroup
|
||||
Date: June 2022
|
||||
KernelVersion: 6.0
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
DH-HMAC-CHAP authentication attributes.
|
||||
|
||||
dhchap_key: Shows or sets the host secret key. Accepts a
|
||||
key string in "DHHC-1:" format.
|
||||
|
||||
dhchap_ctrl_key: Shows or sets the controller secret key
|
||||
for bidirectional authentication. Same format as dhchap_key.
|
||||
|
||||
dhchap_hash: Shows or sets the HMAC hash algorithm.
|
||||
Accepted values: "hmac(sha256)", "hmac(sha384)",
|
||||
"hmac(sha512)".
|
||||
|
||||
dhchap_dhgroup: Shows or sets the Diffie-Hellman group for
|
||||
DH-HMAC-CHAP key exchange. Accepted values: "null",
|
||||
"ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144".
|
||||
Non-null groups require the corresponding KPP crypto
|
||||
algorithm to be available.
|
||||
|
||||
Only available when CONFIG_NVME_TARGET_AUTH is enabled.
|
||||
|
||||
What: /config/nvmet/discovery_nqn
|
||||
Date: April 2024
|
||||
KernelVersion: 6.9
|
||||
Contact: Hannes Reinecke <hare@kernel.org>
|
||||
Description:
|
||||
Shows or sets the NQN of the discovery subsystem. The
|
||||
value must be unique and not duplicate any existing
|
||||
subsystem name.
|
||||
@@ -0,0 +1,453 @@
|
||||
What: /sys/class/nvme/nvmeX/model
|
||||
What: /sys/class/nvme/nvmeX/serial
|
||||
What: /sys/class/nvme/nvmeX/firmware_rev
|
||||
Date: January 2016
|
||||
KernelVersion: 4.5
|
||||
Contact: Keith Busch <kbusch@kernel.org>
|
||||
Description:
|
||||
Shows the model, serial number, or firmware revision string
|
||||
of the NVMe controller, as reported in the Identify
|
||||
Controller data structure.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/cntlid
|
||||
Date: February 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: Ming Lin <ming.l@ssi.samsung.com>
|
||||
Description:
|
||||
Shows the controller identifier assigned by the NVMe
|
||||
subsystem.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/cntrltype
|
||||
What: /sys/class/nvme/nvmeX/dctype
|
||||
Date: February 2022
|
||||
KernelVersion: 5.18
|
||||
Contact: Martin Belanger <martin.belanger@dell.com>
|
||||
Description:
|
||||
cntrltype: Shows the controller type. Possible values: "io",
|
||||
"discovery", "admin", "reserved".
|
||||
|
||||
dctype: Shows the discovery controller type. Possible values:
|
||||
"none", "ddc", "cdc", "reserved".
|
||||
|
||||
What: /sys/class/nvme/nvmeX/reset_controller
|
||||
Date: November 2015
|
||||
KernelVersion: 4.5
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
Write-only. Writing any value triggers a synchronous
|
||||
controller reset.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/rescan_controller
|
||||
Date: April 2016
|
||||
KernelVersion: 4.7
|
||||
Contact: Keith Busch <kbusch@kernel.org>
|
||||
Description:
|
||||
Write-only. Writing any value triggers a namespace rescan
|
||||
on this controller.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/transport
|
||||
What: /sys/class/nvme/nvmeX/subsysnqn
|
||||
What: /sys/class/nvme/nvmeX/address
|
||||
What: /sys/class/nvme/nvmeX/delete_controller
|
||||
What: /sys/class/nvme/nvmeX/reconnect_delay
|
||||
What: /sys/class/nvme/nvmeX/ctrl_loss_tmo
|
||||
Date: June 2016
|
||||
KernelVersion: 4.8
|
||||
Contact: Ming Lin <ming.l@ssi.samsung.com>
|
||||
Description:
|
||||
Fabrics controller attributes added with NVMe-oF support.
|
||||
|
||||
transport: Shows the transport type string. Possible values:
|
||||
"pcie", "tcp", "rdma", "fc", "loop".
|
||||
|
||||
subsysnqn: Shows the NVMe Qualified Name (NQN) of the
|
||||
subsystem this controller belongs to.
|
||||
|
||||
address: Shows the transport-specific address string. Only
|
||||
available for fabrics controllers.
|
||||
|
||||
delete_controller: Write-only. Triggers deletion of this
|
||||
fabrics controller.
|
||||
|
||||
reconnect_delay: Shows or sets the reconnect delay in
|
||||
seconds. Reading returns the delay value, or "off" if
|
||||
disabled.
|
||||
|
||||
ctrl_loss_tmo: Shows or sets the controller loss timeout in
|
||||
seconds. Reading returns the timeout value, or "off" if
|
||||
infinite reconnects are allowed. Writing a negative value
|
||||
disables the timeout.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/hostnqn
|
||||
What: /sys/class/nvme/nvmeX/hostid
|
||||
Date: February 2020
|
||||
KernelVersion: 5.7
|
||||
Contact: Sagi Grimberg <sagi@grimberg.me>
|
||||
Description:
|
||||
hostnqn: Shows the host NQN used by this fabrics controller.
|
||||
|
||||
hostid: Shows the host identifier (UUID format) used by this
|
||||
fabrics controller.
|
||||
|
||||
Only available for fabrics controllers.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/fast_io_fail_tmo
|
||||
Date: November 2020
|
||||
KernelVersion: 5.11
|
||||
Contact: Victor Gladkov <Victor.Gladkov@kioxia.com>
|
||||
Description:
|
||||
Shows or sets the fast I/O fail timeout in seconds. Reading
|
||||
returns the timeout value, or "off" if disabled. Writing a
|
||||
negative value disables the fast I/O fail. Only available
|
||||
for fabrics controllers.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/kato
|
||||
Date: April 2021
|
||||
KernelVersion: 5.13
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
Shows the Keep Alive Timeout value in milliseconds for
|
||||
this controller.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/cmb
|
||||
Date: October 2016
|
||||
KernelVersion: 4.9
|
||||
Contact: Stephen Bates <sbates@raithlin.com>
|
||||
Description:
|
||||
Shows the Controller Memory Buffer (CMB) register values
|
||||
in format "cmbloc : 0x%08x\ncmbsz : 0x%08x\n". Only
|
||||
visible when the controller has a CMB (cmbsz != 0).
|
||||
PCI transport only.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/cmbloc
|
||||
What: /sys/class/nvme/nvmeX/cmbsz
|
||||
What: /sys/class/nvme/nvmeX/hmb
|
||||
Date: July 2021
|
||||
KernelVersion: 5.15
|
||||
Contact: Keith Busch <kbusch@kernel.org>
|
||||
Description:
|
||||
cmbloc: Shows the CMBLOC register value.
|
||||
|
||||
cmbsz: Shows the CMBSZ register value.
|
||||
|
||||
cmbloc and cmbsz are only visible when the controller has
|
||||
a CMB. PCI transport only.
|
||||
|
||||
hmb: Shows or sets whether the Host Memory Buffer (HMB) is
|
||||
enabled. Reading returns 1 (enabled) or 0 (disabled).
|
||||
Writing 1 enables HMB; writing 0 disables it. Only
|
||||
visible when the controller supports HMB (hmpre != 0).
|
||||
PCI transport only.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/state
|
||||
Date: November 2016
|
||||
KernelVersion: 4.11
|
||||
Contact: Sagi Grimberg <sagi@grimberg.me>
|
||||
Description:
|
||||
Shows the current state of the controller. Possible values:
|
||||
"new", "live", "resetting", "connecting", "deleting",
|
||||
"deleting (no IO)", "dead".
|
||||
|
||||
What: /sys/class/nvme/nvmeX/numa_node
|
||||
Date: November 2018
|
||||
KernelVersion: 5.0
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
Shows the NUMA node the controller is attached to.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/queue_count
|
||||
What: /sys/class/nvme/nvmeX/sqsize
|
||||
Date: September 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: James Smart <jsmart2021@gmail.com>
|
||||
Description:
|
||||
queue_count: Shows the total number of queues (admin + I/O)
|
||||
for this controller.
|
||||
|
||||
sqsize: Shows the submission queue size for this controller.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/dhchap_secret
|
||||
What: /sys/class/nvme/nvmeX/dhchap_ctrl_secret
|
||||
Date: June 2022
|
||||
KernelVersion: 6.0
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
dhchap_secret: Shows or sets the host DH-HMAC-CHAP secret
|
||||
for this controller. Reading returns "none" if not set.
|
||||
Writing must use the "DHHC-1:" key format and triggers
|
||||
re-authentication.
|
||||
|
||||
dhchap_ctrl_secret: Shows or sets the controller
|
||||
DH-HMAC-CHAP secret for bidirectional authentication.
|
||||
Same format as dhchap_secret.
|
||||
|
||||
Only available when CONFIG_NVME_HOST_AUTH is enabled and
|
||||
for fabrics controllers.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/tls_key
|
||||
Date: August 2023
|
||||
KernelVersion: 6.7
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
Shows the serial of the currently active TLS PSK as hex.
|
||||
Returns empty if no TLS key is active. Only available for
|
||||
TCP controllers with TLS or secure concatenation enabled
|
||||
(CONFIG_NVME_TCP_TLS).
|
||||
|
||||
What: /sys/class/nvme/nvmeX/tls_configured_key
|
||||
Date: July 2024
|
||||
KernelVersion: 6.12
|
||||
Contact: Hannes Reinecke <hare@kernel.org>
|
||||
Description:
|
||||
Shows the serial of the configured TLS key. Writing 0
|
||||
triggers a PSK reauthentication (REPLACETLSPSK) with
|
||||
the target. After reauthentication the returned serial
|
||||
will be the new key. Only available for TCP controllers
|
||||
with secure concatenation enabled (CONFIG_NVME_TCP_TLS).
|
||||
|
||||
What: /sys/class/nvme/nvmeX/tls_keyring
|
||||
Date: July 2024
|
||||
KernelVersion: 6.12
|
||||
Contact: Hannes Reinecke <hare@kernel.org>
|
||||
Description:
|
||||
Shows the TLS keyring description. Only available for TCP
|
||||
controllers with a keyring configured (CONFIG_NVME_TCP_TLS).
|
||||
|
||||
What: /sys/class/nvme/nvmeX/tls_mode
|
||||
Date: April 2026
|
||||
KernelVersion: 7.1
|
||||
Contact: Daniel Wagner <wagi@kernel.org>
|
||||
Description:
|
||||
Shows the TLS mode: "tls" for direct TLS or "concat" for
|
||||
secure concatenation. Only available for TCP controllers
|
||||
with TLS or secure concatenation enabled
|
||||
(CONFIG_NVME_TCP_TLS).
|
||||
|
||||
What: /sys/class/nvme/nvmeX/passthru_err_log_enabled
|
||||
Date: January 2024
|
||||
KernelVersion: 6.8
|
||||
Contact: Alan Adamson <alan.adamson@oracle.com>
|
||||
Description:
|
||||
Shows or sets whether admin passthrough error logging is
|
||||
enabled for this controller. Reading returns "on" or "off".
|
||||
Writing accepts a boolean value.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/quirks
|
||||
Date: November 2025
|
||||
KernelVersion: 7.0
|
||||
Contact: Maurizio Lombardi <mlombard@redhat.com>
|
||||
Description:
|
||||
Shows the active quirk names for this controller, one per
|
||||
line. Shows "none" if no quirks are active.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/admin_timeout
|
||||
What: /sys/class/nvme/nvmeX/io_timeout
|
||||
Date: May 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Maurizio Lombardi <mlombard@redhat.com>
|
||||
Description:
|
||||
admin_timeout: Shows or sets the admin command timeout in
|
||||
milliseconds.
|
||||
|
||||
io_timeout: Shows or sets the I/O command timeout in
|
||||
milliseconds. Changes are propagated to all namespace
|
||||
request queues.
|
||||
|
||||
The value must be nonzero. Only writable after the
|
||||
controller has been started at least once.
|
||||
|
||||
What: /sys/block/nvmeXnY/uuid
|
||||
What: /sys/block/nvmeXnY/eui
|
||||
What: /sys/block/nvmeXnY/nsid
|
||||
Date: December 2015
|
||||
KernelVersion: 4.5
|
||||
Contact: Keith Busch <kbusch@kernel.org>
|
||||
Description:
|
||||
Namespace identification attributes.
|
||||
|
||||
uuid: Shows the UUID for this namespace. Falls back to
|
||||
showing the NGUID for backward compatibility. Hidden if
|
||||
both are all zeros.
|
||||
|
||||
eui: Shows the IEEE Extended Unique Identifier (EUI-64).
|
||||
Hidden if all zeros.
|
||||
|
||||
nsid: Shows the namespace identifier (NSID).
|
||||
|
||||
What: /sys/block/nvmeXnY/wwid
|
||||
Date: February 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: Keith Busch <kbusch@kernel.org>
|
||||
Description:
|
||||
Shows the World Wide Identifier for this namespace. The
|
||||
format depends on available identifiers (in priority
|
||||
order): "uuid.{UUID}", "eui.{NGUID}", "eui.{EUI64}", or
|
||||
"nvme.{VID}-{SERIAL}-{MODEL}-{NSID}".
|
||||
|
||||
What: /sys/block/nvmeXnY/nguid
|
||||
Date: June 2017
|
||||
KernelVersion: 4.13
|
||||
Contact: Johannes Thumshirn <jthumshirn@suse.de>
|
||||
Description:
|
||||
Shows the Namespace Globally Unique Identifier (NGUID).
|
||||
Hidden if the NGUID is all zeros.
|
||||
|
||||
What: /sys/block/nvmeXcYnZ/ana_grpid
|
||||
What: /sys/block/nvmeXcYnZ/ana_state
|
||||
Date: May 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Christoph Hellwig <hch@lst.de>
|
||||
Description:
|
||||
ana_grpid: Shows the ANA Group ID for this namespace
|
||||
path device.
|
||||
|
||||
ana_state: Shows the ANA state. Possible values:
|
||||
"optimized", "non-optimized", "inaccessible",
|
||||
"persistent-loss", "change".
|
||||
|
||||
Only visible when the controller supports ANA.
|
||||
Requires CONFIG_NVME_MULTIPATH.
|
||||
|
||||
What: /sys/block/nvmeXcYnZ/queue_depth
|
||||
Date: June 2024
|
||||
KernelVersion: 6.11
|
||||
Contact: Thomas Song <tsong@purestorage.com>
|
||||
Description:
|
||||
Shows the current active I/O count on this path's
|
||||
controller. Returns empty if iopolicy is not "queue-depth".
|
||||
Requires CONFIG_NVME_MULTIPATH.
|
||||
|
||||
What: /sys/block/nvmeXcYnZ/numa_nodes
|
||||
Date: January 2025
|
||||
KernelVersion: 6.15
|
||||
Contact: Nilay Shroff <nilay@linux.ibm.com>
|
||||
Description:
|
||||
Shows the NUMA node mask for which this path is the
|
||||
currently selected path. Returns empty if iopolicy is not
|
||||
"numa". Requires CONFIG_NVME_MULTIPATH.
|
||||
|
||||
What: /sys/block/nvmeXnY/delayed_removal_secs
|
||||
Date: May 2025
|
||||
KernelVersion: 6.16
|
||||
Contact: Nilay Shroff <nilay@linux.ibm.com>
|
||||
Description:
|
||||
Shows or sets the delayed removal timeout in seconds for
|
||||
the multipath head device. When nonzero, I/O is queued
|
||||
instead of failed when all paths are gone, and head removal
|
||||
is deferred. Only visible on multipath head devices.
|
||||
Requires CONFIG_NVME_MULTIPATH.
|
||||
|
||||
What: /sys/block/nvmeXnY/csi
|
||||
What: /sys/block/nvmeXnY/metadata_bytes
|
||||
What: /sys/block/nvmeXnY/nuse
|
||||
Date: December 2023
|
||||
KernelVersion: 6.8
|
||||
Contact: Daniel Wagner <dwagner@suse.de>
|
||||
Description:
|
||||
csi: Shows the Command Set Identifier for this namespace.
|
||||
|
||||
metadata_bytes: Shows the metadata size in bytes.
|
||||
|
||||
nuse: Shows the Namespace Utilization (NUSE) value. Reading
|
||||
triggers an Identify Namespace command to refresh the
|
||||
value (rate-limited to avoid excessive commands).
|
||||
|
||||
What: /sys/block/nvmeXnY/passthru_err_log_enabled
|
||||
Date: January 2024
|
||||
KernelVersion: 6.8
|
||||
Contact: Alan Adamson <alan.adamson@oracle.com>
|
||||
Description:
|
||||
Shows or sets whether I/O passthrough error logging is
|
||||
enabled for this namespace. Reading returns "on" or "off".
|
||||
Writing accepts a boolean value.
|
||||
|
||||
What: /sys/class/nvme/nvmeX/diag/command_error_count
|
||||
What: /sys/class/nvme/nvmeX/diag/reset_count
|
||||
What: /sys/class/nvme/nvmeX/diag/reconnect_count
|
||||
Date: May 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Nilay Shroff <nilay@linux.ibm.com>
|
||||
Description:
|
||||
Controller diagnostic counters.
|
||||
|
||||
command_error_count: Admin command error counter.
|
||||
|
||||
reset_count: Controller reset counter.
|
||||
|
||||
reconnect_count: Accumulated reconnect counter. Only
|
||||
available for fabrics controllers.
|
||||
|
||||
All counters can be reset by writing a value.
|
||||
|
||||
What: /sys/block/nvmeXnY/diag/command_retries_count
|
||||
What: /sys/block/nvmeXnY/diag/command_error_count
|
||||
Date: May 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Nilay Shroff <nilay@linux.ibm.com>
|
||||
Description:
|
||||
Namespace diagnostic counters for non-multipath
|
||||
configurations (when CONFIG_NVME_MULTIPATH is not
|
||||
configured).
|
||||
|
||||
command_retries_count: I/O command retry counter.
|
||||
|
||||
command_error_count: I/O command error counter.
|
||||
|
||||
All counters can be reset by writing any value.
|
||||
|
||||
What: /sys/block/nvmeXcYnZ/diag/command_retries_count
|
||||
What: /sys/block/nvmeXcYnZ/diag/command_error_count
|
||||
What: /sys/block/nvmeXcYnZ/diag/multipath_failover_count
|
||||
What: /sys/block/nvmeXnY/diag/io_requeue_no_usable_path_count
|
||||
What: /sys/block/nvmeXnY/diag/io_fail_no_available_path_count
|
||||
Date: May 2026
|
||||
KernelVersion: 7.2
|
||||
Contact: Nilay Shroff <nilay@linux.ibm.com>
|
||||
Description:
|
||||
Namespace diagnostic counters for multipath
|
||||
configurations (when CONFIG_NVME_MULTIPATH is
|
||||
configured).
|
||||
|
||||
command_retries_count: I/O command retry counter.
|
||||
|
||||
command_error_count: I/O command error counter.
|
||||
|
||||
multipath_failover_count: Multipath failover counter.
|
||||
|
||||
io_requeue_no_usable_path_count: Counter of I/Os
|
||||
requeued because no usable path was available.
|
||||
|
||||
io_fail_no_available_path_count: Counter of I/Os
|
||||
failed because no available path existed.
|
||||
|
||||
All counters can be reset by writing any value.
|
||||
|
||||
What: /sys/class/nvme-subsystem/nvme-subsysX/model
|
||||
What: /sys/class/nvme-subsystem/nvme-subsysX/serial
|
||||
What: /sys/class/nvme-subsystem/nvme-subsysX/firmware_rev
|
||||
What: /sys/class/nvme-subsystem/nvme-subsysX/subsysnqn
|
||||
Date: November 2017
|
||||
KernelVersion: 4.15
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
Shows the model, serial number, firmware revision, or NQN
|
||||
of the NVMe subsystem.
|
||||
|
||||
What: /sys/class/nvme-subsystem/nvme-subsysX/iopolicy
|
||||
Date: February 2019
|
||||
KernelVersion: 5.1
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
Shows or sets the multipath I/O path selection policy for
|
||||
this subsystem. Accepted values: "numa", "round-robin",
|
||||
"queue-depth". Changing the policy clears all current path
|
||||
selections. Only available when CONFIG_NVME_MULTIPATH is
|
||||
enabled.
|
||||
|
||||
What: /sys/class/nvme-subsystem/nvme-subsysX/subsystype
|
||||
Date: September 2021
|
||||
KernelVersion: 5.16
|
||||
Contact: Hannes Reinecke <hare@suse.de>
|
||||
Description:
|
||||
Shows the subsystem type. Possible values: "discovery",
|
||||
"nvm", "reserved".
|
||||
@@ -1,13 +0,0 @@
|
||||
What: /sys/devices/virtual/nvme-fabrics/ctl/.../tls_configured_key
|
||||
Date: November 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: Linux NVMe mailing list <linux-nvme@lists.infradead.org>
|
||||
Description:
|
||||
The file is avaliable when using a secure concatanation
|
||||
connection to a NVMe target. Reading the file will return
|
||||
the serial of the currently negotiated key.
|
||||
|
||||
Writing 0 to the file will trigger a PSK reauthentication
|
||||
(REPLACETLSPSK) with the target. After a reauthentication
|
||||
the value returned by tls_configured_key will be the new
|
||||
serial.
|
||||
@@ -30,11 +30,10 @@ indicates the position of the write pointer of the zone.
|
||||
|
||||
When resetting a sequential zone, its backing file size is truncated to zero.
|
||||
Conversely, for a zone finish operation, the backing file is truncated to the
|
||||
zone size. With this, the maximum capacity of a zloop zoned block device created
|
||||
can be larger configured to be larger than the storage space available on the
|
||||
backing file system. Of course, for such configuration, writing more data than
|
||||
the storage space available on the backing file system will result in write
|
||||
errors.
|
||||
zone capacity. With this, a zloop zoned block device can be configured with a
|
||||
larger capacity than the storage space available on the backing file system. Of
|
||||
course, for such configuration, writing more data than the storage space
|
||||
available on the backing file system will result in write errors.
|
||||
|
||||
The zoned loop block device driver implements a complete zone transition state
|
||||
machine. That is, zones can be empty, implicitly opened, explicitly opened,
|
||||
@@ -71,8 +70,10 @@ follows.
|
||||
=================== =========================================================
|
||||
id Device number (the X in /dev/zloopX).
|
||||
Default: automatically assigned.
|
||||
capacity_mb Device total capacity in MiB. This is always rounded up
|
||||
to the nearest higher multiple of the zone size.
|
||||
capacity_mb Device total capacity in MiB. A smaller last zone is not
|
||||
supported, so a capacity value that is not a multiple of
|
||||
the zone size is rounded down to the closest multiple of
|
||||
the zone size.
|
||||
Default: 16384 MiB (16 GiB).
|
||||
zone_size_mb Device zone size in MiB. Default: 256 MiB.
|
||||
zone_capacity_mb Device zone capacity (must always be equal to or lower
|
||||
|
||||
@@ -16,16 +16,16 @@ bv_len by the number of bytes completed in that biovec.
|
||||
In the new scheme of things, everything that must be mutated in order to
|
||||
partially complete a bio is segregated into struct bvec_iter: bi_sector,
|
||||
bi_size and bi_idx have been moved there; and instead of modifying bv_offset
|
||||
and bv_len, struct bvec_iter has bi_bvec_done, which represents the number of
|
||||
and bv_len, struct bvec_iter has bi_offset, which represents the number of
|
||||
bytes completed in the current bvec.
|
||||
|
||||
There are a bunch of new helper macros for hiding the gory details - in
|
||||
particular, presenting the illusion of partially completed biovecs so that
|
||||
normal code doesn't have to deal with bi_bvec_done.
|
||||
normal code doesn't have to deal with bi_offset.
|
||||
|
||||
* Driver code should no longer refer to biovecs directly; we now have
|
||||
bio_iovec() and bio_iter_iovec() macros that return literal struct biovecs,
|
||||
constructed from the raw biovecs but taking into account bi_bvec_done and
|
||||
constructed from the raw biovecs but taking into account bi_offset and
|
||||
bi_size.
|
||||
|
||||
bio_for_each_segment() has been updated to take a bvec_iter argument
|
||||
@@ -101,7 +101,7 @@ Other implications:
|
||||
I.e. instead of using bio_iovec_idx() (or bio->bi_iovec[bio->bi_idx]), you
|
||||
now use bio_iter_iovec(), which takes a bvec_iter and returns a
|
||||
literal struct bio_vec - constructed on the fly from the raw biovec but
|
||||
taking into account bi_bvec_done (and bi_size).
|
||||
taking into account bi_offset (and bi_size).
|
||||
|
||||
* bi_vcnt can't be trusted or relied upon by driver code - i.e. anything that
|
||||
doesn't actually own the bio. The reason is twofold: firstly, it's not
|
||||
|
||||
@@ -19426,6 +19426,9 @@ L: linux-nvme@lists.infradead.org
|
||||
S: Supported
|
||||
W: http://git.infradead.org/nvme.git
|
||||
T: git git://git.infradead.org/nvme.git
|
||||
F: Documentation/ABI/stable/sysfs-nvme
|
||||
F: Documentation/admin-guide/nvme-multipath.rst
|
||||
F: Documentation/fault-injection/nvme-fault-injection.rst
|
||||
F: Documentation/nvme/
|
||||
F: drivers/nvme/common/
|
||||
F: drivers/nvme/host/
|
||||
@@ -19468,6 +19471,7 @@ L: linux-nvme@lists.infradead.org
|
||||
S: Supported
|
||||
W: http://git.infradead.org/nvme.git
|
||||
T: git git://git.infradead.org/nvme.git
|
||||
F: Documentation/ABI/stable/configfs-nvmet
|
||||
F: drivers/nvme/target/
|
||||
|
||||
NVMEM FRAMEWORK
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <linux/major.h>
|
||||
#include <linux/device_cgroup.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blk-integrity.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/blkpg.h>
|
||||
|
||||
+2
-2
@@ -610,7 +610,7 @@ struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)
|
||||
struct bfq_group *bfqg;
|
||||
|
||||
while (blkg) {
|
||||
if (!blkg->online) {
|
||||
if (!data_race(blkg->online)) {
|
||||
blkg = blkg->parent;
|
||||
continue;
|
||||
}
|
||||
@@ -1168,7 +1168,7 @@ static u64 bfqg_prfill_stat_recursive(struct seq_file *sf,
|
||||
struct blkg_policy_data *pd;
|
||||
struct bfq_stat *stat;
|
||||
|
||||
if (!pos_blkg->online)
|
||||
if (!data_race(pos_blkg->online))
|
||||
continue;
|
||||
|
||||
pd = blkg_to_pd(pos_blkg, &blkcg_policy_bfq);
|
||||
|
||||
@@ -44,12 +44,6 @@ static void bio_integrity_verify_fn(struct work_struct *work)
|
||||
bio_endio(bio);
|
||||
}
|
||||
|
||||
#define BIP_CHECK_FLAGS (BIP_CHECK_GUARD | BIP_CHECK_REFTAG | BIP_CHECK_APPTAG)
|
||||
static bool bip_should_check(struct bio_integrity_payload *bip)
|
||||
{
|
||||
return bip->bip_flags & BIP_CHECK_FLAGS;
|
||||
}
|
||||
|
||||
/**
|
||||
* __bio_integrity_endio - Integrity I/O completion function
|
||||
* @bio: Protected bio
|
||||
@@ -66,7 +60,7 @@ bool __bio_integrity_endio(struct bio *bio)
|
||||
container_of(bip, struct bio_integrity_data, bip);
|
||||
|
||||
if (bio_op(bio) == REQ_OP_READ && !bio->bi_status &&
|
||||
bip_should_check(bip)) {
|
||||
(bip->bip_flags & BIP_CHECK_FLAGS)) {
|
||||
INIT_WORK(&bid->work, bio_integrity_verify_fn);
|
||||
queue_work(kintegrityd_wq, &bid->work);
|
||||
return false;
|
||||
@@ -99,7 +93,7 @@ void bio_integrity_prep(struct bio *bio, unsigned int action)
|
||||
bio_integrity_setup_default(bio);
|
||||
|
||||
/* Auto-generate integrity metadata if this is a write */
|
||||
if (bio_data_dir(bio) == WRITE && bip_should_check(&bid->bip))
|
||||
if (bio_data_dir(bio) == WRITE && (bid->bip.bip_flags & BIP_CHECK_FLAGS))
|
||||
bio_integrity_generate(bio);
|
||||
else
|
||||
bid->saved_bio_iter = bio->bi_iter;
|
||||
|
||||
@@ -46,7 +46,8 @@ void fs_bio_integrity_free(struct bio *bio)
|
||||
|
||||
void fs_bio_integrity_generate(struct bio *bio)
|
||||
{
|
||||
if (fs_bio_integrity_alloc(bio))
|
||||
if (fs_bio_integrity_alloc(bio) &&
|
||||
(bio_integrity(bio)->bip_flags & BIP_CHECK_FLAGS))
|
||||
bio_integrity_generate(bio);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fs_bio_integrity_generate);
|
||||
@@ -60,6 +61,9 @@ int fs_bio_integrity_verify(struct bio *bio, sector_t sector, unsigned int size)
|
||||
.bi_size = size,
|
||||
};
|
||||
|
||||
if (!bip || !(bip->bip_flags & BIP_CHECK_FLAGS))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Reinitialize bip->bip_iter.
|
||||
*
|
||||
|
||||
+194
-12
@@ -229,7 +229,7 @@ void bio_init(struct bio *bio, struct block_device *bdev, struct bio_vec *table,
|
||||
bio->bi_iter.bi_sector = 0;
|
||||
bio->bi_iter.bi_size = 0;
|
||||
bio->bi_iter.bi_idx = 0;
|
||||
bio->bi_iter.bi_bvec_done = 0;
|
||||
bio->bi_iter.bi_offset = 0;
|
||||
bio->bi_end_io = NULL;
|
||||
bio->bi_private = NULL;
|
||||
#ifdef CONFIG_BLK_CGROUP
|
||||
@@ -860,6 +860,7 @@ static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp)
|
||||
bio->bi_write_hint = bio_src->bi_write_hint;
|
||||
bio->bi_write_stream = bio_src->bi_write_stream;
|
||||
bio->bi_iter = bio_src->bi_iter;
|
||||
bio->bi_io_vec = bio_src->bi_io_vec;
|
||||
|
||||
if (bio->bi_bdev) {
|
||||
if (bio->bi_bdev == bio_src->bi_bdev &&
|
||||
@@ -902,8 +903,6 @@ struct bio *bio_alloc_clone(struct block_device *bdev, struct bio *bio_src,
|
||||
bio_put(bio);
|
||||
return NULL;
|
||||
}
|
||||
bio->bi_io_vec = bio_src->bi_io_vec;
|
||||
|
||||
return bio;
|
||||
}
|
||||
EXPORT_SYMBOL(bio_alloc_clone);
|
||||
@@ -923,7 +922,7 @@ int bio_init_clone(struct block_device *bdev, struct bio *bio,
|
||||
{
|
||||
int ret;
|
||||
|
||||
bio_init(bio, bdev, bio_src->bi_io_vec, 0, bio_src->bi_opf);
|
||||
bio_init(bio, bdev, NULL, 0, bio_src->bi_opf);
|
||||
ret = __bio_clone(bio, bio_src, gfp);
|
||||
if (ret)
|
||||
bio_uninit(bio);
|
||||
@@ -1182,15 +1181,19 @@ void __bio_release_pages(struct bio *bio, bool mark_dirty)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__bio_release_pages);
|
||||
|
||||
void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter)
|
||||
bool bio_iov_iter_set(struct bio *bio, const struct iov_iter *iter)
|
||||
{
|
||||
if (!iov_iter_is_bvec(iter))
|
||||
return false;
|
||||
|
||||
WARN_ON_ONCE(bio->bi_max_vecs);
|
||||
|
||||
bio->bi_io_vec = (struct bio_vec *)iter->bvec;
|
||||
bio->bi_iter.bi_idx = 0;
|
||||
bio->bi_iter.bi_bvec_done = iter->iov_offset;
|
||||
bio->bi_iter.bi_offset = iter->iov_offset;
|
||||
bio->bi_iter.bi_size = iov_iter_count(iter);
|
||||
bio_set_flag(bio, BIO_CLONED);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1221,10 +1224,45 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_KERNEL
|
||||
static inline bool bio_iov_bvec_aligned(const struct bio *bio,
|
||||
unsigned mem_align_mask)
|
||||
{
|
||||
struct bvec_iter iter;
|
||||
struct bio_vec bv;
|
||||
|
||||
/*
|
||||
* Correct callers never break the alignment requirements, so this
|
||||
* exhaustive check is only paid for in debug builds.
|
||||
*/
|
||||
for_each_mp_bvec(bv, bio->bi_io_vec, iter, bio->bi_iter)
|
||||
if ((bv.bv_offset | bv.bv_len) & mem_align_mask)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static inline bool bio_iov_bvec_aligned(const struct bio *bio,
|
||||
unsigned mem_align_mask)
|
||||
{
|
||||
/*
|
||||
* We forward the bio_vec as-is, so ITER_BVEC callers must provide
|
||||
* segments already aligned to the device's DMA alignment. The only
|
||||
* unchecked user-controllable offset that reaches here is an io_uring
|
||||
* registered buffer where just the first segment can be unaligned
|
||||
* (the rest is virtually contiguous), so checking only that one is
|
||||
* sufficient to know if the entire vector is valid.
|
||||
*/
|
||||
return !(mp_bvec_iter_offset(bio->bi_io_vec, bio->bi_iter) &
|
||||
mem_align_mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* bio_iov_iter_get_pages - add user or kernel pages to a bio
|
||||
* @bio: bio to add pages to
|
||||
* @iter: iov iterator describing the region to be added
|
||||
* @mem_align_mask: the mask the source address and length must be aligned to,
|
||||
* 0 for no requirement
|
||||
* @len_align_mask: the mask to align the total size to, 0 for any length
|
||||
*
|
||||
* This takes either an iterator pointing to user memory, or one pointing to
|
||||
@@ -1243,15 +1281,18 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
|
||||
* is returned only if 0 pages could be pinned.
|
||||
*/
|
||||
int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
|
||||
unsigned len_align_mask)
|
||||
unsigned mem_align_mask, unsigned len_align_mask)
|
||||
{
|
||||
iov_iter_extraction_t flags = 0;
|
||||
|
||||
if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)))
|
||||
return -EIO;
|
||||
|
||||
if (iov_iter_is_bvec(iter)) {
|
||||
bio_iov_bvec_set(bio, iter);
|
||||
if (bio_iov_iter_set(bio, iter)) {
|
||||
if (iov_iter_is_bvec(iter) &&
|
||||
!bio_iov_bvec_aligned(bio, mem_align_mask))
|
||||
return -EINVAL;
|
||||
|
||||
iov_iter_advance(iter, bio->bi_iter.bi_size);
|
||||
return 0;
|
||||
}
|
||||
@@ -1266,8 +1307,19 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
|
||||
|
||||
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec,
|
||||
BIO_MAX_SIZE - bio->bi_iter.bi_size,
|
||||
&bio->bi_vcnt, bio->bi_max_vecs, flags);
|
||||
&bio->bi_vcnt, bio->bi_max_vecs,
|
||||
mem_align_mask, flags);
|
||||
if (ret <= 0) {
|
||||
/*
|
||||
* A misaligned vector fails the whole I/O. Release any
|
||||
* pages pinned by earlier iterations before returning
|
||||
* since this bio won't be submitted to release them.
|
||||
*/
|
||||
if (ret == -EINVAL) {
|
||||
bio_release_pages(bio, false);
|
||||
bio_clear_flag(bio, BIO_PAGE_PINNED);
|
||||
bio->bi_vcnt = 0;
|
||||
}
|
||||
if (!bio->bi_vcnt)
|
||||
return ret;
|
||||
break;
|
||||
@@ -1380,7 +1432,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
|
||||
|
||||
do {
|
||||
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len,
|
||||
&bio->bi_vcnt, bio->bi_max_vecs - 1, 0);
|
||||
&bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0);
|
||||
if (ret <= 0) {
|
||||
if (!bio->bi_vcnt)
|
||||
goto out_folio_put;
|
||||
@@ -1741,6 +1793,61 @@ defer:
|
||||
schedule_work(&bio_dirty_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Infrastructure for deferring bio completions to task-context via a per-CPU
|
||||
* workqueue. Triggered either by the BIO_COMPLETE_IN_TASK bio flag (static
|
||||
* decision at submit time) or by calling bio_complete_in_task() from
|
||||
* bi_end_io() (dynamic decision at completion time).
|
||||
*/
|
||||
|
||||
struct bio_complete_batch {
|
||||
struct bio_list list;
|
||||
struct work_struct work;
|
||||
int cpu;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct bio_complete_batch, bio_complete_batch);
|
||||
static struct workqueue_struct *bio_complete_wq;
|
||||
|
||||
static void bio_complete_work_fn(struct work_struct *w)
|
||||
{
|
||||
struct bio_complete_batch *batch =
|
||||
container_of(w, struct bio_complete_batch, work);
|
||||
|
||||
while (1) {
|
||||
struct bio_list list;
|
||||
struct bio *bio;
|
||||
|
||||
local_irq_disable();
|
||||
list = batch->list;
|
||||
bio_list_init(&batch->list);
|
||||
local_irq_enable();
|
||||
|
||||
if (bio_list_empty(&list))
|
||||
break;
|
||||
|
||||
while ((bio = bio_list_pop(&list)))
|
||||
bio->bi_end_io(bio);
|
||||
}
|
||||
}
|
||||
|
||||
void __bio_complete_in_task(struct bio *bio)
|
||||
{
|
||||
struct bio_complete_batch *batch;
|
||||
unsigned long flags;
|
||||
bool was_empty;
|
||||
|
||||
local_irq_save(flags);
|
||||
batch = this_cpu_ptr(&bio_complete_batch);
|
||||
was_empty = bio_list_empty(&batch->list);
|
||||
bio_list_add(&batch->list, bio);
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (was_empty)
|
||||
queue_work_on(batch->cpu, bio_complete_wq, &batch->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__bio_complete_in_task);
|
||||
|
||||
static inline bool bio_remaining_done(struct bio *bio)
|
||||
{
|
||||
/*
|
||||
@@ -1815,7 +1922,9 @@ again:
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bio->bi_end_io)
|
||||
if (bio_flagged(bio, BIO_COMPLETE_IN_TASK) && bio_in_atomic())
|
||||
__bio_complete_in_task(bio);
|
||||
else if (bio->bi_end_io)
|
||||
bio->bi_end_io(bio);
|
||||
}
|
||||
EXPORT_SYMBOL(bio_endio);
|
||||
@@ -2001,6 +2110,55 @@ bad:
|
||||
}
|
||||
EXPORT_SYMBOL(bioset_init);
|
||||
|
||||
static int bio_complete_batch_cpu_online(unsigned int cpu)
|
||||
{
|
||||
struct bio_complete_batch *batch = &per_cpu(bio_complete_batch, cpu);
|
||||
|
||||
enable_work(&batch->work);
|
||||
if (!bio_list_empty(&batch->list))
|
||||
queue_work_on(cpu, bio_complete_wq, &batch->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable this CPU's work item so that it cannot run on an unbound worker
|
||||
* after the CPU is offlined.
|
||||
*/
|
||||
static int bio_complete_batch_cpu_down_prep(unsigned int cpu)
|
||||
{
|
||||
disable_work_sync(&per_cpu(bio_complete_batch, cpu).work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drain a dead CPU's deferred bio completions. The CPU is dead and the worker
|
||||
* is canceled so no locking is needed.
|
||||
*/
|
||||
static int bio_complete_batch_cpu_dead(unsigned int cpu)
|
||||
{
|
||||
struct bio_complete_batch *batch =
|
||||
per_cpu_ptr(&bio_complete_batch, cpu);
|
||||
struct bio *bio;
|
||||
|
||||
while ((bio = bio_list_pop(&batch->list)))
|
||||
bio->bi_end_io(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init bio_complete_batch_init(int cpu)
|
||||
{
|
||||
struct bio_complete_batch *batch =
|
||||
per_cpu_ptr(&bio_complete_batch, cpu);
|
||||
|
||||
bio_list_init(&batch->list);
|
||||
INIT_WORK(&batch->work, bio_complete_work_fn);
|
||||
batch->cpu = cpu;
|
||||
|
||||
if (!cpu_online(cpu))
|
||||
disable_work_sync(&batch->work);
|
||||
}
|
||||
|
||||
static int __init init_bio(void)
|
||||
{
|
||||
int i;
|
||||
@@ -2015,6 +2173,30 @@ static int __init init_bio(void)
|
||||
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
|
||||
}
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
bio_complete_batch_init(i);
|
||||
|
||||
bio_complete_wq = alloc_workqueue("bio_complete",
|
||||
WQ_MEM_RECLAIM | WQ_PERCPU, 0);
|
||||
if (!bio_complete_wq)
|
||||
panic("bio: can't allocate bio_complete workqueue\n");
|
||||
|
||||
/*
|
||||
* bio task-context completion draining on hot-unplugged CPUs:
|
||||
*
|
||||
* 1. Stop the per-CPU work item while the CPU is still online, so
|
||||
* that it cannot run on an unbound worker later.
|
||||
* 2. Drain leftover bios added between worker disabling and CPU
|
||||
* offlining.
|
||||
*/
|
||||
cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
|
||||
"block/bio:complete:online",
|
||||
bio_complete_batch_cpu_online,
|
||||
bio_complete_batch_cpu_down_prep);
|
||||
cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,
|
||||
"block/bio:complete:dead",
|
||||
NULL, bio_complete_batch_cpu_dead);
|
||||
|
||||
cpuhp_setup_state_multi(CPUHP_BIO_DEAD, "block/bio:dead", NULL,
|
||||
bio_cpu_dead);
|
||||
|
||||
|
||||
@@ -88,8 +88,7 @@ EXPORT_SYMBOL_GPL(blkg_prfill_rwstat);
|
||||
* @sum: blkg_rwstat_sample structure containing the results
|
||||
*
|
||||
* Collect the blkg_rwstat specified by @blkg, @pol and @off and all its
|
||||
* online descendants and their aux counts. The caller must be holding the
|
||||
* queue lock for online tests.
|
||||
* online descendants and their aux counts.
|
||||
*
|
||||
* If @pol is NULL, blkg_rwstat is at @off bytes into @blkg; otherwise, it
|
||||
* is at @off bytes into @blkg's blkg_policy_data of the policy.
|
||||
@@ -107,7 +106,7 @@ void blkg_rwstat_recursive_sum(struct blkcg_gq *blkg, struct blkcg_policy *pol,
|
||||
blkg_for_each_descendant_pre(pos_blkg, pos_css, blkg) {
|
||||
struct blkg_rwstat *rwstat;
|
||||
|
||||
if (!pos_blkg->online)
|
||||
if (!data_race(pos_blkg->online))
|
||||
continue;
|
||||
|
||||
if (pol) {
|
||||
|
||||
+49
-31
@@ -53,6 +53,9 @@ EXPORT_SYMBOL_GPL(blkcg_root);
|
||||
struct cgroup_subsys_state * const blkcg_root_css = &blkcg_root.css;
|
||||
EXPORT_SYMBOL_GPL(blkcg_root_css);
|
||||
|
||||
/* number of blkcgs with a non-zero congestion_count */
|
||||
atomic_t blkcg_nr_congested __read_mostly = ATOMIC_INIT(0);
|
||||
|
||||
static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS];
|
||||
|
||||
static LIST_HEAD(all_blkcgs); /* protected by blkcg_pol_mutex */
|
||||
@@ -569,6 +572,7 @@ static void blkg_destroy_all(struct gendisk *disk)
|
||||
int i;
|
||||
|
||||
restart:
|
||||
mutex_lock(&q->blkcg_mutex);
|
||||
spin_lock_irq(&q->queue_lock);
|
||||
list_for_each_entry(blkg, &q->blkg_list, q_node) {
|
||||
struct blkcg *blkcg = blkg->blkcg;
|
||||
@@ -587,6 +591,7 @@ restart:
|
||||
if (!(--count)) {
|
||||
count = BLKG_DESTROY_BATCH_SIZE;
|
||||
spin_unlock_irq(&q->queue_lock);
|
||||
mutex_unlock(&q->blkcg_mutex);
|
||||
cond_resched();
|
||||
goto restart;
|
||||
}
|
||||
@@ -606,6 +611,7 @@ restart:
|
||||
|
||||
q->root_blkg = NULL;
|
||||
spin_unlock_irq(&q->queue_lock);
|
||||
mutex_unlock(&q->blkcg_mutex);
|
||||
|
||||
wake_up_var(&q->root_blkg);
|
||||
}
|
||||
@@ -1347,6 +1353,16 @@ static void blkcg_css_free(struct cgroup_subsys_state *css)
|
||||
struct blkcg *blkcg = css_to_blkcg(css);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Every blkg holds a reference on this css and drops any delay it
|
||||
* still has from pd_free_fn(), so this is expected to be zero. Should
|
||||
* a policy ever leave one behind, drop it here rather than let it pin
|
||||
* blkcg_nr_congested and disable the fast path for the rest of the
|
||||
* boot. Nothing can race with us at this point.
|
||||
*/
|
||||
if (WARN_ON_ONCE(atomic_xchg(&blkcg->congestion_count, 0) > 0))
|
||||
atomic_dec(&blkcg_nr_congested);
|
||||
|
||||
mutex_lock(&blkcg_pol_mutex);
|
||||
|
||||
list_del(&blkcg->all_blkcgs_node);
|
||||
@@ -1526,6 +1542,31 @@ struct cgroup_subsys io_cgrp_subsys = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(io_cgrp_subsys);
|
||||
|
||||
/*
|
||||
* Tear down per-blkg policy data for @pol on @q.
|
||||
*/
|
||||
static void blkcg_policy_teardown_pds(struct request_queue *q,
|
||||
const struct blkcg_policy *pol)
|
||||
{
|
||||
struct blkcg_gq *blkg;
|
||||
|
||||
list_for_each_entry(blkg, &q->blkg_list, q_node) {
|
||||
struct blkcg *blkcg = blkg->blkcg;
|
||||
struct blkg_policy_data *pd;
|
||||
|
||||
spin_lock(&blkcg->lock);
|
||||
pd = blkg->pd[pol->plid];
|
||||
if (pd) {
|
||||
if (pd->online && pol->pd_offline_fn)
|
||||
pol->pd_offline_fn(pd);
|
||||
pd->online = false;
|
||||
pol->pd_free_fn(pd);
|
||||
WRITE_ONCE(blkg->pd[pol->plid], NULL);
|
||||
}
|
||||
spin_unlock(&blkcg->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* blkcg_activate_policy - activate a blkcg policy on a gendisk
|
||||
* @disk: gendisk of interest
|
||||
@@ -1563,6 +1604,8 @@ int blkcg_activate_policy(struct gendisk *disk, const struct blkcg_policy *pol)
|
||||
|
||||
if (queue_is_mq(q))
|
||||
memflags = blk_mq_freeze_queue(q);
|
||||
|
||||
mutex_lock(&q->blkcg_mutex);
|
||||
retry:
|
||||
spin_lock_irq(&q->queue_lock);
|
||||
|
||||
@@ -1572,6 +1615,8 @@ retry:
|
||||
|
||||
if (blkg->pd[pol->plid])
|
||||
continue;
|
||||
if (hlist_unhashed(&blkg->blkcg_node))
|
||||
continue;
|
||||
|
||||
/* If prealloc matches, use it; otherwise try GFP_NOWAIT */
|
||||
if (blkg == pinned_blkg) {
|
||||
@@ -1625,6 +1670,7 @@ retry:
|
||||
|
||||
spin_unlock_irq(&q->queue_lock);
|
||||
out:
|
||||
mutex_unlock(&q->blkcg_mutex);
|
||||
if (queue_is_mq(q))
|
||||
blk_mq_unfreeze_queue(q, memflags);
|
||||
if (pinned_blkg)
|
||||
@@ -1636,21 +1682,7 @@ out:
|
||||
enomem:
|
||||
/* alloc failed, take down everything */
|
||||
spin_lock_irq(&q->queue_lock);
|
||||
list_for_each_entry(blkg, &q->blkg_list, q_node) {
|
||||
struct blkcg *blkcg = blkg->blkcg;
|
||||
struct blkg_policy_data *pd;
|
||||
|
||||
spin_lock(&blkcg->lock);
|
||||
pd = blkg->pd[pol->plid];
|
||||
if (pd) {
|
||||
if (pd->online && pol->pd_offline_fn)
|
||||
pol->pd_offline_fn(pd);
|
||||
pd->online = false;
|
||||
pol->pd_free_fn(pd);
|
||||
WRITE_ONCE(blkg->pd[pol->plid], NULL);
|
||||
}
|
||||
spin_unlock(&blkcg->lock);
|
||||
}
|
||||
blkcg_policy_teardown_pds(q, pol);
|
||||
spin_unlock_irq(&q->queue_lock);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
@@ -1669,7 +1701,6 @@ void blkcg_deactivate_policy(struct gendisk *disk,
|
||||
const struct blkcg_policy *pol)
|
||||
{
|
||||
struct request_queue *q = disk->queue;
|
||||
struct blkcg_gq *blkg;
|
||||
unsigned int memflags;
|
||||
|
||||
if (!blkcg_policy_enabled(q, pol))
|
||||
@@ -1682,20 +1713,7 @@ void blkcg_deactivate_policy(struct gendisk *disk,
|
||||
spin_lock_irq(&q->queue_lock);
|
||||
|
||||
__clear_bit(pol->plid, q->blkcg_pols);
|
||||
|
||||
list_for_each_entry(blkg, &q->blkg_list, q_node) {
|
||||
struct blkcg *blkcg = blkg->blkcg;
|
||||
|
||||
spin_lock(&blkcg->lock);
|
||||
if (blkg->pd[pol->plid]) {
|
||||
if (blkg->pd[pol->plid]->online && pol->pd_offline_fn)
|
||||
pol->pd_offline_fn(blkg->pd[pol->plid]);
|
||||
pol->pd_free_fn(blkg->pd[pol->plid]);
|
||||
blkg->pd[pol->plid] = NULL;
|
||||
}
|
||||
spin_unlock(&blkcg->lock);
|
||||
}
|
||||
|
||||
blkcg_policy_teardown_pds(q, pol);
|
||||
spin_unlock_irq(&q->queue_lock);
|
||||
mutex_unlock(&q->blkcg_mutex);
|
||||
|
||||
@@ -2223,7 +2241,7 @@ void blk_cgroup_bio_start(struct bio *bio)
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
bool blk_cgroup_congested(void)
|
||||
bool __blk_cgroup_congested(void)
|
||||
{
|
||||
struct blkcg *blkcg;
|
||||
bool ret = false;
|
||||
|
||||
+21
-4
@@ -375,12 +375,29 @@ static inline void blkg_put(struct blkcg_gq *blkg)
|
||||
if (((d_blkg) = blkg_lookup(css_to_blkcg(pos_css), \
|
||||
(p_blkg)->q)))
|
||||
|
||||
/*
|
||||
* blkcg_nr_congested gates the hierarchy walk in blk_cgroup_congested().
|
||||
* These two helpers keep it in step with each blkcg's congestion_count in
|
||||
* normal operation; blkcg_css_free() drops a residual count as a backstop.
|
||||
*/
|
||||
static inline void blkcg_inc_congestion_count(struct blkcg *blkcg)
|
||||
{
|
||||
if (atomic_inc_return(&blkcg->congestion_count) == 1)
|
||||
atomic_inc(&blkcg_nr_congested);
|
||||
}
|
||||
|
||||
static inline void blkcg_dec_congestion_count(struct blkcg *blkcg)
|
||||
{
|
||||
if (atomic_dec_and_test(&blkcg->congestion_count))
|
||||
atomic_dec(&blkcg_nr_congested);
|
||||
}
|
||||
|
||||
static inline void blkcg_use_delay(struct blkcg_gq *blkg)
|
||||
{
|
||||
if (WARN_ON_ONCE(atomic_read(&blkg->use_delay) < 0))
|
||||
return;
|
||||
if (atomic_add_return(1, &blkg->use_delay) == 1)
|
||||
atomic_inc(&blkg->blkcg->congestion_count);
|
||||
blkcg_inc_congestion_count(blkg->blkcg);
|
||||
}
|
||||
|
||||
static inline int blkcg_unuse_delay(struct blkcg_gq *blkg)
|
||||
@@ -405,7 +422,7 @@ static inline int blkcg_unuse_delay(struct blkcg_gq *blkg)
|
||||
if (old == 0)
|
||||
return 0;
|
||||
if (old == 1)
|
||||
atomic_dec(&blkg->blkcg->congestion_count);
|
||||
blkcg_dec_congestion_count(blkg->blkcg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -424,7 +441,7 @@ static inline void blkcg_set_delay(struct blkcg_gq *blkg, u64 delay)
|
||||
|
||||
/* We only want 1 person setting the congestion count for this blkg. */
|
||||
if (!old && atomic_try_cmpxchg(&blkg->use_delay, &old, -1))
|
||||
atomic_inc(&blkg->blkcg->congestion_count);
|
||||
blkcg_inc_congestion_count(blkg->blkcg);
|
||||
|
||||
atomic64_set(&blkg->delay_nsec, delay);
|
||||
}
|
||||
@@ -441,7 +458,7 @@ static inline void blkcg_clear_delay(struct blkcg_gq *blkg)
|
||||
|
||||
/* We only want 1 person clearing the congestion count for this blkg. */
|
||||
if (old && atomic_try_cmpxchg(&blkg->use_delay, &old, 0))
|
||||
atomic_dec(&blkg->blkcg->congestion_count);
|
||||
blkcg_dec_congestion_count(blkg->blkcg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-1
@@ -897,10 +897,14 @@ void submit_bio_noacct(struct bio *bio)
|
||||
if (!q->limits.max_write_zeroes_sectors)
|
||||
goto not_supported;
|
||||
break;
|
||||
case REQ_OP_ZONE_RESET:
|
||||
case REQ_OP_ZONE_OPEN:
|
||||
case REQ_OP_ZONE_CLOSE:
|
||||
case REQ_OP_ZONE_RESET:
|
||||
case REQ_OP_ZONE_FINISH:
|
||||
/* Zone management operations require sequential zones. */
|
||||
if (!bdev_zone_is_seq(bio->bi_bdev, bio->bi_iter.bi_sector))
|
||||
goto end_io;
|
||||
break;
|
||||
case REQ_OP_ZONE_RESET_ALL:
|
||||
if (!bdev_is_zoned(bio->bi_bdev))
|
||||
goto not_supported;
|
||||
|
||||
+14
-7
@@ -1592,7 +1592,7 @@ static void ioc_lat_stat(struct ioc *ioc, u32 *missed_ppm_ar, u32 *rq_wait_pct_p
|
||||
u64 rq_wait_ns = 0;
|
||||
int cpu, rw;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct ioc_pcpu_stat *stat = per_cpu_ptr(ioc->pcpu_stat, cpu);
|
||||
u64 this_rq_wait_ns;
|
||||
|
||||
@@ -3063,6 +3063,7 @@ static void iocg_release(struct rcu_head *rcu)
|
||||
static void ioc_pd_free(struct blkg_policy_data *pd)
|
||||
{
|
||||
struct ioc_gq *iocg = pd_to_iocg(pd);
|
||||
struct blkcg_gq *blkg = pd_to_blkg(pd);
|
||||
struct ioc *ioc = iocg->ioc;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -3085,6 +3086,12 @@ static void ioc_pd_free(struct blkg_policy_data *pd)
|
||||
hrtimer_cancel(&iocg->waitq_timer);
|
||||
}
|
||||
|
||||
/* off ->active_iocgs and timer gone, so nothing can re-arm the delay */
|
||||
iocg->delay = 0;
|
||||
iocg->indelay_since = 0;
|
||||
if (blkg)
|
||||
blkcg_clear_delay(blkg);
|
||||
|
||||
call_rcu(&pd->rcu_head, iocg_release);
|
||||
}
|
||||
|
||||
@@ -3093,23 +3100,23 @@ static void ioc_pd_stat(struct blkg_policy_data *pd, struct seq_file *s)
|
||||
struct ioc_gq *iocg = pd_to_iocg(pd);
|
||||
struct ioc *ioc = iocg->ioc;
|
||||
|
||||
if (!ioc->enabled)
|
||||
if (!data_race(ioc->enabled))
|
||||
return;
|
||||
|
||||
if (iocg->level == 0) {
|
||||
unsigned vp10k = DIV64_U64_ROUND_CLOSEST(
|
||||
ioc->vtime_base_rate * 10000,
|
||||
data_race(ioc->vtime_base_rate) * 10000,
|
||||
VTIME_PER_USEC);
|
||||
seq_printf(s, " cost.vrate=%u.%02u", vp10k / 100, vp10k % 100);
|
||||
}
|
||||
|
||||
seq_printf(s, " cost.usage=%llu", iocg->last_stat.usage_us);
|
||||
seq_printf(s, " cost.usage=%llu", data_race(iocg->last_stat.usage_us));
|
||||
|
||||
if (blkcg_debug_stats)
|
||||
seq_printf(s, " cost.wait=%llu cost.indebt=%llu cost.indelay=%llu",
|
||||
iocg->last_stat.wait_us,
|
||||
iocg->last_stat.indebt_us,
|
||||
iocg->last_stat.indelay_us);
|
||||
data_race(iocg->last_stat.wait_us),
|
||||
data_race(iocg->last_stat.indebt_us),
|
||||
data_race(iocg->last_stat.indelay_us));
|
||||
}
|
||||
|
||||
static u64 ioc_weight_prfill(struct seq_file *sf, struct blkg_policy_data *pd,
|
||||
|
||||
+11
-2
@@ -523,7 +523,7 @@ static void iolatency_check_latencies(struct iolatency_grp *iolat, u64 now)
|
||||
|
||||
latency_stat_init(iolat, &stat);
|
||||
preempt_disable();
|
||||
for_each_online_cpu(cpu) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct latency_stat *s;
|
||||
s = per_cpu_ptr(iolat->stats, cpu);
|
||||
latency_stat_sum(iolat, &stat, s);
|
||||
@@ -925,7 +925,7 @@ static void iolatency_ssd_stat(struct iolatency_grp *iolat, struct seq_file *s)
|
||||
|
||||
latency_stat_init(iolat, &stat);
|
||||
preempt_disable();
|
||||
for_each_online_cpu(cpu) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct latency_stat *s;
|
||||
s = per_cpu_ptr(iolat->stats, cpu);
|
||||
latency_stat_sum(iolat, &stat, s);
|
||||
@@ -1043,6 +1043,15 @@ static void iolat_release(struct rcu_head *rcu)
|
||||
|
||||
static void iolatency_pd_free(struct blkg_policy_data *pd)
|
||||
{
|
||||
struct blkcg_gq *blkg = pd_to_blkg(pd);
|
||||
|
||||
/*
|
||||
* Groups throttled as collateral have min_lat_nsec == 0, so
|
||||
* iolatency_pd_offline() leaves their delay set. Drop it here, where
|
||||
* no in-flight bio can re-arm it via check_scale_change().
|
||||
*/
|
||||
if (blkg)
|
||||
blkcg_clear_delay(blkg);
|
||||
call_rcu(&pd->rcu_head, iolat_release);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -274,7 +274,7 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter,
|
||||
* No alignment requirements on our part to support arbitrary
|
||||
* passthrough commands.
|
||||
*/
|
||||
ret = bio_iov_iter_get_pages(bio, iter, 0);
|
||||
ret = bio_iov_iter_get_pages(bio, iter, 0, 0);
|
||||
if (ret)
|
||||
goto out_put;
|
||||
ret = blk_rq_append_bio(rq, bio);
|
||||
@@ -473,7 +473,7 @@ static int blk_rq_map_user_bvec(struct request *rq, const struct iov_iter *iter)
|
||||
bio = blk_rq_map_bio_alloc(rq, 0, GFP_KERNEL);
|
||||
if (!bio)
|
||||
return -ENOMEM;
|
||||
bio_iov_bvec_set(bio, iter);
|
||||
bio_iov_iter_set(bio, iter);
|
||||
|
||||
ret = blk_rq_append_bio(rq, bio);
|
||||
if (ret)
|
||||
|
||||
+4
-4
@@ -33,7 +33,7 @@ static inline void bio_get_last_bvec(struct bio *bio, struct bio_vec *bv)
|
||||
|
||||
bio_advance_iter(bio, &iter, iter.bi_size);
|
||||
|
||||
if (!iter.bi_bvec_done)
|
||||
if (!iter.bi_offset)
|
||||
idx = iter.bi_idx - 1;
|
||||
else /* in the middle of bvec */
|
||||
idx = iter.bi_idx;
|
||||
@@ -41,11 +41,11 @@ static inline void bio_get_last_bvec(struct bio *bio, struct bio_vec *bv)
|
||||
*bv = bio->bi_io_vec[idx];
|
||||
|
||||
/*
|
||||
* iter.bi_bvec_done records actual length of the last bvec
|
||||
* iter.bi_offset records actual length of the last bvec
|
||||
* if this bio ends in the middle of one io vector
|
||||
*/
|
||||
if (iter.bi_bvec_done)
|
||||
bv->bv_len = iter.bi_bvec_done;
|
||||
if (iter.bi_offset)
|
||||
bv->bv_len = iter.bi_offset;
|
||||
}
|
||||
|
||||
static inline bool bio_will_gap(struct request_queue *q,
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ static bool blk_map_iter_next(struct request *req, struct blk_map_iter *iter,
|
||||
* one could be merged into it. This typically happens when moving to
|
||||
* the next bio, but some callers also don't pack bvecs tight.
|
||||
*/
|
||||
while (!iter->iter.bi_size || !iter->iter.bi_bvec_done) {
|
||||
while (!iter->iter.bi_size || !iter->iter.bi_offset) {
|
||||
struct bio_vec next;
|
||||
|
||||
if (!__blk_map_iter_next(iter))
|
||||
|
||||
@@ -4975,6 +4975,7 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set)
|
||||
srcu_barrier(&set->tags_srcu);
|
||||
cleanup_srcu_struct(&set->tags_srcu);
|
||||
if (set->flags & BLK_MQ_F_BLOCKING) {
|
||||
srcu_barrier(set->srcu);
|
||||
cleanup_srcu_struct(set->srcu);
|
||||
kfree(set->srcu);
|
||||
}
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ static void blk_stat_timer_fn(struct timer_list *t)
|
||||
for (bucket = 0; bucket < cb->buckets; bucket++)
|
||||
blk_rq_stat_init(&cb->stat[bucket]);
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct blk_rq_stat *cpu_stat;
|
||||
|
||||
cpu_stat = per_cpu_ptr(cb->cpu_stat, cpu);
|
||||
|
||||
@@ -120,8 +120,6 @@ struct throtl_grp {
|
||||
int64_t bytes_disp[2];
|
||||
int io_disp[2];
|
||||
|
||||
unsigned long last_check_time;
|
||||
|
||||
/* When did we start a new slice */
|
||||
unsigned long slice_start[2];
|
||||
unsigned long slice_end[2];
|
||||
|
||||
@@ -1574,30 +1574,6 @@ static void blk_zone_wplug_handle_native_zone_append(struct bio *bio)
|
||||
disk_put_zone_wplug(zwplug);
|
||||
}
|
||||
|
||||
static bool blk_zone_wplug_handle_zone_mgmt(struct bio *bio)
|
||||
{
|
||||
if (bio_op(bio) != REQ_OP_ZONE_RESET_ALL &&
|
||||
!bdev_zone_is_seq(bio->bi_bdev, bio->bi_iter.bi_sector)) {
|
||||
/*
|
||||
* Zone reset and zone finish operations do not apply to
|
||||
* conventional zones.
|
||||
*/
|
||||
bio_io_error(bio);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* No-wait zone management BIOs do not make much sense as the callers
|
||||
* issue these as blocking operations in most cases. To avoid issues
|
||||
* with the BIO execution potentially failing with BLK_STS_AGAIN, warn
|
||||
* about REQ_NOWAIT being set and ignore that flag.
|
||||
*/
|
||||
if (WARN_ON_ONCE(bio->bi_opf & REQ_NOWAIT))
|
||||
bio->bi_opf &= ~REQ_NOWAIT;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* blk_zone_plug_bio - Handle a zone write BIO with zone write plugging
|
||||
* @bio: The BIO being submitted
|
||||
@@ -1644,15 +1620,9 @@ bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)
|
||||
case REQ_OP_WRITE:
|
||||
case REQ_OP_WRITE_ZEROES:
|
||||
return blk_zone_wplug_handle_write(bio, nr_segs);
|
||||
case REQ_OP_ZONE_RESET:
|
||||
case REQ_OP_ZONE_FINISH:
|
||||
case REQ_OP_ZONE_RESET_ALL:
|
||||
return blk_zone_wplug_handle_zone_mgmt(bio);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_zone_plug_bio);
|
||||
|
||||
|
||||
+1
-1
@@ -406,7 +406,7 @@ static inline bool bio_may_need_split(struct bio *bio,
|
||||
return true;
|
||||
|
||||
bv = __bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter);
|
||||
if (bio->bi_iter.bi_size > bv->bv_len - bio->bi_iter.bi_bvec_done)
|
||||
if (bio->bi_iter.bi_size > bv->bv_len - bio->bi_iter.bi_offset)
|
||||
return true;
|
||||
if ((bv->bv_offset | bv->bv_len) & lim->dma_alignment)
|
||||
return true;
|
||||
|
||||
+14
-14
@@ -46,7 +46,7 @@ static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb,
|
||||
static inline int blkdev_iov_iter_get_pages(struct bio *bio,
|
||||
struct iov_iter *iter, struct block_device *bdev)
|
||||
{
|
||||
return bio_iov_iter_get_pages(bio, iter,
|
||||
return bio_iov_iter_get_pages(bio, iter, bdev_dma_alignment(bdev),
|
||||
bdev_logical_block_size(bdev) - 1);
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
||||
|
||||
ret = blkdev_iov_iter_get_pages(bio, iter, bdev);
|
||||
if (unlikely(ret)) {
|
||||
bio_endio_status(bio, BLK_STS_IOERR);
|
||||
bio_endio_status(bio, errno_to_blk_status(ret));
|
||||
break;
|
||||
}
|
||||
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||
@@ -238,8 +238,10 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
|
||||
}
|
||||
if (iocb->ki_flags & IOCB_HAS_METADATA) {
|
||||
ret = bio_integrity_map_iter(bio, iocb->private);
|
||||
if (unlikely(ret))
|
||||
goto fail;
|
||||
if (unlikely(ret)) {
|
||||
bio_endio_status(bio, errno_to_blk_status(ret));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_read) {
|
||||
@@ -340,15 +342,13 @@ static ssize_t __blkdev_direct_IO_async(struct kiocb *iocb,
|
||||
bio->bi_end_io = blkdev_bio_end_io_async;
|
||||
bio->bi_ioprio = iocb->ki_ioprio;
|
||||
|
||||
if (iov_iter_is_bvec(iter)) {
|
||||
/*
|
||||
* Users don't rely on the iterator being in any particular
|
||||
* state for async I/O returning -EIOCBQUEUED, hence we can
|
||||
* avoid expensive iov_iter_advance(). Bypass
|
||||
* bio_iov_iter_get_pages() and set the bvec directly.
|
||||
*/
|
||||
bio_iov_bvec_set(bio, iter);
|
||||
} else {
|
||||
/*
|
||||
* Users don't rely on the iterator being in any particular
|
||||
* state for async I/O returning -EIOCBQUEUED, hence we can
|
||||
* avoid expensive iov_iter_advance(). Bypass
|
||||
* bio_iov_iter_get_pages() and set the bvec directly.
|
||||
*/
|
||||
if (!bio_iov_iter_set(bio, iter)) {
|
||||
ret = blkdev_iov_iter_get_pages(bio, iter, bdev);
|
||||
if (unlikely(ret))
|
||||
goto out_bio_put;
|
||||
@@ -943,7 +943,7 @@ const struct file_operations def_blk_fops = {
|
||||
.splice_write = iter_file_splice_write,
|
||||
.fallocate = blkdev_fallocate,
|
||||
.uring_cmd = blkdev_uring_cmd,
|
||||
.fop_flags = FOP_BUFFER_RASYNC,
|
||||
.fop_flags = FOP_BUFFER_RASYNC | FOP_DONTCACHE,
|
||||
};
|
||||
|
||||
static __init int blkdev_init(void)
|
||||
|
||||
@@ -681,6 +681,7 @@ static bool __blk_mark_disk_dead(struct gendisk *disk)
|
||||
*/
|
||||
void blk_mark_disk_dead(struct gendisk *disk)
|
||||
{
|
||||
blk_queue_flag_set(QUEUE_FLAG_DYING, disk->queue);
|
||||
__blk_mark_disk_dead(disk);
|
||||
blk_report_disk_dead(disk, true);
|
||||
}
|
||||
|
||||
+47
-13
@@ -14,6 +14,7 @@
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/io_uring/cmd.h>
|
||||
#include <linux/blk-integrity.h>
|
||||
#include <trace/events/block.h>
|
||||
#include <uapi/linux/blkdev.h>
|
||||
#include "blk.h"
|
||||
#include "blk-crypto-internal.h"
|
||||
@@ -887,14 +888,13 @@ static void bio_cmd_bio_end_io(struct bio *bio)
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
static int blkdev_cmd_discard(struct io_uring_cmd *cmd,
|
||||
struct block_device *bdev,
|
||||
uint64_t start, uint64_t len, bool nowait)
|
||||
static int blkdev_cmd_discard(struct io_uring_cmd *cmd)
|
||||
{
|
||||
struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd);
|
||||
gfp_t gfp = nowait ? GFP_NOWAIT : GFP_KERNEL;
|
||||
sector_t sector = start >> SECTOR_SHIFT;
|
||||
sector_t nr_sects = len >> SECTOR_SHIFT;
|
||||
struct block_device *bdev = I_BDEV(cmd->file->f_mapping->host);
|
||||
gfp_t gfp = bic->nowait ? GFP_NOWAIT : GFP_KERNEL;
|
||||
sector_t sector = bic->start >> SECTOR_SHIFT;
|
||||
sector_t nr_sects = bic->len >> SECTOR_SHIFT;
|
||||
struct bio *prev = NULL, *bio;
|
||||
int err;
|
||||
|
||||
@@ -904,12 +904,12 @@ static int blkdev_cmd_discard(struct io_uring_cmd *cmd,
|
||||
return -EBADF;
|
||||
if (bdev_read_only(bdev))
|
||||
return -EPERM;
|
||||
err = blk_validate_byte_range(bdev, start, len);
|
||||
err = blk_validate_byte_range(bdev, bic->start, bic->len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = filemap_invalidate_pages(bdev->bd_mapping, start,
|
||||
start + len - 1, nowait);
|
||||
err = filemap_invalidate_pages(bdev->bd_mapping, bic->start,
|
||||
bic->start + bic->len - 1, bic->nowait);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -917,7 +917,7 @@ static int blkdev_cmd_discard(struct io_uring_cmd *cmd,
|
||||
bio = blk_alloc_discard_bio(bdev, §or, &nr_sects, gfp);
|
||||
if (!bio)
|
||||
break;
|
||||
if (nowait) {
|
||||
if (bic->nowait) {
|
||||
/*
|
||||
* Don't allow multi-bio non-blocking submissions as
|
||||
* subsequent bios may fail but we won't get a direct
|
||||
@@ -944,9 +944,42 @@ static int blkdev_cmd_discard(struct io_uring_cmd *cmd,
|
||||
return -EIOCBQUEUED;
|
||||
}
|
||||
|
||||
static int blkdev_cmd_zone_reset_all(struct io_uring_cmd *cmd)
|
||||
{
|
||||
struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd);
|
||||
struct block_device *bdev = I_BDEV(cmd->file->f_mapping->host);
|
||||
struct bio *bio;
|
||||
int err;
|
||||
|
||||
if (!(file_to_blk_mode(cmd->file) & BLK_OPEN_WRITE))
|
||||
return -EBADF;
|
||||
if (bdev_read_only(bdev))
|
||||
return -EPERM;
|
||||
if (!bdev_is_zoned(bdev))
|
||||
return -EOPNOTSUPP;
|
||||
if (bic->start || bic->len)
|
||||
return -EINVAL;
|
||||
|
||||
err = filemap_invalidate_pages(bdev->bd_mapping, 0,
|
||||
bdev_nr_bytes(bdev) - 1, bic->nowait);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
bio = bio_alloc(bdev, 0, REQ_OP_ZONE_RESET_ALL,
|
||||
bic->nowait ? GFP_NOWAIT : GFP_KERNEL);
|
||||
if (!bio)
|
||||
return -EAGAIN;
|
||||
if (bic->nowait)
|
||||
bio->bi_opf |= REQ_NOWAIT;
|
||||
trace_blkdev_zone_mgmt(bio, 0);
|
||||
bio->bi_private = cmd;
|
||||
bio->bi_end_io = bio_cmd_bio_end_io;
|
||||
submit_bio(bio);
|
||||
return -EIOCBQUEUED;
|
||||
}
|
||||
|
||||
int blkdev_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
|
||||
{
|
||||
struct block_device *bdev = I_BDEV(cmd->file->f_mapping->host);
|
||||
struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd);
|
||||
u32 cmd_op = cmd->cmd_op;
|
||||
|
||||
@@ -967,8 +1000,9 @@ int blkdev_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
|
||||
|
||||
switch (cmd_op) {
|
||||
case BLOCK_URING_CMD_DISCARD:
|
||||
return blkdev_cmd_discard(cmd, bdev, bic->start, bic->len,
|
||||
bic->nowait);
|
||||
return blkdev_cmd_discard(cmd);
|
||||
case BLOCK_URING_CMD_ZONE_RESET_ALL:
|
||||
return blkdev_cmd_zone_reset_all(cmd);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ static void kyber_timer_fn(struct timer_list *t)
|
||||
bool bad = false;
|
||||
|
||||
/* Sum all of the per-cpu latency histograms. */
|
||||
for_each_online_cpu(cpu) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct kyber_cpu_latency *cpu_latency;
|
||||
|
||||
cpu_latency = per_cpu_ptr(kqd->cpu_latency, cpu);
|
||||
|
||||
+11
-14
@@ -233,9 +233,8 @@ static void dd_merged_requests(struct request_queue *q, struct request *req,
|
||||
/*
|
||||
* move an entry to dispatch queue
|
||||
*/
|
||||
static void
|
||||
deadline_move_request(struct deadline_data *dd, struct dd_per_prio *per_prio,
|
||||
struct request *rq)
|
||||
static void deadline_move_request(struct dd_per_prio *per_prio,
|
||||
struct request *rq)
|
||||
{
|
||||
/*
|
||||
* take it off the sort and fifo list
|
||||
@@ -269,9 +268,8 @@ static inline bool deadline_check_fifo(struct dd_per_prio *per_prio,
|
||||
* For the specified data direction, return the next request to
|
||||
* dispatch using arrival ordered lists.
|
||||
*/
|
||||
static struct request *
|
||||
deadline_fifo_request(struct deadline_data *dd, struct dd_per_prio *per_prio,
|
||||
enum dd_data_dir data_dir)
|
||||
static struct request *deadline_fifo_request(struct dd_per_prio *per_prio,
|
||||
enum dd_data_dir data_dir)
|
||||
{
|
||||
if (list_empty(&per_prio->fifo_list[data_dir]))
|
||||
return NULL;
|
||||
@@ -283,9 +281,8 @@ deadline_fifo_request(struct deadline_data *dd, struct dd_per_prio *per_prio,
|
||||
* For the specified data direction, return the next request to
|
||||
* dispatch using sector position sorted lists.
|
||||
*/
|
||||
static struct request *
|
||||
deadline_next_request(struct deadline_data *dd, struct dd_per_prio *per_prio,
|
||||
enum dd_data_dir data_dir)
|
||||
static struct request *deadline_next_request(struct dd_per_prio *per_prio,
|
||||
enum dd_data_dir data_dir)
|
||||
{
|
||||
return deadline_from_pos(per_prio, data_dir,
|
||||
per_prio->latest_pos[data_dir]);
|
||||
@@ -334,7 +331,7 @@ static struct request *__dd_dispatch_request(struct deadline_data *dd,
|
||||
/*
|
||||
* batches are currently reads XOR writes
|
||||
*/
|
||||
rq = deadline_next_request(dd, per_prio, dd->last_dir);
|
||||
rq = deadline_next_request(per_prio, dd->last_dir);
|
||||
if (rq && dd->batching < dd->fifo_batch) {
|
||||
/* we have a next request and are still entitled to batch */
|
||||
data_dir = rq_data_dir(rq);
|
||||
@@ -349,7 +346,7 @@ static struct request *__dd_dispatch_request(struct deadline_data *dd,
|
||||
if (!list_empty(&per_prio->fifo_list[DD_READ])) {
|
||||
BUG_ON(RB_EMPTY_ROOT(&per_prio->sort_list[DD_READ]));
|
||||
|
||||
if (deadline_fifo_request(dd, per_prio, DD_WRITE) &&
|
||||
if (deadline_fifo_request(per_prio, DD_WRITE) &&
|
||||
(dd->starved++ >= dd->writes_starved))
|
||||
goto dispatch_writes;
|
||||
|
||||
@@ -379,14 +376,14 @@ dispatch_find_request:
|
||||
/*
|
||||
* we are not running a batch, find best request for selected data_dir
|
||||
*/
|
||||
next_rq = deadline_next_request(dd, per_prio, data_dir);
|
||||
next_rq = deadline_next_request(per_prio, data_dir);
|
||||
if (deadline_check_fifo(per_prio, data_dir) || !next_rq) {
|
||||
/*
|
||||
* A deadline has expired, the last request was in the other
|
||||
* direction, or we have run out of higher-sectored requests.
|
||||
* Start again from the request with the earliest expiry time.
|
||||
*/
|
||||
rq = deadline_fifo_request(dd, per_prio, data_dir);
|
||||
rq = deadline_fifo_request(per_prio, data_dir);
|
||||
} else {
|
||||
/*
|
||||
* The last req was the same dir and we have a next request in
|
||||
@@ -409,7 +406,7 @@ dispatch_request:
|
||||
* rq is the selected appropriate request.
|
||||
*/
|
||||
dd->batching++;
|
||||
deadline_move_request(dd, per_prio, rq);
|
||||
deadline_move_request(per_prio, rq);
|
||||
return dd_start_request(dd, data_dir, rq);
|
||||
}
|
||||
|
||||
|
||||
+39
-9
@@ -54,6 +54,7 @@ struct loop_device {
|
||||
|
||||
struct file *lo_backing_file;
|
||||
unsigned int lo_min_dio_size;
|
||||
unsigned int lo_dio_mem_align;
|
||||
struct block_device *lo_device;
|
||||
|
||||
gfp_t old_gfp_mask;
|
||||
@@ -378,7 +379,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
|
||||
iov_iter_bvec(&iter, rw,
|
||||
__bvec_iter_bvec(rq->bio->bi_io_vec, rq->bio->bi_iter),
|
||||
nr_bvec, blk_rq_bytes(rq));
|
||||
iter.iov_offset = rq->bio->bi_iter.bi_bvec_done;
|
||||
iter.iov_offset = rq->bio->bi_iter.bi_offset;
|
||||
}
|
||||
atomic_set(&cmd->ref, 2);
|
||||
|
||||
@@ -447,26 +448,37 @@ static void loop_reread_partitions(struct loop_device *lo)
|
||||
__func__, lo->lo_number, lo->lo_file_name, rc);
|
||||
}
|
||||
|
||||
static unsigned int loop_query_min_dio_size(struct loop_device *lo)
|
||||
static void loop_update_dio_alignment(struct loop_device *lo)
|
||||
{
|
||||
struct file *file = lo->lo_backing_file;
|
||||
struct block_device *sb_bdev = file->f_mapping->host->i_sb->s_bdev;
|
||||
struct kstat st;
|
||||
|
||||
/*
|
||||
* Use the minimal dio alignment of the file system if provided.
|
||||
* Use the dio alignment of the file system if provided. The incomoing
|
||||
* request's bio_vec is forwarded to the backing file unchanged, so its
|
||||
* required memory alignment becomes the device's dma_alignment when
|
||||
* used for direct-io.
|
||||
*/
|
||||
if (!vfs_getattr(&file->f_path, &st, STATX_DIOALIGN, 0) &&
|
||||
(st.result_mask & STATX_DIOALIGN))
|
||||
return st.dio_offset_align;
|
||||
(st.result_mask & STATX_DIOALIGN)) {
|
||||
lo->lo_min_dio_size = st.dio_offset_align;
|
||||
lo->lo_dio_mem_align = st.dio_mem_align - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* In a perfect world this wouldn't be needed, but as of Linux 6.13 only
|
||||
* a handful of file systems support the STATX_DIOALIGN flag.
|
||||
*/
|
||||
if (sb_bdev)
|
||||
return bdev_logical_block_size(sb_bdev);
|
||||
return SECTOR_SIZE;
|
||||
if (sb_bdev) {
|
||||
lo->lo_min_dio_size = bdev_logical_block_size(sb_bdev);
|
||||
lo->lo_dio_mem_align = bdev_dma_alignment(sb_bdev);
|
||||
return;
|
||||
}
|
||||
|
||||
lo->lo_min_dio_size = SECTOR_SIZE;
|
||||
lo->lo_dio_mem_align = SECTOR_SIZE - 1;
|
||||
}
|
||||
|
||||
static inline int is_loop_device(struct file *file)
|
||||
@@ -509,7 +521,7 @@ static void loop_assign_backing_file(struct loop_device *lo, struct file *file)
|
||||
lo->old_gfp_mask & ~(__GFP_IO | __GFP_FS));
|
||||
if (lo->lo_backing_file->f_flags & O_DIRECT)
|
||||
lo->lo_flags |= LO_FLAGS_DIRECT_IO;
|
||||
lo->lo_min_dio_size = loop_query_min_dio_size(lo);
|
||||
loop_update_dio_alignment(lo);
|
||||
}
|
||||
|
||||
static int loop_check_backing_file(struct file *file)
|
||||
@@ -940,6 +952,19 @@ static unsigned int loop_default_blocksize(struct loop_device *lo)
|
||||
return SECTOR_SIZE;
|
||||
}
|
||||
|
||||
static void loop_set_dma_limit(struct loop_device *lo, struct queue_limits *lim)
|
||||
{
|
||||
/*
|
||||
* Direct I/O forwards the user pages to the backing file unchanged, so
|
||||
* track the backing's DMA alignment requirement as the mode is toggled.
|
||||
*/
|
||||
if (lo->lo_flags & LO_FLAGS_DIRECT_IO)
|
||||
lim->dma_alignment = max_t(unsigned int, lo->lo_dio_mem_align,
|
||||
SECTOR_SIZE - 1);
|
||||
else
|
||||
lim->dma_alignment = SECTOR_SIZE - 1;
|
||||
}
|
||||
|
||||
static void loop_update_limits(struct loop_device *lo, struct queue_limits *lim,
|
||||
unsigned int bsize)
|
||||
{
|
||||
@@ -961,6 +986,7 @@ static void loop_update_limits(struct loop_device *lo, struct queue_limits *lim,
|
||||
lim->logical_block_size = bsize;
|
||||
lim->physical_block_size = bsize;
|
||||
lim->io_min = bsize;
|
||||
loop_set_dma_limit(lo, lim);
|
||||
lim->features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_ROTATIONAL);
|
||||
if (file->f_op->fsync && !(lo->lo_flags & LO_FLAGS_READ_ONLY))
|
||||
lim->features |= BLK_FEAT_WRITE_CACHE;
|
||||
@@ -1412,6 +1438,7 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg)
|
||||
{
|
||||
bool use_dio = !!arg;
|
||||
unsigned int memflags;
|
||||
struct queue_limits lim;
|
||||
|
||||
if (lo->lo_state != Lo_bound)
|
||||
return -ENXIO;
|
||||
@@ -1425,11 +1452,14 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg)
|
||||
vfs_fsync(lo->lo_backing_file, 0);
|
||||
}
|
||||
|
||||
lim = queue_limits_start_update(lo->lo_queue);
|
||||
memflags = blk_mq_freeze_queue(lo->lo_queue);
|
||||
if (use_dio)
|
||||
lo->lo_flags |= LO_FLAGS_DIRECT_IO;
|
||||
else
|
||||
lo->lo_flags &= ~LO_FLAGS_DIRECT_IO;
|
||||
loop_set_dma_limit(lo, &lim);
|
||||
queue_limits_commit_update(lo->lo_queue, &lim);
|
||||
blk_mq_unfreeze_queue(lo->lo_queue, memflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3048,6 +3048,8 @@ static int mtip_block_ioctl(struct block_device *dev,
|
||||
if (!dd)
|
||||
return -ENOTTY;
|
||||
|
||||
guard(mutex)(&dd->ioctl_mutex);
|
||||
|
||||
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)))
|
||||
return -ENOTTY;
|
||||
|
||||
@@ -3086,6 +3088,8 @@ static int mtip_block_compat_ioctl(struct block_device *dev,
|
||||
if (!dd)
|
||||
return -ENOTTY;
|
||||
|
||||
guard(mutex)(&dd->ioctl_mutex);
|
||||
|
||||
if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)))
|
||||
return -ENOTTY;
|
||||
|
||||
@@ -3721,6 +3725,7 @@ static int mtip_pci_probe(struct pci_dev *pdev,
|
||||
dd = kzalloc_node(sizeof(struct driver_data), GFP_KERNEL, my_node);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
mutex_init(&dd->ioctl_mutex);
|
||||
|
||||
/* Attach the private data to this PCI device. */
|
||||
pci_set_drvdata(pdev, dd);
|
||||
@@ -3887,6 +3892,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)
|
||||
}
|
||||
|
||||
set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag);
|
||||
mutex_lock(&dd->ioctl_mutex);
|
||||
|
||||
if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag))
|
||||
del_gendisk(dd->disk);
|
||||
@@ -3915,6 +3921,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)
|
||||
|
||||
/* De-initialize the protocol layer. */
|
||||
mtip_hw_exit(dd);
|
||||
mutex_unlock(&dd->ioctl_mutex);
|
||||
|
||||
if (dd->isr_workq) {
|
||||
destroy_workqueue(dd->isr_workq);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define __MTIP32XX_H__
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/ata.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -432,6 +433,7 @@ struct driver_data {
|
||||
struct request_queue *queue; /* Our request queue. */
|
||||
|
||||
struct blk_mq_tag_set tags; /* blk_mq tags */
|
||||
struct mutex ioctl_mutex;
|
||||
|
||||
struct mtip_port *port; /* Pointer to the port data structure. */
|
||||
|
||||
|
||||
+137
-120
@@ -166,6 +166,7 @@ static struct dentry *nbd_dbg_dir;
|
||||
|
||||
static unsigned int nbds_max = 16;
|
||||
static int max_part = 16;
|
||||
static int pre_defined_connections = 1;
|
||||
static int part_shift;
|
||||
|
||||
static int nbd_dev_dbg_init(struct nbd_device *nbd);
|
||||
@@ -331,6 +332,26 @@ static void nbd_mark_nsock_dead(struct nbd_device *nbd, struct nbd_sock *nsock,
|
||||
nsock->sent = 0;
|
||||
}
|
||||
|
||||
static void nbd_apply_limits(struct queue_limits *lim, u32 flags)
|
||||
{
|
||||
lim->features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA | BLK_FEAT_ROTATIONAL);
|
||||
lim->max_hw_discard_sectors = 0;
|
||||
lim->max_write_zeroes_sectors = 0;
|
||||
|
||||
if (flags & NBD_FLAG_SEND_TRIM)
|
||||
lim->max_hw_discard_sectors = UINT_MAX >> SECTOR_SHIFT;
|
||||
if (flags & NBD_FLAG_SEND_FLUSH) {
|
||||
lim->features |= BLK_FEAT_WRITE_CACHE;
|
||||
if (flags & NBD_FLAG_SEND_FUA)
|
||||
lim->features |= BLK_FEAT_FUA;
|
||||
}
|
||||
|
||||
if (flags & NBD_FLAG_ROTATIONAL)
|
||||
lim->features |= BLK_FEAT_ROTATIONAL;
|
||||
if (flags & NBD_FLAG_SEND_WRITE_ZEROES)
|
||||
lim->max_write_zeroes_sectors = UINT_MAX >> SECTOR_SHIFT;
|
||||
}
|
||||
|
||||
static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize, loff_t blksize)
|
||||
{
|
||||
struct queue_limits lim;
|
||||
@@ -352,26 +373,14 @@ static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize, loff_t blksize)
|
||||
return 0;
|
||||
|
||||
lim = queue_limits_start_update(nbd->disk->queue);
|
||||
if (nbd->config->flags & NBD_FLAG_SEND_TRIM)
|
||||
lim.max_hw_discard_sectors = UINT_MAX >> SECTOR_SHIFT;
|
||||
else
|
||||
lim.max_hw_discard_sectors = 0;
|
||||
if (!(nbd->config->flags & NBD_FLAG_SEND_FLUSH)) {
|
||||
lim.features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA);
|
||||
} else if (nbd->config->flags & NBD_FLAG_SEND_FUA) {
|
||||
lim.features |= BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA;
|
||||
} else {
|
||||
lim.features |= BLK_FEAT_WRITE_CACHE;
|
||||
lim.features &= ~BLK_FEAT_FUA;
|
||||
}
|
||||
if (nbd->config->flags & NBD_FLAG_ROTATIONAL)
|
||||
lim.features |= BLK_FEAT_ROTATIONAL;
|
||||
if (nbd->config->flags & NBD_FLAG_SEND_WRITE_ZEROES)
|
||||
lim.max_write_zeroes_sectors = UINT_MAX >> SECTOR_SHIFT;
|
||||
|
||||
nbd_apply_limits(&lim, nbd->config->flags);
|
||||
lim.logical_block_size = blksize;
|
||||
lim.physical_block_size = blksize;
|
||||
error = queue_limits_commit_update_frozen(nbd->disk->queue, &lim);
|
||||
/* No need freeze with 0 capacity and write cache disabled */
|
||||
if (!get_capacity(nbd->disk) && !blk_queue_write_cache(nbd->disk->queue))
|
||||
error = queue_limits_commit_update(nbd->disk->queue, &lim);
|
||||
else
|
||||
error = queue_limits_commit_update_frozen(nbd->disk->queue, &lim);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -1061,40 +1070,31 @@ static int find_fallback(struct nbd_device *nbd, int index)
|
||||
int new_index = -1;
|
||||
struct nbd_sock *nsock = config->socks[index];
|
||||
int fallback = nsock->fallback_index;
|
||||
int i;
|
||||
|
||||
if (test_bit(NBD_RT_DISCONNECTED, &config->runtime_flags))
|
||||
return new_index;
|
||||
|
||||
if (config->num_connections <= 1) {
|
||||
dev_err_ratelimited(disk_to_dev(nbd->disk),
|
||||
"Dead connection, failed to find a fallback\n");
|
||||
return new_index;
|
||||
}
|
||||
if (config->num_connections <= 1)
|
||||
goto no_fallback;
|
||||
|
||||
if (fallback >= 0 && fallback < config->num_connections &&
|
||||
!config->socks[fallback]->dead)
|
||||
return fallback;
|
||||
|
||||
if (nsock->fallback_index < 0 ||
|
||||
nsock->fallback_index >= config->num_connections ||
|
||||
config->socks[nsock->fallback_index]->dead) {
|
||||
int i;
|
||||
for (i = 0; i < config->num_connections; i++) {
|
||||
if (i == index)
|
||||
continue;
|
||||
if (!config->socks[i]->dead) {
|
||||
new_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
nsock->fallback_index = new_index;
|
||||
if (new_index < 0) {
|
||||
dev_err_ratelimited(disk_to_dev(nbd->disk),
|
||||
"Dead connection, failed to find a fallback\n");
|
||||
return new_index;
|
||||
for (i = 0; i < config->num_connections; i++) {
|
||||
if (i != index && !config->socks[i]->dead) {
|
||||
new_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_index = nsock->fallback_index;
|
||||
nsock->fallback_index = new_index;
|
||||
if (new_index >= 0)
|
||||
return new_index;
|
||||
|
||||
no_fallback:
|
||||
dev_err_ratelimited(disk_to_dev(nbd->disk),
|
||||
"Dead connection, failed to find a fallback\n");
|
||||
return new_index;
|
||||
}
|
||||
|
||||
@@ -1281,23 +1281,23 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
|
||||
struct socket *sock;
|
||||
struct nbd_sock **socks;
|
||||
struct nbd_sock *nsock;
|
||||
unsigned int memflags;
|
||||
int err;
|
||||
|
||||
/* Arg will be cast to int, check it to avoid overflow */
|
||||
if (arg > INT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (nbd->pid) {
|
||||
dev_err(disk_to_dev(nbd->disk),
|
||||
"Cannot add socket to a running device\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
sock = nbd_get_socket(nbd, arg, &err);
|
||||
if (!sock)
|
||||
return err;
|
||||
nbd_reclassify_socket(sock);
|
||||
|
||||
/*
|
||||
* We need to make sure we don't get any errant requests while we're
|
||||
* reallocating the ->socks array.
|
||||
*/
|
||||
memflags = blk_mq_freeze_queue(nbd->disk->queue);
|
||||
|
||||
if (!netlink && !nbd->task_setup &&
|
||||
!test_bit(NBD_RT_BOUND, &config->runtime_flags))
|
||||
nbd->task_setup = current;
|
||||
@@ -1337,17 +1337,15 @@ static int nbd_add_socket(struct nbd_device *nbd, unsigned long arg,
|
||||
INIT_WORK(&nsock->work, nbd_pending_cmd_work);
|
||||
socks[config->num_connections++] = nsock;
|
||||
atomic_inc(&config->live_connections);
|
||||
blk_mq_unfreeze_queue(nbd->disk->queue, memflags);
|
||||
|
||||
return 0;
|
||||
|
||||
put_socket:
|
||||
blk_mq_unfreeze_queue(nbd->disk->queue, memflags);
|
||||
sockfd_put(sock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg)
|
||||
static int nbd_genl_reconnect_sock_cb(struct nbd_device *nbd, unsigned long arg)
|
||||
{
|
||||
struct nbd_config *config = nbd->config;
|
||||
struct socket *sock, *old;
|
||||
@@ -1402,11 +1400,12 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg)
|
||||
|
||||
atomic_inc(&config->live_connections);
|
||||
wake_up(&config->conn_wait);
|
||||
dev_info(nbd_to_dev(nbd), "reconnected socket\n");
|
||||
return 0;
|
||||
}
|
||||
sockfd_put(sock);
|
||||
kfree(args);
|
||||
return -ENOSPC;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void nbd_bdev_reset(struct nbd_device *nbd)
|
||||
@@ -1471,8 +1470,13 @@ static void nbd_config_put(struct nbd_device *nbd)
|
||||
if (refcount_dec_and_mutex_lock(&nbd->config_refs,
|
||||
&nbd->config_lock)) {
|
||||
struct nbd_config *config = nbd->config;
|
||||
struct queue_limits lim;
|
||||
nbd_dev_dbg_close(nbd);
|
||||
invalidate_disk(nbd->disk);
|
||||
/* reset queue limits to default */
|
||||
lim = queue_limits_start_update(nbd->disk->queue);
|
||||
nbd_apply_limits(&lim, 0);
|
||||
queue_limits_commit_update(nbd->disk->queue, &lim);
|
||||
if (nbd->config->bytesize)
|
||||
kobject_uevent(&nbd_to_dev(nbd)->kobj, KOBJ_CHANGE);
|
||||
if (test_and_clear_bit(NBD_RT_HAS_PID_FILE,
|
||||
@@ -1941,7 +1945,8 @@ static const struct blk_mq_ops nbd_mq_ops = {
|
||||
.timeout = nbd_xmit_timeout,
|
||||
};
|
||||
|
||||
static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
|
||||
static struct nbd_device *nbd_dev_add(int index, unsigned int refs,
|
||||
int nr_hw_queues)
|
||||
{
|
||||
struct queue_limits lim = {
|
||||
.max_hw_sectors = 65536,
|
||||
@@ -1958,7 +1963,7 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
|
||||
goto out;
|
||||
|
||||
nbd->tag_set.ops = &nbd_mq_ops;
|
||||
nbd->tag_set.nr_hw_queues = 1;
|
||||
nbd->tag_set.nr_hw_queues = nr_hw_queues;
|
||||
nbd->tag_set.queue_depth = 128;
|
||||
nbd->tag_set.numa_node = NUMA_NO_NODE;
|
||||
nbd->tag_set.cmd_size = sizeof(struct nbd_cmd);
|
||||
@@ -2107,6 +2112,58 @@ static int nbd_genl_size_set(struct genl_info *info, struct nbd_device *nbd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the NBD_ATTR_SOCKETS nested list can call @cb for each socket fd.
|
||||
*
|
||||
* Return the number of fds walked, or a negative errno.
|
||||
*/
|
||||
static int nbd_genl_foreach_sock(struct genl_info *info,
|
||||
int (*cb)(struct nbd_device *nbd, unsigned long fd),
|
||||
struct nbd_device *nbd)
|
||||
{
|
||||
struct nlattr *attr;
|
||||
int rem, count = 0;
|
||||
|
||||
if (!info->attrs[NBD_ATTR_SOCKETS])
|
||||
return 0;
|
||||
|
||||
nla_for_each_nested(attr, info->attrs[NBD_ATTR_SOCKETS], rem) {
|
||||
struct nlattr *socks[NBD_SOCK_MAX + 1];
|
||||
int ret;
|
||||
|
||||
if (nla_type(attr) != NBD_SOCK_ITEM) {
|
||||
pr_err("socks must be embedded in a SOCK_ITEM attr\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nla_parse_nested_deprecated(socks, NBD_SOCK_MAX,
|
||||
attr,
|
||||
nbd_sock_policy,
|
||||
info->extack)) {
|
||||
pr_err("error processing sock list\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!socks[NBD_SOCK_FD])
|
||||
continue;
|
||||
|
||||
count++;
|
||||
if (cb) {
|
||||
ret = cb(nbd, (int)nla_get_u32(socks[NBD_SOCK_FD]));
|
||||
if (ret > 0)
|
||||
return count;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int nbd_genl_connect_sock_cb(struct nbd_device *nbd, unsigned long fd)
|
||||
{
|
||||
return nbd_add_socket(nbd, fd, true);
|
||||
}
|
||||
|
||||
static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nbd_device *nbd;
|
||||
@@ -2159,7 +2216,11 @@ again:
|
||||
mutex_unlock(&nbd_index_mutex);
|
||||
|
||||
if (!nbd) {
|
||||
nbd = nbd_dev_add(index, 2);
|
||||
ret = nbd_genl_foreach_sock(info, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
nbd = nbd_dev_add(index, 2, ret > 0 ? ret : 1);
|
||||
if (IS_ERR(nbd)) {
|
||||
pr_err("failed to add new device\n");
|
||||
return PTR_ERR(nbd);
|
||||
@@ -2226,36 +2287,9 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
if (info->attrs[NBD_ATTR_SOCKETS]) {
|
||||
struct nlattr *attr;
|
||||
int rem, fd;
|
||||
|
||||
nla_for_each_nested(attr, info->attrs[NBD_ATTR_SOCKETS],
|
||||
rem) {
|
||||
struct nlattr *socks[NBD_SOCK_MAX+1];
|
||||
|
||||
if (nla_type(attr) != NBD_SOCK_ITEM) {
|
||||
pr_err("socks must be embedded in a SOCK_ITEM attr\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = nla_parse_nested_deprecated(socks, NBD_SOCK_MAX,
|
||||
attr,
|
||||
nbd_sock_policy,
|
||||
info->extack);
|
||||
if (ret != 0) {
|
||||
pr_err("error processing sock list\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!socks[NBD_SOCK_FD])
|
||||
continue;
|
||||
fd = (int)nla_get_u32(socks[NBD_SOCK_FD]);
|
||||
ret = nbd_add_socket(nbd, fd, true);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = nbd_genl_foreach_sock(info, nbd_genl_connect_sock_cb, nbd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (info->attrs[NBD_ATTR_BACKEND_IDENTIFIER]) {
|
||||
nbd->backend = nla_strdup(info->attrs[NBD_ATTR_BACKEND_IDENTIFIER],
|
||||
@@ -2440,40 +2474,10 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
}
|
||||
|
||||
if (info->attrs[NBD_ATTR_SOCKETS]) {
|
||||
struct nlattr *attr;
|
||||
int rem, fd;
|
||||
|
||||
nla_for_each_nested(attr, info->attrs[NBD_ATTR_SOCKETS],
|
||||
rem) {
|
||||
struct nlattr *socks[NBD_SOCK_MAX+1];
|
||||
|
||||
if (nla_type(attr) != NBD_SOCK_ITEM) {
|
||||
pr_err("socks must be embedded in a SOCK_ITEM attr\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = nla_parse_nested_deprecated(socks, NBD_SOCK_MAX,
|
||||
attr,
|
||||
nbd_sock_policy,
|
||||
info->extack);
|
||||
if (ret != 0) {
|
||||
pr_err("error processing sock list\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!socks[NBD_SOCK_FD])
|
||||
continue;
|
||||
fd = (int)nla_get_u32(socks[NBD_SOCK_FD]);
|
||||
ret = nbd_reconnect_socket(nbd, fd);
|
||||
if (ret) {
|
||||
if (ret == -ENOSPC)
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
dev_info(nbd_to_dev(nbd), "reconnected socket\n");
|
||||
}
|
||||
}
|
||||
ret = nbd_genl_foreach_sock(info, nbd_genl_reconnect_sock_cb, nbd);
|
||||
/* foreach_sock returns a positive count on success; doit must return 0 */
|
||||
if (ret >= 0)
|
||||
ret = 0;
|
||||
out:
|
||||
mutex_unlock(&nbd->config_lock);
|
||||
nbd_config_put(nbd);
|
||||
@@ -2709,6 +2713,12 @@ static int __init nbd_init(void)
|
||||
if (nbds_max > 1UL << (MINORBITS - part_shift))
|
||||
return -EINVAL;
|
||||
|
||||
/* An excessively large value will be adjusted in blk_mq_alloc_tag_set */
|
||||
if (pre_defined_connections < 1) {
|
||||
pr_err("pre_defined_connections must be >= 1\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (register_blkdev(NBD_MAJOR, "nbd"))
|
||||
return -EIO;
|
||||
|
||||
@@ -2725,8 +2735,12 @@ static int __init nbd_init(void)
|
||||
}
|
||||
nbd_dbg_init();
|
||||
|
||||
/*
|
||||
* Set to the intended connection count so nbd_start_device() can skip
|
||||
* the queue-freezing blk_mq_update_nr_hw_queues() call.
|
||||
*/
|
||||
for (i = 0; i < nbds_max; i++)
|
||||
nbd_dev_add(i, 1);
|
||||
nbd_dev_add(i, 1, pre_defined_connections);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2787,3 +2801,6 @@ module_param(nbds_max, int, 0444);
|
||||
MODULE_PARM_DESC(nbds_max, "number of network block devices to initialize (default: 16)");
|
||||
module_param(max_part, int, 0444);
|
||||
MODULE_PARM_DESC(max_part, "number of partitions per device (default: 16)");
|
||||
module_param(pre_defined_connections, int, 0444);
|
||||
MODULE_PARM_DESC(pre_defined_connections,
|
||||
"number of connections for devices pre-created at module load (default: 1)");
|
||||
|
||||
@@ -66,7 +66,7 @@ struct nullb_page {
|
||||
#define NULLB_PAGE_FREE (MAP_SZ - 2)
|
||||
|
||||
static LIST_HEAD(nullb_list);
|
||||
static struct mutex lock;
|
||||
static DEFINE_MUTEX(lock);
|
||||
static int null_major;
|
||||
static DEFINE_IDA(nullb_indexes);
|
||||
static struct blk_mq_tag_set tag_set;
|
||||
@@ -340,11 +340,20 @@ static ssize_t nullb_device_bool_attr_store(bool *val, const char *page,
|
||||
return count;
|
||||
}
|
||||
|
||||
/* The following macro should only be used with TYPE = {uint, ulong, bool}. */
|
||||
/*
|
||||
* The following macro should only be used with TYPE = {uint, ulong, bool}.
|
||||
*
|
||||
* The device configuration is modified under the global lock to serialize
|
||||
* attribute changes against null_add_dev() and null_del_dev(): without this,
|
||||
* an attribute could be changed while null_add_dev() is running, that is,
|
||||
* before NULLB_DEV_FL_CONFIGURED is set, which would let null_add_dev()
|
||||
* observe inconsistent values for the device configuration.
|
||||
*/
|
||||
#define NULLB_DEVICE_ATTR(NAME, TYPE, APPLY) \
|
||||
static ssize_t \
|
||||
nullb_device_##NAME##_show(struct config_item *item, char *page) \
|
||||
{ \
|
||||
guard(mutex)(&lock); \
|
||||
return nullb_device_##TYPE##_attr_show( \
|
||||
to_nullb_device(item)->NAME, page); \
|
||||
} \
|
||||
@@ -360,6 +369,7 @@ nullb_device_##NAME##_store(struct config_item *item, const char *page, \
|
||||
ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\
|
||||
if (ret < 0) \
|
||||
return ret; \
|
||||
guard(mutex)(&lock); \
|
||||
if (apply_fn) \
|
||||
ret = apply_fn(dev, new_value); \
|
||||
else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
|
||||
@@ -379,9 +389,20 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,
|
||||
struct blk_mq_tag_set *set;
|
||||
int ret, nr_hw_queues;
|
||||
|
||||
lockdep_assert_held(&lock);
|
||||
|
||||
if (!dev->nullb)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* A shared tag_set is mapped via the module-wide queue counts, so a
|
||||
* per-device resize is meaningless. On shrink it would also leave
|
||||
* mq_map[] pointing at NULLed hctx slots, causing a NULL deref in
|
||||
* blk_mq_map_swqueue(). Reject it.
|
||||
*/
|
||||
if (dev->shared_tags)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Make sure at least one submit queue exists.
|
||||
*/
|
||||
@@ -421,25 +442,13 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,
|
||||
static int nullb_apply_submit_queues(struct nullb_device *dev,
|
||||
unsigned int submit_queues)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&lock);
|
||||
ret = nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
|
||||
mutex_unlock(&lock);
|
||||
|
||||
return ret;
|
||||
return nullb_update_nr_hw_queues(dev, submit_queues, dev->poll_queues);
|
||||
}
|
||||
|
||||
static int nullb_apply_poll_queues(struct nullb_device *dev,
|
||||
unsigned int poll_queues)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&lock);
|
||||
ret = nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
|
||||
mutex_unlock(&lock);
|
||||
|
||||
return ret;
|
||||
return nullb_update_nr_hw_queues(dev, dev->submit_queues, poll_queues);
|
||||
}
|
||||
|
||||
NULLB_DEVICE_ATTR(size, ulong, NULL);
|
||||
@@ -478,6 +487,7 @@ NULLB_DEVICE_ATTR(badblocks_partial_io, bool, NULL);
|
||||
|
||||
static ssize_t nullb_device_power_show(struct config_item *item, char *page)
|
||||
{
|
||||
guard(mutex)(&lock);
|
||||
return nullb_device_bool_attr_show(to_nullb_device(item)->power, page);
|
||||
}
|
||||
|
||||
@@ -493,15 +503,15 @@ static ssize_t nullb_device_power_store(struct config_item *item,
|
||||
return ret;
|
||||
|
||||
ret = count;
|
||||
mutex_lock(&lock);
|
||||
guard(mutex)(&lock);
|
||||
if (!dev->power && newp) {
|
||||
if (test_and_set_bit(NULLB_DEV_FL_UP, &dev->flags))
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = null_add_dev(dev);
|
||||
if (ret) {
|
||||
clear_bit(NULLB_DEV_FL_UP, &dev->flags);
|
||||
goto out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags);
|
||||
@@ -515,8 +525,6 @@ static ssize_t nullb_device_power_store(struct config_item *item,
|
||||
clear_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -580,6 +588,7 @@ static ssize_t nullb_device_zone_readonly_store(struct config_item *item,
|
||||
{
|
||||
struct nullb_device *dev = to_nullb_device(item);
|
||||
|
||||
guard(mutex)(&lock);
|
||||
return zone_cond_store(dev, page, count, BLK_ZONE_COND_READONLY);
|
||||
}
|
||||
CONFIGFS_ATTR_WO(nullb_device_, zone_readonly);
|
||||
@@ -589,6 +598,7 @@ static ssize_t nullb_device_zone_offline_store(struct config_item *item,
|
||||
{
|
||||
struct nullb_device *dev = to_nullb_device(item);
|
||||
|
||||
guard(mutex)(&lock);
|
||||
return zone_cond_store(dev, page, count, BLK_ZONE_COND_OFFLINE);
|
||||
}
|
||||
CONFIGFS_ATTR_WO(nullb_device_, zone_offline);
|
||||
@@ -707,10 +717,9 @@ nullb_group_drop_item(struct config_group *group, struct config_item *item)
|
||||
struct nullb_device *dev = to_nullb_device(item);
|
||||
|
||||
if (test_and_clear_bit(NULLB_DEV_FL_UP, &dev->flags)) {
|
||||
mutex_lock(&lock);
|
||||
guard(mutex)(&lock);
|
||||
dev->power = false;
|
||||
null_del_dev(dev->nullb);
|
||||
mutex_unlock(&lock);
|
||||
}
|
||||
nullb_del_fault_config(dev);
|
||||
config_item_put(item);
|
||||
@@ -836,7 +845,6 @@ static void null_free_dev(struct nullb_device *dev)
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
null_free_zoned_dev(dev);
|
||||
badblocks_exit(&dev->badblocks);
|
||||
kfree(dev);
|
||||
}
|
||||
@@ -1770,18 +1778,19 @@ static void null_del_dev(struct nullb *nullb)
|
||||
|
||||
del_gendisk(nullb->disk);
|
||||
|
||||
if (test_bit(NULLB_DEV_FL_THROTTLED, &nullb->dev->flags)) {
|
||||
if (test_bit(NULLB_DEV_FL_THROTTLED, &dev->flags)) {
|
||||
hrtimer_cancel(&nullb->bw_timer);
|
||||
atomic_long_set(&nullb->cur_bytes, LONG_MAX);
|
||||
blk_mq_start_stopped_hw_queues(nullb->q, true);
|
||||
}
|
||||
|
||||
put_disk(nullb->disk);
|
||||
null_free_zoned_dev(dev);
|
||||
if (nullb->tag_set == &nullb->__tag_set)
|
||||
blk_mq_free_tag_set(nullb->tag_set);
|
||||
kfree(nullb->queues);
|
||||
if (null_cache_active(nullb))
|
||||
null_free_device_storage(nullb->dev, true);
|
||||
null_free_device_storage(dev, true);
|
||||
kfree(nullb);
|
||||
dev->nullb = NULL;
|
||||
}
|
||||
@@ -2081,14 +2090,13 @@ static struct nullb *null_find_dev_by_name(const char *name)
|
||||
{
|
||||
struct nullb *nullb = NULL, *nb;
|
||||
|
||||
mutex_lock(&lock);
|
||||
guard(mutex)(&lock);
|
||||
list_for_each_entry(nb, &nullb_list, list) {
|
||||
if (strcmp(nb->disk_name, name) == 0) {
|
||||
nullb = nb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&lock);
|
||||
|
||||
return nullb;
|
||||
}
|
||||
@@ -2102,9 +2110,9 @@ static int null_create_dev(void)
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&lock);
|
||||
ret = null_add_dev(dev);
|
||||
mutex_unlock(&lock);
|
||||
scoped_guard(mutex, &lock) {
|
||||
ret = null_add_dev(dev);
|
||||
}
|
||||
if (ret) {
|
||||
null_free_dev(dev);
|
||||
return ret;
|
||||
@@ -2162,17 +2170,9 @@ static int __init null_init(void)
|
||||
config_group_init(&nullb_subsys.su_group);
|
||||
mutex_init(&nullb_subsys.su_mutex);
|
||||
|
||||
ret = configfs_register_subsystem(&nullb_subsys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&lock);
|
||||
|
||||
null_major = register_blkdev(0, "nullb");
|
||||
if (null_major < 0) {
|
||||
ret = null_major;
|
||||
goto err_conf;
|
||||
}
|
||||
if (null_major < 0)
|
||||
return null_major;
|
||||
|
||||
for (i = 0; i < nr_devices; i++) {
|
||||
ret = null_create_dev();
|
||||
@@ -2180,6 +2180,10 @@ static int __init null_init(void)
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
ret = configfs_register_subsystem(&nullb_subsys);
|
||||
if (ret)
|
||||
goto err_dev;
|
||||
|
||||
pr_info("module loaded\n");
|
||||
return 0;
|
||||
|
||||
@@ -2189,8 +2193,8 @@ err_dev:
|
||||
null_destroy_dev(nullb);
|
||||
}
|
||||
unregister_blkdev(null_major, "nullb");
|
||||
err_conf:
|
||||
configfs_unregister_subsystem(&nullb_subsys);
|
||||
if (tag_set.ops)
|
||||
blk_mq_free_tag_set(&tag_set);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2200,19 +2204,17 @@ static void __exit null_exit(void)
|
||||
|
||||
configfs_unregister_subsystem(&nullb_subsys);
|
||||
|
||||
unregister_blkdev(null_major, "nullb");
|
||||
|
||||
mutex_lock(&lock);
|
||||
while (!list_empty(&nullb_list)) {
|
||||
nullb = list_entry(nullb_list.next, struct nullb, list);
|
||||
null_destroy_dev(nullb);
|
||||
scoped_guard(mutex, &lock) {
|
||||
while (!list_empty(&nullb_list)) {
|
||||
nullb = list_entry(nullb_list.next, struct nullb, list);
|
||||
null_destroy_dev(nullb);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&lock);
|
||||
|
||||
unregister_blkdev(null_major, "nullb");
|
||||
|
||||
if (tag_set.ops)
|
||||
blk_mq_free_tag_set(&tag_set);
|
||||
|
||||
mutex_destroy(&lock);
|
||||
}
|
||||
|
||||
module_init(null_init);
|
||||
|
||||
@@ -18,6 +18,8 @@ static inline sector_t mb_to_sects(unsigned long mb)
|
||||
|
||||
static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
|
||||
{
|
||||
if (WARN_ON_ONCE(!dev->zone_size_sects))
|
||||
return 0;
|
||||
return sect >> ilog2(dev->zone_size_sects);
|
||||
}
|
||||
|
||||
@@ -56,8 +58,8 @@ int null_init_zoned_dev(struct nullb_device *dev,
|
||||
sector_t sector = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (!is_power_of_2(dev->zone_size)) {
|
||||
pr_err("zone_size must be power-of-two\n");
|
||||
if (!dev->zone_size || !is_power_of_2(dev->zone_size)) {
|
||||
pr_err("zone_size must be non-zero power-of-two\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (dev->zone_size > dev->size) {
|
||||
@@ -88,6 +90,10 @@ int null_init_zoned_dev(struct nullb_device *dev,
|
||||
zone_capacity_sects = mb_to_sects(dev->zone_capacity);
|
||||
dev_capacity_sects = mb_to_sects(dev->size);
|
||||
dev->zone_size_sects = mb_to_sects(dev->zone_size);
|
||||
if (!dev->zone_size_sects) {
|
||||
pr_err("zone_size too large or too small, leads to zero sectors\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev->nr_zones = round_up(dev_capacity_sects, dev->zone_size_sects)
|
||||
>> ilog2(dev->zone_size_sects);
|
||||
|
||||
|
||||
@@ -1290,4 +1290,5 @@ module_init(swim3_init)
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Paul Mackerras");
|
||||
MODULE_DESCRIPTION("SWIM3 floppy driver for PowerMacs");
|
||||
MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR);
|
||||
|
||||
+196
-182
@@ -89,7 +89,8 @@
|
||||
| UBLK_F_SAFE_STOP_DEV \
|
||||
| UBLK_F_BATCH_IO \
|
||||
| UBLK_F_NO_AUTO_PART_SCAN \
|
||||
| UBLK_F_SHMEM_ZC)
|
||||
| UBLK_F_SHMEM_ZC \
|
||||
| UBLK_F_IO_DESC_SIZE)
|
||||
|
||||
#define UBLK_F_ALL_RECOVERY_FLAGS (UBLK_F_USER_RECOVERY \
|
||||
| UBLK_F_USER_RECOVERY_REISSUE \
|
||||
@@ -107,6 +108,8 @@
|
||||
UBLK_BATCH_F_HAS_BUF_ADDR | \
|
||||
UBLK_BATCH_F_AUTO_BUF_REG_FALLBACK)
|
||||
|
||||
#define UBLK_MAX_IO_DESC_SIZE 256
|
||||
|
||||
/* ublk batch fetch uring_cmd */
|
||||
struct ublk_batch_fetch_cmd {
|
||||
struct list_head node;
|
||||
@@ -237,8 +240,9 @@ struct ublk_io {
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
struct ublk_queue {
|
||||
int q_id;
|
||||
int q_depth;
|
||||
u16 q_id;
|
||||
u16 q_depth;
|
||||
u16 io_desc_size;
|
||||
|
||||
unsigned long flags;
|
||||
struct ublksrv_io_desc *io_cmd_buf;
|
||||
@@ -248,7 +252,7 @@ struct ublk_queue {
|
||||
bool fail_io; /* copy of dev->state == UBLK_S_DEV_FAIL_IO */
|
||||
spinlock_t cancel_lock;
|
||||
struct ublk_device *dev;
|
||||
u32 nr_io_ready;
|
||||
u16 nr_io_ready;
|
||||
|
||||
/*
|
||||
* For supporting UBLK_F_BATCH_IO only.
|
||||
@@ -327,7 +331,7 @@ struct ublk_device {
|
||||
|
||||
struct ublk_params params;
|
||||
|
||||
u32 nr_queue_ready;
|
||||
u16 nr_queue_ready;
|
||||
bool unprivileged_daemons;
|
||||
struct mutex cancel_mutex;
|
||||
bool canceling;
|
||||
@@ -403,9 +407,9 @@ static inline void ublk_io_evts_deinit(struct ublk_queue *q)
|
||||
}
|
||||
|
||||
static inline struct ublksrv_io_desc *
|
||||
ublk_get_iod(const struct ublk_queue *ubq, unsigned tag)
|
||||
ublk_get_iod(const struct ublk_queue *ubq, u16 tag)
|
||||
{
|
||||
return &ubq->io_cmd_buf[tag];
|
||||
return (void *)ubq->io_cmd_buf + tag * (size_t)ubq->io_desc_size;
|
||||
}
|
||||
|
||||
static inline bool ublk_support_zero_copy(const struct ublk_queue *ubq)
|
||||
@@ -423,8 +427,7 @@ static inline bool ublk_support_shmem_zc(const struct ublk_queue *ubq)
|
||||
return ubq->flags & UBLK_F_SHMEM_ZC;
|
||||
}
|
||||
|
||||
static inline bool ublk_iod_is_shmem_zc(const struct ublk_queue *ubq,
|
||||
unsigned int tag)
|
||||
static inline bool ublk_iod_is_shmem_zc(const struct ublk_queue *ubq, u16 tag)
|
||||
{
|
||||
return ublk_get_iod(ubq, tag)->op_flags & UBLK_IO_F_SHMEM_ZC;
|
||||
}
|
||||
@@ -529,7 +532,6 @@ static void ublk_init_iod(struct ublk_queue *ubq, struct request *req,
|
||||
|
||||
struct ublk_zoned_report_desc {
|
||||
__u64 sector;
|
||||
__u32 operation;
|
||||
__u32 nr_zones;
|
||||
};
|
||||
|
||||
@@ -659,7 +661,6 @@ static int ublk_report_zones(struct gendisk *disk, sector_t sector,
|
||||
goto out;
|
||||
}
|
||||
|
||||
desc.operation = UBLK_IO_OP_REPORT_ZONES;
|
||||
desc.sector = sector;
|
||||
desc.nr_zones = zones_in_request;
|
||||
ret = ublk_zoned_insert_report_desc(req, &desc);
|
||||
@@ -703,8 +704,24 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
|
||||
struct request *req)
|
||||
static bool ublk_validate_req_zoned(const struct request *req)
|
||||
{
|
||||
switch (req_op(req)) {
|
||||
case REQ_OP_ZONE_OPEN:
|
||||
case REQ_OP_ZONE_CLOSE:
|
||||
case REQ_OP_ZONE_FINISH:
|
||||
case REQ_OP_ZONE_RESET:
|
||||
case REQ_OP_ZONE_APPEND:
|
||||
case REQ_OP_ZONE_RESET_ALL:
|
||||
return true;
|
||||
case REQ_OP_DRV_IN:
|
||||
return !!ublk_zoned_get_report_desc(req);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ublk_setup_iod_zoned(struct ublk_queue *ubq, struct request *req)
|
||||
{
|
||||
struct ublk_zoned_report_desc *desc;
|
||||
u32 ublk_op;
|
||||
@@ -730,26 +747,15 @@ static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
|
||||
break;
|
||||
case REQ_OP_DRV_IN:
|
||||
desc = ublk_zoned_get_report_desc(req);
|
||||
if (!desc)
|
||||
return BLK_STS_IOERR;
|
||||
ublk_op = desc->operation;
|
||||
switch (ublk_op) {
|
||||
case UBLK_IO_OP_REPORT_ZONES:
|
||||
ublk_init_iod(ubq, req, ublk_op, desc->nr_zones,
|
||||
desc->sector);
|
||||
return BLK_STS_OK;
|
||||
default:
|
||||
return BLK_STS_IOERR;
|
||||
}
|
||||
case REQ_OP_DRV_OUT:
|
||||
/* We do not support drv_out */
|
||||
return BLK_STS_NOTSUPP;
|
||||
ublk_init_iod(ubq, req, UBLK_IO_OP_REPORT_ZONES, desc->nr_zones,
|
||||
desc->sector);
|
||||
return;
|
||||
default:
|
||||
return BLK_STS_IOERR;
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
}
|
||||
|
||||
ublk_init_iod(ubq, req, ublk_op, blk_rq_sectors(req), blk_rq_pos(req));
|
||||
return BLK_STS_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -770,10 +776,14 @@ static int ublk_revalidate_disk_zones(struct ublk_device *ub)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
|
||||
struct request *req)
|
||||
static bool ublk_validate_req_zoned(const struct request *req)
|
||||
{
|
||||
return BLK_STS_NOTSUPP;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ublk_setup_iod_zoned(struct ublk_queue *ubq, struct request *req)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -864,7 +874,7 @@ static unsigned int unprivileged_ublks_added; /* protected by ublk_ctl_mutex */
|
||||
|
||||
static struct miscdevice ublk_misc;
|
||||
|
||||
static inline unsigned ublk_pos_to_hwq(loff_t pos)
|
||||
static inline u16 ublk_pos_to_hwq(loff_t pos)
|
||||
{
|
||||
return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_QID_OFF) &
|
||||
UBLK_QID_BITS_MASK;
|
||||
@@ -875,7 +885,7 @@ static inline unsigned ublk_pos_to_buf_off(loff_t pos)
|
||||
return (pos - UBLKSRV_IO_BUF_OFFSET) & UBLK_IO_BUF_BITS_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned ublk_pos_to_tag(loff_t pos)
|
||||
static inline u16 ublk_pos_to_tag(loff_t pos)
|
||||
{
|
||||
return ((pos - UBLKSRV_IO_BUF_OFFSET) >> UBLK_TAG_OFF) &
|
||||
UBLK_TAG_BITS_MASK;
|
||||
@@ -970,7 +980,7 @@ static int ublk_validate_params(const struct ublk_device *ub)
|
||||
if (p->max_sectors < PAGE_SECTORS)
|
||||
return -EINVAL;
|
||||
|
||||
if (ublk_dev_is_zoned(ub) && !p->chunk_sectors)
|
||||
if (ublk_dev_is_zoned(ub) && !is_power_of_2(p->chunk_sectors))
|
||||
return -EINVAL;
|
||||
} else
|
||||
return -EINVAL;
|
||||
@@ -1231,30 +1241,31 @@ static noinline void ublk_put_device(struct ublk_device *ub)
|
||||
}
|
||||
|
||||
static inline struct ublk_queue *ublk_get_queue(struct ublk_device *dev,
|
||||
int qid)
|
||||
u16 qid)
|
||||
{
|
||||
return dev->queues[qid];
|
||||
}
|
||||
|
||||
static inline struct ublksrv_io_desc *
|
||||
ublk_queue_cmd_buf(struct ublk_device *ub, int q_id)
|
||||
ublk_queue_cmd_buf(struct ublk_device *ub, u16 q_id)
|
||||
{
|
||||
return ublk_get_queue(ub, q_id)->io_cmd_buf;
|
||||
}
|
||||
|
||||
static inline int __ublk_queue_cmd_buf_size(int depth)
|
||||
static inline size_t __ublk_queue_cmd_buf_size(const struct ublk_device *ub,
|
||||
u16 depth)
|
||||
{
|
||||
return round_up(depth * sizeof(struct ublksrv_io_desc), PAGE_SIZE);
|
||||
return round_up(depth * (size_t)ub->dev_info.io_desc_size, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static inline int ublk_queue_cmd_buf_size(struct ublk_device *ub)
|
||||
static inline size_t ublk_queue_cmd_buf_size(const struct ublk_device *ub)
|
||||
{
|
||||
return __ublk_queue_cmd_buf_size(ub->dev_info.queue_depth);
|
||||
return __ublk_queue_cmd_buf_size(ub, ub->dev_info.queue_depth);
|
||||
}
|
||||
|
||||
static int ublk_max_cmd_buf_size(void)
|
||||
static size_t ublk_max_cmd_buf_size(const struct ublk_device *ub)
|
||||
{
|
||||
return __ublk_queue_cmd_buf_size(UBLK_MAX_QUEUE_DEPTH);
|
||||
return __ublk_queue_cmd_buf_size(ub, UBLK_MAX_QUEUE_DEPTH);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1457,52 +1468,47 @@ static inline bool ublk_need_unmap_req(const struct request *req)
|
||||
(req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN);
|
||||
}
|
||||
|
||||
static unsigned int ublk_map_io(const struct ublk_queue *ubq,
|
||||
const struct request *req,
|
||||
static unsigned int ublk_map_io(const struct request *req,
|
||||
const struct ublk_io *io)
|
||||
{
|
||||
const unsigned int rq_bytes = blk_rq_bytes(req);
|
||||
struct iov_iter iter;
|
||||
const int dir = ITER_DEST;
|
||||
|
||||
if (!ublk_need_map_io(ubq))
|
||||
return rq_bytes;
|
||||
if (import_ubuf(dir, u64_to_user_ptr(io->buf.addr), blk_rq_bytes(req),
|
||||
&iter) < 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* no zero copy, we delay copy WRITE request data into ublksrv
|
||||
* context and the big benefit is that pinning pages in current
|
||||
* context is pretty fast, see ublk_pin_user_pages
|
||||
*/
|
||||
if (ublk_need_map_req(req)) {
|
||||
struct iov_iter iter;
|
||||
const int dir = ITER_DEST;
|
||||
|
||||
import_ubuf(dir, u64_to_user_ptr(io->buf.addr), rq_bytes, &iter);
|
||||
return ublk_copy_user_pages(req, 0, &iter, dir);
|
||||
}
|
||||
return rq_bytes;
|
||||
return ublk_copy_user_pages(req, 0, &iter, dir);
|
||||
}
|
||||
|
||||
static unsigned int ublk_unmap_io(bool need_map,
|
||||
const struct request *req,
|
||||
static unsigned int ublk_unmap_io(const struct request *req,
|
||||
const struct ublk_io *io)
|
||||
{
|
||||
const unsigned int rq_bytes = blk_rq_bytes(req);
|
||||
struct iov_iter iter;
|
||||
const int dir = ITER_SOURCE;
|
||||
|
||||
if (!need_map)
|
||||
return rq_bytes;
|
||||
if (import_ubuf(dir, u64_to_user_ptr(io->buf.addr), io->res, &iter) < 0)
|
||||
return 0;
|
||||
|
||||
if (ublk_need_unmap_req(req)) {
|
||||
struct iov_iter iter;
|
||||
const int dir = ITER_SOURCE;
|
||||
|
||||
WARN_ON_ONCE(io->res > rq_bytes);
|
||||
|
||||
import_ubuf(dir, u64_to_user_ptr(io->buf.addr), io->res, &iter);
|
||||
return ublk_copy_user_pages(req, 0, &iter, dir);
|
||||
}
|
||||
return rq_bytes;
|
||||
return ublk_copy_user_pages(req, 0, &iter, dir);
|
||||
}
|
||||
|
||||
static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
|
||||
static bool ublk_validate_req(const struct ublk_queue *ubq,
|
||||
const struct request *req)
|
||||
{
|
||||
switch (req_op(req)) {
|
||||
case REQ_OP_READ:
|
||||
case REQ_OP_WRITE:
|
||||
case REQ_OP_FLUSH:
|
||||
case REQ_OP_DISCARD:
|
||||
case REQ_OP_WRITE_ZEROES:
|
||||
return true;
|
||||
default:
|
||||
return ublk_queue_is_zoned(ubq) && ublk_validate_req_zoned(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
|
||||
{
|
||||
u32 ublk_op;
|
||||
|
||||
@@ -1523,13 +1529,11 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
|
||||
ublk_op = UBLK_IO_OP_WRITE_ZEROES;
|
||||
break;
|
||||
default:
|
||||
if (ublk_queue_is_zoned(ubq))
|
||||
return ublk_setup_iod_zoned(ubq, req);
|
||||
return BLK_STS_IOERR;
|
||||
ublk_setup_iod_zoned(ubq, req);
|
||||
return;
|
||||
}
|
||||
|
||||
ublk_init_iod(ubq, req, ublk_op, blk_rq_sectors(req), blk_rq_pos(req));
|
||||
return BLK_STS_OK;
|
||||
}
|
||||
|
||||
static inline struct ublk_uring_cmd_pdu *ublk_get_uring_cmd_pdu(
|
||||
@@ -1562,30 +1566,27 @@ static inline void __ublk_complete_rq(struct request *req, struct ublk_io *io,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* FLUSH, DISCARD or WRITE_ZEROES usually won't return bytes returned, so end them
|
||||
* directly.
|
||||
*
|
||||
* Both the two needn't unmap.
|
||||
*/
|
||||
if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE &&
|
||||
req_op(req) != REQ_OP_DRV_IN)
|
||||
goto exit;
|
||||
|
||||
/* shmem zero copy: no data to unmap, pages already shared */
|
||||
if (ublk_iod_is_shmem_zc(req->mq_hctx->driver_data, req->tag))
|
||||
if (!need_map || !ublk_need_unmap_req(req) ||
|
||||
ublk_iod_is_shmem_zc(req->mq_hctx->driver_data, req->tag))
|
||||
goto exit;
|
||||
|
||||
/* for READ request, writing data in iod->addr to rq buffers */
|
||||
unmapped_bytes = ublk_unmap_io(need_map, req, io);
|
||||
unmapped_bytes = ublk_unmap_io(req, io);
|
||||
|
||||
/*
|
||||
* Extremely impossible since we got data filled in just before
|
||||
*
|
||||
* Re-read simply for this unlikely case.
|
||||
*/
|
||||
if (unlikely(unmapped_bytes < io->res))
|
||||
if (unlikely(unmapped_bytes < io->res)) {
|
||||
if (unlikely(!unmapped_bytes)) {
|
||||
res = BLK_STS_IOERR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
io->res = unmapped_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run bio->bi_end_io() with softirqs disabled. If the final fput
|
||||
@@ -1657,7 +1658,7 @@ static inline void __ublk_abort_rq(struct ublk_queue *ubq,
|
||||
}
|
||||
|
||||
static void
|
||||
ublk_auto_buf_reg_fallback(const struct ublk_queue *ubq, unsigned tag)
|
||||
ublk_auto_buf_reg_fallback(const struct ublk_queue *ubq, u16 tag)
|
||||
{
|
||||
struct ublksrv_io_desc *iod = ublk_get_iod(ubq, tag);
|
||||
|
||||
@@ -1737,10 +1738,11 @@ static bool ublk_start_io(const struct ublk_queue *ubq, struct request *req,
|
||||
unsigned mapped_bytes;
|
||||
|
||||
/* shmem zero copy: skip data copy, pages already shared */
|
||||
if (ublk_iod_is_shmem_zc(ubq, req->tag))
|
||||
if (!ublk_need_map_io(ubq) || !ublk_need_map_req(req) ||
|
||||
ublk_iod_is_shmem_zc(ubq, req->tag))
|
||||
return true;
|
||||
|
||||
mapped_bytes = ublk_map_io(ubq, req, io);
|
||||
mapped_bytes = ublk_map_io(req, io);
|
||||
|
||||
/* partially mapped, update io descriptor */
|
||||
if (unlikely(mapped_bytes != blk_rq_bytes(req))) {
|
||||
@@ -1768,9 +1770,10 @@ static bool ublk_start_io(const struct ublk_queue *ubq, struct request *req,
|
||||
static void ublk_dispatch_req(struct ublk_queue *ubq, struct request *req)
|
||||
{
|
||||
unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
|
||||
int tag = req->tag;
|
||||
u16 tag = req->tag;
|
||||
struct ublk_io *io = &ubq->ios[tag];
|
||||
|
||||
ublk_setup_iod(ubq, req);
|
||||
pr_devel("%s: complete: qid %d tag %d io_flags %x addr %llx\n",
|
||||
__func__, ubq->q_id, req->tag, io->flags,
|
||||
ublk_get_iod(ubq, req->tag)->addr);
|
||||
@@ -1824,6 +1827,7 @@ static bool __ublk_batch_prep_dispatch(struct ublk_queue *ubq,
|
||||
enum auto_buf_reg_res res = AUTO_BUF_REG_FALLBACK;
|
||||
struct io_uring_cmd *cmd = data->cmd;
|
||||
|
||||
ublk_setup_iod(ubq, req);
|
||||
if (!ublk_start_io(ubq, req, io))
|
||||
return false;
|
||||
|
||||
@@ -2137,8 +2141,6 @@ static enum blk_eh_timer_return ublk_timeout(struct request *rq)
|
||||
static blk_status_t ublk_prep_req(struct ublk_queue *ubq, struct request *rq,
|
||||
bool check_cancel)
|
||||
{
|
||||
blk_status_t res;
|
||||
|
||||
if (unlikely(READ_ONCE(ubq->fail_io)))
|
||||
return BLK_STS_TARGET;
|
||||
|
||||
@@ -2159,8 +2161,7 @@ static blk_status_t ublk_prep_req(struct ublk_queue *ubq, struct request *rq,
|
||||
return BLK_STS_IOERR;
|
||||
|
||||
/* fill iod to slot in io cmd buffer */
|
||||
res = ublk_setup_iod(ubq, rq);
|
||||
if (unlikely(res != BLK_STS_OK))
|
||||
if (unlikely(!ublk_validate_req(ubq, rq)))
|
||||
return BLK_STS_IOERR;
|
||||
|
||||
blk_mq_start_request(rq);
|
||||
@@ -2358,7 +2359,7 @@ static const struct blk_mq_ops ublk_batch_mq_ops = {
|
||||
|
||||
static void ublk_queue_reinit(struct ublk_device *ub, struct ublk_queue *ubq)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
ubq->nr_io_ready = 0;
|
||||
|
||||
@@ -2403,7 +2404,7 @@ static int ublk_ch_open(struct inode *inode, struct file *filp)
|
||||
|
||||
static void ublk_reset_ch_dev(struct ublk_device *ub)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
|
||||
struct ublk_queue *ubq = ublk_get_queue(ub, i);
|
||||
@@ -2475,7 +2476,7 @@ out:
|
||||
static void ublk_set_canceling(struct ublk_device *ub, bool canceling)
|
||||
__must_hold(&ub->cancel_mutex)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
ub->canceling = canceling;
|
||||
for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
|
||||
@@ -2484,7 +2485,7 @@ static void ublk_set_canceling(struct ublk_device *ub, bool canceling)
|
||||
|
||||
static bool ublk_check_and_reset_active_ref(struct ublk_device *ub)
|
||||
{
|
||||
int i, j;
|
||||
u16 i, j;
|
||||
|
||||
if (!ublk_dev_need_req_ref(ub))
|
||||
return false;
|
||||
@@ -2517,7 +2518,7 @@ static void ublk_ch_release_work_fn(struct work_struct *work)
|
||||
struct ublk_device *ub =
|
||||
container_of(work, struct ublk_device, exit_work.work);
|
||||
struct gendisk *disk;
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
/*
|
||||
* For zero-copy and auto buffer register modes, I/O references
|
||||
@@ -2634,9 +2635,10 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
struct ublk_device *ub = filp->private_data;
|
||||
size_t sz = vma->vm_end - vma->vm_start;
|
||||
unsigned max_sz = ublk_max_cmd_buf_size();
|
||||
size_t max_sz = ublk_max_cmd_buf_size(ub);
|
||||
unsigned long pfn, end, phys_off = vma->vm_pgoff << PAGE_SHIFT;
|
||||
int q_id, ret = 0;
|
||||
int ret = 0;
|
||||
u16 q_id;
|
||||
|
||||
spin_lock(&ub->lock);
|
||||
if (!ub->mm)
|
||||
@@ -2709,7 +2711,7 @@ static void ublk_abort_batch_queue(struct ublk_device *ub,
|
||||
*/
|
||||
static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < ubq->q_depth; i++) {
|
||||
struct ublk_io *io = &ubq->ios[i];
|
||||
@@ -2752,7 +2754,7 @@ out:
|
||||
ublk_put_disk(disk);
|
||||
}
|
||||
|
||||
static void ublk_cancel_cmd(struct ublk_queue *ubq, unsigned tag,
|
||||
static void ublk_cancel_cmd(struct ublk_queue *ubq, u16 tag,
|
||||
unsigned int issue_flags)
|
||||
{
|
||||
struct ublk_io *io = &ubq->ios[tag];
|
||||
@@ -2903,7 +2905,7 @@ static inline bool ublk_dev_ready(const struct ublk_device *ub)
|
||||
|
||||
static void ublk_cancel_queue(struct ublk_queue *ubq)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
if (ublk_support_batch_io(ubq)) {
|
||||
ublk_batch_cancel_queue(ubq);
|
||||
@@ -2917,7 +2919,7 @@ static void ublk_cancel_queue(struct ublk_queue *ubq)
|
||||
/* Cancel all pending commands, must be called after del_gendisk() returns */
|
||||
static void ublk_cancel_dev(struct ublk_device *ub)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
|
||||
ublk_cancel_queue(ublk_get_queue(ub, i));
|
||||
@@ -2951,7 +2953,7 @@ static void ublk_wait_tagset_rqs_idle(struct ublk_device *ub)
|
||||
|
||||
static void ublk_force_abort_dev(struct ublk_device *ub)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
pr_devel("%s: force abort ub: dev_id %d state %s\n",
|
||||
__func__, ub->dev_info.dev_id,
|
||||
@@ -3075,18 +3077,19 @@ static inline int ublk_check_cmd_op(u32 cmd_op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ublk_set_auto_buf_reg(struct ublk_io *io, struct io_uring_cmd *cmd)
|
||||
/* Must run before ublk_fill_io_cmd() / __ublk_fetch(). */
|
||||
static inline int ublk_validate_io_buf(const struct ublk_device *ub,
|
||||
struct io_uring_cmd *cmd,
|
||||
struct ublk_auto_buf_reg *buf)
|
||||
{
|
||||
struct ublk_auto_buf_reg buf;
|
||||
if (!ublk_dev_support_auto_buf_reg(ub))
|
||||
return 0;
|
||||
|
||||
buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr));
|
||||
|
||||
if (buf.reserved0 || buf.reserved1)
|
||||
*buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr));
|
||||
if (buf->reserved0 || buf->reserved1)
|
||||
return -EINVAL;
|
||||
|
||||
if (buf.flags & ~UBLK_AUTO_BUF_REG_F_MASK)
|
||||
if (buf->flags & ~UBLK_AUTO_BUF_REG_F_MASK)
|
||||
return -EINVAL;
|
||||
io->buf.auto_reg = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3107,17 +3110,25 @@ static void ublk_clear_auto_buf_reg(struct ublk_io *io,
|
||||
* responsibility for unregistering the buffer, otherwise
|
||||
* this ublk request gets stuck.
|
||||
*/
|
||||
if (io->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd))
|
||||
if (buf_idx &&
|
||||
io->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd))
|
||||
*buf_idx = io->buf.auto_reg.index;
|
||||
}
|
||||
}
|
||||
|
||||
static int ublk_handle_auto_buf_reg(struct ublk_io *io,
|
||||
struct io_uring_cmd *cmd,
|
||||
u16 *buf_idx)
|
||||
static inline void ublk_apply_io_buf(const struct ublk_device *ub,
|
||||
struct ublk_io *io,
|
||||
struct io_uring_cmd *cmd,
|
||||
unsigned long buf_addr,
|
||||
const struct ublk_auto_buf_reg *auto_buf,
|
||||
u16 *buf_idx)
|
||||
{
|
||||
ublk_clear_auto_buf_reg(io, cmd, buf_idx);
|
||||
return ublk_set_auto_buf_reg(io, cmd);
|
||||
if (ublk_dev_support_auto_buf_reg(ub)) {
|
||||
ublk_clear_auto_buf_reg(io, cmd, buf_idx);
|
||||
io->buf.auto_reg = *auto_buf;
|
||||
} else {
|
||||
io->buf.addr = buf_addr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Once we return, `io->req` can't be used any more */
|
||||
@@ -3134,21 +3145,9 @@ ublk_fill_io_cmd(struct ublk_io *io, struct io_uring_cmd *cmd)
|
||||
return req;
|
||||
}
|
||||
|
||||
static inline int
|
||||
ublk_config_io_buf(const struct ublk_device *ub, struct ublk_io *io,
|
||||
struct io_uring_cmd *cmd, unsigned long buf_addr,
|
||||
u16 *buf_idx)
|
||||
{
|
||||
if (ublk_dev_support_auto_buf_reg(ub))
|
||||
return ublk_handle_auto_buf_reg(io, cmd, buf_idx);
|
||||
|
||||
io->buf.addr = buf_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ublk_prep_cancel(struct io_uring_cmd *cmd,
|
||||
unsigned int issue_flags,
|
||||
struct ublk_queue *ubq, unsigned int tag)
|
||||
struct ublk_queue *ubq, u16 tag)
|
||||
{
|
||||
struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
|
||||
|
||||
@@ -3286,6 +3285,7 @@ static int __ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
|
||||
static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
|
||||
struct ublk_io *io, __u64 buf_addr, u16 q_id)
|
||||
{
|
||||
struct ublk_auto_buf_reg auto_buf;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@@ -3294,11 +3294,13 @@ static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
|
||||
* FETCH, so it is fine even for IO_URING_F_NONBLOCK.
|
||||
*/
|
||||
mutex_lock(&ub->mutex);
|
||||
ret = __ublk_fetch(cmd, ub, io, q_id);
|
||||
if (!ret)
|
||||
ret = ublk_config_io_buf(ub, io, cmd, buf_addr, NULL);
|
||||
ret = ublk_validate_io_buf(ub, cmd, &auto_buf);
|
||||
if (!ret)
|
||||
ret = __ublk_fetch(cmd, ub, io, q_id);
|
||||
if (!ret) {
|
||||
ublk_apply_io_buf(ub, io, cmd, buf_addr, &auto_buf, NULL);
|
||||
ublk_mark_io_ready(ub, q_id, io);
|
||||
}
|
||||
mutex_unlock(&ub->mutex);
|
||||
return ret;
|
||||
}
|
||||
@@ -3441,13 +3443,18 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
|
||||
case UBLK_IO_REGISTER_IO_BUF:
|
||||
return ublk_daemon_register_io_buf(cmd, ub, q_id, tag, io, addr,
|
||||
issue_flags);
|
||||
case UBLK_IO_COMMIT_AND_FETCH_REQ:
|
||||
case UBLK_IO_COMMIT_AND_FETCH_REQ: {
|
||||
struct ublk_auto_buf_reg auto_buf;
|
||||
|
||||
ret = ublk_check_commit_and_fetch(ub, io, addr);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = ublk_validate_io_buf(ub, cmd, &auto_buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
io->res = result;
|
||||
req = ublk_fill_io_cmd(io, cmd);
|
||||
ret = ublk_config_io_buf(ub, io, cmd, addr, &buf_idx);
|
||||
ublk_apply_io_buf(ub, io, cmd, addr, &auto_buf, &buf_idx);
|
||||
if (buf_idx != UBLK_INVALID_BUF_IDX)
|
||||
io_buffer_unregister_bvec(cmd, buf_idx, issue_flags);
|
||||
compl = ublk_need_complete_req(ub, io);
|
||||
@@ -3456,10 +3463,8 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
|
||||
req->__sector = addr;
|
||||
if (compl)
|
||||
__ublk_complete_rq(req, io, ublk_dev_need_map_io(ub), NULL);
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
case UBLK_IO_NEED_GET_DATA:
|
||||
/*
|
||||
* ublk_get_data() may fail and fallback to requeue, so keep
|
||||
@@ -3467,8 +3472,7 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
|
||||
* request
|
||||
*/
|
||||
req = ublk_fill_io_cmd(io, cmd);
|
||||
ret = ublk_config_io_buf(ub, io, cmd, addr, NULL);
|
||||
WARN_ON_ONCE(ret);
|
||||
io->buf.addr = addr;
|
||||
if (likely(ublk_get_data(ubq, io, req))) {
|
||||
__ublk_prep_compl_io_cmd(io, req);
|
||||
return UBLK_IO_RES_OK;
|
||||
@@ -3780,11 +3784,11 @@ static int ublk_batch_commit_io(struct ublk_queue *ubq,
|
||||
ret = ublk_batch_commit_io_check(ubq, io, &buf);
|
||||
if (!ret) {
|
||||
io->res = elem->result;
|
||||
io->buf = buf;
|
||||
req = ublk_fill_io_cmd(io, data->cmd);
|
||||
|
||||
if (auto_reg)
|
||||
ublk_clear_auto_buf_reg(io, data->cmd, &buf_idx);
|
||||
io->buf = buf;
|
||||
compl = ublk_need_complete_req(data->ub, io);
|
||||
}
|
||||
ublk_io_unlock(io);
|
||||
@@ -3952,8 +3956,8 @@ static int ublk_handle_non_batch_cmd(struct io_uring_cmd *cmd,
|
||||
const struct ublksrv_io_cmd *ub_cmd = io_uring_sqe_cmd(cmd->sqe,
|
||||
struct ublksrv_io_cmd);
|
||||
struct ublk_device *ub = cmd->file->private_data;
|
||||
unsigned tag = READ_ONCE(ub_cmd->tag);
|
||||
unsigned q_id = READ_ONCE(ub_cmd->q_id);
|
||||
u16 tag = READ_ONCE(ub_cmd->tag);
|
||||
u16 q_id = READ_ONCE(ub_cmd->q_id);
|
||||
unsigned index = READ_ONCE(ub_cmd->addr);
|
||||
struct ublk_queue *ubq;
|
||||
struct ublk_io *io;
|
||||
@@ -4159,7 +4163,8 @@ static const struct file_operations ublk_ch_batch_io_fops = {
|
||||
|
||||
static void __ublk_deinit_queue(struct ublk_device *ub, struct ublk_queue *ubq)
|
||||
{
|
||||
int size, i;
|
||||
size_t size;
|
||||
u16 i;
|
||||
|
||||
size = ublk_queue_cmd_buf_size(ub);
|
||||
|
||||
@@ -4180,7 +4185,7 @@ static void __ublk_deinit_queue(struct ublk_device *ub, struct ublk_queue *ubq)
|
||||
kvfree(ubq);
|
||||
}
|
||||
|
||||
static void ublk_deinit_queue(struct ublk_device *ub, int q_id)
|
||||
static void ublk_deinit_queue(struct ublk_device *ub, u16 q_id)
|
||||
{
|
||||
struct ublk_queue *ubq = ub->queues[q_id];
|
||||
|
||||
@@ -4191,7 +4196,7 @@ static void ublk_deinit_queue(struct ublk_device *ub, int q_id)
|
||||
ub->queues[q_id] = NULL;
|
||||
}
|
||||
|
||||
static int ublk_get_queue_numa_node(struct ublk_device *ub, int q_id)
|
||||
static int ublk_get_queue_numa_node(struct ublk_device *ub, u16 q_id)
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
@@ -4204,14 +4209,16 @@ static int ublk_get_queue_numa_node(struct ublk_device *ub, int q_id)
|
||||
return NUMA_NO_NODE;
|
||||
}
|
||||
|
||||
static int ublk_init_queue(struct ublk_device *ub, int q_id)
|
||||
static int ublk_init_queue(struct ublk_device *ub, u16 q_id)
|
||||
{
|
||||
int depth = ub->dev_info.queue_depth;
|
||||
u16 depth = ub->dev_info.queue_depth;
|
||||
gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
|
||||
struct ublk_queue *ubq;
|
||||
struct page *page;
|
||||
int numa_node;
|
||||
int size, i, ret;
|
||||
size_t size;
|
||||
int ret;
|
||||
u16 i;
|
||||
|
||||
/* Determine NUMA node based on queue's CPU affinity */
|
||||
numa_node = ublk_get_queue_numa_node(ub, q_id);
|
||||
@@ -4235,6 +4242,7 @@ static int ublk_init_queue(struct ublk_device *ub, int q_id)
|
||||
return -ENOMEM;
|
||||
}
|
||||
ubq->io_cmd_buf = page_address(page);
|
||||
ubq->io_desc_size = ub->dev_info.io_desc_size;
|
||||
|
||||
for (i = 0; i < ubq->q_depth; i++)
|
||||
spin_lock_init(&ubq->ios[i].lock);
|
||||
@@ -4256,7 +4264,7 @@ fail:
|
||||
|
||||
static void ublk_deinit_queues(struct ublk_device *ub)
|
||||
{
|
||||
int i;
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
|
||||
ublk_deinit_queue(ub, i);
|
||||
@@ -4264,7 +4272,8 @@ static void ublk_deinit_queues(struct ublk_device *ub)
|
||||
|
||||
static int ublk_init_queues(struct ublk_device *ub)
|
||||
{
|
||||
int i, ret;
|
||||
int ret;
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
|
||||
ret = ublk_init_queue(ub, i);
|
||||
@@ -4718,6 +4727,15 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
|
||||
if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
|
||||
return -EINVAL;
|
||||
|
||||
if (info.flags & UBLK_F_IO_DESC_SIZE) {
|
||||
if (info.io_desc_size < sizeof(struct ublksrv_io_desc) ||
|
||||
info.io_desc_size % _Alignof(struct ublksrv_io_desc) ||
|
||||
info.io_desc_size > UBLK_MAX_IO_DESC_SIZE)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
info.io_desc_size = sizeof(struct ublksrv_io_desc);
|
||||
}
|
||||
|
||||
/* the created device is always owned by current user */
|
||||
ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
|
||||
|
||||
@@ -5171,7 +5189,7 @@ out:
|
||||
|
||||
struct count_busy {
|
||||
const struct ublk_queue *ubq;
|
||||
unsigned int nr_busy;
|
||||
u16 nr_busy;
|
||||
};
|
||||
|
||||
static bool ublk_count_busy_req(struct request *rq, void *data)
|
||||
@@ -5209,8 +5227,7 @@ static int ublk_wait_for_idle_io(struct ublk_device *ub,
|
||||
return 0;
|
||||
|
||||
while (elapsed < timeout_ms && !signal_pending(current)) {
|
||||
unsigned int queues_cancelable = 0;
|
||||
int i;
|
||||
u16 i, queues_cancelable = 0;
|
||||
|
||||
for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
|
||||
struct ublk_queue *ubq = ublk_get_queue(ub, i);
|
||||
@@ -5502,39 +5519,36 @@ static void ublk_unpin_range_pages(unsigned long base_pfn,
|
||||
|
||||
/*
|
||||
* Inner loop: erase up to UBLK_REMOVE_BATCH matching ranges under
|
||||
* mas_lock, collecting them into an xarray. Then drop the lock and
|
||||
* unpin pages + free ranges outside spinlock context.
|
||||
* mas_lock, collecting the page ranges in a fixed-size array. Then
|
||||
* drop the lock and unpin pages + free ranges outside spinlock context.
|
||||
*
|
||||
* Returns true if the tree walk completed, false if more ranges remain.
|
||||
* Xarray key is the base PFN, value encodes nr_pages via xa_mk_value().
|
||||
*/
|
||||
#define UBLK_REMOVE_BATCH 64
|
||||
|
||||
struct ublk_unpin_range {
|
||||
unsigned long base_pfn;
|
||||
unsigned long nr_pages;
|
||||
};
|
||||
|
||||
static bool __ublk_shmem_remove_ranges(struct ublk_device *ub,
|
||||
int buf_index, int *ret)
|
||||
{
|
||||
MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
|
||||
struct ublk_buf_range *range;
|
||||
struct xarray to_unpin;
|
||||
unsigned long idx;
|
||||
struct ublk_unpin_range to_unpin[UBLK_REMOVE_BATCH];
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
bool done = false;
|
||||
void *entry;
|
||||
|
||||
xa_init(&to_unpin);
|
||||
|
||||
mas_lock(&mas);
|
||||
mas_for_each(&mas, range, ULONG_MAX) {
|
||||
unsigned long nr;
|
||||
|
||||
if (buf_index >= 0 && range->buf_index != buf_index)
|
||||
continue;
|
||||
|
||||
*ret = 0;
|
||||
nr = mas.last - mas.index + 1;
|
||||
if (xa_err(xa_store(&to_unpin, mas.index,
|
||||
xa_mk_value(nr), GFP_ATOMIC)))
|
||||
goto unlock;
|
||||
to_unpin[count].base_pfn = mas.index;
|
||||
to_unpin[count].nr_pages = mas.last - mas.index + 1;
|
||||
mas_erase(&mas);
|
||||
kfree(range);
|
||||
if (++count >= UBLK_REMOVE_BATCH)
|
||||
@@ -5544,9 +5558,9 @@ static bool __ublk_shmem_remove_ranges(struct ublk_device *ub,
|
||||
unlock:
|
||||
mas_unlock(&mas);
|
||||
|
||||
xa_for_each(&to_unpin, idx, entry)
|
||||
ublk_unpin_range_pages(idx, xa_to_value(entry));
|
||||
xa_destroy(&to_unpin);
|
||||
for (i = 0; i < count; i++)
|
||||
ublk_unpin_range_pages(to_unpin[i].base_pfn,
|
||||
to_unpin[i].nr_pages);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
+29
-13
@@ -144,6 +144,7 @@ struct zloop_device {
|
||||
unsigned int nr_conv_zones;
|
||||
unsigned int max_open_zones;
|
||||
unsigned int block_size;
|
||||
unsigned int dio_mem_align;
|
||||
|
||||
spinlock_t open_zones_lock;
|
||||
struct list_head open_zones_lru_list;
|
||||
@@ -478,7 +479,8 @@ static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
|
||||
zone->cond == BLK_ZONE_COND_FULL)
|
||||
goto unlock;
|
||||
|
||||
if (vfs_truncate(&zone->file->f_path, zlo->zone_size << SECTOR_SHIFT)) {
|
||||
if (vfs_truncate(&zone->file->f_path,
|
||||
zlo->zone_capacity << SECTOR_SHIFT)) {
|
||||
set_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
@@ -553,7 +555,7 @@ static int zloop_do_rw(struct zloop_cmd *cmd)
|
||||
iov_iter_bvec(&iter, rw,
|
||||
__bvec_iter_bvec(rq->bio->bi_io_vec, rq->bio->bi_iter),
|
||||
nr_bvec, blk_rq_bytes(rq));
|
||||
iter.iov_offset = rq->bio->bi_iter.bi_bvec_done;
|
||||
iter.iov_offset = rq->bio->bi_iter.bi_offset;
|
||||
}
|
||||
|
||||
cmd->iocb.ki_pos = (cmd->sector - zone->start) << SECTOR_SHIFT;
|
||||
@@ -1037,20 +1039,30 @@ static int zloop_get_block_size(struct zloop_device *zlo,
|
||||
struct kstat st;
|
||||
|
||||
/*
|
||||
* If the FS block size is lower than or equal to 4K, use that as the
|
||||
* device block size. Otherwise, fallback to the FS direct IO alignment
|
||||
* constraint if that is provided, and to the FS underlying device
|
||||
* physical block size if the direct IO alignment is unknown.
|
||||
* Use the dio alignment of the file system if provided. The incoming
|
||||
* request's bio_vec is forwarded to the backing file unchanged, so its
|
||||
* required memory alignment becomes the device's dma_alignment when
|
||||
* used for direct-io.
|
||||
*/
|
||||
if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) &&
|
||||
(st.result_mask & STATX_DIOALIGN)) {
|
||||
zlo->block_size = st.dio_offset_align;
|
||||
zlo->dio_mem_align = st.dio_mem_align - 1;
|
||||
} else if (sb_bdev) {
|
||||
zlo->block_size = bdev_physical_block_size(sb_bdev);
|
||||
zlo->dio_mem_align = bdev_dma_alignment(sb_bdev);
|
||||
} else {
|
||||
zlo->block_size = SECTOR_SIZE;
|
||||
zlo->dio_mem_align = SECTOR_SIZE - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prefer the FS block size for the device block size when it is no
|
||||
* larger than 4K; otherwise keep the direct I/O / physical block size
|
||||
* selected above.
|
||||
*/
|
||||
if (file_inode(zone->file)->i_sb->s_blocksize <= SZ_4K)
|
||||
zlo->block_size = file_inode(zone->file)->i_sb->s_blocksize;
|
||||
else if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) &&
|
||||
(st.result_mask & STATX_DIOALIGN))
|
||||
zlo->block_size = st.dio_offset_align;
|
||||
else if (sb_bdev)
|
||||
zlo->block_size = bdev_physical_block_size(sb_bdev);
|
||||
else
|
||||
zlo->block_size = SECTOR_SIZE;
|
||||
|
||||
if (zlo->zone_capacity & ((zlo->block_size >> SECTOR_SHIFT) - 1)) {
|
||||
pr_err("Zone capacity is not aligned to block size %u\n",
|
||||
@@ -1279,6 +1291,10 @@ static int zloop_ctl_add(struct zloop_options *opts)
|
||||
|
||||
lim.physical_block_size = zlo->block_size;
|
||||
lim.logical_block_size = zlo->block_size;
|
||||
/* Direct I/O forwards the request pages to the backing files as-is. */
|
||||
if (!opts->buffered_io)
|
||||
lim.dma_alignment = max_t(unsigned int, zlo->dio_mem_align,
|
||||
SECTOR_SIZE - 1);
|
||||
if (zlo->zone_append)
|
||||
lim.max_hw_zone_append_sectors = lim.max_hw_sectors;
|
||||
lim.max_open_zones = zlo->max_open_zones;
|
||||
|
||||
@@ -16,12 +16,12 @@ static inline bool dm_bvec_iter_rewind(const struct bio_vec *bv,
|
||||
int idx;
|
||||
|
||||
iter->bi_size += bytes;
|
||||
if (bytes <= iter->bi_bvec_done) {
|
||||
iter->bi_bvec_done -= bytes;
|
||||
if (bytes <= iter->bi_offset) {
|
||||
iter->bi_offset -= bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
bytes -= iter->bi_bvec_done;
|
||||
bytes -= iter->bi_offset;
|
||||
idx = iter->bi_idx - 1;
|
||||
|
||||
while (idx >= 0 && bytes && bytes > bv[idx].bv_len) {
|
||||
@@ -32,13 +32,13 @@ static inline bool dm_bvec_iter_rewind(const struct bio_vec *bv,
|
||||
if (WARN_ONCE(idx < 0 && bytes,
|
||||
"Attempted to rewind iter beyond bvec's boundaries\n")) {
|
||||
iter->bi_size -= bytes;
|
||||
iter->bi_bvec_done = 0;
|
||||
iter->bi_offset = 0;
|
||||
iter->bi_idx = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
iter->bi_idx = idx;
|
||||
iter->bi_bvec_done = bv[idx].bv_len - bytes;
|
||||
iter->bi_offset = bv[idx].bv_len - bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ int segment_copy_to_bio(struct pcache_segment *segment,
|
||||
|
||||
iov_iter_bvec(&iter, ITER_DEST, &bio->bi_io_vec[bio->bi_iter.bi_idx],
|
||||
bio_segments(bio), bio->bi_iter.bi_size);
|
||||
iter.iov_offset = bio->bi_iter.bi_bvec_done;
|
||||
iter.iov_offset = bio->bi_iter.bi_offset;
|
||||
if (bio_off)
|
||||
iov_iter_advance(&iter, bio_off);
|
||||
|
||||
@@ -35,7 +35,7 @@ int segment_copy_from_bio(struct pcache_segment *segment,
|
||||
|
||||
iov_iter_bvec(&iter, ITER_SOURCE, &bio->bi_io_vec[bio->bi_iter.bi_idx],
|
||||
bio_segments(bio), bio->bi_iter.bi_size);
|
||||
iter.iov_offset = bio->bi_iter.bi_bvec_done;
|
||||
iter.iov_offset = bio->bi_iter.bi_offset;
|
||||
if (bio_off)
|
||||
iov_iter_advance(&iter, bio_off);
|
||||
|
||||
|
||||
+14
-8
@@ -1730,6 +1730,13 @@ static void bitmap_start_write(struct mddev *mddev, sector_t offset,
|
||||
}
|
||||
}
|
||||
|
||||
static void bitmap_prepare_range(struct mddev *mddev, sector_t *offset,
|
||||
unsigned long *sectors, bool discard)
|
||||
{
|
||||
if (mddev->pers->bitmap_sector)
|
||||
mddev->pers->bitmap_sector(mddev, offset, sectors);
|
||||
}
|
||||
|
||||
static void bitmap_end_write(struct mddev *mddev, sector_t offset,
|
||||
unsigned long sectors)
|
||||
{
|
||||
@@ -2064,23 +2071,18 @@ static void bitmap_end_behind_write(struct mddev *mddev)
|
||||
bitmap->mddev->bitmap_info.max_write_behind);
|
||||
}
|
||||
|
||||
static bool bitmap_wait_behind_writes(struct mddev *mddev, bool nowait)
|
||||
static void bitmap_wait_behind_writes(struct mddev *mddev)
|
||||
{
|
||||
struct bitmap *bitmap = mddev->bitmap;
|
||||
|
||||
/* wait for behind writes to complete */
|
||||
if (bitmap && atomic_read(&bitmap->behind_writes) > 0) {
|
||||
if (nowait)
|
||||
return false;
|
||||
|
||||
pr_debug("md:%s: behind writes in progress - waiting to stop.\n",
|
||||
mdname(mddev));
|
||||
/* need to kick something here to make sure I/O goes? */
|
||||
wait_event(bitmap->behind_wait,
|
||||
atomic_read(&bitmap->behind_writes) == 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bitmap_destroy(struct mddev *mddev)
|
||||
@@ -2090,7 +2092,7 @@ static void bitmap_destroy(struct mddev *mddev)
|
||||
if (!bitmap) /* there was no bitmap */
|
||||
return;
|
||||
|
||||
bitmap_wait_behind_writes(mddev, false);
|
||||
bitmap_wait_behind_writes(mddev);
|
||||
if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags))
|
||||
mddev_destroy_serial_pool(mddev, NULL);
|
||||
|
||||
@@ -2629,10 +2631,12 @@ static ssize_t
|
||||
location_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
{
|
||||
int rv;
|
||||
unsigned int noio_flags;
|
||||
|
||||
rv = mddev_suspend_and_lock(mddev);
|
||||
if (rv)
|
||||
return rv;
|
||||
noio_flags = memalloc_noio_save();
|
||||
|
||||
if (mddev->pers) {
|
||||
if (mddev->recovery || mddev->sync_thread) {
|
||||
@@ -2719,6 +2723,7 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
}
|
||||
rv = 0;
|
||||
out:
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
if (rv)
|
||||
return rv;
|
||||
@@ -2862,7 +2867,7 @@ backlog_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
if (!has_write_mostly) {
|
||||
pr_warn_ratelimited("%s: can't set backlog, no write mostly device available\n",
|
||||
mdname(mddev));
|
||||
mddev_unlock(mddev);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -3083,6 +3088,7 @@ static struct bitmap_operations bitmap_ops = {
|
||||
.flush = bitmap_flush,
|
||||
.write_all = bitmap_write_all,
|
||||
.dirty_bits = bitmap_dirty_bits,
|
||||
.prepare_range = bitmap_prepare_range,
|
||||
.unplug = bitmap_unplug,
|
||||
.daemon_work = bitmap_daemon_work,
|
||||
|
||||
|
||||
+11
-1
@@ -29,6 +29,7 @@ enum bitmap_state {
|
||||
BITMAP_FIRST_USE = 3, /* llbitmap is just created */
|
||||
BITMAP_CLEAN = 4, /* llbitmap is created with assume_clean */
|
||||
BITMAP_DAEMON_BUSY = 5, /* llbitmap daemon is not finished after daemon_sleep */
|
||||
BITMAP_SHUTDOWN = 6, /* llbitmap is being destroyed */
|
||||
BITMAP_HOSTENDIAN =15,
|
||||
};
|
||||
|
||||
@@ -93,12 +94,21 @@ struct bitmap_operations {
|
||||
void (*write_all)(struct mddev *mddev);
|
||||
void (*dirty_bits)(struct mddev *mddev, unsigned long s,
|
||||
unsigned long e);
|
||||
/* Prepare a range for this bitmap implementation. */
|
||||
void (*prepare_range)(struct mddev *mddev,
|
||||
sector_t *offset,
|
||||
unsigned long *sectors,
|
||||
bool discard);
|
||||
void (*reshape_finish)(struct mddev *mddev);
|
||||
int (*reshape_can_start)(struct mddev *mddev);
|
||||
void (*reshape_mark)(struct mddev *mddev, sector_t old_pos,
|
||||
sector_t new_pos);
|
||||
void (*unplug)(struct mddev *mddev, bool sync);
|
||||
void (*daemon_work)(struct mddev *mddev);
|
||||
|
||||
void (*start_behind_write)(struct mddev *mddev);
|
||||
void (*end_behind_write)(struct mddev *mddev);
|
||||
bool (*wait_behind_writes)(struct mddev *mddev, bool nowait);
|
||||
void (*wait_behind_writes)(struct mddev *mddev);
|
||||
|
||||
md_bitmap_fn *start_write;
|
||||
md_bitmap_fn *end_write;
|
||||
|
||||
@@ -71,6 +71,7 @@ static int linear_set_limits(struct mddev *mddev)
|
||||
int err;
|
||||
|
||||
md_init_stacking_limits(&lim);
|
||||
lim.features |= BLK_FEAT_NOWAIT;
|
||||
lim.max_hw_sectors = mddev->chunk_sectors;
|
||||
lim.logical_block_size = mddev->logical_block_size;
|
||||
lim.max_write_zeroes_sectors = mddev->chunk_sectors;
|
||||
|
||||
+681
-73
File diff suppressed because it is too large
Load Diff
+140
-38
@@ -233,23 +233,22 @@ static int rdev_need_serial(struct md_rdev *rdev)
|
||||
void mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int noio_flags;
|
||||
|
||||
if (rdev && !rdev_need_serial(rdev) &&
|
||||
if (!test_bit(MD_SERIALIZE_POLICY, &mddev->flags) &&
|
||||
rdev && !rdev_need_serial(rdev) &&
|
||||
!test_bit(CollisionCheck, &rdev->flags))
|
||||
return;
|
||||
|
||||
noio_flags = memalloc_noio_save();
|
||||
if (!rdev)
|
||||
ret = rdevs_init_serial(mddev);
|
||||
else
|
||||
ret = rdev_init_serial(rdev);
|
||||
if (ret)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
if (mddev->serial_info_pool == NULL) {
|
||||
/*
|
||||
* already in memalloc noio context by
|
||||
* mddev_suspend()
|
||||
*/
|
||||
mddev->serial_info_pool =
|
||||
mempool_create_kmalloc_pool(NR_SERIAL_INFOS,
|
||||
sizeof(struct serial_info));
|
||||
@@ -258,6 +257,8 @@ void mddev_create_serial_pool(struct mddev *mddev, struct md_rdev *rdev)
|
||||
pr_err("can't alloc memory pool for serialization\n");
|
||||
}
|
||||
}
|
||||
out:
|
||||
memalloc_noio_restore(noio_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -516,9 +517,6 @@ int mddev_suspend(struct mddev *mddev, bool interruptible)
|
||||
*/
|
||||
WRITE_ONCE(mddev->suspended, mddev->suspended + 1);
|
||||
|
||||
/* restrict memory reclaim I/O during raid array is suspend */
|
||||
mddev->noio_flag = memalloc_noio_save();
|
||||
|
||||
mutex_unlock(&mddev->suspend_mutex);
|
||||
return 0;
|
||||
}
|
||||
@@ -535,9 +533,6 @@ static void __mddev_resume(struct mddev *mddev, bool recovery_needed)
|
||||
return;
|
||||
}
|
||||
|
||||
/* entred the memalloc scope from mddev_suspend() */
|
||||
memalloc_noio_restore(mddev->noio_flag);
|
||||
|
||||
percpu_ref_resurrect(&mddev->active_io);
|
||||
wake_up(&mddev->sb_wait);
|
||||
|
||||
@@ -1920,6 +1915,13 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
|
||||
rdev->bb_page, REQ_OP_READ, true))
|
||||
return -EIO;
|
||||
bbp = (__le64 *)page_address(rdev->bb_page);
|
||||
|
||||
/* check for badblocks api. */
|
||||
if (sb->bblog_shift >= BITS_PER_TYPE(sector_t)) {
|
||||
pr_err("md: %pg: bogus bblog_shift %u for badblocks.\n",
|
||||
rdev->bdev, sb->bblog_shift);
|
||||
return -EINVAL;
|
||||
}
|
||||
rdev->badblocks.shift = sb->bblog_shift;
|
||||
for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) {
|
||||
u64 bb = le64_to_cpu(*bbp);
|
||||
@@ -4047,6 +4049,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
char clevel[16];
|
||||
ssize_t rv;
|
||||
size_t slen = len;
|
||||
unsigned int noio_flags;
|
||||
struct md_personality *pers, *oldpers;
|
||||
long level;
|
||||
void *priv, *oldpriv;
|
||||
@@ -4058,6 +4061,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
rv = mddev_suspend_and_lock(mddev);
|
||||
if (rv)
|
||||
return rv;
|
||||
noio_flags = memalloc_noio_save();
|
||||
|
||||
if (mddev->pers == NULL) {
|
||||
memcpy(mddev->clevel, buf, slen);
|
||||
@@ -4233,6 +4237,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
md_new_event();
|
||||
rv = len;
|
||||
out_unlock:
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
return rv;
|
||||
}
|
||||
@@ -4412,6 +4417,7 @@ static ssize_t
|
||||
raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
{
|
||||
unsigned int n;
|
||||
unsigned int noio_flags;
|
||||
int err;
|
||||
|
||||
err = kstrtouint(buf, 10, &n);
|
||||
@@ -4421,6 +4427,7 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
err = mddev_suspend_and_lock(mddev);
|
||||
if (err)
|
||||
return err;
|
||||
noio_flags = memalloc_noio_save();
|
||||
if (mddev->pers) {
|
||||
if (n != mddev->raid_disks)
|
||||
err = update_raid_disks(mddev, n);
|
||||
@@ -4444,6 +4451,7 @@ raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
} else
|
||||
mddev->raid_disks = n;
|
||||
out_unlock:
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
return err ? err : len;
|
||||
}
|
||||
@@ -4824,6 +4832,7 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
int minor;
|
||||
dev_t dev;
|
||||
struct md_rdev *rdev;
|
||||
unsigned int noio_flags;
|
||||
int err;
|
||||
|
||||
if (!*buf || *e != ':' || !e[1] || e[1] == '\n')
|
||||
@@ -4839,6 +4848,7 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
err = mddev_suspend_and_lock(mddev);
|
||||
if (err)
|
||||
return err;
|
||||
noio_flags = memalloc_noio_save();
|
||||
if (mddev->persistent) {
|
||||
rdev = md_import_device(dev, mddev->major_version,
|
||||
mddev->minor_version);
|
||||
@@ -4857,6 +4867,7 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
rdev = md_import_device(dev, -1, -1);
|
||||
|
||||
if (IS_ERR(rdev)) {
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
@@ -4864,6 +4875,7 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
|
||||
out:
|
||||
if (err)
|
||||
export_rdev(rdev);
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
if (!err)
|
||||
md_new_event();
|
||||
@@ -5263,21 +5275,28 @@ action_store(struct mddev *mddev, const char *page, size_t len)
|
||||
if (!mddev->pers || !mddev->pers->sync_request)
|
||||
return -EINVAL;
|
||||
|
||||
action = md_sync_action_by_name(page);
|
||||
if (action == ACTION_RESHAPE) {
|
||||
ret = mddev_suspend(mddev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
retry:
|
||||
if (work_busy(&mddev->sync_work))
|
||||
flush_work(&mddev->sync_work);
|
||||
|
||||
ret = mddev_lock(mddev);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (action == ACTION_RESHAPE)
|
||||
mddev_resume(mddev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (work_busy(&mddev->sync_work)) {
|
||||
mddev_unlock(mddev);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
action = md_sync_action_by_name(page);
|
||||
|
||||
/* TODO: mdadm rely on "idle" to start sync_thread. */
|
||||
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) {
|
||||
switch (action) {
|
||||
@@ -5347,6 +5366,8 @@ retry:
|
||||
|
||||
out:
|
||||
mddev_unlock(mddev);
|
||||
if (action == ACTION_RESHAPE)
|
||||
mddev_resume(mddev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -6283,7 +6304,7 @@ void md_init_stacking_limits(struct queue_limits *lim)
|
||||
{
|
||||
blk_set_stacking_limits(lim);
|
||||
lim->features = BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA |
|
||||
BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT;
|
||||
BLK_FEAT_IO_STAT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(md_init_stacking_limits);
|
||||
|
||||
@@ -6631,7 +6652,6 @@ int md_run(struct mddev *mddev)
|
||||
int err;
|
||||
struct md_rdev *rdev;
|
||||
struct md_personality *pers;
|
||||
bool nowait = true;
|
||||
|
||||
if (list_empty(&mddev->disks))
|
||||
/* cannot run an array with no devices.. */
|
||||
@@ -6702,7 +6722,6 @@ int md_run(struct mddev *mddev)
|
||||
}
|
||||
}
|
||||
sysfs_notify_dirent_safe(rdev->sysfs_state);
|
||||
nowait = nowait && bdev_nowait(rdev->bdev);
|
||||
}
|
||||
|
||||
pers = get_pers(mddev->level, mddev->clevel);
|
||||
@@ -7050,7 +7069,7 @@ EXPORT_SYMBOL_GPL(md_stop_writes);
|
||||
static void mddev_detach(struct mddev *mddev)
|
||||
{
|
||||
if (md_bitmap_enabled(mddev, false))
|
||||
mddev->bitmap_ops->wait_behind_writes(mddev, false);
|
||||
mddev->bitmap_ops->wait_behind_writes(mddev);
|
||||
if (mddev->pers && mddev->pers->quiesce && !is_md_suspended(mddev)) {
|
||||
mddev->pers->quiesce(mddev, 1);
|
||||
mddev->pers->quiesce(mddev, 0);
|
||||
@@ -7066,8 +7085,8 @@ static void __md_stop(struct mddev *mddev)
|
||||
{
|
||||
struct md_personality *pers = mddev->pers;
|
||||
|
||||
md_bitmap_destroy(mddev);
|
||||
mddev_detach(mddev);
|
||||
md_bitmap_destroy(mddev);
|
||||
spin_lock(&mddev->lock);
|
||||
mddev->pers = NULL;
|
||||
spin_unlock(&mddev->lock);
|
||||
@@ -8324,8 +8343,10 @@ static int md_ioctl(struct block_device *bdev, blk_mode_t mode,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned int noio_flags = 0;
|
||||
void __user *argp = (void __user *)arg;
|
||||
struct mddev *mddev = NULL;
|
||||
bool suspend;
|
||||
|
||||
err = md_ioctl_valid(cmd);
|
||||
if (err)
|
||||
@@ -8375,13 +8396,15 @@ static int md_ioctl(struct block_device *bdev, blk_mode_t mode,
|
||||
if (!md_is_rdwr(mddev))
|
||||
flush_work(&mddev->sync_work);
|
||||
|
||||
err = md_ioctl_need_suspend(cmd) ? mddev_suspend_and_lock(mddev) :
|
||||
mddev_lock(mddev);
|
||||
suspend = md_ioctl_need_suspend(cmd);
|
||||
err = suspend ? mddev_suspend_and_lock(mddev) : mddev_lock(mddev);
|
||||
if (err) {
|
||||
pr_debug("md: ioctl lock interrupted, reason %d, cmd %d\n",
|
||||
err, cmd);
|
||||
goto out;
|
||||
}
|
||||
if (suspend)
|
||||
noio_flags = memalloc_noio_save();
|
||||
|
||||
if (cmd == SET_ARRAY_INFO) {
|
||||
err = __md_set_array_info(mddev, argp);
|
||||
@@ -8506,8 +8529,12 @@ unlock:
|
||||
err != -EINVAL)
|
||||
mddev->hold_active = 0;
|
||||
|
||||
md_ioctl_need_suspend(cmd) ? mddev_unlock_and_resume(mddev) :
|
||||
mddev_unlock(mddev);
|
||||
if (suspend) {
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
} else {
|
||||
mddev_unlock(mddev);
|
||||
}
|
||||
|
||||
out:
|
||||
if (cmd == STOP_ARRAY_RO || (err && cmd == STOP_ARRAY))
|
||||
@@ -9350,6 +9377,10 @@ void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
|
||||
{
|
||||
struct bio *discard_bio = NULL;
|
||||
|
||||
/* Discard is optional, so silently skip members that do not support it. */
|
||||
if (unlikely(!bdev_max_discard_sectors(rdev->bdev)))
|
||||
return;
|
||||
|
||||
__blkdev_issue_discard(rdev->bdev, start, size, GFP_NOIO, &discard_bio);
|
||||
if (!discard_bio)
|
||||
return;
|
||||
@@ -9361,17 +9392,62 @@ void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(md_submit_discard_bio);
|
||||
|
||||
struct bio *mddev_bio_split_at_reshape_offset(struct mddev *mddev,
|
||||
struct bio *bio,
|
||||
unsigned int *max_sectors,
|
||||
struct bio_set *bs)
|
||||
{
|
||||
sector_t boundary;
|
||||
sector_t start;
|
||||
sector_t end;
|
||||
unsigned int split_sectors;
|
||||
|
||||
split_sectors = bio_sectors(bio);
|
||||
if (max_sectors && *max_sectors && *max_sectors < split_sectors)
|
||||
split_sectors = *max_sectors;
|
||||
|
||||
if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
|
||||
goto split;
|
||||
|
||||
boundary = READ_ONCE(mddev->reshape_position);
|
||||
start = bio->bi_iter.bi_sector;
|
||||
end = bio_end_sector(bio);
|
||||
if (start >= boundary || end <= boundary)
|
||||
goto split;
|
||||
|
||||
if (boundary - start < split_sectors)
|
||||
split_sectors = boundary - start;
|
||||
|
||||
split:
|
||||
if (max_sectors)
|
||||
*max_sectors = split_sectors;
|
||||
if (split_sectors < bio_sectors(bio)) {
|
||||
bio = bio_submit_split_bioset(bio, split_sectors, bs);
|
||||
if (bio)
|
||||
bio->bi_opf |= REQ_NOMERGE;
|
||||
}
|
||||
|
||||
return bio;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mddev_bio_split_at_reshape_offset);
|
||||
|
||||
static void md_bitmap_prepare_range(struct mddev *mddev, sector_t *offset,
|
||||
unsigned long *sectors, bool discard)
|
||||
{
|
||||
mddev->bitmap_ops->prepare_range(mddev, offset, sectors, discard);
|
||||
}
|
||||
|
||||
static void md_bitmap_start(struct mddev *mddev,
|
||||
struct md_io_clone *md_io_clone)
|
||||
{
|
||||
md_bitmap_fn *fn = unlikely(md_io_clone->rw == STAT_DISCARD) ?
|
||||
mddev->bitmap_ops->start_discard :
|
||||
bool discard = md_io_clone->rw == STAT_DISCARD;
|
||||
md_bitmap_fn *fn = discard ? mddev->bitmap_ops->start_discard :
|
||||
mddev->bitmap_ops->start_write;
|
||||
|
||||
if (mddev->pers->bitmap_sector)
|
||||
mddev->pers->bitmap_sector(mddev, &md_io_clone->offset,
|
||||
&md_io_clone->sectors);
|
||||
|
||||
md_bitmap_prepare_range(mddev, &md_io_clone->offset,
|
||||
&md_io_clone->sectors, discard);
|
||||
if (!md_io_clone->sectors)
|
||||
return;
|
||||
fn(mddev, md_io_clone->offset, md_io_clone->sectors);
|
||||
}
|
||||
|
||||
@@ -9392,7 +9468,8 @@ static void md_end_clone_io(struct bio *bio)
|
||||
struct mddev *mddev = md_io_clone->mddev;
|
||||
struct completion *reshape_completion = bio->bi_private;
|
||||
|
||||
if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
|
||||
if (bio_data_dir(orig_bio) == WRITE && md_io_clone->sectors &&
|
||||
md_bitmap_enabled(mddev, false))
|
||||
md_bitmap_end(mddev, md_io_clone);
|
||||
|
||||
if (bio->bi_status && !orig_bio->bi_status)
|
||||
@@ -9419,10 +9496,14 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
|
||||
md_io_clone = container_of(clone, struct md_io_clone, bio_clone);
|
||||
md_io_clone->orig_bio = *bio;
|
||||
md_io_clone->mddev = mddev;
|
||||
md_io_clone->sectors = 0;
|
||||
if (blk_queue_io_stat(bdev->bd_disk->queue))
|
||||
md_io_clone->start_time = bio_start_io_acct(*bio);
|
||||
else
|
||||
md_io_clone->start_time = 0;
|
||||
|
||||
if (bio_data_dir(*bio) == WRITE && md_bitmap_enabled(mddev, false)) {
|
||||
if (bio_data_dir(*bio) == WRITE && bio_sectors(*bio) &&
|
||||
md_bitmap_enabled(mddev, false)) {
|
||||
md_io_clone->offset = (*bio)->bi_iter.bi_sector;
|
||||
md_io_clone->sectors = bio_sectors(*bio);
|
||||
md_io_clone->rw = op_stat_group(bio_op(*bio));
|
||||
@@ -9881,8 +9962,10 @@ update:
|
||||
*/
|
||||
md_new_event();
|
||||
|
||||
if (last_check + window > io_sectors || j == max_sectors)
|
||||
if (last_check + window > io_sectors || j == max_sectors) {
|
||||
cond_resched();
|
||||
continue;
|
||||
}
|
||||
|
||||
last_check = io_sectors;
|
||||
repeat:
|
||||
@@ -10175,19 +10258,34 @@ static void md_start_sync(struct work_struct *ws)
|
||||
struct mddev *mddev = container_of(ws, struct mddev, sync_work);
|
||||
int spares = 0;
|
||||
bool suspend = false;
|
||||
unsigned int noio_flags = 0;
|
||||
char *name;
|
||||
|
||||
/*
|
||||
* If reshape is still in progress, spares won't be added or removed
|
||||
* from conf until reshape is done.
|
||||
*/
|
||||
if (mddev->reshape_position == MaxSector &&
|
||||
if ((mddev->reshape_position == MaxSector || !md_is_rdwr(mddev)) &&
|
||||
md_spares_need_change(mddev)) {
|
||||
suspend = true;
|
||||
mddev_suspend(mddev, false);
|
||||
noio_flags = memalloc_noio_save();
|
||||
}
|
||||
|
||||
mddev_lock_nointr(mddev);
|
||||
|
||||
/*
|
||||
* The spare configuration can change before reconfig_mutex is acquired.
|
||||
* Recheck while holding the lock and suspend if needed.
|
||||
*/
|
||||
if (!suspend && (mddev->reshape_position == MaxSector || !md_is_rdwr(mddev)) &&
|
||||
md_spares_need_change(mddev)) {
|
||||
mddev_unlock(mddev);
|
||||
mddev_suspend_and_lock_nointr(mddev);
|
||||
suspend = true;
|
||||
noio_flags = memalloc_noio_save();
|
||||
}
|
||||
|
||||
if (!md_is_rdwr(mddev)) {
|
||||
/*
|
||||
* On a read-only array we can:
|
||||
@@ -10231,8 +10329,10 @@ static void md_start_sync(struct work_struct *ws)
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=218200
|
||||
* Therefore, use __mddev_resume(mddev, false).
|
||||
*/
|
||||
if (suspend)
|
||||
if (suspend) {
|
||||
memalloc_noio_restore(noio_flags);
|
||||
__mddev_resume(mddev, false);
|
||||
}
|
||||
md_wakeup_thread(mddev->sync_thread);
|
||||
sysfs_notify_dirent_safe(mddev->sysfs_action);
|
||||
md_new_event();
|
||||
@@ -10251,8 +10351,10 @@ not_running:
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=218200
|
||||
* Therefore, use __mddev_resume(mddev, false).
|
||||
*/
|
||||
if (suspend)
|
||||
if (suspend) {
|
||||
memalloc_noio_restore(noio_flags);
|
||||
__mddev_resume(mddev, false);
|
||||
}
|
||||
|
||||
wake_up(&resync_wait);
|
||||
if (test_and_clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery) &&
|
||||
@@ -10553,7 +10655,7 @@ EXPORT_SYMBOL(md_finish_reshape);
|
||||
/* Bad block management */
|
||||
|
||||
/* Returns true on success, false on failure */
|
||||
bool rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
|
||||
bool rdev_set_badblocks(struct md_rdev *rdev, sector_t s, sector_t sectors,
|
||||
int is_new)
|
||||
{
|
||||
struct mddev *mddev = rdev->mddev;
|
||||
@@ -10593,7 +10695,7 @@ bool rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rdev_set_badblocks);
|
||||
|
||||
void rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
|
||||
void rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, sector_t sectors,
|
||||
int is_new)
|
||||
{
|
||||
if (is_new)
|
||||
|
||||
+11
-4
@@ -311,7 +311,7 @@ static inline int is_badblock(struct md_rdev *rdev, sector_t s, sector_t sectors
|
||||
}
|
||||
|
||||
static inline int rdev_has_badblock(struct md_rdev *rdev, sector_t s,
|
||||
int sectors)
|
||||
sector_t sectors)
|
||||
{
|
||||
sector_t first_bad;
|
||||
sector_t bad_sectors;
|
||||
@@ -319,9 +319,9 @@ static inline int rdev_has_badblock(struct md_rdev *rdev, sector_t s,
|
||||
return is_badblock(rdev, s, sectors, &first_bad, &bad_sectors);
|
||||
}
|
||||
|
||||
extern bool rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
|
||||
extern bool rdev_set_badblocks(struct md_rdev *rdev, sector_t s, sector_t sectors,
|
||||
int is_new);
|
||||
extern void rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
|
||||
extern void rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, sector_t sectors,
|
||||
int is_new);
|
||||
struct md_cluster_info;
|
||||
struct md_cluster_operations;
|
||||
@@ -621,7 +621,6 @@ struct mddev {
|
||||
struct md_cluster_info *cluster_info;
|
||||
struct md_cluster_operations *cluster_ops;
|
||||
unsigned int good_device_nr; /* good device num within cluster raid */
|
||||
unsigned int noio_flag; /* for memalloc scope API */
|
||||
|
||||
/*
|
||||
* Temporarily store rdev that will be finally removed when
|
||||
@@ -798,6 +797,10 @@ struct md_personality
|
||||
/* convert io ranges from array to bitmap */
|
||||
void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
|
||||
unsigned long *sectors);
|
||||
void (*bitmap_sector_map)(struct mddev *mddev, sector_t *offset,
|
||||
unsigned long *sectors, bool previous);
|
||||
sector_t (*bitmap_sync_size)(struct mddev *mddev, bool previous);
|
||||
sector_t (*bitmap_array_sectors)(struct mddev *mddev, bool previous);
|
||||
};
|
||||
|
||||
struct md_sysfs_entry {
|
||||
@@ -921,6 +924,10 @@ extern void md_error(struct mddev *mddev, struct md_rdev *rdev);
|
||||
extern void md_finish_reshape(struct mddev *mddev);
|
||||
void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
|
||||
struct bio *bio, sector_t start, sector_t size);
|
||||
struct bio *mddev_bio_split_at_reshape_offset(struct mddev *mddev,
|
||||
struct bio *bio,
|
||||
unsigned int *max_sectors,
|
||||
struct bio_set *bs);
|
||||
void md_account_bio(struct mddev *mddev, struct bio **bio);
|
||||
|
||||
extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
|
||||
|
||||
@@ -385,6 +385,7 @@ static int raid0_set_limits(struct mddev *mddev)
|
||||
int err;
|
||||
|
||||
md_init_stacking_limits(&lim);
|
||||
lim.features |= BLK_FEAT_NOWAIT;
|
||||
lim.max_hw_sectors = mddev->chunk_sectors;
|
||||
lim.max_write_zeroes_sectors = mddev->chunk_sectors;
|
||||
lim.max_hw_wzeroes_unmap_sectors = mddev->chunk_sectors;
|
||||
|
||||
@@ -290,9 +290,8 @@ static inline bool raid1_should_read_first(struct mddev *mddev,
|
||||
}
|
||||
|
||||
/*
|
||||
* bio with REQ_RAHEAD or REQ_NOWAIT can fail at anytime, before such IO is
|
||||
* submitted to the underlying disks, hence don't record badblocks or retry
|
||||
* in this case.
|
||||
* bio with REQ_RAHEAD can fail at anytime, before such IO is submitted to the
|
||||
* underlying disks, hence don't record badblocks or retry in this case.
|
||||
*
|
||||
* BLK_STS_INVAL means the bio was not valid for the underlying device. This
|
||||
* is a user error, not a device failure, so retrying or recording bad blocks
|
||||
@@ -300,6 +299,5 @@ static inline bool raid1_should_read_first(struct mddev *mddev,
|
||||
*/
|
||||
static inline bool raid1_should_handle_error(struct bio *bio)
|
||||
{
|
||||
return !(bio->bi_opf & (REQ_RAHEAD | REQ_NOWAIT)) &&
|
||||
bio->bi_status != BLK_STS_INVAL;
|
||||
return !(bio->bi_opf & REQ_RAHEAD) && bio->bi_status != BLK_STS_INVAL;
|
||||
}
|
||||
|
||||
+41
-98
@@ -604,9 +604,9 @@ static void update_read_sectors(struct r1conf *conf, int disk,
|
||||
struct raid1_info *info = &conf->mirrors[disk];
|
||||
|
||||
atomic_inc(&info->rdev->nr_pending);
|
||||
if (info->next_seq_sect != this_sector)
|
||||
info->seq_start = this_sector;
|
||||
info->next_seq_sect = this_sector + len;
|
||||
if (READ_ONCE(info->next_seq_sect) != this_sector)
|
||||
WRITE_ONCE(info->seq_start, this_sector);
|
||||
WRITE_ONCE(info->next_seq_sect, this_sector + len);
|
||||
}
|
||||
|
||||
static int choose_first_rdev(struct r1conf *conf, struct r1bio *r1_bio,
|
||||
@@ -735,8 +735,7 @@ static int choose_slow_rdev(struct r1conf *conf, struct r1bio *r1_bio,
|
||||
|
||||
static bool is_sequential(struct r1conf *conf, int disk, struct r1bio *r1_bio)
|
||||
{
|
||||
/* TODO: address issues with this check and concurrency. */
|
||||
return conf->mirrors[disk].next_seq_sect == r1_bio->sector ||
|
||||
return READ_ONCE(conf->mirrors[disk].next_seq_sect) == r1_bio->sector ||
|
||||
READ_ONCE(conf->mirrors[disk].head_position) == r1_bio->sector;
|
||||
}
|
||||
|
||||
@@ -747,15 +746,18 @@ static bool is_sequential(struct r1conf *conf, int disk, struct r1bio *r1_bio)
|
||||
static bool should_choose_next(struct r1conf *conf, int disk)
|
||||
{
|
||||
struct raid1_info *mirror = &conf->mirrors[disk];
|
||||
sector_t seq_start, next_seq_sect;
|
||||
int opt_iosize;
|
||||
|
||||
if (!test_bit(Nonrot, &mirror->rdev->flags))
|
||||
return false;
|
||||
|
||||
opt_iosize = bdev_io_opt(mirror->rdev->bdev) >> 9;
|
||||
return opt_iosize > 0 && mirror->seq_start != MaxSector &&
|
||||
mirror->next_seq_sect > opt_iosize &&
|
||||
mirror->next_seq_sect - opt_iosize >= mirror->seq_start;
|
||||
seq_start = READ_ONCE(mirror->seq_start);
|
||||
next_seq_sect = READ_ONCE(mirror->next_seq_sect);
|
||||
return opt_iosize > 0 && seq_start != MaxSector &&
|
||||
next_seq_sect > opt_iosize &&
|
||||
next_seq_sect - opt_iosize >= seq_start;
|
||||
}
|
||||
|
||||
static bool rdev_readable(struct md_rdev *rdev, struct r1bio *r1_bio)
|
||||
@@ -1051,10 +1053,8 @@ static void lower_barrier(struct r1conf *conf, sector_t sector_nr)
|
||||
wake_up(&conf->wait_barrier);
|
||||
}
|
||||
|
||||
static bool _wait_barrier(struct r1conf *conf, int idx, bool nowait)
|
||||
static void _wait_barrier(struct r1conf *conf, int idx)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
/*
|
||||
* We need to increase conf->nr_pending[idx] very early here,
|
||||
* then raise_barrier() can be blocked when it waits for
|
||||
@@ -1085,7 +1085,7 @@ static bool _wait_barrier(struct r1conf *conf, int idx, bool nowait)
|
||||
*/
|
||||
if (!READ_ONCE(conf->array_frozen) &&
|
||||
!atomic_read(&conf->barrier[idx]))
|
||||
return ret;
|
||||
return;
|
||||
|
||||
/*
|
||||
* After holding conf->resync_lock, conf->nr_pending[idx]
|
||||
@@ -1104,26 +1104,18 @@ static bool _wait_barrier(struct r1conf *conf, int idx, bool nowait)
|
||||
wake_up_barrier(conf);
|
||||
/* Wait for the barrier in same barrier unit bucket to drop. */
|
||||
|
||||
/* Return false when nowait flag is set */
|
||||
if (nowait) {
|
||||
ret = false;
|
||||
} else {
|
||||
wait_event_lock_irq(conf->wait_barrier,
|
||||
!conf->array_frozen &&
|
||||
!atomic_read(&conf->barrier[idx]),
|
||||
conf->resync_lock);
|
||||
atomic_inc(&conf->nr_pending[idx]);
|
||||
}
|
||||
wait_event_lock_irq(conf->wait_barrier, !conf->array_frozen &&
|
||||
!atomic_read(&conf->barrier[idx]),
|
||||
conf->resync_lock);
|
||||
|
||||
atomic_inc(&conf->nr_pending[idx]);
|
||||
atomic_dec(&conf->nr_waiting[idx]);
|
||||
spin_unlock_irq(&conf->resync_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool wait_read_barrier(struct r1conf *conf, sector_t sector_nr, bool nowait)
|
||||
static void wait_read_barrier(struct r1conf *conf, sector_t sector_nr)
|
||||
{
|
||||
int idx = sector_to_idx(sector_nr);
|
||||
bool ret = true;
|
||||
|
||||
/*
|
||||
* Very similar to _wait_barrier(). The difference is, for read
|
||||
@@ -1135,7 +1127,7 @@ static bool wait_read_barrier(struct r1conf *conf, sector_t sector_nr, bool nowa
|
||||
atomic_inc(&conf->nr_pending[idx]);
|
||||
|
||||
if (!READ_ONCE(conf->array_frozen))
|
||||
return ret;
|
||||
return;
|
||||
|
||||
spin_lock_irq(&conf->resync_lock);
|
||||
atomic_inc(&conf->nr_waiting[idx]);
|
||||
@@ -1147,27 +1139,19 @@ static bool wait_read_barrier(struct r1conf *conf, sector_t sector_nr, bool nowa
|
||||
wake_up_barrier(conf);
|
||||
/* Wait for array to be unfrozen */
|
||||
|
||||
/* Return false when nowait flag is set */
|
||||
if (nowait) {
|
||||
/* Return false when nowait flag is set */
|
||||
ret = false;
|
||||
} else {
|
||||
wait_event_lock_irq(conf->wait_barrier,
|
||||
!conf->array_frozen,
|
||||
conf->resync_lock);
|
||||
atomic_inc(&conf->nr_pending[idx]);
|
||||
}
|
||||
wait_event_lock_irq(conf->wait_barrier, !conf->array_frozen,
|
||||
conf->resync_lock);
|
||||
|
||||
atomic_inc(&conf->nr_pending[idx]);
|
||||
atomic_dec(&conf->nr_waiting[idx]);
|
||||
spin_unlock_irq(&conf->resync_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool wait_barrier(struct r1conf *conf, sector_t sector_nr, bool nowait)
|
||||
static void wait_barrier(struct r1conf *conf, sector_t sector_nr)
|
||||
{
|
||||
int idx = sector_to_idx(sector_nr);
|
||||
|
||||
return _wait_barrier(conf, idx, nowait);
|
||||
_wait_barrier(conf, idx);
|
||||
}
|
||||
|
||||
static void _allow_barrier(struct r1conf *conf, int idx)
|
||||
@@ -1342,7 +1326,6 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
|
||||
int max_sectors;
|
||||
int rdisk;
|
||||
bool r1bio_existed = !!r1_bio;
|
||||
bool nowait = bio->bi_opf & REQ_NOWAIT;
|
||||
|
||||
/*
|
||||
* An md cloned bio indicates we are in the error path.
|
||||
@@ -1362,16 +1345,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
|
||||
* Still need barrier for READ in case that whole
|
||||
* array is frozen.
|
||||
*/
|
||||
if (!wait_read_barrier(conf, bio->bi_iter.bi_sector, nowait)) {
|
||||
bio_wouldblock_error(bio);
|
||||
|
||||
if (r1bio_existed) {
|
||||
set_bit(R1BIO_Returned, &r1_bio->state);
|
||||
raid_end_bio_io(r1_bio);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
wait_read_barrier(conf, bio->bi_iter.bi_sector);
|
||||
|
||||
if (!r1_bio)
|
||||
r1_bio = alloc_r1bio(mddev, bio);
|
||||
@@ -1406,14 +1380,10 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
|
||||
md_bitmap_enabled(mddev, false)) {
|
||||
/*
|
||||
* Reading from a write-mostly device must take care not to
|
||||
* over-take any writes that are 'behind'
|
||||
*/
|
||||
mddev_add_trace_msg(mddev, "raid1 wait behind writes");
|
||||
if (!mddev->bitmap_ops->wait_behind_writes(mddev, nowait)) {
|
||||
bio_wouldblock_error(bio);
|
||||
set_bit(R1BIO_Returned, &r1_bio->state);
|
||||
goto err_handle;
|
||||
}
|
||||
* over-take any writes that are 'behind'
|
||||
*/
|
||||
mddev_add_trace_msg(mddev, "raid1 wait behind writes");
|
||||
mddev->bitmap_ops->wait_behind_writes(mddev);
|
||||
}
|
||||
|
||||
if (max_sectors < bio_sectors(bio)) {
|
||||
@@ -1435,7 +1405,6 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
|
||||
}
|
||||
read_bio = bio_alloc_clone(mirror->rdev->bdev, bio, gfp,
|
||||
&mddev->bio_set);
|
||||
read_bio->bi_opf &= ~REQ_NOWAIT;
|
||||
r1_bio->bios[rdisk] = read_bio;
|
||||
|
||||
read_bio->bi_iter.bi_sector = r1_bio->sector +
|
||||
@@ -1454,7 +1423,7 @@ err_handle:
|
||||
raid_end_bio_io(r1_bio);
|
||||
}
|
||||
|
||||
static bool wait_blocked_rdev(struct mddev *mddev, struct bio *bio)
|
||||
static void wait_blocked_rdev(struct mddev *mddev, struct bio *bio)
|
||||
{
|
||||
struct r1conf *conf = mddev->private;
|
||||
int disks = conf->raid_disks * 2;
|
||||
@@ -1474,9 +1443,6 @@ retry:
|
||||
set_bit(BlockedBadBlocks, &rdev->flags);
|
||||
|
||||
if (rdev_blocked(rdev)) {
|
||||
if (bio->bi_opf & REQ_NOWAIT)
|
||||
return false;
|
||||
|
||||
mddev_add_trace_msg(rdev->mddev, "raid1 wait rdev %d blocked",
|
||||
rdev->raid_disk);
|
||||
atomic_inc(&rdev->nr_pending);
|
||||
@@ -1484,8 +1450,6 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void raid1_start_write_behind(struct mddev *mddev, struct r1bio *r1_bio,
|
||||
@@ -1521,18 +1485,13 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
|
||||
unsigned long flags;
|
||||
int first_clone;
|
||||
bool write_behind = false;
|
||||
bool nowait = bio->bi_opf & REQ_NOWAIT;
|
||||
bool atomic = bio->bi_opf & REQ_ATOMIC;
|
||||
bool is_discard = op_is_discard(bio->bi_opf);
|
||||
sector_t sector = bio->bi_iter.bi_sector;
|
||||
|
||||
if (mddev_is_clustered(mddev) &&
|
||||
mddev->cluster_ops->area_resyncing(mddev, WRITE, sector,
|
||||
bio_end_sector(bio))) {
|
||||
|
||||
if (nowait) {
|
||||
bio_wouldblock_error(bio);
|
||||
return false;
|
||||
}
|
||||
wait_event_idle(conf->wait_barrier,
|
||||
!mddev->cluster_ops->area_resyncing(mddev, WRITE,
|
||||
sector,
|
||||
@@ -1544,15 +1503,9 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
|
||||
* thread has put up a bar for new requests.
|
||||
* Continue immediately if no resync is active currently.
|
||||
*/
|
||||
if (!wait_barrier(conf, sector, nowait)) {
|
||||
bio_wouldblock_error(bio);
|
||||
return false;
|
||||
}
|
||||
wait_barrier(conf, sector);
|
||||
|
||||
if (!wait_blocked_rdev(mddev, bio)) {
|
||||
bio_wouldblock_error(bio);
|
||||
goto err_allow_barrier;
|
||||
}
|
||||
wait_blocked_rdev(mddev, bio);
|
||||
|
||||
r1_bio = alloc_r1bio(mddev, bio);
|
||||
r1_bio->sectors = max_sectors;
|
||||
@@ -1579,6 +1532,8 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
|
||||
*/
|
||||
if (!is_discard && rdev && test_bit(WriteMostly, &rdev->flags))
|
||||
write_behind = true;
|
||||
if (atomic && max_sectors > BIO_MAX_VECS * (PAGE_SIZE >> 9))
|
||||
write_behind = false;
|
||||
|
||||
r1_bio->bios[i] = NULL;
|
||||
if (!rdev || test_bit(Faulty, &rdev->flags))
|
||||
@@ -1604,19 +1559,6 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
|
||||
if (is_bad) {
|
||||
int good_sectors;
|
||||
|
||||
/*
|
||||
* We cannot atomically write this, so just
|
||||
* error in that case. It could be possible to
|
||||
* atomically write other mirrors, but the
|
||||
* complexity of supporting that is not worth
|
||||
* the benefit.
|
||||
*/
|
||||
if (bio->bi_opf & REQ_ATOMIC) {
|
||||
bio->bi_status = BLK_STS_NOTSUPP;
|
||||
bio_endio(bio);
|
||||
goto err_dec_pending;
|
||||
}
|
||||
|
||||
good_sectors = first_bad - sector;
|
||||
if (good_sectors < max_sectors)
|
||||
max_sectors = good_sectors;
|
||||
@@ -1637,6 +1579,11 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
|
||||
max_sectors = min_t(int, max_sectors,
|
||||
BIO_MAX_VECS * (PAGE_SIZE >> 9));
|
||||
if (max_sectors < bio_sectors(bio)) {
|
||||
if (atomic) {
|
||||
bio_io_error(bio);
|
||||
goto err_dec_pending;
|
||||
}
|
||||
|
||||
bio = bio_submit_split_bioset(bio, max_sectors,
|
||||
&conf->bio_split);
|
||||
if (!bio)
|
||||
@@ -1681,7 +1628,6 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
|
||||
wait_for_serialization(rdev, r1_bio);
|
||||
}
|
||||
|
||||
mbio->bi_opf &= ~REQ_NOWAIT;
|
||||
r1_bio->bios[i] = mbio;
|
||||
|
||||
mbio->bi_iter.bi_sector = sector + rdev->data_offset;
|
||||
@@ -1720,8 +1666,6 @@ err_dec_pending:
|
||||
}
|
||||
|
||||
free_r1bio(r1_bio);
|
||||
|
||||
err_allow_barrier:
|
||||
allow_barrier(conf, sector);
|
||||
|
||||
return false;
|
||||
@@ -1850,7 +1794,7 @@ static void close_sync(struct r1conf *conf)
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < BARRIER_BUCKETS_NR; idx++) {
|
||||
_wait_barrier(conf, idx, false);
|
||||
_wait_barrier(conf, idx);
|
||||
_allow_barrier(conf, idx);
|
||||
}
|
||||
|
||||
@@ -3228,6 +3172,7 @@ static int raid1_set_limits(struct mddev *mddev)
|
||||
md_init_stacking_limits(&lim);
|
||||
lim.max_write_zeroes_sectors = 0;
|
||||
lim.max_hw_wzeroes_unmap_sectors = 0;
|
||||
lim.chunk_sectors = BARRIER_UNIT_SECTOR_SIZE;
|
||||
lim.logical_block_size = mddev->logical_block_size;
|
||||
lim.features |= BLK_FEAT_ATOMIC_WRITES;
|
||||
lim.features |= BLK_FEAT_PCI_P2PDMA;
|
||||
@@ -3485,8 +3430,6 @@ static void *raid1_takeover(struct mddev *mddev)
|
||||
mddev->new_chunk_sectors = 0;
|
||||
conf = setup_conf(mddev);
|
||||
if (!IS_ERR(conf)) {
|
||||
/* Array must appear to be quiesced */
|
||||
conf->array_frozen = 1;
|
||||
mddev_clear_unsupported_flags(mddev,
|
||||
UNSUPPORTED_MDDEV_FLAGS);
|
||||
}
|
||||
|
||||
+126
-101
@@ -103,13 +103,23 @@ static inline struct r10bio *get_resync_r10bio(struct bio *bio)
|
||||
return get_resync_pages(bio)->raid_bio;
|
||||
}
|
||||
|
||||
static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
|
||||
static inline int calc_r10bio_size(unsigned int raid_disks)
|
||||
{
|
||||
struct r10conf *conf = data;
|
||||
int size = offsetof(struct r10bio, devs[conf->geo.raid_disks]);
|
||||
return offsetof(struct r10bio, devs[raid_disks]);
|
||||
}
|
||||
|
||||
/* allocate a r10bio with room for raid_disks entries in the
|
||||
* bios array */
|
||||
static mempool_t *create_r10bio_pool(unsigned int raid_disks)
|
||||
{
|
||||
int size = calc_r10bio_size(raid_disks);
|
||||
|
||||
return mempool_create_kmalloc_pool(NR_RAID_BIOS, size);
|
||||
}
|
||||
|
||||
static struct r10bio *alloc_r10bio(unsigned int raid_disks, gfp_t gfp_flags)
|
||||
{
|
||||
int size = calc_r10bio_size(raid_disks);
|
||||
|
||||
/* allocate a r10bio sized for current geometry */
|
||||
return kzalloc(size, gfp_flags);
|
||||
}
|
||||
|
||||
@@ -137,7 +147,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
|
||||
int nalloc, nalloc_rp;
|
||||
struct resync_pages *rps;
|
||||
|
||||
r10_bio = r10bio_pool_alloc(gfp_flags, conf);
|
||||
r10_bio = alloc_r10bio(conf->geo.raid_disks, gfp_flags);
|
||||
if (!r10_bio)
|
||||
return NULL;
|
||||
|
||||
@@ -277,7 +287,7 @@ static void free_r10bio(struct r10bio *r10_bio)
|
||||
struct r10conf *conf = r10_bio->mddev->private;
|
||||
|
||||
put_all_bios(conf, r10_bio);
|
||||
mempool_free(r10_bio, &conf->r10bio_pool);
|
||||
mempool_free(r10_bio, conf->r10bio_pool);
|
||||
}
|
||||
|
||||
static void put_buf(struct r10bio *r10_bio)
|
||||
@@ -321,20 +331,24 @@ static void raid_end_bio_io(struct r10bio *r10_bio)
|
||||
{
|
||||
struct bio *bio = r10_bio->master_bio;
|
||||
struct r10conf *conf = r10_bio->mddev->private;
|
||||
bool returned = true;
|
||||
|
||||
if (!test_and_set_bit(R10BIO_Returned, &r10_bio->state)) {
|
||||
if (!test_bit(R10BIO_Uptodate, &r10_bio->state))
|
||||
bio->bi_status = BLK_STS_IOERR;
|
||||
bio_endio(bio);
|
||||
returned = false;
|
||||
}
|
||||
|
||||
free_r10bio(r10_bio);
|
||||
|
||||
if (!returned)
|
||||
bio_endio(bio);
|
||||
|
||||
/*
|
||||
* Wake up any possible resync thread that waits for the device
|
||||
* to go idle.
|
||||
*/
|
||||
allow_barrier(conf);
|
||||
|
||||
free_r10bio(r10_bio);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1002,32 +1016,22 @@ static bool wait_barrier_nolock(struct r10conf *conf)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool wait_barrier(struct r10conf *conf, bool nowait)
|
||||
static void wait_barrier(struct r10conf *conf)
|
||||
{
|
||||
bool ret = true;
|
||||
|
||||
if (wait_barrier_nolock(conf))
|
||||
return true;
|
||||
return;
|
||||
|
||||
write_seqlock_irq(&conf->resync_lock);
|
||||
if (conf->barrier) {
|
||||
/* Return false when nowait flag is set */
|
||||
if (nowait) {
|
||||
ret = false;
|
||||
} else {
|
||||
conf->nr_waiting++;
|
||||
mddev_add_trace_msg(conf->mddev, "raid10 wait barrier");
|
||||
wait_event_barrier(conf, stop_waiting_barrier(conf));
|
||||
conf->nr_waiting--;
|
||||
}
|
||||
conf->nr_waiting++;
|
||||
mddev_add_trace_msg(conf->mddev, "raid10 wait barrier");
|
||||
wait_event_barrier(conf, stop_waiting_barrier(conf));
|
||||
conf->nr_waiting--;
|
||||
if (!conf->nr_waiting)
|
||||
wake_up(&conf->wait_barrier);
|
||||
}
|
||||
/* Only increment nr_pending when we wait */
|
||||
if (ret)
|
||||
atomic_inc(&conf->nr_pending);
|
||||
atomic_inc(&conf->nr_pending);
|
||||
write_sequnlock_irq(&conf->resync_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void allow_barrier(struct r10conf *conf)
|
||||
@@ -1119,30 +1123,22 @@ static void raid10_unplug(struct blk_plug_cb *cb, bool from_schedule)
|
||||
* currently.
|
||||
* 2. If IO spans the reshape position. Need to wait for reshape to pass.
|
||||
*/
|
||||
static bool regular_request_wait(struct mddev *mddev, struct r10conf *conf,
|
||||
static void regular_request_wait(struct mddev *mddev, struct r10conf *conf,
|
||||
struct bio *bio, sector_t sectors)
|
||||
{
|
||||
/* Bail out if REQ_NOWAIT is set for the bio */
|
||||
if (!wait_barrier(conf, bio->bi_opf & REQ_NOWAIT)) {
|
||||
bio_wouldblock_error(bio);
|
||||
return false;
|
||||
}
|
||||
wait_barrier(conf);
|
||||
|
||||
while (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
|
||||
bio->bi_iter.bi_sector < conf->reshape_progress &&
|
||||
bio->bi_iter.bi_sector + sectors > conf->reshape_progress) {
|
||||
allow_barrier(conf);
|
||||
if (bio->bi_opf & REQ_NOWAIT) {
|
||||
bio_wouldblock_error(bio);
|
||||
return false;
|
||||
}
|
||||
mddev_add_trace_msg(conf->mddev, "raid10 wait reshape");
|
||||
wait_event(conf->wait_barrier,
|
||||
conf->reshape_progress <= bio->bi_iter.bi_sector ||
|
||||
conf->reshape_progress >= bio->bi_iter.bi_sector +
|
||||
sectors);
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void raid10_read_request(struct mddev *mddev, struct bio *bio,
|
||||
@@ -1191,10 +1187,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
|
||||
}
|
||||
}
|
||||
|
||||
if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
|
||||
free_r10bio(r10_bio);
|
||||
return;
|
||||
}
|
||||
regular_request_wait(mddev, conf, bio, r10_bio->sectors);
|
||||
|
||||
rdev = read_balance(conf, r10_bio, &max_sectors);
|
||||
if (!rdev) {
|
||||
@@ -1215,7 +1208,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
|
||||
allow_barrier(conf);
|
||||
bio = bio_submit_split_bioset(bio, max_sectors,
|
||||
&conf->bio_split);
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
if (!bio) {
|
||||
set_bit(R10BIO_Returned, &r10_bio->state);
|
||||
goto err_handle;
|
||||
@@ -1231,7 +1224,6 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
|
||||
r10_bio->master_bio = bio;
|
||||
}
|
||||
read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
|
||||
read_bio->bi_opf &= ~REQ_NOWAIT;
|
||||
|
||||
r10_bio->devs[slot].bio = read_bio;
|
||||
r10_bio->devs[slot].rdev = rdev;
|
||||
@@ -1265,7 +1257,6 @@ static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio,
|
||||
conf->mirrors[devnum].rdev;
|
||||
|
||||
mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO, &mddev->bio_set);
|
||||
mbio->bi_opf &= ~REQ_NOWAIT;
|
||||
if (replacement)
|
||||
r10_bio->devs[n_copy].repl_bio = mbio;
|
||||
else
|
||||
@@ -1344,7 +1335,7 @@ retry_wait:
|
||||
"raid10 %s wait rdev %d blocked",
|
||||
__func__, blocked_rdev->raid_disk);
|
||||
md_wait_for_blocked_rdev(blocked_rdev, mddev);
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
goto retry_wait;
|
||||
}
|
||||
}
|
||||
@@ -1356,33 +1347,20 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
|
||||
int i, k;
|
||||
sector_t sectors;
|
||||
int max_sectors;
|
||||
bool atomic = bio->bi_opf & REQ_ATOMIC;
|
||||
|
||||
if ((mddev_is_clustered(mddev) &&
|
||||
mddev->cluster_ops->area_resyncing(mddev, WRITE,
|
||||
bio->bi_iter.bi_sector,
|
||||
bio_end_sector(bio)))) {
|
||||
DEFINE_WAIT(w);
|
||||
/* Bail out if REQ_NOWAIT is set for the bio */
|
||||
if (bio->bi_opf & REQ_NOWAIT) {
|
||||
bio_wouldblock_error(bio);
|
||||
return false;
|
||||
}
|
||||
for (;;) {
|
||||
prepare_to_wait(&conf->wait_barrier,
|
||||
&w, TASK_IDLE);
|
||||
if (!mddev->cluster_ops->area_resyncing(mddev, WRITE,
|
||||
bio->bi_iter.bi_sector, bio_end_sector(bio)))
|
||||
break;
|
||||
schedule();
|
||||
}
|
||||
finish_wait(&conf->wait_barrier, &w);
|
||||
wait_event_idle(conf->wait_barrier,
|
||||
!mddev->cluster_ops->area_resyncing(mddev, WRITE,
|
||||
bio->bi_iter.bi_sector,
|
||||
bio_end_sector(bio)));
|
||||
}
|
||||
|
||||
sectors = r10_bio->sectors;
|
||||
if (!regular_request_wait(mddev, conf, bio, sectors)) {
|
||||
free_r10bio(r10_bio);
|
||||
return false;
|
||||
}
|
||||
regular_request_wait(mddev, conf, bio, sectors);
|
||||
|
||||
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
|
||||
(mddev->reshape_backwards
|
||||
@@ -1395,11 +1373,6 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
|
||||
set_mask_bits(&mddev->sb_flags, 0,
|
||||
BIT(MD_SB_CHANGE_DEVS) | BIT(MD_SB_CHANGE_PENDING));
|
||||
md_wakeup_thread(mddev->thread);
|
||||
if (bio->bi_opf & REQ_NOWAIT) {
|
||||
allow_barrier(conf);
|
||||
bio_wouldblock_error(bio);
|
||||
return false;
|
||||
}
|
||||
mddev_add_trace_msg(conf->mddev,
|
||||
"raid10 wait reshape metadata");
|
||||
wait_event(mddev->sb_wait,
|
||||
@@ -1462,16 +1435,6 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
|
||||
if (is_bad) {
|
||||
int good_sectors;
|
||||
|
||||
/*
|
||||
* We cannot atomically write this, so just
|
||||
* error in that case. It could be possible to
|
||||
* atomically write other mirrors, but the
|
||||
* complexity of supporting that is not worth
|
||||
* the benefit.
|
||||
*/
|
||||
if (bio->bi_opf & REQ_ATOMIC)
|
||||
goto err_handle;
|
||||
|
||||
good_sectors = first_bad - dev_sector;
|
||||
if (good_sectors < max_sectors)
|
||||
max_sectors = good_sectors;
|
||||
@@ -1491,10 +1454,11 @@ static bool raid10_write_request(struct mddev *mddev, struct bio *bio,
|
||||
r10_bio->sectors = max_sectors;
|
||||
|
||||
if (r10_bio->sectors < bio_sectors(bio)) {
|
||||
allow_barrier(conf);
|
||||
if (atomic)
|
||||
goto err_handle;
|
||||
|
||||
bio = bio_submit_split_bioset(bio, r10_bio->sectors,
|
||||
&conf->bio_split);
|
||||
wait_barrier(conf, false);
|
||||
if (!bio) {
|
||||
set_bit(R10BIO_Returned, &r10_bio->state);
|
||||
goto err_handle;
|
||||
@@ -1542,7 +1506,7 @@ static bool __make_request(struct mddev *mddev, struct bio *bio, int sectors)
|
||||
struct r10conf *conf = mddev->private;
|
||||
struct r10bio *r10_bio;
|
||||
|
||||
r10_bio = mempool_alloc(&conf->r10bio_pool, GFP_NOIO);
|
||||
r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
|
||||
|
||||
r10_bio->master_bio = bio;
|
||||
r10_bio->sectors = sectors;
|
||||
@@ -1577,9 +1541,11 @@ static void raid_end_discard_bio(struct r10bio *r10bio)
|
||||
free_r10bio(r10bio);
|
||||
r10bio = first_r10bio;
|
||||
} else {
|
||||
struct bio *master_bio = r10bio->master_bio;
|
||||
|
||||
md_write_end(r10bio->mddev);
|
||||
bio_endio(r10bio->master_bio);
|
||||
free_r10bio(r10bio);
|
||||
bio_endio(master_bio);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1637,11 +1603,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
|
||||
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
|
||||
return -EAGAIN;
|
||||
|
||||
if (!wait_barrier(conf, bio->bi_opf & REQ_NOWAIT)) {
|
||||
bio_wouldblock_error(bio);
|
||||
md_write_end(mddev);
|
||||
return 0;
|
||||
}
|
||||
wait_barrier(conf);
|
||||
|
||||
/*
|
||||
* Check reshape again to avoid reshape happens after checking
|
||||
@@ -1692,7 +1654,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
|
||||
allow_barrier(conf);
|
||||
/* Resend the fist split part */
|
||||
submit_bio_noacct(split);
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
}
|
||||
div_u64_rem(bio_end, stripe_size, &remainder);
|
||||
if (remainder) {
|
||||
@@ -1712,7 +1674,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
|
||||
/* Resend the second split part */
|
||||
submit_bio_noacct(bio);
|
||||
bio = split;
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
}
|
||||
|
||||
bio_start = bio->bi_iter.bi_sector;
|
||||
@@ -1742,7 +1704,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
|
||||
(last_stripe_index << geo->chunk_shift);
|
||||
|
||||
retry_discard:
|
||||
r10_bio = mempool_alloc(&conf->r10bio_pool, GFP_NOIO);
|
||||
r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
|
||||
r10_bio->mddev = mddev;
|
||||
r10_bio->state = 0;
|
||||
r10_bio->sectors = 0;
|
||||
@@ -1870,7 +1832,7 @@ retry_discard:
|
||||
end_disk_offset += geo->stride;
|
||||
atomic_inc(&first_r10bio->remaining);
|
||||
raid_end_discard_bio(r10_bio);
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
goto retry_discard;
|
||||
}
|
||||
|
||||
@@ -1886,6 +1848,7 @@ static bool raid10_make_request(struct mddev *mddev, struct bio *bio)
|
||||
{
|
||||
struct r10conf *conf = mddev->private;
|
||||
sector_t chunk_mask = (conf->geo.chunk_mask & conf->prev.chunk_mask);
|
||||
const int rw = bio_data_dir(bio);
|
||||
int chunk_sects = chunk_mask + 1;
|
||||
int sectors = bio_sectors(bio);
|
||||
|
||||
@@ -1911,6 +1874,15 @@ static bool raid10_make_request(struct mddev *mddev, struct bio *bio)
|
||||
sectors = chunk_sects -
|
||||
(bio->bi_iter.bi_sector &
|
||||
(chunk_sects - 1));
|
||||
|
||||
bio = mddev_bio_split_at_reshape_offset(mddev, bio, §ors,
|
||||
&conf->bio_split);
|
||||
if (!bio) {
|
||||
if (rw == WRITE)
|
||||
md_write_end(mddev);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!__make_request(mddev, bio, sectors))
|
||||
md_write_end(mddev);
|
||||
|
||||
@@ -2069,7 +2041,7 @@ static void print_conf(struct r10conf *conf)
|
||||
|
||||
static void close_sync(struct r10conf *conf)
|
||||
{
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
allow_barrier(conf);
|
||||
|
||||
mempool_exit(&conf->r10buf_pool);
|
||||
@@ -3365,7 +3337,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
|
||||
struct md_rdev *rdev = conf->mirrors[j].rdev;
|
||||
|
||||
if (rdev == NULL || test_bit(Faulty, &rdev->flags)) {
|
||||
still_degraded = false;
|
||||
still_degraded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3844,7 +3816,7 @@ static void raid10_free_conf(struct r10conf *conf)
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
mempool_exit(&conf->r10bio_pool);
|
||||
mempool_destroy(conf->r10bio_pool);
|
||||
kfree(conf->mirrors);
|
||||
kfree(conf->mirrors_old);
|
||||
kfree(conf->mirrors_new);
|
||||
@@ -3891,9 +3863,8 @@ static struct r10conf *setup_conf(struct mddev *mddev)
|
||||
|
||||
conf->geo = geo;
|
||||
conf->copies = copies;
|
||||
err = mempool_init(&conf->r10bio_pool, NR_RAID_BIOS, r10bio_pool_alloc,
|
||||
rbio_pool_free, conf);
|
||||
if (err)
|
||||
conf->r10bio_pool = create_r10bio_pool(conf->geo.raid_disks);
|
||||
if (!conf->r10bio_pool)
|
||||
goto out;
|
||||
|
||||
err = bioset_init(&conf->bio_split, BIO_POOL_SIZE, 0, 0);
|
||||
@@ -4284,6 +4255,10 @@ static int raid10_check_reshape(struct mddev *mddev)
|
||||
|
||||
if (conf->geo.far_copies != 1 && !conf->geo.far_offset)
|
||||
return -EINVAL;
|
||||
if (mddev->bitmap_id == ID_LLBITMAP &&
|
||||
mddev->new_chunk_sectors &&
|
||||
mddev->new_chunk_sectors < mddev->chunk_sectors)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (setup_geo(&geo, mddev, geo_start) != conf->copies)
|
||||
/* mustn't change number of copies */
|
||||
@@ -4387,9 +4362,16 @@ static int raid10_start_reshape(struct mddev *mddev)
|
||||
struct md_rdev *rdev;
|
||||
int spares = 0;
|
||||
int ret;
|
||||
mempool_t *new_pool = NULL;
|
||||
|
||||
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
|
||||
return -EBUSY;
|
||||
if (md_bitmap_enabled(mddev, false) &&
|
||||
mddev->bitmap_ops->reshape_can_start) {
|
||||
ret = mddev->bitmap_ops->reshape_can_start(mddev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (setup_geo(&new, mddev, geo_start) != conf->copies)
|
||||
return -EINVAL;
|
||||
@@ -4423,6 +4405,11 @@ static int raid10_start_reshape(struct mddev *mddev)
|
||||
return -EINVAL;
|
||||
|
||||
conf->offset_diff = min_offset_diff;
|
||||
if (mddev->delta_disks > 0) {
|
||||
new_pool = create_r10bio_pool(new.raid_disks);
|
||||
if (!new_pool)
|
||||
return -ENOMEM;
|
||||
}
|
||||
spin_lock_irq(&conf->device_lock);
|
||||
if (conf->mirrors_new) {
|
||||
memcpy(conf->mirrors_new, conf->mirrors,
|
||||
@@ -4523,6 +4510,10 @@ out:
|
||||
mddev->raid_disks = conf->geo.raid_disks;
|
||||
mddev->reshape_position = conf->reshape_progress;
|
||||
set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
|
||||
if (new_pool) {
|
||||
mempool_destroy(conf->r10bio_pool);
|
||||
conf->r10bio_pool = new_pool;
|
||||
}
|
||||
|
||||
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
|
||||
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
|
||||
@@ -4545,6 +4536,7 @@ abort:
|
||||
conf->reshape_safe = MaxSector;
|
||||
mddev->reshape_position = MaxSector;
|
||||
spin_unlock_irq(&conf->device_lock);
|
||||
mempool_destroy(new_pool);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -4702,7 +4694,14 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
|
||||
if (need_flush ||
|
||||
time_after(jiffies, conf->reshape_checkpoint + 10*HZ)) {
|
||||
/* Need to update reshape_position in metadata */
|
||||
wait_barrier(conf, false);
|
||||
wait_barrier(conf);
|
||||
if (md_bitmap_enabled(mddev, false) &&
|
||||
mddev->bitmap_ops->reshape_mark &&
|
||||
conf->reshape_safe != conf->reshape_progress) {
|
||||
mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
|
||||
conf->reshape_progress);
|
||||
mddev->bitmap_ops->unplug(mddev, true);
|
||||
}
|
||||
mddev->reshape_position = conf->reshape_progress;
|
||||
if (mddev->reshape_backwards)
|
||||
mddev->curr_resync_completed = raid10_size(mddev, 0, 0)
|
||||
@@ -4901,9 +4900,19 @@ static void reshape_request_write(struct mddev *mddev, struct r10bio *r10_bio)
|
||||
|
||||
static void end_reshape(struct r10conf *conf)
|
||||
{
|
||||
struct mddev *mddev = conf->mddev;
|
||||
|
||||
if (test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery))
|
||||
return;
|
||||
|
||||
if (md_bitmap_enabled(mddev, false) &&
|
||||
mddev->bitmap_ops->reshape_mark &&
|
||||
conf->reshape_safe != conf->reshape_progress) {
|
||||
mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
|
||||
conf->reshape_progress);
|
||||
mddev->bitmap_ops->unplug(mddev, true);
|
||||
}
|
||||
|
||||
spin_lock_irq(&conf->device_lock);
|
||||
conf->prev = conf->geo;
|
||||
md_finish_reshape(conf->mddev);
|
||||
@@ -5035,10 +5044,15 @@ static void end_reshape_request(struct r10bio *r10_bio)
|
||||
static void raid10_finish_reshape(struct mddev *mddev)
|
||||
{
|
||||
struct r10conf *conf = mddev->private;
|
||||
bool llbitmap = mddev->bitmap_id == ID_LLBITMAP &&
|
||||
md_bitmap_enabled(mddev, false);
|
||||
|
||||
if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
|
||||
return;
|
||||
|
||||
if (llbitmap && mddev->bitmap_ops->reshape_finish)
|
||||
mddev->bitmap_ops->reshape_finish(mddev);
|
||||
|
||||
if (mddev->delta_disks > 0) {
|
||||
if (mddev->resync_offset > mddev->resync_max_sectors) {
|
||||
mddev->resync_offset = mddev->resync_max_sectors;
|
||||
@@ -5065,6 +5079,15 @@ static void raid10_finish_reshape(struct mddev *mddev)
|
||||
mddev->reshape_backwards = 0;
|
||||
}
|
||||
|
||||
static sector_t raid10_bitmap_sync_size(struct mddev *mddev, bool previous)
|
||||
{
|
||||
struct r10conf *conf = mddev->private;
|
||||
|
||||
if (previous)
|
||||
return raid10_size(mddev, 0, 0);
|
||||
return raid10_size(mddev, 0, conf->geo.raid_disks);
|
||||
}
|
||||
|
||||
static struct md_personality raid10_personality =
|
||||
{
|
||||
.head = {
|
||||
@@ -5091,6 +5114,8 @@ static struct md_personality raid10_personality =
|
||||
.start_reshape = raid10_start_reshape,
|
||||
.finish_reshape = raid10_finish_reshape,
|
||||
.update_reshape_pos = raid10_update_reshape_pos,
|
||||
.bitmap_sync_size = raid10_bitmap_sync_size,
|
||||
.bitmap_array_sectors = raid10_bitmap_sync_size,
|
||||
};
|
||||
|
||||
static int __init raid10_init(void)
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ struct r10conf {
|
||||
*/
|
||||
wait_queue_head_t wait_barrier;
|
||||
|
||||
mempool_t r10bio_pool;
|
||||
mempool_t *r10bio_pool;
|
||||
mempool_t r10buf_pool;
|
||||
struct page *tmppage;
|
||||
struct bio_set bio_split;
|
||||
|
||||
@@ -643,8 +643,10 @@ static void ppl_do_flush(struct ppl_io_unit *io)
|
||||
log->disk_flush_bitmap = 0;
|
||||
|
||||
for (i = flushed_disks ; i < raid_disks; i++) {
|
||||
if (atomic_dec_and_test(&io->pending_flushes))
|
||||
if (atomic_dec_and_test(&io->pending_flushes)) {
|
||||
ppl_io_unit_finished(io);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+246
-72
@@ -801,7 +801,7 @@ static bool is_inactive_blocked(struct r5conf *conf, int hash)
|
||||
return true;
|
||||
|
||||
return (atomic_read(&conf->active_stripes) <
|
||||
(conf->max_nr_stripes * 3 / 4));
|
||||
(READ_ONCE(conf->max_nr_stripes) * 3 / 4));
|
||||
}
|
||||
|
||||
struct stripe_head *raid5_get_active_stripe(struct r5conf *conf,
|
||||
@@ -2471,11 +2471,6 @@ static int scribble_alloc(struct raid5_percpu *percpu,
|
||||
sizeof(unsigned int) * (num + 2);
|
||||
void *scribble;
|
||||
|
||||
/*
|
||||
* If here is in raid array suspend context, it is in memalloc noio
|
||||
* context as well, there is no potential recursive memory reclaim
|
||||
* I/Os with the GFP_KERNEL flag.
|
||||
*/
|
||||
scribble = kvmalloc_array(cnt, obj_size, GFP_KERNEL);
|
||||
if (!scribble)
|
||||
return -ENOMEM;
|
||||
@@ -2490,6 +2485,7 @@ static int scribble_alloc(struct raid5_percpu *percpu,
|
||||
static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
|
||||
{
|
||||
unsigned long cpu;
|
||||
unsigned int noio_flags;
|
||||
int err = 0;
|
||||
|
||||
/* Never shrink. */
|
||||
@@ -2498,6 +2494,7 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
|
||||
return 0;
|
||||
|
||||
raid5_quiesce(conf->mddev, true);
|
||||
noio_flags = memalloc_noio_save();
|
||||
cpus_read_lock();
|
||||
|
||||
for_each_present_cpu(cpu) {
|
||||
@@ -2511,6 +2508,7 @@ static int resize_chunks(struct r5conf *conf, int new_disks, int new_sectors)
|
||||
}
|
||||
|
||||
cpus_read_unlock();
|
||||
memalloc_noio_restore(noio_flags);
|
||||
raid5_quiesce(conf->mddev, false);
|
||||
|
||||
if (!err) {
|
||||
@@ -2785,6 +2783,7 @@ static void raid5_end_read_request(struct bio * bi)
|
||||
} else {
|
||||
int retry = 0;
|
||||
int set_bad = 0;
|
||||
int max_nr_stripes = READ_ONCE(conf->max_nr_stripes);
|
||||
|
||||
clear_bit(R5_UPTODATE, &sh->dev[i].flags);
|
||||
if (!(bi->bi_status == BLK_STS_PROTECTION))
|
||||
@@ -2810,13 +2809,12 @@ static void raid5_end_read_request(struct bio * bi)
|
||||
mdname(conf->mddev),
|
||||
(unsigned long long)s,
|
||||
rdev->bdev);
|
||||
} else if (atomic_read(&rdev->read_errors)
|
||||
> conf->max_nr_stripes) {
|
||||
} else if (atomic_read(&rdev->read_errors) > max_nr_stripes) {
|
||||
if (!test_bit(Faulty, &rdev->flags)) {
|
||||
pr_warn("md/raid:%s: %d read_errors > %d stripes\n",
|
||||
mdname(conf->mddev),
|
||||
atomic_read(&rdev->read_errors),
|
||||
conf->max_nr_stripes);
|
||||
max_nr_stripes);
|
||||
pr_warn("md/raid:%s: Too many read errors, failing device %pg.\n",
|
||||
mdname(conf->mddev), rdev->bdev);
|
||||
}
|
||||
@@ -3553,7 +3551,7 @@ static void __add_stripe_bio(struct stripe_head *sh, struct bio *bi,
|
||||
sh->dev[dd_idx].sector);
|
||||
|
||||
if (conf->mddev->bitmap && firstwrite && !sh->batch_head) {
|
||||
sh->bm_seq = conf->seq_flush+1;
|
||||
sh->bm_seq = READ_ONCE(conf->seq_flush) + 1;
|
||||
set_bit(STRIPE_BIT_DELAY, &sh->state);
|
||||
}
|
||||
}
|
||||
@@ -3745,17 +3743,89 @@ handle_failed_sync(struct r5conf *conf, struct stripe_head *sh,
|
||||
md_sync_error(conf->mddev);
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_failed_reshape - handle failed stripes when reshape failed and
|
||||
* degraded devices >= max_degraded
|
||||
*
|
||||
* handle following kinds of stripe:
|
||||
* 1. cleanup the following kinds of destination stripe:
|
||||
* - new regions past the old end of the array, zero-filled in place,
|
||||
* requires no source data.
|
||||
* (STRIPE_EXPANDING | STRIPE_EXPAND_READY)
|
||||
* - prepare source data chunks already done, and writeout failed
|
||||
* (STRIPE_EXPAND_READY)
|
||||
* 2. dest stripes that need source data (STRIPE_EXPANDING, no STRIPE_HANDLE)
|
||||
* - these kind of stripes sit idle in the stripe cache and are never seen
|
||||
* by handle_stripe(). So clean up indirectly when their source stripe
|
||||
* (type 3) is processed.
|
||||
* 3. src stripes (STRIPE_EXPAND_SOURCE)
|
||||
* - hit handle_stripe() after their member disks are marked Faulty.
|
||||
* - clear STRIPE_EXPAND_SOURCE, finds and cleanup all dependent destination
|
||||
* stripes that were waiting for data.
|
||||
* - walks the source's data disks, compute the corresponding destination
|
||||
* sector, looks up the destination stripe, and do cleanup(clear flags,
|
||||
* dec counters, call md_done_sync())
|
||||
*/
|
||||
static void handle_failed_reshape(struct r5conf *conf, struct stripe_head *sh,
|
||||
struct stripe_head_state *s)
|
||||
{
|
||||
int i;
|
||||
bool was_expanding = test_and_clear_bit(STRIPE_EXPANDING, &sh->state);
|
||||
bool was_ready = test_and_clear_bit(STRIPE_EXPAND_READY, &sh->state);
|
||||
|
||||
if (was_expanding || was_ready) {
|
||||
atomic_dec(&conf->reshape_stripes);
|
||||
wake_up(&conf->wait_for_reshape);
|
||||
md_done_sync(conf->mddev, RAID5_STRIPE_SECTORS(conf));
|
||||
}
|
||||
|
||||
s->expanded = 0;
|
||||
s->expanding = 0;
|
||||
|
||||
/* release the destination stripes that are waiting to be filled */
|
||||
if (test_and_clear_bit(STRIPE_EXPAND_SOURCE, &sh->state)) {
|
||||
for (i = 0; i < sh->disks; i++) {
|
||||
int dd_idx;
|
||||
struct stripe_head *sh2;
|
||||
sector_t bn, sec;
|
||||
|
||||
if (i == sh->pd_idx)
|
||||
continue;
|
||||
if (conf->level == 6 && i == sh->qd_idx)
|
||||
continue;
|
||||
|
||||
bn = raid5_compute_blocknr(sh, i, 1);
|
||||
sec = raid5_compute_sector(conf, bn, 0, &dd_idx, NULL);
|
||||
sh2 = raid5_get_active_stripe(conf, NULL, sec,
|
||||
R5_GAS_NOBLOCK |
|
||||
R5_GAS_NOQUIESCE);
|
||||
if (!sh2)
|
||||
continue;
|
||||
|
||||
if (test_and_clear_bit(STRIPE_EXPANDING, &sh2->state)) {
|
||||
atomic_dec(&conf->reshape_stripes);
|
||||
wake_up(&conf->wait_for_reshape);
|
||||
md_done_sync(conf->mddev,
|
||||
RAID5_STRIPE_SECTORS(conf));
|
||||
}
|
||||
|
||||
clear_bit(STRIPE_EXPAND_READY, &sh2->state);
|
||||
|
||||
raid5_release_stripe(sh2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int want_replace(struct stripe_head *sh, int disk_idx)
|
||||
{
|
||||
struct md_rdev *rdev;
|
||||
int rv = 0;
|
||||
|
||||
rdev = sh->raid_conf->disks[disk_idx].replacement;
|
||||
if (rdev
|
||||
&& !test_bit(Faulty, &rdev->flags)
|
||||
&& !test_bit(In_sync, &rdev->flags)
|
||||
&& (rdev->recovery_offset <= sh->sector
|
||||
|| rdev->mddev->resync_offset <= sh->sector))
|
||||
if (rdev && !test_bit(Faulty, &rdev->flags) &&
|
||||
!test_bit(In_sync, &rdev->flags) &&
|
||||
(READ_ONCE(rdev->recovery_offset) <= sh->sector ||
|
||||
rdev->mddev->resync_offset <= sh->sector))
|
||||
rv = 1;
|
||||
return rv;
|
||||
}
|
||||
@@ -4672,7 +4742,8 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
|
||||
*/
|
||||
rdev = conf->disks[i].replacement;
|
||||
if (rdev && !test_bit(Faulty, &rdev->flags) &&
|
||||
rdev->recovery_offset >= sh->sector + RAID5_STRIPE_SECTORS(conf) &&
|
||||
READ_ONCE(rdev->recovery_offset) >=
|
||||
sh->sector + RAID5_STRIPE_SECTORS(conf) &&
|
||||
!rdev_has_badblock(rdev, sh->sector,
|
||||
RAID5_STRIPE_SECTORS(conf)))
|
||||
set_bit(R5_ReadRepl, &dev->flags);
|
||||
@@ -4714,7 +4785,7 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
|
||||
} else if (test_bit(In_sync, &rdev->flags))
|
||||
set_bit(R5_Insync, &dev->flags);
|
||||
else if (sh->sector + RAID5_STRIPE_SECTORS(conf) <=
|
||||
rdev->recovery_offset) {
|
||||
READ_ONCE(rdev->recovery_offset)) {
|
||||
/*
|
||||
* in sync if:
|
||||
* - normal IO, or
|
||||
@@ -5025,6 +5096,8 @@ static void handle_stripe(struct stripe_head *sh)
|
||||
handle_failed_stripe(conf, sh, &s, disks);
|
||||
if (s.syncing + s.replacing)
|
||||
handle_failed_sync(conf, sh, &s);
|
||||
if (s.expanding + s.expanded)
|
||||
handle_failed_reshape(conf, sh, &s);
|
||||
}
|
||||
|
||||
/* Now we check to see if any write operations have recently
|
||||
@@ -5458,13 +5531,13 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)
|
||||
|
||||
rdev = conf->disks[dd_idx].replacement;
|
||||
if (!rdev || test_bit(Faulty, &rdev->flags) ||
|
||||
rdev->recovery_offset < end_sector) {
|
||||
READ_ONCE(rdev->recovery_offset) < end_sector) {
|
||||
rdev = conf->disks[dd_idx].rdev;
|
||||
if (!rdev)
|
||||
return 0;
|
||||
if (test_bit(Faulty, &rdev->flags) ||
|
||||
!(test_bit(In_sync, &rdev->flags) ||
|
||||
rdev->recovery_offset >= end_sector))
|
||||
READ_ONCE(rdev->recovery_offset) >= end_sector))
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5718,13 +5791,8 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
|
||||
struct bio *orig_bi = bi;
|
||||
int stripe_sectors;
|
||||
|
||||
/* We need to handle this when io_uring supports discard/trim */
|
||||
if (WARN_ON_ONCE(bi->bi_opf & REQ_NOWAIT))
|
||||
return;
|
||||
|
||||
if (mddev->reshape_position != MaxSector)
|
||||
/* Skip discard while reshape is happening */
|
||||
return;
|
||||
goto complete_again;
|
||||
|
||||
if (!raid5_discard_limits(mddev, bi))
|
||||
return;
|
||||
@@ -5799,7 +5867,7 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
|
||||
}
|
||||
spin_unlock_irq(&sh->stripe_lock);
|
||||
if (conf->mddev->bitmap) {
|
||||
sh->bm_seq = conf->seq_flush + 1;
|
||||
sh->bm_seq = READ_ONCE(conf->seq_flush) + 1;
|
||||
set_bit(STRIPE_BIT_DELAY, &sh->state);
|
||||
}
|
||||
|
||||
@@ -5811,6 +5879,11 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
|
||||
}
|
||||
|
||||
bio_endio(bi);
|
||||
return;
|
||||
|
||||
complete_again:
|
||||
/* Skip discard while reshape is happening */
|
||||
bio_endio_status(bi, BLK_STS_AGAIN);
|
||||
}
|
||||
|
||||
static bool ahead_of_reshape(struct mddev *mddev, sector_t sector,
|
||||
@@ -5942,6 +6015,34 @@ static enum reshape_loc get_reshape_loc(struct mddev *mddev,
|
||||
return LOC_BEHIND_RESHAPE;
|
||||
}
|
||||
|
||||
static void raid5_bitmap_sector_map(struct mddev *mddev, sector_t *offset,
|
||||
unsigned long *sectors,
|
||||
bool previous)
|
||||
{
|
||||
struct r5conf *conf = mddev->private;
|
||||
sector_t start = *offset;
|
||||
sector_t end = start + *sectors;
|
||||
int sectors_per_chunk;
|
||||
int dd_idx;
|
||||
|
||||
if (previous)
|
||||
sectors_per_chunk = conf->prev_chunk_sectors *
|
||||
(conf->previous_raid_disks - conf->max_degraded);
|
||||
else
|
||||
sectors_per_chunk = conf->chunk_sectors *
|
||||
(conf->raid_disks - conf->max_degraded);
|
||||
sector_div(start, sectors_per_chunk);
|
||||
start *= sectors_per_chunk;
|
||||
if (sector_div(end, sectors_per_chunk))
|
||||
end++;
|
||||
end *= sectors_per_chunk;
|
||||
|
||||
start = raid5_compute_sector(conf, start, previous, &dd_idx, NULL);
|
||||
end = raid5_compute_sector(conf, end, previous, &dd_idx, NULL);
|
||||
*offset = start;
|
||||
*sectors = end - start;
|
||||
}
|
||||
|
||||
static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
|
||||
unsigned long *sectors)
|
||||
{
|
||||
@@ -5949,18 +6050,11 @@ static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
|
||||
sector_t start = *offset;
|
||||
sector_t end = start + *sectors;
|
||||
sector_t prev_start = start;
|
||||
sector_t prev_end = end;
|
||||
int sectors_per_chunk;
|
||||
unsigned long prev_sectors = end - start;
|
||||
enum reshape_loc loc;
|
||||
int dd_idx;
|
||||
|
||||
sectors_per_chunk = conf->chunk_sectors *
|
||||
(conf->raid_disks - conf->max_degraded);
|
||||
start = round_down(start, sectors_per_chunk);
|
||||
end = round_up(end, sectors_per_chunk);
|
||||
|
||||
start = raid5_compute_sector(conf, start, 0, &dd_idx, NULL);
|
||||
end = raid5_compute_sector(conf, end, 0, &dd_idx, NULL);
|
||||
raid5_bitmap_sector_map(mddev, &start, sectors, false);
|
||||
end = start + *sectors;
|
||||
|
||||
/*
|
||||
* For LOC_INSIDE_RESHAPE, this IO will wait for reshape to make
|
||||
@@ -5969,17 +6063,10 @@ static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
|
||||
loc = get_reshape_loc(mddev, conf, prev_start);
|
||||
if (likely(loc != LOC_AHEAD_OF_RESHAPE)) {
|
||||
*offset = start;
|
||||
*sectors = end - start;
|
||||
return;
|
||||
}
|
||||
|
||||
sectors_per_chunk = conf->prev_chunk_sectors *
|
||||
(conf->previous_raid_disks - conf->max_degraded);
|
||||
prev_start = round_down(prev_start, sectors_per_chunk);
|
||||
prev_end = round_down(prev_end, sectors_per_chunk);
|
||||
|
||||
prev_start = raid5_compute_sector(conf, prev_start, 1, &dd_idx, NULL);
|
||||
prev_end = raid5_compute_sector(conf, prev_end, 1, &dd_idx, NULL);
|
||||
raid5_bitmap_sector_map(mddev, &prev_start, &prev_sectors, true);
|
||||
|
||||
/*
|
||||
* for LOC_AHEAD_OF_RESHAPE, reshape can make progress before this IO
|
||||
@@ -5987,7 +6074,7 @@ static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
|
||||
* we set bits for both.
|
||||
*/
|
||||
*offset = min(start, prev_start);
|
||||
*sectors = max(end, prev_end) - *offset;
|
||||
*sectors = max(end, prev_start + prev_sectors) - *offset;
|
||||
}
|
||||
|
||||
static enum stripe_result make_stripe_request(struct mddev *mddev,
|
||||
@@ -6134,9 +6221,11 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
|
||||
struct r5conf *conf = mddev->private;
|
||||
const int rw = bio_data_dir(bi);
|
||||
struct stripe_request_ctx *ctx;
|
||||
struct bio *front_bio;
|
||||
sector_t logical_sector;
|
||||
enum stripe_result res;
|
||||
int s, stripe_cnt;
|
||||
bool split = false;
|
||||
bool on_wq;
|
||||
|
||||
if (unlikely(bi->bi_opf & REQ_PREFLUSH)) {
|
||||
@@ -6170,6 +6259,18 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
|
||||
return true;
|
||||
}
|
||||
|
||||
front_bio = bi;
|
||||
bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
|
||||
&conf->bio_split);
|
||||
if (!bi) {
|
||||
if (rw == WRITE)
|
||||
md_write_end(mddev);
|
||||
return true;
|
||||
}
|
||||
if (bi != front_bio)
|
||||
split = true;
|
||||
front_bio = bi;
|
||||
|
||||
logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
|
||||
bi->bi_next = NULL;
|
||||
|
||||
@@ -6191,15 +6292,6 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
|
||||
pr_debug("raid456: %s, logical %llu to %llu\n", __func__,
|
||||
bi->bi_iter.bi_sector, ctx->last_sector);
|
||||
|
||||
/* Bail out if conflicts with reshape and REQ_NOWAIT is set */
|
||||
if ((bi->bi_opf & REQ_NOWAIT) &&
|
||||
get_reshape_loc(mddev, conf, logical_sector) == LOC_INSIDE_RESHAPE) {
|
||||
bio_wouldblock_error(bi);
|
||||
if (rw == WRITE)
|
||||
md_write_end(mddev);
|
||||
mempool_free(ctx, conf->ctx_pool);
|
||||
return true;
|
||||
}
|
||||
md_account_bio(mddev, &bi);
|
||||
|
||||
/*
|
||||
@@ -6270,6 +6362,11 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
|
||||
bio_endio(bi);
|
||||
|
||||
wait_for_completion(&done);
|
||||
front_bio->bi_status = BLK_STS_OK;
|
||||
if (split) {
|
||||
submit_bio_noacct(front_bio);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6419,6 +6516,13 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
|
||||
|| test_bit(MD_RECOVERY_INTR, &mddev->recovery));
|
||||
if (atomic_read(&conf->reshape_stripes) != 0)
|
||||
return 0;
|
||||
if (md_bitmap_enabled(mddev, false) &&
|
||||
mddev->bitmap_ops->reshape_mark &&
|
||||
conf->reshape_safe != conf->reshape_progress) {
|
||||
mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
|
||||
conf->reshape_progress);
|
||||
mddev->bitmap_ops->unplug(mddev, true);
|
||||
}
|
||||
mddev->reshape_position = conf->reshape_progress;
|
||||
mddev->curr_resync_completed = sector_nr;
|
||||
if (!mddev->reshape_backwards)
|
||||
@@ -6427,8 +6531,8 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
|
||||
if (rdev->raid_disk >= 0 &&
|
||||
!test_bit(Journal, &rdev->flags) &&
|
||||
!test_bit(In_sync, &rdev->flags) &&
|
||||
rdev->recovery_offset < sector_nr)
|
||||
rdev->recovery_offset = sector_nr;
|
||||
READ_ONCE(rdev->recovery_offset) < sector_nr)
|
||||
WRITE_ONCE(rdev->recovery_offset, sector_nr);
|
||||
|
||||
conf->reshape_checkpoint = jiffies;
|
||||
set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
|
||||
@@ -6528,6 +6632,13 @@ finish:
|
||||
|| test_bit(MD_RECOVERY_INTR, &mddev->recovery));
|
||||
if (atomic_read(&conf->reshape_stripes) != 0)
|
||||
goto ret;
|
||||
if (md_bitmap_enabled(mddev, false) &&
|
||||
mddev->bitmap_ops->reshape_mark &&
|
||||
conf->reshape_safe != conf->reshape_progress) {
|
||||
mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
|
||||
conf->reshape_progress);
|
||||
mddev->bitmap_ops->unplug(mddev, true);
|
||||
}
|
||||
mddev->reshape_position = conf->reshape_progress;
|
||||
mddev->curr_resync_completed = sector_nr;
|
||||
if (!mddev->reshape_backwards)
|
||||
@@ -6536,8 +6647,8 @@ finish:
|
||||
if (rdev->raid_disk >= 0 &&
|
||||
!test_bit(Journal, &rdev->flags) &&
|
||||
!test_bit(In_sync, &rdev->flags) &&
|
||||
rdev->recovery_offset < sector_nr)
|
||||
rdev->recovery_offset = sector_nr;
|
||||
READ_ONCE(rdev->recovery_offset) < sector_nr)
|
||||
WRITE_ONCE(rdev->recovery_offset, sector_nr);
|
||||
conf->reshape_checkpoint = jiffies;
|
||||
set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
|
||||
md_wakeup_thread(mddev->thread);
|
||||
@@ -6849,12 +6960,14 @@ static void raid5d(struct md_thread *thread)
|
||||
if (
|
||||
!list_empty(&conf->bitmap_list)) {
|
||||
/* Now is a good time to flush some bitmap updates */
|
||||
conf->seq_flush++;
|
||||
int seq = conf->seq_flush + 1;
|
||||
|
||||
WRITE_ONCE(conf->seq_flush, seq);
|
||||
spin_unlock_irq(&conf->device_lock);
|
||||
if (md_bitmap_enabled(mddev, true))
|
||||
mddev->bitmap_ops->unplug(mddev, true);
|
||||
spin_lock_irq(&conf->device_lock);
|
||||
conf->seq_write = conf->seq_flush;
|
||||
conf->seq_write = seq;
|
||||
activate_bit_delay(conf, conf->temp_inactive_list);
|
||||
}
|
||||
raid5_activate_delayed(conf);
|
||||
@@ -7039,6 +7152,7 @@ raid5_store_stripe_size(struct mddev *mddev, const char *page, size_t len)
|
||||
{
|
||||
struct r5conf *conf;
|
||||
unsigned long new;
|
||||
unsigned int noio_flags = 0;
|
||||
int err;
|
||||
int size;
|
||||
|
||||
@@ -7079,6 +7193,7 @@ raid5_store_stripe_size(struct mddev *mddev, const char *page, size_t len)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
noio_flags = memalloc_noio_save();
|
||||
mutex_lock(&conf->cache_size_mutex);
|
||||
size = conf->max_nr_stripes;
|
||||
|
||||
@@ -7095,6 +7210,7 @@ raid5_store_stripe_size(struct mddev *mddev, const char *page, size_t len)
|
||||
mutex_unlock(&conf->cache_size_mutex);
|
||||
|
||||
out_unlock:
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
return err ?: len;
|
||||
}
|
||||
@@ -8056,9 +8172,9 @@ static int raid5_run(struct mddev *mddev)
|
||||
/* Hack because v0.91 doesn't store recovery_offset properly. */
|
||||
if (mddev->major_version == 0 &&
|
||||
mddev->minor_version > 90)
|
||||
rdev->recovery_offset = reshape_offset;
|
||||
WRITE_ONCE(rdev->recovery_offset, reshape_offset);
|
||||
|
||||
if (rdev->recovery_offset < reshape_offset) {
|
||||
if (READ_ONCE(rdev->recovery_offset) < reshape_offset) {
|
||||
/* We need to check old and new layout */
|
||||
if (!only_parity(rdev->raid_disk,
|
||||
conf->algorithm,
|
||||
@@ -8213,10 +8329,10 @@ static int raid5_spare_active(struct mddev *mddev)
|
||||
for (i = 0; i < conf->raid_disks; i++) {
|
||||
rdev = conf->disks[i].rdev;
|
||||
replacement = conf->disks[i].replacement;
|
||||
if (replacement
|
||||
&& replacement->recovery_offset == MaxSector
|
||||
&& !test_bit(Faulty, &replacement->flags)
|
||||
&& !test_and_set_bit(In_sync, &replacement->flags)) {
|
||||
if (replacement &&
|
||||
READ_ONCE(replacement->recovery_offset) == MaxSector &&
|
||||
!test_bit(Faulty, &replacement->flags) &&
|
||||
!test_and_set_bit(In_sync, &replacement->flags)) {
|
||||
/* Replacement has just become active. */
|
||||
if (!rdev
|
||||
|| !test_and_clear_bit(In_sync, &rdev->flags))
|
||||
@@ -8231,10 +8347,10 @@ static int raid5_spare_active(struct mddev *mddev)
|
||||
rdev->sysfs_state);
|
||||
}
|
||||
sysfs_notify_dirent_safe(replacement->sysfs_state);
|
||||
} else if (rdev
|
||||
&& rdev->recovery_offset == MaxSector
|
||||
&& !test_bit(Faulty, &rdev->flags)
|
||||
&& !test_and_set_bit(In_sync, &rdev->flags)) {
|
||||
} else if (rdev &&
|
||||
READ_ONCE(rdev->recovery_offset) == MaxSector &&
|
||||
!test_bit(Faulty, &rdev->flags) &&
|
||||
!test_and_set_bit(In_sync, &rdev->flags)) {
|
||||
count++;
|
||||
sysfs_notify_dirent_safe(rdev->sysfs_state);
|
||||
}
|
||||
@@ -8479,6 +8595,8 @@ static int check_reshape(struct mddev *mddev)
|
||||
return 0; /* nothing to do */
|
||||
if (has_failed(conf))
|
||||
return -EINVAL;
|
||||
if (!mddev->new_chunk_sectors)
|
||||
return -EINVAL;
|
||||
if (mddev->delta_disks < 0 && mddev->reshape_position == MaxSector) {
|
||||
/* We might be able to shrink, but the devices must
|
||||
* be made bigger first.
|
||||
@@ -8495,6 +8613,9 @@ static int check_reshape(struct mddev *mddev)
|
||||
if (!check_stripe_cache(mddev))
|
||||
return -ENOSPC;
|
||||
|
||||
if (mddev->bitmap_id == ID_LLBITMAP &&
|
||||
mddev->new_chunk_sectors < mddev->chunk_sectors)
|
||||
return -EOPNOTSUPP;
|
||||
if (mddev->new_chunk_sectors > mddev->chunk_sectors ||
|
||||
mddev->delta_disks > 0)
|
||||
if (resize_chunks(conf,
|
||||
@@ -8522,6 +8643,9 @@ static int raid5_start_reshape(struct mddev *mddev)
|
||||
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
|
||||
return -EBUSY;
|
||||
|
||||
if (!mddev->new_chunk_sectors)
|
||||
return -EINVAL;
|
||||
|
||||
if (!check_stripe_cache(mddev))
|
||||
return -ENOSPC;
|
||||
|
||||
@@ -8557,6 +8681,12 @@ static int raid5_start_reshape(struct mddev *mddev)
|
||||
mdname(mddev));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (md_bitmap_enabled(mddev, false) &&
|
||||
mddev->bitmap_id == ID_LLBITMAP) {
|
||||
i = mddev->bitmap_ops->resize(mddev, mddev->dev_sectors, 0);
|
||||
if (i)
|
||||
return i;
|
||||
}
|
||||
|
||||
atomic_set(&conf->reshape_stripes, 0);
|
||||
spin_lock_irq(&conf->device_lock);
|
||||
@@ -8603,7 +8733,7 @@ static int raid5_start_reshape(struct mddev *mddev)
|
||||
>= conf->previous_raid_disks)
|
||||
set_bit(In_sync, &rdev->flags);
|
||||
else
|
||||
rdev->recovery_offset = 0;
|
||||
WRITE_ONCE(rdev->recovery_offset, 0);
|
||||
|
||||
/* Failure here is OK */
|
||||
sysfs_link_rdev(mddev, rdev);
|
||||
@@ -8641,10 +8771,19 @@ static int raid5_start_reshape(struct mddev *mddev)
|
||||
*/
|
||||
static void end_reshape(struct r5conf *conf)
|
||||
{
|
||||
struct mddev *mddev = conf->mddev;
|
||||
|
||||
if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
|
||||
struct md_rdev *rdev;
|
||||
|
||||
if (md_bitmap_enabled(mddev, false) &&
|
||||
mddev->bitmap_ops->reshape_mark &&
|
||||
conf->reshape_safe != conf->reshape_progress) {
|
||||
mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
|
||||
conf->reshape_progress);
|
||||
mddev->bitmap_ops->unplug(mddev, true);
|
||||
}
|
||||
|
||||
spin_lock_irq(&conf->device_lock);
|
||||
conf->previous_raid_disks = conf->raid_disks;
|
||||
md_finish_reshape(conf->mddev);
|
||||
@@ -8655,7 +8794,7 @@ static void end_reshape(struct r5conf *conf)
|
||||
if (rdev->raid_disk >= 0 &&
|
||||
!test_bit(Journal, &rdev->flags) &&
|
||||
!test_bit(In_sync, &rdev->flags))
|
||||
rdev->recovery_offset = MaxSector;
|
||||
WRITE_ONCE(rdev->recovery_offset, MaxSector);
|
||||
spin_unlock_irq(&conf->device_lock);
|
||||
wake_up(&conf->wait_for_reshape);
|
||||
|
||||
@@ -8671,8 +8810,16 @@ static void raid5_finish_reshape(struct mddev *mddev)
|
||||
{
|
||||
struct r5conf *conf = mddev->private;
|
||||
struct md_rdev *rdev;
|
||||
bool llbitmap = mddev->bitmap_id == ID_LLBITMAP &&
|
||||
md_bitmap_enabled(mddev, false);
|
||||
|
||||
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
|
||||
if (llbitmap && mddev->bitmap_ops->reshape_finish)
|
||||
mddev->bitmap_ops->reshape_finish(mddev);
|
||||
if (llbitmap) {
|
||||
mddev->resync_offset = 0;
|
||||
mddev->resync_max_sectors = mddev->dev_sectors;
|
||||
}
|
||||
|
||||
if (mddev->delta_disks <= 0) {
|
||||
int d;
|
||||
@@ -8975,6 +9122,7 @@ static void *raid6_takeover(struct mddev *mddev)
|
||||
static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf)
|
||||
{
|
||||
struct r5conf *conf;
|
||||
unsigned int noio_flags;
|
||||
int err;
|
||||
|
||||
err = mddev_suspend_and_lock(mddev);
|
||||
@@ -8986,6 +9134,7 @@ static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
noio_flags = memalloc_noio_save();
|
||||
if (strncmp(buf, "ppl", 3) == 0) {
|
||||
/* ppl only works with RAID 5 */
|
||||
if (!raid5_has_ppl(conf) && conf->level == 5) {
|
||||
@@ -9025,6 +9174,7 @@ static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf)
|
||||
if (!err)
|
||||
md_update_sb(mddev, 1);
|
||||
|
||||
memalloc_noio_restore(noio_flags);
|
||||
mddev_unlock_and_resume(mddev);
|
||||
|
||||
return err;
|
||||
@@ -9049,6 +9199,21 @@ static void raid5_prepare_suspend(struct mddev *mddev)
|
||||
wake_up(&conf->wait_for_reshape);
|
||||
}
|
||||
|
||||
static sector_t raid5_bitmap_sync_size(struct mddev *mddev, bool previous)
|
||||
{
|
||||
return mddev->dev_sectors;
|
||||
}
|
||||
|
||||
static sector_t raid5_bitmap_array_sectors(struct mddev *mddev, bool previous)
|
||||
{
|
||||
struct r5conf *conf = mddev->private;
|
||||
|
||||
if (previous)
|
||||
return raid5_size(mddev, mddev->dev_sectors,
|
||||
conf->previous_raid_disks);
|
||||
return raid5_size(mddev, mddev->dev_sectors, conf->raid_disks);
|
||||
}
|
||||
|
||||
static struct md_personality raid6_personality =
|
||||
{
|
||||
.head = {
|
||||
@@ -9078,6 +9243,9 @@ static struct md_personality raid6_personality =
|
||||
.change_consistency_policy = raid5_change_consistency_policy,
|
||||
.prepare_suspend = raid5_prepare_suspend,
|
||||
.bitmap_sector = raid5_bitmap_sector,
|
||||
.bitmap_sector_map = raid5_bitmap_sector_map,
|
||||
.bitmap_sync_size = raid5_bitmap_sync_size,
|
||||
.bitmap_array_sectors = raid5_bitmap_array_sectors,
|
||||
};
|
||||
static struct md_personality raid5_personality =
|
||||
{
|
||||
@@ -9108,6 +9276,9 @@ static struct md_personality raid5_personality =
|
||||
.change_consistency_policy = raid5_change_consistency_policy,
|
||||
.prepare_suspend = raid5_prepare_suspend,
|
||||
.bitmap_sector = raid5_bitmap_sector,
|
||||
.bitmap_sector_map = raid5_bitmap_sector_map,
|
||||
.bitmap_sync_size = raid5_bitmap_sync_size,
|
||||
.bitmap_array_sectors = raid5_bitmap_array_sectors,
|
||||
};
|
||||
|
||||
static struct md_personality raid4_personality =
|
||||
@@ -9139,6 +9310,9 @@ static struct md_personality raid4_personality =
|
||||
.change_consistency_policy = raid5_change_consistency_policy,
|
||||
.prepare_suspend = raid5_prepare_suspend,
|
||||
.bitmap_sector = raid5_bitmap_sector,
|
||||
.bitmap_sector_map = raid5_bitmap_sector_map,
|
||||
.bitmap_sync_size = raid5_bitmap_sync_size,
|
||||
.bitmap_array_sectors = raid5_bitmap_array_sectors,
|
||||
};
|
||||
|
||||
static int __init raid5_init(void)
|
||||
|
||||
@@ -1163,7 +1163,7 @@ static int btt_rw_integrity(struct btt *btt, struct bio_integrity_payload *bip,
|
||||
bv = bvec_iter_bvec(bip->bip_vec, bip->bip_iter);
|
||||
/*
|
||||
* The 'bv' obtained from bvec_iter_bvec has its .bv_len and
|
||||
* .bv_offset already adjusted for iter->bi_bvec_done, and we
|
||||
* .bv_offset already adjusted for iter->bi_offset, and we
|
||||
* can use those directly
|
||||
*/
|
||||
|
||||
|
||||
@@ -692,8 +692,7 @@ int nvme_auth_derive_tls_psk(int hmac_id, const u8 *psk, size_t psk_len,
|
||||
const char *psk_digest, u8 **ret_psk)
|
||||
{
|
||||
static const u8 default_salt[NVME_AUTH_MAX_DIGEST_SIZE];
|
||||
static const char label[] = "tls13 nvme-tls-psk";
|
||||
const size_t label_len = sizeof(label) - 1;
|
||||
static const char label[18] = "tls13 nvme-tls-psk";
|
||||
u8 prk[NVME_AUTH_MAX_DIGEST_SIZE];
|
||||
size_t hash_len, ctx_len;
|
||||
u8 *hmac_data = NULL, *tls_key;
|
||||
@@ -729,7 +728,7 @@ int nvme_auth_derive_tls_psk(int hmac_id, const u8 *psk, size_t psk_len,
|
||||
*/
|
||||
|
||||
hmac_data = kmalloc(/* output length */ 2 +
|
||||
/* label */ 1 + label_len +
|
||||
/* label */ 1 + sizeof(label) +
|
||||
/* context (max) */ 1 + 3 + 1 + strlen(psk_digest) +
|
||||
/* counter */ 1,
|
||||
GFP_KERNEL);
|
||||
@@ -743,10 +742,10 @@ int nvme_auth_derive_tls_psk(int hmac_id, const u8 *psk, size_t psk_len,
|
||||
hmac_data[i++] = hash_len;
|
||||
|
||||
/* label */
|
||||
static_assert(label_len <= 255);
|
||||
hmac_data[i] = label_len;
|
||||
memcpy(&hmac_data[i + 1], label, label_len);
|
||||
i += 1 + label_len;
|
||||
static_assert(sizeof(label) <= 255);
|
||||
hmac_data[i] = sizeof(label);
|
||||
memcpy(&hmac_data[i + 1], label, sizeof(label));
|
||||
i += 1 + sizeof(label);
|
||||
|
||||
/* context */
|
||||
ctx_len = sprintf(&hmac_data[i + 1], "%02d %s", hmac_id, psk_digest);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
ccflags-y += -I$(src)
|
||||
|
||||
obj-$(CONFIG_NVME_CORE) += nvme-core.o
|
||||
|
||||
+39
-27
@@ -47,9 +47,6 @@
|
||||
#define APPLE_ANS_BOOT_STATUS 0x1300
|
||||
#define APPLE_ANS_BOOT_STATUS_OK 0xde71ce55
|
||||
|
||||
#define APPLE_ANS_UNKNOWN_CTRL 0x24008
|
||||
#define APPLE_ANS_PRP_NULL_CHECK BIT(11)
|
||||
|
||||
#define APPLE_ANS_LINEAR_SQ_CTRL 0x24908
|
||||
#define APPLE_ANS_LINEAR_SQ_EN BIT(0)
|
||||
|
||||
@@ -151,6 +148,23 @@ struct apple_nvme_queue {
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static inline bool apple_nvme_queue_enabled(struct apple_nvme_queue *q)
|
||||
{
|
||||
/* Pair with apple_nvme_enable_queue(). */
|
||||
return smp_load_acquire(&q->enabled);
|
||||
}
|
||||
|
||||
static inline void apple_nvme_enable_queue(struct apple_nvme_queue *q)
|
||||
{
|
||||
/* Publish queue initialization before setting q->enabled. */
|
||||
smp_store_release(&q->enabled, true);
|
||||
}
|
||||
|
||||
static inline void apple_nvme_disable_queue(struct apple_nvme_queue *q)
|
||||
{
|
||||
WRITE_ONCE(q->enabled, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* The apple_nvme_iod describes the data in an I/O.
|
||||
*
|
||||
@@ -318,13 +332,15 @@ static void apple_nvme_submit_cmd_t8103(struct apple_nvme_queue *q,
|
||||
u32 tag = nvme_tag_from_cid(cmd->common.command_id);
|
||||
struct apple_nvmmu_tcb *tcb = &q->tcbs[tag];
|
||||
|
||||
tcb->opcode = cmd->common.opcode;
|
||||
tcb->opcode = 0;
|
||||
tcb->prp1 = cmd->common.dptr.prp1;
|
||||
tcb->prp2 = cmd->common.dptr.prp2;
|
||||
tcb->length = cmd->rw.length;
|
||||
tcb->command_id = tag;
|
||||
|
||||
if (nvme_is_write(cmd))
|
||||
if (!cmd->common.dptr.prp1)
|
||||
tcb->dma_flags = 0;
|
||||
else if (nvme_is_write(cmd))
|
||||
tcb->dma_flags = APPLE_ANS_TCB_DMA_TO_DEVICE;
|
||||
else
|
||||
tcb->dma_flags = APPLE_ANS_TCB_DMA_FROM_DEVICE;
|
||||
@@ -677,7 +693,7 @@ static bool apple_nvme_handle_cq(struct apple_nvme_queue *q, bool force)
|
||||
bool found;
|
||||
DEFINE_IO_COMP_BATCH(iob);
|
||||
|
||||
if (!READ_ONCE(q->enabled) && !force)
|
||||
if (!apple_nvme_queue_enabled(q) && !force)
|
||||
return false;
|
||||
|
||||
found = apple_nvme_poll_cq(q, &iob);
|
||||
@@ -780,7 +796,7 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||
* We should not need to do this, but we're still using this to
|
||||
* ensure we can drain requests on a dying queue.
|
||||
*/
|
||||
if (unlikely(!READ_ONCE(q->enabled)))
|
||||
if (unlikely(!apple_nvme_queue_enabled(q)))
|
||||
return BLK_STS_IOERR;
|
||||
|
||||
if (!nvme_check_ready(&anv->ctrl, req, true))
|
||||
@@ -863,7 +879,7 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown)
|
||||
nvme_quiesce_io_queues(&anv->ctrl);
|
||||
|
||||
if (!dead) {
|
||||
if (READ_ONCE(anv->ioq.enabled)) {
|
||||
if (apple_nvme_queue_enabled(&anv->ioq)) {
|
||||
apple_nvme_remove_sq(anv);
|
||||
apple_nvme_remove_cq(anv);
|
||||
}
|
||||
@@ -887,8 +903,8 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown)
|
||||
nvme_disable_ctrl(&anv->ctrl, false);
|
||||
}
|
||||
|
||||
WRITE_ONCE(anv->ioq.enabled, false);
|
||||
WRITE_ONCE(anv->adminq.enabled, false);
|
||||
apple_nvme_disable_queue(&anv->ioq);
|
||||
apple_nvme_disable_queue(&anv->adminq);
|
||||
mb(); /* ensure that nvme_queue_rq() sees that enabled is cleared */
|
||||
nvme_quiesce_admin_queue(&anv->ctrl);
|
||||
|
||||
@@ -1016,8 +1032,7 @@ static void apple_nvme_init_queue(struct apple_nvme_queue *q)
|
||||
memset(q->tcbs, 0, anv->hw->max_queue_depth
|
||||
* sizeof(struct apple_nvmmu_tcb));
|
||||
memset(q->cqes, 0, depth * sizeof(struct nvme_completion));
|
||||
WRITE_ONCE(q->enabled, true);
|
||||
wmb(); /* ensure the first interrupt sees the initialization */
|
||||
apple_nvme_enable_queue(q);
|
||||
}
|
||||
|
||||
static void apple_nvme_reset_work(struct work_struct *work)
|
||||
@@ -1125,17 +1140,6 @@ static void apple_nvme_reset_work(struct work_struct *work)
|
||||
/* Setup the NVMMU for the maximum admin and IO queue depth */
|
||||
writel(anv->hw->max_queue_depth - 1,
|
||||
anv->mmio_nvme + APPLE_NVMMU_NUM_TCBS);
|
||||
|
||||
/*
|
||||
* This is probably a chicken bit: without it all commands
|
||||
* where any PRP is set to zero (including those that don't use
|
||||
* that field) fail and the co-processor complains about
|
||||
* "completed with err BAD_CMD-" or a "NULL_PRP_PTR_ERR" in the
|
||||
* syslog
|
||||
*/
|
||||
writel(readl(anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL) &
|
||||
~APPLE_ANS_PRP_NULL_CHECK,
|
||||
anv->mmio_nvme + APPLE_ANS_UNKNOWN_CTRL);
|
||||
}
|
||||
|
||||
/* Setup the admin queue */
|
||||
@@ -1567,10 +1571,8 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_irq(anv->dev, anv->irq, apple_nvme_irq, 0,
|
||||
"nvme-apple", anv);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Failed to request IRQ");
|
||||
if (ret)
|
||||
goto put_dev;
|
||||
}
|
||||
|
||||
anv->rtk =
|
||||
devm_apple_rtkit_init(dev, anv, NULL, 0, &apple_nvme_rtkit_ops);
|
||||
@@ -1581,7 +1583,8 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = nvme_init_ctrl(&anv->ctrl, anv->dev, &nvme_ctrl_ops,
|
||||
NVME_QUIRK_SKIP_CID_GEN | NVME_QUIRK_IDENTIFY_CNS);
|
||||
NVME_QUIRK_SKIP_CID_GEN | NVME_QUIRK_IDENTIFY_CNS |
|
||||
NVME_QUIRK_ADMIN_PAGE_ALIGN);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Failed to initialize nvme_ctrl");
|
||||
goto put_dev;
|
||||
@@ -1636,6 +1639,15 @@ static void apple_nvme_remove(struct platform_device *pdev)
|
||||
nvme_stop_ctrl(&anv->ctrl);
|
||||
nvme_remove_namespaces(&anv->ctrl);
|
||||
apple_nvme_disable(anv, true);
|
||||
if (anv->ctrl.admin_q && !blk_queue_dying(anv->ctrl.admin_q)) {
|
||||
/*
|
||||
* If the controller was reset during removal, it's possible
|
||||
* user requests may be waiting on a stopped queue. Start the
|
||||
* queue to flush these to completion.
|
||||
*/
|
||||
nvme_unquiesce_admin_queue(&anv->ctrl);
|
||||
blk_mq_destroy_queue(anv->ctrl.admin_q);
|
||||
}
|
||||
nvme_uninit_ctrl(&anv->ctrl);
|
||||
|
||||
if (apple_rtkit_is_running(anv->rtk)) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/prandom.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <crypto/dh.h>
|
||||
#include <crypto/utils.h>
|
||||
#include "nvme.h"
|
||||
#include "fabrics.h"
|
||||
#include <linux/nvme-auth.h>
|
||||
@@ -361,7 +362,7 @@ static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl,
|
||||
return 0;
|
||||
|
||||
/* Validate controller response */
|
||||
if (memcmp(chap->response, data->rval, data->hl)) {
|
||||
if (crypto_memneq(chap->response, data->rval, data->hl)) {
|
||||
dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
|
||||
__func__, chap->qid, (int)chap->hash_len, data->rval);
|
||||
dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
|
||||
|
||||
@@ -33,6 +33,13 @@
|
||||
|
||||
#define NVME_MINORS (1U << MINORBITS)
|
||||
|
||||
/*
|
||||
* Write hints (bio->bi_write_stream) are u8, so FDP placement handles beyond
|
||||
* U8_MAX can never be selected. Cap the handle count to bound both the RUH
|
||||
* status buffer and the per-head plids array.
|
||||
*/
|
||||
#define NVME_MAX_PLIDS U8_MAX
|
||||
|
||||
struct nvme_ns_info {
|
||||
struct nvme_ns_ids ids;
|
||||
u32 nsid;
|
||||
@@ -126,8 +133,8 @@ EXPORT_SYMBOL_GPL(nvme_reset_wq);
|
||||
struct workqueue_struct *nvme_delete_wq;
|
||||
EXPORT_SYMBOL_GPL(nvme_delete_wq);
|
||||
|
||||
static LIST_HEAD(nvme_subsystems);
|
||||
DEFINE_MUTEX(nvme_subsystems_lock);
|
||||
static LIST_HEAD_GUARDED(nvme_subsystems, nvme_subsystems_lock);
|
||||
|
||||
static DEFINE_IDA(nvme_instance_ida);
|
||||
static dev_t nvme_ctrl_base_chr_devt;
|
||||
@@ -693,6 +700,11 @@ static void nvme_free_ns_head(struct kref *ref)
|
||||
kfree(head);
|
||||
}
|
||||
|
||||
void nvme_get_ns_head(struct nvme_ns_head *head)
|
||||
{
|
||||
kref_get(&head->ref);
|
||||
}
|
||||
|
||||
bool nvme_tryget_ns_head(struct nvme_ns_head *head)
|
||||
{
|
||||
return kref_get_unless_zero(&head->ref);
|
||||
@@ -1268,7 +1280,7 @@ u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u8 opcode)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(nvme_passthru_start, "NVME_TARGET_PASSTHRU");
|
||||
|
||||
void nvme_passthru_end(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u32 effects,
|
||||
u32 nvme_passthru_end(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u32 effects,
|
||||
struct nvme_command *cmd, int status)
|
||||
{
|
||||
if (effects & NVME_CMD_EFFECTS_CSE_MASK) {
|
||||
@@ -1289,7 +1301,7 @@ void nvme_passthru_end(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u32 effects,
|
||||
flush_work(&ctrl->scan_work);
|
||||
}
|
||||
if (ns)
|
||||
return;
|
||||
return effects;
|
||||
|
||||
switch (cmd->common.opcode) {
|
||||
case nvme_admin_set_features:
|
||||
@@ -1310,6 +1322,8 @@ void nvme_passthru_end(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u32 effects,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return effects;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(nvme_passthru_end, "NVME_TARGET_PASSTHRU");
|
||||
|
||||
@@ -1583,8 +1597,12 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl,
|
||||
for (pos = 0; pos < NVME_IDENTIFY_DATA_SIZE; pos += len) {
|
||||
struct nvme_ns_id_desc *cur = data + pos;
|
||||
|
||||
if (pos + sizeof(*cur) > NVME_IDENTIFY_DATA_SIZE)
|
||||
break;
|
||||
if (cur->nidl == 0)
|
||||
break;
|
||||
if (pos + sizeof(*cur) + cur->nidl > NVME_IDENTIFY_DATA_SIZE)
|
||||
break;
|
||||
|
||||
len = nvme_process_ns_desc(ctrl, &info->ids, cur, &csi_seen);
|
||||
if (len < 0)
|
||||
@@ -2071,7 +2089,10 @@ static void nvme_set_ctrl_limits(struct nvme_ctrl *ctrl,
|
||||
lim->max_integrity_segments = ctrl->max_integrity_segments;
|
||||
lim->virt_boundary_mask = ctrl->ops->get_virt_boundary(ctrl, is_admin);
|
||||
lim->max_segment_size = UINT_MAX;
|
||||
lim->dma_alignment = 3;
|
||||
if (is_admin && (ctrl->quirks & NVME_QUIRK_ADMIN_PAGE_ALIGN))
|
||||
lim->dma_alignment = NVME_CTRL_PAGE_SIZE - 1;
|
||||
else
|
||||
lim->dma_alignment = 3;
|
||||
}
|
||||
|
||||
static bool nvme_update_disk_info(struct nvme_ns *ns, struct nvme_id_ns *id,
|
||||
@@ -2342,7 +2363,7 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info)
|
||||
if (!info->runs)
|
||||
return ret;
|
||||
|
||||
size = struct_size(ruhs, ruhsd, S8_MAX - 1);
|
||||
size = struct_size(ruhs, ruhsd, NVME_MAX_PLIDS);
|
||||
ruhs = kzalloc(size, GFP_KERNEL);
|
||||
if (!ruhs)
|
||||
return -ENOMEM;
|
||||
@@ -2357,7 +2378,7 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info)
|
||||
goto free;
|
||||
}
|
||||
|
||||
head->nr_plids = le16_to_cpu(ruhs->nruhsd);
|
||||
head->nr_plids = min(le16_to_cpu(ruhs->nruhsd), NVME_MAX_PLIDS);
|
||||
if (!head->nr_plids)
|
||||
goto free;
|
||||
|
||||
@@ -2592,11 +2613,15 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
|
||||
lim.max_write_streams = ns_lim->max_write_streams;
|
||||
lim.write_stream_granularity = ns_lim->write_stream_granularity;
|
||||
ret = queue_limits_commit_update(ns->head->disk->queue, &lim);
|
||||
if (ret)
|
||||
goto unfreeze_head_queue;
|
||||
|
||||
set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk));
|
||||
set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
|
||||
nvme_mpath_revalidate_paths(ns->head);
|
||||
ret = nvme_mpath_revalidate_zones(ns->head);
|
||||
|
||||
unfreeze_head_queue:
|
||||
blk_mq_unfreeze_queue(ns->head->disk->queue, memflags);
|
||||
}
|
||||
|
||||
@@ -3198,6 +3223,7 @@ static void nvme_put_subsystem(struct nvme_subsystem *subsys)
|
||||
}
|
||||
|
||||
static struct nvme_subsystem *__nvme_find_get_subsystem(const char *subsysnqn)
|
||||
__must_hold(&nvme_subsystems_lock)
|
||||
{
|
||||
struct nvme_subsystem *subsys;
|
||||
|
||||
@@ -3242,6 +3268,7 @@ static inline bool nvme_is_io_ctrl(struct nvme_ctrl *ctrl)
|
||||
|
||||
static bool nvme_validate_cntlid(struct nvme_subsystem *subsys,
|
||||
struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
|
||||
__must_hold(&nvme_subsystems_lock)
|
||||
{
|
||||
struct nvme_ctrl *tmp;
|
||||
|
||||
@@ -3272,6 +3299,7 @@ static bool nvme_validate_cntlid(struct nvme_subsystem *subsys,
|
||||
}
|
||||
|
||||
static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
|
||||
__context_unsafe(/* initialize unpublished/lock-guarded variables */)
|
||||
{
|
||||
struct nvme_subsystem *subsys, *found;
|
||||
int ret;
|
||||
@@ -3843,6 +3871,7 @@ static const struct file_operations nvme_dev_fops = {
|
||||
|
||||
static struct nvme_ns_head *nvme_find_ns_head(struct nvme_ctrl *ctrl,
|
||||
unsigned nsid)
|
||||
__must_hold(&ctrl->subsys->lock)
|
||||
{
|
||||
struct nvme_ns_head *h;
|
||||
|
||||
@@ -3865,6 +3894,7 @@ static struct nvme_ns_head *nvme_find_ns_head(struct nvme_ctrl *ctrl,
|
||||
|
||||
static int nvme_subsys_check_duplicate_ids(struct nvme_subsystem *subsys,
|
||||
struct nvme_ns_ids *ids)
|
||||
__must_hold(&subsys->lock)
|
||||
{
|
||||
bool has_uuid = !uuid_is_null(&ids->uuid);
|
||||
bool has_nguid = memchr_inv(ids->nguid, 0, sizeof(ids->nguid));
|
||||
@@ -3890,6 +3920,11 @@ static int nvme_subsys_check_duplicate_ids(struct nvme_subsystem *subsys,
|
||||
static void nvme_cdev_rel(struct device *dev)
|
||||
{
|
||||
ida_free(&nvme_ns_chr_minor_ida, MINOR(dev->devt));
|
||||
if (dev->parent->class == &nvme_class)
|
||||
nvme_put_ns(container_of(dev, struct nvme_ns, cdev_device));
|
||||
else
|
||||
nvme_put_ns_head(container_of(dev, struct nvme_ns_head,
|
||||
cdev_device));
|
||||
}
|
||||
|
||||
void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device)
|
||||
@@ -3955,10 +3990,12 @@ static void nvme_add_ns_cdev(struct nvme_ns *ns)
|
||||
snprintf(name, sizeof(name), "ng%dn%d", ns->ctrl->instance,
|
||||
ns->head->instance);
|
||||
|
||||
nvme_get_ns(ns); /* Undone in nvme_cdev_rel() */
|
||||
if (nvme_cdev_add(name, &ns->cdev, &ns->cdev_device,
|
||||
&nvme_ns_chr_fops, ns->ctrl->ops->module)) {
|
||||
dev_err(ns->ctrl->device, "Unable to create the %s device\n",
|
||||
name);
|
||||
nvme_put_ns(ns);
|
||||
return;
|
||||
}
|
||||
set_bit(NVME_NS_CDEV_LIVE, &ns->flags);
|
||||
@@ -3966,6 +4003,7 @@ static void nvme_add_ns_cdev(struct nvme_ns *ns)
|
||||
|
||||
static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
|
||||
struct nvme_ns_info *info)
|
||||
__must_hold(&ctrl->subsys->lock)
|
||||
{
|
||||
struct nvme_ns_head *head;
|
||||
size_t size = sizeof(*head);
|
||||
@@ -5195,7 +5233,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
|
||||
|
||||
BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) >
|
||||
PAGE_SIZE);
|
||||
ctrl->discard_page = alloc_page(GFP_KERNEL);
|
||||
ctrl->discard_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
||||
if (!ctrl->discard_page) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
#include "fabrics.h"
|
||||
#include <linux/nvme-keyring.h>
|
||||
|
||||
static LIST_HEAD(nvmf_transports);
|
||||
static DECLARE_RWSEM(nvmf_transports_rwsem);
|
||||
static LIST_HEAD_GUARDED(nvmf_transports, nvmf_transports_rwsem);
|
||||
|
||||
static LIST_HEAD(nvmf_hosts);
|
||||
static DEFINE_MUTEX(nvmf_hosts_mutex);
|
||||
static LIST_HEAD_GUARDED(nvmf_hosts, nvmf_hosts_mutex);
|
||||
|
||||
static struct nvmf_host *nvmf_default_host;
|
||||
|
||||
|
||||
@@ -2100,9 +2100,15 @@ __nvme_fc_init_request(struct nvme_fc_ctrl *ctrl,
|
||||
dev_err(ctrl->dev,
|
||||
"FCP Op failed - rspiu dma mapping failed.\n");
|
||||
ret = -EFAULT;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
atomic_set(&op->state, FCPOP_STATE_IDLE);
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
fc_dma_unmap_single(ctrl->lport->dev, op->fcp_req.cmddma,
|
||||
sizeof(op->cmd_iu), DMA_TO_DEVICE);
|
||||
out_on_error:
|
||||
return ret;
|
||||
}
|
||||
@@ -2318,7 +2324,7 @@ nvme_fc_create_hw_io_queues(struct nvme_fc_ctrl *ctrl, u16 qsize)
|
||||
return 0;
|
||||
|
||||
delete_queues:
|
||||
for (; i > 0; i--)
|
||||
for (--i; i > 0; i--)
|
||||
__nvme_fc_delete_hw_queue(ctrl, &ctrl->queues[i], i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+79
-42
@@ -14,45 +14,54 @@ enum {
|
||||
NVME_IOCTL_PARTITION = (1 << 1),
|
||||
};
|
||||
|
||||
static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
|
||||
unsigned int flags, bool open_for_write)
|
||||
static bool nvme_admin_cmd_allowed(struct nvme_ctrl *ctrl,
|
||||
struct nvme_command *c)
|
||||
{
|
||||
u32 effects;
|
||||
|
||||
/*
|
||||
* Do not allow unprivileged passthrough on partitions, as that allows an
|
||||
* escape from the containment of the partition.
|
||||
*/
|
||||
if (flags & NVME_IOCTL_PARTITION)
|
||||
goto admin;
|
||||
|
||||
/*
|
||||
* Do not allow unprivileged processes to send vendor specific or fabrics
|
||||
* commands as we can't be sure about their effects.
|
||||
*/
|
||||
if (c->common.opcode >= nvme_cmd_vendor_start ||
|
||||
c->common.opcode == nvme_fabrics_command)
|
||||
goto admin;
|
||||
|
||||
/*
|
||||
* Do not allow unprivileged passthrough of admin commands except
|
||||
* for a subset of identify commands that contain information required
|
||||
* to form proper I/O commands in userspace and do not expose any
|
||||
* potentially sensitive information.
|
||||
*/
|
||||
if (!ns) {
|
||||
if (c->common.opcode == nvme_admin_identify) {
|
||||
switch (c->identify.cns) {
|
||||
case NVME_ID_CNS_NS:
|
||||
case NVME_ID_CNS_CS_NS:
|
||||
case NVME_ID_CNS_NS_CS_INDEP:
|
||||
case NVME_ID_CNS_CS_CTRL:
|
||||
case NVME_ID_CNS_CTRL:
|
||||
return true;
|
||||
}
|
||||
switch (c->common.opcode) {
|
||||
case nvme_admin_identify:
|
||||
switch (c->identify.cns) {
|
||||
case NVME_ID_CNS_NS:
|
||||
case NVME_ID_CNS_CS_NS:
|
||||
case NVME_ID_CNS_NS_CS_INDEP:
|
||||
case NVME_ID_CNS_CS_CTRL:
|
||||
case NVME_ID_CNS_CTRL:
|
||||
return true;
|
||||
}
|
||||
goto admin;
|
||||
break;
|
||||
case nvme_admin_set_features:
|
||||
/*
|
||||
* Reject Set Features that change controller state the driver
|
||||
* manages itself; setting them behind the driver's back from
|
||||
* userspace leaves it unable to react correctly. Keep Alive is
|
||||
* only armed for fabrics - on other transports it has no
|
||||
* reserved tag and harms idle power states.
|
||||
*/
|
||||
switch (le32_to_cpu(c->features.fid) & 0xff) {
|
||||
case NVME_FEAT_KATO:
|
||||
if (ctrl->ops->flags & NVME_F_FABRICS)
|
||||
break;
|
||||
fallthrough;
|
||||
case NVME_FEAT_HOST_BEHAVIOR:
|
||||
case NVME_FEAT_HOST_MEM_BUF:
|
||||
case NVME_FEAT_NUM_QUEUES:
|
||||
case NVME_FEAT_AUTO_PST:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
}
|
||||
|
||||
static bool nvme_ns_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
|
||||
bool open_for_write)
|
||||
{
|
||||
u32 effects;
|
||||
|
||||
/*
|
||||
* Check if the controller provides a Commands Supported and Effects log
|
||||
@@ -61,7 +70,7 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
|
||||
*/
|
||||
effects = nvme_command_effects(ns->ctrl, ns, c->common.opcode);
|
||||
if (!(effects & NVME_CMD_EFFECTS_CSUPP))
|
||||
goto admin;
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
|
||||
/*
|
||||
* Don't allow passthrough for command that have intrusive (or unknown)
|
||||
@@ -70,7 +79,7 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
|
||||
if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC |
|
||||
NVME_CMD_EFFECTS_UUID_SEL |
|
||||
NVME_CMD_EFFECTS_SCOPE_MASK))
|
||||
goto admin;
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
|
||||
/*
|
||||
* Only allow I/O commands that transfer data to the controller or that
|
||||
@@ -79,11 +88,34 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
|
||||
*/
|
||||
if ((nvme_is_write(c) || (effects & NVME_CMD_EFFECTS_LBCC)) &&
|
||||
!open_for_write)
|
||||
goto admin;
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
|
||||
return true;
|
||||
admin:
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
}
|
||||
|
||||
static bool nvme_cmd_allowed(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
||||
struct nvme_command *c, unsigned int flags,
|
||||
bool open_for_write)
|
||||
{
|
||||
/*
|
||||
* Do not allow unprivileged passthrough on partitions, as that
|
||||
* allows an escape from the containment of the partition.
|
||||
*/
|
||||
if (flags & NVME_IOCTL_PARTITION)
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
|
||||
/*
|
||||
* Do not allow unprivileged processes to send vendor specific or
|
||||
* fabrics commands as we can't be sure about their effects.
|
||||
*/
|
||||
if (c->common.opcode >= nvme_cmd_vendor_start ||
|
||||
c->common.opcode == nvme_fabrics_command)
|
||||
return capable(CAP_SYS_ADMIN);
|
||||
|
||||
if (!ns)
|
||||
return nvme_admin_cmd_allowed(ctrl, c);
|
||||
|
||||
return nvme_ns_cmd_allowed(ns, c, open_for_write);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -202,7 +234,8 @@ out_free_req:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
|
||||
static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio,
|
||||
unsigned int flags, bool open_for_write)
|
||||
{
|
||||
struct nvme_user_io io;
|
||||
struct nvme_command c;
|
||||
@@ -260,6 +293,9 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
|
||||
c.rw.lbat = cpu_to_le16(io.apptag);
|
||||
c.rw.lbatm = cpu_to_le16(io.appmask);
|
||||
|
||||
if (!nvme_cmd_allowed(ns->ctrl, ns, &c, flags, open_for_write))
|
||||
return -EACCES;
|
||||
|
||||
return nvme_submit_user_cmd(ns->queue, &c, io.addr, length, metadata,
|
||||
meta_len, NULL, 0, 0);
|
||||
}
|
||||
@@ -307,7 +343,7 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
||||
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
|
||||
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
|
||||
|
||||
if (!nvme_cmd_allowed(ns, &c, 0, open_for_write))
|
||||
if (!nvme_cmd_allowed(ctrl, ns, &c, 0, open_for_write))
|
||||
return -EACCES;
|
||||
|
||||
if (cmd.timeout_ms)
|
||||
@@ -354,7 +390,7 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
||||
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
|
||||
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
|
||||
|
||||
if (!nvme_cmd_allowed(ns, &c, flags, open_for_write))
|
||||
if (!nvme_cmd_allowed(ctrl, ns, &c, flags, open_for_write))
|
||||
return -EACCES;
|
||||
|
||||
if (cmd.timeout_ms)
|
||||
@@ -449,6 +485,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
||||
const struct nvme_uring_cmd *cmd = io_uring_sqe128_cmd(ioucmd->sqe,
|
||||
struct nvme_uring_cmd);
|
||||
struct request_queue *q = ns ? ns->queue : ctrl->admin_q;
|
||||
bool open_for_write = ioucmd->file->f_mode & FMODE_WRITE;
|
||||
struct nvme_uring_data d;
|
||||
struct nvme_command c;
|
||||
struct iov_iter iter;
|
||||
@@ -479,7 +516,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
||||
c.common.cdw14 = cpu_to_le32(READ_ONCE(cmd->cdw14));
|
||||
c.common.cdw15 = cpu_to_le32(READ_ONCE(cmd->cdw15));
|
||||
|
||||
if (!nvme_cmd_allowed(ns, &c, 0, ioucmd->file->f_mode & FMODE_WRITE))
|
||||
if (!nvme_cmd_allowed(ctrl, ns, &c, 0, open_for_write))
|
||||
return -EACCES;
|
||||
|
||||
d.metadata = READ_ONCE(cmd->metadata);
|
||||
@@ -595,7 +632,7 @@ static int nvme_ns_ioctl(struct nvme_ns *ns, unsigned int cmd,
|
||||
case NVME_IOCTL_SUBMIT_IO32:
|
||||
#endif
|
||||
case NVME_IOCTL_SUBMIT_IO:
|
||||
return nvme_submit_io(ns, argp);
|
||||
return nvme_submit_io(ns, argp, flags, open_for_write);
|
||||
case NVME_IOCTL_IO64_CMD_VEC:
|
||||
flags |= NVME_IOCTL_VEC;
|
||||
fallthrough;
|
||||
@@ -692,14 +729,14 @@ int nvme_ns_chr_uring_cmd_iopoll(struct io_uring_cmd *ioucmd,
|
||||
static int nvme_ns_head_ctrl_ioctl(struct nvme_ns *ns, unsigned int cmd,
|
||||
void __user *argp, struct nvme_ns_head *head, int srcu_idx,
|
||||
bool open_for_write)
|
||||
__releases(&head->srcu)
|
||||
__releases_shared(&head->srcu)
|
||||
{
|
||||
struct nvme_ctrl *ctrl = ns->ctrl;
|
||||
int ret;
|
||||
|
||||
nvme_get_ctrl(ns->ctrl);
|
||||
srcu_read_unlock(&head->srcu, srcu_idx);
|
||||
ret = nvme_ctrl_ioctl(ns->ctrl, cmd, argp, open_for_write);
|
||||
ret = nvme_ctrl_ioctl(ctrl, cmd, argp, open_for_write);
|
||||
|
||||
nvme_put_ctrl(ctrl);
|
||||
return ret;
|
||||
|
||||
@@ -288,6 +288,25 @@ void nvme_mpath_revalidate_paths(struct nvme_ns_head *head)
|
||||
kblockd_schedule_work(&head->requeue_work);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
int nvme_mpath_revalidate_zones(struct nvme_ns_head *head)
|
||||
{
|
||||
struct gendisk *disk = head->disk;
|
||||
int ret;
|
||||
|
||||
if (!disk || !blk_queue_is_zoned(disk->queue) ||
|
||||
!test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags))
|
||||
return 0;
|
||||
|
||||
ret = blk_revalidate_disk_zones(disk);
|
||||
if (ret)
|
||||
dev_warn_ratelimited(disk_to_dev(disk),
|
||||
"failed to revalidate zoned namespace head: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_BLK_DEV_ZONED */
|
||||
|
||||
static bool nvme_path_is_disabled(struct nvme_ns *ns)
|
||||
{
|
||||
enum nvme_ctrl_state state = nvme_ctrl_state(ns->ctrl);
|
||||
@@ -306,6 +325,7 @@ static bool nvme_path_is_disabled(struct nvme_ns *ns)
|
||||
}
|
||||
|
||||
static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
|
||||
__must_hold_shared(&head->srcu)
|
||||
{
|
||||
int found_distance = INT_MAX, fallback_distance = INT_MAX, distance;
|
||||
struct nvme_ns *found = NULL, *fallback = NULL, *ns;
|
||||
@@ -348,6 +368,7 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
|
||||
|
||||
static struct nvme_ns *nvme_next_ns(struct nvme_ns_head *head,
|
||||
struct nvme_ns *ns)
|
||||
__must_hold_shared(&head->srcu)
|
||||
{
|
||||
ns = list_next_or_null_rcu(&head->list, &ns->siblings, struct nvme_ns,
|
||||
siblings);
|
||||
@@ -357,6 +378,7 @@ static struct nvme_ns *nvme_next_ns(struct nvme_ns_head *head,
|
||||
}
|
||||
|
||||
static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
|
||||
__must_hold_shared(&head->srcu)
|
||||
{
|
||||
struct nvme_ns *ns, *found = NULL;
|
||||
int node = numa_node_id();
|
||||
@@ -405,6 +427,7 @@ out:
|
||||
}
|
||||
|
||||
static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head)
|
||||
__must_hold_shared(&head->srcu)
|
||||
{
|
||||
struct nvme_ns *best_opt = NULL, *best_nonopt = NULL, *ns;
|
||||
unsigned int min_depth_opt = UINT_MAX, min_depth_nonopt = UINT_MAX;
|
||||
@@ -448,6 +471,7 @@ static inline bool nvme_path_is_optimized(struct nvme_ns *ns)
|
||||
}
|
||||
|
||||
static struct nvme_ns *nvme_numa_path(struct nvme_ns_head *head)
|
||||
__must_hold_shared(&head->srcu)
|
||||
{
|
||||
int node = numa_node_id();
|
||||
struct nvme_ns *ns;
|
||||
@@ -473,6 +497,7 @@ inline struct nvme_ns *nvme_find_path(struct nvme_ns_head *head)
|
||||
}
|
||||
|
||||
static bool nvme_available_path(struct nvme_ns_head *head)
|
||||
__must_hold_shared(&head->srcu)
|
||||
{
|
||||
struct nvme_ns *ns;
|
||||
|
||||
@@ -611,28 +636,8 @@ const struct block_device_operations nvme_ns_head_ops = {
|
||||
.pr_ops = &nvme_pr_ops,
|
||||
};
|
||||
|
||||
static inline struct nvme_ns_head *cdev_to_ns_head(struct cdev *cdev)
|
||||
{
|
||||
return container_of(cdev, struct nvme_ns_head, cdev);
|
||||
}
|
||||
|
||||
static int nvme_ns_head_chr_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (!nvme_tryget_ns_head(cdev_to_ns_head(inode->i_cdev)))
|
||||
return -ENXIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvme_ns_head_chr_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
nvme_put_ns_head(cdev_to_ns_head(inode->i_cdev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations nvme_ns_head_chr_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = nvme_ns_head_chr_open,
|
||||
.release = nvme_ns_head_chr_release,
|
||||
.unlocked_ioctl = nvme_ns_head_chr_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.uring_cmd = nvme_ns_head_chr_uring_cmd,
|
||||
@@ -647,10 +652,12 @@ static void nvme_add_ns_head_cdev(struct nvme_ns_head *head)
|
||||
snprintf(name, sizeof(name), "ng%dn%d", head->subsys->instance,
|
||||
head->instance);
|
||||
|
||||
nvme_get_ns_head(head); /* Undone in nvme_cdev_rel() */
|
||||
if (nvme_cdev_add(name, &head->cdev, &head->cdev_device,
|
||||
&nvme_ns_head_chr_fops, THIS_MODULE)) {
|
||||
dev_err(disk_to_dev(head->disk),
|
||||
"Unable to create the %s device\n", name);
|
||||
nvme_put_ns_head(head);
|
||||
return;
|
||||
}
|
||||
set_bit(NVME_NSHEAD_CDEV_LIVE, &head->flags);
|
||||
@@ -692,14 +699,15 @@ static void nvme_remove_head(struct nvme_ns_head *head)
|
||||
{
|
||||
if (test_and_clear_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) {
|
||||
/*
|
||||
* requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared
|
||||
* to allow multipath to fail all I/O.
|
||||
* Requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared
|
||||
* to allow multipath to fail all I/O. First synchronize to
|
||||
* add any bios to the requeue list.
|
||||
*/
|
||||
synchronize_srcu(&head->srcu);
|
||||
kblockd_schedule_work(&head->requeue_work);
|
||||
|
||||
if (test_and_clear_bit(NVME_NSHEAD_CDEV_LIVE, &head->flags))
|
||||
nvme_cdev_del(&head->cdev, &head->cdev_device);
|
||||
synchronize_srcu(&head->srcu);
|
||||
del_gendisk(head->disk);
|
||||
}
|
||||
nvme_put_ns_head(head);
|
||||
@@ -728,12 +736,10 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
|
||||
struct queue_limits lim;
|
||||
|
||||
mutex_init(&head->lock);
|
||||
bio_list_init(&head->requeue_list);
|
||||
spin_lock_init(&head->requeue_lock);
|
||||
INIT_WORK(&head->requeue_work, nvme_requeue_work);
|
||||
INIT_WORK(&head->partition_scan_work, nvme_partition_scan_work);
|
||||
INIT_DELAYED_WORK(&head->remove_work, nvme_remove_head_work);
|
||||
head->delayed_removal_secs = 0;
|
||||
|
||||
/*
|
||||
* If "multipath_always_on" is enabled, a multipath node is added
|
||||
@@ -777,7 +783,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
|
||||
set_bit(GD_SUPPRESS_PART_SCAN, &head->disk->state);
|
||||
sprintf(head->disk->disk_name, "nvme%dn%d",
|
||||
ctrl->subsys->instance, head->instance);
|
||||
nvme_tryget_ns_head(head);
|
||||
nvme_get_ns_head(head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -819,12 +825,14 @@ static void nvme_mpath_set_live(struct nvme_ns *ns)
|
||||
mutex_unlock(&head->lock);
|
||||
|
||||
synchronize_srcu(&head->srcu);
|
||||
nvme_mpath_revalidate_zones(head);
|
||||
kblockd_schedule_work(&head->requeue_work);
|
||||
}
|
||||
|
||||
static int nvme_parse_ana_log(struct nvme_ctrl *ctrl, void *data,
|
||||
int (*cb)(struct nvme_ctrl *ctrl, struct nvme_ana_group_desc *,
|
||||
void *))
|
||||
__must_hold(&ctrl->ana_lock)
|
||||
{
|
||||
void *base = ctrl->ana_log_buf;
|
||||
size_t offset = sizeof(struct nvme_ana_rsp_hdr);
|
||||
@@ -1375,10 +1383,6 @@ void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid)
|
||||
nvme_mpath_set_live(ns);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
if (blk_queue_is_zoned(ns->queue) && ns->head->disk)
|
||||
ns->head->disk->nr_zones = ns->disk->nr_zones;
|
||||
#endif
|
||||
}
|
||||
|
||||
void nvme_mpath_remove_disk(struct nvme_ns_head *head)
|
||||
|
||||
+50
-17
@@ -178,6 +178,11 @@ enum nvme_quirks {
|
||||
* Align dma pool segment size to 512 bytes
|
||||
*/
|
||||
NVME_QUIRK_DMAPOOL_ALIGN_512 = (1 << 22),
|
||||
|
||||
/*
|
||||
* Admin queue DMA buffers must be page aligned
|
||||
*/
|
||||
NVME_QUIRK_ADMIN_PAGE_ALIGN = (1 << 23),
|
||||
};
|
||||
|
||||
static inline char *nvme_quirk_name(enum nvme_quirks q)
|
||||
@@ -229,6 +234,8 @@ static inline char *nvme_quirk_name(enum nvme_quirks q)
|
||||
return "broken_msi";
|
||||
case NVME_QUIRK_DMAPOOL_ALIGN_512:
|
||||
return "dmapool_align_512";
|
||||
case NVME_QUIRK_ADMIN_PAGE_ALIGN:
|
||||
return "admin_page_align";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
@@ -361,7 +368,8 @@ struct nvme_ctrl {
|
||||
wait_queue_head_t state_wq;
|
||||
|
||||
struct nvme_subsystem *subsys;
|
||||
struct list_head subsys_entry;
|
||||
struct list_head subsys_entry
|
||||
__guarded_by(&nvme_subsystems_lock);
|
||||
|
||||
struct opal_dev *opal_dev;
|
||||
|
||||
@@ -493,10 +501,13 @@ struct nvme_subsystem {
|
||||
* a separate refcount.
|
||||
*/
|
||||
struct kref ref;
|
||||
struct list_head entry;
|
||||
struct list_head entry
|
||||
__guarded_by(&nvme_subsystems_lock);
|
||||
struct mutex lock;
|
||||
struct list_head ctrls;
|
||||
struct list_head nsheads;
|
||||
struct list_head ctrls
|
||||
__guarded_by(&nvme_subsystems_lock);
|
||||
struct list_head nsheads
|
||||
__guarded_by(&lock);
|
||||
char subnqn[NVMF_NQN_SIZE];
|
||||
char serial[20];
|
||||
char model[40];
|
||||
@@ -561,20 +572,22 @@ struct nvme_ns_head {
|
||||
u16 nr_plids;
|
||||
u16 *plids;
|
||||
#ifdef CONFIG_NVME_MULTIPATH
|
||||
struct bio_list requeue_list;
|
||||
struct bio_list requeue_list
|
||||
__guarded_by(&requeue_lock);
|
||||
spinlock_t requeue_lock;
|
||||
struct work_struct requeue_work;
|
||||
struct work_struct partition_scan_work;
|
||||
struct mutex lock;
|
||||
unsigned long flags;
|
||||
struct delayed_work remove_work;
|
||||
unsigned int delayed_removal_secs;
|
||||
unsigned int delayed_removal_secs
|
||||
__guarded_by(&subsys->lock);
|
||||
atomic_long_t io_requeue_no_usable_path_count;
|
||||
atomic_long_t io_fail_no_available_path_count;
|
||||
#define NVME_NSHEAD_DISK_LIVE 0
|
||||
#define NVME_NSHEAD_QUEUE_IF_NO_PATH 1
|
||||
#define NVME_NSHEAD_CDEV_LIVE 2
|
||||
struct nvme_ns __rcu *current_path[];
|
||||
struct nvme_ns __rcu_guarded *current_path[];
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -679,12 +692,12 @@ static inline struct request *nvme_find_rq(struct blk_mq_tags *tags,
|
||||
|
||||
rq = blk_mq_tag_to_rq(tags, tag);
|
||||
if (unlikely(!rq)) {
|
||||
pr_err("could not locate request for tag %#x\n",
|
||||
tag);
|
||||
pr_err_ratelimited("could not locate request for tag %#x\n",
|
||||
tag);
|
||||
return NULL;
|
||||
}
|
||||
if (unlikely(nvme_genctr_mask(nvme_req(rq)->genctr) != genctr)) {
|
||||
dev_err(nvme_req(rq)->ctrl->device,
|
||||
dev_err_ratelimited(nvme_req(rq)->ctrl->device,
|
||||
"request %#x genctr mismatch (got %#x expected %#x)\n",
|
||||
tag, genctr, nvme_genctr_mask(nvme_req(rq)->genctr));
|
||||
return NULL;
|
||||
@@ -995,6 +1008,7 @@ int nvme_delete_ctrl(struct nvme_ctrl *ctrl);
|
||||
void nvme_queue_scan(struct nvme_ctrl *ctrl);
|
||||
int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi,
|
||||
void *log, size_t size, u64 offset);
|
||||
void nvme_get_ns_head(struct nvme_ns_head *head);
|
||||
bool nvme_tryget_ns_head(struct nvme_ns_head *head);
|
||||
void nvme_put_ns_head(struct nvme_ns_head *head);
|
||||
int nvme_cdev_add(const char *name, struct cdev *cdev,
|
||||
@@ -1032,16 +1046,20 @@ extern const struct attribute_group *nvme_dev_attr_groups[];
|
||||
extern const struct block_device_operations nvme_bdev_ops;
|
||||
|
||||
void nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl);
|
||||
struct nvme_ns *nvme_find_path(struct nvme_ns_head *head);
|
||||
struct nvme_ns *nvme_find_path(struct nvme_ns_head *head)
|
||||
__must_hold_shared(&head->srcu);
|
||||
#ifdef CONFIG_NVME_MULTIPATH
|
||||
static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl)
|
||||
{
|
||||
return ctrl->ana_log_buf != NULL;
|
||||
}
|
||||
|
||||
void nvme_mpath_unfreeze(struct nvme_subsystem *subsys);
|
||||
void nvme_mpath_wait_freeze(struct nvme_subsystem *subsys);
|
||||
void nvme_mpath_start_freeze(struct nvme_subsystem *subsys);
|
||||
void nvme_mpath_unfreeze(struct nvme_subsystem *subsys)
|
||||
__must_hold(&subsys->lock);
|
||||
void nvme_mpath_wait_freeze(struct nvme_subsystem *subsys)
|
||||
__must_hold(&subsys->lock);
|
||||
void nvme_mpath_start_freeze(struct nvme_subsystem *subsys)
|
||||
__must_hold(&subsys->lock);
|
||||
void nvme_mpath_default_iopolicy(struct nvme_subsystem *subsys);
|
||||
void nvme_failover_req(struct request *req);
|
||||
void nvme_kick_requeue_lists(struct nvme_ctrl *ctrl);
|
||||
@@ -1184,6 +1202,15 @@ static inline bool nvme_mpath_queue_if_no_path(struct nvme_ns_head *head)
|
||||
}
|
||||
#endif /* CONFIG_NVME_MULTIPATH */
|
||||
|
||||
#if defined(CONFIG_NVME_MULTIPATH) && defined(CONFIG_BLK_DEV_ZONED)
|
||||
int nvme_mpath_revalidate_zones(struct nvme_ns_head *head);
|
||||
#else
|
||||
static inline int nvme_mpath_revalidate_zones(struct nvme_ns_head *head)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int nvme_ns_get_unique_id(struct nvme_ns *ns, u8 id[16],
|
||||
enum blk_unique_id type);
|
||||
|
||||
@@ -1290,10 +1317,16 @@ static inline void nvme_auth_revoke_tls_key(struct nvme_ctrl *ctrl) {};
|
||||
|
||||
u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
|
||||
u8 opcode);
|
||||
u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u8 opcode);
|
||||
u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u8 opcode)
|
||||
__cond_acquires(nonzero, &ctrl->subsys->lock)
|
||||
__cond_acquires(nonzero, &ctrl->scan_lock);
|
||||
|
||||
int nvme_execute_rq(struct request *rq, bool at_head);
|
||||
void nvme_passthru_end(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u32 effects,
|
||||
struct nvme_command *cmd, int status);
|
||||
u32 nvme_passthru_end(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u32 effects,
|
||||
struct nvme_command *cmd, int status)
|
||||
__cond_releases(nonzero, &ctrl->scan_lock)
|
||||
__cond_releases(nonzero, &ctrl->subsys->lock);
|
||||
|
||||
struct nvme_ctrl *nvme_ctrl_from_file(struct file *file);
|
||||
struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid);
|
||||
bool nvme_get_ns(struct nvme_ns *ns);
|
||||
|
||||
+24
-7
@@ -213,6 +213,7 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
|
||||
if (nvme_parse_quirk_entry(field, &qlist[i])) {
|
||||
pr_err("nvme: failed to parse quirk string %s\n",
|
||||
value);
|
||||
err = -EINVAL;
|
||||
goto out_free_qlist;
|
||||
}
|
||||
|
||||
@@ -366,7 +367,8 @@ struct nvme_queue {
|
||||
struct nvme_dev *dev;
|
||||
struct nvme_descriptor_pools descriptor_pools;
|
||||
spinlock_t sq_lock;
|
||||
void *sq_cmds;
|
||||
void *sq_cmds
|
||||
__guarded_by(&sq_lock);
|
||||
/* only used for poll queues: */
|
||||
spinlock_t cq_poll_lock ____cacheline_aligned_in_smp;
|
||||
struct nvme_completion *cqes;
|
||||
@@ -375,9 +377,11 @@ struct nvme_queue {
|
||||
u32 __iomem *q_db;
|
||||
u32 q_depth;
|
||||
u16 cq_vector;
|
||||
u16 sq_tail;
|
||||
u16 last_sq_tail;
|
||||
u16 cq_head;
|
||||
u16 sq_tail
|
||||
__guarded_by(&sq_lock);
|
||||
u16 last_sq_tail
|
||||
__guarded_by(&sq_lock);
|
||||
u16 qid;
|
||||
u8 cq_phase;
|
||||
u8 sqes;
|
||||
@@ -716,6 +720,7 @@ static void nvme_pci_map_queues(struct blk_mq_tag_set *set)
|
||||
* Write sq tail if we are asked to, or if the next command would wrap.
|
||||
*/
|
||||
static inline void nvme_write_sq_db(struct nvme_queue *nvmeq, bool write_sq)
|
||||
__must_hold(&nvmeq->sq_lock)
|
||||
{
|
||||
if (!write_sq) {
|
||||
u16 next_tail = nvmeq->sq_tail + 1;
|
||||
@@ -734,6 +739,7 @@ static inline void nvme_write_sq_db(struct nvme_queue *nvmeq, bool write_sq)
|
||||
|
||||
static inline void nvme_sq_copy_cmd(struct nvme_queue *nvmeq,
|
||||
struct nvme_command *cmd)
|
||||
__must_hold(&nvmeq->sq_lock)
|
||||
{
|
||||
memcpy(nvmeq->sq_cmds + (nvmeq->sq_tail << nvmeq->sqes),
|
||||
absolute_pointer(cmd), sizeof(*cmd));
|
||||
@@ -1580,13 +1586,18 @@ static inline void nvme_handle_cqe(struct nvme_queue *nvmeq,
|
||||
|
||||
req = nvme_find_rq(nvme_queue_tagset(nvmeq), command_id);
|
||||
if (unlikely(!req)) {
|
||||
dev_warn(nvmeq->dev->ctrl.device,
|
||||
"invalid id %d completed on queue %d\n",
|
||||
command_id, le16_to_cpu(cqe->sq_id));
|
||||
dev_warn_ratelimited(nvmeq->dev->ctrl.device,
|
||||
"invalid id %d completed on queue %d\n",
|
||||
command_id, le16_to_cpu(cqe->sq_id));
|
||||
return;
|
||||
}
|
||||
|
||||
trace_nvme_sq(req, cqe->sq_head, nvmeq->sq_tail);
|
||||
/*
|
||||
* Tracing only; annotate a lockless snapshot of nvmeq->sq_tail using
|
||||
* data_race(). This would also help suppress context analysis warning
|
||||
* while accessing nvmeq->sq_tail without acquiring ->sq_lock.
|
||||
*/
|
||||
trace_nvme_sq(req, cqe->sq_head, data_race(nvmeq->sq_tail));
|
||||
if (!nvme_try_complete_req(req, cqe->status, cqe->result) &&
|
||||
!blk_mq_add_to_batch(req, iob,
|
||||
nvme_req(req)->status != NVME_SC_SUCCESS,
|
||||
@@ -2013,6 +2024,7 @@ disable:
|
||||
}
|
||||
|
||||
static void nvme_free_queue(struct nvme_queue *nvmeq)
|
||||
__context_unsafe(/* frees queue which is no longer in use */)
|
||||
{
|
||||
dma_free_coherent(nvmeq->dev->dev, CQ_SIZE(nvmeq),
|
||||
(void *)nvmeq->cqes, nvmeq->cq_dma_addr);
|
||||
@@ -2107,6 +2119,7 @@ static int nvme_cmb_qdepth(struct nvme_dev *dev, int nr_io_queues,
|
||||
|
||||
static int nvme_alloc_sq_cmds(struct nvme_dev *dev, struct nvme_queue *nvmeq,
|
||||
int qid)
|
||||
__context_unsafe(/* safe to allocate sq_cmds without any protection */)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
||||
|
||||
@@ -2181,6 +2194,7 @@ static int queue_request_irq(struct nvme_queue *nvmeq)
|
||||
}
|
||||
|
||||
static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)
|
||||
__context_unsafe(/* initialize unpublished/lock-guarded variables */)
|
||||
{
|
||||
struct nvme_dev *dev = nvmeq->dev;
|
||||
|
||||
@@ -2199,6 +2213,7 @@ static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)
|
||||
* Try getting shutdown_lock while setting up IO queues.
|
||||
*/
|
||||
static int nvme_setup_io_queues_trylock(struct nvme_dev *dev)
|
||||
__cond_acquires(0, &dev->shutdown_lock)
|
||||
{
|
||||
/*
|
||||
* Give up if the lock is being held by nvme_dev_disable.
|
||||
@@ -2400,6 +2415,7 @@ static int nvme_pci_configure_admin_queue(struct nvme_dev *dev)
|
||||
result = queue_request_irq(nvmeq);
|
||||
if (result) {
|
||||
dev->online_queues--;
|
||||
nvme_disable_ctrl(&dev->ctrl, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3838,6 +3854,7 @@ out_disable:
|
||||
nvme_dev_remove_admin(dev);
|
||||
nvme_dbbuf_dma_free(dev);
|
||||
nvme_free_queues(dev, 0);
|
||||
nvme_release_descriptor_pools(dev);
|
||||
out_release_iod_mempool:
|
||||
mempool_destroy(dev->dmavec_mempool);
|
||||
out_dev_unmap:
|
||||
|
||||
+97
-63
@@ -16,6 +16,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/nvme.h>
|
||||
#include <linux/unaligned.h>
|
||||
@@ -39,11 +40,18 @@
|
||||
#define NVME_RDMA_METADATA_SGL_SIZE \
|
||||
(sizeof(struct scatterlist) * NVME_INLINE_METADATA_SG_CNT)
|
||||
|
||||
static DEFINE_MUTEX(device_list_mutex);
|
||||
static LIST_HEAD_GUARDED(device_list, device_list_mutex);
|
||||
|
||||
static DEFINE_MUTEX(nvme_rdma_ctrl_mutex);
|
||||
static LIST_HEAD_GUARDED(nvme_rdma_ctrl_list, nvme_rdma_ctrl_mutex);
|
||||
|
||||
struct nvme_rdma_device {
|
||||
struct ib_device *dev;
|
||||
struct ib_pd *pd;
|
||||
struct kref ref;
|
||||
struct list_head entry;
|
||||
struct list_head entry
|
||||
__guarded_by(&device_list_mutex);
|
||||
unsigned int num_inline_segments;
|
||||
};
|
||||
|
||||
@@ -100,6 +108,11 @@ struct nvme_rdma_queue {
|
||||
struct mutex queue_lock;
|
||||
};
|
||||
|
||||
struct nvme_rdma_setup_ctx {
|
||||
struct nvme_rdma_queue *queue;
|
||||
int *err;
|
||||
};
|
||||
|
||||
struct nvme_rdma_ctrl {
|
||||
/* read only in the hot path */
|
||||
struct nvme_rdma_queue *queues;
|
||||
@@ -112,7 +125,8 @@ struct nvme_rdma_ctrl {
|
||||
|
||||
struct delayed_work reconnect_work;
|
||||
|
||||
struct list_head list;
|
||||
struct list_head list
|
||||
__guarded_by(&nvme_rdma_ctrl_mutex);
|
||||
|
||||
struct blk_mq_tag_set admin_tag_set;
|
||||
struct nvme_rdma_device *device;
|
||||
@@ -132,12 +146,6 @@ static inline struct nvme_rdma_ctrl *to_rdma_ctrl(struct nvme_ctrl *ctrl)
|
||||
return container_of(ctrl, struct nvme_rdma_ctrl, ctrl);
|
||||
}
|
||||
|
||||
static LIST_HEAD(device_list);
|
||||
static DEFINE_MUTEX(device_list_mutex);
|
||||
|
||||
static LIST_HEAD(nvme_rdma_ctrl_list);
|
||||
static DEFINE_MUTEX(nvme_rdma_ctrl_mutex);
|
||||
|
||||
/*
|
||||
* Disabling this option makes small I/O goes faster, but is fundamentally
|
||||
* unsafe. With it turned off we will have to register a global rkey that
|
||||
@@ -566,16 +574,14 @@ out_put_dev:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl,
|
||||
int idx, size_t queue_size)
|
||||
static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
|
||||
{
|
||||
struct nvme_rdma_queue *queue;
|
||||
struct nvme_rdma_ctrl *ctrl = queue->ctrl;
|
||||
int idx = nvme_rdma_queue_idx(queue);
|
||||
struct sockaddr *src_addr = NULL;
|
||||
int ret;
|
||||
|
||||
queue = &ctrl->queues[idx];
|
||||
mutex_init(&queue->queue_lock);
|
||||
queue->ctrl = ctrl;
|
||||
if (idx && ctrl->ctrl.max_integrity_segments)
|
||||
queue->pi_support = true;
|
||||
else
|
||||
@@ -587,8 +593,6 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl,
|
||||
else
|
||||
queue->cmnd_capsule_len = sizeof(struct nvme_command);
|
||||
|
||||
queue->queue_size = queue_size;
|
||||
|
||||
queue->cm_id = rdma_create_id(&init_net, nvme_rdma_cm_handler, queue,
|
||||
RDMA_PS_TCP, IB_QPT_RC);
|
||||
if (IS_ERR(queue->cm_id)) {
|
||||
@@ -694,59 +698,68 @@ static int nvme_rdma_start_queue(struct nvme_rdma_ctrl *ctrl, int idx)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nvme_rdma_start_io_queues(struct nvme_rdma_ctrl *ctrl,
|
||||
int first, int last)
|
||||
static void nvme_rdma_setup_queue_async(void *data, async_cookie_t cookie)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct nvme_rdma_setup_ctx *ctx = data;
|
||||
struct nvme_rdma_queue *queue;
|
||||
int ret;
|
||||
|
||||
for (i = first; i < last; i++) {
|
||||
ret = nvme_rdma_start_queue(ctrl, i);
|
||||
if (ret)
|
||||
goto out_stop_queues;
|
||||
}
|
||||
queue = ctx->queue;
|
||||
ret = nvme_rdma_alloc_queue(queue);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
return 0;
|
||||
ret = nvme_rdma_start_queue(queue->ctrl, nvme_rdma_queue_idx(queue));
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
out_stop_queues:
|
||||
for (i--; i >= first; i--)
|
||||
nvme_rdma_stop_queue(&ctrl->queues[i]);
|
||||
return ret;
|
||||
return;
|
||||
out_err:
|
||||
WRITE_ONCE(*ctx->err, ret);
|
||||
}
|
||||
|
||||
static int nvme_rdma_alloc_io_queues(struct nvme_rdma_ctrl *ctrl)
|
||||
static int nvme_rdma_setup_io_queues(struct nvme_rdma_ctrl *ctrl,
|
||||
unsigned int first, unsigned int last, size_t queue_size)
|
||||
{
|
||||
struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
|
||||
unsigned int nr_io_queues;
|
||||
int i, ret;
|
||||
ASYNC_DOMAIN_EXCLUSIVE(queue_domain);
|
||||
struct nvme_rdma_setup_ctx *ctxs;
|
||||
int nr_queues = last - first;
|
||||
int err = 0, i, ret;
|
||||
|
||||
nr_io_queues = nvmf_nr_io_queues(opts);
|
||||
ret = nvme_set_queue_count(&ctrl->ctrl, &nr_io_queues);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nr_io_queues == 0) {
|
||||
dev_err(ctrl->ctrl.device,
|
||||
"unable to set any I/O queues\n");
|
||||
ctxs = kmalloc_objs(*ctxs, nr_queues);
|
||||
if (!ctxs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_queues; i++) {
|
||||
struct nvme_rdma_queue *queue = &ctrl->queues[first + i];
|
||||
|
||||
queue->ctrl = ctrl;
|
||||
queue->queue_size = queue_size;
|
||||
|
||||
ctxs[i].queue = queue;
|
||||
ctxs[i].err = &err;
|
||||
async_schedule_domain(nvme_rdma_setup_queue_async, &ctxs[i],
|
||||
&queue_domain);
|
||||
}
|
||||
|
||||
ctrl->ctrl.queue_count = nr_io_queues + 1;
|
||||
dev_info(ctrl->ctrl.device,
|
||||
"creating %d I/O queues.\n", nr_io_queues);
|
||||
async_synchronize_full_domain(&queue_domain);
|
||||
kfree(ctxs);
|
||||
|
||||
nvmf_set_io_queues(opts, nr_io_queues, ctrl->io_queues);
|
||||
for (i = 1; i < ctrl->ctrl.queue_count; i++) {
|
||||
ret = nvme_rdma_alloc_queue(ctrl, i,
|
||||
ctrl->ctrl.sqsize + 1);
|
||||
if (ret)
|
||||
goto out_free_queues;
|
||||
}
|
||||
ret = READ_ONCE(err);
|
||||
if (ret)
|
||||
goto out_free_queues;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_queues:
|
||||
for (i--; i >= 1; i--)
|
||||
nvme_rdma_free_queue(&ctrl->queues[i]);
|
||||
for (i = 0; i < nr_queues; i++) {
|
||||
struct nvme_rdma_queue *queue =
|
||||
&ctrl->queues[first + i];
|
||||
|
||||
if (test_bit(NVME_RDMA_Q_LIVE, &queue->flags))
|
||||
nvme_rdma_stop_queue(queue);
|
||||
if (test_bit(NVME_RDMA_Q_ALLOCATED, &queue->flags))
|
||||
nvme_rdma_free_queue(queue);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -783,7 +796,9 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl,
|
||||
bool pi_capable = false;
|
||||
int error;
|
||||
|
||||
error = nvme_rdma_alloc_queue(ctrl, 0, NVME_AQ_DEPTH);
|
||||
ctrl->queues[0].ctrl = ctrl;
|
||||
ctrl->queues[0].queue_size = NVME_AQ_DEPTH;
|
||||
error = nvme_rdma_alloc_queue(&ctrl->queues[0]);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -863,12 +878,23 @@ out_free_queue:
|
||||
|
||||
static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
|
||||
{
|
||||
unsigned int nr_io_queues;
|
||||
int ret, nr_queues;
|
||||
|
||||
ret = nvme_rdma_alloc_io_queues(ctrl);
|
||||
nr_io_queues = nvmf_nr_io_queues(ctrl->ctrl.opts);
|
||||
ret = nvme_set_queue_count(&ctrl->ctrl, &nr_io_queues);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (nr_io_queues == 0) {
|
||||
dev_err(ctrl->ctrl.device, "unable to set any I/O queues\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ctrl->ctrl.queue_count = nr_io_queues + 1;
|
||||
dev_info(ctrl->ctrl.device, "creating %d I/O queues.\n", nr_io_queues);
|
||||
nvmf_set_io_queues(ctrl->ctrl.opts, nr_io_queues, ctrl->io_queues);
|
||||
|
||||
if (new) {
|
||||
ret = nvme_rdma_alloc_tag_set(&ctrl->ctrl);
|
||||
if (ret)
|
||||
@@ -881,7 +907,9 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
|
||||
* queue number might have changed.
|
||||
*/
|
||||
nr_queues = min(ctrl->tag_set.nr_hw_queues + 1, ctrl->ctrl.queue_count);
|
||||
ret = nvme_rdma_start_io_queues(ctrl, 1, nr_queues);
|
||||
ret = nvme_rdma_setup_io_queues(ctrl, 1, nr_queues,
|
||||
ctrl->ctrl.sqsize + 1);
|
||||
|
||||
if (ret)
|
||||
goto out_cleanup_tagset;
|
||||
|
||||
@@ -905,12 +933,15 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new)
|
||||
|
||||
/*
|
||||
* If the number of queues has increased (reconnect case)
|
||||
* start all new queues now.
|
||||
* setup all new queues now.
|
||||
*/
|
||||
ret = nvme_rdma_start_io_queues(ctrl, nr_queues,
|
||||
ctrl->tag_set.nr_hw_queues + 1);
|
||||
if (ret)
|
||||
goto out_wait_freeze_timed_out;
|
||||
if (ctrl->tag_set.nr_hw_queues + 1 > nr_queues) {
|
||||
ret = nvme_rdma_setup_io_queues(ctrl, nr_queues,
|
||||
ctrl->tag_set.nr_hw_queues + 1,
|
||||
ctrl->ctrl.sqsize + 1);
|
||||
if (ret)
|
||||
goto out_wait_freeze_timed_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -969,7 +1000,7 @@ static void nvme_rdma_free_ctrl(struct nvme_ctrl *nctrl)
|
||||
{
|
||||
struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl);
|
||||
|
||||
if (list_empty(&ctrl->list))
|
||||
if (list_empty_careful(&ctrl->list))
|
||||
goto free_ctrl;
|
||||
|
||||
mutex_lock(&nvme_rdma_ctrl_mutex);
|
||||
@@ -2254,7 +2285,10 @@ static struct nvme_rdma_ctrl *nvme_rdma_alloc_ctrl(struct device *dev,
|
||||
if (!ctrl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ctrl->ctrl.opts = opts;
|
||||
INIT_LIST_HEAD(&ctrl->list);
|
||||
/*
|
||||
* Safe to init list while allocating ctrl object.
|
||||
*/
|
||||
context_unsafe(INIT_LIST_HEAD(&ctrl->list));
|
||||
|
||||
if (!(opts->mask & NVMF_OPT_TRSVCID)) {
|
||||
opts->trsvcid =
|
||||
|
||||
@@ -240,8 +240,10 @@ static ssize_t nuse_show(struct device *dev, struct device_attribute *attr,
|
||||
ret = ns_head_update_nuse(head);
|
||||
else
|
||||
ret = ns_update_nuse(disk->private_data);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret > 0)
|
||||
return -EIO;
|
||||
|
||||
return sysfs_emit(buf, "%llu\n", head->nuse);
|
||||
}
|
||||
|
||||
+65
-8
@@ -80,6 +80,7 @@ struct nvme_tcp_request {
|
||||
|
||||
struct bio *curr_bio;
|
||||
struct iov_iter iter;
|
||||
u32 data_recvd;
|
||||
|
||||
/* send state */
|
||||
size_t offset;
|
||||
@@ -108,6 +109,7 @@ struct nvme_tcp_queue {
|
||||
|
||||
struct mutex queue_lock;
|
||||
struct mutex send_mutex;
|
||||
struct mutex pf_cache_lock;
|
||||
struct llist_head req_list;
|
||||
struct list_head send_list;
|
||||
|
||||
@@ -149,13 +151,17 @@ struct nvme_tcp_queue {
|
||||
#endif
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(nvme_tcp_ctrl_mutex);
|
||||
static LIST_HEAD_GUARDED(nvme_tcp_ctrl_list, nvme_tcp_ctrl_mutex);
|
||||
|
||||
struct nvme_tcp_ctrl {
|
||||
/* read only in the hot path */
|
||||
struct nvme_tcp_queue *queues;
|
||||
struct blk_mq_tag_set tag_set;
|
||||
|
||||
/* other member variables */
|
||||
struct list_head list;
|
||||
struct list_head list
|
||||
__guarded_by(&nvme_tcp_ctrl_mutex);
|
||||
struct blk_mq_tag_set admin_tag_set;
|
||||
struct sockaddr_storage addr;
|
||||
struct sockaddr_storage src_addr;
|
||||
@@ -167,8 +173,6 @@ struct nvme_tcp_ctrl {
|
||||
u32 io_queues[HCTX_MAX_TYPES];
|
||||
};
|
||||
|
||||
static LIST_HEAD(nvme_tcp_ctrl_list);
|
||||
static DEFINE_MUTEX(nvme_tcp_ctrl_mutex);
|
||||
static struct workqueue_struct *nvme_tcp_wq;
|
||||
static const struct blk_mq_ops nvme_tcp_mq_ops;
|
||||
static const struct blk_mq_ops nvme_tcp_admin_mq_ops;
|
||||
@@ -357,7 +361,7 @@ static void nvme_tcp_init_iter(struct nvme_tcp_request *req,
|
||||
iov_iter_bvec(&req->iter, dir,
|
||||
__bvec_iter_bvec(bio->bi_io_vec, bio->bi_iter), nr_bvec,
|
||||
bio->bi_iter.bi_size);
|
||||
req->iter.iov_offset = bio->bi_iter.bi_bvec_done;
|
||||
req->iter.iov_offset = bio->bi_iter.bi_offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,9 +554,11 @@ static int nvme_tcp_init_request(struct blk_mq_tag_set *set,
|
||||
struct nvme_tcp_queue *queue = &ctrl->queues[queue_idx];
|
||||
u8 hdgst = nvme_tcp_hdgst_len(queue);
|
||||
|
||||
mutex_lock(&queue->pf_cache_lock);
|
||||
req->pdu = page_frag_alloc(&queue->pf_cache,
|
||||
sizeof(struct nvme_tcp_cmd_pdu) + hdgst,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
mutex_unlock(&queue->pf_cache_lock);
|
||||
if (!req->pdu)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -612,6 +618,29 @@ static void nvme_tcp_error_recovery(struct nvme_ctrl *ctrl)
|
||||
queue_work(nvme_reset_wq, &to_tcp_ctrl(ctrl)->err_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* NVMe has no short read: a read that completes successfully must
|
||||
* have transferred everything it asked for.
|
||||
*/
|
||||
static bool nvme_tcp_data_in_short(struct nvme_tcp_queue *queue,
|
||||
struct request *rq)
|
||||
{
|
||||
struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq);
|
||||
|
||||
if (le16_to_cpu(req->status) >> 1)
|
||||
return false;
|
||||
if (req_op(rq) != REQ_OP_READ || !req->data_len)
|
||||
return false;
|
||||
if (likely(req->data_recvd == req->data_len))
|
||||
return false;
|
||||
|
||||
dev_err(queue->ctrl->ctrl.device,
|
||||
"queue %d tag %#x short data-in: got %u of %u\n",
|
||||
nvme_tcp_queue_id(queue), rq->tag,
|
||||
req->data_recvd, req->data_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue,
|
||||
struct nvme_completion *cqe)
|
||||
{
|
||||
@@ -631,6 +660,9 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue,
|
||||
if (req->status == cpu_to_le16(NVME_SC_SUCCESS))
|
||||
req->status = cqe->status;
|
||||
|
||||
if (unlikely(nvme_tcp_data_in_short(queue, rq)))
|
||||
return -EPROTO;
|
||||
|
||||
if (!nvme_try_complete_req(rq, req->status, cqe->result))
|
||||
nvme_complete_rq(rq);
|
||||
queue->nr_cqe++;
|
||||
@@ -641,6 +673,7 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue,
|
||||
static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
|
||||
struct nvme_tcp_data_pdu *pdu)
|
||||
{
|
||||
struct nvme_tcp_request *req;
|
||||
struct request *rq;
|
||||
|
||||
rq = nvme_find_rq(nvme_tcp_tagset(queue), pdu->command_id);
|
||||
@@ -651,7 +684,8 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!blk_rq_payload_bytes(rq)) {
|
||||
req = blk_mq_rq_to_pdu(rq);
|
||||
if (!blk_rq_payload_bytes(rq) || !req->curr_bio || !req->data_len) {
|
||||
dev_err(queue->ctrl->ctrl.device,
|
||||
"queue %d tag %#x unexpected data\n",
|
||||
nvme_tcp_queue_id(queue), rq->tag);
|
||||
@@ -745,6 +779,13 @@ static int nvme_tcp_handle_r2t(struct nvme_tcp_queue *queue,
|
||||
}
|
||||
req = blk_mq_rq_to_pdu(rq);
|
||||
|
||||
if (unlikely(rq_data_dir(rq) != WRITE)) {
|
||||
dev_err(queue->ctrl->ctrl.device,
|
||||
"req %d unexpected r2t for a non-write command\n",
|
||||
rq->tag);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (unlikely(!r2t_length)) {
|
||||
dev_err(queue->ctrl->ctrl.device,
|
||||
"req %d r2t len is %u, probably a bug...\n",
|
||||
@@ -953,6 +994,7 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
|
||||
*len -= recv_len;
|
||||
*offset += recv_len;
|
||||
queue->data_remaining -= recv_len;
|
||||
req->data_recvd += recv_len;
|
||||
}
|
||||
|
||||
if (!queue->data_remaining) {
|
||||
@@ -961,6 +1003,8 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb,
|
||||
queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH;
|
||||
} else {
|
||||
if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) {
|
||||
if (unlikely(nvme_tcp_data_in_short(queue, rq)))
|
||||
return -EPROTO;
|
||||
nvme_tcp_end_request(rq,
|
||||
le16_to_cpu(req->status));
|
||||
queue->nr_cqe++;
|
||||
@@ -1009,6 +1053,9 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue,
|
||||
pdu->command_id);
|
||||
struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq);
|
||||
|
||||
if (unlikely(nvme_tcp_data_in_short(queue, rq)))
|
||||
return -EPROTO;
|
||||
|
||||
nvme_tcp_end_request(rq, le16_to_cpu(req->status));
|
||||
queue->nr_cqe++;
|
||||
}
|
||||
@@ -1417,9 +1464,11 @@ static int nvme_tcp_alloc_async_req(struct nvme_tcp_ctrl *ctrl)
|
||||
struct nvme_tcp_request *async = &ctrl->async_req;
|
||||
u8 hdgst = nvme_tcp_hdgst_len(queue);
|
||||
|
||||
mutex_lock(&queue->pf_cache_lock);
|
||||
async->pdu = page_frag_alloc(&queue->pf_cache,
|
||||
sizeof(struct nvme_tcp_cmd_pdu) + hdgst,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
mutex_unlock(&queue->pf_cache_lock);
|
||||
if (!async->pdu)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1461,6 +1510,7 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, int qid)
|
||||
kfree(queue->pdu);
|
||||
mutex_destroy(&queue->send_mutex);
|
||||
mutex_destroy(&queue->queue_lock);
|
||||
mutex_destroy(&queue->pf_cache_lock);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
lockdep_unregister_key(&queue->nvme_tcp_sk_key);
|
||||
@@ -1788,6 +1838,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
|
||||
INIT_LIST_HEAD(&queue->send_list);
|
||||
mutex_init(&queue->send_mutex);
|
||||
INIT_WORK(&queue->io_work, nvme_tcp_io_work);
|
||||
mutex_init(&queue->pf_cache_lock);
|
||||
|
||||
if (qid > 0)
|
||||
queue->cmnd_capsule_len = nctrl->ioccsz * 16;
|
||||
@@ -1928,6 +1979,7 @@ err_sock:
|
||||
err_destroy_mutex:
|
||||
mutex_destroy(&queue->send_mutex);
|
||||
mutex_destroy(&queue->queue_lock);
|
||||
mutex_destroy(&queue->pf_cache_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2577,7 +2629,7 @@ static void nvme_tcp_free_ctrl(struct nvme_ctrl *nctrl)
|
||||
{
|
||||
struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl);
|
||||
|
||||
if (list_empty(&ctrl->list))
|
||||
if (list_empty_careful(&ctrl->list))
|
||||
goto free_ctrl;
|
||||
|
||||
mutex_lock(&nvme_tcp_ctrl_mutex);
|
||||
@@ -2736,6 +2788,7 @@ static blk_status_t nvme_tcp_setup_cmd_pdu(struct nvme_ns *ns,
|
||||
req->status = cpu_to_le16(NVME_SC_SUCCESS);
|
||||
req->offset = 0;
|
||||
req->data_sent = 0;
|
||||
req->data_recvd = 0;
|
||||
req->pdu_len = 0;
|
||||
req->pdu_sent = 0;
|
||||
req->h2cdata_left = 0;
|
||||
@@ -2919,7 +2972,10 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
|
||||
if (!ctrl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&ctrl->list);
|
||||
/*
|
||||
* Safe to init list while allocating ctrl object.
|
||||
*/
|
||||
context_unsafe(INIT_LIST_HEAD(&ctrl->list));
|
||||
ctrl->ctrl.opts = opts;
|
||||
ctrl->ctrl.queue_count = opts->nr_io_queues + opts->nr_write_queues +
|
||||
opts->nr_poll_queues + 1;
|
||||
@@ -2960,7 +3016,8 @@ static struct nvme_tcp_ctrl *nvme_tcp_alloc_ctrl(struct device *dev,
|
||||
}
|
||||
|
||||
if (opts->mask & NVMF_OPT_HOST_IFACE) {
|
||||
if (!__dev_get_by_name(&init_net, opts->host_iface)) {
|
||||
if (!__dev_get_by_name(current->nsproxy->net_ns,
|
||||
opts->host_iface)) {
|
||||
pr_err("invalid interface passed: %s\n",
|
||||
opts->host_iface);
|
||||
ret = -ENODEV;
|
||||
|
||||
@@ -155,7 +155,8 @@ static int nvme_zone_parse_entry(struct nvme_ns *ns,
|
||||
struct blk_zone zone = { };
|
||||
|
||||
if ((entry->zt & 0xf) != NVME_ZONE_TYPE_SEQWRITE_REQ) {
|
||||
dev_err(ns->ctrl->device, "invalid zone type %#x\n", entry->zt);
|
||||
dev_err(ns->ctrl->device, "invalid zone type %#x at zone %u\n",
|
||||
entry->zt, idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -178,7 +179,7 @@ int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector,
|
||||
struct nvme_zone_report *report;
|
||||
struct nvme_command c = { };
|
||||
int ret, zone_idx = 0;
|
||||
unsigned int nz, i;
|
||||
unsigned int max_in_buf, nz, i;
|
||||
size_t buflen;
|
||||
|
||||
if (ns->head->ids.csi != NVME_CSI_ZNS)
|
||||
@@ -188,6 +189,9 @@ int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector,
|
||||
if (!report)
|
||||
return -ENOMEM;
|
||||
|
||||
max_in_buf = (buflen - sizeof(struct nvme_zone_report)) /
|
||||
sizeof(struct nvme_zone_descriptor);
|
||||
|
||||
c.zmr.opcode = nvme_cmd_zone_mgmt_recv;
|
||||
c.zmr.nsid = cpu_to_le32(ns->head->ns_id);
|
||||
c.zmr.numd = cpu_to_le32(nvme_bytes_to_numd(buflen));
|
||||
@@ -207,7 +211,8 @@ int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector,
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
nz = min((unsigned int)le64_to_cpu(report->nr_zones), nr_zones);
|
||||
nz = min3((unsigned int)le64_to_cpu(report->nr_zones),
|
||||
nr_zones - zone_idx, max_in_buf);
|
||||
if (!nz)
|
||||
break;
|
||||
|
||||
|
||||
@@ -309,8 +309,10 @@ static void nvmet_execute_get_log_page_rmi(struct nvmet_req *req)
|
||||
}
|
||||
|
||||
log = kzalloc_obj(*log);
|
||||
if (!log)
|
||||
if (!log) {
|
||||
status = NVME_SC_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
log->endgid = req->cmd->get_log_page.lsi;
|
||||
disk = req->ns->bdev->bd_disk;
|
||||
@@ -958,7 +960,7 @@ static void nvmet_execute_identify_nslist(struct nvmet_req *req, bool match_css)
|
||||
nvmet_for_each_enabled_ns(&ctrl->subsys->namespaces, idx, ns) {
|
||||
if (ns->nsid <= min_nsid)
|
||||
continue;
|
||||
if (match_css && req->ns->csi != req->cmd->identify.csi)
|
||||
if (match_css && ns->csi != req->cmd->identify.csi)
|
||||
continue;
|
||||
list[i++] = cpu_to_le32(ns->nsid);
|
||||
if (i == buf_size / sizeof(__le32))
|
||||
@@ -1335,7 +1337,7 @@ static u16 nvmet_set_feat_arbitration(struct nvmet_req *req)
|
||||
|
||||
void nvmet_execute_set_features(struct nvmet_req *req)
|
||||
{
|
||||
struct nvmet_subsys *subsys = nvmet_req_subsys(req);
|
||||
struct nvmet_ctrl *ctrl = nvmet_req_ctrl(req);
|
||||
u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10);
|
||||
u32 cdw11 = le32_to_cpu(req->cmd->common.cdw11);
|
||||
u16 status = 0;
|
||||
@@ -1357,7 +1359,7 @@ void nvmet_execute_set_features(struct nvmet_req *req)
|
||||
break;
|
||||
}
|
||||
nvmet_set_result(req,
|
||||
(subsys->max_qid - 1) | ((subsys->max_qid - 1) << 16));
|
||||
(ctrl->max_qid - 1) | ((ctrl->max_qid - 1) << 16));
|
||||
break;
|
||||
case NVME_FEAT_IRQ_COALESCE:
|
||||
status = nvmet_set_feat_irq_coalesce(req);
|
||||
@@ -1494,7 +1496,7 @@ void nvmet_get_feat_async_event(struct nvmet_req *req)
|
||||
|
||||
void nvmet_execute_get_features(struct nvmet_req *req)
|
||||
{
|
||||
struct nvmet_subsys *subsys = nvmet_req_subsys(req);
|
||||
struct nvmet_ctrl *ctrl = nvmet_req_ctrl(req);
|
||||
u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10);
|
||||
u16 status = 0;
|
||||
|
||||
@@ -1534,7 +1536,7 @@ void nvmet_execute_get_features(struct nvmet_req *req)
|
||||
break;
|
||||
case NVME_FEAT_NUM_QUEUES:
|
||||
nvmet_set_result(req,
|
||||
(subsys->max_qid-1) | ((subsys->max_qid-1) << 16));
|
||||
(ctrl->max_qid-1) | ((ctrl->max_qid-1) << 16));
|
||||
break;
|
||||
case NVME_FEAT_KATO:
|
||||
nvmet_get_feat_kato(req);
|
||||
|
||||
@@ -312,15 +312,17 @@ static ssize_t nvmet_param_mdts_store(struct config_item *item,
|
||||
const char *page, size_t count)
|
||||
{
|
||||
struct nvmet_port *port = to_nvmet_port(item);
|
||||
int ret;
|
||||
int ret, mdts;
|
||||
|
||||
if (nvmet_is_port_enabled(port, __func__))
|
||||
return -EACCES;
|
||||
ret = kstrtoint(page, 0, &port->mdts);
|
||||
if (ret) {
|
||||
pr_err("Invalid value '%s' for mdts\n", page);
|
||||
ret = kstrtoint(page, 0, &mdts);
|
||||
if (ret || mdts < 0 || mdts > NVMET_MAX_MDTS) {
|
||||
pr_err("Invalid value '%s' for mdts, should be 0-%d\n",
|
||||
page, NVMET_MAX_MDTS);
|
||||
return -EINVAL;
|
||||
}
|
||||
port->mdts = mdts;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
+32
-24
@@ -610,12 +610,14 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
|
||||
goto out_dev_put;
|
||||
}
|
||||
|
||||
if (percpu_ref_init(&ns->ref, nvmet_destroy_namespace, 0, GFP_KERNEL))
|
||||
ret = percpu_ref_init(&ns->ref, nvmet_destroy_namespace, 0, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto out_pr_exit;
|
||||
|
||||
nvmet_ns_changed(subsys, ns->nsid);
|
||||
ns->enabled = true;
|
||||
xa_set_mark(&subsys->namespaces, ns->nsid, NVMET_NS_ENABLED);
|
||||
nvmet_debugfs_ns_setup(ns);
|
||||
ret = 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&subsys->lock);
|
||||
@@ -642,6 +644,7 @@ void nvmet_ns_disable(struct nvmet_ns *ns)
|
||||
|
||||
ns->enabled = false;
|
||||
xa_clear_mark(&subsys->namespaces, ns->nsid, NVMET_NS_ENABLED);
|
||||
nvmet_debugfs_ns_free(ns);
|
||||
|
||||
list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry)
|
||||
pci_dev_put(radix_tree_delete(&ctrl->p2p_ns_map, ns->nsid));
|
||||
@@ -875,7 +878,7 @@ u16 nvmet_check_cqid(struct nvmet_ctrl *ctrl, u16 cqid, bool create)
|
||||
if (!ctrl->cqs)
|
||||
return NVME_SC_INTERNAL | NVME_STATUS_DNR;
|
||||
|
||||
if (cqid > ctrl->subsys->max_qid)
|
||||
if (cqid > ctrl->max_qid)
|
||||
return NVME_SC_QID_INVALID | NVME_STATUS_DNR;
|
||||
|
||||
if ((create && ctrl->cqs[cqid]) || (!create && !ctrl->cqs[cqid]))
|
||||
@@ -923,7 +926,7 @@ u16 nvmet_check_sqid(struct nvmet_ctrl *ctrl, u16 sqid,
|
||||
if (!ctrl->sqs)
|
||||
return NVME_SC_INTERNAL | NVME_STATUS_DNR;
|
||||
|
||||
if (sqid > ctrl->subsys->max_qid)
|
||||
if (sqid > ctrl->max_qid)
|
||||
return NVME_SC_QID_INVALID | NVME_STATUS_DNR;
|
||||
|
||||
if ((create && ctrl->sqs[sqid]) ||
|
||||
@@ -1652,23 +1655,6 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
|
||||
if (!ctrl->changed_ns_list)
|
||||
goto out_free_ctrl;
|
||||
|
||||
ctrl->sqs = kzalloc_objs(struct nvmet_sq *, subsys->max_qid + 1);
|
||||
if (!ctrl->sqs)
|
||||
goto out_free_changed_ns_list;
|
||||
|
||||
ctrl->cqs = kzalloc_objs(struct nvmet_cq *, subsys->max_qid + 1);
|
||||
if (!ctrl->cqs)
|
||||
goto out_free_sqs;
|
||||
|
||||
ret = ida_alloc_range(&cntlid_ida,
|
||||
subsys->cntlid_min, subsys->cntlid_max,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
args->status = NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
|
||||
goto out_free_cqs;
|
||||
}
|
||||
ctrl->cntlid = ret;
|
||||
|
||||
/*
|
||||
* Discovery controllers may use some arbitrary high value
|
||||
* in order to cleanup stale discovery sessions
|
||||
@@ -1682,9 +1668,28 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
|
||||
ctrl->err_counter = 0;
|
||||
spin_lock_init(&ctrl->error_lock);
|
||||
|
||||
nvmet_start_keep_alive_timer(ctrl);
|
||||
|
||||
down_read(&nvmet_config_sem);
|
||||
mutex_lock(&subsys->lock);
|
||||
|
||||
ctrl->max_qid = subsys->max_qid;
|
||||
|
||||
ctrl->sqs = kzalloc_objs(struct nvmet_sq *, ctrl->max_qid + 1);
|
||||
if (!ctrl->sqs)
|
||||
goto out_free_changed_ns_list;
|
||||
|
||||
ctrl->cqs = kzalloc_objs(struct nvmet_cq *, ctrl->max_qid + 1);
|
||||
if (!ctrl->cqs)
|
||||
goto out_free_sqs;
|
||||
|
||||
ret = ida_alloc_range(&cntlid_ida,
|
||||
subsys->cntlid_min, subsys->cntlid_max,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
args->status = NVME_SC_CONNECT_CTRL_BUSY | NVME_STATUS_DNR;
|
||||
goto out_free_cqs;
|
||||
}
|
||||
ctrl->cntlid = ret;
|
||||
|
||||
ret = nvmet_ctrl_init_pr(ctrl);
|
||||
if (ret)
|
||||
goto init_pr_fail;
|
||||
@@ -1692,6 +1697,9 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
|
||||
nvmet_setup_p2p_ns_map(ctrl, args->p2p_client);
|
||||
nvmet_debugfs_ctrl_setup(ctrl);
|
||||
mutex_unlock(&subsys->lock);
|
||||
up_read(&nvmet_config_sem);
|
||||
|
||||
nvmet_start_keep_alive_timer(ctrl);
|
||||
|
||||
if (args->hostid)
|
||||
uuid_copy(&ctrl->hostid, args->hostid);
|
||||
@@ -1721,14 +1729,14 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
|
||||
return ctrl;
|
||||
|
||||
init_pr_fail:
|
||||
mutex_unlock(&subsys->lock);
|
||||
nvmet_stop_keep_alive_timer(ctrl);
|
||||
ida_free(&cntlid_ida, ctrl->cntlid);
|
||||
out_free_cqs:
|
||||
kfree(ctrl->cqs);
|
||||
out_free_sqs:
|
||||
kfree(ctrl->sqs);
|
||||
out_free_changed_ns_list:
|
||||
mutex_unlock(&subsys->lock);
|
||||
up_read(&nvmet_config_sem);
|
||||
kfree(ctrl->changed_ns_list);
|
||||
out_free_ctrl:
|
||||
kfree(ctrl);
|
||||
|
||||
@@ -153,6 +153,109 @@ static int nvmet_ctrl_tls_concat_show(struct seq_file *m, void *p)
|
||||
NVMET_DEBUGFS_ATTR(nvmet_ctrl_tls_concat);
|
||||
#endif
|
||||
|
||||
static const char *const nvmet_pr_type_names[] = {
|
||||
[NVME_PR_WRITE_EXCLUSIVE] = "write_exclusive",
|
||||
[NVME_PR_EXCLUSIVE_ACCESS] = "exclusive_access",
|
||||
[NVME_PR_WRITE_EXCLUSIVE_REG_ONLY] = "write_exclusive_reg_only",
|
||||
[NVME_PR_EXCLUSIVE_ACCESS_REG_ONLY] = "exclusive_access_reg_only",
|
||||
[NVME_PR_WRITE_EXCLUSIVE_ALL_REGS] = "write_exclusive_all_regs",
|
||||
[NVME_PR_EXCLUSIVE_ACCESS_ALL_REGS] = "exclusive_access_all_regs",
|
||||
};
|
||||
|
||||
static const char *nvmet_pr_type_to_str(enum nvme_pr_type type)
|
||||
{
|
||||
if (type < ARRAY_SIZE(nvmet_pr_type_names) &&
|
||||
nvmet_pr_type_names[type])
|
||||
return nvmet_pr_type_names[type];
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static const char *const nvmet_pr_notify_names[] = {
|
||||
[NVME_PR_NOTIFY_BIT_REG_PREEMPTED] = "reg_preempted",
|
||||
[NVME_PR_NOTIFY_BIT_RESV_RELEASED] = "resv_released",
|
||||
[NVME_PR_NOTIFY_BIT_RESV_PREEMPTED] = "resv_preempted",
|
||||
};
|
||||
|
||||
static void nvmet_pr_notify_mask_to_str(struct seq_file *m, unsigned long mask)
|
||||
{
|
||||
bool sep = false;
|
||||
int i;
|
||||
|
||||
if (!mask) {
|
||||
seq_puts(m, "none");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nvmet_pr_notify_names); i++) {
|
||||
if (!test_bit(i, &mask) || !nvmet_pr_notify_names[i])
|
||||
continue;
|
||||
if (sep)
|
||||
seq_putc(m, ',');
|
||||
seq_puts(m, nvmet_pr_notify_names[i]);
|
||||
sep = true;
|
||||
}
|
||||
}
|
||||
|
||||
static int nvmet_ns_pr_show(struct seq_file *m, void *p)
|
||||
{
|
||||
struct nvmet_ns *ns = m->private;
|
||||
struct nvmet_pr *pr = &ns->pr;
|
||||
struct nvmet_pr_registrant *holder, *reg;
|
||||
|
||||
seq_printf(m, "enable=%d\n", pr->enable);
|
||||
if (!pr->enable)
|
||||
return 0;
|
||||
|
||||
seq_printf(m, "generation=%u\n", atomic_read(&pr->generation));
|
||||
seq_puts(m, "notify_mask=");
|
||||
nvmet_pr_notify_mask_to_str(m, pr->notify_mask);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
rcu_read_lock();
|
||||
holder = rcu_dereference(pr->holder);
|
||||
if (holder) {
|
||||
seq_printf(m, "rtype=%s\n",
|
||||
nvmet_pr_type_to_str(holder->rtype));
|
||||
seq_printf(m, "holder=%pUb,0x%llx\n",
|
||||
&holder->hostid, holder->rkey);
|
||||
} else {
|
||||
seq_puts(m, "rtype=none\n");
|
||||
seq_puts(m, "holder=none\n");
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(reg, &pr->registrant_list, entry) {
|
||||
seq_printf(m, "reg=%pUb,0x%llx\n",
|
||||
®->hostid, reg->rkey);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
NVMET_DEBUGFS_ATTR(nvmet_ns_pr);
|
||||
|
||||
void nvmet_debugfs_ns_setup(struct nvmet_ns *ns)
|
||||
{
|
||||
char name[16];
|
||||
struct dentry *parent = ns->subsys->debugfs_dir;
|
||||
|
||||
if (!parent)
|
||||
return;
|
||||
snprintf(name, sizeof(name), "ns%u", ns->nsid);
|
||||
ns->debugfs_dir = debugfs_create_dir(name, parent);
|
||||
if (IS_ERR(ns->debugfs_dir)) {
|
||||
ns->debugfs_dir = NULL;
|
||||
return;
|
||||
}
|
||||
debugfs_create_file("reservation", 0400, ns->debugfs_dir, ns,
|
||||
&nvmet_ns_pr_fops);
|
||||
}
|
||||
|
||||
void nvmet_debugfs_ns_free(struct nvmet_ns *ns)
|
||||
{
|
||||
debugfs_remove_recursive(ns->debugfs_dir);
|
||||
ns->debugfs_dir = NULL;
|
||||
}
|
||||
|
||||
int nvmet_debugfs_ctrl_setup(struct nvmet_ctrl *ctrl)
|
||||
{
|
||||
char name[32];
|
||||
|
||||
@@ -14,6 +14,8 @@ int nvmet_debugfs_subsys_setup(struct nvmet_subsys *subsys);
|
||||
void nvmet_debugfs_subsys_free(struct nvmet_subsys *subsys);
|
||||
int nvmet_debugfs_ctrl_setup(struct nvmet_ctrl *ctrl);
|
||||
void nvmet_debugfs_ctrl_free(struct nvmet_ctrl *ctrl);
|
||||
void nvmet_debugfs_ns_setup(struct nvmet_ns *ns);
|
||||
void nvmet_debugfs_ns_free(struct nvmet_ns *ns);
|
||||
|
||||
int __init nvmet_init_debugfs(void);
|
||||
void nvmet_exit_debugfs(void);
|
||||
@@ -30,6 +32,9 @@ static inline int nvmet_debugfs_ctrl_setup(struct nvmet_ctrl *ctrl)
|
||||
}
|
||||
static inline void nvmet_debugfs_ctrl_free(struct nvmet_ctrl *ctrl) {}
|
||||
|
||||
static inline void nvmet_debugfs_ns_setup(struct nvmet_ns *ns) {}
|
||||
static inline void nvmet_debugfs_ns_free(struct nvmet_ns *ns) {}
|
||||
|
||||
static inline int __init nvmet_init_debugfs(void)
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/random.h>
|
||||
#include <linux/nvme-auth.h>
|
||||
#include <crypto/kpp.h>
|
||||
#include <crypto/utils.h>
|
||||
#include "nvmet.h"
|
||||
|
||||
static void nvmet_auth_expired_work(struct work_struct *work)
|
||||
@@ -30,12 +31,16 @@ void nvmet_auth_sq_init(struct nvmet_sq *sq)
|
||||
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
|
||||
}
|
||||
|
||||
static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
|
||||
static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d, u32 tl)
|
||||
{
|
||||
struct nvmet_ctrl *ctrl = req->sq->ctrl;
|
||||
struct nvmf_auth_dhchap_negotiate_data *data = d;
|
||||
int i, hash_id = 0, fallback_hash_id = 0, dhgid, fallback_dhgid;
|
||||
|
||||
if (tl < sizeof(*data) +
|
||||
sizeof(struct nvmf_auth_dhchap_protocol_descriptor))
|
||||
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
|
||||
|
||||
pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n",
|
||||
__func__, ctrl->cntlid, req->sq->qid,
|
||||
data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
|
||||
@@ -71,6 +76,10 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
|
||||
NVME_AUTH_DHCHAP_AUTH_ID)
|
||||
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
|
||||
|
||||
if (data->auth_protocol[0].dhchap.dhlen > NVME_AUTH_DHCHAP_MAX_DH_IDS ||
|
||||
data->auth_protocol[0].dhchap.halen > NVME_AUTH_DHCHAP_MAX_HASH_IDS)
|
||||
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;
|
||||
|
||||
for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
|
||||
u8 host_hmac_id = data->auth_protocol[0].dhchap.idlist[i];
|
||||
|
||||
@@ -177,7 +186,7 @@ static u8 nvmet_auth_reply(struct nvmet_req *req, void *d, u32 tl)
|
||||
return NVME_AUTH_DHCHAP_FAILURE_FAILED;
|
||||
}
|
||||
|
||||
if (memcmp(data->rval, response, data->hl)) {
|
||||
if (crypto_memneq(data->rval, response, data->hl)) {
|
||||
pr_info("ctrl %d qid %d host response mismatch\n",
|
||||
ctrl->cntlid, req->sq->qid);
|
||||
pr_debug("ctrl %d qid %d rval %*ph\n",
|
||||
@@ -316,7 +325,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
|
||||
} else if (data->auth_id != req->sq->dhchap_step)
|
||||
goto done_failure1;
|
||||
/* Validate negotiation parameters */
|
||||
dhchap_status = nvmet_auth_negotiate(req, d);
|
||||
dhchap_status = nvmet_auth_negotiate(req, d, tl);
|
||||
if (dhchap_status == 0)
|
||||
req->sq->dhchap_step =
|
||||
NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
|
||||
@@ -557,7 +566,7 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
|
||||
return;
|
||||
}
|
||||
|
||||
d = kmalloc(al, GFP_KERNEL);
|
||||
d = kzalloc(al, GFP_KERNEL);
|
||||
if (!d) {
|
||||
status = NVME_SC_INTERNAL;
|
||||
goto done;
|
||||
|
||||
@@ -370,7 +370,7 @@ static void nvmet_execute_io_connect(struct nvmet_req *req)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(qid > ctrl->subsys->max_qid)) {
|
||||
if (unlikely(qid > ctrl->max_qid)) {
|
||||
pr_warn("invalid queue id (%d)\n", qid);
|
||||
status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR;
|
||||
req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(qid);
|
||||
|
||||
@@ -566,7 +566,7 @@ out_fail:
|
||||
list_del(&iod->ls_rcv_list);
|
||||
}
|
||||
|
||||
kfree(iod);
|
||||
kfree(tgtport->iod);
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,9 @@ struct nvmet_ns {
|
||||
u8 csi;
|
||||
struct nvmet_pr pr;
|
||||
struct xarray pr_per_ctrl_refs;
|
||||
#ifdef CONFIG_NVME_TARGET_DEBUGFS
|
||||
struct dentry *debugfs_dir;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct nvmet_ns *to_nvmet_ns(struct config_item *item)
|
||||
@@ -265,6 +268,7 @@ struct nvmet_ctrl {
|
||||
|
||||
uuid_t hostid;
|
||||
u16 cntlid;
|
||||
u16 max_qid;
|
||||
u32 kato;
|
||||
|
||||
struct nvmet_port *port;
|
||||
@@ -753,6 +757,11 @@ static inline struct nvmet_subsys *nvmet_req_subsys(struct nvmet_req *req)
|
||||
return req->sq->ctrl->subsys;
|
||||
}
|
||||
|
||||
static inline struct nvmet_ctrl *nvmet_req_ctrl(struct nvmet_req *req)
|
||||
{
|
||||
return req->sq->ctrl;
|
||||
}
|
||||
|
||||
static inline bool nvmet_is_disc_subsys(struct nvmet_subsys *subsys)
|
||||
{
|
||||
return subsys->type != NVME_NQN_NVME;
|
||||
|
||||
@@ -53,13 +53,22 @@ static u16 nvmet_passthru_override_id_descs(struct nvmet_req *req)
|
||||
for (pos = 0; pos < NVME_IDENTIFY_DATA_SIZE; pos += len) {
|
||||
struct nvme_ns_id_desc *cur = data + pos;
|
||||
|
||||
if (pos + sizeof(*cur) > NVME_IDENTIFY_DATA_SIZE)
|
||||
break;
|
||||
|
||||
if (cur->nidl == 0)
|
||||
break;
|
||||
|
||||
if (cur->nidt == NVME_NIDT_CSI) {
|
||||
if (pos + sizeof(*cur) + NVME_NIDT_CSI_LEN >
|
||||
NVME_IDENTIFY_DATA_SIZE)
|
||||
break;
|
||||
|
||||
memcpy(&csi, cur + 1, NVME_NIDT_CSI_LEN);
|
||||
csi_seen = true;
|
||||
break;
|
||||
}
|
||||
|
||||
len = sizeof(struct nvme_ns_id_desc) + cur->nidl;
|
||||
}
|
||||
|
||||
|
||||
@@ -1339,6 +1339,7 @@ err_unmap_queue:
|
||||
nvmet_pci_epf_mem_unmap(ctrl->nvme_epf, &cq->pci_map);
|
||||
err_internal:
|
||||
status = NVME_SC_INTERNAL | NVME_STATUS_DNR;
|
||||
nvmet_cq_put(&cq->nvme_cq);
|
||||
err:
|
||||
if (test_and_clear_bit(NVMET_PCI_EPF_Q_IRQ_ENABLED, &cq->flags))
|
||||
nvmet_pci_epf_remove_irq_vector(ctrl, cq->vector);
|
||||
@@ -1594,6 +1595,7 @@ static void nvmet_pci_epf_exec_iod_work(struct work_struct *work)
|
||||
struct nvmet_pci_epf_iod *iod =
|
||||
container_of(work, struct nvmet_pci_epf_iod, work);
|
||||
struct nvmet_req *req = &iod->req;
|
||||
bool no_wait;
|
||||
int ret;
|
||||
|
||||
if (!iod->ctrl->link_up) {
|
||||
@@ -1638,14 +1640,16 @@ static void nvmet_pci_epf_exec_iod_work(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
req->execute(req);
|
||||
|
||||
/*
|
||||
* If we do not have data to transfer after the command execution
|
||||
* finishes, nvmet_pci_epf_queue_response() will complete the command
|
||||
* directly. No need to wait for the completion in this case.
|
||||
*/
|
||||
if (!iod->data_len || iod->dma_dir != DMA_TO_DEVICE)
|
||||
no_wait = !iod->data_len || iod->dma_dir != DMA_TO_DEVICE;
|
||||
|
||||
req->execute(req);
|
||||
|
||||
if (no_wait)
|
||||
return;
|
||||
|
||||
wait_for_completion(&iod->done);
|
||||
@@ -2077,7 +2081,7 @@ static int nvmet_pci_epf_create_ctrl(struct nvmet_pci_epf *nvme_epf,
|
||||
}
|
||||
|
||||
/* Allocate our queues, up to the maximum number. */
|
||||
ctrl->nr_queues = min(ctrl->tctrl->subsys->max_qid + 1, max_nr_queues);
|
||||
ctrl->nr_queues = min(ctrl->tctrl->max_qid + 1, max_nr_queues);
|
||||
ret = nvmet_pci_epf_alloc_queues(ctrl);
|
||||
if (ret)
|
||||
goto out_put_ctrl;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <linux/unaligned.h>
|
||||
#include "nvmet.h"
|
||||
|
||||
#define NVMET_PR_NOTIFI_MASK_ALL \
|
||||
#define NVMET_PR_NOTIFY_MASK_ALL \
|
||||
(1 << NVME_PR_NOTIFY_BIT_REG_PREEMPTED | \
|
||||
1 << NVME_PR_NOTIFY_BIT_RESV_RELEASED | \
|
||||
1 << NVME_PR_NOTIFY_BIT_RESV_PREEMPTED)
|
||||
@@ -44,7 +44,7 @@ u16 nvmet_set_feat_resv_notif_mask(struct nvmet_req *req, u32 mask)
|
||||
unsigned long idx;
|
||||
u16 status;
|
||||
|
||||
if (mask & ~(NVMET_PR_NOTIFI_MASK_ALL)) {
|
||||
if (mask & ~(NVMET_PR_NOTIFY_MASK_ALL)) {
|
||||
req->error_loc = offsetof(struct nvme_common_command, cdw11);
|
||||
return NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ static void nvmet_pr_resv_released(struct nvmet_pr *pr, uuid_t *hostid)
|
||||
nvmet_pr_add_resv_log(ctrl,
|
||||
NVME_PR_LOG_RESERVATION_RELEASED, ns->nsid);
|
||||
nvmet_add_async_event(ctrl, NVME_AER_CSS,
|
||||
NVME_AEN_RESV_LOG_PAGE_AVALIABLE,
|
||||
NVME_AEN_RESV_LOG_PAGE_AVAILABLE,
|
||||
NVME_LOG_RESERVATION);
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ static void nvmet_pr_send_event_to_host(struct nvmet_pr *pr, uuid_t *hostid,
|
||||
if (uuid_equal(hostid, &ctrl->hostid)) {
|
||||
nvmet_pr_add_resv_log(ctrl, log_type, ns->nsid);
|
||||
nvmet_add_async_event(ctrl, NVME_AER_CSS,
|
||||
NVME_AEN_RESV_LOG_PAGE_AVALIABLE,
|
||||
NVME_AEN_RESV_LOG_PAGE_AVAILABLE,
|
||||
NVME_LOG_RESERVATION);
|
||||
}
|
||||
}
|
||||
@@ -201,7 +201,7 @@ static void nvmet_pr_resv_preempted(struct nvmet_pr *pr, uuid_t *hostid)
|
||||
return;
|
||||
|
||||
nvmet_pr_send_event_to_host(pr, hostid,
|
||||
NVME_PR_LOG_RESERVATOIN_PREEMPTED);
|
||||
NVME_PR_LOG_RESERVATION_PREEMPTED);
|
||||
}
|
||||
|
||||
static void nvmet_pr_registration_preempted(struct nvmet_pr *pr,
|
||||
@@ -355,9 +355,15 @@ static u16 nvmet_pr_replace(struct nvmet_req *req,
|
||||
u16 status = NVME_SC_RESERVATION_CONFLICT | NVME_STATUS_DNR;
|
||||
struct nvmet_ctrl *ctrl = req->sq->ctrl;
|
||||
struct nvmet_pr *pr = &req->ns->pr;
|
||||
struct nvmet_pr_registrant *reg;
|
||||
struct nvmet_pr_registrant *reg, *new = NULL;
|
||||
u64 nrkey = le64_to_cpu(d->nrkey);
|
||||
|
||||
if (ignore_key && nrkey) {
|
||||
new = kzalloc_obj(*new);
|
||||
if (!new)
|
||||
return NVME_SC_INTERNAL;
|
||||
}
|
||||
|
||||
down(&pr->pr_sem);
|
||||
list_for_each_entry_rcu(reg, &pr->registrant_list, entry) {
|
||||
if (uuid_equal(®->hostid, &ctrl->hostid)) {
|
||||
@@ -365,9 +371,26 @@ static u16 nvmet_pr_replace(struct nvmet_req *req,
|
||||
status = nvmet_pr_update_reg_attr(pr, reg,
|
||||
nvmet_pr_update_reg_rkey,
|
||||
&nrkey);
|
||||
break;
|
||||
goto free_data;
|
||||
}
|
||||
}
|
||||
|
||||
if (ignore_key) {
|
||||
if (!nrkey) {
|
||||
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
||||
goto free_data;
|
||||
}
|
||||
INIT_LIST_HEAD(&new->entry);
|
||||
new->rkey = nrkey;
|
||||
uuid_copy(&new->hostid, &ctrl->hostid);
|
||||
list_add_tail_rcu(&new->entry, &pr->registrant_list);
|
||||
status = NVME_SC_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
free_data:
|
||||
kfree(new);
|
||||
out:
|
||||
up(&pr->pr_sem);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -657,18 +657,25 @@ static void nvmet_rdma_rw_ctx_destroy(struct nvmet_rdma_rsp *rsp)
|
||||
req->sg, req->sg_cnt, nvmet_data_dir(req));
|
||||
}
|
||||
|
||||
static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp)
|
||||
static void nvmet_rdma_free_rsp_resources(struct nvmet_rdma_rsp *rsp)
|
||||
{
|
||||
struct nvmet_rdma_queue *queue = rsp->queue;
|
||||
|
||||
atomic_add(1 + rsp->n_rdma, &queue->sq_wr_avail);
|
||||
|
||||
if (rsp->n_rdma)
|
||||
nvmet_rdma_rw_ctx_destroy(rsp);
|
||||
|
||||
if (rsp->req.sg < rsp->cmd->inline_sg ||
|
||||
rsp->req.sg >= rsp->cmd->inline_sg + queue->dev->inline_page_count)
|
||||
nvmet_req_free_sgls(&rsp->req);
|
||||
}
|
||||
|
||||
static void nvmet_rdma_release_rsp(struct nvmet_rdma_rsp *rsp)
|
||||
{
|
||||
struct nvmet_rdma_queue *queue = rsp->queue;
|
||||
|
||||
atomic_add(1 + rsp->n_rdma, &queue->sq_wr_avail);
|
||||
|
||||
nvmet_rdma_free_rsp_resources(rsp);
|
||||
|
||||
if (unlikely(!list_empty_careful(&queue->rsp_wr_wait_list)))
|
||||
nvmet_rdma_process_wr_wait_list(queue);
|
||||
@@ -1338,9 +1345,27 @@ err_destroy_cq:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static bool nvmet_rdma_reclaim_rsp(struct sbitmap *sb, unsigned int bitnr,
|
||||
void *data)
|
||||
{
|
||||
struct nvmet_rdma_queue *queue = data;
|
||||
|
||||
nvmet_rdma_free_rsp_resources(&queue->rsps[bitnr]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nvmet_rdma_destroy_queue_ib(struct nvmet_rdma_queue *queue)
|
||||
{
|
||||
ib_drain_qp(queue->qp);
|
||||
|
||||
/*
|
||||
* Reclaim resources of a response that is still in-flight when the
|
||||
* queue is being torn down. This happens when the connection was
|
||||
* forcefully disconnected while an I/O is in flight.
|
||||
*/
|
||||
sbitmap_for_each_set(&queue->rsp_tags, nvmet_rdma_reclaim_rsp, queue);
|
||||
|
||||
if (queue->cm_id)
|
||||
rdma_destroy_id(queue->cm_id);
|
||||
ib_destroy_qp(queue->qp);
|
||||
|
||||
@@ -422,6 +422,19 @@ static int nvmet_tcp_map_data(struct nvmet_tcp_cmd *cmd)
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* inline_data_size only bounds the in-capsule (type 0x01) SGL
|
||||
* descriptor below. A non-inline transport SGL data-block
|
||||
* descriptor skips that check entirely and would otherwise reach
|
||||
* sgl_alloc() with an attacker-controlled len of up to 4 GiB,
|
||||
* pinning that much kernel memory for a command that may never
|
||||
* complete. Bound every descriptor type here, before allocating
|
||||
* anything, using the same ceiling this file already applies to
|
||||
* per-PDU H2C data.
|
||||
*/
|
||||
if (len > NVMET_TCP_MAXH2CDATA)
|
||||
return NVME_SC_SGL_INVALID_DATA | NVME_STATUS_DNR;
|
||||
|
||||
if (sgl->type == ((NVME_SGL_FMT_DATA_DESC << 4) |
|
||||
NVME_SGL_FMT_OFFSET)) {
|
||||
if (!nvme_is_write(cmd->req.cmd))
|
||||
@@ -433,13 +446,15 @@ static int nvmet_tcp_map_data(struct nvmet_tcp_cmd *cmd)
|
||||
}
|
||||
cmd->req.transfer_len += len;
|
||||
|
||||
cmd->req.sg = sgl_alloc(len, GFP_KERNEL, &cmd->req.sg_cnt);
|
||||
cmd->req.sg = sgl_alloc(len, GFP_KERNEL | __GFP_NOWARN,
|
||||
&cmd->req.sg_cnt);
|
||||
if (!cmd->req.sg)
|
||||
return NVME_SC_INTERNAL;
|
||||
cmd->cur_sg = cmd->req.sg;
|
||||
|
||||
if (nvmet_tcp_has_data_in(cmd)) {
|
||||
cmd->iov = kmalloc_objs(*cmd->iov, cmd->req.sg_cnt);
|
||||
cmd->iov = kmalloc_objs(*cmd->iov, cmd->req.sg_cnt,
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!cmd->iov)
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ void nvmet_execute_identify_ns_zns(struct nvmet_req *req)
|
||||
mutex_unlock(&req->ns->subsys->lock);
|
||||
}
|
||||
|
||||
if (!bdev_is_zoned(req->ns->bdev)) {
|
||||
if (!req->ns->bdev || !bdev_is_zoned(req->ns->bdev)) {
|
||||
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
||||
req->error_loc = offsetof(struct nvme_identify, nsid);
|
||||
goto out;
|
||||
@@ -295,11 +295,18 @@ static void nvmet_bdev_zone_zmgmt_recv_work(struct work_struct *w)
|
||||
}
|
||||
|
||||
/*
|
||||
* When partial bit is set nr_zones must indicate the number of zone
|
||||
* descriptors actually transferred.
|
||||
* Partial report (PR bit set): the host accepts an incomplete listing,
|
||||
* so cap Number of Zones to the descriptors that fit in the buffer.
|
||||
* Full report (PR bit clear): Number of Zones is the match count; fail
|
||||
* if the buffer cannot hold every matching zone descriptor.
|
||||
*/
|
||||
if (req->cmd->zmr.pr)
|
||||
if (req->cmd->zmr.pr) {
|
||||
rz_data.nr_zones = min(rz_data.nr_zones, rz_data.out_nr_zones);
|
||||
} else if (rz_data.nr_zones > rz_data.out_nr_zones) {
|
||||
req->error_loc = offsetof(struct nvme_zone_mgmt_recv_cmd, numd);
|
||||
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nr_zones = cpu_to_le64(rz_data.nr_zones);
|
||||
status = nvmet_copy_to_sgl(req, 0, &nr_zones, sizeof(nr_zones));
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
# S/390 block devices
|
||||
#
|
||||
|
||||
CONTEXT_ANALYSIS := y
|
||||
|
||||
dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o
|
||||
dasd_fba_mod-objs := dasd_fba.o
|
||||
dasd_diag_mod-objs := dasd_diag.o
|
||||
|
||||
+191
-55
@@ -87,35 +87,57 @@ struct dasd_device *dasd_alloc_device(void)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
|
||||
device = kzalloc_obj(struct dasd_device, GFP_ATOMIC);
|
||||
device = kzalloc_obj(struct dasd_device, GFP_KERNEL);
|
||||
if (!device)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Get two pages for normal block device operations. */
|
||||
device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
|
||||
/*
|
||||
* Four pages: a full-track ITCW is roughly twice the size of a plain
|
||||
* track-mode one, so this keeps two maximum-size requests in flight.
|
||||
*/
|
||||
device->ccw_mem = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, 2);
|
||||
if (!device->ccw_mem) {
|
||||
kfree(device);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
/* Get one page for error recovery. */
|
||||
device->erp_mem = (void *) get_zeroed_page(GFP_ATOMIC | GFP_DMA);
|
||||
if (!device->erp_mem) {
|
||||
free_pages((unsigned long) device->ccw_mem, 1);
|
||||
/* per-request track-filler buffers (R0 + count records) */
|
||||
device->fill_mem = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, 1);
|
||||
if (!device->fill_mem) {
|
||||
free_pages((unsigned long)device->ccw_mem, 2);
|
||||
kfree(device);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
/* Get two pages for ese format. */
|
||||
device->ese_mem = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
|
||||
/* Get one page for error recovery. */
|
||||
device->erp_mem = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
||||
if (!device->erp_mem) {
|
||||
free_pages((unsigned long)device->fill_mem, 1);
|
||||
free_pages((unsigned long)device->ccw_mem, 2);
|
||||
kfree(device);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
/* sized like ccw_chunks: two max-size NRF format requests in flight */
|
||||
device->ese_mem = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, 2);
|
||||
if (!device->ese_mem) {
|
||||
free_page((unsigned long) device->erp_mem);
|
||||
free_pages((unsigned long) device->ccw_mem, 1);
|
||||
free_page((unsigned long)device->erp_mem);
|
||||
free_pages((unsigned long)device->fill_mem, 1);
|
||||
free_pages((unsigned long)device->ccw_mem, 2);
|
||||
kfree(device);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
device->nulldata = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
||||
if (!device->nulldata) {
|
||||
free_page((unsigned long)device->erp_mem);
|
||||
free_pages((unsigned long)device->fill_mem, 1);
|
||||
free_pages((unsigned long)device->ccw_mem, 2);
|
||||
free_pages((unsigned long)device->ese_mem, 2);
|
||||
kfree(device);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
|
||||
dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE * 4);
|
||||
dasd_init_chunklist(&device->fill_chunks, device->fill_mem, PAGE_SIZE * 2);
|
||||
dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
|
||||
dasd_init_chunklist(&device->ese_chunks, device->ese_mem, PAGE_SIZE * 2);
|
||||
dasd_init_chunklist(&device->ese_chunks, device->ese_mem, PAGE_SIZE * 4);
|
||||
spin_lock_init(&device->mem_lock);
|
||||
atomic_set(&device->tasklet_scheduled, 0);
|
||||
tasklet_init(&device->tasklet, dasd_device_tasklet,
|
||||
@@ -138,9 +160,11 @@ struct dasd_device *dasd_alloc_device(void)
|
||||
void dasd_free_device(struct dasd_device *device)
|
||||
{
|
||||
kfree(device->private);
|
||||
free_pages((unsigned long) device->ese_mem, 1);
|
||||
free_page((unsigned long) device->erp_mem);
|
||||
free_pages((unsigned long) device->ccw_mem, 1);
|
||||
free_pages((unsigned long)device->ese_mem, 2);
|
||||
free_page((unsigned long)device->erp_mem);
|
||||
free_pages((unsigned long)device->fill_mem, 1);
|
||||
free_pages((unsigned long)device->ccw_mem, 2);
|
||||
free_page((unsigned long)device->nulldata);
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
@@ -164,6 +188,8 @@ struct dasd_block *dasd_alloc_block(void)
|
||||
spin_lock_init(&block->queue_lock);
|
||||
INIT_LIST_HEAD(&block->format_list);
|
||||
spin_lock_init(&block->format_lock);
|
||||
INIT_LIST_HEAD(&block->ese_staging);
|
||||
spin_lock_init(&block->ese_lock);
|
||||
timer_setup(&block->timer, dasd_block_timeout, 0);
|
||||
spin_lock_init(&block->profile.lock);
|
||||
|
||||
@@ -328,17 +354,19 @@ static int dasd_state_basic_to_ready(struct dasd_device *device)
|
||||
*/
|
||||
lim.dma_alignment = lim.logical_block_size - 1;
|
||||
|
||||
if (device->discipline->has_discard) {
|
||||
if (device->features & DASD_FEATURE_DISCARD) {
|
||||
unsigned int max_bytes;
|
||||
|
||||
lim.discard_granularity = block->bp_block;
|
||||
|
||||
/* Calculate max_discard_sectors and make it PAGE aligned */
|
||||
max_bytes = USHRT_MAX * block->bp_block;
|
||||
max_bytes = ALIGN_DOWN(max_bytes, PAGE_SIZE);
|
||||
|
||||
lim.max_hw_discard_sectors = max_bytes / block->bp_block;
|
||||
lim.max_write_zeroes_sectors = lim.max_hw_discard_sectors;
|
||||
if (device->discipline->disc_limits) {
|
||||
device->discipline->disc_limits(block, &lim);
|
||||
} else {
|
||||
lim.discard_granularity = block->bp_block;
|
||||
/* Calculate max_discard_sectors and make it PAGE aligned */
|
||||
max_bytes = USHRT_MAX * block->bp_block;
|
||||
max_bytes = ALIGN_DOWN(max_bytes, PAGE_SIZE);
|
||||
lim.max_hw_discard_sectors = max_bytes / block->bp_block;
|
||||
lim.max_write_zeroes_sectors = lim.max_hw_discard_sectors;
|
||||
}
|
||||
}
|
||||
rc = queue_limits_commit_update(block->gdp->queue, &lim);
|
||||
if (rc)
|
||||
@@ -364,7 +392,8 @@ int _wait_for_empty_queues(struct dasd_device *device)
|
||||
{
|
||||
if (device->block)
|
||||
return list_empty(&device->ccw_queue) &&
|
||||
list_empty(&device->block->ccw_queue);
|
||||
list_empty(&device->block->ccw_queue) &&
|
||||
list_empty(&device->block->ese_staging);
|
||||
else
|
||||
return list_empty(&device->ccw_queue);
|
||||
}
|
||||
@@ -1224,7 +1253,18 @@ void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&device->mem_lock, flags);
|
||||
dasd_free_chunk(&device->ccw_chunks, cqr->mem_chunk);
|
||||
/*
|
||||
* Free the request block from the pool it came from: smalloc() sets
|
||||
* mem_chunk (ccw_chunks), fmalloc() leaves it NULL (ese_chunks). A
|
||||
* full-track request also frees its track-filler buffer.
|
||||
*/
|
||||
if (cqr->filldata)
|
||||
dasd_free_chunk(&device->fill_chunks, cqr->filldata);
|
||||
if (cqr->mem_chunk)
|
||||
dasd_free_chunk(&device->ccw_chunks, cqr->mem_chunk);
|
||||
else
|
||||
dasd_free_chunk(&device->ese_chunks, cqr);
|
||||
|
||||
spin_unlock_irqrestore(&device->mem_lock, flags);
|
||||
dasd_put_device(device);
|
||||
}
|
||||
@@ -1235,6 +1275,8 @@ void dasd_ffree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&device->mem_lock, flags);
|
||||
if (cqr->filldata)
|
||||
dasd_free_chunk(&device->fill_chunks, cqr->filldata);
|
||||
dasd_free_chunk(&device->ese_chunks, cqr);
|
||||
spin_unlock_irqrestore(&device->mem_lock, flags);
|
||||
dasd_put_device(device);
|
||||
@@ -1362,13 +1404,6 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
|
||||
if (!cqr->lpm)
|
||||
cqr->lpm = dasd_path_get_opm(device);
|
||||
}
|
||||
/*
|
||||
* remember the amount of formatted tracks to prevent double format on
|
||||
* ESE devices
|
||||
*/
|
||||
if (cqr->block)
|
||||
cqr->trkcount = atomic_read(&cqr->block->trkcount);
|
||||
|
||||
if (cqr->cpmode == 1) {
|
||||
rc = ccw_device_tm_start(device->cdev, cqr->cpaddr,
|
||||
(long) cqr, cqr->lpm);
|
||||
@@ -1585,7 +1620,7 @@ static int dasd_ese_oos_cond(u8 *sense)
|
||||
void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
|
||||
struct irb *irb)
|
||||
{
|
||||
struct dasd_ccw_req *cqr, *next, *fcqr;
|
||||
struct dasd_ccw_req *cqr, *next;
|
||||
struct dasd_device *device;
|
||||
unsigned long now;
|
||||
int nrf_suppressed = 0;
|
||||
@@ -1698,33 +1733,32 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
|
||||
return;
|
||||
}
|
||||
if (rq_data_dir(req) == READ) {
|
||||
device->discipline->ese_read(cqr, irb);
|
||||
cqr->status = DASD_CQR_SUCCESS;
|
||||
if (device->discipline->ese_read(cqr, irb))
|
||||
cqr->status = DASD_CQR_ERROR;
|
||||
else
|
||||
cqr->status = DASD_CQR_SUCCESS;
|
||||
cqr->stopclk = now;
|
||||
dasd_device_clear_timer(device);
|
||||
dasd_schedule_device_bh(device);
|
||||
return;
|
||||
}
|
||||
fcqr = device->discipline->ese_format(device, cqr, irb);
|
||||
if (IS_ERR(fcqr)) {
|
||||
if (PTR_ERR(fcqr) == -EINVAL) {
|
||||
cqr->status = DASD_CQR_ERROR;
|
||||
return;
|
||||
}
|
||||
if (cqr->filldata) {
|
||||
/*
|
||||
* If we can't format now, let the request go
|
||||
* one extra round. Maybe we can format later.
|
||||
* A WRITE_FULL_TRACK cqr carries the complete
|
||||
* track image; INV_TRACK_FORMAT here means the
|
||||
* generated image or the media itself is bad, not
|
||||
* that the track still needs formatting - retrying
|
||||
* via ese_format() would just resubmit the same
|
||||
* write. Let it fail instead.
|
||||
*/
|
||||
cqr->status = DASD_CQR_QUEUED;
|
||||
dasd_schedule_device_bh(device);
|
||||
return;
|
||||
} else {
|
||||
fcqr->status = DASD_CQR_QUEUED;
|
||||
cqr->status = DASD_CQR_QUEUED;
|
||||
list_add(&fcqr->devlist, &device->ccw_queue);
|
||||
cqr->status = DASD_CQR_ERROR;
|
||||
cqr->stopclk = now;
|
||||
dasd_device_clear_timer(device);
|
||||
dasd_schedule_device_bh(device);
|
||||
return;
|
||||
}
|
||||
device->discipline->ese_format(device, cqr, irb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for clear pending */
|
||||
@@ -1883,6 +1917,17 @@ static void __dasd_process_cqr(struct dasd_device *device,
|
||||
case DASD_CQR_CLEARED:
|
||||
cqr->status = DASD_CQR_TERMINATED;
|
||||
break;
|
||||
case DASD_CQR_ABORT:
|
||||
cqr->status = DASD_CQR_ABORTED;
|
||||
/*
|
||||
* ABORT is only set on the block-layer origin write that a
|
||||
* full-track format replaces. Clear the callback so the request
|
||||
* is not completed here - the replacement completes it. Internal
|
||||
* requests never take this path, so no sleep_on waiter is left
|
||||
* without its wakeup.
|
||||
*/
|
||||
cqr->callback = NULL;
|
||||
break;
|
||||
default:
|
||||
dev_err(&device->cdev->dev,
|
||||
"Unexpected CQR status %02x", cqr->status);
|
||||
@@ -2210,6 +2255,7 @@ EXPORT_SYMBOL(dasd_add_request_tail);
|
||||
void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
|
||||
{
|
||||
spin_lock_irq(get_ccwdev_lock(cqr->startdev->cdev));
|
||||
cqr->endclk = get_tod_clock();
|
||||
cqr->callback_data = DASD_SLEEPON_END_TAG;
|
||||
spin_unlock_irq(get_ccwdev_lock(cqr->startdev->cdev));
|
||||
wake_up(&generic_waitq);
|
||||
@@ -2686,6 +2732,13 @@ static void __dasd_process_erp(struct dasd_device *device,
|
||||
|
||||
if (cqr->status == DASD_CQR_DONE)
|
||||
DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
|
||||
else if (cqr->status == DASD_CQR_ABORTED)
|
||||
/*
|
||||
* ESE format aborts the request and replaces it with a format
|
||||
* CQR - this is not an ERP failure.
|
||||
*/
|
||||
DBF_DEV_EVENT(DBF_NOTICE, device, "%s",
|
||||
"ERP request aborted, replaced by ESE format");
|
||||
else
|
||||
dev_err(&device->cdev->dev, "ERP failed for the DASD\n");
|
||||
erp_fn = device->discipline->erp_postaction(cqr);
|
||||
@@ -2697,17 +2750,23 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr)
|
||||
struct request *req;
|
||||
blk_status_t error = BLK_STS_OK;
|
||||
unsigned int proc_bytes;
|
||||
int status;
|
||||
int status, intrc;
|
||||
|
||||
req = (struct request *) cqr->callback_data;
|
||||
dasd_profile_end(cqr->block, cqr, req);
|
||||
|
||||
/*
|
||||
* free_cp() returns the request block to its memory pool, so snapshot
|
||||
* everything still needed from cqr before calling it - another CPU can
|
||||
* reallocate and overwrite the block right after.
|
||||
*/
|
||||
proc_bytes = cqr->proc_bytes;
|
||||
intrc = cqr->intrc;
|
||||
status = cqr->block->base->discipline->free_cp(cqr, req);
|
||||
if (status < 0)
|
||||
error = errno_to_blk_status(status);
|
||||
else if (status == 0) {
|
||||
switch (cqr->intrc) {
|
||||
switch (intrc) {
|
||||
case -EPERM:
|
||||
/*
|
||||
* DASD doesn't implement SCSI/NVMe reservations, but it
|
||||
@@ -2726,6 +2785,9 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr)
|
||||
error = BLK_STS_IOERR;
|
||||
break;
|
||||
}
|
||||
} else if (status == DASD_CQR_ABORTED) {
|
||||
/* aborted requests are replaced with a new one so do not complete this */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2771,7 +2833,8 @@ restart:
|
||||
if (cqr->status != DASD_CQR_DONE &&
|
||||
cqr->status != DASD_CQR_FAILED &&
|
||||
cqr->status != DASD_CQR_NEED_ERP &&
|
||||
cqr->status != DASD_CQR_TERMINATED)
|
||||
cqr->status != DASD_CQR_TERMINATED &&
|
||||
cqr->status != DASD_CQR_ABORTED)
|
||||
continue;
|
||||
|
||||
if (cqr->status == DASD_CQR_TERMINATED) {
|
||||
@@ -2819,6 +2882,28 @@ restart:
|
||||
|
||||
static void dasd_return_cqr_cb(struct dasd_ccw_req *cqr, void *data)
|
||||
{
|
||||
struct dasd_ccw_req *temp_cqr;
|
||||
struct dasd_block *block;
|
||||
|
||||
/* only format CQRs are candidates */
|
||||
if (!cqr->block || unlikely(!cqr->format))
|
||||
goto out;
|
||||
|
||||
block = cqr->block;
|
||||
/*
|
||||
* Mark in-flight (IN_IO) CQRs that overlap this just-completed format
|
||||
* range so they re-check in test_and_set_format on completion; FILLED
|
||||
* or QUEUED CQRs re-check the format_list on their next round anyway.
|
||||
*/
|
||||
list_for_each_entry(temp_cqr, &block->ccw_queue, blocklist) {
|
||||
if (temp_cqr != cqr &&
|
||||
temp_cqr->status != DASD_CQR_FILLED &&
|
||||
temp_cqr->status != DASD_CQR_QUEUED &&
|
||||
dasd_req_conflict(cqr, temp_cqr)) {
|
||||
WRITE_ONCE(temp_cqr->collision, true);
|
||||
}
|
||||
}
|
||||
out:
|
||||
dasd_schedule_block_bh(cqr->block);
|
||||
}
|
||||
|
||||
@@ -2882,6 +2967,14 @@ static void dasd_block_tasklet(unsigned long data)
|
||||
atomic_set(&block->tasklet_scheduled, 0);
|
||||
INIT_LIST_HEAD(&final_queue);
|
||||
spin_lock_irq(&block->queue_lock);
|
||||
/*
|
||||
* Splice the hardirq-staged ESE format CQRs onto ccw_queue. Splice to
|
||||
* the tail so an aborted origin request (already on ccw_queue) is
|
||||
* retired before its format-CQR replacement completes and requeues it.
|
||||
*/
|
||||
spin_lock(&block->ese_lock);
|
||||
list_splice_tail_init(&block->ese_staging, &block->ccw_queue);
|
||||
spin_unlock(&block->ese_lock);
|
||||
/* Finish off requests on ccw queue */
|
||||
__dasd_process_block_ccw_queue(block, &final_queue);
|
||||
spin_unlock_irq(&block->queue_lock);
|
||||
@@ -2941,6 +3034,15 @@ static int _dasd_requests_to_flushqueue(struct dasd_block *block,
|
||||
int rc, i;
|
||||
|
||||
spin_lock_irqsave(&block->queue_lock, flags);
|
||||
/*
|
||||
* Splice any hardirq-staged ESE format CQRs onto ccw_queue first so
|
||||
* they are seen and canceled by the walk below instead of being
|
||||
* orphaned across this flush / state transition. Mirrors the splice
|
||||
* in dasd_block_tasklet().
|
||||
*/
|
||||
spin_lock(&block->ese_lock);
|
||||
list_splice_tail_init(&block->ese_staging, &block->ccw_queue);
|
||||
spin_unlock(&block->ese_lock);
|
||||
rc = 0;
|
||||
restart:
|
||||
list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) {
|
||||
@@ -3036,6 +3138,7 @@ static blk_status_t do_dasd_request(struct blk_mq_hw_ctx *hctx,
|
||||
struct dasd_device *basedev;
|
||||
struct dasd_ccw_req *cqr;
|
||||
blk_status_t rc = BLK_STS_OK;
|
||||
bool complete_noop = false;
|
||||
|
||||
basedev = block->base;
|
||||
spin_lock_irq(&dq->lock);
|
||||
@@ -3084,6 +3187,17 @@ static blk_status_t do_dasd_request(struct blk_mq_hw_ctx *hctx,
|
||||
rc = BLK_STS_RESOURCE;
|
||||
} else if (PTR_ERR(cqr) == -EINVAL) {
|
||||
rc = BLK_STS_INVAL;
|
||||
} else if (PTR_ERR(cqr) == -EOPNOTSUPP) {
|
||||
/*
|
||||
* A discard that covers no whole extent releases
|
||||
* nothing. Discard is advisory, so complete it as a
|
||||
* benign no-op: the device does support discard, this
|
||||
* range just does not align to the large ESE extent
|
||||
* granularity.
|
||||
* Completed after the lock is dropped.
|
||||
*/
|
||||
rc = BLK_STS_OK;
|
||||
complete_noop = true;
|
||||
} else {
|
||||
DBF_DEV_EVENT(DBF_ERR, basedev,
|
||||
"CCW creation failed (rc=%ld) on request %p",
|
||||
@@ -3092,6 +3206,13 @@ static blk_status_t do_dasd_request(struct blk_mq_hw_ctx *hctx,
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
if (!cqr) {
|
||||
/* build_cp may collapse a non-transient build error to NULL */
|
||||
DBF_DEV_EVENT(DBF_ERR, basedev,
|
||||
"CCW creation returned NULL on request %p", req);
|
||||
rc = BLK_STS_IOERR;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Note: callback is set to dasd_return_cqr_cb in
|
||||
* __dasd_block_start_head to cover erp requests as well
|
||||
@@ -3110,6 +3231,8 @@ static blk_status_t do_dasd_request(struct blk_mq_hw_ctx *hctx,
|
||||
|
||||
out:
|
||||
spin_unlock_irq(&dq->lock);
|
||||
if (complete_noop)
|
||||
blk_mq_end_request(req, BLK_STS_OK);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3885,6 +4008,19 @@ restart_cb:
|
||||
*/
|
||||
goto restart_cb;
|
||||
}
|
||||
/*
|
||||
* An aborted request was replaced by a full-track write and is
|
||||
* retired by that replacement; do not requeue it, just release
|
||||
* it (mirrors the DASD_CQR_ABORTED handling in
|
||||
* __dasd_cleanup_cqr()).
|
||||
*/
|
||||
if (cqr->status == DASD_CQR_ABORTED) {
|
||||
struct request *req = cqr->callback_data;
|
||||
|
||||
list_del_init(&cqr->blocklist);
|
||||
cqr->block->base->discipline->free_cp(cqr, req);
|
||||
continue;
|
||||
}
|
||||
_dasd_requeue_request(cqr);
|
||||
list_del_init(&cqr->blocklist);
|
||||
cqr->block->base->discipline->free_cp(
|
||||
|
||||
@@ -2400,6 +2400,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
|
||||
erp->startdev = device;
|
||||
erp->memdev = device;
|
||||
erp->block = cqr->block;
|
||||
erp->filldata = cqr->filldata;
|
||||
erp->magic = cqr->magic;
|
||||
erp->expires = cqr->expires;
|
||||
erp->retries = device->default_retries;
|
||||
|
||||
@@ -1630,6 +1630,88 @@ dasd_expires_store(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store);
|
||||
|
||||
/* ESE fulltrack write aggressiveness knob (0..100, see DASD_FT_BIAS_*) */
|
||||
static ssize_t
|
||||
full_track_bias_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
int len;
|
||||
|
||||
device = dasd_device_from_cdev(to_ccwdev(dev));
|
||||
if (IS_ERR(device))
|
||||
return -ENODEV;
|
||||
len = sysfs_emit(buf, "%u\n", device->ft_bias);
|
||||
dasd_put_device(device);
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t full_track_bias_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
unsigned int val;
|
||||
|
||||
if (kstrtouint(buf, 0, &val) || val > DASD_FT_BIAS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
device = dasd_device_from_cdev(to_ccwdev(dev));
|
||||
if (IS_ERR(device))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* ft_bias is the tuning target; fulltrack is a best-effort mode hint
|
||||
* that the per-IO heuristic also updates locklessly. A racing writer can
|
||||
* at most leave a transient mismatch that self-corrects on the next IO,
|
||||
* never corruption, so the update is left unlocked.
|
||||
*/
|
||||
device->ft_bias = val;
|
||||
dasd_ft_bias_apply(device);
|
||||
|
||||
dasd_put_device(device);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(full_track_bias);
|
||||
|
||||
static const char * const dasd_ese_heu_state_names[] = {
|
||||
[DASD_ESE_HEU_FT1_ACTIVE] = "fulltrack active",
|
||||
[DASD_ESE_HEU_PROBING] = "probing",
|
||||
[DASD_ESE_HEU_FT0_STABLE] = "fulltrack inactive",
|
||||
};
|
||||
|
||||
/* read-only: current full-track mode / adaptive FSM state, for observability */
|
||||
static ssize_t
|
||||
ese_heuristic_state_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
unsigned int state;
|
||||
int len;
|
||||
|
||||
device = dasd_device_from_cdev(to_ccwdev(dev));
|
||||
if (IS_ERR(device))
|
||||
return -ENODEV;
|
||||
if (device->ft_bias == 0) {
|
||||
len = sysfs_emit(buf, "fulltrack deactivated\n");
|
||||
} else if (device->ft_bias >= DASD_FT_BIAS_MAX) {
|
||||
len = sysfs_emit(buf, "fulltrack permanent active\n");
|
||||
} else if (!dasd_ese_adaptive(device)) {
|
||||
/* adaptive range but not ESE: the heuristic does not run */
|
||||
len = sysfs_emit(buf, "fulltrack deactivated\n");
|
||||
} else {
|
||||
state = device->ese_probe_state;
|
||||
if (state < ARRAY_SIZE(dasd_ese_heu_state_names))
|
||||
len = sysfs_emit(buf, "%s\n", dasd_ese_heu_state_names[state]);
|
||||
else
|
||||
len = sysfs_emit(buf, "unknown\n");
|
||||
}
|
||||
dasd_put_device(device);
|
||||
return len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(ese_heuristic_state);
|
||||
|
||||
static ssize_t
|
||||
dasd_retries_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -2400,9 +2482,10 @@ static ssize_t dasd_##_name##_show(struct device *dev, \
|
||||
\
|
||||
return sysfs_emit(buf, "%d\n", val); \
|
||||
} \
|
||||
static DEVICE_ATTR(_name, 0444, dasd_##_name##_show, NULL); \
|
||||
static DEVICE_ATTR(_name, 0444, dasd_##_name##_show, NULL);
|
||||
|
||||
DASD_DEFINE_ATTR(ese, device->discipline->is_ese);
|
||||
DASD_DEFINE_ATTR(ese, device->discipline->ese_capable);
|
||||
DASD_DEFINE_ATTR(on_demand_formatting, device->discipline->on_demand_format);
|
||||
DASD_DEFINE_ATTR(extent_size, device->discipline->ext_size);
|
||||
DASD_DEFINE_ATTR(pool_id, device->discipline->ext_pool_id);
|
||||
DASD_DEFINE_ATTR(space_configured, device->discipline->space_configured);
|
||||
@@ -2425,6 +2508,8 @@ static struct attribute * dasd_attrs[] = {
|
||||
&dev_attr_erplog.attr,
|
||||
&dev_attr_failfast.attr,
|
||||
&dev_attr_expires.attr,
|
||||
&dev_attr_full_track_bias.attr,
|
||||
&dev_attr_ese_heuristic_state.attr,
|
||||
&dev_attr_retries.attr,
|
||||
&dev_attr_timeout.attr,
|
||||
&dev_attr_reservation_policy.attr,
|
||||
@@ -2438,6 +2523,7 @@ static struct attribute * dasd_attrs[] = {
|
||||
&dev_attr_path_reset.attr,
|
||||
&dev_attr_hpf.attr,
|
||||
&dev_attr_ese.attr,
|
||||
&dev_attr_on_demand_formatting.attr,
|
||||
&dev_attr_fc_security.attr,
|
||||
&dev_attr_copy_pair.attr,
|
||||
&dev_attr_copy_role.attr,
|
||||
|
||||
+1133
-137
File diff suppressed because it is too large
Load Diff
@@ -131,7 +131,7 @@
|
||||
/*
|
||||
* Maximum number of blocks to be chained
|
||||
*/
|
||||
#define DASD_ECKD_MAX_BLOCKS 190
|
||||
#define DASD_ECKD_MAX_BLOCKS 180
|
||||
#define DASD_ECKD_MAX_BLOCKS_RAW 256
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -146,6 +146,52 @@ struct eckd_count {
|
||||
__u16 dl;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct eckd_r0 {
|
||||
struct eckd_count count;
|
||||
__u8 data[8];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Extended Address Volume track address: the head field carries the actual
|
||||
* head in its low-order 4 bits; the cylinder bits that do not fit the 16-bit
|
||||
* cyl field are shifted in just above them.
|
||||
*/
|
||||
#define DASD_EAV_CYL_HI_SHIFT 16 /* cylinder bits beyond the 16-bit cyl field */
|
||||
#define DASD_EAV_HEAD_HI_SHIFT 4 /* head occupies the low-order 4 bits of head */
|
||||
|
||||
/*
|
||||
* On-disk DASD format label.
|
||||
*
|
||||
* Written into track 0, head 0, record 4 (R4 - the first non-special CDL
|
||||
* record) as part of the same channel program that formats track 0, so it is
|
||||
* stored atomically with the track: either both the track format and the label
|
||||
* make it to disk or neither does. Its presence with a valid magic therefore
|
||||
* marks a completed format and can be used for format detection.
|
||||
*
|
||||
* The structure is exactly the smallest supported block size (512 bytes) so it
|
||||
* always fits into a single record.
|
||||
* For larger block sizes the rest of the record is zero padded.
|
||||
* The magic together with the version is used to recognise a valid label.
|
||||
*/
|
||||
#define DASD_ESE_LABEL_MAGIC 0xC4C1E2C4C6D4E3F1ULL /* EBCDIC "DASDFMT1" */
|
||||
#define DASD_ESE_LABEL_VERSION 1
|
||||
|
||||
/* dasd_format_label.flags */
|
||||
#define DASD_ESE_LABEL_F_ESE 0x00000001 /* volume is extent space efficient */
|
||||
#define DASD_ESE_LABEL_F_QUICK 0x00000002 /* quick (space released) format */
|
||||
#define DASD_ESE_LABEL_F_FULL 0x00000004 /* full format */
|
||||
|
||||
struct dasd_format_label {
|
||||
__u64 magic; /* DASD_ESE_LABEL_MAGIC */
|
||||
__u32 version; /* DASD_ESE_LABEL_VERSION */
|
||||
__u32 flags; /* DASD_ESE_LABEL_F_* */
|
||||
__u32 blksize; /* block size the volume was formatted with */
|
||||
__u32 reserved0;
|
||||
__u64 format_tod; /* TOD clock at format time */
|
||||
__u8 kernel_version[64]; /* NUL terminated kernel release (uname -r) */
|
||||
__u8 reserved[416]; /* pad the struct to 512 bytes */
|
||||
} __packed;
|
||||
|
||||
struct ch_t {
|
||||
__u16 cyl;
|
||||
__u16 head;
|
||||
@@ -690,12 +736,32 @@ struct dasd_eckd_private {
|
||||
|
||||
/* alias management */
|
||||
struct dasd_uid uid;
|
||||
/*
|
||||
* Cached copies of conf.ned->ID (the LSS) and conf.ned->unit_addr,
|
||||
* refreshed under ccwdev_lock. Kept outside uid because create_uid()
|
||||
* memsets uid before repopulating it, which would expose a transient
|
||||
* zero to the lockless CCW-build readers.
|
||||
*/
|
||||
__u8 ned_lss;
|
||||
__u8 ned_ua;
|
||||
struct alias_pav_group *pavgroup;
|
||||
struct alias_lcu *lcu;
|
||||
int count;
|
||||
|
||||
u32 fcx_max_data;
|
||||
char suc_reason;
|
||||
/*
|
||||
* Set when the whole volume's space was released (full RAS); consumed by
|
||||
* the next format to mark the on-disk label as a quick (vs full) format.
|
||||
*/
|
||||
int ese_format_quick;
|
||||
/*
|
||||
* Cached on-disk format label (R4), read at online and refreshed on
|
||||
* format. When valid, is_ese() is derived from it; otherwise it falls
|
||||
* back to the hardware ESE field (vsq.vol_info.ese).
|
||||
*/
|
||||
struct dasd_format_label ese_label;
|
||||
bool ese_label_valid;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -120,16 +120,19 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr)
|
||||
*/
|
||||
struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
|
||||
{
|
||||
int success;
|
||||
int success, aborted;
|
||||
unsigned long startclk, stopclk;
|
||||
struct dasd_device *startdev;
|
||||
unsigned int proc_bytes;
|
||||
|
||||
BUG_ON(cqr->refers == NULL || cqr->function == NULL);
|
||||
|
||||
success = cqr->status == DASD_CQR_DONE;
|
||||
aborted = cqr->status == DASD_CQR_ABORTED;
|
||||
startclk = cqr->startclk;
|
||||
stopclk = cqr->stopclk;
|
||||
startdev = cqr->startdev;
|
||||
proc_bytes = cqr->proc_bytes;
|
||||
|
||||
/* free all ERPs - but NOT the original cqr */
|
||||
while (cqr->refers != NULL) {
|
||||
@@ -147,7 +150,11 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
|
||||
cqr->startclk = startclk;
|
||||
cqr->stopclk = stopclk;
|
||||
cqr->startdev = startdev;
|
||||
if (success)
|
||||
cqr->proc_bytes = proc_bytes;
|
||||
if (aborted)
|
||||
/* base request is owned by the ESE format replacement CQR */
|
||||
cqr->status = DASD_CQR_ABORTED;
|
||||
else if (success)
|
||||
cqr->status = DASD_CQR_DONE;
|
||||
else {
|
||||
cqr->status = DASD_CQR_FAILED;
|
||||
|
||||
@@ -159,6 +159,11 @@ struct dasd_ccw_req {
|
||||
void *callback_data;
|
||||
unsigned int proc_bytes; /* bytes for partial completion */
|
||||
unsigned int trkcount; /* count formatted tracks */
|
||||
void *filldata; /* address of filler data */
|
||||
struct dasd_format_entry *format;
|
||||
sector_t start_trk;
|
||||
sector_t end_trk;
|
||||
bool collision;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -170,6 +175,7 @@ struct dasd_ccw_req {
|
||||
#define DASD_CQR_IN_ERP 0x03 /* request is in recovery */
|
||||
#define DASD_CQR_FAILED 0x04 /* request is finally failed */
|
||||
#define DASD_CQR_TERMINATED 0x05 /* request was stopped by driver */
|
||||
#define DASD_CQR_ABORTED 0x06 /* request was replaced and will be deleted */
|
||||
|
||||
#define DASD_CQR_QUEUED 0x80 /* request is queued to be processed */
|
||||
#define DASD_CQR_IN_IO 0x81 /* request is currently in IO */
|
||||
@@ -177,6 +183,7 @@ struct dasd_ccw_req {
|
||||
#define DASD_CQR_CLEAR_PENDING 0x83 /* request is clear pending */
|
||||
#define DASD_CQR_CLEARED 0x84 /* request was cleared */
|
||||
#define DASD_CQR_SUCCESS 0x85 /* request was successful */
|
||||
#define DASD_CQR_ABORT 0x86 /* request was replaced and will not be handled */
|
||||
|
||||
/* default expiration time*/
|
||||
#define DASD_EXPIRES 300
|
||||
@@ -394,6 +401,11 @@ struct dasd_discipline {
|
||||
* Extent Space Efficient (ESE) relevant functions
|
||||
*/
|
||||
int (*is_ese)(struct dasd_device *);
|
||||
int (*ese_capable)(struct dasd_device *);
|
||||
/* Whether the volume is formatted on demand (thin), from the label */
|
||||
int (*on_demand_format)(struct dasd_device *);
|
||||
/* Fill discard queue limits */
|
||||
void (*disc_limits)(struct dasd_block *, struct queue_limits *);
|
||||
/* Capacity */
|
||||
int (*space_allocated)(struct dasd_device *);
|
||||
int (*space_configured)(struct dasd_device *);
|
||||
@@ -406,8 +418,7 @@ struct dasd_discipline {
|
||||
int (*ext_pool_warn_thrshld)(struct dasd_device *);
|
||||
int (*ext_pool_oos)(struct dasd_device *);
|
||||
int (*ext_pool_exhaust)(struct dasd_device *, struct dasd_ccw_req *);
|
||||
struct dasd_ccw_req *(*ese_format)(struct dasd_device *,
|
||||
struct dasd_ccw_req *, struct irb *);
|
||||
void (*ese_format)(struct dasd_device *, struct dasd_ccw_req *, struct irb *);
|
||||
int (*ese_read)(struct dasd_ccw_req *, struct irb *);
|
||||
int (*pprc_status)(struct dasd_device *, struct dasd_pprc_data_sc4 *);
|
||||
bool (*pprc_enabled)(struct dasd_device *);
|
||||
@@ -538,9 +549,17 @@ struct dasd_profile {
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* concurrent ESE format ranges in flight; also caps a WRITE_FULL_TRACK's
|
||||
* track count, which the LRE track bitmask limits to 16
|
||||
*/
|
||||
#define DASD_NR_FORMAT_ENTRIES 16
|
||||
|
||||
struct dasd_format_entry {
|
||||
struct list_head list;
|
||||
sector_t track;
|
||||
struct dasd_ccw_req *cqr;
|
||||
sector_t start_trk;
|
||||
sector_t end_trk;
|
||||
};
|
||||
|
||||
struct dasd_device {
|
||||
@@ -573,9 +592,12 @@ struct dasd_device {
|
||||
struct list_head ccw_queue;
|
||||
spinlock_t mem_lock;
|
||||
void *ccw_mem;
|
||||
void *fill_mem;
|
||||
void *erp_mem;
|
||||
void *ese_mem;
|
||||
void *nulldata;
|
||||
struct list_head ccw_chunks;
|
||||
struct list_head fill_chunks;
|
||||
struct list_head erp_chunks;
|
||||
struct list_head ese_chunks;
|
||||
|
||||
@@ -607,11 +629,24 @@ struct dasd_device {
|
||||
struct dentry *debugfs_dentry;
|
||||
struct dentry *hosts_dentry;
|
||||
struct dasd_profile profile;
|
||||
struct dasd_format_entry format_entry;
|
||||
struct dasd_format_entry format_entry[DASD_NR_FORMAT_ENTRIES];
|
||||
struct kset *paths_info;
|
||||
struct dasd_copy_relation *copy;
|
||||
unsigned long aq_mask;
|
||||
unsigned int aq_timeouts;
|
||||
|
||||
/* ESE fulltrack write control (see full_track_bias sysfs attribute) */
|
||||
unsigned int ft_bias; /* aggressiveness 0..100: 0=off, 100=always */
|
||||
unsigned int fulltrack; /* internal: use WRITE_FULL_TRACK for aligned writes */
|
||||
/* adaptive heuristic (active for ft_bias 1..99), derived from ft_bias */
|
||||
unsigned int ese_probe_state; /* heuristic FSM state */
|
||||
unsigned int ese_probe_interval; /* IOs between evaluations */
|
||||
atomic_t ese_io_cnt; /* IO counter for current window */
|
||||
atomic_t ese_nrf_window; /* NRF/INV_TRACK_FORMAT events in window */
|
||||
unsigned int ese_heu_start_interval; /* IOs before first probe */
|
||||
unsigned int ese_heu_probe_window; /* IOs in probe window */
|
||||
unsigned int ese_heu_max_interval; /* max IOs between probes (backoff cap) */
|
||||
unsigned int ese_heu_nrf_high; /* NRF per-mille threshold → activate ft1 */
|
||||
};
|
||||
|
||||
struct dasd_block {
|
||||
@@ -640,6 +675,15 @@ struct dasd_block {
|
||||
struct list_head format_list;
|
||||
spinlock_t format_lock;
|
||||
atomic_t trkcount;
|
||||
|
||||
/*
|
||||
* ESE format CQRs staged from hardirq, spliced into
|
||||
* ccw_queue in dasd_block_tasklet under queue_lock. Direct enqueue from
|
||||
* the IRQ handler would invert the queue_lock / ccwdev_lock order.
|
||||
*/
|
||||
struct list_head ese_staging;
|
||||
/* lock for ese_staging */
|
||||
spinlock_t ese_lock;
|
||||
};
|
||||
|
||||
struct dasd_attention_data {
|
||||
@@ -660,6 +704,39 @@ struct dasd_queue {
|
||||
#define DASD_STOPPED_PPRC 32 /* PPRC swap */
|
||||
#define DASD_STOPPED_NOSPC 128 /* no space left */
|
||||
|
||||
/*
|
||||
* ESE fulltrack write aggressiveness (full_track_bias sysfs attribute), 0..100:
|
||||
* 0 - never use proactively WRITE_FULL_TRACK
|
||||
* 100 - always use proactively WRITE_FULL_TRACK, no probing
|
||||
* 1..99 - adaptive; higher means switch to ft more eagerly
|
||||
* WRITE_FULL_TRACK has an advantage on sparse formatted ESE devices
|
||||
* but it has an overall penalty for maximum throughput for fully
|
||||
* formatted devices.
|
||||
* The default of 50 tries to balance both and do some probing in between
|
||||
* to choose the best mode for default IO.
|
||||
*/
|
||||
#define DASD_FT_BIAS_MAX 100
|
||||
#define DASD_FT_BIAS_DEFAULT 50
|
||||
|
||||
/* ESE fulltrack heuristic FSM states (adaptive range, ft_bias 1..99) */
|
||||
#define DASD_ESE_HEU_FT1_ACTIVE 0 /* fulltrack write active */
|
||||
#define DASD_ESE_HEU_PROBING 1 /* ft0 probe window, measuring NRF rate */
|
||||
#define DASD_ESE_HEU_FT0_STABLE 2 /* device formatted, ft0 active */
|
||||
|
||||
/*
|
||||
* Heuristic parameters are derived from ft_bias by linear interpolation,
|
||||
* anchored so that ft_bias == 50 reproduces the previously shipped defaults
|
||||
* and ft_bias == 100 is the most aggressive end of the range.
|
||||
* probe_window is constant.
|
||||
*/
|
||||
#define DASD_ESE_HEU_PROBE_WINDOW 100
|
||||
#define DASD_ESE_HEU_NRF_HIGH_A50 10 /* NRF per-mille threshold */
|
||||
#define DASD_ESE_HEU_NRF_HIGH_A100 1
|
||||
#define DASD_ESE_HEU_START_A50 2000 /* IOs before first probe */
|
||||
#define DASD_ESE_HEU_START_A100 500
|
||||
#define DASD_ESE_HEU_MAX_A50 500000 /* backoff cap */
|
||||
#define DASD_ESE_HEU_MAX_A100 20000
|
||||
|
||||
/* per device flags */
|
||||
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
|
||||
#define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */
|
||||
@@ -815,6 +892,70 @@ static inline void *dasd_get_callback_data(struct dasd_ccw_req *cqr)
|
||||
return cqr->callback_data;
|
||||
}
|
||||
|
||||
static inline bool dasd_req_conflict(struct dasd_ccw_req *cqr1,
|
||||
struct dasd_ccw_req *cqr2)
|
||||
{
|
||||
return !(cqr1->format->end_trk < cqr2->start_trk ||
|
||||
cqr2->end_trk < cqr1->format->start_trk);
|
||||
}
|
||||
|
||||
/*
|
||||
* true when device is ese device and ft_bias selects the adaptive
|
||||
* heuristic (neither hard endpoint)
|
||||
*/
|
||||
static inline bool dasd_ese_adaptive(struct dasd_device *device)
|
||||
{
|
||||
return device->discipline &&
|
||||
device->discipline->is_ese &&
|
||||
device->discipline->is_ese(device) &&
|
||||
device->ft_bias > 0 &&
|
||||
device->ft_bias < DASD_FT_BIAS_MAX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Linear interpolation of a heuristic parameter between its value at aggr==50
|
||||
* (v50) and its value at aggr==100 (v100).
|
||||
*/
|
||||
static inline unsigned int dasd_ese_lerp(unsigned int v50, unsigned int v100,
|
||||
unsigned int aggr)
|
||||
{
|
||||
return (unsigned int)((int)v50 +
|
||||
((int)v100 - (int)v50) * ((int)aggr - 50) / 50);
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply the ft_bias knob. For the hard endpoints just pin the mode; for the
|
||||
* adaptive range derive the heuristic parameters from ft_bias and (re)start
|
||||
* the FSM in ft1 so a freshly sparse device avoids the NRF penalty right away.
|
||||
*/
|
||||
static inline void dasd_ft_bias_apply(struct dasd_device *device)
|
||||
{
|
||||
unsigned int a = device->ft_bias;
|
||||
|
||||
if (!dasd_ese_adaptive(device)) {
|
||||
device->fulltrack = (a >= DASD_FT_BIAS_MAX) ? 1 : 0;
|
||||
device->ese_probe_state = DASD_ESE_HEU_FT1_ACTIVE;
|
||||
return;
|
||||
}
|
||||
|
||||
device->ese_heu_nrf_high =
|
||||
dasd_ese_lerp(DASD_ESE_HEU_NRF_HIGH_A50,
|
||||
DASD_ESE_HEU_NRF_HIGH_A100, a);
|
||||
device->ese_heu_start_interval =
|
||||
dasd_ese_lerp(DASD_ESE_HEU_START_A50,
|
||||
DASD_ESE_HEU_START_A100, a);
|
||||
device->ese_heu_max_interval =
|
||||
dasd_ese_lerp(DASD_ESE_HEU_MAX_A50,
|
||||
DASD_ESE_HEU_MAX_A100, a);
|
||||
device->ese_heu_probe_window = DASD_ESE_HEU_PROBE_WINDOW;
|
||||
|
||||
device->ese_probe_state = DASD_ESE_HEU_FT1_ACTIVE;
|
||||
device->ese_probe_interval = device->ese_heu_start_interval;
|
||||
device->fulltrack = 1;
|
||||
atomic_set(&device->ese_io_cnt, 0);
|
||||
atomic_set(&device->ese_nrf_window, 0);
|
||||
}
|
||||
|
||||
/* externals in dasd.c */
|
||||
#define DASD_PROFILE_OFF 0
|
||||
#define DASD_PROFILE_ON 1
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/eventfd.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blk_plug.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/migrate.h>
|
||||
#include <linux/ramfs.h>
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ static inline struct bvec_iter init_bvec_iter_for_bio(struct bio *bio)
|
||||
.bi_sector = 0,
|
||||
.bi_size = bio_size,
|
||||
.bi_idx = 0,
|
||||
.bi_bvec_done = 0,
|
||||
.bi_offset = 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1107,6 +1107,9 @@ static void __bh_submit(struct buffer_head *bh, blk_opf_t opf,
|
||||
|
||||
bio = bio_alloc(bh->b_bdev, 1, opf, GFP_NOIO);
|
||||
|
||||
if (folio_test_dropbehind(bh->b_folio) && op_is_write(opf))
|
||||
bio_set_flag(bio, BIO_COMPLETE_IN_TASK);
|
||||
|
||||
if (IS_ENABLED(CONFIG_FS_ENCRYPTION))
|
||||
buffer_set_crypto_ctx(bio, bh, GFP_NOIO);
|
||||
|
||||
|
||||
+1
-10
@@ -1432,15 +1432,6 @@ static void z_erofs_decompressqueue_kthread_work(struct kthread_work *work)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Use (kthread_)work in atomic contexts to minimize scheduling overhead */
|
||||
static inline bool z_erofs_in_atomic(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PREEMPTION) && rcu_preempt_depth())
|
||||
return true;
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_COUNT))
|
||||
return true;
|
||||
return !preemptible();
|
||||
}
|
||||
|
||||
static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io,
|
||||
int bios)
|
||||
@@ -1457,7 +1448,7 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io,
|
||||
|
||||
if (atomic_add_return(bios, &io->pending_bios))
|
||||
return;
|
||||
if (z_erofs_in_atomic()) {
|
||||
if (bio_in_atomic()) {
|
||||
/* See `sync_decompress` in sysfs-fs-erofs for more details */
|
||||
if (sbi->sync_decompress == EROFS_SYNC_DECOMPRESS_AUTO)
|
||||
sbi->sync_decompress = EROFS_SYNC_DECOMPRESS_FORCE_ON;
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blk_plug.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
@@ -359,6 +359,7 @@ static ssize_t iomap_dio_bio_iter_one(struct iomap_iter *iter,
|
||||
iomap_max_bio_size(&iter->iomap), alignment);
|
||||
else
|
||||
ret = bio_iov_iter_get_pages(bio, dio->submit.iter,
|
||||
bdev_dma_alignment(bio->bi_bdev),
|
||||
alignment - 1);
|
||||
if (unlikely(ret))
|
||||
goto out_put_bio;
|
||||
@@ -1033,7 +1034,9 @@ ssize_t __iomap_dio_read_simple(struct kiocb *iocb, struct iov_iter *iter,
|
||||
bio->bi_iter.bi_sector = iomap_sector(&iomi->iomap, iomi->pos);
|
||||
bio->bi_ioprio = iocb->ki_ioprio;
|
||||
|
||||
ret = bio_iov_iter_get_pages(bio, iter, alignment - 1);
|
||||
ret = bio_iov_iter_get_pages(bio, iter,
|
||||
bdev_dma_alignment(bio->bi_bdev),
|
||||
alignment - 1);
|
||||
if (unlikely(ret))
|
||||
goto out_bio_put;
|
||||
|
||||
|
||||
+3
-2
@@ -239,8 +239,6 @@ ssize_t iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, struct folio *folio,
|
||||
|
||||
if (wpc->iomap.flags & IOMAP_F_SHARED)
|
||||
ioend_flags |= IOMAP_IOEND_SHARED;
|
||||
if (folio_test_dropbehind(folio))
|
||||
ioend_flags |= IOMAP_IOEND_DONTCACHE;
|
||||
if (pos == wpc->iomap.offset && (wpc->iomap.flags & IOMAP_F_BOUNDARY))
|
||||
ioend_flags |= IOMAP_IOEND_BOUNDARY;
|
||||
|
||||
@@ -257,6 +255,9 @@ new_ioend:
|
||||
if (!bio_add_folio(&ioend->io_bio, folio, map_len, poff))
|
||||
goto new_ioend;
|
||||
|
||||
if (folio_test_dropbehind(folio))
|
||||
bio_set_flag(&ioend->io_bio, BIO_COMPLETE_IN_TASK);
|
||||
|
||||
/*
|
||||
* Clamp io_offset and io_size to the incore EOF so that ondisk
|
||||
* file size updates in the ioend completion are byte-accurate.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user