18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright © 2019 Intel Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "i915_active.h"
118c2ecf20Sopenharmony_ci#include "gem/i915_gem_context.h"
128c2ecf20Sopenharmony_ci#include "gem/i915_gem_object.h"
138c2ecf20Sopenharmony_ci#include "i915_globals.h"
148c2ecf20Sopenharmony_ci#include "i915_request.h"
158c2ecf20Sopenharmony_ci#include "i915_scheduler.h"
168c2ecf20Sopenharmony_ci#include "i915_vma.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic LIST_HEAD(globals);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic atomic_t active;
218c2ecf20Sopenharmony_cistatic atomic_t epoch;
228c2ecf20Sopenharmony_cistatic struct park_work {
238c2ecf20Sopenharmony_ci	struct delayed_work work;
248c2ecf20Sopenharmony_ci	struct rcu_head rcu;
258c2ecf20Sopenharmony_ci	unsigned long flags;
268c2ecf20Sopenharmony_ci#define PENDING 0
278c2ecf20Sopenharmony_ci	int epoch;
288c2ecf20Sopenharmony_ci} park;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void i915_globals_shrink(void)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct i915_global *global;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/*
358c2ecf20Sopenharmony_ci	 * kmem_cache_shrink() discards empty slabs and reorders partially
368c2ecf20Sopenharmony_ci	 * filled slabs to prioritise allocating from the mostly full slabs,
378c2ecf20Sopenharmony_ci	 * with the aim of reducing fragmentation.
388c2ecf20Sopenharmony_ci	 */
398c2ecf20Sopenharmony_ci	list_for_each_entry(global, &globals, link)
408c2ecf20Sopenharmony_ci		global->shrink();
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void __i915_globals_grace(struct rcu_head *rcu)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	/* Ratelimit parking as shrinking is quite slow */
468c2ecf20Sopenharmony_ci	schedule_delayed_work(&park.work, round_jiffies_up_relative(2 * HZ));
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic void __i915_globals_queue_rcu(void)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	park.epoch = atomic_inc_return(&epoch);
528c2ecf20Sopenharmony_ci	if (!atomic_read(&active)) {
538c2ecf20Sopenharmony_ci		init_rcu_head(&park.rcu);
548c2ecf20Sopenharmony_ci		call_rcu(&park.rcu, __i915_globals_grace);
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void __i915_globals_park(struct work_struct *work)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	destroy_rcu_head(&park.rcu);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* Confirm nothing woke up in the last grace period */
638c2ecf20Sopenharmony_ci	if (park.epoch != atomic_read(&epoch)) {
648c2ecf20Sopenharmony_ci		__i915_globals_queue_rcu();
658c2ecf20Sopenharmony_ci		return;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	clear_bit(PENDING, &park.flags);
698c2ecf20Sopenharmony_ci	i915_globals_shrink();
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_civoid __init i915_global_register(struct i915_global *global)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	GEM_BUG_ON(!global->shrink);
758c2ecf20Sopenharmony_ci	GEM_BUG_ON(!global->exit);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	list_add_tail(&global->link, &globals);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void __i915_globals_cleanup(void)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct i915_global *global, *next;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	list_for_each_entry_safe_reverse(global, next, &globals, link)
858c2ecf20Sopenharmony_ci		global->exit();
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic __initconst int (* const initfn[])(void) = {
898c2ecf20Sopenharmony_ci	i915_global_active_init,
908c2ecf20Sopenharmony_ci	i915_global_buddy_init,
918c2ecf20Sopenharmony_ci	i915_global_context_init,
928c2ecf20Sopenharmony_ci	i915_global_gem_context_init,
938c2ecf20Sopenharmony_ci	i915_global_objects_init,
948c2ecf20Sopenharmony_ci	i915_global_request_init,
958c2ecf20Sopenharmony_ci	i915_global_scheduler_init,
968c2ecf20Sopenharmony_ci	i915_global_vma_init,
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciint __init i915_globals_init(void)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	int i;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(initfn); i++) {
1048c2ecf20Sopenharmony_ci		int err;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		err = initfn[i]();
1078c2ecf20Sopenharmony_ci		if (err) {
1088c2ecf20Sopenharmony_ci			__i915_globals_cleanup();
1098c2ecf20Sopenharmony_ci			return err;
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&park.work, __i915_globals_park);
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_civoid i915_globals_park(void)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	/*
1208c2ecf20Sopenharmony_ci	 * Defer shrinking the global slab caches (and other work) until
1218c2ecf20Sopenharmony_ci	 * after a RCU grace period has completed with no activity. This
1228c2ecf20Sopenharmony_ci	 * is to try and reduce the latency impact on the consumers caused
1238c2ecf20Sopenharmony_ci	 * by us shrinking the caches the same time as they are trying to
1248c2ecf20Sopenharmony_ci	 * allocate, with the assumption being that if we idle long enough
1258c2ecf20Sopenharmony_ci	 * for an RCU grace period to elapse since the last use, it is likely
1268c2ecf20Sopenharmony_ci	 * to be longer until we need the caches again.
1278c2ecf20Sopenharmony_ci	 */
1288c2ecf20Sopenharmony_ci	if (!atomic_dec_and_test(&active))
1298c2ecf20Sopenharmony_ci		return;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Queue cleanup after the next RCU grace period has freed slabs */
1328c2ecf20Sopenharmony_ci	if (!test_and_set_bit(PENDING, &park.flags))
1338c2ecf20Sopenharmony_ci		__i915_globals_queue_rcu();
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_civoid i915_globals_unpark(void)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	atomic_inc(&epoch);
1398c2ecf20Sopenharmony_ci	atomic_inc(&active);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void __exit __i915_globals_flush(void)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	atomic_inc(&active); /* skip shrinking */
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	rcu_barrier(); /* wait for the work to be queued */
1478c2ecf20Sopenharmony_ci	flush_delayed_work(&park.work);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	atomic_dec(&active);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_civoid __exit i915_globals_exit(void)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	GEM_BUG_ON(atomic_read(&active));
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	__i915_globals_flush();
1578c2ecf20Sopenharmony_ci	__i915_globals_cleanup();
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* And ensure that our DESTROY_BY_RCU slabs are truly destroyed */
1608c2ecf20Sopenharmony_ci	rcu_barrier();
1618c2ecf20Sopenharmony_ci}
162