Viewing: vmalloc.c

/* SPDX-License-Identifier: GPL-2.0+ */

#include <lustre_compat/linux/vmalloc.h>
#include <lustre_compat/linux/workqueue.h>

/*
 * This is opencoding of vfree_atomic from Linux kernel added in 4.10 with
 * minimum changes needed to work on older kernels too.
 *
 * We do this because using kallaym_lookup_name() can fail with exporting
 * vfree_atomic() which shows Lustre's days out of tree are numbered. In
 * reality the only reason we do this is because of the poor handling of
 * locking in the ptlrpc layer. Hoepfully a rework of ptlrpc layer will
 * remove the need of this code.
 */
#ifndef llist_for_each_safe
#define llist_for_each_safe(pos, n, node)                       \
	for ((pos) = (node); (pos) && ((n) = (pos)->next, true); (pos) = (n))
#endif

struct vfree_deferred {
	struct llist_head list;
	struct work_struct wq;
};
static DEFINE_PER_CPU(struct vfree_deferred, vfree_deferred);

static void free_work(struct work_struct *w)
{
	struct vfree_deferred *p = container_of(w, struct vfree_deferred, wq);
	struct llist_node *t, *llnode;

	llist_for_each_safe(llnode, t, llist_del_all(&p->list))
		vfree((void *)llnode);
}

void init_compat_vfree_atomic(void)
{
	int i;

	for_each_possible_cpu(i) {
		struct vfree_deferred *p;

		p = &per_cpu(vfree_deferred, i);
		init_llist_head(&p->list);
		INIT_WORK(&p->wq, free_work);
	}
}

void exit_compat_vfree_atomic(void)
{
	/* exit_libcfs_vfree_atomic */
	__flush_workqueue(system_wq);
}

void compat_vfree_atomic(const void *addr)
{
	struct vfree_deferred *p = raw_cpu_ptr(&vfree_deferred);

	if (!addr)
		 return;

	if (llist_add((struct llist_node *)addr, &p->list))
		schedule_work(&p->wq);
}
EXPORT_SYMBOL(compat_vfree_atomic);