android_kernel_samsung_sm8650/block/blk-sec-wb.c
2024-10-20 20:09:27 +02:00

239 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Samsung Block Write Booster
*
* Copyright (C) 2023 Jisoo Oh <jisoo2146.oh@samsung.com>
* Copyright (C) 2023 Changheun Lee <nanich.lee@samsung.com>
*/
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/blk_types.h>
#include <linux/blkdev.h>
#include <linux/part_stat.h>
#include <linux/timer.h>
#include "blk-sec.h"
#include "../drivers/ufs/host/ufs-sec-feature.h"
struct blk_sec_wb {
struct mutex lock;
volatile unsigned long request;
unsigned int state;
struct work_struct ctrl_work;
struct timer_list user_wb_off_timer;
};
static struct blk_sec_wb wb;
static void notify_wb_change(bool enabled)
{
#define BUF_SIZE 16
char buf[BUF_SIZE];
char *envp[] = { "NAME=BLK_SEC_WB", buf, NULL, };
int ret;
if (unlikely(IS_ERR(blk_sec_dev)))
return;
memset(buf, 0, BUF_SIZE);
snprintf(buf, BUF_SIZE, "ENABLED=%d", enabled);
ret = kobject_uevent_env(&blk_sec_dev->kobj, KOBJ_CHANGE, envp);
if (ret)
pr_err("%s: couldn't send uevent (%d)", __func__, ret);
}
/*
* don't call this function in interrupt context,
* it will be sleep when ufs_sec_wb_ctrl() is called
*
* Context: can sleep
*/
static int wb_ctrl(bool enable)
{
int ret = 0;
might_sleep();
mutex_lock(&wb.lock);
if (enable && (wb.state == WB_ON))
goto out;
if (!enable && (wb.state == WB_OFF))
goto out;
ret = ufs_sec_wb_ctrl(enable);
if (ret)
goto out;
if (enable)
wb.state = WB_ON;
else
wb.state = WB_OFF;
notify_wb_change(enable);
out:
mutex_unlock(&wb.lock);
return ret;
}
static void wb_ctrl_work(struct work_struct *work)
{
wb_ctrl(!!wb.request);
}
static void user_wb_off_handler(struct timer_list *timer)
{
clear_bit(WB_REQ_USER, &wb.request);
queue_work(blk_sec_common_wq, &wb.ctrl_work);
}
static void ufs_reset_notify(void)
{
queue_work(blk_sec_common_wq, &wb.ctrl_work);
}
int blk_sec_wb_ctrl(bool enable, int req_type)
{
if (req_type < 0 || req_type >= NR_WB_REQ_TYPE)
return -EINVAL;
if (enable)
set_bit(req_type, &wb.request);
else
clear_bit(req_type, &wb.request);
return wb_ctrl(!!wb.request);
}
EXPORT_SYMBOL(blk_sec_wb_ctrl);
int blk_sec_wb_ctrl_async(bool enable, int req_type)
{
if (req_type < 0 || req_type >= NR_WB_REQ_TYPE)
return -EINVAL;
if (enable)
set_bit(req_type, &wb.request);
else
clear_bit(req_type, &wb.request);
queue_work(blk_sec_common_wq, &wb.ctrl_work);
return 0;
}
EXPORT_SYMBOL(blk_sec_wb_ctrl_async);
bool blk_sec_wb_is_supported(struct gendisk *gd)
{
if (blk_sec_internal_disk() != gd)
return false;
return ufs_sec_is_wb_supported();
}
EXPORT_SYMBOL(blk_sec_wb_is_supported);
static ssize_t request_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%lx\n", wb.request);
}
static ssize_t state_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%u\n", wb.state);
}
static ssize_t enable_ms_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
unsigned long expire_jiffies = wb.user_wb_off_timer.expires;
unsigned long current_jiffies = jiffies;
return scnprintf(buf, PAGE_SIZE, "%u\n",
time_after(expire_jiffies, current_jiffies) ?
jiffies_to_msecs(expire_jiffies - current_jiffies) : 0);
}
static ssize_t enable_ms_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int wb_on_duration = 0;
unsigned long expire_jiffies = 0;
int ret;
ret = kstrtoint(buf, 10, &wb_on_duration);
if (ret)
return ret;
if (wb_on_duration <= 0)
return count;
if (wb_on_duration < 100)
wb_on_duration = 100;
if (wb_on_duration > 5000)
wb_on_duration = 5000;
expire_jiffies = jiffies + msecs_to_jiffies(wb_on_duration);
if (time_after(expire_jiffies, wb.user_wb_off_timer.expires))
mod_timer(&wb.user_wb_off_timer, expire_jiffies);
blk_sec_wb_ctrl(true, WB_REQ_USER);
return count;
}
static struct kobj_attribute request_attr = __ATTR_RO(request);
static struct kobj_attribute state_attr = __ATTR_RO(state);
static struct kobj_attribute enable_ms_attr =
__ATTR(enable_ms, 0644, enable_ms_show, enable_ms_store);
static const struct attribute *blk_sec_wb_attrs[] = {
&request_attr.attr,
&state_attr.attr,
&enable_ms_attr.attr,
NULL,
};
static struct kobject *blk_sec_wb_kobj;
static int __init blk_sec_wb_init(void)
{
int retval;
blk_sec_wb_kobj = kobject_create_and_add("blk_sec_wb", kernel_kobj);
if (!blk_sec_wb_kobj)
return -ENOMEM;
retval = sysfs_create_files(blk_sec_wb_kobj, blk_sec_wb_attrs);
if (retval) {
kobject_put(blk_sec_wb_kobj);
return retval;
}
mutex_init(&wb.lock);
wb.state = WB_OFF;
INIT_WORK(&wb.ctrl_work, wb_ctrl_work);
timer_setup(&wb.user_wb_off_timer, user_wb_off_handler, 0);
ufs_sec_wb_register_reset_notify(&ufs_reset_notify);
return 0;
}
static void __exit blk_sec_wb_exit(void)
{
del_timer_sync(&wb.user_wb_off_timer);
sysfs_remove_files(blk_sec_wb_kobj, blk_sec_wb_attrs);
kobject_put(blk_sec_wb_kobj);
}
module_init(blk_sec_wb_init);
module_exit(blk_sec_wb_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jisoo Oh <jisoo2146.oh@samsung.com>");
MODULE_DESCRIPTION("Samsung write booster module in block layer");
MODULE_VERSION("1.0");