cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

percpu_freelist.c (4770B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Copyright (c) 2016 Facebook
      3 */
      4#include "percpu_freelist.h"
      5
      6int pcpu_freelist_init(struct pcpu_freelist *s)
      7{
      8	int cpu;
      9
     10	s->freelist = alloc_percpu(struct pcpu_freelist_head);
     11	if (!s->freelist)
     12		return -ENOMEM;
     13
     14	for_each_possible_cpu(cpu) {
     15		struct pcpu_freelist_head *head = per_cpu_ptr(s->freelist, cpu);
     16
     17		raw_spin_lock_init(&head->lock);
     18		head->first = NULL;
     19	}
     20	raw_spin_lock_init(&s->extralist.lock);
     21	s->extralist.first = NULL;
     22	return 0;
     23}
     24
     25void pcpu_freelist_destroy(struct pcpu_freelist *s)
     26{
     27	free_percpu(s->freelist);
     28}
     29
     30static inline void pcpu_freelist_push_node(struct pcpu_freelist_head *head,
     31					   struct pcpu_freelist_node *node)
     32{
     33	node->next = head->first;
     34	head->first = node;
     35}
     36
     37static inline void ___pcpu_freelist_push(struct pcpu_freelist_head *head,
     38					 struct pcpu_freelist_node *node)
     39{
     40	raw_spin_lock(&head->lock);
     41	pcpu_freelist_push_node(head, node);
     42	raw_spin_unlock(&head->lock);
     43}
     44
     45static inline bool pcpu_freelist_try_push_extra(struct pcpu_freelist *s,
     46						struct pcpu_freelist_node *node)
     47{
     48	if (!raw_spin_trylock(&s->extralist.lock))
     49		return false;
     50
     51	pcpu_freelist_push_node(&s->extralist, node);
     52	raw_spin_unlock(&s->extralist.lock);
     53	return true;
     54}
     55
     56static inline void ___pcpu_freelist_push_nmi(struct pcpu_freelist *s,
     57					     struct pcpu_freelist_node *node)
     58{
     59	int cpu, orig_cpu;
     60
     61	orig_cpu = cpu = raw_smp_processor_id();
     62	while (1) {
     63		struct pcpu_freelist_head *head;
     64
     65		head = per_cpu_ptr(s->freelist, cpu);
     66		if (raw_spin_trylock(&head->lock)) {
     67			pcpu_freelist_push_node(head, node);
     68			raw_spin_unlock(&head->lock);
     69			return;
     70		}
     71		cpu = cpumask_next(cpu, cpu_possible_mask);
     72		if (cpu >= nr_cpu_ids)
     73			cpu = 0;
     74
     75		/* cannot lock any per cpu lock, try extralist */
     76		if (cpu == orig_cpu &&
     77		    pcpu_freelist_try_push_extra(s, node))
     78			return;
     79	}
     80}
     81
     82void __pcpu_freelist_push(struct pcpu_freelist *s,
     83			struct pcpu_freelist_node *node)
     84{
     85	if (in_nmi())
     86		___pcpu_freelist_push_nmi(s, node);
     87	else
     88		___pcpu_freelist_push(this_cpu_ptr(s->freelist), node);
     89}
     90
     91void pcpu_freelist_push(struct pcpu_freelist *s,
     92			struct pcpu_freelist_node *node)
     93{
     94	unsigned long flags;
     95
     96	local_irq_save(flags);
     97	__pcpu_freelist_push(s, node);
     98	local_irq_restore(flags);
     99}
    100
    101void pcpu_freelist_populate(struct pcpu_freelist *s, void *buf, u32 elem_size,
    102			    u32 nr_elems)
    103{
    104	struct pcpu_freelist_head *head;
    105	int i, cpu, pcpu_entries;
    106
    107	pcpu_entries = nr_elems / num_possible_cpus() + 1;
    108	i = 0;
    109
    110	for_each_possible_cpu(cpu) {
    111again:
    112		head = per_cpu_ptr(s->freelist, cpu);
    113		/* No locking required as this is not visible yet. */
    114		pcpu_freelist_push_node(head, buf);
    115		i++;
    116		buf += elem_size;
    117		if (i == nr_elems)
    118			break;
    119		if (i % pcpu_entries)
    120			goto again;
    121	}
    122}
    123
    124static struct pcpu_freelist_node *___pcpu_freelist_pop(struct pcpu_freelist *s)
    125{
    126	struct pcpu_freelist_head *head;
    127	struct pcpu_freelist_node *node;
    128	int orig_cpu, cpu;
    129
    130	orig_cpu = cpu = raw_smp_processor_id();
    131	while (1) {
    132		head = per_cpu_ptr(s->freelist, cpu);
    133		raw_spin_lock(&head->lock);
    134		node = head->first;
    135		if (node) {
    136			head->first = node->next;
    137			raw_spin_unlock(&head->lock);
    138			return node;
    139		}
    140		raw_spin_unlock(&head->lock);
    141		cpu = cpumask_next(cpu, cpu_possible_mask);
    142		if (cpu >= nr_cpu_ids)
    143			cpu = 0;
    144		if (cpu == orig_cpu)
    145			break;
    146	}
    147
    148	/* per cpu lists are all empty, try extralist */
    149	raw_spin_lock(&s->extralist.lock);
    150	node = s->extralist.first;
    151	if (node)
    152		s->extralist.first = node->next;
    153	raw_spin_unlock(&s->extralist.lock);
    154	return node;
    155}
    156
    157static struct pcpu_freelist_node *
    158___pcpu_freelist_pop_nmi(struct pcpu_freelist *s)
    159{
    160	struct pcpu_freelist_head *head;
    161	struct pcpu_freelist_node *node;
    162	int orig_cpu, cpu;
    163
    164	orig_cpu = cpu = raw_smp_processor_id();
    165	while (1) {
    166		head = per_cpu_ptr(s->freelist, cpu);
    167		if (raw_spin_trylock(&head->lock)) {
    168			node = head->first;
    169			if (node) {
    170				head->first = node->next;
    171				raw_spin_unlock(&head->lock);
    172				return node;
    173			}
    174			raw_spin_unlock(&head->lock);
    175		}
    176		cpu = cpumask_next(cpu, cpu_possible_mask);
    177		if (cpu >= nr_cpu_ids)
    178			cpu = 0;
    179		if (cpu == orig_cpu)
    180			break;
    181	}
    182
    183	/* cannot pop from per cpu lists, try extralist */
    184	if (!raw_spin_trylock(&s->extralist.lock))
    185		return NULL;
    186	node = s->extralist.first;
    187	if (node)
    188		s->extralist.first = node->next;
    189	raw_spin_unlock(&s->extralist.lock);
    190	return node;
    191}
    192
    193struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
    194{
    195	if (in_nmi())
    196		return ___pcpu_freelist_pop_nmi(s);
    197	return ___pcpu_freelist_pop(s);
    198}
    199
    200struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)
    201{
    202	struct pcpu_freelist_node *ret;
    203	unsigned long flags;
    204
    205	local_irq_save(flags);
    206	ret = __pcpu_freelist_pop(s);
    207	local_irq_restore(flags);
    208	return ret;
    209}