123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- #include <linux/cgroup.h>
- #include <linux/fs.h>
- #include <linux/log2.h>
- #include <linux/sched.h>
- #include <linux/mm.h>
- #include <linux/vmstat.h>
- #include <linux/eventfd.h>
- #include <linux/slab.h>
- #include <linux/swap.h>
- #include <linux/printk.h>
- #include <linux/vmpressure.h>
- static const unsigned long vmpressure_win = SWAP_CLUSTER_MAX * 16;
- static const unsigned int vmpressure_level_med = 60;
- static const unsigned int vmpressure_level_critical = 95;
- static const unsigned int vmpressure_level_critical_prio = ilog2(100 / 10);
- static struct vmpressure *work_to_vmpressure(struct work_struct *work)
- {
- return container_of(work, struct vmpressure, work);
- }
- static struct vmpressure *vmpressure_parent(struct vmpressure *vmpr)
- {
- struct cgroup_subsys_state *css = vmpressure_to_css(vmpr);
- struct mem_cgroup *memcg = mem_cgroup_from_css(css);
- memcg = parent_mem_cgroup(memcg);
- if (!memcg)
- return NULL;
- return memcg_to_vmpressure(memcg);
- }
- enum vmpressure_levels {
- VMPRESSURE_LOW = 0,
- VMPRESSURE_MEDIUM,
- VMPRESSURE_CRITICAL,
- VMPRESSURE_NUM_LEVELS,
- };
- static const char * const vmpressure_str_levels[] = {
- [VMPRESSURE_LOW] = "low",
- [VMPRESSURE_MEDIUM] = "medium",
- [VMPRESSURE_CRITICAL] = "critical",
- };
- static enum vmpressure_levels vmpressure_level(unsigned long pressure)
- {
- if (pressure >= vmpressure_level_critical)
- return VMPRESSURE_CRITICAL;
- else if (pressure >= vmpressure_level_med)
- return VMPRESSURE_MEDIUM;
- return VMPRESSURE_LOW;
- }
- static enum vmpressure_levels vmpressure_calc_level(unsigned long scanned,
- unsigned long reclaimed)
- {
- unsigned long scale = scanned + reclaimed;
- unsigned long pressure = 0;
-
- if (reclaimed >= scanned)
- goto out;
-
- pressure = scale - (reclaimed * scale / scanned);
- pressure = pressure * 100 / scale;
- out:
- pr_debug("%s: %3lu (s: %lu r: %lu)\n", __func__, pressure,
- scanned, reclaimed);
- return vmpressure_level(pressure);
- }
- struct vmpressure_event {
- struct eventfd_ctx *efd;
- enum vmpressure_levels level;
- struct list_head node;
- };
- static bool vmpressure_event(struct vmpressure *vmpr,
- enum vmpressure_levels level)
- {
- struct vmpressure_event *ev;
- bool signalled = false;
- mutex_lock(&vmpr->events_lock);
- list_for_each_entry(ev, &vmpr->events, node) {
- if (level >= ev->level) {
- eventfd_signal(ev->efd, 1);
- signalled = true;
- }
- }
- mutex_unlock(&vmpr->events_lock);
- return signalled;
- }
- static void vmpressure_work_fn(struct work_struct *work)
- {
- struct vmpressure *vmpr = work_to_vmpressure(work);
- unsigned long scanned;
- unsigned long reclaimed;
- enum vmpressure_levels level;
- spin_lock(&vmpr->sr_lock);
-
- scanned = vmpr->tree_scanned;
- if (!scanned) {
- spin_unlock(&vmpr->sr_lock);
- return;
- }
- reclaimed = vmpr->tree_reclaimed;
- vmpr->tree_scanned = 0;
- vmpr->tree_reclaimed = 0;
- spin_unlock(&vmpr->sr_lock);
- level = vmpressure_calc_level(scanned, reclaimed);
- do {
- if (vmpressure_event(vmpr, level))
- break;
-
- } while ((vmpr = vmpressure_parent(vmpr)));
- }
- void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree,
- unsigned long scanned, unsigned long reclaimed)
- {
- struct vmpressure *vmpr = memcg_to_vmpressure(memcg);
-
- if (!(gfp & (__GFP_HIGHMEM | __GFP_MOVABLE | __GFP_IO | __GFP_FS)))
- return;
-
- if (!scanned)
- return;
- if (tree) {
- spin_lock(&vmpr->sr_lock);
- scanned = vmpr->tree_scanned += scanned;
- vmpr->tree_reclaimed += reclaimed;
- spin_unlock(&vmpr->sr_lock);
- if (scanned < vmpressure_win)
- return;
- schedule_work(&vmpr->work);
- } else {
- enum vmpressure_levels level;
-
- if (!memcg || memcg == root_mem_cgroup)
- return;
- spin_lock(&vmpr->sr_lock);
- scanned = vmpr->scanned += scanned;
- reclaimed = vmpr->reclaimed += reclaimed;
- if (scanned < vmpressure_win) {
- spin_unlock(&vmpr->sr_lock);
- return;
- }
- vmpr->scanned = vmpr->reclaimed = 0;
- spin_unlock(&vmpr->sr_lock);
- level = vmpressure_calc_level(scanned, reclaimed);
- if (level > VMPRESSURE_LOW) {
-
- memcg->socket_pressure = jiffies + HZ;
- }
- }
- }
- void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio)
- {
-
- if (prio > vmpressure_level_critical_prio)
- return;
-
- vmpressure(gfp, memcg, true, vmpressure_win, 0);
- }
- int vmpressure_register_event(struct mem_cgroup *memcg,
- struct eventfd_ctx *eventfd, const char *args)
- {
- struct vmpressure *vmpr = memcg_to_vmpressure(memcg);
- struct vmpressure_event *ev;
- int level;
- for (level = 0; level < VMPRESSURE_NUM_LEVELS; level++) {
- if (!strcmp(vmpressure_str_levels[level], args))
- break;
- }
- if (level >= VMPRESSURE_NUM_LEVELS)
- return -EINVAL;
- ev = kzalloc(sizeof(*ev), GFP_KERNEL);
- if (!ev)
- return -ENOMEM;
- ev->efd = eventfd;
- ev->level = level;
- mutex_lock(&vmpr->events_lock);
- list_add(&ev->node, &vmpr->events);
- mutex_unlock(&vmpr->events_lock);
- return 0;
- }
- void vmpressure_unregister_event(struct mem_cgroup *memcg,
- struct eventfd_ctx *eventfd)
- {
- struct vmpressure *vmpr = memcg_to_vmpressure(memcg);
- struct vmpressure_event *ev;
- mutex_lock(&vmpr->events_lock);
- list_for_each_entry(ev, &vmpr->events, node) {
- if (ev->efd != eventfd)
- continue;
- list_del(&ev->node);
- kfree(ev);
- break;
- }
- mutex_unlock(&vmpr->events_lock);
- }
- void vmpressure_init(struct vmpressure *vmpr)
- {
- spin_lock_init(&vmpr->sr_lock);
- mutex_init(&vmpr->events_lock);
- INIT_LIST_HEAD(&vmpr->events);
- INIT_WORK(&vmpr->work, vmpressure_work_fn);
- }
- void vmpressure_cleanup(struct vmpressure *vmpr)
- {
-
- flush_work(&vmpr->work);
- }
|