scsi: ibmvfc: make ibmvfc support FPIN messages
Subsystems (3)
v18 cover scsi: ibmvfc: make ibmvfc support FPIN messages
This patch series adds FPIN (fabric performance impact notification) support to the ibmvfc (IBM Virtual Fibre Channel) driver. This comes in three flavors:
- basic, to recognize existing FPIN messages from the virtual I/O server (VIOS) (patch 6)
- full, supporting additional FPIN information and using its own asynchronous sub-queue and interrupt (patches 8-12)
- extended, supporting FC-LS-5 (patch 13)
Full and extended FPIN support requires a new asynchronous sub-queue with its own interrupt. The asynchronous sub-queue support requires ibmvfc to also support a new IBMVFC_NOOP command, which the driver recognizes and ignores (patch 7).
The asynchronous sub-queue work is split across five patches: patch 8 defines the necessary data structures, patch 9 extends the async event handlers to accept events from the new queue, patch 10 adds the interrupt and drain routines for the queue, patch 11 extends channel registration to accommodate the new queue, and patch 12 registers the queue during channel initialization, advertises support to VIOS during NPIV login, and routes events from the queue to the handlers. Patches 1-5 fix IRQ cleanup and error handling bugs exposed during that work. Patch 13 adds extended FPIN handling on top of the new queue.
All three modes convert an incoming FPIN message from VIOS to an FC extended link service message, in some cases using default values for information not provided by the VIOS FPIN message but expected in the FC ELS message. This FC ELS message is passed to fc_host_fpin_rcv for updating statistics and sending the information upstream by netlink multicast, where it may be read by listeners including the DM multipath daemon "multipathd."
---
Changes in v18:
- Removed stray diffs in commit message for patch 12.
- Link to v17: https://lore.kernel.org/r/[email protected]
Changes in v17:
- new patch 4 (fix stale rc and IRQ mapping leak in
ibmvfc_register_channel()): set rc = -EINVAL in the default: case of
the protocol switch to prevent a stale h_reg_sub_crq() return value
(0 or H_CLOSED) being returned to the caller after the sub-CRQ is
freed; introduce irq_mapping_failed: label to dispose the IRQ mapping
and clear scrq->irq before falling through to irq_failed, fixing a
leak when irq_create_mapping() succeeds but a later step fails
- patch 5 (fix resource leaks on partial queue allocation failure):
add ibmvfc_free_event_pool() calls on the get_zeroed_page() and
dma_mapping_error() failure paths in ibmvfc_alloc_queue() to fix
an event pool leak when ibmvfc_init_event_pool() succeeds but a
later step fails; rename patch subject to reflect broader scope
- patch 6 (add basic FPIN support): allocate fpin_workq after
ibmvfc_alloc_mem() in ibmvfc_probe() to prevent double-free of
resources that ibmvfc_alloc_mem() already unwound on failure; deliver LINK_CONGESTED and CONGESTION_CLEARED events whose identifiers are all
zero as host-scope FPIN events, and silently drop other statuses with
no identifiers; set detecting_wwpn in the FPIN descriptors from
fc_host_port_name() instead of leaving it zero; add KUnit cases for the
host-scope and dropped zero-identifier events
- patch 9 (extend async event handlers for async sub-CRQ events): add
assoc_id parameter to ibmvfc_find_target() and match on tgt->assoc_id
when nonzero; when an async sub-CRQ event has IBMVFC_ASYNC_ID_IS_ASSOC_ID
set, extract assoc_id from subq->id.assoc_id and pass it to the target
lookup so NVMe targets sharing a wwpn are distinguished by association ID; propagate assoc_id into the ELS LOGO/PRLO/PLOGI target loops in ibmvfc_handle_async() for the same reason; when no target is found for
a LINK_CONGESTED or CONGESTION_CLEARED event, fall back to a
host-scope descriptor as for basic FPIN; set detecting_wwpn in the
full FPIN descriptors; use a severity of 0 for PORT_CLEARED and
CONGESTION_CLEARED events
- patch 10 (add interrupt routine for asynchronous sub CRQ): collect
async sub-CRQ events into a deferred list while holding host_lock and
q_lock and dispatch them after releasing both, re-taking host_lock
around each ibmvfc_handle_async() call, avoiding a potential lockdep
recursive locking warning from ibmvfc_handle_async() calling ibmvfc_purge_requests() which acquires q_lock instances of the same
lockdep class
- patch 11 (extend channel registration for async sub-CRQ): rename the
patch and correct its commit message, which said
ibmvfc_deregister_channel() was changed when only
ibmvfc_register_channel() is
- patch 12 (register and use asynchronous sub CRQ): move
mq_enabled/do_enquiry flag clearing out of ibmvfc_reg_sub_crqs() and
into each caller; in ibmvfc_reenable_crq_queue() and
ibmvfc_reset_crq(), deregister all channel sets and clear mq_enabled
and do_enquiry if any ibmvfc_reg_sub_crqs() call fails, preventing
leaked hypervisor sub-CRQ registrations; set async_sub_crq_handle
unconditionally in ibmvfc_channel_setup() so the handle is sent to
VIOS even on the CANCEL_CHANNELS path
- patch 13 (handle extended FPIN events): pass detecting_wwpn through ibmvfc_ext_fpin_to_desc(); correct the @severity kernel-doc to say
severity is only carried by the congestion descriptor
(LINK_CONGESTED and CONGESTION_CLEARED); drop the redundant IBMVFC_FPIN_EVENT_TYPE_VALID test in ibmvfc_ext_fpin_to_desc(); remove
volatile from the valid field of struct ibmvfc_async_subq_fpin; move
the static_assert() checks to file scope
- Link to v16: https://lore.kernel.org/r/[email protected]
Changes in v16:
- new patch 4: fix unguarded dma_unmap_single() in ibmvfc_free_queue();
guard dma_unmap_single() and free_page() together under the msgs.handle
check to prevent calling dma_unmap_single() with address 0 on a
partially initialized queue
- patch 5 (add basic FPIN support): restore per-adapter fpin_workq instead
of schedule_work(); destroy it after ibmvfc_release_crq_queue() in
ibmvfc_remove() to ensure in-flight work items drain before tgt_pool is
freed and to close the original race where queue_work() could be called
on a destroyed workqueue pointer; fix probe error unwind to call destroy_workqueue() before ibmvfc_free_mem()
- patch 10 (extend channel reg/dereg helpers): initialize scrq->hwq_id
before request_irq() so error logging in ibmvfc_toggle_scrq_irq() shows
the correct index if an early interrupt triggers before hwq_id was set
- patch 11 (register and use asynchronous sub CRQ): clamp
scsi->active_queues and nvme->active_queues to max_queues in ibmvfc_channel_setup_done() to prevent out-of-bounds scrqs[] access on
a malformed hypervisor response; take host_lock around bitfield writes
to mq_enabled, do_enquiry, and nvme_enabled in ibmvfc_reg_sub_crqs()
and ibmvfc_init_sub_crqs() to prevent lost updates from concurrent read-modify-write on the packed bitfield word; remove mq_enabled guard
from ibmvfc_dereg_sub_crqs() to prevent previously registered channel
sets being skipped on teardown after a registration failure; change ibmvfc_reg_sub_crqs() to return int and check its return value in ibmvfc_init_sub_crqs() so each channel set failure triggers correct
per-set cleanup
- Link to v15: https://patch.msgid.link/[email protected]
Changes in v15:
- patch 4 (add basic FPIN support): restore per-adapter fpin_workq instead
of schedule_work(); destroy it after ibmvfc_release_crq_queue() in
ibmvfc_remove() to ensure in-flight work items drain before tgt_pool is
freed and to close the original race where queue_work() could be called
on a destroyed workqueue pointer
- patch 9 (extend channel reg/dereg helpers): initialize scrq->hwq_id
before request_irq() so error logging in ibmvfc_toggle_scrq_irq() shows
the correct index if an early interrupt triggers before hwq_id was set
- patch 10 (register and use asynchronous sub CRQ): clamp scsi->active_queues
and nvme->active_queues to max_queues in ibmvfc_channel_setup_done() to
prevent out-of-bounds scrqs[] access on a malformed hypervisor response;
take host_lock around bitfield writes to mq_enabled, do_enquiry, and
nvme_enabled in ibmvfc_reg_sub_crqs() and ibmvfc_init_sub_crqs() to
prevent lost updates from concurrent read-modify-write on the packed
bitfield word
- Link to v14: https://patch.msgid.link/[email protected]
Changes in v14:
- Fix double-free of sub-CRQ at the hypervisor level: guard
H_FREE_SUB_CRQ in ibmvfc_deregister_channel() on scrq->cookie != 0
and clear cookie to zero on success; also clear cookie in the ibmvfc_register_channel() irq_failed: rollback path (patch 3)
- Add SCSI_IBMVFC_KUNIT_TEST Kconfig entry, Makefile line, and
ibmvfc_kunit.c with ibmvfc_deregister_channel_guard_test() verifying
the cookie and msgs.handle guard paths (patch 3)
- Fix wild pointer dereference in ibmvfc_handle_scrq(): change signature
to accept struct ibmvfc_queue *scrq and use &scrq->evt_pool for event
validation instead of &evt->queue->evt_pool (patch 5)
- Fix NULL pointer dereference in ibmvfc_channel_setup_done() when
mq_enabled=0: compute async->active_queues from the VIOS response
guarded by async->scrqs != NULL; guard async->scrqs[0].cookie access
in ibmvfc_channel_setup() with a NULL check (patch 10)
- Fix host state machine stall on ibmvfc_reg_sub_crqs() failure: clear vhost->mq_enabled in addition to do_enquiry so ibmvfc_npiv_login_done()
takes the non-channel fallback path and wakes the work queue (patch 10)
- Link to v13: https://patch.msgid.link/[email protected]
Changes in v13:
- Updated to 7.4/scsi-staging base
- Extend ibmvfc_common_fpin_to_desc() with a severity parameter; ibmvfc_ext_fpin_to_desc() now honours IBMVFC_FPIN_SEVERITY_VALID
and propagates the severity from the VIOS rather than always
defaulting to FPIN_CONGN_SEVERITY_WARNING (patch 11)
- Handle scsi_host_get() failure in ibmvfc_get_first_vhost() (patch 4)
- Add kernel-doc comment to ibmvfc_get_first_vhost() (patch 4)
- Use list_for_each_entry to find an online FCP/NVMe target rport in
KUnit tests rather than taking the first list entry unconditionally
(patches 4, 7, 11)
- Link to v12: https://patch.msgid.link/[email protected]
Changes in v12:
- Wrap ibmvfc_handle_async() invocations in KUnit tests with host_lock to prevent races with workqueue destruction on concurrent device removal
(patches 4, 7, 11)
- Add missing scsi_host_put() in success exit path of ibmvfc_async_fpin_test() (patch 4)
- Pass resolved tgt->wwpn into ibmvfc_full_fpin_to_desc() instead of subq->wwpn so FPIN descriptors are correctly populated when subq->wwpn is 0 (patch 7)
- Fix kernel-doc comments in ibmvfc_register_channel() and ibmvfc_deregister_channel() that incorrectly directed passing a negative index (patch 9)
- Fix double registration of scsi_scrqs in ibmvfc_reenable_crq_queue() by registering async_scrqs instead (patch 10)
- Link to v11: https://lore.kernel.org/linux-scsi/[email protected]/
Changes in v11:
- Split the IRQ cleanup and NULL-pointer fixes out of the "register and
use" patch into two dedicated bug-fix patches (patches 1-2 and 1-3),
making the series 11 patches instead of 9
- Replaced the single ibmvfc_queue async_sub_crq field in ibmvfc_host with
an ibmvfc_channels async_scrqs field, reusing the existing channel infrastructure for allocation, registration, and teardown (patches 6, 9, 10)
- Added IBMVFC_PROTO_ASYNC to enum ibmvfc_protocol; ibmvfc_register_channel() and ibmvfc_deregister_channel() now dispatch on protocol rather than a negative-index sentinel (patch 9)
- Added IBMVFC_ASYNC_SUB_CRQ_FMT format enum value and corresponding
async_scrq pointer in union ibmvfc_msgs (patch 6)
- ibmvfc_alloc_channels() selects IBMVFC_ASYNC_SUB_CRQ_FMT for async
protocol channels so queues are sized for ibmvfc_async_sub_crq entries
(patch 10)
- Link to v10: https://lore.kernel.org/linux-scsi/[email protected]/
Changes in v10:
- Fix ibmvfc_find_target() to use logical and (&&) instead of logical or (||) when checking whether fc and nvme remote ports are NULL (patch 4)
- Fix build issues due to missing & operator when referencing the host lock during irqsave/restore calls (patch 4, patch 7, patch 8, patch 11)
- Fix kunit build issues caused by inclusion of code meant for later patches (patch 4, patch 7, patch 11)
- Link to v9: https://lore.kernel.org/linux-scsi/[email protected]/
Changes in v9:
- Fix ibmvfc_find_target() to check for either a valid rport or nvme_remote_port
so nvme targets are not silently dropped (patch 4)
- Restore xxx_host_remove() ordering and destroy async work queue prior to these
calls. Check that the host is not offline prior to enqueuing new async work
(patch 4)
- Link to v8: https://lore.kernel.org/linux-scsi/[email protected]
Changes in v8:
- Split patch 6 into two patches: extend channel registration/deregistration
for async subq (patch 9), and clear sub-CRQ IRQ on registration failure
and guard teardown (patch 1)
- Fixed NULL dereference in ibmvfc_deregister_channel() when called on
an unallocated queue: guard memset() behind scrq->msgs.handle check
- Fixed variable-shadowing bug in ibmvfc_register_channel() error path
where H_FREE_SUB_CRQ hcall result clobbered the return code
- Deregister and re-register async sub-CRQ in ibmvfc_reenable_crq_queue()
and ibmvfc_reset_crq() alongside SCSI channel queues
- Link to v7: https://patch.msgid.link/[email protected]
Changes in v7:
- Removed CRQ valid bit clearing to callers of ibmvfc_handle_async in
all cases
- Added validity checks that IBMVFC_ASYNC_ID_IS_ASSOC_ID is clear
before using node_name
- Fixed error path cleanup in ibmvfc_init_sub_crqs
- Deregister and reregister async sub-CRQ in CRQ reset and reenable
code.
- Moved work queue destruction after sub CRQs teardown in ibmvfc_remove
- Modified async event handling code to use a tagged union rather than
void *.
- Use WQ_UNBOUND rather than WQ_PERCPU for fpin workqueue, as FPIN
events should be rare. Hold q_lock only in
ibmvfc_drain_async_subq(), like ibmvfc_drain_sub_crq().
- Hardened KUnit tests to use host_lock and kref.
- Link to v6: https://patch.msgid.link/[email protected]
Changes in v6:
- Use alloc_workqueue rather than devm_alloc_workqueue, and add
explicit destroy_workqueue calls
- Moved ibmvfc_find_target into patch 4
- Link to v5: https://patch.msgid.link/[email protected]
Changes in v5:
- Incorporate changes for NVME-over-FC.
- Removed now redundant fabric login patch.
- Link to v4: https://patch.msgid.link/[email protected]
Changes in v4:
- Refactored channel registration
- Check whether async work queue is allocated before using or freeing
- Fixed work queue allocation/destruction
- Skip basic KUnit test when there are no ibmvfc devices available
- Fix target not found condition in ibmvfc_process_async_work
- Link to v3: https://patch.msgid.link/[email protected]
Changes in v3:
- Fixed latent bug, exposed by VFC_NOOP, related to dataless CRQs and events
- Fixed FPIN TLV descriptor length calculations
- Use safe list walker to walk targets in ibmvfc_process_async_work
- Added write memory barriers after clearing CRQ valid field
- Use per-vhost work queue for FPIN work
- Link to v2: https://patch.msgid.link/[email protected]
Highlights of changes in v2:
- Refactored mostly common FPIN conversion routines and async event
processing into single routines with wrappers for differences.
- Moved FPIN processing to a work queue to avoid conflicts with
fc_host_fpin_rcv and memory allocation
- Set descriptor sizes correctly
- Use target WWPN for basic FPIN descriptor
- Split patch 4 into 3 patches, for definition, allocation, and use of
the asynchronous sub-queue for events
- Link to v1: https://patch.msgid.link/[email protected]
---
Dave Marquardt (11):
scsi: ibmvfc: fix IRQ leak and guard deregister on channel reg failure
scsi: ibmvfc: fix stale rc and IRQ mapping leak in ibmvfc_register_channel()
scsi: ibmvfc: fix resource leaks on partial queue allocation failure
scsi: ibmvfc: add basic FPIN support
scsi: ibmvfc: add NOOP command support
scsi: ibmvfc: add FPIN extended flag and async sub-CRQ queue handle
scsi: ibmvfc: extend async event handlers for async sub-CRQ events
scsi: ibmvfc: add interrupt routine for asynchronous sub CRQ
scsi: ibmvfc: extend channel registration for async sub-CRQ
scsi: ibmvfc: register and use asynchronous sub CRQ for events
scsi: ibmvfc: handle extended FPIN events
Tyrel Datwyler (2):
scsi: ibmvfc: fix potential clobbering of rc after failed irq setup
scsi: ibmvfc: fix potential NULL pointer dereference on failed queue allocation
drivers/scsi/Kconfig | 10 + drivers/scsi/ibmvscsi/Makefile | 1 + drivers/scsi/ibmvscsi/ibmvfc-core.c | 1009 ++++++++++++++++++++++++++++++---- drivers/scsi/ibmvscsi/ibmvfc.h | 69 +++ drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 584 ++++++++++++++++++++ 5 files changed, 1575 insertions(+), 98 deletions(-)
---
base-commit: c3cff7fac01638ab58e85fe7df41a04fa25c5bae
change-id: 20260925-ibmvfc-fpin-support-eeba4d08b425
Best regards,
Signature · 1 line
Dave Marquardt <[email protected]>
PATCH v18 1/13 scsi: ibmvfc: fix IRQ leak and guard deregister on channel reg failure
If request_irq() fails during ibmvfc_register_channel(), the error path disposes of the IRQ mapping via irq_dispose_mapping() but leaves scrq->irq populated with the stale virq number. A subsequent call to ibmvfc_deregister_channel() during teardown or reset cleanup then unconditionally invokes free_irq() and irq_dispose_mapping() on the stale IRQ descriptor, triggering kernel warnings.
Fix this by:
1. Explicitly setting scrq->irq to 0 after irq_dispose_mapping() in the ibmvfc_register_channel() error path.
2. Guarding free_irq() and irq_dispose_mapping() with a check for scrq->irq in ibmvfc_deregister_channel() so unmapped or unregistered queues are safely skipped.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +6 -3
@@ -6435,6 +6435,7 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,if (rc) {dev_err (dev,"Couldn't register sub-crq[%d] irq\n" , index);irq_dispose_mapping (scrq->irq); + scrq->irq =0 ; goto irq_failed; }
@@ -6463,9 +6464,11 @@ static void ibmvfc_deregister_channel(struct ibmvfc_host *vhost,ENTER ; -free_irq (scrq->irq, scrq); -irq_dispose_mapping (scrq->irq); - scrq->irq =0 ; +if (scrq->irq) { +free_irq (scrq->irq, scrq); +irq_dispose_mapping (scrq->irq); + scrq->irq =0 ; + } do { rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address,
Signature · 1 line
2.56.0
PATCH v18 2/13 scsi: ibmvfc: fix potential clobbering of rc after failed irq setup
In reply to Dave MarquardtFrom: Tyrel Datwyler <[email protected]>
Fix a variable-shadowing bug in ibmvfc_register_channel() where the irq_failed cleanup loop reused rc for the H_FREE_SUB_CRQ hcall result, clobbering the error code returned to the caller.
Introduce hcall_rc for the cleanup loop instead.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +3 -2
@@ -6391,6 +6391,7 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,struct vio_dev *vdev =to_vio_dev (dev);struct ibmvfc_queue *scrq = &channels->scrqs[index];int rc = -ENOMEM ; +int hcall_rc;ENTER ;
@@ -6446,8 +6447,8 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost, irq_failed:do { - rc =plpar_hcall_norets (H_FREE_SUB_CRQ , vdev->unit_address, scrq->cookie); - }while (rc ==H_BUSY ||H_IS_LONG_BUSY (rc)); + hcall_rc =plpar_hcall_norets (H_FREE_SUB_CRQ , vdev->unit_address, scrq->cookie); + }while (hcall_rc ==H_BUSY ||H_IS_LONG_BUSY (hcall_rc)); reg_failed:LEAVE ;return rc;
Signature · 1 line
2.56.0
PATCH v18 3/13 scsi: ibmvfc: fix potential NULL pointer dereference on failed queue allocation
In reply to Dave MarquardtFrom: Tyrel Datwyler <[email protected]>
Guard ibmvfc_deregister_channel() against calling H_FREE_SUB_CRQ on a queue that was never registered and against memsetting a buffer that was never allocated.
Add an scrq->cookie guard around the H_FREE_SUB_CRQ hypercall so it is only issued when the cookie is non-zero; clear scrq->cookie to zero on success to prevent double-free if the function is called again during reset or teardown. Add scrq->cookie = 0 in the ibmvfc_register_channel()irq_failed: cleanup path for the same reason. Guard the memset() of the queue message buffer behind a check of scrq->msgs.handle to prevent a NULL dereference when the function is called on a queue that was never allocated, such as async_sub_crq when multi-queue is disabled or initialization failed before ibmvfc_alloc_queue() was reached. Update the error message to include channels->protocol so failures can be distinguished across SCSI, NVMe, and async sub-CRQ channels.
Expose ibmvfc_deregister_channel() via VISIBLE_IF_KUNIT and EXPORT_SYMBOL_IF_KUNIT and add a kernel-doc header to support KUnit testing.
Add ibmvfc_get_first_vhost() to retrieve the first registered ibmvfc adapter with a reference held; expose it via VISIBLE_IF_KUNIT and EXPORT_SYMBOL_IF_KUNIT for use by KUnit tests.
Add SCSI_IBMVFC_KUNIT_TEST Kconfig entry and Makefile line, and create ibmvfc_kunit.c with an ibmvfc-channel-test suite containing ibmvfc_deregister_channel_guard_test(). The test verifies that calling ibmvfc_deregister_channel() on a queue with cookie == 0 and msgs.handle == NULL issues no hypercall, performs no memset, and leaves the queue state unchanged.
--- drivers/scsi/Kconfig | 10 ++++++ drivers/scsi/ibmvscsi/Makefile | 1 + drivers/scsi/ibmvscsi/ibmvfc-core.c | 65 +++++++++++++++++++++++++++++++----- drivers/scsi/ibmvscsi/ibmvfc.h | 8 +++++ drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 63 ++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 9 deletions(-)
drivers/scsi/Kconfig +10 -0
@@ -759,6 +759,16 @@ config SCSI_IBMVFC To compile this driver as a module, choose M here: the module will be called ibmvfc. +config SCSI_IBMVFC_KUNIT_TEST + tristate "KUnit tests for the IBM POWER Virtual FC Client" if !KUNIT_ALL_TESTS + depends on SCSI_IBMVFC && KUNIT + default KUNIT_ALL_TESTS + help + Compile IBM POWER Virtual FC client KUnit tests. These tests + cover the sub-CRQ channel deregistration guard path. + To compile this driver as a module, choose M here: the module + will be called ibmvfc_kunit. + config SCSI_IBMVFC_TRACE bool "enable driver internal trace" depends on SCSI_IBMVFC
drivers/scsi/ibmvscsi/Makefile +1 -0
@@ -3,3 +3,4 @@ ibmvfc-objs := ibmvfc-core.o ibmvfc-nvme.o obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi.o obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o +obj-$(CONFIG_SCSI_IBMVFC_KUNIT_TEST) += ibmvfc_kunit.o
drivers/scsi/ibmvscsi/ibmvfc-core.c +56 -9
98 lines not on this page · raw message
drivers/scsi/ibmvscsi/ibmvfc.h +8 -0
@@ -1088,4 +1088,12 @@ static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channel#define ibmvfc_remove_trace_file (kobj, attr) do { } while (0)#endif +#if IS_ENABLED (CONFIG_KUNIT ) +#include <kunit/visibility.h> +VISIBLE_IF_KUNIT void ibmvfc_deregister_channel(struct ibmvfc_host *vhost, +struct ibmvfc_channels *channels, +int index); +VISIBLE_IF_KUNIT struct ibmvfc_host *ibmvfc_get_first_vhost(void); +#endif +#endif
drivers/scsi/ibmvscsi/ibmvfc_kunit.c new file +63 -0
63 lines not on this page · raw message
Signature · 1 line
2.56.0
PATCH v18 4/13 scsi: ibmvfc: fix stale rc and IRQ mapping leak in ibmvfc_register_channel()
In reply to Dave MarquardtThe default: case of the protocol switch in ibmvfc_register_channel() jumps to irq_failed without setting rc, leaving a stale value from h_reg_sub_crq() (0 or H_CLOSED) that the caller sees as success even though the sub-CRQ was just freed. The mapping created by irq_create_mapping() is also never disposed, leaving scrq->irq non-zero.
Set rc = -EINVAL in the default: case. Introduce an irq_mapping_failed:
label that calls irq_dispose_mapping() and clears scrq->irq before falling through to irq_failed, and jump to it from both the default:
case and the request_irq() failure path, eliminating the duplicate inline dispose/clear in the latter.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +6 -4
@@ -6429,16 +6429,15 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,default :dev_err (dev,"Unknown channel protocol (%d)\n" , channels->protocol); - goto irq_failed; + rc = -EINVAL ; + goto irq_mapping_failed; } rc =request_irq (scrq->irq, scrq->handler,0 , scrq->name, scrq);if (rc) {dev_err (dev,"Couldn't register sub-crq[%d] irq\n" , index); -irq_dispose_mapping (scrq->irq); - scrq->irq =0 ; - goto irq_failed; + goto irq_mapping_failed; } scrq->hwq_id = index;
@@ -6446,6 +6445,9 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,LEAVE ;return 0 ; +irq_mapping_failed: +irq_dispose_mapping (scrq->irq); + scrq->irq =0 ; irq_failed: do { hcall_rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie);
Signature · 1 line
2.56.0
PATCH v18 5/13 scsi: ibmvfc: fix resource leaks on partial queue allocation failure
In reply to Dave Marquardtibmvfc_free_queue() unconditionally calls dma_unmap_single() and free_page() but does not guard either call against a partially or never-initialized queue. If ibmvfc_alloc_queue() fails before the DMA mapping is established (e.g. get_zeroed_page() fails or dma_mapping_error()), msgs.handle and msg_token are both zero/NULL. Calling dma_unmap_single() with address 0 is unsafe on many architectures.
Guard dma_unmap_single() and free_page() together under a msgs.handle check so ibmvfc_free_queue() is safe to call on a partially or never-initialized queue.
ibmvfc_alloc_queue() also leaks the event pool on both failure paths after ibmvfc_init_event_pool() succeeds: neither the get_zeroed_page() failure path nor the dma_mapping_error() failure path calls ibmvfc_free_event_pool(). No caller compensates, since ibmvfc_init_crq() returns immediately on ibmvfc_alloc_queue() failure, ibmvfc_alloc_channels() only unwinds previously succeeded queues, and ibmvfc_alloc_mem() jumps to a label that does not free the failed queue. Add ibmvfc_free_event_pool() calls on both error paths in ibmvfc_alloc_queue() to close the leak.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +9 -4
@@ -914,9 +914,11 @@ static void ibmvfc_free_queue(struct ibmvfc_host *vhost, {struct device *dev = vhost->dev; -dma_unmap_single (dev, queue->msg_token,PAGE_SIZE ,DMA_BIDIRECTIONAL ); -free_page ((unsigned long )queue->msgs.handle); - queue->msgs.handle =NULL ; +if (queue->msgs.handle) { +dma_unmap_single (dev, queue->msg_token,PAGE_SIZE ,DMA_BIDIRECTIONAL ); +free_page ((unsigned long )queue->msgs.handle); + queue->msgs.handle =NULL ; + }ibmvfc_free_event_pool (vhost, queue); }
@@ -6303,8 +6305,10 @@ static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost, } queue->msgs.handle = (void *)get_zeroed_page (GFP_KERNEL ); -if (!queue->msgs.handle) +if (!queue->msgs.handle) { +ibmvfc_free_event_pool (vhost, queue);return -ENOMEM ; + } queue->msg_token =dma_map_single (dev, queue->msgs.handle,PAGE_SIZE ,DMA_BIDIRECTIONAL );
@@ -6312,6 +6316,7 @@ static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost,if (dma_mapping_error (dev, queue->msg_token)) {free_page ((unsigned long )queue->msgs.handle); queue->msgs.handle =NULL ; +ibmvfc_free_event_pool (vhost, queue);return -ENOMEM ; }
Signature · 1 line
2.56.0
[email protected]
AI reviewRe: [PATCH v18 05/13] scsi: ibmvfc: fix resource leaks on partial queue allocation failure
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] scsi: ibmvfc: stale evt_pool state in ibmvfc_free_queue() on partial alloc --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] scsi: ibmvfc: stale evt_pool state in ibmvfc_free_queue() on partial alloc --
commit 226240ace51c2767f2bb52b147409116cfbb8d78
Author: Dave Marquardt <[email protected]>
scsi: ibmvfc: fix resource leaks on partial queue allocation failure
This commit guards dma_unmap_single() and free_page() in ibmvfc_free_queue() under a queue->msgs.handle check and adds ibmvfc_free_event_pool() calls on the error paths of ibmvfc_alloc_queue() after ibmvfc_init_event_pool() succeeds.
Quoted 32 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +8
-4@@ -914,9 +914,11 @@ static void ibmvfc_free_queue(struct ibmvfc_host *vhost, {struct device *dev = vhost->dev; -dma_unmap_single (dev, queue->msg_token,PAGE_SIZE ,DMA_BIDIRECTIONAL ); -free_page ((unsigned long )queue->msgs.handle); - queue->msgs.handle =NULL ; +if (queue->msgs.handle) { +dma_unmap_single (dev, queue->msg_token,PAGE_SIZE ,DMA_BIDIRECTIONAL ); +free_page ((unsigned long )queue->msgs.handle); + queue->msgs.handle =NULL ; + }ibmvfc_free_event_pool (vhost, queue); }@@ -6303,8 +6305,10 @@ static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost, } queue->msgs.handle = (void *)get_zeroed_page (GFP_KERNEL ); -if (!queue->msgs.handle) +if (!queue->msgs.handle) { +ibmvfc_free_event_pool (vhost, queue);return -ENOMEM ; + } queue->msg_token =dma_map_single (dev, queue->msgs.handle,PAGE_SIZE ,DMA_BIDIRECTIONAL );
drivers/scsi/ibmvscsi/ibmvfc-core.c static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost, · line 6321
Quoted 6 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +1
-0@@ -6312,6 +6316,7 @@ static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost,if (dma_mapping_error (dev, queue->msg_token)) {free_page ((unsigned long )queue->msgs.handle); queue->msgs.handle =NULL ; +ibmvfc_free_event_pool (vhost, queue);return -ENOMEM ; }
[Severity: High]
The commit message states that guarding dma_unmap_single() and free_page() under queue->msgs.handle makes ibmvfc_free_queue() safe to call on a partially initialized queue. However, does calling ibmvfc_free_queue() after a partial queue allocation failure still trigger a use-after-free, double-free, or NULL pointer dereference in ibmvfc_free_event_pool()?
In drivers/scsi/ibmvscsi/ibmvfc-core.c, ibmvfc_init_event_pool() sets pool->size before allocating pool->events and pool->iu_storage, and does not reset pool->size or clear pool->events on failure:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_init_event_pool() {
...
pool->size = queue->total_depth;
pool->events = kzalloc_objs(*pool->events, pool->size);
if (!pool->events)
return -ENOMEM;
pool->iu_storage = dma_alloc_coherent(vhost->dev,
pool->size * sizeof(*pool->iu_storage),
&pool->iu_token, 0);
if (!pool->iu_storage) {
kfree(pool->events);
return -ENOMEM;
}
...
}Similarly, when ibmvfc_alloc_queue() fails at get_zeroed_page() or dma_mapping_error() and calls ibmvfc_free_event_pool(), pool->events and pool->iu_storage are freed, but pool->size, pool->events, and pool->iu_storage are left with their old values:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_free_event_pool() {
...
for (i = 0; i < pool->size; ++i) {
list_del(&pool->events[i].queue_list);
BUG_ON(atomic_read(&pool->events[i].free) != 1);
if (pool->events[i].ext_list)
dma_pool_free(vhost->sg_pool,
pool->events[i].ext_list,
pool->events[i].ext_list_token);
}
kfree(pool->events);
dma_free_coherent(vhost->dev,
pool->size * sizeof(*pool->iu_storage),
pool->iu_storage, pool->iu_token);
LEAVE;
}Because ibmvfc_free_queue() still calls ibmvfc_free_event_pool() unconditionally outside the if (queue->msgs.handle) check, calling ibmvfc_free_queue() on a queue after ibmvfc_alloc_queue() fails (for IBMVFC_CRQ_FMT or IBMVFC_SUB_CRQ_FMT where queue->total_depth is non-zero) will iterate over the stale pool->size and either dereference a NULL pool->events pointer or access and free pool->events and pool->iu_storage a second time.
Should ibmvfc_free_event_pool() also be guarded or reset pool->size, pool->events, and pool->iu_storage so that ibmvfc_free_queue() is safe on a partially initialized queue?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
Re: [PATCH v18 05/13] scsi: ibmvfc: fix resource leaks on partial queue allocation failure
Quoted 113 lines · [email protected] writes:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] scsi: ibmvfc: staleevt_poolstate inibmvfc_free_queue()on partial alloc --commit
226240ace51c2767f2bb52b147409116cfbb8d78
Author: Dave Marquardt <[email protected]>scsi: ibmvfc: fix resource leaks on partial queue allocation failure
This commit guards
dma_unmap_single()andfree_page()inibmvfc_free_queue()under aqueue->msgs.handlecheck and addsibmvfc_free_event_pool()calls on the error paths ofibmvfc_alloc_queue()afteribmvfc_init_event_pool()succeeds.Quoted 32 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +8
-4@@ -914,9 +914,11 @@ static void ibmvfc_free_queue(struct ibmvfc_host *vhost, {struct device *dev = vhost->dev; -dma_unmap_single (dev, queue->msg_token,PAGE_SIZE ,DMA_BIDIRECTIONAL ); -free_page ((unsigned long )queue->msgs.handle); - queue->msgs.handle =NULL ; +if (queue->msgs.handle) { +dma_unmap_single (dev, queue->msg_token,PAGE_SIZE ,DMA_BIDIRECTIONAL ); +free_page ((unsigned long )queue->msgs.handle); + queue->msgs.handle =NULL ; + }ibmvfc_free_event_pool (vhost, queue); }@@ -6303,8 +6305,10 @@ static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost, } queue->msgs.handle = (void *)get_zeroed_page (GFP_KERNEL ); -if (!queue->msgs.handle) +if (!queue->msgs.handle) { +ibmvfc_free_event_pool (vhost, queue);return -ENOMEM ; + } queue->msg_token =dma_map_single (dev, queue->msgs.handle,PAGE_SIZE ,DMA_BIDIRECTIONAL );drivers/scsi/ibmvscsi/ibmvfc-core.c static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost, · line 6321
Quoted 6 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +1
-0@@ -6312,6 +6316,7 @@ static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost,if (dma_mapping_error (dev, queue->msg_token)) {free_page ((unsigned long )queue->msgs.handle); queue->msgs.handle =NULL ; +ibmvfc_free_event_pool (vhost, queue);return -ENOMEM ; }[Severity: High]
The commit message states that guardingdma_unmap_single()andfree_page()underqueue->msgs.handlemakesibmvfc_free_queue()safe to call on a partially initialized queue. However, does callingibmvfc_free_queue()after a partial queue allocation failure still trigger a use-after-free, double-free, or NULL pointer dereference inibmvfc_free_event_pool()?In
drivers/scsi/ibmvscsi/ibmvfc-core.c,ibmvfc_init_event_pool()setspool->sizebefore allocatingpool->eventsandpool->iu_storage, and does not resetpool->sizeor clearpool->eventson failure:drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_init_event_pool() { ... pool->size = queue->total_depth; pool->events = kzalloc_objs(*pool->events, pool->size); if (!pool->events) return -ENOMEM; pool->iu_storage = dma_alloc_coherent(vhost->dev, pool->size * sizeof(*pool->iu_storage), &pool->iu_token, 0); if (!pool->iu_storage) { kfree(pool->events); return -ENOMEM; } ... }Similarly, when
ibmvfc_alloc_queue()fails atget_zeroed_page()ordma_mapping_error()and callsibmvfc_free_event_pool(),pool->eventsandpool->iu_storageare freed, butpool->size,pool->events, andpool->iu_storageare left with their old values:drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_free_event_pool() { ... for (i = 0; i < pool->size; ++i) { list_del(&pool->events[i].queue_list); BUG_ON(atomic_read(&pool->events[i].free) != 1); if (pool->events[i].ext_list) dma_pool_free(vhost->sg_pool, pool->events[i].ext_list, pool->events[i].ext_list_token); } kfree(pool->events); dma_free_coherent(vhost->dev, pool->size * sizeof(*pool->iu_storage), pool->iu_storage, pool->iu_token); LEAVE; }Because
ibmvfc_free_queue()still callsibmvfc_free_event_pool()unconditionally outside the if (queue->msgs.handle) check, callingibmvfc_free_queue()on a queue afteribmvfc_alloc_queue()fails (forIBMVFC_CRQ_FMTorIBMVFC_SUB_CRQ_FMTwherequeue->total_depthis non-zero) will iterate over the stalepool->sizeand either dereference a NULLpool->eventspointer or access and freepool->eventsandpool->iu_storagea second time.
Quoted 3 lines
Should
ibmvfc_free_event_pool()also be guarded or resetpool->size,pool->events, andpool->iu_storageso thatibmvfc_free_queue()is safe on a partially initialized queue?
These problems aren't reachable as the code stands
today. ibmvfc_alloc_queue() frees the event pool itself on failure, and none of its callers call ibmvfc_free_queue() on a queue it failed to allocate. ibmvfc_alloc_channels() unwinds only the queues that succeeded. ibmvfc_init_crq() and ibmvfc_alloc_mem() return without freeing. I'll reword the commit message so it doesn't claim ibmvfc_free_queue() is safe after a failed alloc.
-Dave
PATCH v18 6/13 scsi: ibmvfc: add basic FPIN support
In reply to Dave MarquardtImplement support for a basic level of Fabric Performance Impact Notifications (FPIN) in the ibmvfc driver to enable monitoring of fabric congestion and link integrity events.
Add async event handler for IBMVFC_AE_FPIN events that offloads FPIN processing via a per-adapter ordered workqueue (fpin_workq). Convert VIOS FPIN messages to standard fc_els_fpin structures and pass them to fc_host_fpin_rcv() for processing by the FC transport layer.
The per-adapter workqueue is allocated in ibmvfc_probe() and destroyed in ibmvfc_remove() after ibmvfc_release_crq_queue(). This ordering is
critical: ibmvfc_release_crq_queue() disables CRQ interrupts and kills the tasklet, ensuring no new work items can be queued after that point. destroy_workqueue() then drains any already-queued items before ibmvfc_free_mem() runs, preventing use-after-free of tgt_pool. In ibmvfc_probe(), allocate fpin_workq after ibmvfc_alloc_mem() succeeds so that the error unwind path never calls destroy_workqueue() before ibmvfc_free_mem() -- which would otherwise double-free resources that ibmvfc_alloc_mem() already unwound internally.
Introduce common FPIN conversion routines that will be reused for full and extended FPIN support in subsequent patches. Add KUnit test infrastructure to validate FPIN event handling and statistics updates.
The KUnit test uses a private owned workqueue per test created with alloc_ordered_workqueue(). This gives ibmvfc_process_async_work() its own thread stack, avoiding kernel stack overflow when called from the deep KUnit test context.
Host-scope FPIN events (IBMVFC_AE_FPIN_LINK_CONGESTED and IBMVFC_AE_FPIN_CONGESTION_CLEARED) carry no per-target WWPN -- the all-zero identifier fields in the CRQ are intentional, not an error. Handle these events by calling ibmvfc_basic_fpin_to_desc() with wwpn=0 and delivering the resulting descriptor directly to fc_host_fpin_rcv() without requiring a target lookup. Per-target events continue to look up the target by scsi_id/wwpn/node_name as before. Use vhost->host (available in both paths) rather than tgt->vhost->host for the fc_host_fpin_rcv() call.
The basic CRQ format carries no detecting port WWPN. Set detecting_wwpn in fc_fn_peer_congn_desc and fc_fn_li_desc to fc_host_port_name(), the local port's own WWPN, which is the port that received and is reporting the event. Without this, detecting_wwpn would be zero from kzalloc and propagate to userspace verbatim via fc_host_post_fc_event(). The basic CRQ format also carries no event_threshold or event_count; compile-time defaults are used for those fields and documented as such in the ibmvfc_basic_fpin_to_desc() kernel-doc.
Add ibmvfc_handle_async() support for IBMVFC_AE_FPIN events, FPIN message conversion to fc_els_fpin format, handling of link congestion, port congestion, port cleared, port degraded, and congestion cleared events, and a KUnit test module for FPIN functionality.
--- drivers/scsi/Kconfig | 6 +- drivers/scsi/ibmvscsi/ibmvfc-core.c | 308 ++++++++++++++++++++++++++++++++++- drivers/scsi/ibmvscsi/ibmvfc.h | 12 ++ drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 238 +++++++++++++++++++++++++++ 4 files changed, 559 insertions(+), 5 deletions(-)
drivers/scsi/Kconfig +3 -3
@@ -765,9 +765,9 @@ config SCSI_IBMVFC_KUNIT_TEST default KUNIT_ALL_TESTS help Compile IBM POWER Virtual FC client KUnit tests. These tests - cover the sub-CRQ channel deregistration guard path. - To compile this driver as a module, choose M here: the module - will be called ibmvfc_kunit. + cover the sub-CRQ channel deregistration guard path and FPIN + event processing. To compile this driver as a module, choose + M here: the module will be called ibmvfc_kunit. config SCSI_IBMVFC_TRACE bool "enable driver internal trace"
drivers/scsi/ibmvscsi/ibmvfc-core.c +306 -2
370 lines not on this page · raw message
drivers/scsi/ibmvscsi/ibmvfc.h +12 -0
@@ -750,8 +750,12 @@ enum ibmvfc_ae_fpin_status {IBMVFC_AE_FPIN_PORT_CONGESTED =0x2 ,IBMVFC_AE_FPIN_PORT_CLEARED =0x3 ,IBMVFC_AE_FPIN_PORT_DEGRADED =0x4 , + IBMVFC_AE_FPIN_CONGESTION_CLEARED = 0x5, }; +#define IBMVFC_FPIN_DEFAULT_EVENT_PERIOD (5*60*MSEC_PER_SEC)/* 5 minutes */ +#define IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD (5*60*MSEC_PER_SEC/2)/* 2.5 minutes */ + struct ibmvfc_async_crq { volatile u8 valid; u8 link_state;
@@ -781,6 +785,12 @@ struct ibmvfc_async_sub_crq { } id; } __packed__aligned(8) ; +struct ibmvfc_async_work { +struct ibmvfc_host *vhost; +struct ibmvfc_async_crq crq; +struct work_struct async_work_s; +}; +union ibmvfc_iu {struct ibmvfc_mad_common mad_common;struct ibmvfc_npiv_login_mad npiv_login;
@@ -1022,6 +1032,7 @@ struct ibmvfc_host {wait_queue_head_t work_wait_q;struct nvme_fc_local_port *nvme_local_port;struct completion nvme_delete_done; +struct workqueue_struct *fpin_workq; };struct ibmvfc_event *__ibmvfc_get_event (struct ibmvfc_queue *queue,int reserved);
@@ -1090,6 +1101,7 @@ static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channel#if IS_ENABLED (CONFIG_KUNIT )#include <kunit/visibility.h> +VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work); VISIBLE_IF_KUNIT void ibmvfc_deregister_channel(struct ibmvfc_host *vhost,struct ibmvfc_channels *channels,int index);
drivers/scsi/ibmvscsi/ibmvfc_kunit.c +238 -0
254 lines not on this page · raw message
Signature · 1 line
2.56.0
[email protected]
AI reviewRe: [PATCH v18 06/13] scsi: ibmvfc: add basic FPIN support
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Critical] scsi: ibmvfc: uninitialized tgt and fpin in ibmvfc_process_async_work() --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] scsi: ibmvfc: uninitialized tgt and fpin in ibmvfc_process_async_work() --
commit f3f5aed7c70a8803eed36bba7b018020fbce24fa
Author: Dave Marquardt <[email protected]>
scsi: ibmvfc: add basic FPIN support
This commit adds basic Fabric Performance Impact Notification (FPIN) support to the ibmvfc driver, offloading IBMVFC_AE_FPIN async events to a per-adapter ordered workqueue and converting them into fc_els_fpin descriptors for fc_host_fpin_rcv(). It also adds a KUnit test suite to validate FPIN event handling and statistics updates.
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0
[ ... ]
Quoted 1 line
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0@@ -3264,6 +3268,277 @@ static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)
[ ... ]
Quoted 48 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +48
-0+/** +* ibmvfc_process_async_work - Process IBMVFC_AE_FPIN async CRQ from work queue +* @work: pointer to work_struct +*/ +VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) +{ +struct ibmvfc_async_work *aw; +struct ibmvfc_async_crq *crq; +struct ibmvfc_target *tgt; +struct ibmvfc_host *vhost; +struct fc_els_fpin *fpin; +unsigned long flags; + + aw = container_of_const(work, struct ibmvfc_async_work, async_work_s); + vhost = aw->vhost; + crq = &aw->crq; + +if (!crq->scsi_id && !crq->wwpn && !crq->node_name) { +if (crq->fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || + crq->fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) + fpin =ibmvfc_basic_fpin_to_desc (crq, +cpu_to_be64 (fc_host_port_name (vhost->host)),0 ); + goto send_fpin; + } + +spin_lock_irqsave (&vhost->host->host_lock, flags); + tgt =ibmvfc_find_target (&vhost->scsi_scrqs.targets, crq->scsi_id, + crq->wwpn, crq->node_name); +if (!tgt) { +/* Target not found in scsi_scrqs, search nvme_scrqs */ + tgt =ibmvfc_find_target (&vhost->nvme_scrqs.targets, + crq->scsi_id, crq->wwpn, + crq->node_name); + } + +if (tgt) { +kref_get (&tgt->kref); +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }else { +spin_unlock_irqrestore (&vhost->host->host_lock, flags); +dev_err_ratelimited (vhost->dev,"Invalid target for FPIN\n" ); + goto free; + } + + fpin =ibmvfc_basic_fpin_to_desc (crq, +cpu_to_be64 (fc_host_port_name (vhost->host)), + tgt->wwpn); +send_fpin:
drivers/scsi/ibmvscsi/ibmvfc-core.c static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state) · line 3535
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+if (fpin) { +fc_host_fpin_rcv (vhost->host, +sizeof (*fpin) +be32_to_cpu (fpin->desc_len), + (char *)fpin,0 ); +kfree (fpin); + }else +dev_err_ratelimited (vhost->dev,"FPIN event received, unable to process\n" ); + +if (tgt) +kref_put (&tgt->kref, ibmvfc_release_tgt);
[Severity: Critical]
In ibmvfc_process_async_work() in drivers/scsi/ibmvscsi/ibmvfc-core.c, both tgt and fpin are declared on the stack without being initialized to NULL.
When crq->scsi_id, crq->wwpn, and crq->node_name are all zero, the function jumps directly to the send_fpin label without ever initializing tgt. At send_fpin, checking if (tgt) and calling kref_put(&tgt->kref, ibmvfc_release_tgt) will operate on an uninitialized stack pointer on every zero-identifier FPIN event, including valid host-scope IBMVFC_AE_FPIN_LINK_CONGESTED and IBMVFC_AE_FPIN_CONGESTION_CLEARED events.
Furthermore, if crq->fpin_status on that zero-identifier path is neither IBMVFC_AE_FPIN_LINK_CONGESTED nor IBMVFC_AE_FPIN_CONGESTION_CLEARED (such as the zero-identifier IBMVFC_AE_FPIN_PORT_CONGESTED case exercised by ibmvfc_async_fpin_test() in drivers/scsi/ibmvscsi/ibmvfc_kunit.c), fpin is also left uninitialized before jumping to send_fpin. If the uninitialized fpin pointer on the stack is non-zero, send_fpin will dereference fpin->desc_len, pass the garbage pointer to fc_host_fpin_rcv(), and pass it to kfree(fpin).
Note that while the subsequent commit 6abed589f0fb ("scsi: ibmvfc: extend async event handlers for async sub-CRQ events") initializes fpin to NULL, tgt still remains uninitialized on the zero-identifier goto send_fpin path.
Should both tgt and fpin be initialized to NULL at declaration in ibmvfc_process_async_work()?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
Re: [PATCH v18 06/13] scsi: ibmvfc: add basic FPIN support
Quoted 90 lines · [email protected] writes:
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0[ ... ]
Quoted 1 line
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0@@ -3264,6 +3268,277 @@ static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)[ ... ]
Quoted 48 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +48
-0+/** +* ibmvfc_process_async_work - Process IBMVFC_AE_FPIN async CRQ from work queue +* @work: pointer to work_struct +*/ +VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) +{ +struct ibmvfc_async_work *aw; +struct ibmvfc_async_crq *crq; +struct ibmvfc_target *tgt; +struct ibmvfc_host *vhost; +struct fc_els_fpin *fpin; +unsigned long flags; + + aw = container_of_const(work, struct ibmvfc_async_work, async_work_s); + vhost = aw->vhost; + crq = &aw->crq; + +if (!crq->scsi_id && !crq->wwpn && !crq->node_name) { +if (crq->fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || + crq->fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) + fpin =ibmvfc_basic_fpin_to_desc (crq, +cpu_to_be64 (fc_host_port_name (vhost->host)),0 ); + goto send_fpin; + } + +spin_lock_irqsave (&vhost->host->host_lock, flags); + tgt =ibmvfc_find_target (&vhost->scsi_scrqs.targets, crq->scsi_id, + crq->wwpn, crq->node_name); +if (!tgt) { +/* Target not found in scsi_scrqs, search nvme_scrqs */ + tgt =ibmvfc_find_target (&vhost->nvme_scrqs.targets, + crq->scsi_id, crq->wwpn, + crq->node_name); + } + +if (tgt) { +kref_get (&tgt->kref); +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }else { +spin_unlock_irqrestore (&vhost->host->host_lock, flags); +dev_err_ratelimited (vhost->dev,"Invalid target for FPIN\n" ); + goto free; + } + + fpin =ibmvfc_basic_fpin_to_desc (crq, +cpu_to_be64 (fc_host_port_name (vhost->host)), + tgt->wwpn); +send_fpin:drivers/scsi/ibmvscsi/ibmvfc-core.c static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state) · line 3535
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+if (fpin) { +fc_host_fpin_rcv (vhost->host, +sizeof (*fpin) +be32_to_cpu (fpin->desc_len), + (char *)fpin,0 ); +kfree (fpin); + }else +dev_err_ratelimited (vhost->dev,"FPIN event received, unable to process\n" ); + +if (tgt) +kref_put (&tgt->kref, ibmvfc_release_tgt);[Severity: Critical]
Inibmvfc_process_async_work()indrivers/scsi/ibmvscsi/ibmvfc-core.c, both tgt and fpin are declared on the stack without being initialized to NULL.When
crq->scsi_id,crq->wwpn, andcrq->node_nameare all zero, the function jumps directly to thesend_fpinlabel without ever initializing tgt. Atsend_fpin, checking if (tgt) and callingkref_put(&tgt->kref, ibmvfc_release_tgt)will operate on an uninitialized stack pointer on every zero-identifier FPIN event, including valid host-scopeIBMVFC_AE_FPIN_LINK_CONGESTEDandIBMVFC_AE_FPIN_CONGESTION_CLEAREDevents.Furthermore, if
crq->fpin_statuson that zero-identifier path is neitherIBMVFC_AE_FPIN_LINK_CONGESTEDnorIBMVFC_AE_FPIN_CONGESTION_CLEARED(such as the zero-identifierIBMVFC_AE_FPIN_PORT_CONGESTEDcase exercised byibmvfc_async_fpin_test()indrivers/scsi/ibmvscsi/ibmvfc_kunit.c), fpin is also left uninitialized before jumping tosend_fpin. If the uninitialized fpin pointer on the stack is non-zero,send_fpinwill dereferencefpin->desc_len, pass the garbage pointer tofc_host_fpin_rcv(), and pass it to kfree(fpin).
Quoted 6 lines
Note that while the subsequent commit
6abed589f0fb("scsi: ibmvfc: extend async event handlers for async sub-CRQ events") initializes fpin to NULL, tgt still remains uninitialized on the zero-identifier gotosend_fpinpath.Should both tgt and fpin be initialized to NULL at declaration in
ibmvfc_process_async_work()?
Yes, tgt and fpin should be initialized to NULL. I'll update this in the next revision.
-Dave
PATCH v18 7/13 scsi: ibmvfc: add NOOP command support
In reply to Dave MarquardtAdd support for handling IBMVFC_NOOP format CRQ messages from the VIOS partner.
Advertise the client's ability to handle NOOP commands by setting the IBMVFC_CAN_USE_NOOP_CMD capability in ibmvfc_set_login_info().
Handle and ignore IBMVFC_NOOP format messages in both ibmvfc_handle_crq() and ibmvfc_handle_scrq(). In both handlers, log a rate-limited error if a NOOP is received while in the IBMVFC_ACTIVE state without the partner having advertised IBMVFC_SUPPORT_NOOP_CMD.
In ibmvfc_handle_scrq(), also fix a typo ("Got and invalid" -> "Got an invalid"), add a missing return on invalid message types, and guard against null event pointers before processing CRQ responses.
Change ibmvfc_handle_scrq() to take a struct ibmvfc_queue *scrq parameter instead of struct ibmvfc_host *vhost, deriving vhost from scrq->vhost internally. Use &scrq->evt_pool for event validation instead of &evt->queue->evt_pool, avoiding a potential wild pointer dereference if crq->ioba contains a non-zero garbage token before ibmvfc_valid_event() has a chance to validate it.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +30 -6
@@ -1580,7 +1580,9 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) login_info->flags |=cpu_to_be16 (IBMVFC_CLIENT_MIGRATED ); login_info->max_cmds =cpu_to_be32 (max_cmds); - login_info->capabilities =cpu_to_be64 (IBMVFC_CAN_MIGRATE |IBMVFC_CAN_SEND_VF_WWPN ); + login_info->capabilities = +cpu_to_be64 (IBMVFC_CAN_MIGRATE |IBMVFC_CAN_SEND_VF_WWPN | +IBMVFC_CAN_USE_NOOP_CMD );if (vhost->mq_enabled || vhost->using_channels) { login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_CHANNELS );
@@ -3729,6 +3731,14 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost,if (crq->format ==IBMVFC_ASYNC_EVENT )return ; +if (crq->format ==IBMVFC_NOOP ) { +if (vhost->state ==IBMVFC_ACTIVE && + !ibmvfc_check_caps (vhost,IBMVFC_SUPPORT_NOOP_CMD )) +dev_err_ratelimited (vhost->dev, +"Received unexpected NOOP command from partner\n" ); +return ; + } +/* The only kind of payload CRQs we should get are responses to * things we send. Make sure this response is to something we * actually sent
@@ -4248,9 +4258,10 @@ static int ibmvfc_toggle_scrq_irq(struct ibmvfc_queue *scrq, int enable)return rc; } -static void ibmvfc_handle_scrq (struct ibmvfc_crq *crq,struct ibmvfc_host *vhost, +static void ibmvfc_handle_scrq (struct ibmvfc_crq *crq,struct ibmvfc_queue *scrq,struct list_head *evt_doneq) { +struct ibmvfc_host *vhost = scrq->vhost;struct ibmvfc_event *evt = (struct ibmvfc_event *)be64_to_cpu (crq->ioba);switch (crq->valid) {
@@ -4259,7 +4270,20 @@ static void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhostcase IBMVFC_CRQ_XPORT_EVENT :return ;default : -dev_err (vhost->dev,"Got and invalid message type 0x%02x\n" , crq->valid); +dev_err (vhost->dev,"Got an invalid message type 0x%02x\n" , crq->valid); +return ; + } + +if (crq->format ==IBMVFC_NOOP ) { +if (vhost->state ==IBMVFC_ACTIVE && + !ibmvfc_check_caps (vhost,IBMVFC_SUPPORT_NOOP_CMD )) +dev_err_ratelimited (vhost->dev, +"Received unexpected NOOP command from partner\n" ); +return ; + } + +if (unlikely (!evt)) { +dev_err (vhost->dev,"Received null event\n" );return ; }
@@ -4267,7 +4291,7 @@ static void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost* things we send. Make sure this response is to something we * actually sent */ -if (unlikely (!ibmvfc_valid_event (&evt->queue->evt_pool, evt))) { +if (unlikely (!ibmvfc_valid_event (&scrq->evt_pool, evt))) {dev_err (vhost->dev,"Returned correlation_token 0x%08llx is invalid!\n" , crq->ioba);return ;
@@ -4310,7 +4334,7 @@ static void ibmvfc_drain_sub_crq(struct ibmvfc_queue *scrq)spin_lock_irqsave (scrq->q_lock, flags);while (!done) {while ((crq =ibmvfc_next_scrq (scrq)) !=NULL ) { -ibmvfc_handle_scrq (crq, scrq->vhost, &evt_doneq); +ibmvfc_handle_scrq (crq, scrq, &evt_doneq); crq->valid =0 ;wmb (); }
@@ -4318,7 +4342,7 @@ static void ibmvfc_drain_sub_crq(struct ibmvfc_queue *scrq)ibmvfc_toggle_scrq_irq (scrq,1 );if ((crq =ibmvfc_next_scrq (scrq)) !=NULL ) {ibmvfc_toggle_scrq_irq (scrq,0 ); -ibmvfc_handle_scrq (crq, scrq->vhost, &evt_doneq); +ibmvfc_handle_scrq (crq, scrq, &evt_doneq); crq->valid =0 ;wmb (); }else
Signature · 1 line
2.56.0
PATCH v18 8/13 scsi: ibmvfc: add FPIN extended flag and async sub-CRQ queue handle
In reply to Dave MarquardtAdd IBMVFC_ASYNC_IS_FPIN_EXT flag bit (0x02) to ibmvfc_async_sub_crq.flags to distinguish extended FPIN events from standard ones.
Add async_scrqs field of type ibmvfc_channels to ibmvfc_host to hold the channel set for the asynchronous sub-CRQ, alongside the existing scsi_scrqs and nvme_scrqs channels. This uses the same ibmvfc_channels infrastructure as the SCSI and NVMe channel queues.
Add IBMVFC_ASYNC_SUB_CRQ_FMT to enum ibmvfc_msg_fmt and a corresponding async_scrq pointer to union ibmvfc_msgs to support the new async sub-CRQ message format.
--- drivers/scsi/ibmvscsi/ibmvfc.h | 4 ++++ 1 file changed, 4 insertions(+)
drivers/scsi/ibmvscsi/ibmvfc.h +4 -0
@@ -773,6 +773,7 @@ struct ibmvfc_async_sub_crq {volatile u8 valid;u8 flags;#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01 +#define IBMVFC_ASYNC_IS_FPIN_EXT 0x02u8 link_state;u8 fpin_status;__be16 event;
@@ -893,6 +894,7 @@ enum ibmvfc_msg_fmt {IBMVFC_CRQ_FMT =0 ,IBMVFC_ASYNC_FMT ,IBMVFC_SUB_CRQ_FMT , + IBMVFC_ASYNC_SUB_CRQ_FMT, }; union ibmvfc_msgs {
@@ -900,6 +902,7 @@ union ibmvfc_msgs {struct ibmvfc_crq *crq;struct ibmvfc_async_crq *async;struct ibmvfc_sub_crq *scrq; +struct ibmvfc_async_sub_crq *async_scrq; };struct ibmvfc_queue {
@@ -992,6 +995,7 @@ struct ibmvfc_host {struct ibmvfc_queue async_crq;struct ibmvfc_channels scsi_scrqs;struct ibmvfc_channels nvme_scrqs; +struct ibmvfc_channels async_scrqs;struct ibmvfc_npiv_login login_info;union ibmvfc_npiv_login_data *login_buf;dma_addr_t login_buf_dma;
Signature · 1 line
2.56.0
PATCH v18 9/13 scsi: ibmvfc: extend async event handlers for async sub-CRQ events
In reply to Dave MarquardtRefactor async event handling to support both traditional async CRQs and new asynchronous sub-queue CRQs.
Introduce struct ibmvfc_async_crq_event, a tagged union that wraps either an ibmvfc_async_crq (main CRQ) or an ibmvfc_async_sub_crq (async sub-CRQ), with an enum ibmvfc_async_crq_type discriminator. Replace the ibmvfc_async_work bare union and is_subq bool with a single event field of this type.
Modify ibmvfc_handle_async() to accept a struct ibmvfc_async_crq_event * instead of a void * plus a bool flag. Update ibmvfc_process_async_work() to dispatch based on event.type.
Add ibmvfc_full_fpin_to_desc() to convert full FPIN messages from async sub-queue format to fc_els_fpin structures. Thread detecting_wwpn (fc_host_port_name()) through ibmvfc_full_fpin_to_desc() to ibmvfc_common_fpin_to_desc() so that fc_fn_peer_congn_desc.detecting_wwpn and fc_fn_li_desc.detecting_wwpn are set to the local port WWPN rather than left as zero from kzalloc. Set severity to 0 for PORT_CLEARED and CONGESTION_CLEARED events in ibmvfc_full_fpin_to_desc() since those cleared states carry no active severity level. Update FPIN processing logic to extract WWPN, node_name, and scsi_id from the appropriate union member based on event type. Extract fpin_status as a local u8 from either subq->fpin_status or crq->fpin_status so it is available for both paths.
Add ibmvfc_find_target() for target lookup by scsi_id, wwpn, node_name, and assoc_id (all optional; 0 skips that field). When an async sub-CRQ event has IBMVFC_ASYNC_ID_IS_ASSOC_ID set, extract assoc_id from subq->id.assoc_id and pass it to ibmvfc_find_target() so NVMe targets with a shared wwpn are distinguished by their association ID rather than matched by wwpn alone. Propagate assoc_id into the ELS LOGO/PRLO/PLOGI target loops in ibmvfc_handle_async() for the same reason.
ibmvfc_find_target() skips targets mid-teardown (rport == NULL and nvme_remote_port == NULL), so a LINK_CONGESTED or CONGESTION_CLEARED event with non-zero identifiers may still fail to find a target when the matching port is being torn down. Since those event types describe host-scope congestion that does not require a live rport, fall through to a host-scope descriptor (wwpn=0) via the existing send_fpin path rather than dropping the event with "Invalid target for FPIN".
Update KUnit tests to use ibmvfc_run_async_work() with a single reused struct ibmvfc_async_crq_event rather than arrays at call sites.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 213 ++++++++++++++++++++++++++++------- drivers/scsi/ibmvscsi/ibmvfc.h | 15 ++- drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 189 ++++++++++++++++++++++++++----- 3 files changed, 349 insertions(+), 68 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +173 -40
@@ -3446,19 +3446,62 @@ ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, __be64 detecting_wwpn, cpu_to_be32(1)); } +/** +* ibmvfc_full_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct +* containing a descriptor. +* @ibmvfc_fpin: Pointer to async subq FPIN data +* @detecting_wwpn: WWPN of the local port that detected the event (big-endian) +* @wwpn: attached port WWPN (host-endian) +* +* Allocate a struct fc_els_fpin containing a descriptor and populate +* based on data from *ibmvfc_fpin. +* +* Return: +* NULL - unable to allocate structure +* non-NULL - pointer to populated struct fc_els_fpin +*/ +static struct fc_els_fpin * +ibmvfc_full_fpin_to_desc (struct ibmvfc_async_sub_crq *ibmvfc_fpin, +__be64 detecting_wwpn,u64 wwpn) +{ +__be16 type; + +switch (ibmvfc_fpin->fpin_status) { +case IBMVFC_AE_FPIN_LINK_CONGESTED : +case IBMVFC_AE_FPIN_PORT_CONGESTED : + type =cpu_to_be16 (FPIN_CONGN_DEVICE_SPEC ); +break ; +case IBMVFC_AE_FPIN_PORT_CLEARED : +case IBMVFC_AE_FPIN_CONGESTION_CLEARED : + type =cpu_to_be16 (FPIN_CONGN_CLEAR ); +break ; +case IBMVFC_AE_FPIN_PORT_DEGRADED : + type =cpu_to_be16 (FPIN_LI_UNKNOWN ); +break ; +default : +return NULL ; + } + +return ibmvfc_common_fpin_to_desc (ibmvfc_fpin->fpin_status, detecting_wwpn, +cpu_to_be64 (wwpn), type,cpu_to_be16 (0 ), +cpu_to_be32 (IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD ), +cpu_to_be32 (1 )); +} +/** * ibmvfc_find_target - Search for a target in a target list * @target_list: list head of targets to search * @scsi_id: SCSI ID to match (0 to skip this check) * @wwpn: WWPN to match (0 to skip this check) * @node_name: Node name to match (0 to skip this check) +* @assoc_id: Association ID to match (0 to skip this check) * * Returns: * Pointer to matching target, or NULL if not found **/ static struct ibmvfc_target *ibmvfc_find_target (struct list_head *target_list,__be64 scsi_id,__be64 wwpn, -__be64 node_name) +__be64 node_name,__be64 assoc_id) {struct ibmvfc_target *tgt;
@@ -3469,6 +3512,8 @@ static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list,continue ;if (node_name && tgt->ids.node_name !=be64_to_cpu (node_name))continue ; +if (assoc_id && tgt->assoc_id !=be64_to_cpu (assoc_id)) +continue ;if (!tgt->rport && !tgt->nvme_remote_port)continue ;return tgt;
@@ -3483,33 +3528,59 @@ static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list,*/ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) { +struct ibmvfc_async_sub_crq *subq =NULL ;struct ibmvfc_async_work *aw; -struct ibmvfc_async_crq *crq; +struct ibmvfc_async_crq *crq =NULL ;struct ibmvfc_target *tgt;struct ibmvfc_host *vhost; -struct fc_els_fpin *fpin; +struct fc_els_fpin *fpin =NULL ;unsigned long flags; +__be64 assoc_id; +__be64 node_name; +__be64 scsi_id; +__be64 wwpn; +u8 fpin_status; aw = container_of_const(work, struct ibmvfc_async_work, async_work_s); vhost = aw->vhost; - crq = &aw->crq; +if (aw->event.type ==IBMVFC_ASYNC_CRQ_SUB ) { + subq = &aw->event.subq; + scsi_id =0 ; + wwpn = subq->wwpn; + fpin_status = subq->fpin_status; +if (subq->flags &IBMVFC_ASYNC_ID_IS_ASSOC_ID ) { + node_name =0 ; + assoc_id = subq->id.assoc_id; + }else if (subq->flags &IBMVFC_ASYNC_IS_FPIN_EXT ) { + node_name =0 ; + assoc_id =0 ; + }else { + node_name = subq->id.node_name; + assoc_id =0 ; + } + }else { + crq = &aw->event.async_crq; + scsi_id = crq->scsi_id; + wwpn = crq->wwpn; + node_name = crq->node_name; + fpin_status = crq->fpin_status; + assoc_id =0 ; + } -if (!crq->scsi_id && !crq->wwpn && !crq->node_name) { -if (crq->fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || - crq->fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) +if (!scsi_id && !wwpn && !node_name && !assoc_id) { +if (fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || + fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) fpin =ibmvfc_basic_fpin_to_desc (crq,cpu_to_be64 (fc_host_port_name (vhost->host)),0 ); goto send_fpin; }spin_lock_irqsave (&vhost->host->host_lock, flags); - tgt =ibmvfc_find_target (&vhost->scsi_scrqs.targets, crq->scsi_id, - crq->wwpn, crq->node_name); + tgt =ibmvfc_find_target (&vhost->scsi_scrqs.targets, scsi_id, wwpn, node_name, assoc_id); +if (!tgt) {/* Target not found in scsi_scrqs, search nvme_scrqs */ - tgt =ibmvfc_find_target (&vhost->nvme_scrqs.targets, - crq->scsi_id, crq->wwpn, - crq->node_name); + tgt =ibmvfc_find_target (&vhost->nvme_scrqs.targets, scsi_id, wwpn, node_name, assoc_id); }if (tgt) {
@@ -3517,13 +3588,29 @@ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work)spin_unlock_irqrestore (&vhost->host->host_lock, flags); }else {spin_unlock_irqrestore (&vhost->host->host_lock, flags); +if (fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || + fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) { +if (crq) + fpin =ibmvfc_basic_fpin_to_desc (crq, +cpu_to_be64 (fc_host_port_name (vhost->host)),0 ); +else + fpin =ibmvfc_full_fpin_to_desc (subq, +cpu_to_be64 (fc_host_port_name (vhost->host)),0 ); + goto send_fpin; + }dev_err_ratelimited (vhost->dev,"Invalid target for FPIN\n" ); goto free; } - fpin =ibmvfc_basic_fpin_to_desc (crq, +if (crq) + fpin =ibmvfc_basic_fpin_to_desc (crq,cpu_to_be64 (fc_host_port_name (vhost->host)), tgt->wwpn); +else + fpin =ibmvfc_full_fpin_to_desc (subq, +cpu_to_be64 (fc_host_port_name (vhost->host)), + tgt->wwpn); + send_fpin:if (fpin) { fc_host_fpin_rcv(vhost->host,
@@ -3543,25 +3630,62 @@ EXPORT_SYMBOL_IF_KUNIT(ibmvfc_process_async_work);/** * ibmvfc_handle_async - Handle an async event from the adapter -* @crq: crq to process +* @ae: tagged union wrapping either an ibmvfc_async_crq (main CRQ) or an +* ibmvfc_async_sub_crq (async sub-CRQ); the type field identifies which * @vhost: ibmvfc host struct * **/ -static void ibmvfc_handle_async (struct ibmvfc_async_crq *crq, +static void ibmvfc_handle_async (struct ibmvfc_async_crq_event *ae,struct ibmvfc_host *vhost) { -const struct ibmvfc_async_desc *desc =ibmvfc_get_ae_desc (be64_to_cpu (crq->event)); +struct ibmvfc_async_crq *async_crq =NULL ; +struct ibmvfc_async_sub_crq *subq =NULL ; +const struct ibmvfc_async_desc *desc;struct ibmvfc_async_work *aw;struct ibmvfc_target *tgt; - -ibmvfc_log (vhost, desc->log_level,"%s event received. scsi_id: %llx, wwpn: %llx," -" node_name: %llx%s\n" , desc->desc,be64_to_cpu (crq->scsi_id), -be64_to_cpu (crq->wwpn),be64_to_cpu (crq->node_name), -ibmvfc_get_link_state (crq->link_state)); - -switch (be64_to_cpu (crq->event)) { +__be64 assoc_id; +__be64 node_name; +__be64 scsi_id; +u8 link_state; +__be64 wwpn; +u64 event; + +if (ae->type ==IBMVFC_ASYNC_CRQ_SUB ) { + subq = &ae->subq; + event = (u64 )be16_to_cpu (subq->event); + link_state = subq->link_state; + scsi_id =0 ; + wwpn = subq->wwpn; +if (subq->flags &IBMVFC_ASYNC_ID_IS_ASSOC_ID ) { + node_name =0 ; + assoc_id = subq->id.assoc_id; + }else if (subq->flags &IBMVFC_ASYNC_IS_FPIN_EXT ) { + node_name =0 ; + assoc_id =0 ; + }else { + node_name = subq->id.node_name; + assoc_id =0 ; + } + }else { + async_crq = &ae->async_crq; + event =be64_to_cpu (async_crq->event); + link_state = async_crq->link_state; + scsi_id = async_crq->scsi_id; + wwpn = async_crq->wwpn; + node_name = async_crq->node_name; + assoc_id =0 ; + } + + desc =ibmvfc_get_ae_desc (event); +ibmvfc_log (vhost, desc->log_level, +"%s event received. scsi_id: %llx, wwpn: %llx, node_name: %llx, event %llx%s\n" , + desc->desc,be64_to_cpu (scsi_id), +be64_to_cpu (wwpn),be64_to_cpu (node_name), event, +ibmvfc_get_link_state (link_state)); + +switch (event) {case IBMVFC_AE_RESUME : -switch (crq->link_state) { +switch (link_state) {case IBMVFC_AE_LS_LINK_DOWN :ibmvfc_link_down (vhost,IBMVFC_LINK_DOWN );break ;
@@ -3600,33 +3724,37 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,case IBMVFC_AE_ELS_PRLO :case IBMVFC_AE_ELS_PLOGI :list_for_each_entry (tgt, &vhost->scsi_scrqs.targets, queue) { -if (!crq->scsi_id && !crq->wwpn && !crq->node_name) +if (!scsi_id && !wwpn && !node_name && !assoc_id)break ; -if (crq->scsi_id &&cpu_to_be64 (tgt->scsi_id) != crq->scsi_id) +if (scsi_id &&cpu_to_be64 (tgt->scsi_id) != scsi_id) +continue ; +if (wwpn &&cpu_to_be64 (tgt->ids.port_name) != wwpn)continue ; -if (crq->wwpn &&cpu_to_be64 (tgt->ids.port_name) != crq->wwpn) +if (node_name &&cpu_to_be64 (tgt->ids.node_name) != node_name)continue ; -if (crq->node_name &&cpu_to_be64 (tgt->ids.node_name) != crq->node_name) +if (assoc_id &&cpu_to_be64 (tgt->assoc_id) != assoc_id)continue ; -if (tgt->need_login &&be64_to_cpu (crq->event) ==IBMVFC_AE_ELS_LOGO ) +if (tgt->need_login && event ==IBMVFC_AE_ELS_LOGO ) tgt->logo_rcvd =1 ; -if (!tgt->need_login ||be64_to_cpu (crq->event) ==IBMVFC_AE_ELS_PLOGI ) { +if (!tgt->need_login || event ==IBMVFC_AE_ELS_PLOGI ) {ibmvfc_del_tgt (tgt);ibmvfc_reinit_host (vhost); } }list_for_each_entry (tgt, &vhost->nvme_scrqs.targets, queue) { -if (!crq->scsi_id && !crq->wwpn && !crq->node_name) +if (!scsi_id && !wwpn && !node_name && !assoc_id)break ; -if (crq->scsi_id &&cpu_to_be64 (tgt->scsi_id) != crq->scsi_id) +if (scsi_id &&cpu_to_be64 (tgt->scsi_id) != scsi_id) +continue ; +if (wwpn &&cpu_to_be64 (tgt->ids.port_name) != wwpn)continue ; -if (crq->wwpn &&cpu_to_be64 (tgt->ids.port_name) != crq->wwpn) +if (node_name &&cpu_to_be64 (tgt->ids.node_name) != node_name)continue ; -if (crq->node_name &&cpu_to_be64 (tgt->ids.node_name) != crq->node_name) +if (assoc_id &&cpu_to_be64 (tgt->assoc_id) != assoc_id)continue ; -if (tgt->need_login &&be64_to_cpu (crq->event) ==IBMVFC_AE_ELS_LOGO ) +if (tgt->need_login && event ==IBMVFC_AE_ELS_LOGO ) tgt->logo_rcvd =1 ; -if (!tgt->need_login ||be64_to_cpu (crq->event) ==IBMVFC_AE_ELS_PLOGI ) { +if (!tgt->need_login || event ==IBMVFC_AE_ELS_PLOGI ) {ibmvfc_del_tgt (tgt);ibmvfc_reinit_host (vhost); }
@@ -3653,14 +3781,14 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, }INIT_WORK (&aw->async_work_s, ibmvfc_process_async_work); aw->vhost = vhost; - aw->crq = *crq; + aw->event = *ae;queue_work (vhost->fpin_workq, &aw->async_work_s); }else dev_err_ratelimited (vhost->dev,"can't offload async CRQ to work queue\n" );break ;default : -dev_err (vhost->dev,"Unknown async event received: %lld\n" , crq->event); +dev_err (vhost->dev,"Unknown async event received: %llu\n" , event);break ; } }
@@ -4188,6 +4316,9 @@ static void ibmvfc_tasklet(void *data) {struct ibmvfc_host *vhost = data;struct vio_dev *vdev =to_vio_dev (vhost->dev); +struct ibmvfc_async_crq_event ae = { + .type =IBMVFC_ASYNC_CRQ_MAIN , + };struct ibmvfc_crq *crq;struct ibmvfc_async_crq *async;struct ibmvfc_event *evt, *temp;
@@ -4200,7 +4331,8 @@ static void ibmvfc_tasklet(void *data)while (!done) {/* Pull all the valid messages off the async CRQ */ while ((async =ibmvfc_next_async_crq (vhost)) !=NULL ) { -ibmvfc_handle_async (async, vhost); + ae.async_crq = *async; +ibmvfc_handle_async (&ae, vhost); async->valid =0 ;wmb (); }
@@ -4214,8 +4346,9 @@ static void ibmvfc_tasklet(void *data)vio_enable_interrupts (vdev);if ((async =ibmvfc_next_async_crq (vhost)) !=NULL ) { + ae.async_crq = *async;vio_disable_interrupts (vdev); -ibmvfc_handle_async (async, vhost); +ibmvfc_handle_async (&ae, vhost); async->valid =0 ;wmb (); }else if ((crq =ibmvfc_next_crq (vhost)) !=NULL ) {
drivers/scsi/ibmvscsi/ibmvfc.h +14 -1
@@ -786,9 +786,22 @@ struct ibmvfc_async_sub_crq { } id; } __packed__aligned(8) ; +enum ibmvfc_async_crq_type { +IBMVFC_ASYNC_CRQ_MAIN =0 , +IBMVFC_ASYNC_CRQ_SUB , +}; + +struct ibmvfc_async_crq_event { +enum ibmvfc_async_crq_type type; +union { +struct ibmvfc_async_crq async_crq; +struct ibmvfc_async_sub_crq subq; + }; +}; +struct ibmvfc_async_work {struct ibmvfc_host *vhost; -struct ibmvfc_async_crq crq; +struct ibmvfc_async_crq_event event;struct work_struct async_work_s; };
drivers/scsi/ibmvscsi/ibmvfc_kunit.c +162 -27
248 lines not on this page · raw message
Signature · 1 line
2.56.0
[email protected]
AI reviewRe: [PATCH v18 09/13] scsi: ibmvfc: extend async event handlers for async sub-CRQ events
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] scsi: ibmvfc: NULL crq dereference in ibmvfc_process_async_work() - [High] scsi: ibmvfc…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] scsi: ibmvfc: NULL crq dereference in ibmvfc_process_async_work()
- [High] scsi: ibmvfc: uninitialized tgt read in ibmvfc_process_async_work() --
commit 6abed589f0fb0c5ef9d54c07ceaaef90b5ef640d
Author: Dave Marquardt <[email protected]>
scsi: ibmvfc: extend async event handlers for async sub-CRQ events
This commit refactors async event handling in ibmvfc to support both main CRQ and async sub-CRQ events via a tagged union struct ibmvfc_async_crq_event, and adds ibmvfc_full_fpin_to_desc() and assoc_id target matching.
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0
[ ... ]
drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list, · line 3534
Quoted 9 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +2
-1@@ -3483,33 +3528,59 @@ static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list,*/ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) { +struct ibmvfc_async_sub_crq *subq =NULL ;struct ibmvfc_async_work *aw; -struct ibmvfc_async_crq *crq; +struct ibmvfc_async_crq *crq =NULL ;struct ibmvfc_target *tgt;
[Severity: High]
Should tgt be initialized to NULL here?
When scsi_id, wwpn, node_name, and assoc_id are all zero, ibmvfc_process_async_work() jumps directly to the send_fpin label before tgt is assigned by ibmvfc_find_target():
if (!scsi_id && !wwpn && !node_name && !assoc_id) {
if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED ||
fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED)
fpin = ibmvfc_basic_fpin_to_desc(crq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
goto send_fpin;
}At the end of send_fpin, the cleanup checks:
if (tgt) kref_put(&tgt->kref, ibmvfc_release_tgt);
Wouldn't that evaluate and potentially dereference an uninitialized tgt pointer on the zero-identifier path?
Quoted 37 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +29
-2struct ibmvfc_host *vhost; -struct fc_els_fpin *fpin; +struct fc_els_fpin *fpin =NULL ;unsigned long flags; +__be64 assoc_id; +__be64 node_name; +__be64 scsi_id; +__be64 wwpn; +u8 fpin_status; aw = container_of_const(work, struct ibmvfc_async_work, async_work_s); vhost = aw->vhost; - crq = &aw->crq; +if (aw->event.type ==IBMVFC_ASYNC_CRQ_SUB ) { + subq = &aw->event.subq; + scsi_id =0 ; + wwpn = subq->wwpn; + fpin_status = subq->fpin_status; +if (subq->flags &IBMVFC_ASYNC_ID_IS_ASSOC_ID ) { + node_name =0 ; + assoc_id = subq->id.assoc_id; + }else if (subq->flags &IBMVFC_ASYNC_IS_FPIN_EXT ) { + node_name =0 ; + assoc_id =0 ; + }else { + node_name = subq->id.node_name; + assoc_id =0 ; + } + }else { + crq = &aw->event.async_crq; + scsi_id = crq->scsi_id; + wwpn = crq->wwpn; + node_name = crq->node_name; + fpin_status = crq->fpin_status; + assoc_id =0 ; + }
drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list, · line 3576
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +3
-3-if (!crq->scsi_id && !crq->wwpn && !crq->node_name) { -if (crq->fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || - crq->fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) +if (!scsi_id && !wwpn && !node_name && !assoc_id) { +if (fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || + fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) fpin =ibmvfc_basic_fpin_to_desc (crq,cpu_to_be64 (fc_host_port_name (vhost->host)),0 ); goto send_fpin; }
[Severity: High]
Can this dereference a NULL crq pointer when handling an async sub-CRQ event?
When aw->event.type is IBMVFC_ASYNC_CRQ_SUB, subq is populated and crq remains NULL. If scsi_id, wwpn, node_name, and assoc_id are all zero for a host-scope IBMVFC_AE_FPIN_LINK_CONGESTED or IBMVFC_AE_FPIN_CONGESTION_CLEARED event, this block unconditionally calls ibmvfc_basic_fpin_to_desc(crq, ...), which immediately dereferences crq->fpin_status:
static struct fc_els_fpin *
ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, __be64 detecting_wwpn,
u64 wwpn)
{
__be16 type;
switch (crq->fpin_status) {Should this path check if (crq) and dispatch to ibmvfc_full_fpin_to_desc(subq, ...) when crq is NULL, matching the target fallback path further down in ibmvfc_process_async_work()?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
Re: [PATCH v18 09/13] scsi: ibmvfc: extend async event handlers for async sub-CRQ events
Quoted 30 lines · [email protected] writes:
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0[ ... ]
drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list, · line 3534
Quoted 9 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +2
-1@@ -3483,33 +3528,59 @@ static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list,*/ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) { +struct ibmvfc_async_sub_crq *subq =NULL ;struct ibmvfc_async_work *aw; -struct ibmvfc_async_crq *crq; +struct ibmvfc_async_crq *crq =NULL ;struct ibmvfc_target *tgt;[Severity: High]
Should tgt be initialized to NULL here?When
scsi_id, wwpn,node_name, andassoc_idare all zero,ibmvfc_process_async_work()jumps directly to thesend_fpinlabel before tgt is assigned byibmvfc_find_target():if (!scsi_id && !wwpn && !node_name && !assoc_id) { if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED || fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED) fpin = ibmvfc_basic_fpin_to_desc(crq, cpu_to_be64(fc_host_port_name(vhost->host)), 0); goto send_fpin; }
Quoted 7 lines
At the end of
send_fpin, the cleanup checks:if (tgt) kref_put(&tgt->kref, ibmvfc_release_tgt);Wouldn't that evaluate and potentially dereference an uninitialized tgt pointer on the zero-identifier path?
This will be fixed in patch 6, as discussed in the response to patch 6.
Quoted 67 lines
Quoted 37 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +29
-2struct ibmvfc_host *vhost; -struct fc_els_fpin *fpin; +struct fc_els_fpin *fpin =NULL ;unsigned long flags; +__be64 assoc_id; +__be64 node_name; +__be64 scsi_id; +__be64 wwpn; +u8 fpin_status; aw = container_of_const(work, struct ibmvfc_async_work, async_work_s); vhost = aw->vhost; - crq = &aw->crq; +if (aw->event.type ==IBMVFC_ASYNC_CRQ_SUB ) { + subq = &aw->event.subq; + scsi_id =0 ; + wwpn = subq->wwpn; + fpin_status = subq->fpin_status; +if (subq->flags &IBMVFC_ASYNC_ID_IS_ASSOC_ID ) { + node_name =0 ; + assoc_id = subq->id.assoc_id; + }else if (subq->flags &IBMVFC_ASYNC_IS_FPIN_EXT ) { + node_name =0 ; + assoc_id =0 ; + }else { + node_name = subq->id.node_name; + assoc_id =0 ; + } + }else { + crq = &aw->event.async_crq; + scsi_id = crq->scsi_id; + wwpn = crq->wwpn; + node_name = crq->node_name; + fpin_status = crq->fpin_status; + assoc_id =0 ; + }drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list, · line 3576
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +3
-3-if (!crq->scsi_id && !crq->wwpn && !crq->node_name) { -if (crq->fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || - crq->fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) +if (!scsi_id && !wwpn && !node_name && !assoc_id) { +if (fpin_status ==IBMVFC_AE_FPIN_LINK_CONGESTED || + fpin_status ==IBMVFC_AE_FPIN_CONGESTION_CLEARED ) fpin =ibmvfc_basic_fpin_to_desc (crq,cpu_to_be64 (fc_host_port_name (vhost->host)),0 ); goto send_fpin; }[Severity: High]
Can this dereference a NULL crq pointer when handling an async sub-CRQ event?When
aw->event.typeisIBMVFC_ASYNC_CRQ_SUB, subq is populated and crq remains NULL. Ifscsi_id, wwpn,node_name, andassoc_idare all zero for a host-scopeIBMVFC_AE_FPIN_LINK_CONGESTEDorIBMVFC_AE_FPIN_CONGESTION_CLEAREDevent, this block unconditionally callsibmvfc_basic_fpin_to_desc(crq, ...), which immediately dereferencescrq->fpin_status:static struct
fc_els_fpin*
ibmvfc_basic_fpin_to_desc(structibmvfc_async_crq*crq,__be64detecting_wwpn,u64 wwpn) { __be16 type; switch (crq->fpin_status) {
Quoted 3 lines
Should this path check if (crq) and dispatch to
ibmvfc_full_fpin_to_desc(subq, ...)when crq is NULL, matching the target fallback path further down inibmvfc_process_async_work()?
Yes, this is problem. For the next revision the code will check whether crq is NULL before calling ibmvfc_basic_fpin_to_desc(). If crq is NULL, the else clause will call ibmvfc_full_fpin_to_desc() with subq.
-Dave
PATCH v18 10/13 scsi: ibmvfc: add interrupt routine for asynchronous sub CRQ
In reply to Dave MarquardtAdd ibmvfc_interrupt_async_subq(), an IRQ handler dedicated to asynchronous sub-CRQ events from the adapter. The handler disables the sub-CRQ IRQ and then calls ibmvfc_drain_async_subq() to consume all pending entries before re-enabling interrupts.
Add ibmvfc_next_async_scrq() to traverse the async sub-CRQ ring directly via msgs.async_scrq[], returning a typed
struct ibmvfc_async_sub_crq * without the cast that would be needed when going through ibmvfc_next_scrq().
ibmvfc_drain_async_subq() separates ring drain from event dispatch. With host_lock and q_lock both held, it loops over available entries via ibmvfc_next_async_scrq() and passes each one to ibmvfc_queue_async_subq_entry(). That helper allocates a struct ibmvfc_async_drain_entry node with kzalloc_obj(..., GFP_ATOMIC), copies the event into it, and appends it to a local doneq list. The node is zero-allocated because struct ibmvfc_async_crq_event is a union and the copied sub-CRQ is smaller than the main CRQ member; the remaining bytes are later copied wholesale into the FPIN work item. The valid bit is cleared and a write barrier issued regardless of whether the allocation succeeded. If the GFP_ATOMIC allocation fails, a ratelimited error is logged and that event is dropped; it is not retried. After draining the inner loop, both locks
are still held while the sub-CRQ IRQ is re-enabled and one final ring check is performed to close the race between the last drain and IRQ re-enable; any newly arrived entry is collected the same way. Only after both locks are released does ibmvfc_drain_async_subq() walk doneq, re-taking host_lock around each ibmvfc_handle_async() call before freeing the node.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 100 ++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+)
drivers/scsi/ibmvscsi/ibmvfc-core.c +100 -0
@@ -4456,6 +4456,106 @@ static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)return crq; } +static struct ibmvfc_async_sub_crq *ibmvfc_next_async_scrq (struct ibmvfc_queue *scrq) +{ +struct ibmvfc_async_sub_crq *crq; + + crq = &scrq->msgs.async_scrq[scrq->cur]; +if (crq->valid &0x80 ) { +if (++scrq->cur == scrq->size) + scrq->cur =0 ; +rmb (); + }else + crq =NULL ; + +return crq; +} + +struct ibmvfc_async_drain_entry { +struct ibmvfc_async_crq_event event; +struct list_head list; +}; + +/** +* ibmvfc_queue_async_subq_entry - Copy an async sub-CRQ entry to a deferred list +* @vhost: ibmvfc host struct +* @crq: async sub-CRQ entry to consume +* @doneq: list on which to queue the copied event for later dispatch +* +* Copies @crq into a newly allocated ibmvfc_async_drain_entry and appends it +* to @doneq. If the allocation fails, a ratelimited error is logged and the +* event is dropped. In either case the entry is returned to the adapter by +* clearing its valid bit. +**/ +static void ibmvfc_queue_async_subq_entry (struct ibmvfc_host *vhost, +struct ibmvfc_async_sub_crq *crq, +struct list_head *doneq) +{ +struct ibmvfc_async_drain_entry *entry; + + entry =kzalloc_obj (*entry,GFP_ATOMIC ); +if (entry) { + entry->event.type =IBMVFC_ASYNC_CRQ_SUB ; + entry->event.subq = *crq; +list_add_tail (&entry->list, doneq); + }else +dev_err_ratelimited (vhost->dev, +"Couldn't allocate async drain entry\n" ); + crq->valid =0 ; +wmb ();/* complete write */ +} + +static void ibmvfc_drain_async_subq (struct ibmvfc_queue *scrq) +{ +struct ibmvfc_host *vhost = scrq->vhost; +struct ibmvfc_async_drain_entry *entry, *temp; +struct ibmvfc_async_sub_crq *crq; +LIST_HEAD (doneq); +unsigned long flags; +int done =0 ; + +spin_lock_irqsave (&vhost->host->host_lock, flags); +spin_lock (scrq->q_lock); +while (!done) { +while ((crq =ibmvfc_next_async_scrq (scrq)) !=NULL ) +ibmvfc_queue_async_subq_entry (vhost, crq, &doneq); + +ibmvfc_toggle_scrq_irq (scrq,1 ); + crq =ibmvfc_next_async_scrq (scrq); +if (crq !=NULL ) { +ibmvfc_toggle_scrq_irq (scrq,0 ); +ibmvfc_queue_async_subq_entry (vhost, crq, &doneq); + }else + done =1 ; + } +spin_unlock (scrq->q_lock); +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + +list_for_each_entry_safe (entry, temp, &doneq, list) { +list_del (&entry->list); +spin_lock_irqsave (&vhost->host->host_lock, flags); +ibmvfc_handle_async (&entry->event, vhost); +spin_unlock_irqrestore (&vhost->host->host_lock, flags); +kfree (entry); + } +} + +/** +* ibmvfc_interrupt_async_subq - Handle an async event from the adapter +* @irq: interrupt request +* @scrq_instance: async subq +* +**/ +static irqreturn_t __maybe_unused ibmvfc_interrupt_async_subq (int irq,void *scrq_instance) +{ +struct ibmvfc_queue *scrq = (struct ibmvfc_queue *)scrq_instance; + +ibmvfc_toggle_scrq_irq (scrq,0 ); +ibmvfc_drain_async_subq (scrq); + +return IRQ_HANDLED ; +} +static void ibmvfc_drain_sub_crq (struct ibmvfc_queue *scrq) {struct ibmvfc_crq *crq;
Signature · 1 line
2.56.0
[email protected]
AI reviewRe: [PATCH v18 10/13] scsi: ibmvfc: add interrupt routine for asynchronous sub CRQ
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] scsi: ibmvfc: NULL crq dereference in ibmvfc_process_async_work() - [High] scsi: ibmvfc…
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] scsi: ibmvfc: NULL crq dereference in ibmvfc_process_async_work()
- [High] scsi: ibmvfc: uninitialized tgt pointer in ibmvfc_process_async_work() --
commit e623a6d56430928cd690d92425a90ffd4dc7ba74
Author: Dave Marquardt <[email protected]>
scsi: ibmvfc: add interrupt routine for asynchronous sub CRQ
This commit adds ibmvfc_interrupt_async_subq() and supporting helpers to drain asynchronous sub-CRQ entries onto a temporary list and dispatch them through ibmvfc_handle_async().
Quoted 5 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0@@ -4456,6 +4456,106 @@ static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)
[ ... ]
drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq) · line 4499
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+static void ibmvfc_queue_async_subq_entry (struct ibmvfc_host *vhost, +struct ibmvfc_async_sub_crq *crq, +struct list_head *doneq) +{ +struct ibmvfc_async_drain_entry *entry; + + entry =kzalloc_obj (*entry,GFP_ATOMIC ); +if (entry) { + entry->event.type =IBMVFC_ASYNC_CRQ_SUB ; + entry->event.subq = *crq;
[Severity: High]
Can dispatching an IBMVFC_ASYNC_CRQ_SUB FPIN event here lead to a NULL pointer dereference in ibmvfc_process_async_work()?
When ibmvfc_drain_async_subq() passes entry->event to ibmvfc_handle_async() for an IBMVFC_AE_FPIN event, it queues ibmvfc_process_async_work() with aw->event.type == IBMVFC_ASYNC_CRQ_SUB.
In drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_process_async_work(), crq is initialized to NULL and remains NULL on the IBMVFC_ASYNC_CRQ_SUB branch:
struct ibmvfc_async_sub_crq *subq = NULL;
struct ibmvfc_async_work *aw;
struct ibmvfc_async_crq *crq = NULL;
...
if (aw->event.type == IBMVFC_ASYNC_CRQ_SUB) {
subq = &aw->event.subq;
scsi_id = 0;
wwpn = subq->wwpn;
...
}If the sub-CRQ FPIN event has all-zero target identifiers (for example, a host-scope IBMVFC_AE_FPIN_LINK_CONGESTED or IBMVFC_AE_FPIN_CONGESTION_CLEARED event with zero wwpn and zero node_name/assoc_id, or with IBMVFC_ASYNC_IS_FPIN_EXT set and wwpn == 0), ibmvfc_process_async_work() unconditionally calls ibmvfc_basic_fpin_to_desc() with crq == NULL instead of ibmvfc_full_fpin_to_desc(subq, ...):
if (!scsi_id && !wwpn && !node_name && !assoc_id) {
if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED ||
fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED)
fpin = ibmvfc_basic_fpin_to_desc(crq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
goto send_fpin;
}ibmvfc_basic_fpin_to_desc() then immediately dereferences crq->fpin_status. Should this path check if (crq) vs else ibmvfc_full_fpin_to_desc(subq, ...) in the same way as the !tgt fallback below it?
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+list_add_tail (&entry->list, doneq); + }else +dev_err_ratelimited (vhost->dev, +"Couldn't allocate async drain entry\n" ); + crq->valid =0 ; +wmb ();/* complete write */ +} + +static void ibmvfc_drain_async_subq (struct ibmvfc_queue *scrq) +{
[ ... ]
drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq) · line 4537
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +4
-0+list_for_each_entry_safe (entry, temp, &doneq, list) { +list_del (&entry->list); +spin_lock_irqsave (&vhost->host->host_lock, flags); +ibmvfc_handle_async (&entry->event, vhost);
[Severity: High]
When ibmvfc_handle_async() queues ibmvfc_process_async_work() for an IBMVFC_AE_FPIN event with all-zero target identifiers, can tgt be used uninitialized at the send_fpin label?
In drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_process_async_work(), tgt is declared without initialization, and the all-zero identifier check jumps over ibmvfc_find_target() directly to send_fpin:
struct ibmvfc_target *tgt;
...
if (!scsi_id && !wwpn && !node_name && !assoc_id) {
if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED ||
fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED)
fpin = ibmvfc_basic_fpin_to_desc(crq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
goto send_fpin;
}
...
send_fpin:
...
if (tgt)
kref_put(&tgt->kref, ibmvfc_release_tgt);Wouldn't that evaluate if (tgt) and call kref_put() on an uninitialized stack pointer?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=10
Re: [PATCH v18 10/13] scsi: ibmvfc: add interrupt routine for asynchronous sub CRQ
Quoted 56 lines · [email protected] writes:
Quoted 5 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0@@ -4456,6 +4456,106 @@ static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)[ ... ]
drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq) · line 4499
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+static void ibmvfc_queue_async_subq_entry (struct ibmvfc_host *vhost, +struct ibmvfc_async_sub_crq *crq, +struct list_head *doneq) +{ +struct ibmvfc_async_drain_entry *entry; + + entry =kzalloc_obj (*entry,GFP_ATOMIC ); +if (entry) { + entry->event.type =IBMVFC_ASYNC_CRQ_SUB ; + entry->event.subq = *crq;[Severity: High]
Can dispatching anIBMVFC_ASYNC_CRQ_SUBFPIN event here lead to a NULL pointer dereference inibmvfc_process_async_work()?When
ibmvfc_drain_async_subq()passesentry->eventtoibmvfc_handle_async()for anIBMVFC_AE_FPINevent, it queuesibmvfc_process_async_work()withaw->event.type==IBMVFC_ASYNC_CRQ_SUB.In drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_process_async_work(), crq is initialized to NULL and remains NULL on the
IBMVFC_ASYNC_CRQ_SUBbranch:struct ibmvfc_async_sub_crq *subq = NULL; struct ibmvfc_async_work *aw; struct ibmvfc_async_crq *crq = NULL; ... if (aw->event.type == IBMVFC_ASYNC_CRQ_SUB) { subq = &aw->event.subq; scsi_id = 0; wwpn = subq->wwpn; ... }If the sub-CRQ FPIN event has all-zero target identifiers (for example, a host-scope
IBMVFC_AE_FPIN_LINK_CONGESTEDorIBMVFC_AE_FPIN_CONGESTION_CLEAREDevent with zero wwpn and zero node_name/assoc_id, or withIBMVFC_ASYNC_IS_FPIN_EXTset and wwpn == 0),ibmvfc_process_async_work()unconditionally callsibmvfc_basic_fpin_to_desc()with crq == NULL instead ofibmvfc_full_fpin_to_desc(subq, ...):if (!scsi_id && !wwpn && !node_name && !assoc_id) { if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED || fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED) fpin = ibmvfc_basic_fpin_to_desc(crq, cpu_to_be64(fc_host_port_name(vhost->host)), 0); goto send_fpin; }
Quoted 3 lines
ibmvfc_basic_fpin_to_desc()then immediately dereferencescrq->fpin_status. Should this path check if (crq) vs elseibmvfc_full_fpin_to_desc(subq, ...)in the same way as the !tgt fallback below it?
Fixed in the next revision of patch 9.
Quoted 42 lines
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+list_add_tail (&entry->list, doneq); + }else +dev_err_ratelimited (vhost->dev, +"Couldn't allocate async drain entry\n" ); + crq->valid =0 ; +wmb ();/* complete write */ +} + +static void ibmvfc_drain_async_subq (struct ibmvfc_queue *scrq) +{[ ... ]
drivers/scsi/ibmvscsi/ibmvfc-core.c static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq) · line 4537
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +4
-0+list_for_each_entry_safe (entry, temp, &doneq, list) { +list_del (&entry->list); +spin_lock_irqsave (&vhost->host->host_lock, flags); +ibmvfc_handle_async (&entry->event, vhost);[Severity: High]
Whenibmvfc_handle_async()queuesibmvfc_process_async_work()for anIBMVFC_AE_FPINevent with all-zero target identifiers, can tgt be used uninitialized at thesend_fpinlabel?In drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_process_async_work(), tgt is declared without initialization, and the all-zero identifier check jumps over
ibmvfc_find_target()directly tosend_fpin:struct ibmvfc_target *tgt; ... if (!scsi_id && !wwpn && !node_name && !assoc_id) { if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED || fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED) fpin = ibmvfc_basic_fpin_to_desc(crq, cpu_to_be64(fc_host_port_name(vhost->host)), 0); goto send_fpin; } ... send_fpin: ... if (tgt) kref_put(&tgt->kref, ibmvfc_release_tgt);
Quoted 2 lines
Wouldn't that evaluate if (tgt) and call
kref_put()on an uninitialized stack pointer?
Fixed in the next revision of patch 6.
-Dave
PATCH v18 11/13 scsi: ibmvfc: extend channel registration for async sub-CRQ
In reply to Dave Marquardtibmvfc_register_channel() previously only handled SCSI and NVMe sub-CRQ channels identified by the channel protocol (IBMVFC_PROTO_SCSI, IBMVFC_PROTO_NVME). The async sub-CRQ had no registration path through this helper, requiring separate handling.
Add a new IBMVFC_PROTO_ASYNC protocol value to enum ibmvfc_protocol and handle it in ibmvfc_register_channel(): the IRQ is named "ibmvfc-<addr>-async<index>" and the handler is set to ibmvfc_interrupt_async_subq rather than the per-protocol ibmvfc_interrupt_mq handler.
ibmvfc_deregister_channel() does not depend on the channel protocol, so it needs no change to handle async sub-CRQs.
Remove the __maybe_unused annotation from ibmvfc_interrupt_async_subq now that it is referenced in ibmvfc_register_channel().
Include the channel protocol in the IRQ mapping and request_irq() error messages in ibmvfc_register_channel() to distinguish async sub-CRQ failures from SCSI/NVMe sub-CRQ failures. Add a kernel-doc header to ibmvfc_register_channel().
Set scrq->hwq_id before calling request_irq() rather than after it succeeds. The handler can run as soon as the IRQ is requested, and ibmvfc_toggle_scrq_irq() reports hwq_id in its error message. The failure path does not use hwq_id, so no cleanup is affected.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 30 +++++++++++++++++++++++++----- drivers/scsi/ibmvscsi/ibmvfc.h | 1 + 2 files changed, 26 insertions(+), 5 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +25 -5
@@ -4546,7 +4546,7 @@ static void ibmvfc_drain_async_subq(struct ibmvfc_queue *scrq)* @scrq_instance: async subq * **/ -static irqreturn_t __maybe_unused ibmvfc_interrupt_async_subq (int irq,void *scrq_instance) +static irqreturn_t ibmvfc_interrupt_async_subq (int irq,void *scrq_instance) {struct ibmvfc_queue *scrq = (struct ibmvfc_queue *)scrq_instance;
@@ -6939,6 +6939,19 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost)return retrc; } +/** +* ibmvfc_register_channel - Register a sub-CRQ channel with the hypervisor +* @vhost: ibmvfc host struct +* @channels: ibmvfc channels struct containing the channel array and protocol +* @index: index into the channels array for the queue to register +* +* Register a sub-CRQ with the hypervisor via h_reg_sub_crq, map its hardware +* IRQ to a Linux IRQ, and bind an interrupt handler to it. The handler is +* selected based on the channel protocol. +* +* Return value: +* 0 on success / non-zero on failure +**/ static int ibmvfc_register_channel (struct ibmvfc_host *vhost,struct ibmvfc_channels *channels,int index)
@@ -6966,7 +6979,8 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,if (!scrq->irq) { rc = -EINVAL ; -dev_err (dev,"Error mapping sub-crq[%d] irq\n" , index); +dev_err (dev,"Error mapping protocol (%d) sub-crq[%d] irq\n" , + channels->protocol, index); goto irq_failed; }
@@ -6981,6 +6995,11 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost, vdev->unit_address, index); scrq->handler = ibmvfc_interrupt_mq;break ; +case IBMVFC_PROTO_ASYNC : +snprintf (scrq->name,sizeof (scrq->name),"ibmvfc-%x-async%d" , + vdev->unit_address, index); + scrq->handler = ibmvfc_interrupt_async_subq; +break ;default :dev_err (dev,"Unknown channel protocol (%d)\n" , channels->protocol);
@@ -6988,15 +7007,16 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost, goto irq_mapping_failed; } + scrq->hwq_id = index; + rc =request_irq (scrq->irq, scrq->handler,0 , scrq->name, scrq);if (rc) { -dev_err (dev,"Couldn't register sub-crq[%d] irq\n" , index); +dev_err (dev,"Couldn't register protocol (%d) sub-crq[%d] irq\n" , + channels->protocol, index); goto irq_mapping_failed; } - scrq->hwq_id = index; -LEAVE ;return 0 ;
drivers/scsi/ibmvscsi/ibmvfc.h +1 -0
@@ -839,6 +839,7 @@ enum ibmvfc_target_action {enum ibmvfc_protocol {IBMVFC_PROTO_SCSI =0 ,IBMVFC_PROTO_NVME =1 , +IBMVFC_PROTO_ASYNC =2 , };struct ibmvfc_target {
Signature · 1 line
2.56.0
PATCH v18 12/13 scsi: ibmvfc: register and use asynchronous sub CRQ for events
In reply to Dave MarquardtWire the async sub-CRQ into the sub-CRQ lifecycle so it is allocated, registered, and freed alongside the SCSI channel queues.
In ibmvfc_probe(), initialize vhost->async_scrqs with desired_queues=1, max_queues=1, and protocol=IBMVFC_PROTO_ASYNC.
In ibmvfc_init_sub_crqs(), allocate the async sub-CRQ via ibmvfc_alloc_channels() before allocating the SCSI channels. Change ibmvfc_reg_sub_crqs() to return int (0 on success, -1 on failure) so ibmvfc_init_sub_crqs() can detect registration failure per channel set and unwind correctly: async registration failure releases async_scrqs and disables multi-queue; SCSI registration failure releases scsi_scrqs, deregisters and releases async_scrqs, and disables multi-queue. ibmvfc_reg_sub_crqs() returns -1 on failure and leaves flag management to its callers. In ibmvfc_init_sub_crqs(), registration failure of any channel set triggers cleanup of that set and all previously registered sets, then clears vhost->mq_enabled and vhost->do_enquiry to prevent a host state machine stall: without this, mq_enabled remaining set causes ibmvfc_npiv_login_done() to enter the multi-queue branch while do_enquiry is 0, bypassing ibmvfc_channel_enquiry() and leaving the host indefinitely in IBMVFC_HOST_ACTION_INIT_WAIT. In ibmvfc_reenable_crq_queue() and ibmvfc_reset_crq(), if any ibmvfc_reg_sub_crqs() call fails, all three channel sets are deregistered and mq_enabled and do_enquiry are cleared.
mq_enabled and do_enquiry are bitfields packed into the same word as other state flags such as using_channels. All failure-path writes to mq_enabled, do_enquiry, and nvme_enabled are performed under host_lock to prevent lost updates from concurrent read-modify-write operations on the packed bitfield word.
Remove vhost->mq_enabled from the ibmvfc_dereg_sub_crqs() guard, relying solely on !channels->scrqs. The previous guard caused all deregistration calls after a registration failure to be silently
skipped: since ibmvfc_reg_sub_crqs() clears mq_enabled on failure, any previously registered channel sets (e.g. async_scrqs registered before scsi_scrqs fails) would be leaked -- their hypervisor sub-CRQ registrations and IRQ handlers left active while ibmvfc_release_channels() subsequently freed the underlying memory. ibmvfc_deregister_channel() already guards individually on scrq->cookie and scrq->irq, so iterating unconditionally over channels with scrqs allocated is safe.
In ibmvfc_alloc_channels(), select IBMVFC_ASYNC_SUB_CRQ_FMT for async protocol channels instead of IBMVFC_SUB_CRQ_FMT, so the queue is sized for ibmvfc_async_sub_crq entries. Zero active_queues on allocation failure to ensure the field is always consistent with scrqs.
In ibmvfc_release_sub_crqs(), ibmvfc_reenable_crq_queue(), and ibmvfc_reset_crq(), deregister and re-register the async sub-CRQ alongside the SCSI channel queues.
In ibmvfc_channel_setup_done(), compute async->active_queues from the VIOS response: set it to 1 only when async->scrqs is non-NULL and the response contains a non-zero async_sub_crq_handle, otherwise 0. Store the handle in vhost->async_scrqs.scrqs[0].vios_cookie via the active_queues loop. Clamp scsi->active_queues and nvme->active_queues to their respective max_queues values before using them as loop bounds, preventing out-of-bounds access to scrqs[] if the hypervisor returns a larger value than was allocated. In ibmvfc_channel_setup(), guard the access to async->scrqs[0].cookie behind a NULL check to prevent a dereference when mq_enabled is 0 and async->scrqs was never allocated. Set async_sub_crq_handle unconditionally so the handle is communicated to VIOS even when scsi_channels and nvme_channels are both 0; omitting it on the CANCEL_CHANNELS path would leave the async sub-CRQ registered and advertised but unused.
In ibmvfc_set_login_info(), advertise IBMVFC_USE_ASYNC_SUBQ, IBMVFC_CAN_HANDLE_FPIN, and IBMVFC_YES_SCSI capabilities whenever multi-queue channels are enabled. IBMVFC_YES_SCSI was previously only set for NVMe-enabled configurations; move it to the common multi-queue path so it is always advertised when channels are in use.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 178 ++++++++++++++++++++++++++++-------- 1 file changed, 139 insertions(+), 39 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +139 -39
@@ -191,7 +191,7 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *);static void ibmvfc_tgt_move_login (struct ibmvfc_target *);static void ibmvfc_dereg_sub_crqs (struct ibmvfc_host *,struct ibmvfc_channels *); -static void ibmvfc_reg_sub_crqs (struct ibmvfc_host *,struct ibmvfc_channels *); +static int ibmvfc_reg_sub_crqs (struct ibmvfc_host *,struct ibmvfc_channels *);static const char *unknown_error ="unknown error" ;
@@ -966,6 +966,7 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)struct vio_dev *vdev =to_vio_dev (vhost->dev);unsigned long flags; +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs);ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs);ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs);
@@ -988,8 +989,17 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)spin_unlock (vhost->crq.q_lock);spin_unlock_irqrestore (&vhost->host->host_lock, flags); -ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs); -ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs); +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->do_enquiry =0 ; + vhost->mq_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }return rc; }
@@ -1008,6 +1018,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)struct vio_dev *vdev =to_vio_dev (vhost->dev);struct ibmvfc_queue *crq = &vhost->crq; +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs);ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs);ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs);
@@ -1044,8 +1055,17 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)spin_unlock (vhost->crq.q_lock);spin_unlock_irqrestore (&vhost->host->host_lock, flags); -ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs); -ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs); +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->do_enquiry =0 ; + vhost->mq_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }return rc; }
@@ -1586,9 +1606,11 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)if (vhost->mq_enabled || vhost->using_channels) { login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_CHANNELS ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_USE_ASYNC_SUBQ ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_HANDLE_FPIN ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_SCSI );if (vhost->nvme_enabled) { login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_NVMEOF ); - login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_SCSI ); login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_WWPN_ALL ); } }
@@ -5907,6 +5929,7 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt) {struct ibmvfc_host *vhost = evt->vhost;struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf; +struct ibmvfc_channels *async = &vhost->async_scrqs;struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;u32 mad_status =be16_to_cpu (evt->xfer_iu->channel_setup.common.status);
@@ -5920,8 +5943,11 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)ibmvfc_dbg (vhost,"Channel Setup succeeded\n" ); flags =be32_to_cpu (setup->flags); vhost->do_enquiry =0 ; - scsi->active_queues =be32_to_cpu (setup->num_scsi_subq_channels); - nvme->active_queues =be32_to_cpu (setup->num_nvme_subq_channels); + scsi->active_queues =min (be32_to_cpu (setup->num_scsi_subq_channels), + scsi->max_queues); + nvme->active_queues =min (be32_to_cpu (setup->num_nvme_subq_channels), + nvme->max_queues); + async->active_queues = (async->scrqs && setup->async_sub_crq_handle) ?1 :0 ;if (flags &IBMVFC_CHANNELS_CANCELED ) {ibmvfc_dbg (vhost,"Channels Canceled\n" );
@@ -5937,6 +5963,8 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)for (i =0 ; i < nvme->active_queues; i++) nvme->scrqs[i].vios_cookie =be64_to_cpu (setup->channel_handles[scsi->active_queues + i]); +for (i =0 ; i < async->active_queues; i++) + async->scrqs[i].vios_cookie =be64_to_cpu (setup->async_sub_crq_handle);ibmvfc_dbg (vhost,"Using %u SCSI channels\n" , scsi->active_queues);
@@ -5971,6 +5999,7 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)struct ibmvfc_event *evt =ibmvfc_get_reserved_event (&vhost->crq);struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;struct ibmvfc_channels *nvme = &vhost->nvme_scrqs; +struct ibmvfc_channels *async = &vhost->async_scrqs;unsigned int scsi_channels =min (scsi->desired_queues, vhost->max_vios_scsi_channels); unsigned int nvme_channels =
@@ -5998,6 +6027,9 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)cpu_to_be64 (nvme->scrqs[i].cookie); } +if (async->scrqs) + setup_buf->async_sub_crq_handle =cpu_to_be64 (async->scrqs[0 ].cookie); +ibmvfc_init_event (evt, ibmvfc_channel_setup_done,IBMVFC_MAD_FORMAT ); mad = &evt->iu.channel_setup;memset (mad,0 ,sizeof (*mad));
@@ -6843,6 +6875,9 @@ static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost, queue->evt_depth = scsi_qdepth; queue->reserved_depth =IBMVFC_NUM_INTERNAL_SUBQ_REQ ;break ; +case IBMVFC_ASYNC_SUB_CRQ_FMT : + fmt_size =sizeof (*queue->msgs.async_scrq); +break ;default :dev_warn (dev,"Unknown command/response queue message format: %d\n" , fmt);return -EINVAL ;
@@ -7084,25 +7119,41 @@ VISIBLE_IF_KUNIT void ibmvfc_deregister_channel(struct ibmvfc_host *vhost, }EXPORT_SYMBOL_IF_KUNIT (ibmvfc_deregister_channel); -static void ibmvfc_reg_sub_crqs (struct ibmvfc_host *vhost, +/** +* ibmvfc_reg_sub_crqs - Register all queues in a channel set with the hypervisor +* @vhost: ibmvfc host struct +* @channels: channel set to register +* +* Iterates over channels->max_queues entries and calls ibmvfc_register_channel() +* for each. If any registration fails, previously registered queues in this +* channel set are deregistered and -1 is returned. The caller is responsible +* for clearing vhost->mq_enabled and vhost->do_enquiry and for deregistering +* any previously registered channel sets. Returns 0 immediately (without +* iterating) if mq_enabled is 0 or channels->scrqs is NULL. +* +* Return: +* * %0 - success, or multi-queue disabled / channels not allocated +* * %-1 - channel registration failed +*/ +static int ibmvfc_reg_sub_crqs (struct ibmvfc_host *vhost,struct ibmvfc_channels *channels) {int i, j;ENTER ;if (!vhost->mq_enabled || !channels->scrqs) -return ; +return 0 ;for (i =0 ; i < channels->max_queues; i++) {if (ibmvfc_register_channel (vhost, channels, i)) {for (j = i; j >0 ; j--)ibmvfc_deregister_channel (vhost, channels, j -1 ); - vhost->do_enquiry =0 ; -return ; +return -1 ; } }LEAVE ; +return 0 ; } static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost,
@@ -7111,7 +7162,7 @@ static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost,int i;ENTER ; -if (!vhost->mq_enabled || !channels->scrqs) +if (!channels->scrqs)return ;for (i =0 ; i < channels->max_queues; i++)
@@ -7124,16 +7175,21 @@ static int ibmvfc_alloc_channels(struct ibmvfc_host *vhost, struct ibmvfc_channels *channels) {struct ibmvfc_queue *scrq; +enum ibmvfc_msg_fmt fmt;int i, j;int rc =0 ; channels->scrqs =kzalloc_objs (*channels->scrqs, channels->max_queues); -if (!channels->scrqs) +if (!channels->scrqs) { + channels->active_queues =0 ;return -ENOMEM ; + } + fmt = (channels->protocol !=IBMVFC_PROTO_ASYNC ) ?IBMVFC_SUB_CRQ_FMT : +IBMVFC_ASYNC_SUB_CRQ_FMT ;for (i =0 ; i < channels->max_queues; i++) { scrq = &channels->scrqs[i]; - rc =ibmvfc_alloc_queue (vhost, scrq,IBMVFC_SUB_CRQ_FMT ); + rc =ibmvfc_alloc_queue (vhost, scrq, fmt);if (rc) {for (j = i; j >0 ; j--) { scrq = &channels->scrqs[j -1 ];
@@ -7149,30 +7205,6 @@ static int ibmvfc_alloc_channels(struct ibmvfc_host *vhost,return rc; } -static void ibmvfc_init_sub_crqs (struct ibmvfc_host *vhost) -{ -ENTER ; -if (!vhost->mq_enabled) -return ; - -if (ibmvfc_alloc_channels (vhost, &vhost->scsi_scrqs)) { - vhost->do_enquiry =0 ; - vhost->mq_enabled =0 ; -return ; - } - -ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs); - -if (vhost->nvme_enabled) { -if (ibmvfc_alloc_channels (vhost, &vhost->nvme_scrqs)) - vhost->nvme_enabled =0 ; -else -ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs); - } - -LEAVE ; -} -static void ibmvfc_release_channels (struct ibmvfc_host *vhost,struct ibmvfc_channels *channels) {
@@ -7191,9 +7223,74 @@ static void ibmvfc_release_channels(struct ibmvfc_host *vhost, } } +/** +* ibmvfc_init_sub_crqs - Allocate and register all sub-CRQ channel sets +* @vhost: ibmvfc host struct +* +* Allocates and registers the async, SCSI, and (if nvme_enabled) NVMe +* sub-CRQ channel sets in order. Returns immediately if mq_enabled is 0. +* +* On failure at any stage, previously registered and allocated channel sets +* are deregistered and freed in reverse order, and vhost->mq_enabled and +* vhost->do_enquiry are cleared under host_lock to prevent the host state +* machine from stalling in IBMVFC_HOST_ACTION_INIT_WAIT. +*/ +static void ibmvfc_init_sub_crqs (struct ibmvfc_host *vhost) +{ +unsigned long flags; + +ENTER ; +if (!vhost->mq_enabled) +return ; + +/* register async_sub_crq channel */ +if (ibmvfc_alloc_channels (vhost, &vhost->async_scrqs)) + goto out; + +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs)) + goto release_async_sub_crq; + +if (ibmvfc_alloc_channels (vhost, &vhost->scsi_scrqs)) + goto deregister_async_sub_crq; + +if (ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs)) + goto release_scsi_sub_crqs; + +if (vhost->nvme_enabled) { +if (ibmvfc_alloc_channels (vhost, &vhost->nvme_scrqs)) { +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->nvme_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }else if (ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_release_channels (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->nvme_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + } + } + +LEAVE ; +return ; + +release_scsi_sub_crqs: +ibmvfc_release_channels (vhost, &vhost->scsi_scrqs); +deregister_async_sub_crq: +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs); +release_async_sub_crq: +ibmvfc_release_channels (vhost, &vhost->async_scrqs); +out: +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->do_enquiry =0 ; + vhost->mq_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); +} +static void ibmvfc_release_sub_crqs (struct ibmvfc_host *vhost) {ENTER ; +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs); +ibmvfc_release_channels (vhost, &vhost->async_scrqs); +if (!vhost->scsi_scrqs.scrqs)return ;
@@ -7459,6 +7556,9 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) vhost->task_set =1 ; vhost->mq_enabled = mq_enabled; + vhost->async_scrqs.desired_queues =1 ; + vhost->async_scrqs.max_queues =1 ; + vhost->async_scrqs.protocol =IBMVFC_PROTO_ASYNC ; vhost->scsi_scrqs.desired_queues =min (shost->nr_hw_queues, nr_scsi_channels); vhost->scsi_scrqs.max_queues = shost->nr_hw_queues; vhost->scsi_scrqs.protocol =IBMVFC_PROTO_SCSI ;
Signature · 1 line
2.56.0
[email protected]
AI reviewRe: [PATCH v18 12/13] scsi: ibmvfc: register and use asynchronous sub CRQ for events
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [Critical] scsi: ibmvfc: uninitialized tgt deref in ibmvfc_process_async_work() - [High] scsi: ibmvfc…
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] scsi: ibmvfc: uninitialized tgt deref in ibmvfc_process_async_work()
- [High] scsi: ibmvfc: NULL crq deref in ibmvfc_process_async_work()
- [High] scsi: ibmvfc: NULL dereference of nvme->scrqs in channel setup
- [Medium] scsi: ibmvfc: stale active_queues after ibmvfc_dereg_sub_crqs() --
commit c8e05d927003244a19b25c7ef71f73f56b0341c8
Author: Dave Marquardt <[email protected]>
scsi: ibmvfc: register and use asynchronous sub CRQ for events
This commit wires the asynchronous sub-CRQ into the ibmvfc sub-CRQ lifecycle so it is allocated, registered, configured during channel setup, and freed alongside the SCSI and NVMe channel queues, while advertising IBMVFC_USE_ASYNC_SUBQ and IBMVFC_CAN_HANDLE_FPIN capabilities during login.
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0
[ ... ]
Quoted 20 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +11
-2@@ -988,8 +989,17 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)spin_unlock (vhost->crq.q_lock);spin_unlock_irqrestore (&vhost->host->host_lock, flags); -ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs); -ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs); +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->do_enquiry =0 ; + vhost->mq_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }return rc; }
[ ... ]
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +4
-2@@ -1044,8 +1055,17 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)spin_unlock (vhost->crq.q_lock);spin_unlock_irqrestore (&vhost->host->host_lock, flags); -ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs); -ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs); +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs);
drivers/scsi/ibmvscsi/ibmvfc-core.c static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) · line 1071
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +7
-0+ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->do_enquiry =0 ; + vhost->mq_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }return rc; }
[Severity: Medium]
When ibmvfc_reg_sub_crqs() fails in ibmvfc_reenable_crq_queue() or ibmvfc_reset_crq(), all three channel sets are deregistered via ibmvfc_dereg_sub_crqs() and vhost->do_enquiry and vhost->mq_enabled are cleared to fall back to single-queue mode, but channels->active_queues is never reset to 0:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_dereg_sub_crqs() {
if (!channels->scrqs)
return;
for (i = 0; i < channels->max_queues; i++)
ibmvfc_deregister_channel(vhost, channels, i);
}Because clearing vhost->mq_enabled and vhost->do_enquiry causes subsequent NPIV login to skip ibmvfc_channel_setup_done(), active_queues remains non-zero on the deregistered channel sets.
In drivers/scsi/ibmvscsi/ibmvfc-nvme.c, ibmvfc_nvme_create_queue() checks only active_queues:
if (!vhost->nvme_scrqs.active_queues) return -ENODEV;
Won't leaving active_queues non-zero after deregistering the sub-CRQs cause ibmvfc_nvme_create_queue() to bind NVMe queues to deregistered nvme_scrqs entries that no longer have hypervisor registrations or IRQ handlers?
drivers/scsi/ibmvscsi/ibmvfc-core.c static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) · line 1616
Quoted 13 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +3
-1@@ -1586,9 +1606,11 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)if (vhost->mq_enabled || vhost->using_channels) { login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_CHANNELS ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_USE_ASYNC_SUBQ ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_HANDLE_FPIN ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_SCSI );if (vhost->nvme_enabled) { login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_NVMEOF ); - login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_SCSI ); login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_WWPN_ALL ); } }
[Severity: Critical]
Now that ibmvfc_set_login_info() advertises IBMVFC_USE_ASYNC_SUBQ and IBMVFC_CAN_HANDLE_FPIN, the VIOS can deliver FPIN events that are queued to ibmvfc_process_async_work() via ibmvfc_interrupt_async_subq()->ibmvfc_drain_async_subq()->ibmvfc_handle_async().
In ibmvfc_process_async_work(), there appear to be two regressions when an FPIN event arrives with all-zero target identifiers (!scsi_id && !wwpn && !node_name && !assoc_id), such as a host-scope IBMVFC_AE_FPIN_LINK_CONGESTED or IBMVFC_AE_FPIN_CONGESTION_CLEARED event:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_process_async_work() {
struct ibmvfc_async_sub_crq *subq = NULL;
struct ibmvfc_async_work *aw;
struct ibmvfc_async_crq *crq = NULL;
struct ibmvfc_target *tgt;
...
if (aw->event.type == IBMVFC_ASYNC_CRQ_SUB) {
subq = &aw->event.subq;
...
} else {
crq = &aw->event.async_crq;
...
}
if (!scsi_id && !wwpn && !node_name && !assoc_id) {
if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED ||
fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED)
fpin = ibmvfc_basic_fpin_to_desc(crq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
goto send_fpin;
}
...
send_fpin:
...
if (tgt)
kref_put(&tgt->kref, ibmvfc_release_tgt);
}First, tgt is declared without being initialized to NULL, and the (!scsi_id && !wwpn && !node_name && !assoc_id) branch jumps directly to send_fpin before tgt is assigned by ibmvfc_find_target(), so kref_put() dereferences an uninitialized stack pointer.
Second, when aw->event.type == IBMVFC_ASYNC_CRQ_SUB, crq is NULL, but that same branch unconditionally calls ibmvfc_basic_fpin_to_desc(crq, ...) instead of checking crq vs subq and calling ibmvfc_full_fpin_to_desc(subq, ...), which immediately dereferences crq->fpin_status in ibmvfc_basic_fpin_to_desc().
Can tgt be initialized to NULL and this branch dispatch between ibmvfc_basic_fpin_to_desc() and ibmvfc_full_fpin_to_desc() like the other call sites in ibmvfc_process_async_work()?
Quoted 49 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +12
-2@@ -5907,6 +5929,7 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt) {struct ibmvfc_host *vhost = evt->vhost;struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf; +struct ibmvfc_channels *async = &vhost->async_scrqs;struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;u32 mad_status =be16_to_cpu (evt->xfer_iu->channel_setup.common.status);@@ -5920,8 +5943,11 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)ibmvfc_dbg (vhost,"Channel Setup succeeded\n" ); flags =be32_to_cpu (setup->flags); vhost->do_enquiry =0 ; - scsi->active_queues =be32_to_cpu (setup->num_scsi_subq_channels); - nvme->active_queues =be32_to_cpu (setup->num_nvme_subq_channels); + scsi->active_queues =min (be32_to_cpu (setup->num_scsi_subq_channels), + scsi->max_queues); + nvme->active_queues =min (be32_to_cpu (setup->num_nvme_subq_channels), + nvme->max_queues); + async->active_queues = (async->scrqs && setup->async_sub_crq_handle) ?1 :0 ;if (flags &IBMVFC_CHANNELS_CANCELED ) {ibmvfc_dbg (vhost,"Channels Canceled\n" );@@ -5937,6 +5963,8 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)for (i =0 ; i < nvme->active_queues; i++) nvme->scrqs[i].vios_cookie =be64_to_cpu (setup->channel_handles[scsi->active_queues + i]); +for (i =0 ; i < async->active_queues; i++) + async->scrqs[i].vios_cookie =be64_to_cpu (setup->async_sub_crq_handle);ibmvfc_dbg (vhost,"Using %u SCSI channels\n" , scsi->active_queues);@@ -5971,6 +5999,7 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)struct ibmvfc_event *evt =ibmvfc_get_reserved_event (&vhost->crq);struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;struct ibmvfc_channels *nvme = &vhost->nvme_scrqs; +struct ibmvfc_channels *async = &vhost->async_scrqs;unsigned int scsi_channels =min (scsi->desired_queues, vhost->max_vios_scsi_channels); unsigned int nvme_channels =@@ -5998,6 +6027,9 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)cpu_to_be64 (nvme->scrqs[i].cookie); } +if (async->scrqs) + setup_buf->async_sub_crq_handle =cpu_to_be64 (async->scrqs[0 ].cookie); +ibmvfc_init_event (evt, ibmvfc_channel_setup_done,IBMVFC_MAD_FORMAT ); mad = &evt->iu.channel_setup;memset (mad,0 ,sizeof (*mad));
[ ... ]
Quoted 39 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +35
-0@@ -7191,9 +7223,74 @@ static void ibmvfc_release_channels(struct ibmvfc_host *vhost, } } +/** +* ibmvfc_init_sub_crqs - Allocate and register all sub-CRQ channel sets +* @vhost: ibmvfc host struct +* +* Allocates and registers the async, SCSI, and (if nvme_enabled) NVMe +* sub-CRQ channel sets in order. Returns immediately if mq_enabled is 0. +* +* On failure at any stage, previously registered and allocated channel sets +* are deregistered and freed in reverse order, and vhost->mq_enabled and +* vhost->do_enquiry are cleared under host_lock to prevent the host state +* machine from stalling in IBMVFC_HOST_ACTION_INIT_WAIT. +*/ +static void ibmvfc_init_sub_crqs (struct ibmvfc_host *vhost) +{ +unsigned long flags; + +ENTER ; +if (!vhost->mq_enabled) +return ; + +/* register async_sub_crq channel */ +if (ibmvfc_alloc_channels (vhost, &vhost->async_scrqs)) + goto out; + +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs)) + goto release_async_sub_crq; + +if (ibmvfc_alloc_channels (vhost, &vhost->scsi_scrqs)) + goto deregister_async_sub_crq; + +if (ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs)) + goto release_scsi_sub_crqs; + +if (vhost->nvme_enabled) { +if (ibmvfc_alloc_channels (vhost, &vhost->nvme_scrqs)) {
drivers/scsi/ibmvscsi/ibmvfc-core.c static void ibmvfc_release_channels(struct ibmvfc_host *vhost, · line 7270
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->nvme_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }else if (ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_release_channels (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->nvme_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + } + }
[Severity: High]
In ibmvfc_probe(), vhost->nvme_scrqs.desired_queues and vhost->nvme_scrqs.max_queues are initialized to non-zero values even when vhost->nvme_enabled is 0:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_probe() {
vhost->nvme_enabled = mq_enabled ? nvme_enabled : 0;
vhost->nvme_scrqs.desired_queues = min(max_nvme_queues, nr_nvme_channels);
vhost->nvme_scrqs.max_queues = max_nvme_queues;
}When vhost->nvme_enabled is 0, or when allocating or registering vhost->nvme_scrqs fails in ibmvfc_init_sub_crqs() after async_scrqs and scsi_scrqs succeed, vhost->nvme_scrqs.scrqs remains NULL while vhost->mq_enabled and vhost->do_enquiry remain 1 and desired_queues and max_queues remain non-zero.
During channel enquiry and setup (ibmvfc_channel_enquiry_done()->ibmvfc_channel_setup()), if the VIOS reports num_nvme_subq_channels > 0, ibmvfc_channel_setup() computes a non-zero nvme_channels without checking vhost->nvme_enabled or nvme->scrqs:
for (i = 0; i < nvme_channels; i++) setup_buf->channel_handles[scsi_channels + i] = cpu_to_be64(nvme->scrqs[i].cookie);
Similarly, in ibmvfc_channel_setup_done(), nvme->active_queues is clamped to nvme->max_queues without checking whether nvme->scrqs is non-NULL, which then dereferences nvme->scrqs[i].vios_cookie in the loop.
Should ibmvfc_channel_setup() and ibmvfc_channel_setup_done() guard the nvme->scrqs accesses (or zero desired_queues and max_queues when nvme_enabled is 0) in the same way async->scrqs is guarded?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=12
Re: [PATCH v18 12/13] scsi: ibmvfc: register and use asynchronous sub CRQ for events
Quoted 73 lines · [email protected] writes:
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0[ ... ]
Quoted 20 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +11
-2@@ -988,8 +989,17 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)spin_unlock (vhost->crq.q_lock);spin_unlock_irqrestore (&vhost->host->host_lock, flags); -ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs); -ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs); +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->do_enquiry =0 ; + vhost->mq_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }return rc; }[ ... ]
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +4
-2@@ -1044,8 +1055,17 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)spin_unlock (vhost->crq.q_lock);spin_unlock_irqrestore (&vhost->host->host_lock, flags); -ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs); -ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs); +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs) || +ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_dereg_sub_crqs (vhost, &vhost->async_scrqs);drivers/scsi/ibmvscsi/ibmvfc-core.c static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) · line 1071
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +7
-0+ibmvfc_dereg_sub_crqs (vhost, &vhost->scsi_scrqs); +ibmvfc_dereg_sub_crqs (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->do_enquiry =0 ; + vhost->mq_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }return rc; }[Severity: Medium]
Whenibmvfc_reg_sub_crqs()fails inibmvfc_reenable_crq_queue()oribmvfc_reset_crq(), all three channel sets are deregistered viaibmvfc_dereg_sub_crqs()andvhost->do_enquiryandvhost->mq_enabledare cleared to fall back to single-queue mode, butchannels->active_queuesis never reset to 0:drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_dereg_sub_crqs() { if (!channels->scrqs) return; for (i = 0; i < channels->max_queues; i++) ibmvfc_deregister_channel(vhost, channels, i); }Because clearing
vhost->mq_enabledandvhost->do_enquirycauses subsequent NPIV login to skipibmvfc_channel_setup_done(),active_queuesremains non-zero on the deregistered channel sets.In
drivers/scsi/ibmvscsi/ibmvfc-nvme.c,ibmvfc_nvme_create_queue()checks onlyactive_queues:
Quoted 6 lines
if (!vhost->nvme_scrqs.active_queues) return -ENODEV;Won't leaving
active_queuesnon-zero after deregistering the sub-CRQs causeibmvfc_nvme_create_queue()to bind NVMe queues to deregisterednvme_scrqsentries that no longer have hypervisor registrations or IRQ handlers?
This will be fixed in v19.
Quoted 64 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) · line 1616
Quoted 13 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +3
-1@@ -1586,9 +1606,11 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)if (vhost->mq_enabled || vhost->using_channels) { login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_CHANNELS ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_USE_ASYNC_SUBQ ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_HANDLE_FPIN ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_SCSI );if (vhost->nvme_enabled) { login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_NVMEOF ); - login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_SCSI ); login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_WWPN_ALL ); } }[Severity: Critical]
Now thatibmvfc_set_login_info()advertisesIBMVFC_USE_ASYNC_SUBQandIBMVFC_CAN_HANDLE_FPIN, the VIOS can deliver FPIN events that are queued toibmvfc_process_async_work()via ibmvfc_interrupt_async_subq()->ibmvfc_drain_async_subq()->ibmvfc_handle_async().In
ibmvfc_process_async_work(), there appear to be two regressions when an FPIN event arrives with all-zero target identifiers (!scsi_id && !wwpn && !node_name && !assoc_id), such as a host-scopeIBMVFC_AE_FPIN_LINK_CONGESTEDorIBMVFC_AE_FPIN_CONGESTION_CLEAREDevent:drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_process_async_work() { struct ibmvfc_async_sub_crq *subq = NULL; struct ibmvfc_async_work *aw; struct ibmvfc_async_crq *crq = NULL; struct ibmvfc_target *tgt; ... if (aw->event.type == IBMVFC_ASYNC_CRQ_SUB) { subq = &aw->event.subq; ... } else { crq = &aw->event.async_crq; ... } if (!scsi_id && !wwpn && !node_name && !assoc_id) { if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED || fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED) fpin = ibmvfc_basic_fpin_to_desc(crq, cpu_to_be64(fc_host_port_name(vhost->host)), 0); goto send_fpin; } ... send_fpin: ... if (tgt) kref_put(&tgt->kref, ibmvfc_release_tgt); }First, tgt is declared without being initialized to NULL, and the (!scsi_id && !wwpn && !node_name && !assoc_id) branch jumps directly to
send_fpinbefore tgt is assigned byibmvfc_find_target(), sokref_put()dereferences an uninitialized stack pointer.Second, when
aw->event.type==IBMVFC_ASYNC_CRQ_SUB, crq is NULL, but that same branch unconditionally callsibmvfc_basic_fpin_to_desc(crq, ...)instead of checking crq vs subq and callingibmvfc_full_fpin_to_desc(subq, ...), which immediately dereferencescrq->fpin_statusinibmvfc_basic_fpin_to_desc().
Quoted 3 lines
Can tgt be initialized to NULL and this branch dispatch between
ibmvfc_basic_fpin_to_desc()andibmvfc_full_fpin_to_desc()like the other call sites inibmvfc_process_async_work()?
This will be fixed in patch 6 in v19.
Quoted 128 lines
Quoted 49 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +12
-2@@ -5907,6 +5929,7 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt) {struct ibmvfc_host *vhost = evt->vhost;struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf; +struct ibmvfc_channels *async = &vhost->async_scrqs;struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;u32 mad_status =be16_to_cpu (evt->xfer_iu->channel_setup.common.status);@@ -5920,8 +5943,11 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)ibmvfc_dbg (vhost,"Channel Setup succeeded\n" ); flags =be32_to_cpu (setup->flags); vhost->do_enquiry =0 ; - scsi->active_queues =be32_to_cpu (setup->num_scsi_subq_channels); - nvme->active_queues =be32_to_cpu (setup->num_nvme_subq_channels); + scsi->active_queues =min (be32_to_cpu (setup->num_scsi_subq_channels), + scsi->max_queues); + nvme->active_queues =min (be32_to_cpu (setup->num_nvme_subq_channels), + nvme->max_queues); + async->active_queues = (async->scrqs && setup->async_sub_crq_handle) ?1 :0 ;if (flags &IBMVFC_CHANNELS_CANCELED ) {ibmvfc_dbg (vhost,"Channels Canceled\n" );@@ -5937,6 +5963,8 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)for (i =0 ; i < nvme->active_queues; i++) nvme->scrqs[i].vios_cookie =be64_to_cpu (setup->channel_handles[scsi->active_queues + i]); +for (i =0 ; i < async->active_queues; i++) + async->scrqs[i].vios_cookie =be64_to_cpu (setup->async_sub_crq_handle);ibmvfc_dbg (vhost,"Using %u SCSI channels\n" , scsi->active_queues);@@ -5971,6 +5999,7 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)struct ibmvfc_event *evt =ibmvfc_get_reserved_event (&vhost->crq);struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;struct ibmvfc_channels *nvme = &vhost->nvme_scrqs; +struct ibmvfc_channels *async = &vhost->async_scrqs;unsigned int scsi_channels =min (scsi->desired_queues, vhost->max_vios_scsi_channels); unsigned int nvme_channels =@@ -5998,6 +6027,9 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)cpu_to_be64 (nvme->scrqs[i].cookie); } +if (async->scrqs) + setup_buf->async_sub_crq_handle =cpu_to_be64 (async->scrqs[0 ].cookie); +ibmvfc_init_event (evt, ibmvfc_channel_setup_done,IBMVFC_MAD_FORMAT ); mad = &evt->iu.channel_setup;memset (mad,0 ,sizeof (*mad));[ ... ]
Quoted 39 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +35
-0@@ -7191,9 +7223,74 @@ static void ibmvfc_release_channels(struct ibmvfc_host *vhost, } } +/** +* ibmvfc_init_sub_crqs - Allocate and register all sub-CRQ channel sets +* @vhost: ibmvfc host struct +* +* Allocates and registers the async, SCSI, and (if nvme_enabled) NVMe +* sub-CRQ channel sets in order. Returns immediately if mq_enabled is 0. +* +* On failure at any stage, previously registered and allocated channel sets +* are deregistered and freed in reverse order, and vhost->mq_enabled and +* vhost->do_enquiry are cleared under host_lock to prevent the host state +* machine from stalling in IBMVFC_HOST_ACTION_INIT_WAIT. +*/ +static void ibmvfc_init_sub_crqs (struct ibmvfc_host *vhost) +{ +unsigned long flags; + +ENTER ; +if (!vhost->mq_enabled) +return ; + +/* register async_sub_crq channel */ +if (ibmvfc_alloc_channels (vhost, &vhost->async_scrqs)) + goto out; + +if (ibmvfc_reg_sub_crqs (vhost, &vhost->async_scrqs)) + goto release_async_sub_crq; + +if (ibmvfc_alloc_channels (vhost, &vhost->scsi_scrqs)) + goto deregister_async_sub_crq; + +if (ibmvfc_reg_sub_crqs (vhost, &vhost->scsi_scrqs)) + goto release_scsi_sub_crqs; + +if (vhost->nvme_enabled) { +if (ibmvfc_alloc_channels (vhost, &vhost->nvme_scrqs)) {drivers/scsi/ibmvscsi/ibmvfc-core.c static void ibmvfc_release_channels(struct ibmvfc_host *vhost, · line 7270
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->nvme_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + }else if (ibmvfc_reg_sub_crqs (vhost, &vhost->nvme_scrqs)) { +ibmvfc_release_channels (vhost, &vhost->nvme_scrqs); +spin_lock_irqsave (&vhost->host->host_lock, flags); + vhost->nvme_enabled =0 ; +spin_unlock_irqrestore (&vhost->host->host_lock, flags); + } + }[Severity: High]
Inibmvfc_probe(),vhost->nvme_scrqs.desired_queuesandvhost->nvme_scrqs.max_queuesare initialized to non-zero values even whenvhost->nvme_enabledis 0:drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_probe() { vhost->nvme_enabled = mq_enabled ? nvme_enabled : 0; vhost->nvme_scrqs.desired_queues = min(max_nvme_queues, nr_nvme_channels); vhost->nvme_scrqs.max_queues = max_nvme_queues; }When
vhost->nvme_enabledis 0, or when allocating or registeringvhost->nvme_scrqsfails inibmvfc_init_sub_crqs()afterasync_scrqsandscsi_scrqssucceed,vhost->nvme_scrqs.scrqsremains NULL whilevhost->mq_enabledandvhost->do_enquiryremain 1 anddesired_queuesandmax_queuesremain non-zero.During channel enquiry and setup (ibmvfc_channel_enquiry_done()->ibmvfc_channel_setup()), if the VIOS reports
num_nvme_subq_channels> 0,ibmvfc_channel_setup()computes a non-zeronvme_channelswithout checkingvhost->nvme_enabledornvme->scrqs:for (i = 0; i < nvme_channels; i++) setup_buf->channel_handles[scsi_channels + i] = cpu_to_be64(nvme->scrqs[i].cookie);
Quoted 7 lines
Similarly, in
ibmvfc_channel_setup_done(),nvme->active_queuesis clamped tonvme->max_queueswithout checking whethernvme->scrqsis non-NULL, which then dereferences nvme->scrqs[i].vios_cookie in the loop.Should
ibmvfc_channel_setup()andibmvfc_channel_setup_done()guard thenvme->scrqsaccesses (or zerodesired_queuesandmax_queueswhennvme_enabledis 0) in the same wayasync->scrqsis guarded?
Yes, this will be fixed in v19.
-Dave
PATCH v18 13/13 scsi: ibmvfc: handle extended FPIN events
In reply to Dave MarquardtImplement support for extended FPIN messages received via the asynchronous sub-queue, completing full FPIN functionality.
Extended FPIN messages provide more detailed information about fabric events compared to basic FPIN messages, including specific event types, modifiers, thresholds, event counts, and severity.
Add ibmvfc_ext_fpin_to_desc() to convert extended FPIN messages from async sub-queue format to fc_els_fpin structures with complete descriptor information. Thread detecting_wwpn (fc_host_port_name()) through ibmvfc_ext_fpin_to_desc() to ibmvfc_common_fpin_to_desc() consistent with ibmvfc_basic_fpin_to_desc() and ibmvfc_full_fpin_to_desc(). Update ibmvfc_process_async_work() and ibmvfc_handle_async() to zero node_name and dispatch to ibmvfc_ext_fpin_to_desc() when the IBMVFC_ASYNC_IS_FPIN_EXT flag is set.
Extend ibmvfc_common_fpin_to_desc() with a severity parameter so that extended FPIN events can propagate the severity value from the VIOS when IBMVFC_FPIN_SEVERITY_VALID is set, rather than always defaulting to FPIN_CONGN_SEVERITY_WARNING. Severity is only carried by the congestion descriptor used for IBMVFC_AE_FPIN_LINK_CONGESTED and IBMVFC_AE_FPIN_CONGESTION_CLEARED; the peer congestion and link integrity descriptors have no severity field, so a severity supplied for the other FPIN statuses is ignored.
Set the IBMVFC_CAN_HANDLE_FPIN_EXT capability bit (0x800) during login to inform the VIOS that the client can process extended FPIN messages. Add the IBMVFC_CAN_CLS_CRQ_PRE_DMP capability bit (0x400) definition for the intervening protocol-defined capability. Also add the IBMVFC_SUPPORT_FPIN_EXT response capability bit definition used to gate extended FPIN processing.
In ibmvfc_process_async_work(), use goto to bypass the generic "FPIN event received, unable to process" error message when a more specific diagnostic has already been emitted for invalid or unsupported extended FPIN events.
Add a KUnit test, ibmvfc_extended_fpin_test(), that sends extended FPIN events through ibmvfc_process_async_work() for each FPIN status with the event types valid for that status, setting only IBMVFC_FPIN_EVENT_TYPE_VALID, and checks that the matching fc_host or rport FPIN statistic is incremented. The test is skipped if no ibmvfc device, online target rport, or IBMVFC_SUPPORT_FPIN_EXT capability is available. It does not exercise the modifier, threshold, severity or event count fields, nor the error paths for a missing IBMVFC_FPIN_EVENT_TYPE_VALID flag or an unadvertised IBMVFC_SUPPORT_FPIN_EXT capability.
--- drivers/scsi/ibmvscsi/ibmvfc-core.c | 92 +++++++++++++++++++--- drivers/scsi/ibmvscsi/ibmvfc.h | 31 ++++++++ drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 148 +++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 9 deletions(-)
drivers/scsi/ibmvscsi/ibmvfc-core.c +83 -9
@@ -1609,6 +1609,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) login_info->capabilities |=cpu_to_be64 (IBMVFC_USE_ASYNC_SUBQ ); login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_HANDLE_FPIN ); login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_SCSI ); + login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_HANDLE_FPIN_EXT );if (vhost->nvme_enabled) { login_info->capabilities |=cpu_to_be64 (IBMVFC_YES_NVMEOF ); login_info->capabilities |=cpu_to_be64 (IBMVFC_CAN_USE_WWPN_ALL );
@@ -3344,6 +3345,11 @@ static size_t ibmvfc_fpin_size_helper(u8 fpin_status)* IBMVFC_AE_FPIN_PORT_DEGRADED only * @event_count: link integrity event count (big-endian); used for * IBMVFC_AE_FPIN_PORT_DEGRADED only +* @severity: congestion severity value; stored in the congestion +* descriptor for IBMVFC_AE_FPIN_LINK_CONGESTED and +* IBMVFC_AE_FPIN_CONGESTION_CLEARED only. Ignored for +* other statuses because their descriptors have no +* severity field * * Return: * NULL - unable to allocate structure or unrecognised fpin_status
@@ -3352,7 +3358,7 @@ static size_t ibmvfc_fpin_size_helper(u8 fpin_status)static struct fc_els_fpin *ibmvfc_common_fpin_to_desc (u8 fpin_status,__be64 detecting_wwpn,__be64 wwpn,__be16 type,__be16 modifier, -__be32 threshold,__be32 event_count) +__be32 threshold,__be32 event_count,__u8 severity) {struct fc_fn_peer_congn_desc *pdesc;struct fc_fn_congn_desc *cdesc;
@@ -3380,7 +3386,7 @@ ibmvfc_common_fpin_to_desc(u8 fpin_status, __be64 detecting_wwpn, __be64 wwpn, cdesc->event_type = type; cdesc->event_modifier = modifier; cdesc->event_period =cpu_to_be32 (IBMVFC_FPIN_DEFAULT_EVENT_PERIOD ); - cdesc->severity =FPIN_CONGN_SEVERITY_WARNING ; + cdesc->severity = severity;break ;case IBMVFC_AE_FPIN_PORT_CONGESTED :case IBMVFC_AE_FPIN_PORT_CLEARED :
@@ -3444,6 +3450,7 @@ static struct fc_els_fpin * ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, __be64 detecting_wwpn, u64 wwpn) { + __u8 severity = FPIN_CONGN_SEVERITY_WARNING;__be16 type;switch (crq->fpin_status) {
@@ -3454,6 +3461,7 @@ ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, __be64 detecting_wwpn,case IBMVFC_AE_FPIN_PORT_CLEARED :case IBMVFC_AE_FPIN_CONGESTION_CLEARED : type =cpu_to_be16 (FPIN_CONGN_CLEAR ); + severity =0 ;break ;case IBMVFC_AE_FPIN_PORT_DEGRADED : type =cpu_to_be16 (FPIN_LI_UNKNOWN );
@@ -3465,7 +3473,7 @@ ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, __be64 detecting_wwpn,return ibmvfc_common_fpin_to_desc (crq->fpin_status, detecting_wwpn,cpu_to_be64 (wwpn), type,cpu_to_be16 (0 ),cpu_to_be32 (IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD ), -cpu_to_be32 (1 )); +cpu_to_be32 (1 ), severity); }/**
@@ -3486,6 +3494,7 @@ static struct fc_els_fpin * ibmvfc_full_fpin_to_desc(struct ibmvfc_async_sub_crq *ibmvfc_fpin, __be64 detecting_wwpn, u64 wwpn) { + __u8 severity = FPIN_CONGN_SEVERITY_WARNING;__be16 type;switch (ibmvfc_fpin->fpin_status) {
@@ -3496,6 +3505,7 @@ ibmvfc_full_fpin_to_desc(struct ibmvfc_async_sub_crq *ibmvfc_fpin,case IBMVFC_AE_FPIN_PORT_CLEARED :case IBMVFC_AE_FPIN_CONGESTION_CLEARED : type =cpu_to_be16 (FPIN_CONGN_CLEAR ); + severity =0 ;break ;case IBMVFC_AE_FPIN_PORT_DEGRADED : type =cpu_to_be16 (FPIN_LI_UNKNOWN );
@@ -3507,7 +3517,48 @@ ibmvfc_full_fpin_to_desc(struct ibmvfc_async_sub_crq *ibmvfc_fpin,return ibmvfc_common_fpin_to_desc (ibmvfc_fpin->fpin_status, detecting_wwpn,cpu_to_be64 (wwpn), type,cpu_to_be16 (0 ),cpu_to_be32 (IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD ), -cpu_to_be32 (1 )); +cpu_to_be32 (1 ), severity); +} + +/** +* ibmvfc_ext_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct +* containing a descriptor. +* @ibmvfc_fpin: Pointer to async subq FPIN data +* @detecting_wwpn: WWPN of the local port that detected the event (big-endian) +* @wwpn: attached port WWPN (host-endian) +* +* Allocate a struct fc_els_fpin containing a descriptor and populate +* based on data from *ibmvfc_fpin. The caller must have verified that +* IBMVFC_FPIN_EVENT_TYPE_VALID is set in the FPIN data flags. +* +* Return: +* NULL - unable to allocate structure +* non-NULL - pointer to populated struct fc_els_fpin +*/ +static struct fc_els_fpin * +ibmvfc_ext_fpin_to_desc (struct ibmvfc_async_subq_fpin *ibmvfc_fpin, +__be64 detecting_wwpn,u64 wwpn) +{ +u8 flags = ibmvfc_fpin->fpin_data.flags; +__be32 threshold =cpu_to_be32 (IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD ); +__be16 modifier =0 ; +__be32 count =cpu_to_be32 (1 ); +__be16 type = ibmvfc_fpin->fpin_data.event_type; +__u8 severity =FPIN_CONGN_SEVERITY_WARNING ; + +if (flags &IBMVFC_FPIN_MODIFIER_VALID ) + modifier = ibmvfc_fpin->fpin_data.event_type_modifier; +if (flags &IBMVFC_FPIN_THRESHOLD_VALID ) + threshold = ibmvfc_fpin->fpin_data.event_threshold; +if (flags &IBMVFC_FPIN_EVENT_COUNT_VALID ) + count = ibmvfc_fpin->fpin_data.event_data.event_count; +if (flags &IBMVFC_FPIN_SEVERITY_VALID ) + severity = ibmvfc_fpin->fpin_data.event_data.severity; + +return ibmvfc_common_fpin_to_desc (ibmvfc_fpin->fpin_status, detecting_wwpn, +cpu_to_be64 (wwpn), type, + modifier, threshold, count, + severity); }/**
@@ -3544,6 +3595,11 @@ static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list,return NULL ; } +static_assert (sizeof (struct ibmvfc_async_subq_fpin ) == +sizeof (struct ibmvfc_async_sub_crq )); +static_assert (offsetof(struct ibmvfc_async_subq_fpin , fpin_data) == + offsetof(struct ibmvfc_async_sub_crq , nport_id)); +/** * ibmvfc_process_async_work - Process IBMVFC_AE_FPIN async CRQ from work queue * @work: pointer to work_struct
@@ -3551,6 +3607,7 @@ static struct ibmvfc_target *ibmvfc_find_target(struct list_head *target_list, VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) {struct ibmvfc_async_sub_crq *subq =NULL ; +struct ibmvfc_async_subq_fpin *sqfpin;struct ibmvfc_async_work *aw;struct ibmvfc_async_crq *crq =NULL ;struct ibmvfc_target *tgt;
@@ -3624,14 +3681,30 @@ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) goto free; } -if (crq) +if (crq) { fpin =ibmvfc_basic_fpin_to_desc (crq,cpu_to_be64 (fc_host_port_name (vhost->host)), tgt->wwpn); -else - fpin =ibmvfc_full_fpin_to_desc (subq, -cpu_to_be64 (fc_host_port_name (vhost->host)), - tgt->wwpn); + }else { + sqfpin = (struct ibmvfc_async_subq_fpin *)subq; +if ((subq->flags &IBMVFC_ASYNC_IS_FPIN_EXT ) ==0 ) { + fpin =ibmvfc_full_fpin_to_desc (subq, +cpu_to_be64 (fc_host_port_name (vhost->host)), + tgt->wwpn); + }else if (!(sqfpin->fpin_data.flags &IBMVFC_FPIN_EVENT_TYPE_VALID )) { +dev_err_ratelimited (vhost->dev, +"Invalid extended FPIN event received\n" ); + goto skip_generic_error; + }else if (!ibmvfc_check_caps (vhost,IBMVFC_SUPPORT_FPIN_EXT )) { +dev_err_ratelimited (vhost->dev, +"Unexpected extended FPIN event received\n" ); + goto skip_generic_error; + }else { + fpin =ibmvfc_ext_fpin_to_desc (sqfpin, +cpu_to_be64 (fc_host_port_name (vhost->host)), + tgt->wwpn); + } + } send_fpin:if (fpin) {
@@ -3642,6 +3715,7 @@ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) }else dev_err_ratelimited (vhost->dev,"FPIN event received, unable to process\n" ); + skip_generic_error:if (tgt)kref_put (&tgt->kref, ibmvfc_release_tgt); free:
drivers/scsi/ibmvscsi/ibmvfc.h +31 -0
@@ -210,6 +210,8 @@ struct ibmvfc_npiv_login {#define IBMVFC_CAN_USE_WWPN_ALL 0x080#define IBMVFC_USE_ASYNC_SUBQ 0x100#define IBMVFC_CAN_USE_NOOP_CMD 0x200 +#define IBMVFC_CAN_CLS_CRQ_PRE_DMP 0x400 +#define IBMVFC_CAN_HANDLE_FPIN_EXT 0x800__be64 node_name;struct srp_direct_buf async;u8 partition_name[IBMVFC_MAX_NAME ];
@@ -261,6 +263,7 @@ struct ibmvfc_npiv_login_resp {#define IBMVFC_SUPPORT_WWPN_ALL 0x0400#define IBMVFC_ASYNC_SUBQ 0x0800#define IBMVFC_SUPPORT_NOOP_CMD 0x1000 +#define IBMVFC_SUPPORT_FPIN_EXT 0x2000__be32 max_cmds;__be32 scsi_id_sz;__be64 max_dma_len;
@@ -786,6 +789,34 @@ struct ibmvfc_async_sub_crq { } id; } __packed__aligned(8) ; +struct ibmvfc_fpin_data { +#define IBMVFC_FPIN_EVENT_TYPE_VALID 0x01 +#define IBMVFC_FPIN_MODIFIER_VALID 0x02 +#define IBMVFC_FPIN_THRESHOLD_VALID 0x04 +#define IBMVFC_FPIN_SEVERITY_VALID 0x08 +#define IBMVFC_FPIN_EVENT_COUNT_VALID 0x10 +u8 flags; +u8 reserved[3 ]; +__be16 event_type; +__be16 event_type_modifier; +__be32 event_threshold; +union { +u8 severity; +__be32 event_count; + } event_data; +} __packed__aligned(8) ; + +struct ibmvfc_async_subq_fpin { +u8 valid; +u8 flags; +u8 link_state; +u8 fpin_status; +__be16 event; +__be16 pad; +__be64 wwpn; +struct ibmvfc_fpin_data fpin_data; +} __packed__aligned(8) ; +enum ibmvfc_async_crq_type {IBMVFC_ASYNC_CRQ_MAIN =0 ,IBMVFC_ASYNC_CRQ_SUB ,
drivers/scsi/ibmvscsi/ibmvfc_kunit.c +148 -0
163 lines not on this page · raw message
Signature · 1 line
2.56.0
[email protected]
AI reviewRe: [PATCH v18 13/13] scsi: ibmvfc: handle extended FPIN events
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [Critical] scsi: ibmvfc: uninitialized tgt pointer passed to kref_put() on FPIN path - [High] scsi…
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] scsi: ibmvfc: uninitialized tgt pointer passed to kref_put() on FPIN path
- [High] scsi: ibmvfc: NULL crq deref and missed ext FPIN on host-scope paths
- [Medium] scsi: ibmvfc: wrong default severity in ibmvfc_ext_fpin_to_desc()
- [Low] scsi: ibmvfc: commit message mismatch with changes in earlier patch --
commit a2aeb896595835b42eba9922a25c766496e63499
Author: Dave Marquardt <[email protected]>
scsi: ibmvfc: handle extended FPIN events
This commit implements support for extended FPIN messages received via the asynchronous sub-queue by adding ibmvfc_ext_fpin_to_desc(), extending ibmvfc_common_fpin_to_desc() with a severity parameter, negotiating IBMVFC_CAN_HANDLE_FPIN_EXT / IBMVFC_SUPPORT_FPIN_EXT capabilities, and adding a KUnit test.
Quoted 50 lines
scsi: ibmvfc: handle extended FPIN events
Implement support for extended FPIN messages received via the asynchronous sub-queue, completing full FPIN functionality.
Extended FPIN messages provide more detailed information about fabric events compared to basic FPIN messages, including specific event types, modifiers, thresholds, event counts, and severity.
Add
ibmvfc_ext_fpin_to_desc()to convert extended FPIN messages from async sub-queue format tofc_els_fpinstructures with complete descriptor information. Threaddetecting_wwpn(fc_host_port_name()) throughibmvfc_ext_fpin_to_desc()toibmvfc_common_fpin_to_desc()consistent withibmvfc_basic_fpin_to_desc()andibmvfc_full_fpin_to_desc(). Updateibmvfc_process_async_work()andibmvfc_handle_async()to zeronode_nameand dispatch toibmvfc_ext_fpin_to_desc()when theIBMVFC_ASYNC_IS_FPIN_EXTflag is set.Extend
ibmvfc_common_fpin_to_desc()with a severity parameter so that extended FPIN events can propagate the severity value from the VIOS whenIBMVFC_FPIN_SEVERITY_VALIDis set, rather than always defaulting toFPIN_CONGN_SEVERITY_WARNING. Severity is only carried by the congestion descriptor used forIBMVFC_AE_FPIN_LINK_CONGESTEDandIBMVFC_AE_FPIN_CONGESTION_CLEARED; the peer congestion and link integrity descriptors have no severity field, so a severity supplied for the other FPIN statuses is ignored.Set the
IBMVFC_CAN_HANDLE_FPIN_EXTcapability bit (0x800) during login to inform the VIOS that the client can process extended FPIN messages. Add theIBMVFC_CAN_CLS_CRQ_PRE_DMPcapability bit (0x400) definition for the intervening protocol-defined capability. Also add theIBMVFC_SUPPORT_FPIN_EXTresponse capability bit definition used to gate extended FPIN processing.In
ibmvfc_process_async_work(), use goto to bypass the generic "FPIN event received, unable to process" error message when a more specific diagnostic has already been emitted for invalid or unsupported extended FPIN events.Add a KUnit test,
ibmvfc_extended_fpin_test(), that sends extended FPIN events throughibmvfc_process_async_work()for each FPIN status with the event types valid for that status, setting onlyIBMVFC_FPIN_EVENT_TYPE_VALID, and checks that the matchingfc_hostor rport FPIN statistic is incremented. The test is skipped if no ibmvfc device, online target rport, orIBMVFC_SUPPORT_FPIN_EXTcapability is available. It does not exercise the modifier, threshold, severity or event count fields, nor the error paths for a missingIBMVFC_FPIN_EVENT_TYPE_VALIDflag or an unadvertisedIBMVFC_SUPPORT_FPIN_EXTcapability.
Quoted 2 lines
Signed-off-by Tyrel Datwyler <[email protected]>Signed-off-by Dave Marquardt <[email protected]>
[Severity: Low]
This isn't a bug in the code, but does the commit message match the changes in this patch?
The third paragraph states that this patch updates ibmvfc_process_async_work() and ibmvfc_handle_async() to zero node_name when IBMVFC_ASYNC_IS_FPIN_EXT is set, however ibmvfc_handle_async() is untouched here and zeroing node_name in both functions was already introduced in commit 6abed589f0fb ("scsi: ibmvfc: extend async event handlers for async sub-CRQ events").
Conversely, setting severity = 0 for IBMVFC_AE_FPIN_PORT_CLEARED and IBMVFC_AE_FPIN_CONGESTION_CLEARED in ibmvfc_basic_fpin_to_desc() and ibmvfc_full_fpin_to_desc() is done in this commit, whereas it was described in the commit message for 6abed589f0fb.
Quoted 4 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +0
-0
[ ... ]
Quoted 38 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +33
-1@@ -3507,7 +3517,48 @@ ibmvfc_full_fpin_to_desc(struct ibmvfc_async_sub_crq *ibmvfc_fpin,return ibmvfc_common_fpin_to_desc (ibmvfc_fpin->fpin_status, detecting_wwpn,cpu_to_be64 (wwpn), type,cpu_to_be16 (0 ),cpu_to_be32 (IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD ), -cpu_to_be32 (1 )); +cpu_to_be32 (1 ), severity); +} + +/** +* ibmvfc_ext_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct +* containing a descriptor. +* @ibmvfc_fpin: Pointer to async subq FPIN data +* @detecting_wwpn: WWPN of the local port that detected the event (big-endian) +* @wwpn: attached port WWPN (host-endian) +* +* Allocate a struct fc_els_fpin containing a descriptor and populate +* based on data from *ibmvfc_fpin. The caller must have verified that +* IBMVFC_FPIN_EVENT_TYPE_VALID is set in the FPIN data flags. +* +* Return: +* NULL - unable to allocate structure +* non-NULL - pointer to populated struct fc_els_fpin +*/ +static struct fc_els_fpin * +ibmvfc_ext_fpin_to_desc (struct ibmvfc_async_subq_fpin *ibmvfc_fpin, +__be64 detecting_wwpn,u64 wwpn) +{ +u8 flags = ibmvfc_fpin->fpin_data.flags; +__be32 threshold =cpu_to_be32 (IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD ); +__be16 modifier =0 ; +__be32 count =cpu_to_be32 (1 ); +__be16 type = ibmvfc_fpin->fpin_data.event_type; +__u8 severity =FPIN_CONGN_SEVERITY_WARNING ; + +if (flags &IBMVFC_FPIN_MODIFIER_VALID ) + modifier = ibmvfc_fpin->fpin_data.event_type_modifier; +if (flags &IBMVFC_FPIN_THRESHOLD_VALID ) + threshold = ibmvfc_fpin->fpin_data.event_threshold;
drivers/scsi/ibmvscsi/ibmvfc-core.c ibmvfc_full_fpin_to_desc(struct ibmvfc_async_sub_crq *ibmvfc_fpin, · line 3562
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +9
-0+if (flags &IBMVFC_FPIN_EVENT_COUNT_VALID ) + count = ibmvfc_fpin->fpin_data.event_data.event_count; +if (flags &IBMVFC_FPIN_SEVERITY_VALID ) + severity = ibmvfc_fpin->fpin_data.event_data.severity; + +return ibmvfc_common_fpin_to_desc (ibmvfc_fpin->fpin_status, detecting_wwpn, +cpu_to_be64 (wwpn), type, + modifier, threshold, count, + severity); }
[Severity: Medium]
Should severity in ibmvfc_ext_fpin_to_desc() default to 0 for cleared congestion events when IBMVFC_FPIN_SEVERITY_VALID is not set?
In ibmvfc_basic_fpin_to_desc() and ibmvfc_full_fpin_to_desc(), this patch sets severity = 0 for IBMVFC_AE_FPIN_PORT_CLEARED and IBMVFC_AE_FPIN_CONGESTION_CLEARED:
case IBMVFC_AE_FPIN_PORT_CLEARED: case IBMVFC_AE_FPIN_CONGESTION_CLEARED: type = cpu_to_be16(FPIN_CONGN_CLEAR); severity = 0; break;
In ibmvfc_ext_fpin_to_desc(), severity is initialized to FPIN_CONGN_SEVERITY_WARNING and is only updated if IBMVFC_FPIN_SEVERITY_VALID is set in flags. When an extended FPIN event for IBMVFC_AE_FPIN_CONGESTION_CLEARED (or with event_type FPIN_CONGN_CLEAR) is received without the optional IBMVFC_FPIN_SEVERITY_VALID flag (as in ibmvfc_extended_fpin_test()), cdesc->severity in the ELS FPIN descriptor is populated with FPIN_CONGN_SEVERITY_WARNING instead of 0.
Quoted 23 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +11
-5@@ -3624,14 +3681,30 @@ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) goto free; } -if (crq) +if (crq) { fpin =ibmvfc_basic_fpin_to_desc (crq,cpu_to_be64 (fc_host_port_name (vhost->host)), tgt->wwpn); -else - fpin =ibmvfc_full_fpin_to_desc (subq, -cpu_to_be64 (fc_host_port_name (vhost->host)), - tgt->wwpn); + }else { + sqfpin = (struct ibmvfc_async_subq_fpin *)subq; +if ((subq->flags &IBMVFC_ASYNC_IS_FPIN_EXT ) ==0 ) { + fpin =ibmvfc_full_fpin_to_desc (subq, +cpu_to_be64 (fc_host_port_name (vhost->host)), + tgt->wwpn); + }else if (!(sqfpin->fpin_data.flags &IBMVFC_FPIN_EVENT_TYPE_VALID )) { +dev_err_ratelimited (vhost->dev, +"Invalid extended FPIN event received\n" ); + goto skip_generic_error;
drivers/scsi/ibmvscsi/ibmvfc-core.c VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) · line 3707
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +10
-0+ }else if (!ibmvfc_check_caps (vhost,IBMVFC_SUPPORT_FPIN_EXT )) { +dev_err_ratelimited (vhost->dev, +"Unexpected extended FPIN event received\n" ); + goto skip_generic_error; + }else { + fpin =ibmvfc_ext_fpin_to_desc (sqfpin, +cpu_to_be64 (fc_host_port_name (vhost->host)), + tgt->wwpn); + } + }
[Severity: High]
What happens in ibmvfc_process_async_work() when a host-scope FPIN event (IBMVFC_AE_FPIN_LINK_CONGESTED or IBMVFC_AE_FPIN_CONGESTION_CLEARED) arrives on the async sub-CRQ with wwpn == 0 or with a WWPN that does not match a target in scsi_scrqs or nvme_scrqs?
Earlier in ibmvfc_process_async_work(), when aw->event.type == IBMVFC_ASYNC_CRQ_SUB and IBMVFC_ASYNC_IS_FPIN_EXT is set, scsi_id, node_name, and assoc_id are all set to 0, while crq remains NULL:
if (aw->event.type == IBMVFC_ASYNC_CRQ_SUB) {
subq = &aw->event.subq;
scsi_id = 0;
wwpn = subq->wwpn;
fpin_status = subq->fpin_status;
if (subq->flags & IBMVFC_ASYNC_ID_IS_ASSOC_ID) {
node_name = 0;
assoc_id = subq->id.assoc_id;
} else if (subq->flags & IBMVFC_ASYNC_IS_FPIN_EXT) {
node_name = 0;
assoc_id = 0;
} else {
node_name = subq->id.node_name;
assoc_id = 0;
}
}If wwpn is also 0, the zero-identifier check right after that is taken:
if (!scsi_id && !wwpn && !node_name && !assoc_id) {
if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED ||
fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED)
fpin = ibmvfc_basic_fpin_to_desc(crq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
goto send_fpin;
}Here crq is NULL, so ibmvfc_basic_fpin_to_desc(crq, ...) dereferences crq->fpin_status on a NULL pointer.
In addition, if wwpn is non-zero (for example, set to the host's own WWPN or a target whose rport/nvme_remote_port is no longer present) so ibmvfc_find_target() returns NULL, the fallback path only calls ibmvfc_basic_fpin_to_desc() or ibmvfc_full_fpin_to_desc():
} else {
spin_unlock_irqrestore(&vhost->host->host_lock, flags);
if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED ||
fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED) {
if (crq)
fpin = ibmvfc_basic_fpin_to_desc(crq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
else
fpin = ibmvfc_full_fpin_to_desc(subq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
goto send_fpin;
}Because IBMVFC_ASYNC_IS_FPIN_EXT and ibmvfc_ext_fpin_to_desc() are only handled on the tgt != NULL path, shouldn't the zero-identifier and target-not-found host-scope paths also handle subq and IBMVFC_ASYNC_IS_FPIN_EXT?
drivers/scsi/ibmvscsi/ibmvfc-core.c VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) · line 3721
Quoted 10 lines
drivers/scsi/ibmvscsi/ibmvfc-core.c +1
-0send_fpin:if (fpin) {@@ -3642,6 +3715,7 @@ VISIBLE_IF_KUNIT void ibmvfc_process_async_work(struct work_struct *work) }else dev_err_ratelimited (vhost->dev,"FPIN event received, unable to process\n" ); + skip_generic_error:if (tgt)kref_put (&tgt->kref, ibmvfc_release_tgt); free:
[Severity: Critical]
Can tgt be uninitialized when reaching kref_put(&tgt->kref, ibmvfc_release_tgt) in ibmvfc_process_async_work()?
At the top of ibmvfc_process_async_work(), tgt is declared without being initialized to NULL:
struct ibmvfc_target *tgt;
When all target identifiers are zero (!scsi_id && !wwpn && !node_name && !assoc_id, such as for host-scope FPIN events or the zero-identifier test cases in ibmvfc_async_fpin_test()), the function jumps to send_fpin before tgt is assigned by ibmvfc_find_target():
if (!scsi_id && !wwpn && !node_name && !assoc_id) {
if (fpin_status == IBMVFC_AE_FPIN_LINK_CONGESTED ||
fpin_status == IBMVFC_AE_FPIN_CONGESTION_CLEARED)
fpin = ibmvfc_basic_fpin_to_desc(crq,
cpu_to_be64(fc_host_port_name(vhost->host)), 0);
goto send_fpin;
}After send_fpin, execution falls through to skip_generic_error and checks if (tgt) kref_put(&tgt->kref, ibmvfc_release_tgt) using the uninitialized tgt pointer on the stack. Should tgt be initialized to NULL at declaration?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=13