Whamcloud - gitweb
LU-1346 libcfs: replace libcfs wrappers with kernel API
[fs/lustre-release.git] / libcfs / libcfs / darwin / darwin-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, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  */
30 /*
31  * This file is part of Lustre, http://www.lustre.org/
32  * Lustre is a trademark of Sun Microsystems, Inc.
33  *
34  * libcfs/libcfs/darwin/darwin-mem.c
35  *
36  * Author: Liang Zhen <liangzhen@clusterfs.com>
37  * Author: Nikita Danilov <nikita@clusterfs.com>
38  */
39 #define DEBUG_SUBSYSTEM S_LNET
40
41 #include <mach/mach_types.h>
42 #include <string.h>
43 #include <sys/malloc.h>
44
45 #include <libcfs/libcfs.h>
46 #include "darwin-internal.h"
47
48 #if CFS_INDIVIDUAL_ZONE
49 extern zone_t zinit( vm_size_t, vm_size_t, vm_size_t, const char *);
50 extern void * zalloc(zone_t zone);
51 extern void *zalloc_noblock(zone_t zone);
52 extern void zfree(zone_t zone, void *addr);
53
54 struct cfs_zone_nob {
55         struct list_head       *z_nob;  /* Pointer to z_link */
56         struct list_head        z_link; /* Do NOT access it directly */       
57 };
58
59 static struct cfs_zone_nob      cfs_zone_nob;
60 static spinlock_t               cfs_zone_guard;
61
62 cfs_mem_cache_t *mem_cache_find(const char *name, size_t objsize)
63 {
64         cfs_mem_cache_t         *walker = NULL;
65
66         LASSERT(cfs_zone_nob.z_nob != NULL);
67
68         spin_lock(&cfs_zone_guard);
69         list_for_each_entry(walker, cfs_zone_nob.z_nob, mc_link) {
70                 if (!strcmp(walker->mc_name, name) && \
71                     walker->mc_size == objsize)
72                         break;
73         }
74         spin_unlock(&cfs_zone_guard);
75
76         return walker;
77 }
78
79 /*
80  * our wrapper around kern/zalloc.c:zinit()
81  *
82  * Creates copy of name and calls zinit() to do real work. Needed because zone
83  * survives kext unloading, so that @name cannot be just static string
84  * embedded into kext image.
85  */
86 cfs_mem_cache_t *mem_cache_create(vm_size_t objsize, const char *name)
87 {
88         cfs_mem_cache_t *mc = NULL;
89         char *cname;
90
91         MALLOC(mc, cfs_mem_cache_t *, sizeof(cfs_mem_cache_t), M_TEMP, M_WAITOK|M_ZERO);
92         if (mc == NULL){
93                 CERROR("cfs_mem_cache created fail!\n");
94                 return NULL;
95         }
96
97         cname = _MALLOC(strlen(name) + 1, M_TEMP, M_WAITOK);
98         LASSERT(cname != NULL);
99         mc->mc_cache = zinit(objsize, (KMEM_MAX_ZONE * objsize), 0, strcpy(cname, name));
100         mc->mc_size = objsize;
101         CFS_INIT_LIST_HEAD(&mc->mc_link);
102         strncpy(mc->mc_name, name, 1 + strlen(name));
103         return mc;
104 }
105
106 void mem_cache_destroy(cfs_mem_cache_t *mc)
107 {
108         /*
109          * zone can NOT be destroyed after creating, 
110          * so just keep it in list.
111          *
112          * We will not lost a zone after we unload
113          * libcfs, it can be found by from libcfs.zone
114          */
115         return;
116 }
117
118 #define mem_cache_alloc(mc)     zalloc((mc)->mc_cache)
119 #ifdef __DARWIN8__
120 # define mem_cache_alloc_nb(mc) zalloc((mc)->mc_cache)
121 #else
122 /* XXX Liang: Tiger doesn't export zalloc_noblock() */
123 # define mem_cache_alloc_nb(mc) zalloc_noblock((mc)->mc_cache)
124 #endif
125 #define mem_cache_free(mc, p)   zfree((mc)->mc_cache, p)
126
127 #else  /* !CFS_INDIVIDUAL_ZONE */
128
129 cfs_mem_cache_t *
130 mem_cache_find(const char *name, size_t objsize)
131 {
132         return NULL;
133 }
134
135 cfs_mem_cache_t *mem_cache_create(vm_size_t size, const char *name)
136 {
137         cfs_mem_cache_t *mc = NULL;
138
139         MALLOC(mc, cfs_mem_cache_t *, sizeof(cfs_mem_cache_t), M_TEMP, M_WAITOK|M_ZERO);
140         if (mc == NULL){
141                 CERROR("cfs_mem_cache created fail!\n");
142                 return NULL;
143         }
144         mc->mc_cache = OSMalloc_Tagalloc(name, OSMT_DEFAULT);
145         mc->mc_size = size;
146         return mc;
147 }
148
149 void mem_cache_destroy(cfs_mem_cache_t *mc)
150 {
151         OSMalloc_Tagfree(mc->mc_cache);
152         FREE(mc, M_TEMP);
153 }
154
155 #define mem_cache_alloc(mc)     OSMalloc((mc)->mc_size, (mc)->mc_cache)
156 #define mem_cache_alloc_nb(mc)  OSMalloc_noblock((mc)->mc_size, (mc)->mc_cache)
157 #define mem_cache_free(mc, p)   OSFree(p, (mc)->mc_size, (mc)->mc_cache)
158
159 #endif /* !CFS_INDIVIDUAL_ZONE */
160
161 cfs_mem_cache_t *
162 cfs_mem_cache_create (const char *name,
163                       size_t objsize, size_t off, unsigned long arg1)
164 {
165         cfs_mem_cache_t *mc;
166
167         mc = mem_cache_find(name, objsize);
168         if (mc)
169                 return mc;
170         mc = mem_cache_create(objsize, name);
171         return mc;
172 }
173
174 int cfs_mem_cache_destroy (cfs_mem_cache_t *cachep)
175 {
176         mem_cache_destroy(cachep);
177         return 0;
178 }
179
180 void *cfs_mem_cache_alloc (cfs_mem_cache_t *cachep, int flags)
181 {
182         void *result;
183
184         /* zalloc_canblock() is not exported... Emulate it. */
185         if (flags & CFS_ALLOC_ATOMIC) {
186                 result = (void *)mem_cache_alloc_nb(cachep);
187         } else {
188                 LASSERT(get_preemption_level() == 0);
189                 result = (void *)mem_cache_alloc(cachep);
190         }
191         if (result != NULL && (flags & CFS_ALLOC_ZERO))
192                 memset(result, 0, cachep->mc_size);
193
194         return result;
195 }
196
197 void cfs_mem_cache_free (cfs_mem_cache_t *cachep, void *objp)
198 {
199         mem_cache_free(cachep, objp);
200 }
201
202 /* ---------------------------------------------------------------------------
203  * Page operations
204  *
205  * --------------------------------------------------------------------------- */
206
207 /*
208  * "Raw" pages
209  */
210
211 static unsigned int raw_pages = 0;
212 static cfs_mem_cache_t  *raw_page_cache = NULL;
213
214 static struct xnu_page_ops raw_page_ops;
215 static struct xnu_page_ops *page_ops[XNU_PAGE_NTYPES] = {
216         [XNU_PAGE_RAW] = &raw_page_ops
217 };
218
219 #if defined(LIBCFS_DEBUG)
220 static int page_type_is_valid(cfs_page_t *page)
221 {
222         LASSERT(page != NULL);
223         return 0 <= page->type && page->type < XNU_PAGE_NTYPES;
224 }
225
226 static int page_is_raw(cfs_page_t *page)
227 {
228         return page->type == XNU_PAGE_RAW;
229 }
230 #endif
231
232 static struct xnu_raw_page *as_raw(cfs_page_t *page)
233 {
234         LASSERT(page_is_raw(page));
235         return list_entry(page, struct xnu_raw_page, header);
236 }
237
238 static void *raw_page_address(cfs_page_t *pg)
239 {
240         return (void *)as_raw(pg)->virtual;
241 }
242
243 static void *raw_page_map(cfs_page_t *pg)
244 {
245         return (void *)as_raw(pg)->virtual;
246 }
247
248 static void raw_page_unmap(cfs_page_t *pg)
249 {
250 }
251
252 static struct xnu_page_ops raw_page_ops = {
253         .page_map       = raw_page_map,
254         .page_unmap     = raw_page_unmap,
255         .page_address   = raw_page_address
256 };
257
258 extern int get_preemption_level(void);
259
260 struct list_head page_death_row;
261 spinlock_t page_death_row_phylax;
262
263 static void raw_page_finish(struct xnu_raw_page *pg)
264 {
265         -- raw_pages;
266         if (pg->virtual != NULL)
267                 cfs_mem_cache_free(raw_page_cache, pg->virtual);
268         cfs_free(pg);
269 }
270
271 void raw_page_death_row_clean(void)
272 {
273         struct xnu_raw_page *pg;
274
275         spin_lock(&page_death_row_phylax);
276         while (!list_empty(&page_death_row)) {
277                 pg = container_of(page_death_row.next,
278                                   struct xnu_raw_page, link);
279                 list_del(&pg->link);
280                 spin_unlock(&page_death_row_phylax);
281                 raw_page_finish(pg);
282                 spin_lock(&page_death_row_phylax);
283         }
284         spin_unlock(&page_death_row_phylax);
285 }
286
287 /* Free a "page" */
288 void free_raw_page(struct xnu_raw_page *pg)
289 {
290         if (!atomic_dec_and_test(&pg->count))
291                 return;
292         /*
293          * kmem_free()->vm_map_remove()->vm_map_delete()->lock_write() may
294          * block. (raw_page_done()->upl_abort() can block too) On the other
295          * hand, cfs_free_page() may be called in non-blockable context. To
296          * work around this, park pages on global list when cannot block.
297          */
298         if (get_preemption_level() > 0) {
299                 spin_lock(&page_death_row_phylax);
300                 list_add(&pg->link, &page_death_row);
301                 spin_unlock(&page_death_row_phylax);
302         } else {
303                 raw_page_finish(pg);
304                 raw_page_death_row_clean();
305         }
306 }
307
308 cfs_page_t *cfs_alloc_page(u_int32_t flags)
309 {
310         struct xnu_raw_page *page;
311
312         /*
313          * XXX nikita: do NOT call libcfs_debug_msg() (CDEBUG/ENTRY/EXIT)
314          * from here: this will lead to infinite recursion.
315          */
316
317         page = cfs_alloc(sizeof *page, flags);
318         if (page != NULL) {
319                 page->virtual = cfs_mem_cache_alloc(raw_page_cache, flags);
320                 if (page->virtual != NULL) {
321                         ++ raw_pages;
322                         page->header.type = XNU_PAGE_RAW;
323                         atomic_set(&page->count, 1);
324                 } else {
325                         cfs_free(page);
326                         page = NULL;
327                 }
328         }
329         return page != NULL ? &page->header : NULL;
330 }
331
332 void cfs_free_page(cfs_page_t *pages)
333 {
334         free_raw_page(as_raw(pages));
335 }
336
337 void cfs_get_page(cfs_page_t *p)
338 {
339         atomic_inc(&as_raw(p)->count);
340 }
341
342 int cfs_put_page_testzero(cfs_page_t *p)
343 {
344         return atomic_dec_and_test(&as_raw(p)->count);
345 }
346
347 int cfs_page_count(cfs_page_t *p)
348 {
349         return atomic_read(&as_raw(p)->count);
350 }
351
352 /*
353  * Generic page operations
354  */
355
356 void *cfs_page_address(cfs_page_t *pg)
357 {
358         /*
359          * XXX nikita: do NOT call libcfs_debug_msg() (CDEBUG/ENTRY/EXIT)
360          * from here: this will lead to infinite recursion.
361          */
362         LASSERT(page_type_is_valid(pg));
363         return page_ops[pg->type]->page_address(pg);
364 }
365
366 void *cfs_kmap(cfs_page_t *pg)
367 {
368         LASSERT(page_type_is_valid(pg));
369         return page_ops[pg->type]->page_map(pg);
370 }
371
372 void cfs_kunmap(cfs_page_t *pg)
373 {
374         LASSERT(page_type_is_valid(pg));
375         return page_ops[pg->type]->page_unmap(pg);
376 }
377
378 void xnu_page_ops_register(int type, struct xnu_page_ops *ops)
379 {
380         LASSERT(0 <= type && type < XNU_PAGE_NTYPES);
381         LASSERT(ops != NULL);
382         LASSERT(page_ops[type] == NULL);
383
384         page_ops[type] = ops;
385 }
386
387 void xnu_page_ops_unregister(int type)
388 {
389         LASSERT(0 <= type && type < XNU_PAGE_NTYPES);
390         LASSERT(page_ops[type] != NULL);
391
392         page_ops[type] = NULL;
393 }
394
395 /*
396  * Portable memory allocator API
397  */
398 #ifdef HAVE_GET_PREEMPTION_LEVEL
399 extern int get_preemption_level(void);
400 #else
401 #define get_preemption_level() (0)
402 #endif
403
404 void *cfs_alloc(size_t nr_bytes, u_int32_t flags)
405 {
406         int mflags;
407
408         mflags = 0;
409         if (flags & CFS_ALLOC_ATOMIC) {
410                 mflags |= M_NOWAIT;
411         } else {
412                 LASSERT(get_preemption_level() == 0);
413                 mflags |= M_WAITOK;
414         }
415
416         if (flags & CFS_ALLOC_ZERO)
417                 mflags |= M_ZERO;
418
419         return _MALLOC(nr_bytes, M_TEMP, mflags);
420 }
421
422 void cfs_free(void *addr)
423 {
424         return _FREE(addr, M_TEMP);
425 }
426
427 void *cfs_alloc_large(size_t nr_bytes)
428 {
429         LASSERT(get_preemption_level() == 0);
430         return _MALLOC(nr_bytes, M_TEMP, M_WAITOK);
431 }
432
433 void  cfs_free_large(void *addr)
434 {
435         LASSERT(get_preemption_level() == 0);
436         return _FREE(addr, M_TEMP);
437 }
438
439 /*
440  * Lookup cfs_zone_nob by sysctl.zone, if it cannot be 
441  * found (first load of * libcfs since boot), allocate 
442  * sysctl libcfs.zone.
443  */
444 int cfs_mem_init(void)
445 {
446 #if     CFS_INDIVIDUAL_ZONE
447         int     rc;
448         size_t  len;
449
450         len = sizeof(struct cfs_zone_nob);
451         rc = sysctlbyname("libcfs.zone",
452                           (void *)&cfs_zone_nob, &len, NULL, 0);
453         if (rc == ENOENT) {
454                 /* zone_nob is not register in libcfs_sysctl */
455                 struct cfs_zone_nob  *nob;
456                 struct sysctl_oid       *oid;
457
458                 assert(cfs_sysctl_isvalid());
459
460                 nob = _MALLOC(sizeof(struct cfs_zone_nob), 
461                               M_TEMP, M_WAITOK | M_ZERO);
462                 CFS_INIT_LIST_HEAD(&nob->z_link);
463                 nob->z_nob = &nob->z_link;
464                 oid = cfs_alloc_sysctl_struct(NULL, OID_AUTO, CTLFLAG_RD | CTLFLAG_KERN, 
465                                               "zone", nob, sizeof(struct cfs_zone_nob));
466                 if (oid == NULL) {
467                         _FREE(nob, M_TEMP);
468                         return -ENOMEM;
469                 }
470                 sysctl_register_oid(oid);
471
472                 cfs_zone_nob.z_nob = nob->z_nob;
473         }
474         spin_lock_init(&cfs_zone_guard);
475 #endif
476         CFS_INIT_LIST_HEAD(&page_death_row);
477         spin_lock_init(&page_death_row_phylax);
478         raw_page_cache = cfs_mem_cache_create("raw-page", CFS_PAGE_SIZE, 0, 0);
479         return 0;
480 }
481
482 void cfs_mem_fini(void)
483 {
484         raw_page_death_row_clean();
485         spin_lock_done(&page_death_row_phylax);
486         cfs_mem_cache_destroy(raw_page_cache);
487
488 #if CFS_INDIVIDUAL_ZONE
489         cfs_zone_nob.z_nob = NULL;
490         spin_lock_done(&cfs_zone_guard);
491 #endif
492 }