Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi: libsas: Keep host active while processing events

Processing events such as PORTE_BROADCAST_RCVD may cause dependency issues
for runtime power management support. Such a problem would be that
handling a PORTE_BROADCAST_RCVD event requires that the host is resumed to
send SMP commands. However, in resuming the host, the phyup events
generated from re-enabling the phys are processed in the same workqueue as
the original PORTE_BROADCAST_RCVD event. As such, the host will never
finish resuming (as it waits for the phyup event processing), and then the
PORTE_BROADCAST_RCVD event can't be processed as the SMP commands are
blocked, and so we have a deadlock. Solve this problem by ensuring that
libsas keeps the host active until completely finished phy or port events,
such as PORTE_BYTES_DMAED. As such, we don't have to worry about resuming
the host for processing individual SMP commands in this example.

Link: https://lore.kernel.org/r/1639999298-244569-15-git-send-email-chenxiang66@hisilicon.com
Reviewed-by: John Garry <john.garry@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Xiang Chen and committed by
Martin K. Petersen
307d9f49 ae9b69e8

+21 -3
+21 -3
drivers/scsi/libsas/sas_event.c
··· 50 50 list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { 51 51 list_del_init(&sw->drain_node); 52 52 ret = sas_queue_work(ha, sw); 53 - if (ret != 1) 53 + if (ret != 1) { 54 + pm_runtime_put(ha->dev); 54 55 sas_free_event(to_asd_sas_event(&sw->work)); 56 + } 55 57 } 56 58 spin_unlock_irq(&ha->lock); 57 59 } ··· 128 126 static void sas_port_event_worker(struct work_struct *work) 129 127 { 130 128 struct asd_sas_event *ev = to_asd_sas_event(work); 129 + struct asd_sas_phy *phy = ev->phy; 130 + struct sas_ha_struct *ha = phy->ha; 131 131 132 132 sas_port_event_fns[ev->event](work); 133 + pm_runtime_put(ha->dev); 133 134 sas_free_event(ev); 134 135 } 135 136 136 137 static void sas_phy_event_worker(struct work_struct *work) 137 138 { 138 139 struct asd_sas_event *ev = to_asd_sas_event(work); 140 + struct asd_sas_phy *phy = ev->phy; 141 + struct sas_ha_struct *ha = phy->ha; 139 142 140 143 sas_phy_event_fns[ev->event](work); 144 + pm_runtime_put(ha->dev); 141 145 sas_free_event(ev); 142 146 } 143 147 ··· 178 170 if (!ev) 179 171 return -ENOMEM; 180 172 173 + /* Call pm_runtime_put() with pairs in sas_port_event_worker() */ 174 + pm_runtime_get_noresume(ha->dev); 175 + 181 176 INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event); 182 177 183 178 if (sas_defer_event(phy, ev)) 184 179 return 0; 185 180 186 181 ret = sas_queue_event(event, &ev->work, ha); 187 - if (ret != 1) 182 + if (ret != 1) { 183 + pm_runtime_put(ha->dev); 188 184 sas_free_event(ev); 185 + } 189 186 190 187 return ret; 191 188 } ··· 209 196 if (!ev) 210 197 return -ENOMEM; 211 198 199 + /* Call pm_runtime_put() with pairs in sas_phy_event_worker() */ 200 + pm_runtime_get_noresume(ha->dev); 201 + 212 202 INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event); 213 203 214 204 if (sas_defer_event(phy, ev)) 215 205 return 0; 216 206 217 207 ret = sas_queue_event(event, &ev->work, ha); 218 - if (ret != 1) 208 + if (ret != 1) { 209 + pm_runtime_put(ha->dev); 219 210 sas_free_event(ev); 211 + } 220 212 221 213 return ret; 222 214 }