blk-sec-wb.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Samsung Block Write Booster
  4. *
  5. * Copyright (C) 2023 Jisoo Oh <[email protected]>
  6. * Copyright (C) 2023 Changheun Lee <[email protected]>
  7. */
  8. #include <linux/sysfs.h>
  9. #include <linux/module.h>
  10. #include <linux/blk_types.h>
  11. #include <linux/blkdev.h>
  12. #include <linux/part_stat.h>
  13. #include <linux/timer.h>
  14. #include "blk-sec.h"
  15. #include "../drivers/ufs/host/ufs-sec-feature.h"
  16. #define MIN_ENABLE_MS 100
  17. #define MAX_ENABLE_MS 5000
  18. struct blk_sec_wb {
  19. struct mutex lock;
  20. volatile unsigned long request;
  21. unsigned int state;
  22. struct work_struct ctrl_work;
  23. struct timer_list user_wb_off_timer;
  24. };
  25. static struct blk_sec_wb wb;
  26. static void notify_wb_change(bool enabled)
  27. {
  28. #define BUF_SIZE 16
  29. char buf[BUF_SIZE];
  30. char *envp[] = { "NAME=BLK_SEC_WB", buf, NULL, };
  31. int ret;
  32. if (unlikely(IS_ERR(blk_sec_dev)))
  33. return;
  34. memset(buf, 0, BUF_SIZE);
  35. snprintf(buf, BUF_SIZE, "ENABLED=%d", enabled);
  36. ret = kobject_uevent_env(&blk_sec_dev->kobj, KOBJ_CHANGE, envp);
  37. if (ret)
  38. pr_err("%s: couldn't send uevent (%d)", __func__, ret);
  39. }
  40. /*
  41. * don't call this function in interrupt context,
  42. * it will be sleep when ufs_sec_wb_ctrl() is called
  43. *
  44. * Context: can sleep
  45. */
  46. static int wb_ctrl(bool enable)
  47. {
  48. int ret = 0;
  49. might_sleep();
  50. mutex_lock(&wb.lock);
  51. if (enable && (wb.state == WB_ON))
  52. goto out;
  53. if (!enable && (wb.state == WB_OFF))
  54. goto out;
  55. ret = ufs_sec_wb_ctrl(enable);
  56. if (ret)
  57. goto out;
  58. if (enable)
  59. wb.state = WB_ON;
  60. else
  61. wb.state = WB_OFF;
  62. notify_wb_change(enable);
  63. out:
  64. mutex_unlock(&wb.lock);
  65. return ret;
  66. }
  67. static void wb_ctrl_work(struct work_struct *work)
  68. {
  69. wb_ctrl(!!wb.request);
  70. }
  71. static void user_wb_off_handler(struct timer_list *timer)
  72. {
  73. clear_bit(WB_REQ_USER, &wb.request);
  74. queue_work(blk_sec_common_wq, &wb.ctrl_work);
  75. }
  76. static void ufs_reset_notify(void)
  77. {
  78. queue_work(blk_sec_common_wq, &wb.ctrl_work);
  79. }
  80. int blk_sec_wb_ctrl(bool enable, int req_type)
  81. {
  82. if (req_type < 0 || req_type >= NR_WB_REQ_TYPE)
  83. return -EINVAL;
  84. if (enable)
  85. set_bit(req_type, &wb.request);
  86. else
  87. clear_bit(req_type, &wb.request);
  88. return wb_ctrl(!!wb.request);
  89. }
  90. EXPORT_SYMBOL(blk_sec_wb_ctrl);
  91. int blk_sec_wb_ctrl_async(bool enable, int req_type)
  92. {
  93. if (req_type < 0 || req_type >= NR_WB_REQ_TYPE)
  94. return -EINVAL;
  95. if (enable)
  96. set_bit(req_type, &wb.request);
  97. else
  98. clear_bit(req_type, &wb.request);
  99. queue_work(blk_sec_common_wq, &wb.ctrl_work);
  100. return 0;
  101. }
  102. EXPORT_SYMBOL(blk_sec_wb_ctrl_async);
  103. bool blk_sec_wb_is_supported(struct gendisk *gd)
  104. {
  105. if (blk_sec_internal_disk() != gd)
  106. return false;
  107. return ufs_sec_is_wb_supported();
  108. }
  109. EXPORT_SYMBOL(blk_sec_wb_is_supported);
  110. static ssize_t request_show(struct kobject *kobj,
  111. struct kobj_attribute *attr, char *buf)
  112. {
  113. return scnprintf(buf, PAGE_SIZE, "%lx\n", wb.request);
  114. }
  115. static ssize_t state_show(struct kobject *kobj,
  116. struct kobj_attribute *attr, char *buf)
  117. {
  118. return scnprintf(buf, PAGE_SIZE, "%u\n", wb.state);
  119. }
  120. static ssize_t enable_ms_show(struct kobject *kobj,
  121. struct kobj_attribute *attr, char *buf)
  122. {
  123. unsigned long expire_jiffies = wb.user_wb_off_timer.expires;
  124. unsigned long current_jiffies = jiffies;
  125. return scnprintf(buf, PAGE_SIZE, "%u\n",
  126. time_after(expire_jiffies, current_jiffies) ?
  127. jiffies_to_msecs(expire_jiffies - current_jiffies) : 0);
  128. }
  129. static ssize_t enable_ms_store(struct kobject *kobj,
  130. struct kobj_attribute *attr, const char *buf, size_t count)
  131. {
  132. int wb_on_duration = 0;
  133. unsigned long expire_jiffies = 0;
  134. int ret;
  135. ret = kstrtoint(buf, 10, &wb_on_duration);
  136. if (ret)
  137. return ret;
  138. if (wb_on_duration <= 0)
  139. return count;
  140. if (wb_on_duration < MIN_ENABLE_MS)
  141. wb_on_duration = MIN_ENABLE_MS;
  142. if (wb_on_duration > MAX_ENABLE_MS)
  143. wb_on_duration = MAX_ENABLE_MS;
  144. expire_jiffies = jiffies + msecs_to_jiffies(wb_on_duration);
  145. if (time_after(expire_jiffies, wb.user_wb_off_timer.expires))
  146. mod_timer(&wb.user_wb_off_timer, expire_jiffies);
  147. blk_sec_wb_ctrl(true, WB_REQ_USER);
  148. return count;
  149. }
  150. static struct kobj_attribute request_attr = __ATTR_RO(request);
  151. static struct kobj_attribute state_attr = __ATTR_RO(state);
  152. static struct kobj_attribute enable_ms_attr =
  153. __ATTR(enable_ms, 0644, enable_ms_show, enable_ms_store);
  154. static const struct attribute *blk_sec_wb_attrs[] = {
  155. &request_attr.attr,
  156. &state_attr.attr,
  157. &enable_ms_attr.attr,
  158. NULL,
  159. };
  160. static struct kobject *blk_sec_wb_kobj;
  161. static int __init blk_sec_wb_init(void)
  162. {
  163. int retval;
  164. blk_sec_wb_kobj = kobject_create_and_add("blk_sec_wb", kernel_kobj);
  165. if (!blk_sec_wb_kobj)
  166. return -ENOMEM;
  167. retval = sysfs_create_files(blk_sec_wb_kobj, blk_sec_wb_attrs);
  168. if (retval) {
  169. kobject_put(blk_sec_wb_kobj);
  170. return retval;
  171. }
  172. mutex_init(&wb.lock);
  173. wb.state = WB_OFF;
  174. INIT_WORK(&wb.ctrl_work, wb_ctrl_work);
  175. timer_setup(&wb.user_wb_off_timer, user_wb_off_handler, 0);
  176. ufs_sec_wb_register_reset_notify(&ufs_reset_notify);
  177. return 0;
  178. }
  179. static void __exit blk_sec_wb_exit(void)
  180. {
  181. del_timer_sync(&wb.user_wb_off_timer);
  182. sysfs_remove_files(blk_sec_wb_kobj, blk_sec_wb_attrs);
  183. kobject_put(blk_sec_wb_kobj);
  184. }
  185. module_init(blk_sec_wb_init);
  186. module_exit(blk_sec_wb_exit);
  187. MODULE_LICENSE("GPL v2");
  188. MODULE_AUTHOR("Jisoo Oh <[email protected]>");
  189. MODULE_DESCRIPTION("Samsung write booster module in block layer");
  190. MODULE_VERSION("1.0");