| .. | .. | 
|---|
 | 1 | +// SPDX-License-Identifier: GPL-2.0-only  | 
|---|
| 1 | 2 |  /* Copyright (c) 2016 Facebook | 
|---|
| 2 |  | - *  | 
|---|
| 3 |  | - * This program is free software; you can redistribute it and/or  | 
|---|
| 4 |  | - * modify it under the terms of version 2 of the GNU General Public  | 
|---|
| 5 |  | - * License as published by the Free Software Foundation.  | 
|---|
| 6 | 3 |   */ | 
|---|
| 7 | 4 |  #include "percpu_freelist.h" | 
|---|
| 8 | 5 |   | 
|---|
| .. | .. | 
|---|
| 20 | 17 |  		raw_spin_lock_init(&head->lock); | 
|---|
| 21 | 18 |  		head->first = NULL; | 
|---|
| 22 | 19 |  	} | 
|---|
 | 20 | +	raw_spin_lock_init(&s->extralist.lock);  | 
|---|
 | 21 | +	s->extralist.first = NULL;  | 
|---|
| 23 | 22 |  	return 0; | 
|---|
| 24 | 23 |  } | 
|---|
| 25 | 24 |   | 
|---|
| .. | .. | 
|---|
| 28 | 27 |  	free_percpu(s->freelist); | 
|---|
| 29 | 28 |  } | 
|---|
| 30 | 29 |   | 
|---|
 | 30 | +static 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 | +  | 
|---|
| 31 | 37 |  static inline void ___pcpu_freelist_push(struct pcpu_freelist_head *head, | 
|---|
| 32 | 38 |  					 struct pcpu_freelist_node *node) | 
|---|
| 33 | 39 |  { | 
|---|
| 34 | 40 |  	raw_spin_lock(&head->lock); | 
|---|
| 35 |  | -	node->next = head->first;  | 
|---|
| 36 |  | -	head->first = node;  | 
|---|
 | 41 | +	pcpu_freelist_push_node(head, node);  | 
|---|
| 37 | 42 |  	raw_spin_unlock(&head->lock); | 
|---|
 | 43 | +}  | 
|---|
 | 44 | +  | 
|---|
 | 45 | +static 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 | +  | 
|---|
 | 56 | +static 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 | +	}  | 
|---|
| 38 | 80 |  } | 
|---|
| 39 | 81 |   | 
|---|
| 40 | 82 |  void __pcpu_freelist_push(struct pcpu_freelist *s, | 
|---|
| 41 | 83 |  			struct pcpu_freelist_node *node) | 
|---|
| 42 | 84 |  { | 
|---|
| 43 |  | -	struct pcpu_freelist_head *head = this_cpu_ptr(s->freelist);  | 
|---|
| 44 |  | -  | 
|---|
| 45 |  | -	___pcpu_freelist_push(head, node);  | 
|---|
 | 85 | +	if (in_nmi())  | 
|---|
 | 86 | +		___pcpu_freelist_push_nmi(s, node);  | 
|---|
 | 87 | +	else  | 
|---|
 | 88 | +		___pcpu_freelist_push(this_cpu_ptr(s->freelist), node);  | 
|---|
| 46 | 89 |  } | 
|---|
| 47 | 90 |   | 
|---|
| 48 | 91 |  void pcpu_freelist_push(struct pcpu_freelist *s, | 
|---|
| .. | .. | 
|---|
| 59 | 102 |  			    u32 nr_elems) | 
|---|
| 60 | 103 |  { | 
|---|
| 61 | 104 |  	struct pcpu_freelist_head *head; | 
|---|
| 62 |  | -	unsigned long flags;  | 
|---|
| 63 |  | -	int i, cpu, pcpu_entries;  | 
|---|
 | 105 | +	unsigned int cpu, cpu_idx, i, j, n, m;  | 
|---|
| 64 | 106 |   | 
|---|
| 65 |  | -	pcpu_entries = nr_elems / num_possible_cpus() + 1;  | 
|---|
| 66 |  | -	i = 0;  | 
|---|
 | 107 | +	n = nr_elems / num_possible_cpus();  | 
|---|
 | 108 | +	m = nr_elems % num_possible_cpus();  | 
|---|
| 67 | 109 |   | 
|---|
| 68 |  | -	/* disable irq to workaround lockdep false positive  | 
|---|
| 69 |  | -	 * in bpf usage pcpu_freelist_populate() will never race  | 
|---|
| 70 |  | -	 * with pcpu_freelist_push()  | 
|---|
| 71 |  | -	 */  | 
|---|
| 72 |  | -	local_irq_save(flags);  | 
|---|
 | 110 | +	cpu_idx = 0;  | 
|---|
| 73 | 111 |  	for_each_possible_cpu(cpu) { | 
|---|
| 74 |  | -again:  | 
|---|
| 75 | 112 |  		head = per_cpu_ptr(s->freelist, cpu); | 
|---|
| 76 |  | -		___pcpu_freelist_push(head, buf);  | 
|---|
| 77 |  | -		i++;  | 
|---|
| 78 |  | -		buf += elem_size;  | 
|---|
| 79 |  | -		if (i == nr_elems)  | 
|---|
| 80 |  | -			break;  | 
|---|
| 81 |  | -		if (i % pcpu_entries)  | 
|---|
| 82 |  | -			goto again;  | 
|---|
 | 113 | +		j = n + (cpu_idx < m ? 1 : 0);  | 
|---|
 | 114 | +		for (i = 0; i < j; i++) {  | 
|---|
 | 115 | +			/* No locking required as this is not visible yet. */  | 
|---|
 | 116 | +			pcpu_freelist_push_node(head, buf);  | 
|---|
 | 117 | +			buf += elem_size;  | 
|---|
 | 118 | +		}  | 
|---|
 | 119 | +		cpu_idx++;  | 
|---|
| 83 | 120 |  	} | 
|---|
| 84 |  | -	local_irq_restore(flags);  | 
|---|
| 85 | 121 |  } | 
|---|
| 86 | 122 |   | 
|---|
| 87 |  | -struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)  | 
|---|
 | 123 | +static struct pcpu_freelist_node *___pcpu_freelist_pop(struct pcpu_freelist *s)  | 
|---|
| 88 | 124 |  { | 
|---|
| 89 | 125 |  	struct pcpu_freelist_head *head; | 
|---|
| 90 | 126 |  	struct pcpu_freelist_node *node; | 
|---|
| .. | .. | 
|---|
| 105 | 141 |  		if (cpu >= nr_cpu_ids) | 
|---|
| 106 | 142 |  			cpu = 0; | 
|---|
| 107 | 143 |  		if (cpu == orig_cpu) | 
|---|
| 108 |  | -			return NULL;  | 
|---|
 | 144 | +			break;  | 
|---|
| 109 | 145 |  	} | 
|---|
 | 146 | +  | 
|---|
 | 147 | +	/* per cpu lists are all empty, try extralist */  | 
|---|
 | 148 | +	raw_spin_lock(&s->extralist.lock);  | 
|---|
 | 149 | +	node = s->extralist.first;  | 
|---|
 | 150 | +	if (node)  | 
|---|
 | 151 | +		s->extralist.first = node->next;  | 
|---|
 | 152 | +	raw_spin_unlock(&s->extralist.lock);  | 
|---|
 | 153 | +	return node;  | 
|---|
 | 154 | +}  | 
|---|
 | 155 | +  | 
|---|
 | 156 | +static struct pcpu_freelist_node *  | 
|---|
 | 157 | +___pcpu_freelist_pop_nmi(struct pcpu_freelist *s)  | 
|---|
 | 158 | +{  | 
|---|
 | 159 | +	struct pcpu_freelist_head *head;  | 
|---|
 | 160 | +	struct pcpu_freelist_node *node;  | 
|---|
 | 161 | +	int orig_cpu, cpu;  | 
|---|
 | 162 | +  | 
|---|
 | 163 | +	orig_cpu = cpu = raw_smp_processor_id();  | 
|---|
 | 164 | +	while (1) {  | 
|---|
 | 165 | +		head = per_cpu_ptr(s->freelist, cpu);  | 
|---|
 | 166 | +		if (raw_spin_trylock(&head->lock)) {  | 
|---|
 | 167 | +			node = head->first;  | 
|---|
 | 168 | +			if (node) {  | 
|---|
 | 169 | +				head->first = node->next;  | 
|---|
 | 170 | +				raw_spin_unlock(&head->lock);  | 
|---|
 | 171 | +				return node;  | 
|---|
 | 172 | +			}  | 
|---|
 | 173 | +			raw_spin_unlock(&head->lock);  | 
|---|
 | 174 | +		}  | 
|---|
 | 175 | +		cpu = cpumask_next(cpu, cpu_possible_mask);  | 
|---|
 | 176 | +		if (cpu >= nr_cpu_ids)  | 
|---|
 | 177 | +			cpu = 0;  | 
|---|
 | 178 | +		if (cpu == orig_cpu)  | 
|---|
 | 179 | +			break;  | 
|---|
 | 180 | +	}  | 
|---|
 | 181 | +  | 
|---|
 | 182 | +	/* cannot pop from per cpu lists, try extralist */  | 
|---|
 | 183 | +	if (!raw_spin_trylock(&s->extralist.lock))  | 
|---|
 | 184 | +		return NULL;  | 
|---|
 | 185 | +	node = s->extralist.first;  | 
|---|
 | 186 | +	if (node)  | 
|---|
 | 187 | +		s->extralist.first = node->next;  | 
|---|
 | 188 | +	raw_spin_unlock(&s->extralist.lock);  | 
|---|
 | 189 | +	return node;  | 
|---|
 | 190 | +}  | 
|---|
 | 191 | +  | 
|---|
 | 192 | +struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)  | 
|---|
 | 193 | +{  | 
|---|
 | 194 | +	if (in_nmi())  | 
|---|
 | 195 | +		return ___pcpu_freelist_pop_nmi(s);  | 
|---|
 | 196 | +	return ___pcpu_freelist_pop(s);  | 
|---|
| 110 | 197 |  } | 
|---|
| 111 | 198 |   | 
|---|
| 112 | 199 |  struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s) | 
|---|