cache_maint.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Generic support for Memory System Cache Maintenance operations.
  4. *
  5. * Coherency maintenance drivers register with this simple framework that will
  6. * iterate over each registered instance to first kick off invalidation and
  7. * then to wait until it is complete.
  8. *
  9. * If no implementations are registered yet cpu_cache_has_invalidate_memregion()
  10. * will return false. If this runs concurrently with unregistration then a
  11. * race exists but this is no worse than the case where the operations instance
  12. * responsible for a given memory region has not yet registered.
  13. */
  14. #include <linux/cache_coherency.h>
  15. #include <linux/cleanup.h>
  16. #include <linux/container_of.h>
  17. #include <linux/export.h>
  18. #include <linux/kref.h>
  19. #include <linux/list.h>
  20. #include <linux/memregion.h>
  21. #include <linux/module.h>
  22. #include <linux/rwsem.h>
  23. #include <linux/slab.h>
  24. static LIST_HEAD(cache_ops_instance_list);
  25. static DECLARE_RWSEM(cache_ops_instance_list_lock);
  26. static void __cache_coherency_ops_instance_free(struct kref *kref)
  27. {
  28. struct cache_coherency_ops_inst *cci =
  29. container_of(kref, struct cache_coherency_ops_inst, kref);
  30. kfree(cci);
  31. }
  32. void cache_coherency_ops_instance_put(struct cache_coherency_ops_inst *cci)
  33. {
  34. kref_put(&cci->kref, __cache_coherency_ops_instance_free);
  35. }
  36. EXPORT_SYMBOL_GPL(cache_coherency_ops_instance_put);
  37. static int cache_inval_one(struct cache_coherency_ops_inst *cci, void *data)
  38. {
  39. if (!cci->ops)
  40. return -EINVAL;
  41. return cci->ops->wbinv(cci, data);
  42. }
  43. static int cache_inval_done_one(struct cache_coherency_ops_inst *cci)
  44. {
  45. if (!cci->ops)
  46. return -EINVAL;
  47. if (!cci->ops->done)
  48. return 0;
  49. return cci->ops->done(cci);
  50. }
  51. static int cache_invalidate_memregion(phys_addr_t addr, size_t size)
  52. {
  53. int ret;
  54. struct cache_coherency_ops_inst *cci;
  55. struct cc_inval_params params = {
  56. .addr = addr,
  57. .size = size,
  58. };
  59. guard(rwsem_read)(&cache_ops_instance_list_lock);
  60. list_for_each_entry(cci, &cache_ops_instance_list, node) {
  61. ret = cache_inval_one(cci, &params);
  62. if (ret)
  63. return ret;
  64. }
  65. list_for_each_entry(cci, &cache_ops_instance_list, node) {
  66. ret = cache_inval_done_one(cci);
  67. if (ret)
  68. return ret;
  69. }
  70. return 0;
  71. }
  72. struct cache_coherency_ops_inst *
  73. _cache_coherency_ops_instance_alloc(const struct cache_coherency_ops *ops,
  74. size_t size)
  75. {
  76. struct cache_coherency_ops_inst *cci;
  77. if (!ops || !ops->wbinv)
  78. return NULL;
  79. cci = kzalloc(size, GFP_KERNEL);
  80. if (!cci)
  81. return NULL;
  82. cci->ops = ops;
  83. INIT_LIST_HEAD(&cci->node);
  84. kref_init(&cci->kref);
  85. return cci;
  86. }
  87. EXPORT_SYMBOL_NS_GPL(_cache_coherency_ops_instance_alloc, "CACHE_COHERENCY");
  88. int cache_coherency_ops_instance_register(struct cache_coherency_ops_inst *cci)
  89. {
  90. guard(rwsem_write)(&cache_ops_instance_list_lock);
  91. list_add(&cci->node, &cache_ops_instance_list);
  92. return 0;
  93. }
  94. EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_register, "CACHE_COHERENCY");
  95. void cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst *cci)
  96. {
  97. guard(rwsem_write)(&cache_ops_instance_list_lock);
  98. list_del(&cci->node);
  99. }
  100. EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_unregister, "CACHE_COHERENCY");
  101. int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len)
  102. {
  103. return cache_invalidate_memregion(start, len);
  104. }
  105. EXPORT_SYMBOL_NS_GPL(cpu_cache_invalidate_memregion, "DEVMEM");
  106. /*
  107. * Used for optimization / debug purposes only as removal can race
  108. *
  109. * Machines that do not support invalidation, e.g. VMs, will not have any
  110. * operations instance to register and so this will always return false.
  111. */
  112. bool cpu_cache_has_invalidate_memregion(void)
  113. {
  114. guard(rwsem_read)(&cache_ops_instance_list_lock);
  115. return !list_empty(&cache_ops_instance_list);
  116. }
  117. EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, "DEVMEM");