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