summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSreekanth Reddy <sreekanth.reddy@avagotech.com>2015-11-11 07:00:31 -0500
committerMartin K. Petersen <martin.petersen@oracle.com>2015-11-11 19:01:11 -0500
commit146b16c8071f5f6c67895d15beeee1163f5107c4 (patch)
tree468dd22608c91e18f91f5d81b382fedf50123715
parentd1cb5e495e5384f6d90a8573f1be9cc79b85c862 (diff)
mpt3sas: Refcount fw_events and fix unsafe list usage
The fw_event_work struct is concurrently referenced at shutdown. Add a refcount to protect it and refactor the code to use it. Additionally, refactor _scsih_fw_event_cleanup_queue() such that it no longer iterates over the list without holding the lock since _firmware_event_work() concurrently deletes items from the list. This patch is ported from commit 008549f6e8a1 ("mpt2sas: Refcount fw_events and fix unsafe list usage"). These changes are also required for mpt3sas. Signed-off-by: Sreekanth Reddy <Sreekanth.Reddy@avagotech.com> Acked-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c116
1 files changed, 94 insertions, 22 deletions
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 5dbf214ecf6d..436e65e513ae 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -200,9 +200,37 @@ struct fw_event_work {
200 u8 VP_ID; 200 u8 VP_ID;
201 u8 ignore; 201 u8 ignore;
202 u16 event; 202 u16 event;
203 struct kref refcount;
203 char event_data[0] __aligned(4); 204 char event_data[0] __aligned(4);
204}; 205};
205 206
207static void fw_event_work_free(struct kref *r)
208{
209 kfree(container_of(r, struct fw_event_work, refcount));
210}
211
212static void fw_event_work_get(struct fw_event_work *fw_work)
213{
214 kref_get(&fw_work->refcount);
215}
216
217static void fw_event_work_put(struct fw_event_work *fw_work)
218{
219 kref_put(&fw_work->refcount, fw_event_work_free);
220}
221
222static struct fw_event_work *alloc_fw_event_work(int len)
223{
224 struct fw_event_work *fw_event;
225
226 fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC);
227 if (!fw_event)
228 return NULL;
229
230 kref_init(&fw_event->refcount);
231 return fw_event;
232}
233
206/** 234/**
207 * struct _scsi_io_transfer - scsi io transfer 235 * struct _scsi_io_transfer - scsi io transfer
208 * @handle: sas device handle (assigned by firmware) 236 * @handle: sas device handle (assigned by firmware)
@@ -2598,32 +2626,36 @@ _scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
2598 return; 2626 return;
2599 2627
2600 spin_lock_irqsave(&ioc->fw_event_lock, flags); 2628 spin_lock_irqsave(&ioc->fw_event_lock, flags);
2629 fw_event_work_get(fw_event);
2601 INIT_LIST_HEAD(&fw_event->list); 2630 INIT_LIST_HEAD(&fw_event->list);
2602 list_add_tail(&fw_event->list, &ioc->fw_event_list); 2631 list_add_tail(&fw_event->list, &ioc->fw_event_list);
2603 INIT_WORK(&fw_event->work, _firmware_event_work); 2632 INIT_WORK(&fw_event->work, _firmware_event_work);
2633 fw_event_work_get(fw_event);
2604 queue_work(ioc->firmware_event_thread, &fw_event->work); 2634 queue_work(ioc->firmware_event_thread, &fw_event->work);
2605 spin_unlock_irqrestore(&ioc->fw_event_lock, flags); 2635 spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
2606} 2636}
2607 2637
2608/** 2638/**
2609 * _scsih_fw_event_free - delete fw_event 2639 * _scsih_fw_event_del_from_list - delete fw_event from the list
2610 * @ioc: per adapter object 2640 * @ioc: per adapter object
2611 * @fw_event: object describing the event 2641 * @fw_event: object describing the event
2612 * Context: This function will acquire ioc->fw_event_lock. 2642 * Context: This function will acquire ioc->fw_event_lock.
2613 * 2643 *
2614 * This removes firmware event object from link list, frees associated memory. 2644 * If the fw_event is on the fw_event_list, remove it and do a put.
2615 * 2645 *
2616 * Return nothing. 2646 * Return nothing.
2617 */ 2647 */
2618static void 2648static void
2619_scsih_fw_event_free(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work 2649_scsih_fw_event_del_from_list(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work
2620 *fw_event) 2650 *fw_event)
2621{ 2651{
2622 unsigned long flags; 2652 unsigned long flags;
2623 2653
2624 spin_lock_irqsave(&ioc->fw_event_lock, flags); 2654 spin_lock_irqsave(&ioc->fw_event_lock, flags);
2625 list_del(&fw_event->list); 2655 if (!list_empty(&fw_event->list)) {
2626 kfree(fw_event); 2656 list_del_init(&fw_event->list);
2657 fw_event_work_put(fw_event);
2658 }
2627 spin_unlock_irqrestore(&ioc->fw_event_lock, flags); 2659 spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
2628} 2660}
2629 2661
@@ -2640,17 +2672,19 @@ mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc,
2640 struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) 2672 struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data)
2641{ 2673{
2642 struct fw_event_work *fw_event; 2674 struct fw_event_work *fw_event;
2675 u16 sz;
2643 2676
2644 if (ioc->is_driver_loading) 2677 if (ioc->is_driver_loading)
2645 return; 2678 return;
2646 fw_event = kzalloc(sizeof(*fw_event) + sizeof(*event_data), 2679 sz = sizeof(*event_data);
2647 GFP_ATOMIC); 2680 fw_event = alloc_fw_event_work(sz);
2648 if (!fw_event) 2681 if (!fw_event)
2649 return; 2682 return;
2650 fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG; 2683 fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG;
2651 fw_event->ioc = ioc; 2684 fw_event->ioc = ioc;
2652 memcpy(fw_event->event_data, event_data, sizeof(*event_data)); 2685 memcpy(fw_event->event_data, event_data, sizeof(*event_data));
2653 _scsih_fw_event_add(ioc, fw_event); 2686 _scsih_fw_event_add(ioc, fw_event);
2687 fw_event_work_put(fw_event);
2654} 2688}
2655 2689
2656/** 2690/**
@@ -2666,12 +2700,13 @@ _scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc)
2666 2700
2667 if (ioc->is_driver_loading) 2701 if (ioc->is_driver_loading)
2668 return; 2702 return;
2669 fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); 2703 fw_event = alloc_fw_event_work(0);
2670 if (!fw_event) 2704 if (!fw_event)
2671 return; 2705 return;
2672 fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES; 2706 fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES;
2673 fw_event->ioc = ioc; 2707 fw_event->ioc = ioc;
2674 _scsih_fw_event_add(ioc, fw_event); 2708 _scsih_fw_event_add(ioc, fw_event);
2709 fw_event_work_put(fw_event);
2675} 2710}
2676 2711
2677/** 2712/**
@@ -2685,12 +2720,29 @@ mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc)
2685{ 2720{
2686 struct fw_event_work *fw_event; 2721 struct fw_event_work *fw_event;
2687 2722
2688 fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); 2723 fw_event = alloc_fw_event_work(0);
2689 if (!fw_event) 2724 if (!fw_event)
2690 return; 2725 return;
2691 fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE; 2726 fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE;
2692 fw_event->ioc = ioc; 2727 fw_event->ioc = ioc;
2693 _scsih_fw_event_add(ioc, fw_event); 2728 _scsih_fw_event_add(ioc, fw_event);
2729 fw_event_work_put(fw_event);
2730}
2731
2732static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc)
2733{
2734 unsigned long flags;
2735 struct fw_event_work *fw_event = NULL;
2736
2737 spin_lock_irqsave(&ioc->fw_event_lock, flags);
2738 if (!list_empty(&ioc->fw_event_list)) {
2739 fw_event = list_first_entry(&ioc->fw_event_list,
2740 struct fw_event_work, list);
2741 list_del_init(&fw_event->list);
2742 }
2743 spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
2744
2745 return fw_event;
2694} 2746}
2695 2747
2696/** 2748/**
@@ -2705,17 +2757,25 @@ mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc)
2705static void 2757static void
2706_scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc) 2758_scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc)
2707{ 2759{
2708 struct fw_event_work *fw_event, *next; 2760 struct fw_event_work *fw_event;
2709 2761
2710 if (list_empty(&ioc->fw_event_list) || 2762 if (list_empty(&ioc->fw_event_list) ||
2711 !ioc->firmware_event_thread || in_interrupt()) 2763 !ioc->firmware_event_thread || in_interrupt())
2712 return; 2764 return;
2713 2765
2714 list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { 2766 while ((fw_event = dequeue_next_fw_event(ioc))) {
2715 if (cancel_delayed_work_sync(&fw_event->delayed_work)) { 2767 /*
2716 _scsih_fw_event_free(ioc, fw_event); 2768 * Wait on the fw_event to complete. If this returns 1, then
2717 continue; 2769 * the event was never executed, and we need a put for the
2718 } 2770 * reference the delayed_work had on the fw_event.
2771 *
2772 * If it did execute, we wait for it to finish, and the put will
2773 * happen from _firmware_event_work()
2774 */
2775 if (cancel_delayed_work_sync(&fw_event->delayed_work))
2776 fw_event_work_put(fw_event);
2777
2778 fw_event_work_put(fw_event);
2719 } 2779 }
2720} 2780}
2721 2781
@@ -4242,13 +4302,14 @@ _scsih_send_event_to_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle)
4242{ 4302{
4243 struct fw_event_work *fw_event; 4303 struct fw_event_work *fw_event;
4244 4304
4245 fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); 4305 fw_event = alloc_fw_event_work(0);
4246 if (!fw_event) 4306 if (!fw_event)
4247 return; 4307 return;
4248 fw_event->event = MPT3SAS_TURN_ON_PFA_LED; 4308 fw_event->event = MPT3SAS_TURN_ON_PFA_LED;
4249 fw_event->device_handle = handle; 4309 fw_event->device_handle = handle;
4250 fw_event->ioc = ioc; 4310 fw_event->ioc = ioc;
4251 _scsih_fw_event_add(ioc, fw_event); 4311 _scsih_fw_event_add(ioc, fw_event);
4312 fw_event_work_put(fw_event);
4252} 4313}
4253 4314
4254/** 4315/**
@@ -7498,10 +7559,11 @@ mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase)
7498static void 7559static void
7499_mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) 7560_mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
7500{ 7561{
7562 _scsih_fw_event_del_from_list(ioc, fw_event);
7563
7501 /* the queue is being flushed so ignore this event */ 7564 /* the queue is being flushed so ignore this event */
7502 if (ioc->remove_host || 7565 if (ioc->remove_host || ioc->pci_error_recovery) {
7503 ioc->pci_error_recovery) { 7566 fw_event_work_put(fw_event);
7504 _scsih_fw_event_free(ioc, fw_event);
7505 return; 7567 return;
7506 } 7568 }
7507 7569
@@ -7512,8 +7574,16 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
7512 fw_event->event_data); 7574 fw_event->event_data);
7513 break; 7575 break;
7514 case MPT3SAS_REMOVE_UNRESPONDING_DEVICES: 7576 case MPT3SAS_REMOVE_UNRESPONDING_DEVICES:
7515 while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery) 7577 while (scsi_host_in_recovery(ioc->shost) ||
7578 ioc->shost_recovery) {
7579 /*
7580 * If we're unloading, bail. Otherwise, this can become
7581 * an infinite loop.
7582 */
7583 if (ioc->remove_host)
7584 goto out;
7516 ssleep(1); 7585 ssleep(1);
7586 }
7517 _scsih_remove_unresponding_sas_devices(ioc); 7587 _scsih_remove_unresponding_sas_devices(ioc);
7518 _scsih_scan_for_devices_after_reset(ioc); 7588 _scsih_scan_for_devices_after_reset(ioc);
7519 break; 7589 break;
@@ -7558,7 +7628,8 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
7558 _scsih_sas_ir_operation_status_event(ioc, fw_event); 7628 _scsih_sas_ir_operation_status_event(ioc, fw_event);
7559 break; 7629 break;
7560 } 7630 }
7561 _scsih_fw_event_free(ioc, fw_event); 7631out:
7632 fw_event_work_put(fw_event);
7562} 7633}
7563 7634
7564/** 7635/**
@@ -7720,7 +7791,7 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
7720 } 7791 }
7721 7792
7722 sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; 7793 sz = le16_to_cpu(mpi_reply->EventDataLength) * 4;
7723 fw_event = kzalloc(sizeof(*fw_event) + sz, GFP_ATOMIC); 7794 fw_event = alloc_fw_event_work(sz);
7724 if (!fw_event) { 7795 if (!fw_event) {
7725 pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", 7796 pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
7726 ioc->name, __FILE__, __LINE__, __func__); 7797 ioc->name, __FILE__, __LINE__, __func__);
@@ -7733,6 +7804,7 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index,
7733 fw_event->VP_ID = mpi_reply->VP_ID; 7804 fw_event->VP_ID = mpi_reply->VP_ID;
7734 fw_event->event = event; 7805 fw_event->event = event;
7735 _scsih_fw_event_add(ioc, fw_event); 7806 _scsih_fw_event_add(ioc, fw_event);
7807 fw_event_work_put(fw_event);
7736 return 1; 7808 return 1;
7737} 7809}
7738 7810