2021-01-19 05:23:00

by Ahmed S. Darwish

[permalink] [raw]
Subject: [PATCH v3 00/19] scsi: libsas: Remove in_interrupt() check

Hi,

Changelog v3
------------

- Include latest version of John's patch. Collect r-b tags.

- Limit all code to 80 columns, even for intermediate patches.

- Rebase over v5.11-rc4

Changelog v2
------------

https://lkml.kernel.org/r/[email protected]

- Rebase on top of John's patch "scsi: libsas and users: Remove notifier
indirection", as it affects every other patch. Include it in this
series (patch #2).

- Introduce patches #13 => #19, which modify call sites back to use the
original libsas notifier function names without _gfp() suffix.

- Rebase over v5.11-rc3

v1 / Cover letter
-----------------

https://lkml.kernel.org/r/[email protected]

In the discussion about preempt count consistency across kernel
configurations:

https://lkml.kernel.org/r/[email protected]

it was concluded that the usage of in_interrupt() and related context
checks should be removed from non-core code.

This includes memory allocation mode decisions (GFP_*). In the long run,
usage of in_interrupt() and its siblings should be banned from driver
code completely.

This series addresses SCSI libsas. Basically, the function:

=> drivers/scsi/libsas/sas_init.c:
struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy)
{
...
gfp_t flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
event = kmem_cache_zalloc(sas_event_cache, flags);
...
}

is transformed so that callers explicitly pass the gfp_t memory
allocation flags. Affected libsas clients are modified accordingly.

Patches #1, #2 => #7 have "Fixes: " tags and address bugs the were
noticed during the context analysis.

Thanks!

8<--------------

Ahmed S. Darwish (18):
Documentation: scsi: libsas: Remove notify_ha_event()
scsi: libsas: Introduce a _gfp() variant of event notifiers
scsi: mvsas: Pass gfp_t flags to libsas event notifiers
scsi: isci: port: link down: Pass gfp_t flags
scsi: isci: port: link up: Pass gfp_t flags
scsi: isci: port: broadcast change: Pass gfp_t flags
scsi: libsas: Pass gfp_t flags to event notifiers
scsi: pm80xx: Pass gfp_t flags to libsas event notifiers
scsi: aic94xx: Pass gfp_t flags to libsas event notifiers
scsi: hisi_sas: Pass gfp_t flags to libsas event notifiers
scsi: libsas: event notifiers API: Add gfp_t flags parameter
scsi: hisi_sas: Switch back to original libsas event notifiers
scsi: aic94xx: Switch back to original libsas event notifiers
scsi: pm80xx: Switch back to original libsas event notifiers
scsi: libsas: Switch back to original event notifiers API
scsi: isci: Switch back to original libsas event notifiers
scsi: mvsas: Switch back to original libsas event notifiers
scsi: libsas: Remove temporarily-added _gfp() API variants

John Garry (1):
scsi: libsas and users: Remove notifier indirection

Documentation/scsi/libsas.rst | 9 +----
drivers/scsi/aic94xx/aic94xx_scb.c | 24 ++++++------
drivers/scsi/hisi_sas/hisi_sas.h | 3 +-
drivers/scsi/hisi_sas/hisi_sas_main.c | 29 +++++++-------
drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 7 ++--
drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 7 ++--
drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 7 ++--
drivers/scsi/isci/port.c | 11 +++---
drivers/scsi/libsas/sas_event.c | 27 ++++++-------
drivers/scsi/libsas/sas_init.c | 19 ++++-----
drivers/scsi/libsas/sas_internal.h | 6 +--
drivers/scsi/mvsas/mv_sas.c | 25 ++++++------
drivers/scsi/pm8001/pm8001_hwi.c | 54 ++++++++++++++++----------
drivers/scsi/pm8001/pm8001_sas.c | 12 ++----
drivers/scsi/pm8001/pm80xx_hwi.c | 46 ++++++++++++----------
include/scsi/libsas.h | 9 +++--
16 files changed, 149 insertions(+), 146 deletions(-)

base-commit: 19c329f6808995b142b3966301f217c831e7cf31
--
2.30.0


2021-01-19 05:23:00

by Ahmed S. Darwish

[permalink] [raw]
Subject: [PATCH v3 12/19] scsi: libsas: event notifiers API: Add gfp_t flags parameter

All call-sites of below libsas APIs:

- sas_alloc_event()
- sas_notify_port_event()
- sas_notify_phy_event()

have been converted to use the _gfp()-suffixed version. Modify the
original APIs above to take a gfp_t flags parameter by default.

For bisectability, call-sites will be modified again to use the original
libsas APIs (while passing gfp_t). The temporary _gfp()-suffixed
versions can then be removed.

Signed-off-by: Ahmed S. Darwish <[email protected]>
Reviewed-by: John Garry <[email protected]>
Cc: Jason Yan <[email protected]>
---
Documentation/scsi/libsas.rst | 4 +-
drivers/scsi/libsas/sas_event.c | 62 +++++++++---------------------
drivers/scsi/libsas/sas_init.c | 12 ++----
drivers/scsi/libsas/sas_internal.h | 5 ++-
include/scsi/libsas.h | 6 ++-
5 files changed, 31 insertions(+), 58 deletions(-)

diff --git a/Documentation/scsi/libsas.rst b/Documentation/scsi/libsas.rst
index ea63ab3a9216..c65086470a15 100644
--- a/Documentation/scsi/libsas.rst
+++ b/Documentation/scsi/libsas.rst
@@ -189,8 +189,8 @@ num_phys
The event interface::

/* LLDD calls these to notify the class of an event. */
- void sas_notify_port_event(struct sas_phy *, enum port_event);
- void sas_notify_phy_event(struct sas_phy *, enum phy_event);
+ void sas_notify_port_event(struct sas_phy *, enum port_event, gfp_t);
+ void sas_notify_phy_event(struct sas_phy *, enum phy_event, gfp_t);
void sas_notify_port_event_gfp(struct sas_phy *, enum port_event, gfp_t);
void sas_notify_phy_event_gfp(struct sas_phy *, enum phy_event, gfp_t);

diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c
index 25f3aaea8142..3d0cc407b33f 100644
--- a/drivers/scsi/libsas/sas_event.c
+++ b/drivers/scsi/libsas/sas_event.c
@@ -132,15 +132,19 @@ static void sas_phy_event_worker(struct work_struct *work)
sas_free_event(ev);
}

-static int __sas_notify_port_event(struct asd_sas_phy *phy,
- enum port_event event,
- struct asd_sas_event *ev)
+int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,
+ gfp_t gfp_flags)
{
struct sas_ha_struct *ha = phy->ha;
+ struct asd_sas_event *ev;
int ret;

BUG_ON(event >= PORT_NUM_EVENTS);

+ ev = sas_alloc_event_gfp(phy, gfp_flags);
+ if (!ev)
+ return -ENOMEM;
+
INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event);

ret = sas_queue_event(event, &ev->work, ha);
@@ -149,41 +153,28 @@ static int __sas_notify_port_event(struct asd_sas_phy *phy,

return ret;
}
+EXPORT_SYMBOL_GPL(sas_notify_port_event);

int sas_notify_port_event_gfp(struct asd_sas_phy *phy, enum port_event event,
gfp_t gfp_flags)
{
- struct asd_sas_event *ev;
-
- ev = sas_alloc_event_gfp(phy, gfp_flags);
- if (!ev)
- return -ENOMEM;
-
- return __sas_notify_port_event(phy, event, ev);
+ return sas_notify_port_event(phy, event, gfp_flags);
}
EXPORT_SYMBOL_GPL(sas_notify_port_event_gfp);

-int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event)
-{
- struct asd_sas_event *ev;
-
- ev = sas_alloc_event(phy);
- if (!ev)
- return -ENOMEM;
-
- return __sas_notify_port_event(phy, event, ev);
-}
-EXPORT_SYMBOL_GPL(sas_notify_port_event);
-
-static inline int __sas_notify_phy_event(struct asd_sas_phy *phy,
- enum phy_event event,
- struct asd_sas_event *ev)
+int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
+ gfp_t gfp_flags)
{
struct sas_ha_struct *ha = phy->ha;
+ struct asd_sas_event *ev;
int ret;

BUG_ON(event >= PHY_NUM_EVENTS);

+ ev = sas_alloc_event_gfp(phy, gfp_flags);
+ if (!ev)
+ return -ENOMEM;
+
INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event);

ret = sas_queue_event(event, &ev->work, ha);
@@ -192,28 +183,11 @@ static inline int __sas_notify_phy_event(struct asd_sas_phy *phy,

return ret;
}
+EXPORT_SYMBOL_GPL(sas_notify_phy_event);

int sas_notify_phy_event_gfp(struct asd_sas_phy *phy, enum phy_event event,
gfp_t gfp_flags)
{
- struct asd_sas_event *ev;
-
- ev = sas_alloc_event_gfp(phy, gfp_flags);
- if (!ev)
- return -ENOMEM;
-
- return __sas_notify_phy_event(phy, event, ev);
+ return sas_notify_phy_event(phy, event, gfp_flags);
}
EXPORT_SYMBOL_GPL(sas_notify_phy_event_gfp);
-
-int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
-{
- struct asd_sas_event *ev;
-
- ev = sas_alloc_event(phy);
- if (!ev)
- return -ENOMEM;
-
- return __sas_notify_phy_event(phy, event, ev);
-}
-EXPORT_SYMBOL_GPL(sas_notify_phy_event);
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index 9ce0cd214eb9..f06b83211e3b 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -585,8 +585,8 @@ sas_domain_attach_transport(struct sas_domain_function_template *dft)
}
EXPORT_SYMBOL_GPL(sas_domain_attach_transport);

-static struct asd_sas_event *__sas_alloc_event(struct asd_sas_phy *phy,
- gfp_t gfp_flags)
+struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy,
+ gfp_t gfp_flags)
{
struct asd_sas_event *event;
struct sas_ha_struct *sas_ha = phy->ha;
@@ -619,15 +619,11 @@ static struct asd_sas_event *__sas_alloc_event(struct asd_sas_phy *phy,
return event;
}

-struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy)
-{
- return __sas_alloc_event(phy, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
-}
-
struct asd_sas_event *sas_alloc_event_gfp(struct asd_sas_phy *phy,
gfp_t gfp_flags)
{
- return __sas_alloc_event(phy, gfp_flags);
+
+ return sas_alloc_event(phy, gfp_flags);
}

void sas_free_event(struct asd_sas_event *event)
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 52e09c3e2b50..294cdcb4ce42 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -48,7 +48,7 @@ int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf);
int sas_register_phys(struct sas_ha_struct *sas_ha);
void sas_unregister_phys(struct sas_ha_struct *sas_ha);

-struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy);
+struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, gfp_t gfp_flags);
struct asd_sas_event *sas_alloc_event_gfp(struct asd_sas_phy *phy,
gfp_t gfp_flags);
void sas_free_event(struct asd_sas_event *event);
@@ -78,7 +78,8 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
enum phy_func phy_func, struct sas_phy_linkrates *);
int sas_smp_get_phy_events(struct sas_phy *phy);

-int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event);
+int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
+ gfp_t flags);
int sas_notify_phy_event_gfp(struct asd_sas_phy *phy, enum phy_event event,
gfp_t flags);
void sas_device_set_phy(struct domain_device *dev, struct sas_port *port);
diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
index e6a43163ab5b..fda56e151695 100644
--- a/include/scsi/libsas.h
+++ b/include/scsi/libsas.h
@@ -702,8 +702,10 @@ struct sas_phy *sas_get_local_phy(struct domain_device *dev);

int sas_request_addr(struct Scsi_Host *shost, u8 *addr);

-int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event);
-int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event);
+int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,
+ gfp_t gfp_flags);
+int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
+ gfp_t gfp_flags);
int sas_notify_port_event_gfp(struct asd_sas_phy *phy, enum port_event event,
gfp_t gfp_flags);
int sas_notify_phy_event_gfp(struct asd_sas_phy *phy, enum phy_event event,
--
2.30.0

2021-01-19 05:23:39

by Ahmed S. Darwish

[permalink] [raw]
Subject: [PATCH v3 07/19] scsi: isci: port: broadcast change: Pass gfp_t flags

Use the new libsas event notifiers API, which requires callers to
explicitly pass the gfp_t memory allocation flags.

libsas sas_notify_port_event() is called from
isci_port_bc_change_received(). Below is the context analysis for all
of its call chains:

host.c: sci_controller_error_handler(): atomic, irq handler (*)
OR host.c: sci_controller_completion_handler(), atomic, tasklet (*)
-> sci_controller_process_completions()
-> sci_controller_event_completion()
-> phy.c: sci_phy_event_handler()
-> port.c: sci_port_broadcast_change_received()
-> isci_port_bc_change_received()

host.c: isci_host_init() (@)
spin_lock_irq(isci_host::scic_lock)
-> sci_controller_initialize(), atomic (*)
-> port_config.c: sci_port_configuration_agent_initialize()
-> sci_mpc_agent_validate_phy_configuration()
-> port.c: sci_port_add_phy()
-> sci_port_set_phy()
-> phy.c: sci_phy_set_port()
-> port.c: sci_port_broadcast_change_received()
-> isci_port_bc_change_received()

port_config.c: apc_agent_timeout(), atomic, timer callback (*)
-> sci_apc_agent_configure_ports()
-> port.c: sci_port_add_phy()
-> sci_port_set_phy()
-> phy.c: sci_phy_set_port()
-> port.c: sci_port_broadcast_change_received()
-> isci_port_bc_change_received()

phy.c: enter SCI state: *SCI_PHY_STOPPED* # Cont. from [1]
-> sci_phy_stopped_state_enter()
-> host.c: sci_controller_link_down()
-> ->link_down_handler()
== port_config.c: sci_apc_agent_link_down()
-> port.c: sci_port_remove_phy()
-> sci_port_clear_phy()
-> phy.c: sci_phy_set_port()
-> port.c: sci_port_broadcast_change_received()
-> isci_port_bc_change_received()

phy.c: enter SCI state: *SCI_PHY_STARTING* # Cont. from [2]
-> sci_phy_starting_state_enter()
-> host.c: sci_controller_link_down()
-> ->link_down_handler()
== port_config.c: sci_apc_agent_link_down()
-> port.c: sci_port_remove_phy()
-> sci_port_clear_phy()
-> phy.c: sci_phy_set_port()
-> port.c: sci_port_broadcast_change_received()
-> isci_port_bc_change_received()

[1] Call chains for entering state: *SCI_PHY_STOPPED*
-----------------------------------------------------

host.c: isci_host_init() (@)
spin_lock_irq(isci_host::scic_lock)
-> sci_controller_initialize(), atomic (*)
-> phy.c: sci_phy_initialize()
-> phy.c: sci_phy_link_layer_initialization()
-> phy.c: sci_change_state(SCI_PHY_STOPPED)

init.c: PCI ->remove() || PM_OPS ->suspend, process context (+)
-> host.c: isci_host_deinit()
-> sci_controller_stop_phys()
-> phy.c: sci_phy_stop()
-> sci_change_state(SCI_PHY_STOPPED)

phy.c: isci_phy_control()
spin_lock_irqsave(isci_host::scic_lock, )
-> sci_phy_stop(), atomic (*)
-> sci_change_state(SCI_PHY_STOPPED)

[2] Call chains for entering state: *SCI_PHY_STARTING*
------------------------------------------------------

phy.c: phy_sata_timeout(), atimer, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> sci_change_state(SCI_PHY_STARTING)

host.c: phy_startup_timeout(), atomic, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> sci_controller_start_next_phy()
-> sci_phy_start()
-> sci_change_state(SCI_PHY_STARTING)

host.c: isci_host_start() (@)
spin_lock_irq(isci_host::scic_lock)
-> sci_controller_start(), atomic (*)
-> sci_controller_start_next_phy()
-> sci_phy_start()
-> sci_change_state(SCI_PHY_STARTING)

phy.c: Enter SCI state *SCI_PHY_SUB_FINAL* # Cont. from [2A]
-> sci_change_state(SCI_PHY_SUB_FINAL)
-> sci_phy_starting_final_substate_enter()
-> sci_change_state(SCI_PHY_READY)
-> Enter SCI state: *SCI_PHY_READY*
-> sci_phy_ready_state_enter()
-> host.c: sci_controller_link_up()
-> sci_controller_start_next_phy()
-> sci_phy_start()
-> sci_change_state(SCI_PHY_STARTING)

phy.c: sci_phy_event_handler(), atomic, discussed earlier (*)
-> sci_change_state(SCI_PHY_STARTING), 11 instances

port.c: isci_port_perform_hard_reset()
spin_lock_irqsave(isci_host::scic_lock, )
-> port.c: sci_port_hard_reset(), atomic (*)
-> phy.c: sci_phy_reset()
-> sci_change_state(SCI_PHY_RESETTING)
-> enter SCI PHY state: *SCI_PHY_RESETTING*
-> sci_phy_resetting_state_enter()
-> sci_change_state(SCI_PHY_STARTING)

[2A] Call chains for entering SCI state: *SCI_PHY_SUB_FINAL*
------------------------------------------------------------

host.c: power_control_timeout(), atomic, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> phy.c: sci_phy_consume_power_handler()
-> phy.c: sci_change_state(SCI_PHY_SUB_FINAL)

host.c: sci_controller_error_handler(): atomic, irq handler (*)
OR host.c: sci_controller_completion_handler(), atomic, tasklet (*)
-> sci_controller_process_completions()
-> sci_controller_unsolicited_frame()
-> phy.c: sci_phy_frame_handler()
-> sci_change_state(SCI_PHY_SUB_AWAIT_SAS_POWER)
-> sci_phy_starting_await_sas_power_substate_enter()
-> host.c: sci_controller_power_control_queue_insert()
-> phy.c: sci_phy_consume_power_handler()
-> sci_change_state(SCI_PHY_SUB_FINAL)
-> sci_change_state(SCI_PHY_SUB_FINAL)
-> sci_controller_event_completion()
-> phy.c: sci_phy_event_handler()
-> sci_phy_start_sata_link_training()
-> sci_change_state(SCI_PHY_SUB_AWAIT_SATA_POWER)
-> sci_phy_starting_await_sata_power_substate_enter
-> host.c: sci_controller_power_control_queue_insert()
-> phy.c: sci_phy_consume_power_handler()
-> sci_change_state(SCI_PHY_SUB_FINAL)

As can be seen from the "(*)" markers above, almost all the call-chains
are atomic. The only exception, marked with "(+)", is a PCI ->remove()
and PM_OPS ->suspend() cold path. Thus, pass GFP_ATOMIC to the libsas
port event notifier.

Note, the now-replaced libsas APIs used in_interrupt() to implicitly
decide which memory allocation type to use. This was only partially
correct, as it fails to choose the correct GFP flags when just
preemption or interrupts are disabled. Such buggy code paths are marked
with "(@)" in the call chains above.

Fixes: 1c393b970e0f ("scsi: libsas: Use dynamic alloced work to avoid sas event lost")
Signed-off-by: Ahmed S. Darwish <[email protected]>
Reviewed-by: John Garry <[email protected]>
Cc: Artur Paszkiewicz <[email protected]>
---
drivers/scsi/isci/port.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 10136ae466e2..e50c3b0deeb3 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -164,7 +164,8 @@ static void isci_port_bc_change_received(struct isci_host *ihost,
"%s: isci_phy = %p, sas_phy = %p\n",
__func__, iphy, &iphy->sas_phy);

- sas_notify_port_event(&iphy->sas_phy, PORTE_BROADCAST_RCVD);
+ sas_notify_port_event_gfp(&iphy->sas_phy,
+ PORTE_BROADCAST_RCVD, GFP_ATOMIC);
sci_port_bcn_enable(iport);
}

--
2.30.0

2021-01-19 05:24:02

by Ahmed S. Darwish

[permalink] [raw]
Subject: [PATCH v3 05/19] scsi: isci: port: link down: Pass gfp_t flags

Use the new libsas event notifiers API, which requires callers to
explicitly pass the gfp_t memory allocation flags.

sas_notify_phy_event() is exclusively called by isci_port_link_down().
Below is the context analysis for all of its call chains:

port.c: port_timeout(), atomic, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> port_state_machine_change(..., SCI_PORT_FAILED)
-> enter SCI port state: *SCI_PORT_FAILED*
-> sci_port_failed_state_enter()
-> isci_port_hard_reset_complete()
-> isci_port_link_down()

port.c: isci_port_perform_hard_reset()
spin_lock_irqsave(isci_host::scic_lock, )
-> port.c: sci_port_hard_reset(), atomic (*)
-> phy.c: sci_phy_reset()
-> sci_change_state(SCI_PHY_RESETTING)
-> enter SCI PHY state: *SCI_PHY_RESETTING*
-> sci_phy_resetting_state_enter()
-> port.c: sci_port_deactivate_phy()
-> isci_port_link_down()

port.c: enter SCI port state: *SCI_PORT_READY* # Cont. from [1]
-> sci_port_ready_state_enter()
-> isci_port_hard_reset_complete()
-> isci_port_link_down()

phy.c: enter SCI state: *SCI_PHY_STOPPED* # Cont. from [2]
-> sci_phy_stopped_state_enter()
-> host.c: sci_controller_link_down()
-> ->link_down_handler()
== port_config.c: sci_apc_agent_link_down()
-> port.c: sci_port_remove_phy()
-> sci_port_deactivate_phy()
-> isci_port_link_down()
== port_config.c: sci_mpc_agent_link_down()
-> port.c: sci_port_link_down()
-> sci_port_deactivate_phy()
-> isci_port_link_down()

phy.c: enter SCI state: *SCI_PHY_STARTING* # Cont. from [3]
-> sci_phy_starting_state_enter()
-> host.c: sci_controller_link_down()
-> ->link_down_handler()
== port_config.c: sci_apc_agent_link_down()
-> port.c: sci_port_remove_phy()
-> isci_port_link_down()
== port_config.c: sci_mpc_agent_link_down()
-> port.c: sci_port_link_down()
-> sci_port_deactivate_phy()
-> isci_port_link_down()

[1] Call chains for 'enter SCI port state: *SCI_PORT_READY*'
------------------------------------------------------------

host.c: isci_host_init() (@)
spin_lock_irq(isci_host::scic_lock)
-> sci_controller_initialize(), atomic (*)
-> port_config.c: sci_port_configuration_agent_initialize()
-> sci_mpc_agent_validate_phy_configuration()
-> port.c: sci_port_add_phy()
-> sci_port_general_link_up_handler()
-> port_state_machine_change(, SCI_PORT_READY)
-> enter port state *SCI_PORT_READY*

host.c: isci_host_start() (@)
spin_lock_irq(isci_host::scic_lock)
-> host.c: sci_controller_start(), atomic (*)
-> host.c: sci_port_start()
-> port.c: port_state_machine_change(, SCI_PORT_READY)
-> enter port state *SCI_PORT_READY*

port_config.c: apc_agent_timeout(), atomic, timer callback (*)
-> sci_apc_agent_configure_ports()
-> port.c: sci_port_add_phy()
-> sci_port_general_link_up_handler()
-> port_state_machine_change(, SCI_PORT_READY)
-> enter port state *SCI_PORT_READY*

port_config.c: mpc_agent_timeout(), atomic, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> ->link_up_handler()
== port.c: sci_apc_agent_link_up()
-> sci_port_general_link_up_handler()
-> port_state_machine_change(, SCI_PORT_READY)
-> enter port state *SCI_PORT_READY*
== port.c: sci_mpc_agent_link_up()
-> port.c: sci_port_link_up()
-> sci_port_general_link_up_handler()
-> port_state_machine_change(, SCI_PORT_READY)
-> enter port state *SCI_PORT_READY*

phy.c: enter SCI state: SCI_PHY_SUB_FINAL # Cont. from [1A]
-> sci_phy_starting_final_substate_enter()
-> sci_change_state(SCI_PHY_READY)
-> enter SCI state: *SCI_PHY_READY*
-> sci_phy_ready_state_enter()
-> host.c: sci_controller_link_up()
-> port_agent.link_up_handler()
== port_config.c: sci_apc_agent_link_up()
-> port.c: sci_port_link_up()
-> sci_port_general_link_up_handler()
-> port_state_machine_change(, SCI_PORT_READY)
-> enter port state *SCI_PORT_READY*
== port_config.c: sci_mpc_agent_link_up()
-> port.c: sci_port_link_up()
-> sci_port_general_link_up_handler()
-> port_state_machine_change(, SCI_PORT_READY)
-> enter port state *SCI_PORT_READY*

[1A] Call chains for entering SCI state: *SCI_PHY_SUB_FINAL*
------------------------------------------------------------

host.c: power_control_timeout(), atomic, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> phy.c: sci_phy_consume_power_handler()
-> phy.c: sci_change_state(SCI_PHY_SUB_FINAL)

host.c: sci_controller_error_handler(): atomic, irq handler (*)
OR host.c: sci_controller_completion_handler(), atomic, tasklet (*)
-> sci_controller_process_completions()
-> sci_controller_unsolicited_frame()
-> phy.c: sci_phy_frame_handler()
-> sci_change_state(SCI_PHY_SUB_AWAIT_SAS_POWER)
-> sci_phy_starting_await_sas_power_substate_enter()
-> host.c: sci_controller_power_control_queue_insert()
-> phy.c: sci_phy_consume_power_handler()
-> sci_change_state(SCI_PHY_SUB_FINAL)
-> sci_change_state(SCI_PHY_SUB_FINAL)
-> sci_controller_event_completion()
-> phy.c: sci_phy_event_handler()
-> sci_phy_start_sata_link_training()
-> sci_change_state(SCI_PHY_SUB_AWAIT_SATA_POWER)
-> sci_phy_starting_await_sata_power_substate_enter
-> host.c: sci_controller_power_control_queue_insert()
-> phy.c: sci_phy_consume_power_handler()
-> sci_change_state(SCI_PHY_SUB_FINAL)

[2] Call chains for entering state: *SCI_PHY_STOPPED*
-----------------------------------------------------

host.c: isci_host_init() (@)
spin_lock_irq(isci_host::scic_lock)
-> sci_controller_initialize(), atomic (*)
-> phy.c: sci_phy_initialize()
-> phy.c: sci_phy_link_layer_initialization()
-> phy.c: sci_change_state(SCI_PHY_STOPPED)

init.c: PCI ->remove() || PM_OPS ->suspend, process context (+)
-> host.c: isci_host_deinit()
-> sci_controller_stop_phys()
-> phy.c: sci_phy_stop()
-> sci_change_state(SCI_PHY_STOPPED)

phy.c: isci_phy_control()
spin_lock_irqsave(isci_host::scic_lock, )
-> sci_phy_stop(), atomic (*)
-> sci_change_state(SCI_PHY_STOPPED)

[3] Call chains for entering state: *SCI_PHY_STARTING*
------------------------------------------------------

phy.c: phy_sata_timeout(), atimer, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> sci_change_state(SCI_PHY_STARTING)

host.c: phy_startup_timeout(), atomic, timer callback (*)
spin_lock_irqsave(isci_host::scic_lock, )
-> sci_controller_start_next_phy()
-> sci_phy_start()
-> sci_change_state(SCI_PHY_STARTING)

host.c: isci_host_start() (@)
spin_lock_irq(isci_host::scic_lock)
-> sci_controller_start(), atomic (*)
-> sci_controller_start_next_phy()
-> sci_phy_start()
-> sci_change_state(SCI_PHY_STARTING)

phy.c: Enter SCI state *SCI_PHY_SUB_FINAL*, atomic, check above (*)
-> sci_change_state(SCI_PHY_SUB_FINAL)
-> sci_phy_starting_final_substate_enter()
-> sci_change_state(SCI_PHY_READY)
-> Enter SCI state: *SCI_PHY_READY*
-> sci_phy_ready_state_enter()
-> host.c: sci_controller_link_up()
-> sci_controller_start_next_phy()
-> sci_phy_start()
-> sci_change_state(SCI_PHY_STARTING)

phy.c: sci_phy_event_handler(), atomic, discussed earlier (*)
-> sci_change_state(SCI_PHY_STARTING), 11 instances

phy.c: enter SCI state: *SCI_PHY_RESETTING*, atomic, discussed (*)
-> sci_phy_resetting_state_enter()
-> sci_change_state(SCI_PHY_STARTING)

As can be seen from the "(*)" markers above, almost all the call-chains
are atomic. The only exception, marked with "(+)", is a PCI ->remove()
and PM_OPS ->suspend() cold path. Thus, pass GFP_ATOMIC to the libsas
phy event notifier.

Note, The now-replaced libsas APIs used in_interrupt() to implicitly
decide which memory allocation type to use. This was only partially
correct, as it fails to choose the correct GFP flags when just
preemption or interrupts are disabled. Such buggy code paths are marked
with "(@)" in the call chains above.

Fixes: 1c393b970e0f ("scsi: libsas: Use dynamic alloced work to avoid sas event lost")
Signed-off-by: Ahmed S. Darwish <[email protected]>
Reviewed-by: John Garry <[email protected]>
Cc: Artur Paszkiewicz <[email protected]>
---
drivers/scsi/isci/port.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 8d9349738067..a3c58718c260 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -269,8 +269,8 @@ static void isci_port_link_down(struct isci_host *isci_host,
* isci_port_deformed and isci_dev_gone functions.
*/
sas_phy_disconnected(&isci_phy->sas_phy);
- sas_notify_phy_event(&isci_phy->sas_phy,
- PHYE_LOSS_OF_SIGNAL);
+ sas_notify_phy_event_gfp(&isci_phy->sas_phy,
+ PHYE_LOSS_OF_SIGNAL, GFP_ATOMIC);

dev_dbg(&isci_host->pdev->dev,
"%s: isci_port = %p - Done\n", __func__, isci_port);
--
2.30.0

2021-01-23 02:37:48

by Martin K. Petersen

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] scsi: libsas: Remove in_interrupt() check


Ahmed,

> Ahmed S. Darwish (18):
> Documentation: scsi: libsas: Remove notify_ha_event()
> scsi: libsas: Introduce a _gfp() variant of event notifiers
> scsi: mvsas: Pass gfp_t flags to libsas event notifiers
> scsi: isci: port: link down: Pass gfp_t flags
> scsi: isci: port: link up: Pass gfp_t flags
> scsi: isci: port: broadcast change: Pass gfp_t flags
> scsi: libsas: Pass gfp_t flags to event notifiers
> scsi: pm80xx: Pass gfp_t flags to libsas event notifiers
> scsi: aic94xx: Pass gfp_t flags to libsas event notifiers
> scsi: hisi_sas: Pass gfp_t flags to libsas event notifiers
> scsi: libsas: event notifiers API: Add gfp_t flags parameter
> scsi: hisi_sas: Switch back to original libsas event notifiers
> scsi: aic94xx: Switch back to original libsas event notifiers
> scsi: pm80xx: Switch back to original libsas event notifiers
> scsi: libsas: Switch back to original event notifiers API
> scsi: isci: Switch back to original libsas event notifiers
> scsi: mvsas: Switch back to original libsas event notifiers
> scsi: libsas: Remove temporarily-added _gfp() API variants
>
> John Garry (1):
> scsi: libsas and users: Remove notifier indirection

Applied to 5.12/scsi-staging, thanks!

--
Martin K. Petersen Oracle Linux Engineering

2021-01-27 21:10:37

by Martin K. Petersen

[permalink] [raw]
Subject: Re: [PATCH v3 00/19] scsi: libsas: Remove in_interrupt() check

On Mon, 18 Jan 2021 11:09:36 +0100, Ahmed S. Darwish wrote:

> Changelog v3
> ------------
>
> - Include latest version of John's patch. Collect r-b tags.
>
> - Limit all code to 80 columns, even for intermediate patches.
>
> [...]

Applied to 5.12/scsi-queue, thanks!

[01/19] Documentation: scsi: libsas: Remove notify_ha_event()
https://git.kernel.org/mkp/scsi/c/3f901c81dfad
[02/19] scsi: libsas and users: Remove notifier indirection
https://git.kernel.org/mkp/scsi/c/121181f3f839
[03/19] scsi: libsas: Introduce a _gfp() variant of event notifiers
https://git.kernel.org/mkp/scsi/c/c2d0f1a65ab9
[04/19] scsi: mvsas: Pass gfp_t flags to libsas event notifiers
https://git.kernel.org/mkp/scsi/c/feb18e900f00
[05/19] scsi: isci: port: link down: Pass gfp_t flags
https://git.kernel.org/mkp/scsi/c/885ab3b8926f
[06/19] scsi: isci: port: link up: Pass gfp_t flags
https://git.kernel.org/mkp/scsi/c/5ce7902902ad
[07/19] scsi: isci: port: broadcast change: Pass gfp_t flags
https://git.kernel.org/mkp/scsi/c/71dca5539fcf
[08/19] scsi: libsas: Pass gfp_t flags to event notifiers
https://git.kernel.org/mkp/scsi/c/19a39831ff99
[09/19] scsi: pm80xx: Pass gfp_t flags to libsas event notifiers
https://git.kernel.org/mkp/scsi/c/cd4e8176989f
[10/19] scsi: aic94xx: Pass gfp_t flags to libsas event notifiers
https://git.kernel.org/mkp/scsi/c/111d06ab77c9
[11/19] scsi: hisi_sas: Pass gfp_t flags to libsas event notifiers
https://git.kernel.org/mkp/scsi/c/26c7efc3f952
[12/19] scsi: libsas: event notifiers API: Add gfp_t flags parameter
https://git.kernel.org/mkp/scsi/c/5d6a75a1edf6
[13/19] scsi: hisi_sas: Switch back to original libsas event notifiers
https://git.kernel.org/mkp/scsi/c/872a90b5b466
[14/19] scsi: aic94xx: Switch back to original libsas event notifiers
https://git.kernel.org/mkp/scsi/c/093289e40b52
[15/19] scsi: pm80xx: Switch back to original libsas event notifiers
https://git.kernel.org/mkp/scsi/c/de6d7547ce1d
[16/19] scsi: libsas: Switch back to original event notifiers API
https://git.kernel.org/mkp/scsi/c/f76d9f1a1511
[17/19] scsi: isci: Switch back to original libsas event notifiers
https://git.kernel.org/mkp/scsi/c/c12208668aef
[18/19] scsi: mvsas: Switch back to original libsas event notifiers
https://git.kernel.org/mkp/scsi/c/36cdfd0f7a8c
[19/19] scsi: libsas: Remove temporarily-added _gfp() API variants
https://git.kernel.org/mkp/scsi/c/65f7cfba6196

--
Martin K. Petersen Oracle Linux Engineering