2018-02-15 20:14:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
// Copyright (C) 2018 Western Digital Corporation
|
|
|
|
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/string.h>
|
2018-03-20 21:07:38 +08:00
|
|
|
#include <linux/bitfield.h>
|
2018-02-15 20:14:02 +08:00
|
|
|
#include <asm/unaligned.h>
|
2018-02-15 20:14:01 +08:00
|
|
|
|
2018-02-15 20:14:02 +08:00
|
|
|
#include "ufs.h"
|
2018-02-15 20:14:01 +08:00
|
|
|
#include "ufs-sysfs.h"
|
|
|
|
|
2021-04-27 15:38:42 +08:00
|
|
|
static const char *ufshcd_uic_link_state_to_string(
|
2018-02-15 20:14:01 +08:00
|
|
|
enum uic_link_state state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case UIC_LINK_OFF_STATE: return "OFF";
|
|
|
|
case UIC_LINK_ACTIVE_STATE: return "ACTIVE";
|
|
|
|
case UIC_LINK_HIBERN8_STATE: return "HIBERN8";
|
scsi: ufs: Fix concurrency of error handler and other error recovery paths
Error recovery can be invoked from multiple code paths, including hibern8
enter/exit (from ufshcd_link_recovery), ufshcd_eh_host_reset_handler() and
eh_work scheduled from IRQ context. Ultimately, these paths are all trying
to invoke ufshcd_reset_and_restore() in either a synchronous or
asynchronous manner. This causes problems:
- If link recovery happens during ungate work, ufshcd_hold() would be
called recursively. Although commit 53c12d0ef6fc ("scsi: ufs: fix error
recovery after the hibern8 exit failure") fixed a deadlock due to
recursive calls of ufshcd_hold() by adding a check of eh_in_progress
into ufshcd_hold, this check allows eh_work to run in parallel while
link recovery is running.
- Similar concurrency can also happen when error recovery is invoked from
ufshcd_eh_host_reset_handler and ufshcd_link_recovery.
- Concurrency can even happen between eh_works. eh_work, currently queued
on system_wq, is allowed to have multiple instances running in parallel,
but we don't have proper protection for that.
If any of above concurrency scenarios happen, error recovery would fail and
lead ufs device and host into bad states. To fix the concurrency problem,
this change queues eh_work on a single threaded workqueue and removes link
recovery calls from the hibern8 enter/exit path. In addition, make use of
eh_work in eh_host_reset_handler instead of calling
ufshcd_reset_and_restore. This unifies the UFS error recovery mechanism.
According to the UFSHCI JEDEC spec, hibern8 enter/exit error occurs when
the link is broken. This essentially applies to any power mode change
operations (since they all use PACP_PWR cmds in UniPro layer). So, if a
power mode change operation (including AH8 enter/exit) fails, mark link
state as UIC_LINK_BROKEN_STATE and schedule the eh_work. In this case,
error handler needs to do a full reset and restore to recover the link back
to active. Before the link state is recovered to active,
ufshcd_uic_pwr_ctrl simply returns -ENOLINK to avoid more errors.
Link: https://lore.kernel.org/r/1596975355-39813-6-git-send-email-cang@codeaurora.org
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Asutosh Das <asutoshd@codeaurora.org>
Signed-off-by: Can Guo <cang@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2020-08-09 20:15:51 +08:00
|
|
|
case UIC_LINK_BROKEN_STATE: return "BROKEN";
|
2018-02-15 20:14:01 +08:00
|
|
|
default: return "UNKNOWN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-27 15:38:42 +08:00
|
|
|
static const char *ufshcd_ufs_dev_pwr_mode_to_string(
|
2018-02-15 20:14:01 +08:00
|
|
|
enum ufs_dev_pwr_mode state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case UFS_ACTIVE_PWR_MODE: return "ACTIVE";
|
|
|
|
case UFS_SLEEP_PWR_MODE: return "SLEEP";
|
|
|
|
case UFS_POWERDOWN_PWR_MODE: return "POWERDOWN";
|
2020-11-03 22:14:02 +08:00
|
|
|
case UFS_DEEPSLEEP_PWR_MODE: return "DEEPSLEEP";
|
2018-02-15 20:14:01 +08:00
|
|
|
default: return "UNKNOWN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline ssize_t ufs_sysfs_pm_lvl_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count,
|
|
|
|
bool rpm)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
2020-11-03 22:14:02 +08:00
|
|
|
struct ufs_dev_info *dev_info = &hba->dev_info;
|
2018-02-15 20:14:01 +08:00
|
|
|
unsigned long flags, value;
|
|
|
|
|
|
|
|
if (kstrtoul(buf, 0, &value))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (value >= UFS_PM_LVL_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2020-11-03 22:14:02 +08:00
|
|
|
if (ufs_pm_lvl_states[value].dev_state == UFS_DEEPSLEEP_PWR_MODE &&
|
|
|
|
(!(hba->caps & UFSHCD_CAP_DEEPSLEEP) ||
|
|
|
|
!(dev_info->wspecversion >= 0x310)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-02-15 20:14:01 +08:00
|
|
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
|
|
|
if (rpm)
|
|
|
|
hba->rpm_lvl = value;
|
|
|
|
else
|
|
|
|
hba->spm_lvl = value;
|
|
|
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t rpm_lvl_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
2018-03-01 18:48:06 +08:00
|
|
|
|
2021-01-07 05:15:41 +08:00
|
|
|
return sysfs_emit(buf, "%d\n", hba->rpm_lvl);
|
2018-02-15 20:14:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t rpm_lvl_store(struct device *dev,
|
|
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
return ufs_sysfs_pm_lvl_store(dev, attr, buf, count, true);
|
|
|
|
}
|
|
|
|
|
2018-03-01 18:48:06 +08:00
|
|
|
static ssize_t rpm_target_dev_state_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
2021-04-27 15:38:42 +08:00
|
|
|
return sysfs_emit(buf, "%s\n", ufshcd_ufs_dev_pwr_mode_to_string(
|
2018-03-01 18:48:06 +08:00
|
|
|
ufs_pm_lvl_states[hba->rpm_lvl].dev_state));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t rpm_target_link_state_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
2021-04-27 15:38:42 +08:00
|
|
|
return sysfs_emit(buf, "%s\n", ufshcd_uic_link_state_to_string(
|
2018-03-01 18:48:06 +08:00
|
|
|
ufs_pm_lvl_states[hba->rpm_lvl].link_state));
|
|
|
|
}
|
|
|
|
|
2018-02-15 20:14:01 +08:00
|
|
|
static ssize_t spm_lvl_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
2021-01-07 05:15:41 +08:00
|
|
|
return sysfs_emit(buf, "%d\n", hba->spm_lvl);
|
2018-02-15 20:14:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t spm_lvl_store(struct device *dev,
|
|
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
return ufs_sysfs_pm_lvl_store(dev, attr, buf, count, false);
|
|
|
|
}
|
|
|
|
|
2018-03-01 18:48:06 +08:00
|
|
|
static ssize_t spm_target_dev_state_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
2021-04-27 15:38:42 +08:00
|
|
|
return sysfs_emit(buf, "%s\n", ufshcd_ufs_dev_pwr_mode_to_string(
|
2018-03-01 18:48:06 +08:00
|
|
|
ufs_pm_lvl_states[hba->spm_lvl].dev_state));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t spm_target_link_state_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
2021-04-27 15:38:42 +08:00
|
|
|
return sysfs_emit(buf, "%s\n", ufshcd_uic_link_state_to_string(
|
2018-03-01 18:48:06 +08:00
|
|
|
ufs_pm_lvl_states[hba->spm_lvl].link_state));
|
|
|
|
}
|
|
|
|
|
2018-03-20 21:07:38 +08:00
|
|
|
/* Convert Auto-Hibernate Idle Timer register value to microseconds */
|
|
|
|
static int ufshcd_ahit_to_us(u32 ahit)
|
|
|
|
{
|
|
|
|
int timer = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK, ahit);
|
|
|
|
int scale = FIELD_GET(UFSHCI_AHIBERN8_SCALE_MASK, ahit);
|
|
|
|
|
|
|
|
for (; scale > 0; --scale)
|
|
|
|
timer *= UFSHCI_AHIBERN8_SCALE_FACTOR;
|
|
|
|
|
|
|
|
return timer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert microseconds to Auto-Hibernate Idle Timer register value */
|
|
|
|
static u32 ufshcd_us_to_ahit(unsigned int timer)
|
|
|
|
{
|
|
|
|
unsigned int scale;
|
|
|
|
|
|
|
|
for (scale = 0; timer > UFSHCI_AHIBERN8_TIMER_MASK; ++scale)
|
|
|
|
timer /= UFSHCI_AHIBERN8_SCALE_FACTOR;
|
|
|
|
|
|
|
|
return FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, timer) |
|
|
|
|
FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t auto_hibern8_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
2020-08-29 09:05:13 +08:00
|
|
|
u32 ahit;
|
2021-01-14 11:13:28 +08:00
|
|
|
int ret;
|
2018-03-20 21:07:38 +08:00
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
2019-05-21 14:44:52 +08:00
|
|
|
if (!ufshcd_is_auto_hibern8_supported(hba))
|
2018-03-20 21:07:38 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2021-01-14 11:13:28 +08:00
|
|
|
down(&hba->host_sem);
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2020-08-29 09:05:13 +08:00
|
|
|
pm_runtime_get_sync(hba->dev);
|
|
|
|
ufshcd_hold(hba, false);
|
|
|
|
ahit = ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
|
|
|
|
ufshcd_release(hba);
|
|
|
|
pm_runtime_put_sync(hba->dev);
|
|
|
|
|
2021-01-14 11:13:28 +08:00
|
|
|
ret = sysfs_emit(buf, "%d\n", ufshcd_ahit_to_us(ahit));
|
|
|
|
|
|
|
|
out:
|
|
|
|
up(&hba->host_sem);
|
|
|
|
return ret;
|
2018-03-20 21:07:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t auto_hibern8_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
unsigned int timer;
|
2021-01-14 11:13:28 +08:00
|
|
|
int ret = 0;
|
2018-03-20 21:07:38 +08:00
|
|
|
|
2019-05-21 14:44:52 +08:00
|
|
|
if (!ufshcd_is_auto_hibern8_supported(hba))
|
2018-03-20 21:07:38 +08:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (kstrtouint(buf, 0, &timer))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (timer > UFSHCI_AHIBERN8_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2021-01-14 11:13:28 +08:00
|
|
|
down(&hba->host_sem);
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-03-20 21:07:38 +08:00
|
|
|
ufshcd_auto_hibern8_update(hba, ufshcd_us_to_ahit(timer));
|
|
|
|
|
2021-01-14 11:13:28 +08:00
|
|
|
out:
|
|
|
|
up(&hba->host_sem);
|
|
|
|
return ret ? ret : count;
|
2018-03-20 21:07:38 +08:00
|
|
|
}
|
|
|
|
|
2021-01-20 00:38:42 +08:00
|
|
|
static ssize_t wb_on_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
2021-01-20 00:38:46 +08:00
|
|
|
return sysfs_emit(buf, "%d\n", hba->dev_info.wb_enabled);
|
2021-01-20 00:38:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wb_on_store(struct device *dev, struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
unsigned int wb_enable;
|
|
|
|
ssize_t res;
|
|
|
|
|
|
|
|
if (!ufshcd_is_wb_allowed(hba) || ufshcd_is_clkscaling_supported(hba)) {
|
|
|
|
/*
|
|
|
|
* If the platform supports UFSHCD_CAP_CLK_SCALING, turn WB
|
|
|
|
* on/off will be done while clock scaling up/down.
|
|
|
|
*/
|
|
|
|
dev_warn(dev, "To control WB through wb_on is not allowed!\n");
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kstrtouint(buf, 0, &wb_enable))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (wb_enable != 0 && wb_enable != 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
down(&hba->host_sem);
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) {
|
|
|
|
res = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_get_sync(hba);
|
2021-03-18 17:55:36 +08:00
|
|
|
res = ufshcd_wb_toggle(hba, wb_enable);
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_put_sync(hba);
|
2021-01-20 00:38:42 +08:00
|
|
|
out:
|
|
|
|
up(&hba->host_sem);
|
|
|
|
return res < 0 ? res : count;
|
|
|
|
}
|
|
|
|
|
2018-02-15 20:14:01 +08:00
|
|
|
static DEVICE_ATTR_RW(rpm_lvl);
|
2018-03-01 18:48:06 +08:00
|
|
|
static DEVICE_ATTR_RO(rpm_target_dev_state);
|
|
|
|
static DEVICE_ATTR_RO(rpm_target_link_state);
|
2018-02-15 20:14:01 +08:00
|
|
|
static DEVICE_ATTR_RW(spm_lvl);
|
2018-03-01 18:48:06 +08:00
|
|
|
static DEVICE_ATTR_RO(spm_target_dev_state);
|
|
|
|
static DEVICE_ATTR_RO(spm_target_link_state);
|
2018-03-20 21:07:38 +08:00
|
|
|
static DEVICE_ATTR_RW(auto_hibern8);
|
2021-01-20 00:38:42 +08:00
|
|
|
static DEVICE_ATTR_RW(wb_on);
|
2018-02-15 20:14:01 +08:00
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
|
|
|
|
&dev_attr_rpm_lvl.attr,
|
2018-03-01 18:48:06 +08:00
|
|
|
&dev_attr_rpm_target_dev_state.attr,
|
|
|
|
&dev_attr_rpm_target_link_state.attr,
|
2018-02-15 20:14:01 +08:00
|
|
|
&dev_attr_spm_lvl.attr,
|
2018-03-01 18:48:06 +08:00
|
|
|
&dev_attr_spm_target_dev_state.attr,
|
|
|
|
&dev_attr_spm_target_link_state.attr,
|
2018-03-20 21:07:38 +08:00
|
|
|
&dev_attr_auto_hibern8.attr,
|
2021-01-20 00:38:42 +08:00
|
|
|
&dev_attr_wb_on.attr,
|
2018-02-15 20:14:01 +08:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_default_group = {
|
|
|
|
.attrs = ufs_sysfs_ufshcd_attrs,
|
|
|
|
};
|
|
|
|
|
2021-04-22 10:28:39 +08:00
|
|
|
static ssize_t monitor_enable_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%d\n", hba->monitor.enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t monitor_enable_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
unsigned long value, flags;
|
|
|
|
|
|
|
|
if (kstrtoul(buf, 0, &value))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
value = !!value;
|
|
|
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
|
|
|
if (value == hba->monitor.enabled)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
memset(&hba->monitor, 0, sizeof(hba->monitor));
|
|
|
|
} else {
|
|
|
|
hba->monitor.enabled = true;
|
|
|
|
hba->monitor.enabled_ts = ktime_get();
|
|
|
|
}
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t monitor_chunk_size_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%lu\n", hba->monitor.chunk_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t monitor_chunk_size_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
unsigned long value, flags;
|
|
|
|
|
|
|
|
if (kstrtoul(buf, 0, &value))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
|
|
|
/* Only allow chunk size change when monitor is disabled */
|
|
|
|
if (!hba->monitor.enabled)
|
|
|
|
hba->monitor.chunk_size = value;
|
|
|
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_total_sectors_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%lu\n", hba->monitor.nr_sec_rw[READ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_total_busy_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.total_busy[READ]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_nr_requests_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%lu\n", hba->monitor.nr_req[READ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_req_latency_avg_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
struct ufs_hba_monitor *m = &hba->monitor;
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n", div_u64(ktime_to_us(m->lat_sum[READ]),
|
|
|
|
m->nr_req[READ]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_req_latency_max_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.lat_max[READ]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_req_latency_min_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.lat_min[READ]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t read_req_latency_sum_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.lat_sum[READ]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t write_total_sectors_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%lu\n", hba->monitor.nr_sec_rw[WRITE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t write_total_busy_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.total_busy[WRITE]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t write_nr_requests_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%lu\n", hba->monitor.nr_req[WRITE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t write_req_latency_avg_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
struct ufs_hba_monitor *m = &hba->monitor;
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n", div_u64(ktime_to_us(m->lat_sum[WRITE]),
|
|
|
|
m->nr_req[WRITE]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t write_req_latency_max_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.lat_max[WRITE]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t write_req_latency_min_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.lat_min[WRITE]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t write_req_latency_sum_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
return sysfs_emit(buf, "%llu\n",
|
|
|
|
ktime_to_us(hba->monitor.lat_sum[WRITE]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR_RW(monitor_enable);
|
|
|
|
static DEVICE_ATTR_RW(monitor_chunk_size);
|
|
|
|
static DEVICE_ATTR_RO(read_total_sectors);
|
|
|
|
static DEVICE_ATTR_RO(read_total_busy);
|
|
|
|
static DEVICE_ATTR_RO(read_nr_requests);
|
|
|
|
static DEVICE_ATTR_RO(read_req_latency_avg);
|
|
|
|
static DEVICE_ATTR_RO(read_req_latency_max);
|
|
|
|
static DEVICE_ATTR_RO(read_req_latency_min);
|
|
|
|
static DEVICE_ATTR_RO(read_req_latency_sum);
|
|
|
|
static DEVICE_ATTR_RO(write_total_sectors);
|
|
|
|
static DEVICE_ATTR_RO(write_total_busy);
|
|
|
|
static DEVICE_ATTR_RO(write_nr_requests);
|
|
|
|
static DEVICE_ATTR_RO(write_req_latency_avg);
|
|
|
|
static DEVICE_ATTR_RO(write_req_latency_max);
|
|
|
|
static DEVICE_ATTR_RO(write_req_latency_min);
|
|
|
|
static DEVICE_ATTR_RO(write_req_latency_sum);
|
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_monitor_attrs[] = {
|
|
|
|
&dev_attr_monitor_enable.attr,
|
|
|
|
&dev_attr_monitor_chunk_size.attr,
|
|
|
|
&dev_attr_read_total_sectors.attr,
|
|
|
|
&dev_attr_read_total_busy.attr,
|
|
|
|
&dev_attr_read_nr_requests.attr,
|
|
|
|
&dev_attr_read_req_latency_avg.attr,
|
|
|
|
&dev_attr_read_req_latency_max.attr,
|
|
|
|
&dev_attr_read_req_latency_min.attr,
|
|
|
|
&dev_attr_read_req_latency_sum.attr,
|
|
|
|
&dev_attr_write_total_sectors.attr,
|
|
|
|
&dev_attr_write_total_busy.attr,
|
|
|
|
&dev_attr_write_nr_requests.attr,
|
|
|
|
&dev_attr_write_req_latency_avg.attr,
|
|
|
|
&dev_attr_write_req_latency_max.attr,
|
|
|
|
&dev_attr_write_req_latency_min.attr,
|
|
|
|
&dev_attr_write_req_latency_sum.attr,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_monitor_group = {
|
|
|
|
.name = "monitor",
|
|
|
|
.attrs = ufs_sysfs_monitor_attrs,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:02 +08:00
|
|
|
static ssize_t ufs_sysfs_read_desc_param(struct ufs_hba *hba,
|
|
|
|
enum desc_idn desc_id,
|
|
|
|
u8 desc_index,
|
|
|
|
u8 param_offset,
|
|
|
|
u8 *sysfs_buf,
|
|
|
|
u8 param_size)
|
|
|
|
{
|
|
|
|
u8 desc_buf[8] = {0};
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (param_size > 8)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2021-01-14 11:13:28 +08:00
|
|
|
down(&hba->host_sem);
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_get_sync(hba);
|
2018-02-15 20:14:02 +08:00
|
|
|
ret = ufshcd_read_desc_param(hba, desc_id, desc_index,
|
|
|
|
param_offset, desc_buf, param_size);
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_put_sync(hba);
|
2021-01-14 11:13:28 +08:00
|
|
|
if (ret) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-02-15 20:14:02 +08:00
|
|
|
switch (param_size) {
|
|
|
|
case 1:
|
2021-01-07 05:15:41 +08:00
|
|
|
ret = sysfs_emit(sysfs_buf, "0x%02X\n", *desc_buf);
|
2018-02-15 20:14:02 +08:00
|
|
|
break;
|
|
|
|
case 2:
|
2021-01-07 05:15:41 +08:00
|
|
|
ret = sysfs_emit(sysfs_buf, "0x%04X\n",
|
2018-02-15 20:14:02 +08:00
|
|
|
get_unaligned_be16(desc_buf));
|
|
|
|
break;
|
|
|
|
case 4:
|
2021-01-07 05:15:41 +08:00
|
|
|
ret = sysfs_emit(sysfs_buf, "0x%08X\n",
|
2018-02-15 20:14:02 +08:00
|
|
|
get_unaligned_be32(desc_buf));
|
|
|
|
break;
|
|
|
|
case 8:
|
2021-01-07 05:15:41 +08:00
|
|
|
ret = sysfs_emit(sysfs_buf, "0x%016llX\n",
|
2018-02-15 20:14:02 +08:00
|
|
|
get_unaligned_be64(desc_buf));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-01-14 11:13:28 +08:00
|
|
|
out:
|
|
|
|
up(&hba->host_sem);
|
2018-02-15 20:14:02 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define UFS_DESC_PARAM(_name, _puname, _duname, _size) \
|
|
|
|
static ssize_t _name##_show(struct device *dev, \
|
|
|
|
struct device_attribute *attr, char *buf) \
|
|
|
|
{ \
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev); \
|
|
|
|
return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \
|
|
|
|
0, _duname##_DESC_PARAM##_puname, buf, _size); \
|
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(_name)
|
|
|
|
|
|
|
|
#define UFS_DEVICE_DESC_PARAM(_name, _uname, _size) \
|
|
|
|
UFS_DESC_PARAM(_name, _uname, DEVICE, _size)
|
|
|
|
|
|
|
|
UFS_DEVICE_DESC_PARAM(device_type, _DEVICE_TYPE, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(device_class, _DEVICE_CLASS, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(device_sub_class, _DEVICE_SUB_CLASS, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(protocol, _PRTCL, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(number_of_luns, _NUM_LU, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(number_of_wluns, _NUM_WLU, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(boot_enable, _BOOT_ENBL, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(descriptor_access_enable, _DESC_ACCSS_ENBL, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(initial_power_mode, _INIT_PWR_MODE, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(high_priority_lun, _HIGH_PR_LUN, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(secure_removal_type, _SEC_RMV_TYPE, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(support_security_lun, _SEC_LU, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(bkops_termination_latency, _BKOP_TERM_LT, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(initial_active_icc_level, _ACTVE_ICC_LVL, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(specification_version, _SPEC_VER, 2);
|
|
|
|
UFS_DEVICE_DESC_PARAM(manufacturing_date, _MANF_DATE, 2);
|
|
|
|
UFS_DEVICE_DESC_PARAM(manufacturer_id, _MANF_ID, 2);
|
|
|
|
UFS_DEVICE_DESC_PARAM(rtt_capability, _RTT_CAP, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(rtc_update, _FRQ_RTC, 2);
|
|
|
|
UFS_DEVICE_DESC_PARAM(ufs_features, _UFS_FEAT, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(ffu_timeout, _FFU_TMT, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(queue_depth, _Q_DPTH, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(device_version, _DEV_VER, 2);
|
|
|
|
UFS_DEVICE_DESC_PARAM(number_of_secure_wpa, _NUM_SEC_WPA, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(psa_max_data_size, _PSA_MAX_DATA, 4);
|
|
|
|
UFS_DEVICE_DESC_PARAM(psa_state_timeout, _PSA_TMT, 1);
|
scsi: ufs: ufshpb: Introduce Host Performance Buffer feature
Implement Host Performance Buffer (HPB) initialization and add function
calls to UFS core driver.
NAND flash-based storage devices, including UFS, have mechanisms to
translate logical addresses of I/O requests to the corresponding physical
addresses of the flash storage. In UFS, logical-to-physical-address (L2P)
map data, which is required to identify the physical address for the
requested I/Os, can only be partially stored in SRAM from NAND flash. Due
to this partial loading, accessing the flash address area, where the L2P
information for that address is not loaded in the SRAM, can result in
serious performance degradation.
The basic concept of HPB is to cache L2P mapping entries in host system
memory so that both physical block address (PBA) and logical block address
(LBA) can be delivered in HPB read command. The HPB read command allows to
read data faster than a regular read command in UFS since it provides the
physical address (HPB Entry) of the desired logical block in addition to
its logical address. The UFS device can access the physical block in NAND
directly without searching and uploading L2P mapping table. This improves
read performance because the NAND read operation for uploading L2P mapping
table is removed.
In HPB initialization, the host checks if the UFS device supports HPB
feature and retrieves related device capabilities. Then, HPB parameters are
configured in the device.
Total start-up time of popular applications was measured and the difference
observed between HPB being enabled and disabled. Popular applications are
12 game apps and 24 non-game apps. Each test cycle consists of running 36
applications in sequence. We repeated the cycle for observing performance
improvement by L2P mapping cache hit in HPB.
The following is the test environment:
- kernel version: 4.4.0
- RAM: 8GB
- UFS 2.1 (64GB)
Results:
+-------+----------+----------+-------+
| cycle | baseline | with HPB | diff |
+-------+----------+----------+-------+
| 1 | 272.4 | 264.9 | -7.5 |
| 2 | 250.4 | 248.2 | -2.2 |
| 3 | 226.2 | 215.6 | -10.6 |
| 4 | 230.6 | 214.8 | -15.8 |
| 5 | 232.0 | 218.1 | -13.9 |
| 6 | 231.9 | 212.6 | -19.3 |
+-------+----------+----------+-------+
We also measured HPB performance using iozone:
$ iozone -r 4k -+n -i2 -ecI -t 16 -l 16 -u 16 -s $IO_RANGE/16 -F \
mnt/tmp_1 mnt/tmp_2 mnt/tmp_3 mnt/tmp_4 mnt/tmp_5 mnt/tmp_6 mnt/tmp_7 \
mnt/tmp_8 mnt/tmp_9 mnt/tmp_10 mnt/tmp_11 mnt/tmp_12 mnt/tmp_13 \
mnt/tmp_14 mnt/tmp_15 mnt/tmp_16
Results:
+----------+--------+---------+
| IO range | HPB on | HPB off |
+----------+--------+---------+
| 1 GB | 294.8 | 300.87 |
| 4 GB | 293.51 | 179.35 |
| 8 GB | 294.85 | 162.52 |
| 16 GB | 293.45 | 156.26 |
| 32 GB | 277.4 | 153.25 |
+----------+--------+---------+
Link: https://lore.kernel.org/r/20210712085830epcms2p8c1288b7f7a81b044158a18232617b572@epcms2p8
Reported-by: kernel test robot <lkp@intel.com>
Tested-by: Bean Huo <beanhuo@micron.com>
Tested-by: Can Guo <cang@codeaurora.org>
Tested-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-07-12 16:58:30 +08:00
|
|
|
UFS_DEVICE_DESC_PARAM(hpb_version, _HPB_VER, 2);
|
|
|
|
UFS_DEVICE_DESC_PARAM(hpb_control, _HPB_CONTROL, 1);
|
2020-04-23 05:41:43 +08:00
|
|
|
UFS_DEVICE_DESC_PARAM(ext_feature_sup, _EXT_UFS_FEATURE_SUP, 4);
|
|
|
|
UFS_DEVICE_DESC_PARAM(wb_presv_us_en, _WB_PRESRV_USRSPC_EN, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(wb_type, _WB_TYPE, 1);
|
|
|
|
UFS_DEVICE_DESC_PARAM(wb_shared_alloc_units, _WB_SHARED_ALLOC_UNITS, 4);
|
2018-02-15 20:14:02 +08:00
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_device_descriptor[] = {
|
|
|
|
&dev_attr_device_type.attr,
|
|
|
|
&dev_attr_device_class.attr,
|
|
|
|
&dev_attr_device_sub_class.attr,
|
|
|
|
&dev_attr_protocol.attr,
|
|
|
|
&dev_attr_number_of_luns.attr,
|
|
|
|
&dev_attr_number_of_wluns.attr,
|
|
|
|
&dev_attr_boot_enable.attr,
|
|
|
|
&dev_attr_descriptor_access_enable.attr,
|
|
|
|
&dev_attr_initial_power_mode.attr,
|
|
|
|
&dev_attr_high_priority_lun.attr,
|
|
|
|
&dev_attr_secure_removal_type.attr,
|
|
|
|
&dev_attr_support_security_lun.attr,
|
|
|
|
&dev_attr_bkops_termination_latency.attr,
|
|
|
|
&dev_attr_initial_active_icc_level.attr,
|
|
|
|
&dev_attr_specification_version.attr,
|
|
|
|
&dev_attr_manufacturing_date.attr,
|
|
|
|
&dev_attr_manufacturer_id.attr,
|
|
|
|
&dev_attr_rtt_capability.attr,
|
|
|
|
&dev_attr_rtc_update.attr,
|
|
|
|
&dev_attr_ufs_features.attr,
|
|
|
|
&dev_attr_ffu_timeout.attr,
|
|
|
|
&dev_attr_queue_depth.attr,
|
|
|
|
&dev_attr_device_version.attr,
|
|
|
|
&dev_attr_number_of_secure_wpa.attr,
|
|
|
|
&dev_attr_psa_max_data_size.attr,
|
|
|
|
&dev_attr_psa_state_timeout.attr,
|
scsi: ufs: ufshpb: Introduce Host Performance Buffer feature
Implement Host Performance Buffer (HPB) initialization and add function
calls to UFS core driver.
NAND flash-based storage devices, including UFS, have mechanisms to
translate logical addresses of I/O requests to the corresponding physical
addresses of the flash storage. In UFS, logical-to-physical-address (L2P)
map data, which is required to identify the physical address for the
requested I/Os, can only be partially stored in SRAM from NAND flash. Due
to this partial loading, accessing the flash address area, where the L2P
information for that address is not loaded in the SRAM, can result in
serious performance degradation.
The basic concept of HPB is to cache L2P mapping entries in host system
memory so that both physical block address (PBA) and logical block address
(LBA) can be delivered in HPB read command. The HPB read command allows to
read data faster than a regular read command in UFS since it provides the
physical address (HPB Entry) of the desired logical block in addition to
its logical address. The UFS device can access the physical block in NAND
directly without searching and uploading L2P mapping table. This improves
read performance because the NAND read operation for uploading L2P mapping
table is removed.
In HPB initialization, the host checks if the UFS device supports HPB
feature and retrieves related device capabilities. Then, HPB parameters are
configured in the device.
Total start-up time of popular applications was measured and the difference
observed between HPB being enabled and disabled. Popular applications are
12 game apps and 24 non-game apps. Each test cycle consists of running 36
applications in sequence. We repeated the cycle for observing performance
improvement by L2P mapping cache hit in HPB.
The following is the test environment:
- kernel version: 4.4.0
- RAM: 8GB
- UFS 2.1 (64GB)
Results:
+-------+----------+----------+-------+
| cycle | baseline | with HPB | diff |
+-------+----------+----------+-------+
| 1 | 272.4 | 264.9 | -7.5 |
| 2 | 250.4 | 248.2 | -2.2 |
| 3 | 226.2 | 215.6 | -10.6 |
| 4 | 230.6 | 214.8 | -15.8 |
| 5 | 232.0 | 218.1 | -13.9 |
| 6 | 231.9 | 212.6 | -19.3 |
+-------+----------+----------+-------+
We also measured HPB performance using iozone:
$ iozone -r 4k -+n -i2 -ecI -t 16 -l 16 -u 16 -s $IO_RANGE/16 -F \
mnt/tmp_1 mnt/tmp_2 mnt/tmp_3 mnt/tmp_4 mnt/tmp_5 mnt/tmp_6 mnt/tmp_7 \
mnt/tmp_8 mnt/tmp_9 mnt/tmp_10 mnt/tmp_11 mnt/tmp_12 mnt/tmp_13 \
mnt/tmp_14 mnt/tmp_15 mnt/tmp_16
Results:
+----------+--------+---------+
| IO range | HPB on | HPB off |
+----------+--------+---------+
| 1 GB | 294.8 | 300.87 |
| 4 GB | 293.51 | 179.35 |
| 8 GB | 294.85 | 162.52 |
| 16 GB | 293.45 | 156.26 |
| 32 GB | 277.4 | 153.25 |
+----------+--------+---------+
Link: https://lore.kernel.org/r/20210712085830epcms2p8c1288b7f7a81b044158a18232617b572@epcms2p8
Reported-by: kernel test robot <lkp@intel.com>
Tested-by: Bean Huo <beanhuo@micron.com>
Tested-by: Can Guo <cang@codeaurora.org>
Tested-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-07-12 16:58:30 +08:00
|
|
|
&dev_attr_hpb_version.attr,
|
|
|
|
&dev_attr_hpb_control.attr,
|
2020-04-23 05:41:43 +08:00
|
|
|
&dev_attr_ext_feature_sup.attr,
|
|
|
|
&dev_attr_wb_presv_us_en.attr,
|
|
|
|
&dev_attr_wb_type.attr,
|
|
|
|
&dev_attr_wb_shared_alloc_units.attr,
|
2018-02-15 20:14:02 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_device_descriptor_group = {
|
|
|
|
.name = "device_descriptor",
|
|
|
|
.attrs = ufs_sysfs_device_descriptor,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:03 +08:00
|
|
|
#define UFS_INTERCONNECT_DESC_PARAM(_name, _uname, _size) \
|
|
|
|
UFS_DESC_PARAM(_name, _uname, INTERCONNECT, _size)
|
|
|
|
|
|
|
|
UFS_INTERCONNECT_DESC_PARAM(unipro_version, _UNIPRO_VER, 2);
|
|
|
|
UFS_INTERCONNECT_DESC_PARAM(mphy_version, _MPHY_VER, 2);
|
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_interconnect_descriptor[] = {
|
|
|
|
&dev_attr_unipro_version.attr,
|
|
|
|
&dev_attr_mphy_version.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_interconnect_descriptor_group = {
|
|
|
|
.name = "interconnect_descriptor",
|
|
|
|
.attrs = ufs_sysfs_interconnect_descriptor,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:04 +08:00
|
|
|
#define UFS_GEOMETRY_DESC_PARAM(_name, _uname, _size) \
|
|
|
|
UFS_DESC_PARAM(_name, _uname, GEOMETRY, _size)
|
|
|
|
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(raw_device_capacity, _DEV_CAP, 8);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(max_number_of_luns, _MAX_NUM_LUN, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(segment_size, _SEG_SIZE, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(allocation_unit_size, _ALLOC_UNIT_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(min_addressable_block_size, _MIN_BLK_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(optimal_read_block_size, _OPT_RD_BLK_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(optimal_write_block_size, _OPT_WR_BLK_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(max_in_buffer_size, _MAX_IN_BUF_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(max_out_buffer_size, _MAX_OUT_BUF_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(rpmb_rw_size, _RPMB_RW_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(dyn_capacity_resource_policy, _DYN_CAP_RSRC_PLC, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(data_ordering, _DATA_ORDER, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(max_number_of_contexts, _MAX_NUM_CTX, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(sys_data_tag_unit_size, _TAG_UNIT_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(sys_data_tag_resource_size, _TAG_RSRC_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(secure_removal_types, _SEC_RM_TYPES, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(memory_types, _MEM_TYPES, 2);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(sys_code_memory_max_alloc_units,
|
|
|
|
_SCM_MAX_NUM_UNITS, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(sys_code_memory_capacity_adjustment_factor,
|
|
|
|
_SCM_CAP_ADJ_FCTR, 2);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(non_persist_memory_max_alloc_units,
|
|
|
|
_NPM_MAX_NUM_UNITS, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(non_persist_memory_capacity_adjustment_factor,
|
|
|
|
_NPM_CAP_ADJ_FCTR, 2);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh1_memory_max_alloc_units,
|
|
|
|
_ENM1_MAX_NUM_UNITS, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh1_memory_capacity_adjustment_factor,
|
|
|
|
_ENM1_CAP_ADJ_FCTR, 2);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh2_memory_max_alloc_units,
|
|
|
|
_ENM2_MAX_NUM_UNITS, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh2_memory_capacity_adjustment_factor,
|
|
|
|
_ENM2_CAP_ADJ_FCTR, 2);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh3_memory_max_alloc_units,
|
|
|
|
_ENM3_MAX_NUM_UNITS, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh3_memory_capacity_adjustment_factor,
|
|
|
|
_ENM3_CAP_ADJ_FCTR, 2);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh4_memory_max_alloc_units,
|
|
|
|
_ENM4_MAX_NUM_UNITS, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(enh4_memory_capacity_adjustment_factor,
|
|
|
|
_ENM4_CAP_ADJ_FCTR, 2);
|
scsi: ufs: ufshpb: Introduce Host Performance Buffer feature
Implement Host Performance Buffer (HPB) initialization and add function
calls to UFS core driver.
NAND flash-based storage devices, including UFS, have mechanisms to
translate logical addresses of I/O requests to the corresponding physical
addresses of the flash storage. In UFS, logical-to-physical-address (L2P)
map data, which is required to identify the physical address for the
requested I/Os, can only be partially stored in SRAM from NAND flash. Due
to this partial loading, accessing the flash address area, where the L2P
information for that address is not loaded in the SRAM, can result in
serious performance degradation.
The basic concept of HPB is to cache L2P mapping entries in host system
memory so that both physical block address (PBA) and logical block address
(LBA) can be delivered in HPB read command. The HPB read command allows to
read data faster than a regular read command in UFS since it provides the
physical address (HPB Entry) of the desired logical block in addition to
its logical address. The UFS device can access the physical block in NAND
directly without searching and uploading L2P mapping table. This improves
read performance because the NAND read operation for uploading L2P mapping
table is removed.
In HPB initialization, the host checks if the UFS device supports HPB
feature and retrieves related device capabilities. Then, HPB parameters are
configured in the device.
Total start-up time of popular applications was measured and the difference
observed between HPB being enabled and disabled. Popular applications are
12 game apps and 24 non-game apps. Each test cycle consists of running 36
applications in sequence. We repeated the cycle for observing performance
improvement by L2P mapping cache hit in HPB.
The following is the test environment:
- kernel version: 4.4.0
- RAM: 8GB
- UFS 2.1 (64GB)
Results:
+-------+----------+----------+-------+
| cycle | baseline | with HPB | diff |
+-------+----------+----------+-------+
| 1 | 272.4 | 264.9 | -7.5 |
| 2 | 250.4 | 248.2 | -2.2 |
| 3 | 226.2 | 215.6 | -10.6 |
| 4 | 230.6 | 214.8 | -15.8 |
| 5 | 232.0 | 218.1 | -13.9 |
| 6 | 231.9 | 212.6 | -19.3 |
+-------+----------+----------+-------+
We also measured HPB performance using iozone:
$ iozone -r 4k -+n -i2 -ecI -t 16 -l 16 -u 16 -s $IO_RANGE/16 -F \
mnt/tmp_1 mnt/tmp_2 mnt/tmp_3 mnt/tmp_4 mnt/tmp_5 mnt/tmp_6 mnt/tmp_7 \
mnt/tmp_8 mnt/tmp_9 mnt/tmp_10 mnt/tmp_11 mnt/tmp_12 mnt/tmp_13 \
mnt/tmp_14 mnt/tmp_15 mnt/tmp_16
Results:
+----------+--------+---------+
| IO range | HPB on | HPB off |
+----------+--------+---------+
| 1 GB | 294.8 | 300.87 |
| 4 GB | 293.51 | 179.35 |
| 8 GB | 294.85 | 162.52 |
| 16 GB | 293.45 | 156.26 |
| 32 GB | 277.4 | 153.25 |
+----------+--------+---------+
Link: https://lore.kernel.org/r/20210712085830epcms2p8c1288b7f7a81b044158a18232617b572@epcms2p8
Reported-by: kernel test robot <lkp@intel.com>
Tested-by: Bean Huo <beanhuo@micron.com>
Tested-by: Can Guo <cang@codeaurora.org>
Tested-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-07-12 16:58:30 +08:00
|
|
|
UFS_GEOMETRY_DESC_PARAM(hpb_region_size, _HPB_REGION_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(hpb_number_lu, _HPB_NUMBER_LU, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(hpb_subregion_size, _HPB_SUBREGION_SIZE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(hpb_max_active_regions, _HPB_MAX_ACTIVE_REGS, 2);
|
2020-04-23 05:41:43 +08:00
|
|
|
UFS_GEOMETRY_DESC_PARAM(wb_max_alloc_units, _WB_MAX_ALLOC_UNITS, 4);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(wb_max_wb_luns, _WB_MAX_WB_LUNS, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(wb_buff_cap_adj, _WB_BUFF_CAP_ADJ, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(wb_sup_red_type, _WB_SUP_RED_TYPE, 1);
|
|
|
|
UFS_GEOMETRY_DESC_PARAM(wb_sup_wb_type, _WB_SUP_WB_TYPE, 1);
|
|
|
|
|
2018-02-15 20:14:04 +08:00
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_geometry_descriptor[] = {
|
|
|
|
&dev_attr_raw_device_capacity.attr,
|
|
|
|
&dev_attr_max_number_of_luns.attr,
|
|
|
|
&dev_attr_segment_size.attr,
|
|
|
|
&dev_attr_allocation_unit_size.attr,
|
|
|
|
&dev_attr_min_addressable_block_size.attr,
|
|
|
|
&dev_attr_optimal_read_block_size.attr,
|
|
|
|
&dev_attr_optimal_write_block_size.attr,
|
|
|
|
&dev_attr_max_in_buffer_size.attr,
|
|
|
|
&dev_attr_max_out_buffer_size.attr,
|
|
|
|
&dev_attr_rpmb_rw_size.attr,
|
|
|
|
&dev_attr_dyn_capacity_resource_policy.attr,
|
|
|
|
&dev_attr_data_ordering.attr,
|
|
|
|
&dev_attr_max_number_of_contexts.attr,
|
|
|
|
&dev_attr_sys_data_tag_unit_size.attr,
|
|
|
|
&dev_attr_sys_data_tag_resource_size.attr,
|
|
|
|
&dev_attr_secure_removal_types.attr,
|
|
|
|
&dev_attr_memory_types.attr,
|
|
|
|
&dev_attr_sys_code_memory_max_alloc_units.attr,
|
|
|
|
&dev_attr_sys_code_memory_capacity_adjustment_factor.attr,
|
|
|
|
&dev_attr_non_persist_memory_max_alloc_units.attr,
|
|
|
|
&dev_attr_non_persist_memory_capacity_adjustment_factor.attr,
|
|
|
|
&dev_attr_enh1_memory_max_alloc_units.attr,
|
|
|
|
&dev_attr_enh1_memory_capacity_adjustment_factor.attr,
|
|
|
|
&dev_attr_enh2_memory_max_alloc_units.attr,
|
|
|
|
&dev_attr_enh2_memory_capacity_adjustment_factor.attr,
|
|
|
|
&dev_attr_enh3_memory_max_alloc_units.attr,
|
|
|
|
&dev_attr_enh3_memory_capacity_adjustment_factor.attr,
|
|
|
|
&dev_attr_enh4_memory_max_alloc_units.attr,
|
|
|
|
&dev_attr_enh4_memory_capacity_adjustment_factor.attr,
|
scsi: ufs: ufshpb: Introduce Host Performance Buffer feature
Implement Host Performance Buffer (HPB) initialization and add function
calls to UFS core driver.
NAND flash-based storage devices, including UFS, have mechanisms to
translate logical addresses of I/O requests to the corresponding physical
addresses of the flash storage. In UFS, logical-to-physical-address (L2P)
map data, which is required to identify the physical address for the
requested I/Os, can only be partially stored in SRAM from NAND flash. Due
to this partial loading, accessing the flash address area, where the L2P
information for that address is not loaded in the SRAM, can result in
serious performance degradation.
The basic concept of HPB is to cache L2P mapping entries in host system
memory so that both physical block address (PBA) and logical block address
(LBA) can be delivered in HPB read command. The HPB read command allows to
read data faster than a regular read command in UFS since it provides the
physical address (HPB Entry) of the desired logical block in addition to
its logical address. The UFS device can access the physical block in NAND
directly without searching and uploading L2P mapping table. This improves
read performance because the NAND read operation for uploading L2P mapping
table is removed.
In HPB initialization, the host checks if the UFS device supports HPB
feature and retrieves related device capabilities. Then, HPB parameters are
configured in the device.
Total start-up time of popular applications was measured and the difference
observed between HPB being enabled and disabled. Popular applications are
12 game apps and 24 non-game apps. Each test cycle consists of running 36
applications in sequence. We repeated the cycle for observing performance
improvement by L2P mapping cache hit in HPB.
The following is the test environment:
- kernel version: 4.4.0
- RAM: 8GB
- UFS 2.1 (64GB)
Results:
+-------+----------+----------+-------+
| cycle | baseline | with HPB | diff |
+-------+----------+----------+-------+
| 1 | 272.4 | 264.9 | -7.5 |
| 2 | 250.4 | 248.2 | -2.2 |
| 3 | 226.2 | 215.6 | -10.6 |
| 4 | 230.6 | 214.8 | -15.8 |
| 5 | 232.0 | 218.1 | -13.9 |
| 6 | 231.9 | 212.6 | -19.3 |
+-------+----------+----------+-------+
We also measured HPB performance using iozone:
$ iozone -r 4k -+n -i2 -ecI -t 16 -l 16 -u 16 -s $IO_RANGE/16 -F \
mnt/tmp_1 mnt/tmp_2 mnt/tmp_3 mnt/tmp_4 mnt/tmp_5 mnt/tmp_6 mnt/tmp_7 \
mnt/tmp_8 mnt/tmp_9 mnt/tmp_10 mnt/tmp_11 mnt/tmp_12 mnt/tmp_13 \
mnt/tmp_14 mnt/tmp_15 mnt/tmp_16
Results:
+----------+--------+---------+
| IO range | HPB on | HPB off |
+----------+--------+---------+
| 1 GB | 294.8 | 300.87 |
| 4 GB | 293.51 | 179.35 |
| 8 GB | 294.85 | 162.52 |
| 16 GB | 293.45 | 156.26 |
| 32 GB | 277.4 | 153.25 |
+----------+--------+---------+
Link: https://lore.kernel.org/r/20210712085830epcms2p8c1288b7f7a81b044158a18232617b572@epcms2p8
Reported-by: kernel test robot <lkp@intel.com>
Tested-by: Bean Huo <beanhuo@micron.com>
Tested-by: Can Guo <cang@codeaurora.org>
Tested-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-07-12 16:58:30 +08:00
|
|
|
&dev_attr_hpb_region_size.attr,
|
|
|
|
&dev_attr_hpb_number_lu.attr,
|
|
|
|
&dev_attr_hpb_subregion_size.attr,
|
|
|
|
&dev_attr_hpb_max_active_regions.attr,
|
2020-04-23 05:41:43 +08:00
|
|
|
&dev_attr_wb_max_alloc_units.attr,
|
|
|
|
&dev_attr_wb_max_wb_luns.attr,
|
|
|
|
&dev_attr_wb_buff_cap_adj.attr,
|
|
|
|
&dev_attr_wb_sup_red_type.attr,
|
|
|
|
&dev_attr_wb_sup_wb_type.attr,
|
2018-02-15 20:14:04 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_geometry_descriptor_group = {
|
|
|
|
.name = "geometry_descriptor",
|
|
|
|
.attrs = ufs_sysfs_geometry_descriptor,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:05 +08:00
|
|
|
#define UFS_HEALTH_DESC_PARAM(_name, _uname, _size) \
|
|
|
|
UFS_DESC_PARAM(_name, _uname, HEALTH, _size)
|
|
|
|
|
|
|
|
UFS_HEALTH_DESC_PARAM(eol_info, _EOL_INFO, 1);
|
|
|
|
UFS_HEALTH_DESC_PARAM(life_time_estimation_a, _LIFE_TIME_EST_A, 1);
|
|
|
|
UFS_HEALTH_DESC_PARAM(life_time_estimation_b, _LIFE_TIME_EST_B, 1);
|
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_health_descriptor[] = {
|
|
|
|
&dev_attr_eol_info.attr,
|
|
|
|
&dev_attr_life_time_estimation_a.attr,
|
|
|
|
&dev_attr_life_time_estimation_b.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_health_descriptor_group = {
|
|
|
|
.name = "health_descriptor",
|
|
|
|
.attrs = ufs_sysfs_health_descriptor,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:06 +08:00
|
|
|
#define UFS_POWER_DESC_PARAM(_name, _uname, _index) \
|
|
|
|
static ssize_t _name##_index##_show(struct device *dev, \
|
|
|
|
struct device_attribute *attr, char *buf) \
|
|
|
|
{ \
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev); \
|
|
|
|
return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_POWER, 0, \
|
|
|
|
PWR_DESC##_uname##_0 + _index * 2, buf, 2); \
|
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(_name##_index)
|
|
|
|
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 0);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 1);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 2);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 3);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 4);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 5);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 6);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 7);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 8);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 9);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 10);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 11);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 12);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 13);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 14);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vcc, _ACTIVE_LVLS_VCC, 15);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 0);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 1);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 2);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 3);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 4);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 5);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 6);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 7);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 8);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 9);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 10);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 11);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 12);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 13);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 14);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq, _ACTIVE_LVLS_VCCQ, 15);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 0);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 1);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 2);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 3);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 4);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 5);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 6);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 7);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 8);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 9);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 10);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 11);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 12);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 13);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 14);
|
|
|
|
UFS_POWER_DESC_PARAM(active_icc_levels_vccq2, _ACTIVE_LVLS_VCCQ2, 15);
|
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_power_descriptor[] = {
|
|
|
|
&dev_attr_active_icc_levels_vcc0.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc1.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc2.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc3.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc4.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc5.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc6.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc7.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc8.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc9.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc10.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc11.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc12.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc13.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc14.attr,
|
|
|
|
&dev_attr_active_icc_levels_vcc15.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq0.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq1.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq2.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq3.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq4.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq5.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq6.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq7.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq8.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq9.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq10.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq11.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq12.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq13.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq14.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq15.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq20.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq21.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq22.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq23.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq24.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq25.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq26.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq27.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq28.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq29.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq210.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq211.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq212.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq213.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq214.attr,
|
|
|
|
&dev_attr_active_icc_levels_vccq215.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_power_descriptor_group = {
|
|
|
|
.name = "power_descriptor",
|
|
|
|
.attrs = ufs_sysfs_power_descriptor,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:07 +08:00
|
|
|
#define UFS_STRING_DESCRIPTOR(_name, _pname) \
|
|
|
|
static ssize_t _name##_show(struct device *dev, \
|
|
|
|
struct device_attribute *attr, char *buf) \
|
|
|
|
{ \
|
|
|
|
u8 index; \
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev); \
|
|
|
|
int ret; \
|
|
|
|
int desc_len = QUERY_DESC_MAX_SIZE; \
|
|
|
|
u8 *desc_buf; \
|
2019-07-30 13:55:17 +08:00
|
|
|
\
|
2021-01-14 11:13:28 +08:00
|
|
|
down(&hba->host_sem); \
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) { \
|
|
|
|
up(&hba->host_sem); \
|
|
|
|
return -EBUSY; \
|
|
|
|
} \
|
2018-02-15 20:14:07 +08:00
|
|
|
desc_buf = kzalloc(QUERY_DESC_MAX_SIZE, GFP_ATOMIC); \
|
2021-01-14 11:13:28 +08:00
|
|
|
if (!desc_buf) { \
|
|
|
|
up(&hba->host_sem); \
|
|
|
|
return -ENOMEM; \
|
|
|
|
} \
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_get_sync(hba); \
|
2018-02-15 20:14:07 +08:00
|
|
|
ret = ufshcd_query_descriptor_retry(hba, \
|
|
|
|
UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_DEVICE, \
|
|
|
|
0, 0, desc_buf, &desc_len); \
|
|
|
|
if (ret) { \
|
|
|
|
ret = -EINVAL; \
|
|
|
|
goto out; \
|
|
|
|
} \
|
|
|
|
index = desc_buf[DEVICE_DESC_PARAM##_pname]; \
|
2019-07-30 13:55:17 +08:00
|
|
|
kfree(desc_buf); \
|
|
|
|
desc_buf = NULL; \
|
|
|
|
ret = ufshcd_read_string_desc(hba, index, &desc_buf, \
|
|
|
|
SD_ASCII_STD); \
|
|
|
|
if (ret < 0) \
|
2018-02-15 20:14:07 +08:00
|
|
|
goto out; \
|
2021-01-14 11:13:28 +08:00
|
|
|
ret = sysfs_emit(buf, "%s\n", desc_buf); \
|
2018-02-15 20:14:07 +08:00
|
|
|
out: \
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_put_sync(hba); \
|
2018-02-15 20:14:07 +08:00
|
|
|
kfree(desc_buf); \
|
2021-01-14 11:13:28 +08:00
|
|
|
up(&hba->host_sem); \
|
2018-02-15 20:14:07 +08:00
|
|
|
return ret; \
|
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(_name)
|
|
|
|
|
|
|
|
UFS_STRING_DESCRIPTOR(manufacturer_name, _MANF_NAME);
|
|
|
|
UFS_STRING_DESCRIPTOR(product_name, _PRDCT_NAME);
|
|
|
|
UFS_STRING_DESCRIPTOR(oem_id, _OEM_ID);
|
|
|
|
UFS_STRING_DESCRIPTOR(serial_number, _SN);
|
|
|
|
UFS_STRING_DESCRIPTOR(product_revision, _PRDCT_REV);
|
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_string_descriptors[] = {
|
|
|
|
&dev_attr_manufacturer_name.attr,
|
|
|
|
&dev_attr_product_name.attr,
|
|
|
|
&dev_attr_oem_id.attr,
|
|
|
|
&dev_attr_serial_number.attr,
|
|
|
|
&dev_attr_product_revision.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_string_descriptors_group = {
|
|
|
|
.name = "string_descriptors",
|
|
|
|
.attrs = ufs_sysfs_string_descriptors,
|
|
|
|
};
|
|
|
|
|
2020-05-08 16:01:13 +08:00
|
|
|
static inline bool ufshcd_is_wb_flags(enum flag_idn idn)
|
|
|
|
{
|
2021-05-13 16:53:20 +08:00
|
|
|
return idn >= QUERY_FLAG_IDN_WB_EN &&
|
|
|
|
idn <= QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8;
|
2020-05-08 16:01:13 +08:00
|
|
|
}
|
|
|
|
|
2018-02-15 20:14:10 +08:00
|
|
|
#define UFS_FLAG(_name, _uname) \
|
|
|
|
static ssize_t _name##_show(struct device *dev, \
|
|
|
|
struct device_attribute *attr, char *buf) \
|
|
|
|
{ \
|
|
|
|
bool flag; \
|
2020-05-08 16:01:13 +08:00
|
|
|
u8 index = 0; \
|
2020-03-25 05:48:21 +08:00
|
|
|
int ret; \
|
2018-02-15 20:14:10 +08:00
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev); \
|
2021-01-14 11:13:28 +08:00
|
|
|
\
|
|
|
|
down(&hba->host_sem); \
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) { \
|
|
|
|
up(&hba->host_sem); \
|
|
|
|
return -EBUSY; \
|
|
|
|
} \
|
2020-05-08 16:01:13 +08:00
|
|
|
if (ufshcd_is_wb_flags(QUERY_FLAG_IDN##_uname)) \
|
2020-05-22 16:32:11 +08:00
|
|
|
index = ufshcd_wb_get_query_index(hba); \
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_get_sync(hba); \
|
2020-03-25 05:48:21 +08:00
|
|
|
ret = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, \
|
2020-05-08 16:01:13 +08:00
|
|
|
QUERY_FLAG_IDN##_uname, index, &flag); \
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_put_sync(hba); \
|
2021-01-14 11:13:28 +08:00
|
|
|
if (ret) { \
|
|
|
|
ret = -EINVAL; \
|
|
|
|
goto out; \
|
|
|
|
} \
|
|
|
|
ret = sysfs_emit(buf, "%s\n", flag ? "true" : "false"); \
|
|
|
|
out: \
|
|
|
|
up(&hba->host_sem); \
|
|
|
|
return ret; \
|
2018-02-15 20:14:10 +08:00
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(_name)
|
|
|
|
|
|
|
|
UFS_FLAG(device_init, _FDEVICEINIT);
|
|
|
|
UFS_FLAG(permanent_wpe, _PERMANENT_WPE);
|
|
|
|
UFS_FLAG(power_on_wpe, _PWR_ON_WPE);
|
|
|
|
UFS_FLAG(bkops_enable, _BKOPS_EN);
|
|
|
|
UFS_FLAG(life_span_mode_enable, _LIFE_SPAN_MODE_ENABLE);
|
|
|
|
UFS_FLAG(phy_resource_removal, _FPHYRESOURCEREMOVAL);
|
|
|
|
UFS_FLAG(busy_rtc, _BUSY_RTC);
|
|
|
|
UFS_FLAG(disable_fw_update, _PERMANENTLY_DISABLE_FW_UPDATE);
|
2020-04-23 05:41:43 +08:00
|
|
|
UFS_FLAG(wb_enable, _WB_EN);
|
|
|
|
UFS_FLAG(wb_flush_en, _WB_BUFF_FLUSH_EN);
|
|
|
|
UFS_FLAG(wb_flush_during_h8, _WB_BUFF_FLUSH_DURING_HIBERN8);
|
2021-07-12 17:00:25 +08:00
|
|
|
UFS_FLAG(hpb_enable, _HPB_EN);
|
2018-02-15 20:14:10 +08:00
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_device_flags[] = {
|
|
|
|
&dev_attr_device_init.attr,
|
|
|
|
&dev_attr_permanent_wpe.attr,
|
|
|
|
&dev_attr_power_on_wpe.attr,
|
|
|
|
&dev_attr_bkops_enable.attr,
|
|
|
|
&dev_attr_life_span_mode_enable.attr,
|
|
|
|
&dev_attr_phy_resource_removal.attr,
|
|
|
|
&dev_attr_busy_rtc.attr,
|
|
|
|
&dev_attr_disable_fw_update.attr,
|
2020-04-23 05:41:43 +08:00
|
|
|
&dev_attr_wb_enable.attr,
|
|
|
|
&dev_attr_wb_flush_en.attr,
|
|
|
|
&dev_attr_wb_flush_during_h8.attr,
|
2021-07-12 17:00:25 +08:00
|
|
|
&dev_attr_hpb_enable.attr,
|
2018-02-15 20:14:10 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_flags_group = {
|
|
|
|
.name = "flags",
|
|
|
|
.attrs = ufs_sysfs_device_flags,
|
|
|
|
};
|
2018-02-15 20:14:07 +08:00
|
|
|
|
2020-05-22 16:32:11 +08:00
|
|
|
static inline bool ufshcd_is_wb_attrs(enum attr_idn idn)
|
|
|
|
{
|
2021-05-13 16:53:20 +08:00
|
|
|
return idn >= QUERY_ATTR_IDN_WB_FLUSH_STATUS &&
|
|
|
|
idn <= QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE;
|
2020-05-22 16:32:11 +08:00
|
|
|
}
|
|
|
|
|
2018-02-15 20:14:11 +08:00
|
|
|
#define UFS_ATTRIBUTE(_name, _uname) \
|
|
|
|
static ssize_t _name##_show(struct device *dev, \
|
|
|
|
struct device_attribute *attr, char *buf) \
|
|
|
|
{ \
|
|
|
|
struct ufs_hba *hba = dev_get_drvdata(dev); \
|
|
|
|
u32 value; \
|
2020-03-25 05:48:21 +08:00
|
|
|
int ret; \
|
2020-05-22 16:32:11 +08:00
|
|
|
u8 index = 0; \
|
2021-01-14 11:13:28 +08:00
|
|
|
\
|
|
|
|
down(&hba->host_sem); \
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) { \
|
|
|
|
up(&hba->host_sem); \
|
|
|
|
return -EBUSY; \
|
|
|
|
} \
|
2020-05-22 16:32:11 +08:00
|
|
|
if (ufshcd_is_wb_attrs(QUERY_ATTR_IDN##_uname)) \
|
|
|
|
index = ufshcd_wb_get_query_index(hba); \
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_get_sync(hba); \
|
2020-03-25 05:48:21 +08:00
|
|
|
ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, \
|
2020-05-22 16:32:11 +08:00
|
|
|
QUERY_ATTR_IDN##_uname, index, 0, &value); \
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_put_sync(hba); \
|
2021-01-14 11:13:28 +08:00
|
|
|
if (ret) { \
|
|
|
|
ret = -EINVAL; \
|
|
|
|
goto out; \
|
|
|
|
} \
|
|
|
|
ret = sysfs_emit(buf, "0x%08X\n", value); \
|
|
|
|
out: \
|
|
|
|
up(&hba->host_sem); \
|
|
|
|
return ret; \
|
2018-02-15 20:14:11 +08:00
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(_name)
|
|
|
|
|
|
|
|
UFS_ATTRIBUTE(boot_lun_enabled, _BOOT_LU_EN);
|
2021-07-12 17:00:25 +08:00
|
|
|
UFS_ATTRIBUTE(max_data_size_hpb_single_cmd, _MAX_HPB_SINGLE_CMD);
|
2018-02-15 20:14:11 +08:00
|
|
|
UFS_ATTRIBUTE(current_power_mode, _POWER_MODE);
|
|
|
|
UFS_ATTRIBUTE(active_icc_level, _ACTIVE_ICC_LVL);
|
|
|
|
UFS_ATTRIBUTE(ooo_data_enabled, _OOO_DATA_EN);
|
|
|
|
UFS_ATTRIBUTE(bkops_status, _BKOPS_STATUS);
|
|
|
|
UFS_ATTRIBUTE(purge_status, _PURGE_STATUS);
|
|
|
|
UFS_ATTRIBUTE(max_data_in_size, _MAX_DATA_IN);
|
|
|
|
UFS_ATTRIBUTE(max_data_out_size, _MAX_DATA_OUT);
|
|
|
|
UFS_ATTRIBUTE(reference_clock_frequency, _REF_CLK_FREQ);
|
|
|
|
UFS_ATTRIBUTE(configuration_descriptor_lock, _CONF_DESC_LOCK);
|
|
|
|
UFS_ATTRIBUTE(max_number_of_rtt, _MAX_NUM_OF_RTT);
|
|
|
|
UFS_ATTRIBUTE(exception_event_control, _EE_CONTROL);
|
|
|
|
UFS_ATTRIBUTE(exception_event_status, _EE_STATUS);
|
|
|
|
UFS_ATTRIBUTE(ffu_status, _FFU_STATUS);
|
|
|
|
UFS_ATTRIBUTE(psa_state, _PSA_STATE);
|
|
|
|
UFS_ATTRIBUTE(psa_data_size, _PSA_DATA_SIZE);
|
2020-04-23 05:41:43 +08:00
|
|
|
UFS_ATTRIBUTE(wb_flush_status, _WB_FLUSH_STATUS);
|
|
|
|
UFS_ATTRIBUTE(wb_avail_buf, _AVAIL_WB_BUFF_SIZE);
|
|
|
|
UFS_ATTRIBUTE(wb_life_time_est, _WB_BUFF_LIFE_TIME_EST);
|
|
|
|
UFS_ATTRIBUTE(wb_cur_buf, _CURR_WB_BUFF_SIZE);
|
|
|
|
|
2018-02-15 20:14:11 +08:00
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_attributes[] = {
|
|
|
|
&dev_attr_boot_lun_enabled.attr,
|
2021-07-12 17:00:25 +08:00
|
|
|
&dev_attr_max_data_size_hpb_single_cmd.attr,
|
2018-02-15 20:14:11 +08:00
|
|
|
&dev_attr_current_power_mode.attr,
|
|
|
|
&dev_attr_active_icc_level.attr,
|
|
|
|
&dev_attr_ooo_data_enabled.attr,
|
|
|
|
&dev_attr_bkops_status.attr,
|
|
|
|
&dev_attr_purge_status.attr,
|
|
|
|
&dev_attr_max_data_in_size.attr,
|
|
|
|
&dev_attr_max_data_out_size.attr,
|
|
|
|
&dev_attr_reference_clock_frequency.attr,
|
|
|
|
&dev_attr_configuration_descriptor_lock.attr,
|
|
|
|
&dev_attr_max_number_of_rtt.attr,
|
|
|
|
&dev_attr_exception_event_control.attr,
|
|
|
|
&dev_attr_exception_event_status.attr,
|
|
|
|
&dev_attr_ffu_status.attr,
|
|
|
|
&dev_attr_psa_state.attr,
|
|
|
|
&dev_attr_psa_data_size.attr,
|
2020-04-23 05:41:43 +08:00
|
|
|
&dev_attr_wb_flush_status.attr,
|
|
|
|
&dev_attr_wb_avail_buf.attr,
|
|
|
|
&dev_attr_wb_life_time_est.attr,
|
|
|
|
&dev_attr_wb_cur_buf.attr,
|
2018-02-15 20:14:11 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group ufs_sysfs_attributes_group = {
|
|
|
|
.name = "attributes",
|
|
|
|
.attrs = ufs_sysfs_attributes,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:01 +08:00
|
|
|
static const struct attribute_group *ufs_sysfs_groups[] = {
|
|
|
|
&ufs_sysfs_default_group,
|
2021-04-22 10:28:39 +08:00
|
|
|
&ufs_sysfs_monitor_group,
|
2018-02-15 20:14:02 +08:00
|
|
|
&ufs_sysfs_device_descriptor_group,
|
2018-02-15 20:14:03 +08:00
|
|
|
&ufs_sysfs_interconnect_descriptor_group,
|
2018-02-15 20:14:04 +08:00
|
|
|
&ufs_sysfs_geometry_descriptor_group,
|
2018-02-15 20:14:05 +08:00
|
|
|
&ufs_sysfs_health_descriptor_group,
|
2018-02-15 20:14:06 +08:00
|
|
|
&ufs_sysfs_power_descriptor_group,
|
2018-02-15 20:14:07 +08:00
|
|
|
&ufs_sysfs_string_descriptors_group,
|
2018-02-15 20:14:10 +08:00
|
|
|
&ufs_sysfs_flags_group,
|
2018-02-15 20:14:11 +08:00
|
|
|
&ufs_sysfs_attributes_group,
|
2018-02-15 20:14:01 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:09 +08:00
|
|
|
#define UFS_LUN_DESC_PARAM(_pname, _puname, _duname, _size) \
|
|
|
|
static ssize_t _pname##_show(struct device *dev, \
|
|
|
|
struct device_attribute *attr, char *buf) \
|
|
|
|
{ \
|
|
|
|
struct scsi_device *sdev = to_scsi_device(dev); \
|
|
|
|
struct ufs_hba *hba = shost_priv(sdev->host); \
|
|
|
|
u8 lun = ufshcd_scsi_to_upiu_lun(sdev->lun); \
|
2021-01-11 17:59:27 +08:00
|
|
|
if (!ufs_is_valid_unit_desc_lun(&hba->dev_info, lun, \
|
|
|
|
_duname##_DESC_PARAM##_puname)) \
|
2018-02-15 20:14:09 +08:00
|
|
|
return -EINVAL; \
|
|
|
|
return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \
|
|
|
|
lun, _duname##_DESC_PARAM##_puname, buf, _size); \
|
|
|
|
} \
|
|
|
|
static DEVICE_ATTR_RO(_pname)
|
|
|
|
|
|
|
|
#define UFS_UNIT_DESC_PARAM(_name, _uname, _size) \
|
|
|
|
UFS_LUN_DESC_PARAM(_name, _uname, UNIT, _size)
|
|
|
|
|
2021-08-05 02:21:28 +08:00
|
|
|
UFS_UNIT_DESC_PARAM(lu_enable, _LU_ENABLE, 1);
|
2018-02-15 20:14:09 +08:00
|
|
|
UFS_UNIT_DESC_PARAM(boot_lun_id, _BOOT_LUN_ID, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(lun_write_protect, _LU_WR_PROTECT, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(lun_queue_depth, _LU_Q_DEPTH, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(psa_sensitive, _PSA_SENSITIVE, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(lun_memory_type, _MEM_TYPE, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(data_reliability, _DATA_RELIABILITY, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(logical_block_size, _LOGICAL_BLK_SIZE, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(logical_block_count, _LOGICAL_BLK_COUNT, 8);
|
|
|
|
UFS_UNIT_DESC_PARAM(erase_block_size, _ERASE_BLK_SIZE, 4);
|
|
|
|
UFS_UNIT_DESC_PARAM(provisioning_type, _PROVISIONING_TYPE, 1);
|
|
|
|
UFS_UNIT_DESC_PARAM(physical_memory_resourse_count, _PHY_MEM_RSRC_CNT, 8);
|
|
|
|
UFS_UNIT_DESC_PARAM(context_capabilities, _CTX_CAPABILITIES, 2);
|
|
|
|
UFS_UNIT_DESC_PARAM(large_unit_granularity, _LARGE_UNIT_SIZE_M1, 1);
|
scsi: ufs: ufshpb: Introduce Host Performance Buffer feature
Implement Host Performance Buffer (HPB) initialization and add function
calls to UFS core driver.
NAND flash-based storage devices, including UFS, have mechanisms to
translate logical addresses of I/O requests to the corresponding physical
addresses of the flash storage. In UFS, logical-to-physical-address (L2P)
map data, which is required to identify the physical address for the
requested I/Os, can only be partially stored in SRAM from NAND flash. Due
to this partial loading, accessing the flash address area, where the L2P
information for that address is not loaded in the SRAM, can result in
serious performance degradation.
The basic concept of HPB is to cache L2P mapping entries in host system
memory so that both physical block address (PBA) and logical block address
(LBA) can be delivered in HPB read command. The HPB read command allows to
read data faster than a regular read command in UFS since it provides the
physical address (HPB Entry) of the desired logical block in addition to
its logical address. The UFS device can access the physical block in NAND
directly without searching and uploading L2P mapping table. This improves
read performance because the NAND read operation for uploading L2P mapping
table is removed.
In HPB initialization, the host checks if the UFS device supports HPB
feature and retrieves related device capabilities. Then, HPB parameters are
configured in the device.
Total start-up time of popular applications was measured and the difference
observed between HPB being enabled and disabled. Popular applications are
12 game apps and 24 non-game apps. Each test cycle consists of running 36
applications in sequence. We repeated the cycle for observing performance
improvement by L2P mapping cache hit in HPB.
The following is the test environment:
- kernel version: 4.4.0
- RAM: 8GB
- UFS 2.1 (64GB)
Results:
+-------+----------+----------+-------+
| cycle | baseline | with HPB | diff |
+-------+----------+----------+-------+
| 1 | 272.4 | 264.9 | -7.5 |
| 2 | 250.4 | 248.2 | -2.2 |
| 3 | 226.2 | 215.6 | -10.6 |
| 4 | 230.6 | 214.8 | -15.8 |
| 5 | 232.0 | 218.1 | -13.9 |
| 6 | 231.9 | 212.6 | -19.3 |
+-------+----------+----------+-------+
We also measured HPB performance using iozone:
$ iozone -r 4k -+n -i2 -ecI -t 16 -l 16 -u 16 -s $IO_RANGE/16 -F \
mnt/tmp_1 mnt/tmp_2 mnt/tmp_3 mnt/tmp_4 mnt/tmp_5 mnt/tmp_6 mnt/tmp_7 \
mnt/tmp_8 mnt/tmp_9 mnt/tmp_10 mnt/tmp_11 mnt/tmp_12 mnt/tmp_13 \
mnt/tmp_14 mnt/tmp_15 mnt/tmp_16
Results:
+----------+--------+---------+
| IO range | HPB on | HPB off |
+----------+--------+---------+
| 1 GB | 294.8 | 300.87 |
| 4 GB | 293.51 | 179.35 |
| 8 GB | 294.85 | 162.52 |
| 16 GB | 293.45 | 156.26 |
| 32 GB | 277.4 | 153.25 |
+----------+--------+---------+
Link: https://lore.kernel.org/r/20210712085830epcms2p8c1288b7f7a81b044158a18232617b572@epcms2p8
Reported-by: kernel test robot <lkp@intel.com>
Tested-by: Bean Huo <beanhuo@micron.com>
Tested-by: Can Guo <cang@codeaurora.org>
Tested-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-07-12 16:58:30 +08:00
|
|
|
UFS_UNIT_DESC_PARAM(hpb_lu_max_active_regions, _HPB_LU_MAX_ACTIVE_RGNS, 2);
|
|
|
|
UFS_UNIT_DESC_PARAM(hpb_pinned_region_start_offset, _HPB_PIN_RGN_START_OFF, 2);
|
|
|
|
UFS_UNIT_DESC_PARAM(hpb_number_pinned_regions, _HPB_NUM_PIN_RGNS, 2);
|
2020-04-23 05:41:43 +08:00
|
|
|
UFS_UNIT_DESC_PARAM(wb_buf_alloc_units, _WB_BUF_ALLOC_UNITS, 4);
|
|
|
|
|
2018-02-15 20:14:09 +08:00
|
|
|
static struct attribute *ufs_sysfs_unit_descriptor[] = {
|
2021-08-05 02:21:28 +08:00
|
|
|
&dev_attr_lu_enable.attr,
|
2018-02-15 20:14:09 +08:00
|
|
|
&dev_attr_boot_lun_id.attr,
|
|
|
|
&dev_attr_lun_write_protect.attr,
|
|
|
|
&dev_attr_lun_queue_depth.attr,
|
|
|
|
&dev_attr_psa_sensitive.attr,
|
|
|
|
&dev_attr_lun_memory_type.attr,
|
|
|
|
&dev_attr_data_reliability.attr,
|
|
|
|
&dev_attr_logical_block_size.attr,
|
|
|
|
&dev_attr_logical_block_count.attr,
|
|
|
|
&dev_attr_erase_block_size.attr,
|
|
|
|
&dev_attr_provisioning_type.attr,
|
|
|
|
&dev_attr_physical_memory_resourse_count.attr,
|
|
|
|
&dev_attr_context_capabilities.attr,
|
|
|
|
&dev_attr_large_unit_granularity.attr,
|
scsi: ufs: ufshpb: Introduce Host Performance Buffer feature
Implement Host Performance Buffer (HPB) initialization and add function
calls to UFS core driver.
NAND flash-based storage devices, including UFS, have mechanisms to
translate logical addresses of I/O requests to the corresponding physical
addresses of the flash storage. In UFS, logical-to-physical-address (L2P)
map data, which is required to identify the physical address for the
requested I/Os, can only be partially stored in SRAM from NAND flash. Due
to this partial loading, accessing the flash address area, where the L2P
information for that address is not loaded in the SRAM, can result in
serious performance degradation.
The basic concept of HPB is to cache L2P mapping entries in host system
memory so that both physical block address (PBA) and logical block address
(LBA) can be delivered in HPB read command. The HPB read command allows to
read data faster than a regular read command in UFS since it provides the
physical address (HPB Entry) of the desired logical block in addition to
its logical address. The UFS device can access the physical block in NAND
directly without searching and uploading L2P mapping table. This improves
read performance because the NAND read operation for uploading L2P mapping
table is removed.
In HPB initialization, the host checks if the UFS device supports HPB
feature and retrieves related device capabilities. Then, HPB parameters are
configured in the device.
Total start-up time of popular applications was measured and the difference
observed between HPB being enabled and disabled. Popular applications are
12 game apps and 24 non-game apps. Each test cycle consists of running 36
applications in sequence. We repeated the cycle for observing performance
improvement by L2P mapping cache hit in HPB.
The following is the test environment:
- kernel version: 4.4.0
- RAM: 8GB
- UFS 2.1 (64GB)
Results:
+-------+----------+----------+-------+
| cycle | baseline | with HPB | diff |
+-------+----------+----------+-------+
| 1 | 272.4 | 264.9 | -7.5 |
| 2 | 250.4 | 248.2 | -2.2 |
| 3 | 226.2 | 215.6 | -10.6 |
| 4 | 230.6 | 214.8 | -15.8 |
| 5 | 232.0 | 218.1 | -13.9 |
| 6 | 231.9 | 212.6 | -19.3 |
+-------+----------+----------+-------+
We also measured HPB performance using iozone:
$ iozone -r 4k -+n -i2 -ecI -t 16 -l 16 -u 16 -s $IO_RANGE/16 -F \
mnt/tmp_1 mnt/tmp_2 mnt/tmp_3 mnt/tmp_4 mnt/tmp_5 mnt/tmp_6 mnt/tmp_7 \
mnt/tmp_8 mnt/tmp_9 mnt/tmp_10 mnt/tmp_11 mnt/tmp_12 mnt/tmp_13 \
mnt/tmp_14 mnt/tmp_15 mnt/tmp_16
Results:
+----------+--------+---------+
| IO range | HPB on | HPB off |
+----------+--------+---------+
| 1 GB | 294.8 | 300.87 |
| 4 GB | 293.51 | 179.35 |
| 8 GB | 294.85 | 162.52 |
| 16 GB | 293.45 | 156.26 |
| 32 GB | 277.4 | 153.25 |
+----------+--------+---------+
Link: https://lore.kernel.org/r/20210712085830epcms2p8c1288b7f7a81b044158a18232617b572@epcms2p8
Reported-by: kernel test robot <lkp@intel.com>
Tested-by: Bean Huo <beanhuo@micron.com>
Tested-by: Can Guo <cang@codeaurora.org>
Tested-by: Stanley Chu <stanley.chu@mediatek.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Can Guo <cang@codeaurora.org>
Reviewed-by: Bean Huo <beanhuo@micron.com>
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-07-12 16:58:30 +08:00
|
|
|
&dev_attr_hpb_lu_max_active_regions.attr,
|
|
|
|
&dev_attr_hpb_pinned_region_start_offset.attr,
|
|
|
|
&dev_attr_hpb_number_pinned_regions.attr,
|
2020-04-23 05:41:43 +08:00
|
|
|
&dev_attr_wb_buf_alloc_units.attr,
|
2018-02-15 20:14:09 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct attribute_group ufs_sysfs_unit_descriptor_group = {
|
|
|
|
.name = "unit_descriptor",
|
|
|
|
.attrs = ufs_sysfs_unit_descriptor,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:11 +08:00
|
|
|
static ssize_t dyn_cap_needed_attribute_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
u32 value;
|
|
|
|
struct scsi_device *sdev = to_scsi_device(dev);
|
|
|
|
struct ufs_hba *hba = shost_priv(sdev->host);
|
|
|
|
u8 lun = ufshcd_scsi_to_upiu_lun(sdev->lun);
|
2020-03-25 05:48:21 +08:00
|
|
|
int ret;
|
2018-02-15 20:14:11 +08:00
|
|
|
|
2021-01-14 11:13:28 +08:00
|
|
|
down(&hba->host_sem);
|
|
|
|
if (!ufshcd_is_user_access_allowed(hba)) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_get_sync(hba);
|
2020-03-25 05:48:21 +08:00
|
|
|
ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
|
|
|
|
QUERY_ATTR_IDN_DYN_CAP_NEEDED, lun, 0, &value);
|
2021-04-24 08:20:17 +08:00
|
|
|
ufshcd_rpm_put_sync(hba);
|
2021-01-14 11:13:28 +08:00
|
|
|
if (ret) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = sysfs_emit(buf, "0x%08X\n", value);
|
|
|
|
|
|
|
|
out:
|
|
|
|
up(&hba->host_sem);
|
|
|
|
return ret;
|
2018-02-15 20:14:11 +08:00
|
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(dyn_cap_needed_attribute);
|
|
|
|
|
|
|
|
static struct attribute *ufs_sysfs_lun_attributes[] = {
|
|
|
|
&dev_attr_dyn_cap_needed_attribute.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct attribute_group ufs_sysfs_lun_attributes_group = {
|
|
|
|
.attrs = ufs_sysfs_lun_attributes,
|
|
|
|
};
|
|
|
|
|
2018-02-15 20:14:01 +08:00
|
|
|
void ufs_sysfs_add_nodes(struct device *dev)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = sysfs_create_groups(&dev->kobj, ufs_sysfs_groups);
|
|
|
|
if (ret)
|
|
|
|
dev_err(dev,
|
|
|
|
"%s: sysfs groups creation failed (err = %d)\n",
|
|
|
|
__func__, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ufs_sysfs_remove_nodes(struct device *dev)
|
|
|
|
{
|
|
|
|
sysfs_remove_groups(&dev->kobj, ufs_sysfs_groups);
|
|
|
|
}
|