diff options
author | Vivek Gautam <vivek.gautam@codeaurora.org> | 2018-08-07 13:47:39 -0400 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2018-09-17 02:04:14 -0400 |
commit | eebcc19646489b68399ce7b35d9c38eb9f4ec40f (patch) | |
tree | 9ea236c75b9b5d833db1cc20e157f9f595fef900 | |
parent | 94e989dee2b730e8e3c3d5b71ce54f93dce7b62e (diff) |
scsi: ufshcd: Fix NULL pointer dereference for in ufshcd_init
Error paths in ufshcd_init() ufshcd_hba_exit() killed clk_scaling workqueue
when the workqueue is actually created quite late in ufshcd_init(). So, we
end up getting NULL pointer dereference in such error paths. Fix this by
moving clk_scaling initialization and kill codes to two separate methods, and
call them at required places.
Fixes: 401f1e4490ee ("scsi: ufs: don't suspend clock scaling during clock
gating")
Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Subhash Jadavani <subhashj@codeaurora.org>
Cc: Matthias Kaehlcke <mka@chromium.org>
Cc: Evan Green <evgreen@chromium.org>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Evan Green <evgreen@chromium.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 53 |
1 files changed, 34 insertions, 19 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9d5d2ca7fc4f..533886233649 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c | |||
@@ -1763,6 +1763,34 @@ out: | |||
1763 | return count; | 1763 | return count; |
1764 | } | 1764 | } |
1765 | 1765 | ||
1766 | static void ufshcd_init_clk_scaling(struct ufs_hba *hba) | ||
1767 | { | ||
1768 | char wq_name[sizeof("ufs_clkscaling_00")]; | ||
1769 | |||
1770 | if (!ufshcd_is_clkscaling_supported(hba)) | ||
1771 | return; | ||
1772 | |||
1773 | INIT_WORK(&hba->clk_scaling.suspend_work, | ||
1774 | ufshcd_clk_scaling_suspend_work); | ||
1775 | INIT_WORK(&hba->clk_scaling.resume_work, | ||
1776 | ufshcd_clk_scaling_resume_work); | ||
1777 | |||
1778 | snprintf(wq_name, sizeof(wq_name), "ufs_clkscaling_%d", | ||
1779 | hba->host->host_no); | ||
1780 | hba->clk_scaling.workq = create_singlethread_workqueue(wq_name); | ||
1781 | |||
1782 | ufshcd_clkscaling_init_sysfs(hba); | ||
1783 | } | ||
1784 | |||
1785 | static void ufshcd_exit_clk_scaling(struct ufs_hba *hba) | ||
1786 | { | ||
1787 | if (!ufshcd_is_clkscaling_supported(hba)) | ||
1788 | return; | ||
1789 | |||
1790 | destroy_workqueue(hba->clk_scaling.workq); | ||
1791 | ufshcd_devfreq_remove(hba); | ||
1792 | } | ||
1793 | |||
1766 | static void ufshcd_init_clk_gating(struct ufs_hba *hba) | 1794 | static void ufshcd_init_clk_gating(struct ufs_hba *hba) |
1767 | { | 1795 | { |
1768 | char wq_name[sizeof("ufs_clk_gating_00")]; | 1796 | char wq_name[sizeof("ufs_clk_gating_00")]; |
@@ -6666,6 +6694,7 @@ out: | |||
6666 | */ | 6694 | */ |
6667 | if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) { | 6695 | if (ret && !ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) { |
6668 | pm_runtime_put_sync(hba->dev); | 6696 | pm_runtime_put_sync(hba->dev); |
6697 | ufshcd_exit_clk_scaling(hba); | ||
6669 | ufshcd_hba_exit(hba); | 6698 | ufshcd_hba_exit(hba); |
6670 | } | 6699 | } |
6671 | 6700 | ||
@@ -7201,12 +7230,9 @@ static void ufshcd_hba_exit(struct ufs_hba *hba) | |||
7201 | ufshcd_variant_hba_exit(hba); | 7230 | ufshcd_variant_hba_exit(hba); |
7202 | ufshcd_setup_vreg(hba, false); | 7231 | ufshcd_setup_vreg(hba, false); |
7203 | ufshcd_suspend_clkscaling(hba); | 7232 | ufshcd_suspend_clkscaling(hba); |
7204 | if (ufshcd_is_clkscaling_supported(hba)) { | 7233 | if (ufshcd_is_clkscaling_supported(hba)) |
7205 | if (hba->devfreq) | 7234 | if (hba->devfreq) |
7206 | ufshcd_suspend_clkscaling(hba); | 7235 | ufshcd_suspend_clkscaling(hba); |
7207 | destroy_workqueue(hba->clk_scaling.workq); | ||
7208 | ufshcd_devfreq_remove(hba); | ||
7209 | } | ||
7210 | ufshcd_setup_clocks(hba, false); | 7236 | ufshcd_setup_clocks(hba, false); |
7211 | ufshcd_setup_hba_vreg(hba, false); | 7237 | ufshcd_setup_hba_vreg(hba, false); |
7212 | hba->is_powered = false; | 7238 | hba->is_powered = false; |
@@ -7881,6 +7907,7 @@ void ufshcd_remove(struct ufs_hba *hba) | |||
7881 | ufshcd_disable_intr(hba, hba->intr_mask); | 7907 | ufshcd_disable_intr(hba, hba->intr_mask); |
7882 | ufshcd_hba_stop(hba, true); | 7908 | ufshcd_hba_stop(hba, true); |
7883 | 7909 | ||
7910 | ufshcd_exit_clk_scaling(hba); | ||
7884 | ufshcd_exit_clk_gating(hba); | 7911 | ufshcd_exit_clk_gating(hba); |
7885 | if (ufshcd_is_clkscaling_supported(hba)) | 7912 | if (ufshcd_is_clkscaling_supported(hba)) |
7886 | device_remove_file(hba->dev, &hba->clk_scaling.enable_attr); | 7913 | device_remove_file(hba->dev, &hba->clk_scaling.enable_attr); |
@@ -8045,6 +8072,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) | |||
8045 | 8072 | ||
8046 | ufshcd_init_clk_gating(hba); | 8073 | ufshcd_init_clk_gating(hba); |
8047 | 8074 | ||
8075 | ufshcd_init_clk_scaling(hba); | ||
8076 | |||
8048 | /* | 8077 | /* |
8049 | * In order to avoid any spurious interrupt immediately after | 8078 | * In order to avoid any spurious interrupt immediately after |
8050 | * registering UFS controller interrupt handler, clear any pending UFS | 8079 | * registering UFS controller interrupt handler, clear any pending UFS |
@@ -8083,21 +8112,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) | |||
8083 | goto out_remove_scsi_host; | 8112 | goto out_remove_scsi_host; |
8084 | } | 8113 | } |
8085 | 8114 | ||
8086 | if (ufshcd_is_clkscaling_supported(hba)) { | ||
8087 | char wq_name[sizeof("ufs_clkscaling_00")]; | ||
8088 | |||
8089 | INIT_WORK(&hba->clk_scaling.suspend_work, | ||
8090 | ufshcd_clk_scaling_suspend_work); | ||
8091 | INIT_WORK(&hba->clk_scaling.resume_work, | ||
8092 | ufshcd_clk_scaling_resume_work); | ||
8093 | |||
8094 | snprintf(wq_name, sizeof(wq_name), "ufs_clkscaling_%d", | ||
8095 | host->host_no); | ||
8096 | hba->clk_scaling.workq = create_singlethread_workqueue(wq_name); | ||
8097 | |||
8098 | ufshcd_clkscaling_init_sysfs(hba); | ||
8099 | } | ||
8100 | |||
8101 | /* | 8115 | /* |
8102 | * Set the default power management level for runtime and system PM. | 8116 | * Set the default power management level for runtime and system PM. |
8103 | * Default power saving mode is to keep UFS link in Hibern8 state | 8117 | * Default power saving mode is to keep UFS link in Hibern8 state |
@@ -8135,6 +8149,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) | |||
8135 | out_remove_scsi_host: | 8149 | out_remove_scsi_host: |
8136 | scsi_remove_host(hba->host); | 8150 | scsi_remove_host(hba->host); |
8137 | exit_gating: | 8151 | exit_gating: |
8152 | ufshcd_exit_clk_scaling(hba); | ||
8138 | ufshcd_exit_clk_gating(hba); | 8153 | ufshcd_exit_clk_gating(hba); |
8139 | out_disable: | 8154 | out_disable: |
8140 | hba->is_irq_enabled = false; | 8155 | hba->is_irq_enabled = false; |