From c875dae34a91e5fc4433483599a76417daba37f8 Mon Sep 17 00:00:00 2001 From: Martin Liu Date: Mon, 22 Jul 2019 14:04:05 +0800 Subject: [PATCH] scsi: ufs: use async operation for hibern8 operation When enabling/disabling hibern8, we will call flush_work to wait for the task finished. Using async operation to avoid long waiting time. Bug: 138085490 Test: Boot, cat /sys/kernel/debug/ufshcd0/show_hba and check hibern8_exit_cnt Signed-off-by: Martin Liu Signed-off-by: Alexander Winkowski Change-Id: I427cd8e40cbc6674a145142babbd92a1062dc511 --- drivers/scsi/ufs/ufshcd.c | 39 ++++++++++++++++++++++++++++++--------- drivers/scsi/ufs/ufshcd.h | 3 +++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 0d536038de25..02d98153d96a 100755 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3350,19 +3350,15 @@ static ssize_t ufshcd_hibern8_on_idle_enable_show(struct device *dev, hba->hibern8_on_idle.is_enabled); } -static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + +static void ufshcd_hibern8_on_idle_switch_work(struct work_struct *work) { - struct ufs_hba *hba = dev_get_drvdata(dev); + struct ufs_hba *hba; unsigned long flags; u32 value; - if (kstrtou32(buf, 0, &value)) - return -EINVAL; - - value = !!value; - if (value == hba->hibern8_on_idle.is_enabled) - goto out; + hba = container_of(work, struct ufs_hba, hibern8_on_idle_enable_work); + value = !hba->hibern8_on_idle.is_enabled; /* Update auto hibern8 timer value if supported */ if (ufshcd_is_auto_hibern8_supported(hba)) { @@ -3386,6 +3382,28 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev, hba->hibern8_on_idle.is_enabled = value; out: + return; +} + +static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 value; + + if (kstrtou32(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&hba->hibern8_on_idle.enable_mutex); + /* + * make sure that former operation has been done before we exectue + * next action. This could gareatee the order and synconization. + */ + flush_work(&hba->hibern8_on_idle_enable_work); + if (hba->hibern8_on_idle.is_enabled != !!value) + schedule_work(&hba->hibern8_on_idle_enable_work); + mutex_unlock(&hba->hibern8_on_idle.enable_mutex); + return count; } @@ -12954,12 +12972,15 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) INIT_WORK(&hba->card_detect_work, ufshcd_card_detect_handler); INIT_WORK(&hba->rls_work, ufshcd_rls_handler); INIT_WORK(&hba->fatal_mode_work, ufshcd_fatal_mode_handler); + INIT_WORK(&hba->hibern8_on_idle_enable_work, + ufshcd_hibern8_on_idle_switch_work); /* Initialize UIC command mutex */ mutex_init(&hba->uic_cmd_mutex); /* Initialize mutex for device management commands */ mutex_init(&hba->dev_cmd.lock); + mutex_init(&hba->hibern8_on_idle.enable_mutex); #if IS_ENABLED(CONFIG_BLK_TURBO_WRITE) /* Initialize TW ctrl mutex */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 88f841c6c99e..b51d9ed67f0e 100755 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -520,6 +520,7 @@ enum ufshcd_hibern8_on_idle_state { * @delay_attr: sysfs attribute to control delay_attr * @enable_attr: sysfs attribute to enable/disable hibern8 on idle * @is_enabled: Indicates the current status of hibern8 + * @enable_mutex: protect sys node race from multithread access */ struct ufs_hibern8_on_idle { struct delayed_work enter_work; @@ -531,6 +532,7 @@ struct ufs_hibern8_on_idle { struct device_attribute delay_attr; struct device_attribute enable_attr; bool is_enabled; + struct mutex enable_mutex; }; /** @@ -1079,6 +1081,7 @@ struct ufs_hba { struct work_struct eh_work; struct work_struct eeh_work; struct work_struct rls_work; + struct work_struct hibern8_on_idle_enable_work; /* HBA Errors */ u32 errors;