gh_mem_notifier.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
  4. *
  5. */
  6. #include <linux/gunyah/gh_mem_notifier.h>
  7. #include <linux/list.h>
  8. #include <linux/module.h>
  9. #include <linux/mutex.h>
  10. #include <linux/notifier.h>
  11. #include <linux/slab.h>
  12. struct mem_notifier_entry {
  13. gh_mem_notifier_handler handler;
  14. void *data;
  15. };
  16. static DEFINE_MUTEX(mem_notifier_entries_lock);
  17. static struct mem_notifier_entry mem_notifier_entries[GH_MEM_NOTIFIER_TAG_MAX];
  18. static bool gh_mem_notifier_tag_valid(enum gh_mem_notifier_tag tag)
  19. {
  20. return tag >= 0 && tag < GH_MEM_NOTIFIER_TAG_MAX;
  21. }
  22. /**
  23. * gh_mem_notifier_register: Bind a callback and arbitrary data to a particular
  24. * notification tag. The callback will be invoked when
  25. * the Gunyah MEM_SHARED and MEM_RELEASED notifications
  26. * involving the tag that was registered with arrive
  27. * at the VM.
  28. * @tag: The tag for which the caller would like to receive MEM_SHARED and
  29. * MEM_RELEASED notifications for
  30. * @handler: The handler that will be invoked when a MEM_SHARED or MEM_RELEASED
  31. * notification pertaining to @tag arrives at the VM. The handler will
  32. * also be invoked with a pointer to caller specific data, as well as
  33. * the original MEM_SHARED/MEM_RELEASED payload from the resource
  34. * manager.
  35. * @data: The data that should be passed to @handler when it is invoked
  36. *
  37. * On success, the function will return a cookie. Otherwise, the function will
  38. * return an error, and thus, callers should use IS_ERR() to check for any
  39. * errors. The cookie must be used when unregistering the handler from the
  40. * tag.
  41. */
  42. void *gh_mem_notifier_register(enum gh_mem_notifier_tag tag,
  43. gh_mem_notifier_handler handler, void *data)
  44. {
  45. struct mem_notifier_entry *entry;
  46. if (!gh_mem_notifier_tag_valid(tag) || !handler)
  47. return ERR_PTR(-EINVAL);
  48. mutex_lock(&mem_notifier_entries_lock);
  49. entry = &mem_notifier_entries[tag];
  50. if (entry->handler) {
  51. mutex_unlock(&mem_notifier_entries_lock);
  52. return ERR_PTR(-EEXIST);
  53. }
  54. entry->handler = handler;
  55. entry->data = data;
  56. mutex_unlock(&mem_notifier_entries_lock);
  57. return entry;
  58. }
  59. EXPORT_SYMBOL(gh_mem_notifier_register);
  60. /**
  61. * gh_mem_notifier_unregister: Unregister for memory notifier notifications
  62. * with respect to a particular tag.
  63. * @cookie: The cookie returned by gh_mem_notifier_register
  64. *
  65. * On success, the function will unbind the handler specified in
  66. * gh_mem_notifier_register from the tag, preventing the handler from being
  67. * invoked when subsequent MEM_SHARED/MEM_RELEASED notifications pertaining
  68. * to the tag arrive.
  69. */
  70. void gh_mem_notifier_unregister(void *cookie)
  71. {
  72. struct mem_notifier_entry *entry = cookie;
  73. if (!cookie)
  74. return;
  75. mutex_lock(&mem_notifier_entries_lock);
  76. entry->handler = NULL;
  77. entry->data = NULL;
  78. mutex_unlock(&mem_notifier_entries_lock);
  79. }
  80. EXPORT_SYMBOL(gh_mem_notifier_unregister);
  81. static enum gh_mem_notifier_tag gh_mem_notifier_get_tag(unsigned long action,
  82. void *msg)
  83. {
  84. if (action == GH_RM_NOTIF_MEM_SHARED)
  85. return
  86. ((struct gh_rm_notif_mem_shared_payload *)msg)->mem_info_tag;
  87. else if (action == GH_RM_NOTIF_MEM_RELEASED)
  88. return
  89. ((struct gh_rm_notif_mem_released_payload *)msg)->mem_info_tag;
  90. return ((struct gh_rm_notif_mem_accepted_payload *)msg)->mem_info_tag;
  91. }
  92. static int gh_mem_notifier_call(struct notifier_block *nb, unsigned long action,
  93. void *msg)
  94. {
  95. struct mem_notifier_entry *entry;
  96. enum gh_mem_notifier_tag tag;
  97. gh_mem_notifier_handler handler = NULL;
  98. void *data;
  99. if ((action != GH_RM_NOTIF_MEM_SHARED) &&
  100. (action != GH_RM_NOTIF_MEM_RELEASED) &&
  101. (action != GH_RM_NOTIF_MEM_ACCEPTED))
  102. return NOTIFY_DONE;
  103. tag = gh_mem_notifier_get_tag(action, msg);
  104. if (!gh_mem_notifier_tag_valid(tag))
  105. return NOTIFY_DONE;
  106. mutex_lock(&mem_notifier_entries_lock);
  107. entry = &mem_notifier_entries[tag];
  108. handler = entry->handler;
  109. data = entry->data;
  110. mutex_unlock(&mem_notifier_entries_lock);
  111. if (handler)
  112. handler(tag, action, data, msg);
  113. return NOTIFY_OK;
  114. }
  115. static struct notifier_block gh_mem_notifier_blk = {
  116. .notifier_call = gh_mem_notifier_call,
  117. };
  118. static int __init gh_mem_notifier_init(void)
  119. {
  120. int ret = gh_rm_register_notifier(&gh_mem_notifier_blk);
  121. if (ret)
  122. pr_err("%s: registration with RM notifier failed rc: %d\n",
  123. __func__, ret);
  124. return ret;
  125. }
  126. module_init(gh_mem_notifier_init);
  127. static void __exit gh_mem_notifier_exit(void)
  128. {
  129. gh_rm_unregister_notifier(&gh_mem_notifier_blk);
  130. }
  131. module_exit(gh_mem_notifier_exit);
  132. MODULE_LICENSE("GPL");
  133. MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Gunyah Memory Notifier");