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

scsi: libsas: shut down the PHY if events reached the threshold

If the PHY burst too many events, we will alloc a lot of events for the
worker. This may leads to memory exhaustion.

Dan Williams suggested to shut down the PHY if the events reached the
threshold, because in this case the PHY may have gone into some
erroneous state. Users can re-enable the PHY by sysfs if they want.

We cannot use the fixed memory pool because if we run out of events, the
shut down event and loss of signal event will lost too. The events still
need to be allocated and processed in this case.

Suggested-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Jason Yan <yanaijie@huawei.com>
CC: John Garry <john.garry@huawei.com>
CC: Johannes Thumshirn <jthumshirn@suse.de>
CC: Ewan Milne <emilne@redhat.com>
CC: Christoph Hellwig <hch@lst.de>
CC: Tomas Henzl <thenzl@redhat.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Jason Yan and committed by
Martin K. Petersen
f12486e0 1c393b97

+64 -2
+32 -1
drivers/scsi/libsas/sas_init.c
··· 123 123 INIT_LIST_HEAD(&sas_ha->defer_q); 124 124 INIT_LIST_HEAD(&sas_ha->eh_dev_q); 125 125 126 + sas_ha->event_thres = SAS_PHY_SHUTDOWN_THRES; 127 + 126 128 error = sas_register_phys(sas_ha); 127 129 if (error) { 128 130 printk(KERN_NOTICE "couldn't register sas phys:%d\n", error); ··· 559 557 560 558 struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy) 561 559 { 560 + struct asd_sas_event *event; 562 561 gfp_t flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; 562 + struct sas_ha_struct *sas_ha = phy->ha; 563 + struct sas_internal *i = 564 + to_sas_internal(sas_ha->core.shost->transportt); 563 565 564 - return kmem_cache_zalloc(sas_event_cache, flags); 566 + event = kmem_cache_zalloc(sas_event_cache, flags); 567 + if (!event) 568 + return NULL; 569 + 570 + atomic_inc(&phy->event_nr); 571 + 572 + if (atomic_read(&phy->event_nr) > phy->ha->event_thres) { 573 + if (i->dft->lldd_control_phy) { 574 + if (cmpxchg(&phy->in_shutdown, 0, 1) == 0) { 575 + sas_printk("The phy%02d bursting events, shut it down.\n", 576 + phy->id); 577 + sas_notify_phy_event(phy, PHYE_SHUTDOWN); 578 + } 579 + } else { 580 + /* Do not support PHY control, stop allocating events */ 581 + WARN_ONCE(1, "PHY control not supported.\n"); 582 + kmem_cache_free(sas_event_cache, event); 583 + atomic_dec(&phy->event_nr); 584 + event = NULL; 585 + } 586 + } 587 + 588 + return event; 565 589 } 566 590 567 591 void sas_free_event(struct asd_sas_event *event) 568 592 { 593 + struct asd_sas_phy *phy = event->phy; 594 + 569 595 kmem_cache_free(sas_event_cache, event); 596 + atomic_dec(&phy->event_nr); 570 597 } 571 598 572 599 /* ---------- SAS Class register/unregister ---------- */
+26 -1
drivers/scsi/libsas/sas_phy.c
··· 35 35 struct asd_sas_event *ev = to_asd_sas_event(work); 36 36 struct asd_sas_phy *phy = ev->phy; 37 37 38 + phy->in_shutdown = 0; 38 39 phy->error = 0; 39 40 sas_deform_port(phy, 1); 40 41 } ··· 45 44 struct asd_sas_event *ev = to_asd_sas_event(work); 46 45 struct asd_sas_phy *phy = ev->phy; 47 46 47 + phy->in_shutdown = 0; 48 48 phy->error = 0; 49 49 } 50 50 ··· 107 105 } 108 106 109 107 108 + static void sas_phye_shutdown(struct work_struct *work) 109 + { 110 + struct asd_sas_event *ev = to_asd_sas_event(work); 111 + struct asd_sas_phy *phy = ev->phy; 112 + struct sas_ha_struct *sas_ha = phy->ha; 113 + struct sas_internal *i = 114 + to_sas_internal(sas_ha->core.shost->transportt); 115 + 116 + if (phy->enabled) { 117 + int ret; 118 + 119 + phy->error = 0; 120 + phy->enabled = 0; 121 + ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); 122 + if (ret) 123 + sas_printk("lldd disable phy%02d returned %d\n", 124 + phy->id, ret); 125 + } else 126 + sas_printk("phy%02d is not enabled, cannot shutdown\n", 127 + phy->id); 128 + } 129 + 110 130 /* ---------- Phy class registration ---------- */ 111 131 112 132 int sas_register_phys(struct sas_ha_struct *sas_ha) ··· 140 116 struct asd_sas_phy *phy = sas_ha->sas_phy[i]; 141 117 142 118 phy->error = 0; 119 + atomic_set(&phy->event_nr, 0); 143 120 INIT_LIST_HEAD(&phy->port_phy_el); 144 121 145 122 phy->port = NULL; ··· 176 151 [PHYE_OOB_ERROR] = sas_phye_oob_error, 177 152 [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, 178 153 [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, 179 - 154 + [PHYE_SHUTDOWN] = sas_phye_shutdown, 180 155 };
+6
include/scsi/libsas.h
··· 75 75 PHYE_OOB_ERROR, 76 76 PHYE_SPINUP_HOLD, /* hot plug SATA, no COMWAKE sent */ 77 77 PHYE_RESUME_TIMEOUT, 78 + PHYE_SHUTDOWN, 78 79 PHY_NUM_EVENTS, 79 80 }; 80 81 ··· 312 311 ev->event = event; 313 312 } 314 313 314 + #define SAS_PHY_SHUTDOWN_THRES 1024 315 315 316 316 /* The phy pretty much is controlled by the LLDD. 317 317 * The class only reads those fields. 318 318 */ 319 319 struct asd_sas_phy { 320 320 /* private: */ 321 + atomic_t event_nr; 322 + int in_shutdown; 321 323 int error; 322 324 int suspended; 323 325 ··· 408 404 409 405 struct list_head eh_done_q; /* complete via scsi_eh_flush_done_q */ 410 406 struct list_head eh_ata_q; /* scmds to promote from sas to ata eh */ 407 + 408 + int event_thres; 411 409 }; 412 410 413 411 #define SHOST_TO_SAS_HA(_shost) (*(struct sas_ha_struct **)(_shost)->hostdata)