qti_virtio_mem.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  4. */
  5. #define pr_fmt(fmt) "qti_virtio_mem: %s: " fmt, __func__
  6. #include <linux/module.h>
  7. #include <linux/file.h>
  8. #include <linux/anon_inodes.h>
  9. #include <linux/kref.h>
  10. #include <linux/cdev.h>
  11. #include <linux/slab.h>
  12. #include <linux/oom.h>
  13. #include <linux/mm.h>
  14. #include <linux/mmzone.h>
  15. #include <linux/vmstat.h>
  16. #include <uapi/linux/qti_virtio_mem.h>
  17. #include "qti_virtio_mem.h"
  18. struct qti_virtio_mem_hint {
  19. struct list_head list;
  20. struct list_head kernel_plugged_list;
  21. struct kref kref;
  22. struct file *filp;
  23. s64 size;
  24. char name[QTI_VIRTIO_MEM_IOC_MAX_NAME_LEN];
  25. };
  26. #define QTI_VIRTIO_MEM_MAX_DEVS 1
  27. static dev_t qvm_dev_no;
  28. static struct class *qvm_class;
  29. static struct cdev qvm_char_dev;
  30. /* Protects qvm_hint_total and qvm_list */
  31. static DEFINE_MUTEX(qvm_lock);
  32. static LIST_HEAD(qvm_list);
  33. static LIST_HEAD(qvm_kernel_plugged);
  34. static DEFINE_MUTEX(qvm_kernel_plugged_lock);
  35. /* Sum of all hints */
  36. static s64 qvm_hint_total;
  37. static uint64_t device_block_size, max_plugin_threshold;
  38. static uint16_t kernel_plugged;
  39. #define QVM_OOM_NOTIFY_PRIORITY 90
  40. static int qti_virtio_mem_hint_update(struct qti_virtio_mem_hint *hint,
  41. s64 new_size, bool sync)
  42. {
  43. int ret;
  44. s64 total = 0;
  45. lockdep_assert_held(&qvm_lock);
  46. total = qvm_hint_total + new_size - hint->size;
  47. ret = virtio_mem_update_config_size(total, sync);
  48. if (ret) {
  49. pr_debug("Hint %s: Invalid request %llx would result in %llx\n",
  50. hint->name, new_size, total);
  51. return ret;
  52. }
  53. hint->size = new_size;
  54. qvm_hint_total = total;
  55. pr_debug("Hint %s: Updated size %llx, new_requested_size %llx\n",
  56. hint->name, hint->size, qvm_hint_total);
  57. return ret;
  58. }
  59. static void qti_virtio_mem_hint_kref_release(struct kref *kref)
  60. {
  61. struct qti_virtio_mem_hint *hint;
  62. int rc;
  63. mutex_lock(&qvm_lock);
  64. hint = container_of(kref, struct qti_virtio_mem_hint, kref);
  65. rc = qti_virtio_mem_hint_update(hint, 0, true);
  66. if (rc)
  67. pr_err("Possible permanent plug of memory to vm\n");
  68. list_del(&hint->list);
  69. mutex_unlock(&qvm_lock);
  70. kfree(hint);
  71. }
  72. static void *qti_virtio_mem_hint_create(char *name, s64 size)
  73. {
  74. struct qti_virtio_mem_hint *hint;
  75. lockdep_assert_held(&qvm_lock);
  76. hint = kzalloc(sizeof(*hint), GFP_KERNEL);
  77. if (!hint)
  78. return ERR_PTR(-ENOMEM);
  79. INIT_LIST_HEAD(&hint->list);
  80. kref_init(&hint->kref);
  81. hint->size = 0;
  82. if (!name || !strlen(name))
  83. name = "(none)";
  84. strscpy(hint->name, name, ARRAY_SIZE(hint->name));
  85. if (qti_virtio_mem_hint_update(hint, size, true)) {
  86. kfree(hint);
  87. return ERR_PTR(-EINVAL);
  88. }
  89. list_add(&hint->list, &qvm_list);
  90. return hint;
  91. }
  92. static void qti_virtio_mem_hint_release(void *handle)
  93. {
  94. struct qti_virtio_mem_hint *hint = handle;
  95. kref_put(&hint->kref, qti_virtio_mem_hint_kref_release);
  96. }
  97. static int qti_virtio_mem_hint_file_release(struct inode *inode, struct file *filp)
  98. {
  99. qti_virtio_mem_hint_release(filp->private_data);
  100. return 0;
  101. }
  102. static const struct file_operations qti_virtio_mem_hint_fops = {
  103. .release = qti_virtio_mem_hint_file_release,
  104. };
  105. static int qti_virtio_mem_hint_create_fd(char *name, u64 size)
  106. {
  107. struct qti_virtio_mem_hint *hint;
  108. int fd;
  109. mutex_lock(&qvm_lock);
  110. hint = qti_virtio_mem_hint_create(name, size);
  111. mutex_unlock(&qvm_lock);
  112. if (IS_ERR(hint))
  113. return PTR_ERR(hint);
  114. hint->filp = anon_inode_getfile("virtio_mem_hint",
  115. &qti_virtio_mem_hint_fops,
  116. hint, O_RDWR);
  117. if (IS_ERR(hint->filp)) {
  118. int ret = PTR_ERR(hint->filp);
  119. qti_virtio_mem_hint_release(hint);
  120. return ret;
  121. }
  122. fd = get_unused_fd_flags(O_CLOEXEC);
  123. if (fd < 0) {
  124. fput(hint->filp);
  125. return fd;
  126. }
  127. fd_install(fd, hint->filp);
  128. return fd;
  129. }
  130. union qti_virtio_mem_ioc_arg {
  131. struct qti_virtio_mem_ioc_hint_create_arg hint_create;
  132. };
  133. static int qti_virtio_mem_ioc_hint_create(struct qti_virtio_mem_ioc_hint_create_arg *arg)
  134. {
  135. int fd;
  136. /* Validate arguments */
  137. if (arg->size <= 0 || arg->reserved0 || arg->reserved1)
  138. return -EINVAL;
  139. /* ensure name is null-terminated */
  140. arg->name[QTI_VIRTIO_MEM_IOC_MAX_NAME_LEN - 1] = '\0';
  141. fd = qti_virtio_mem_hint_create_fd(arg->name, arg->size);
  142. if (fd < 0)
  143. return fd;
  144. arg->fd = fd;
  145. return 0;
  146. }
  147. static long qti_virtio_mem_ioctl(struct file *filp, unsigned int cmd,
  148. unsigned long arg)
  149. {
  150. int ret;
  151. unsigned int dir = _IOC_DIR(cmd);
  152. union qti_virtio_mem_ioc_arg ioctl_arg;
  153. if (_IOC_SIZE(cmd) > sizeof(ioctl_arg))
  154. return -EINVAL;
  155. if (copy_from_user(&ioctl_arg, (void __user *)arg, _IOC_SIZE(cmd)))
  156. return -EFAULT;
  157. if (!(dir & _IOC_WRITE))
  158. memset(&ioctl_arg, 0, sizeof(ioctl_arg));
  159. switch (cmd) {
  160. case QTI_VIRTIO_MEM_IOC_HINT_CREATE:
  161. {
  162. ret = qti_virtio_mem_ioc_hint_create(&ioctl_arg.hint_create);
  163. if (ret)
  164. return ret;
  165. break;
  166. }
  167. default:
  168. return -ENOTTY;
  169. }
  170. if (dir & _IOC_READ) {
  171. if (copy_to_user((void __user *)arg, &ioctl_arg,
  172. _IOC_SIZE(cmd)))
  173. return -EFAULT;
  174. }
  175. return 0;
  176. }
  177. static const struct file_operations qti_virtio_mem_dev_fops = {
  178. .unlocked_ioctl = qti_virtio_mem_ioctl,
  179. .compat_ioctl = compat_ptr_ioctl,
  180. };
  181. static ssize_t device_block_size_show(struct device *dev,
  182. struct device_attribute *attr, char *buf)
  183. {
  184. if (!device_block_size &&
  185. !virtio_mem_get_device_block_size(&device_block_size))
  186. dev_err(dev, "failed to get virtio-mem device block size\n");
  187. return scnprintf(buf, PAGE_SIZE, "%d\n", device_block_size);
  188. }
  189. static ssize_t max_plugin_threshold_show(struct device *dev,
  190. struct device_attribute *attr, char *buf)
  191. {
  192. if (!max_plugin_threshold &&
  193. !virtio_mem_get_max_plugin_threshold(&max_plugin_threshold))
  194. dev_err(dev, "failed to get max plugin threshold\n");
  195. return scnprintf(buf, PAGE_SIZE, "%d\n", max_plugin_threshold);
  196. }
  197. static ssize_t device_block_plugged_show(struct device *dev,
  198. struct device_attribute *attr, char *buf)
  199. {
  200. uint16_t device_block_plugged;
  201. device_block_plugged =
  202. ALIGN(qvm_hint_total, device_block_size) / device_block_size;
  203. return scnprintf(buf, PAGE_SIZE, "%d\n", device_block_plugged);
  204. }
  205. static ssize_t kernel_plugged_show(struct device *dev,
  206. struct device_attribute *attr, char *buf)
  207. {
  208. return scnprintf(buf, PAGE_SIZE, "%d\n", kernel_plugged);
  209. }
  210. static ssize_t kernel_unplug_store(struct device *dev,
  211. struct device_attribute *attr,
  212. const char *buf, size_t size)
  213. {
  214. int val = 0, ret;
  215. uint16_t plugged_out;
  216. struct qti_virtio_mem_hint *hint;
  217. ret = kstrtoint(buf, 0, &val);
  218. if (ret < 0)
  219. return ret;
  220. mutex_lock(&qvm_kernel_plugged_lock);
  221. val = min_t(uint16_t, val, kernel_plugged);
  222. for (plugged_out = 0; plugged_out < val && !list_empty(&qvm_kernel_plugged);
  223. plugged_out++) {
  224. hint = list_first_entry(&qvm_kernel_plugged, struct qti_virtio_mem_hint,
  225. kernel_plugged_list);
  226. list_del(&hint->kernel_plugged_list);
  227. qti_virtio_mem_hint_release(hint);
  228. kernel_plugged--;
  229. }
  230. mutex_unlock(&qvm_kernel_plugged_lock);
  231. return size;
  232. }
  233. static DEVICE_ATTR_RO(device_block_size);
  234. static DEVICE_ATTR_RO(max_plugin_threshold);
  235. static DEVICE_ATTR_RO(device_block_plugged);
  236. static DEVICE_ATTR_RO(kernel_plugged);
  237. static DEVICE_ATTR_WO(kernel_unplug);
  238. static struct attribute *dev_attrs[] = {
  239. &dev_attr_device_block_size.attr,
  240. &dev_attr_max_plugin_threshold.attr,
  241. &dev_attr_device_block_plugged.attr,
  242. &dev_attr_kernel_plugged.attr,
  243. &dev_attr_kernel_unplug.attr,
  244. NULL,
  245. };
  246. static struct attribute_group dev_group = {
  247. .attrs = dev_attrs,
  248. };
  249. static inline unsigned long get_zone_free_pages(enum zone_type zone_class)
  250. {
  251. return zone_page_state(&NODE_DATA(numa_node_id())->node_zones[zone_class],
  252. NR_FREE_PAGES);
  253. }
  254. static int qvm_oom_notify(struct notifier_block *self,
  255. unsigned long dummy, void *parm)
  256. {
  257. unsigned long *freed = parm;
  258. struct qti_virtio_mem_hint *hint;
  259. unsigned long free_pages;
  260. struct zone *z;
  261. z = &NODE_DATA(numa_node_id())->node_zones[ZONE_MOVABLE];
  262. free_pages = get_zone_free_pages(ZONE_MOVABLE);
  263. /* add a block only if movable zone is exhausted */
  264. if ((free_pages > high_wmark_pages(z) + device_block_size / PAGE_SIZE) ||
  265. (qvm_hint_total >= max_plugin_threshold))
  266. return NOTIFY_OK;
  267. pr_info("comm: %s totalram_pages: %lu Normal free_pages: %lu Movable free_pages: %lu\n",
  268. current->comm, totalram_pages(), get_zone_free_pages(ZONE_NORMAL),
  269. get_zone_free_pages(ZONE_MOVABLE));
  270. if (!mutex_trylock(&qvm_lock)) {
  271. *freed = 1;
  272. return NOTIFY_OK;
  273. }
  274. hint = qti_virtio_mem_hint_create("qvm_oom_notifier", device_block_size);
  275. mutex_unlock(&qvm_lock);
  276. if (IS_ERR(hint)) {
  277. pr_err("failed to add memory\n");
  278. return NOTIFY_OK;
  279. }
  280. mutex_lock(&qvm_kernel_plugged_lock);
  281. list_add(&hint->kernel_plugged_list, &qvm_kernel_plugged);
  282. *freed += device_block_size / PAGE_SIZE;
  283. kernel_plugged++;
  284. mutex_unlock(&qvm_kernel_plugged_lock);
  285. return NOTIFY_OK;
  286. }
  287. static struct notifier_block qvm_oom_nb = {
  288. .notifier_call = qvm_oom_notify,
  289. .priority = QVM_OOM_NOTIFY_PRIORITY,
  290. };
  291. static int __init qti_virtio_mem_init(void)
  292. {
  293. int ret;
  294. struct device *dev;
  295. ret = alloc_chrdev_region(&qvm_dev_no, 0, QTI_VIRTIO_MEM_MAX_DEVS,
  296. "qti_virtio_mem");
  297. if (ret < 0)
  298. goto err_chrdev_region;
  299. qvm_class = class_create(THIS_MODULE, "qti_virtio_mem");
  300. if (IS_ERR(qvm_class)) {
  301. ret = PTR_ERR(qvm_class);
  302. goto err_class_create;
  303. }
  304. cdev_init(&qvm_char_dev, &qti_virtio_mem_dev_fops);
  305. ret = cdev_add(&qvm_char_dev, qvm_dev_no, 1);
  306. if (ret < 0)
  307. goto err_cdev_add;
  308. dev = device_create(qvm_class, NULL, qvm_dev_no, NULL,
  309. "qti_virtio_mem");
  310. if (IS_ERR(dev)) {
  311. ret = PTR_ERR(dev);
  312. goto err_dev_create;
  313. }
  314. if (virtio_mem_get_device_block_size(&device_block_size))
  315. dev_err(dev, "failed to get virtio-mem device block size\n");
  316. if (virtio_mem_get_max_plugin_threshold(&max_plugin_threshold))
  317. dev_err(dev, "failed to get max plugin threshold\n");
  318. ret = sysfs_create_group(&dev->kobj, &dev_group);
  319. if (ret < 0) {
  320. dev_err(dev, "failed to create sysfs group\n");
  321. goto err_dev_create;
  322. }
  323. ret = register_oom_notifier(&qvm_oom_nb);
  324. if (ret < 0) {
  325. dev_err(dev, "Failed to register to oom notifier\n");
  326. goto err_dev_create;
  327. }
  328. return 0;
  329. err_dev_create:
  330. cdev_del(&qvm_char_dev);
  331. err_cdev_add:
  332. class_destroy(qvm_class);
  333. err_class_create:
  334. unregister_chrdev_region(qvm_dev_no, QTI_VIRTIO_MEM_MAX_DEVS);
  335. err_chrdev_region:
  336. return ret;
  337. }
  338. module_init(qti_virtio_mem_init);
  339. static void __exit qti_virtio_mem_exit(void)
  340. {
  341. WARN(!list_empty(&qvm_list), "Unloading module with nonzero hint objects\n");
  342. unregister_oom_notifier(&qvm_oom_nb);
  343. device_destroy(qvm_class, qvm_dev_no);
  344. cdev_del(&qvm_char_dev);
  345. class_destroy(qvm_class);
  346. unregister_chrdev_region(qvm_dev_no, QTI_VIRTIO_MEM_MAX_DEVS);
  347. }
  348. module_exit(qti_virtio_mem_exit);