Whamcloud - gitweb
LU-12564 libcfs: Use vfree_atomic instead of vfree
[fs/lustre-release.git] / libcfs / libcfs / libcfs_mem.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 021110-1307, USA
20  *
21  * GPL HEADER END
22  */
23 /*
24  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
25  * Copyright (c) 2012, Intel Corporation.
26  */
27 /*
28  * This file is part of Lustre, http://www.lustre.org/
29  * Lustre is a trademark of Sun Microsystems, Inc.
30  *
31  * Author: liang@whamcloud.com
32  */
33
34 #define DEBUG_SUBSYSTEM S_LNET
35
36 #include <linux/workqueue.h>
37 #include <libcfs/libcfs.h>
38
39 struct cfs_var_array {
40         unsigned int            va_count;       /* # of buffers */
41         unsigned int            va_size;        /* size of each var */
42         struct cfs_cpt_table    *va_cptab;      /* cpu partition table */
43         void                    *va_ptrs[0];    /* buffer addresses */
44 };
45
46 /*
47  * free per-cpu data, see more detail in cfs_percpt_free
48  */
49 void
50 cfs_percpt_free(void *vars)
51 {
52         struct  cfs_var_array *arr;
53         int     i;
54
55         arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
56
57         for (i = 0; i < arr->va_count; i++) {
58                 if (arr->va_ptrs[i] != NULL)
59                         LIBCFS_FREE(arr->va_ptrs[i], arr->va_size);
60         }
61
62         LIBCFS_FREE(arr, offsetof(struct cfs_var_array,
63                                   va_ptrs[arr->va_count]));
64 }
65 EXPORT_SYMBOL(cfs_percpt_free);
66
67 /*
68  * allocate per cpu-partition variables, returned value is an array of pointers,
69  * variable can be indexed by CPU partition ID, i.e:
70  *
71  *      arr = cfs_percpt_alloc(cfs_cpu_pt, size);
72  *      then caller can access memory block for CPU 0 by arr[0],
73  *      memory block for CPU 1 by arr[1]...
74  *      memory block for CPU N by arr[N]...
75  *
76  * cacheline aligned.
77  */
78 void *
79 cfs_percpt_alloc(struct cfs_cpt_table *cptab, unsigned int size)
80 {
81         struct cfs_var_array    *arr;
82         int                     count;
83         int                     i;
84
85         count = cfs_cpt_number(cptab);
86
87         LIBCFS_ALLOC(arr, offsetof(struct cfs_var_array, va_ptrs[count]));
88         if (arr == NULL)
89                 return NULL;
90
91         arr->va_size    = size = L1_CACHE_ALIGN(size);
92         arr->va_count   = count;
93         arr->va_cptab   = cptab;
94
95         for (i = 0; i < count; i++) {
96                 LIBCFS_CPT_ALLOC(arr->va_ptrs[i], cptab, i, size);
97                 if (arr->va_ptrs[i] == NULL) {
98                         cfs_percpt_free((void *)&arr->va_ptrs[0]);
99                         return NULL;
100                 }
101         }
102
103         return (void *)&arr->va_ptrs[0];
104 }
105 EXPORT_SYMBOL(cfs_percpt_alloc);
106
107 /*
108  * return number of CPUs (or number of elements in per-cpu data)
109  * according to cptab of @vars
110  */
111 int
112 cfs_percpt_number(void *vars)
113 {
114         struct cfs_var_array *arr;
115
116         arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
117
118         return arr->va_count;
119 }
120 EXPORT_SYMBOL(cfs_percpt_number);
121
122 /*
123  * free variable array, see more detail in cfs_array_alloc
124  */
125 void
126 cfs_array_free(void *vars)
127 {
128         struct cfs_var_array    *arr;
129         int                     i;
130
131         arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
132
133         for (i = 0; i < arr->va_count; i++) {
134                 if (arr->va_ptrs[i] == NULL)
135                         continue;
136
137                 LIBCFS_FREE(arr->va_ptrs[i], arr->va_size);
138         }
139         LIBCFS_FREE(arr, offsetof(struct cfs_var_array,
140                                   va_ptrs[arr->va_count]));
141 }
142 EXPORT_SYMBOL(cfs_array_free);
143
144 /*
145  * allocate a variable array, returned value is an array of pointers.
146  * Caller can specify length of array by @count, @size is size of each
147  * memory block in array.
148  */
149 void *
150 cfs_array_alloc(int count, unsigned int size)
151 {
152         struct cfs_var_array    *arr;
153         int                     i;
154
155         LIBCFS_ALLOC(arr, offsetof(struct cfs_var_array, va_ptrs[count]));
156         if (arr == NULL)
157                 return NULL;
158
159         arr->va_count   = count;
160         arr->va_size    = size;
161
162         for (i = 0; i < count; i++) {
163                 LIBCFS_ALLOC(arr->va_ptrs[i], size);
164
165                 if (arr->va_ptrs[i] == NULL) {
166                         cfs_array_free((void *)&arr->va_ptrs[0]);
167                         return NULL;
168                 }
169         }
170
171         return (void *)&arr->va_ptrs[0];
172 }
173 EXPORT_SYMBOL(cfs_array_alloc);
174
175 /*
176  * This is opencoding of vfree_atomic from Linux kernel added in 4.10 with
177  * minimum changes needed to work on older kernels too.
178  */
179
180 #ifndef raw_cpu_ptr
181 #define raw_cpu_ptr(p) __this_cpu_ptr(p)
182 #endif
183
184 #ifndef llist_for_each_safe
185 #define llist_for_each_safe(pos, n, node)                       \
186         for ((pos) = (node); (pos) && ((n) = (pos)->next, true); (pos) = (n))
187 #endif
188
189 struct vfree_deferred {
190         struct llist_head list;
191         struct work_struct wq;
192 };
193 static DEFINE_PER_CPU(struct vfree_deferred, vfree_deferred);
194
195 static void free_work(struct work_struct *w)
196 {
197         struct vfree_deferred *p = container_of(w, struct vfree_deferred, wq);
198         struct llist_node *t, *llnode;
199
200         llist_for_each_safe(llnode, t, llist_del_all(&p->list))
201                 vfree((void *)llnode);
202 }
203
204 void libcfs_vfree_atomic(const void *addr)
205 {
206         struct vfree_deferred *p = raw_cpu_ptr(&vfree_deferred);
207
208         if (!addr)
209                 return;
210
211         if (llist_add((struct llist_node *)addr, &p->list))
212                 schedule_work(&p->wq);
213 }
214 EXPORT_SYMBOL(libcfs_vfree_atomic);
215
216 void __init init_libcfs_vfree_atomic(void)
217 {
218         int i;
219
220         for_each_possible_cpu(i) {
221                 struct vfree_deferred *p;
222
223                 p = &per_cpu(vfree_deferred, i);
224                 init_llist_head(&p->list);
225                 INIT_WORK(&p->wq, free_work);
226         }
227 }
228
229 void __exit exit_libcfs_vfree_atomic(void)
230 {
231         flush_scheduled_work();
232 }