// SPDX-License-Identifier: GPL-2.0/* * Generic support for Memory System Cache Maintenance operations. * * Coherency maintenance drivers register with this simple framework that will * iterate over each registered instance to first kick off invalidation and * then to wait until it is complete. * * If no implementations are registered yet cpu_cache_has_invalidate_memregion() * will return false. If this runs concurrently with unregistration then a * race exists but this is no worse than the case where the operations instance * responsible for a given memory region has not yet registered. */#include <linux/cache_coherency.h>#include <linux/cleanup.h>#include <linux/container_of.h>#include <linux/export.h>#include <linux/kref.h>#include <linux/list.h>#include <linux/memregion.h>#include <linux/module.h>#include <linux/rwsem.h>#include <linux/slab.h>static LIST_HEAD(cache_ops_instance_list);static DECLARE_RWSEM(cache_ops_instance_list_lock);static void __cache_coherency_ops_instance_free(struct kref *kref){ struct cache_coherency_ops_inst *cci = container_of(kref, struct cache_coherency_ops_inst, kref); kfree(cci);}void cache_coherency_ops_instance_put(struct cache_coherency_ops_inst *cci){ kref_put(&cci->kref, __cache_coherency_ops_instance_free);}EXPORT_SYMBOL_GPL(cache_coherency_ops_instance_put);static int cache_inval_one(struct cache_coherency_ops_inst *cci, void *data){ if (!cci->ops) return -EINVAL; return cci->ops->wbinv(cci, data);}static int cache_inval_done_one(struct cache_coherency_ops_inst *cci){ if (!cci->ops) return -EINVAL; if (!cci->ops->done) return 0; return cci->ops->done(cci);}static int cache_invalidate_memregion(phys_addr_t addr, size_t size){ int ret; struct cache_coherency_ops_inst *cci; struct cc_inval_params params = { .addr = addr, .size = size, }; guard(rwsem_read)(&cache_ops_instance_list_lock); list_for_each_entry(cci, &cache_ops_instance_list, node) { ret = cache_inval_one(cci, ¶ms); if (ret) return ret; } list_for_each_entry(cci, &cache_ops_instance_list, node) { ret = cache_inval_done_one(cci); if (ret) return ret; } return 0;}struct cache_coherency_ops_inst *_cache_coherency_ops_instance_alloc(const struct cache_coherency_ops *ops, size_t size){ struct cache_coherency_ops_inst *cci; if (!ops || !ops->wbinv) return NULL; cci = kzalloc(size, GFP_KERNEL); if (!cci) return NULL; cci->ops = ops; INIT_LIST_HEAD(&cci->node); kref_init(&cci->kref); return cci;}EXPORT_SYMBOL_NS_GPL(_cache_coherency_ops_instance_alloc, "CACHE_COHERENCY");int cache_coherency_ops_instance_register(struct cache_coherency_ops_inst *cci){ guard(rwsem_write)(&cache_ops_instance_list_lock); list_add(&cci->node, &cache_ops_instance_list); return 0;}EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_register, "CACHE_COHERENCY");void cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst *cci){ guard(rwsem_write)(&cache_ops_instance_list_lock); list_del(&cci->node);}EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_unregister, "CACHE_COHERENCY");int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len){ return cache_invalidate_memregion(start, len);}EXPORT_SYMBOL_NS_GPL(cpu_cache_invalidate_memregion, "DEVMEM");/* * Used for optimization / debug purposes only as removal can race * * Machines that do not support invalidation, e.g. VMs, will not have any * operations instance to register and so this will always return false. */bool cpu_cache_has_invalidate_memregion(void){ guard(rwsem_read)(&cache_ops_instance_list_lock); return !list_empty(&cache_ops_instance_list);}EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, "DEVMEM");
// SPDX-License-Identifier: GPL-2.0
/*
* Generic support for Memory System Cache Maintenance operations.
*
* Coherency maintenance drivers register with this simple framework that will
* iterate over each registered instance to first kick off invalidation and
* then to wait until it is complete.
*
* If no implementations are registered yet cpu_cache_has_invalidate_memregion()
* will return false. If this runs concurrently with unregistration then a
* race exists but this is no worse than the case where the operations instance
* responsible for a given memory region has not yet registered.
*/
#include <linux/cache_coherency.h>
#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/export.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/memregion.h>
#include <linux/module.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
static LIST_HEAD(cache_ops_instance_list);
static DECLARE_RWSEM(cache_ops_instance_list_lock);
static void __cache_coherency_ops_instance_free(struct kref *kref)
{
struct cache_coherency_ops_inst *cci =
container_of(kref, struct cache_coherency_ops_inst, kref);
kfree(cci);
}
void cache_coherency_ops_instance_put(struct cache_coherency_ops_inst *cci)
{
kref_put(&cci->kref, __cache_coherency_ops_instance_free);
}
EXPORT_SYMBOL_GPL(cache_coherency_ops_instance_put);
static int cache_inval_one(struct cache_coherency_ops_inst *cci, void *data)
{
if (!cci->ops)
return -EINVAL;
return cci->ops->wbinv(cci, data);
}
static int cache_inval_done_one(struct cache_coherency_ops_inst *cci)
{
if (!cci->ops)
return -EINVAL;
if (!cci->ops->done)
return 0;
return cci->ops->done(cci);
}
static int cache_invalidate_memregion(phys_addr_t addr, size_t size)
{
int ret;
struct cache_coherency_ops_inst *cci;
struct cc_inval_params params = {
.addr = addr,
.size = size,
};
guard(rwsem_read)(&cache_ops_instance_list_lock);
list_for_each_entry(cci, &cache_ops_instance_list, node) {
ret = cache_inval_one(cci, ¶ms);
if (ret)
return ret;
}
list_for_each_entry(cci, &cache_ops_instance_list, node) {
ret = cache_inval_done_one(cci);
if (ret)
return ret;
}
return 0;
}
struct cache_coherency_ops_inst *
_cache_coherency_ops_instance_alloc(const struct cache_coherency_ops *ops,
size_t size)
{
struct cache_coherency_ops_inst *cci;
if (!ops || !ops->wbinv)
return NULL;
cci = kzalloc(size, GFP_KERNEL);
if (!cci)
return NULL;
cci->ops = ops;
INIT_LIST_HEAD(&cci->node);
kref_init(&cci->kref);
return cci;
}
EXPORT_SYMBOL_NS_GPL(_cache_coherency_ops_instance_alloc, "CACHE_COHERENCY");
int cache_coherency_ops_instance_register(struct cache_coherency_ops_inst *cci)
{
guard(rwsem_write)(&cache_ops_instance_list_lock);
list_add(&cci->node, &cache_ops_instance_list);
return 0;
}
EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_register, "CACHE_COHERENCY");
void cache_coherency_ops_instance_unregister(struct cache_coherency_ops_inst *cci)
{
guard(rwsem_write)(&cache_ops_instance_list_lock);
list_del(&cci->node);
}
EXPORT_SYMBOL_NS_GPL(cache_coherency_ops_instance_unregister, "CACHE_COHERENCY");
int cpu_cache_invalidate_memregion(phys_addr_t start, size_t len)
{
return cache_invalidate_memregion(start, len);
}
EXPORT_SYMBOL_NS_GPL(cpu_cache_invalidate_memregion, "DEVMEM");
/*
* Used for optimization / debug purposes only as removal can race
*
* Machines that do not support invalidation, e.g. VMs, will not have any
* operations instance to register and so this will always return false.
*/
bool cpu_cache_has_invalidate_memregion(void)
{
guard(rwsem_read)(&cache_ops_instance_list_lock);
return !list_empty(&cache_ops_instance_list);
}
EXPORT_SYMBOL_NS_GPL(cpu_cache_has_invalidate_memregion, "DEVMEM");