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