Whamcloud - gitweb
* Landed portals:b_port_step as follows...
[fs/lustre-release.git] / lnet / 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: Phil Schwan <phil@clusterfs.com>
6  *
7  * This file is part of Lustre, http://www.lustre.org.
8  *
9  * Lustre is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU General Public
11  * License as published by the Free Software Foundation.
12  *
13  * Lustre is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Lustre; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * Darwin porting library
23  * Make things easy to port
24  */
25 #define DEBUG_SUBSYSTEM S_PORTALS
26
27 #include <mach/mach_types.h>
28 #include <string.h>
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
31 #include <sys/file.h>
32 #include <sys/conf.h>
33 #include <sys/vnode.h>
34 #include <sys/uio.h>
35 #include <sys/filedesc.h>
36 #include <sys/namei.h>
37 #include <miscfs/devfs/devfs.h>
38 #include <kern/kalloc.h>
39 #include <kern/zalloc.h>
40 #include <kern/thread.h>
41
42 #include <libcfs/libcfs.h>
43 #include <libcfs/kp30.h>
44
45 /*
46  * Definition of struct zone, copied from osfmk/kern/zalloc.h.
47  */
48 struct zone_hack {
49         int             count;          /* Number of elements used now */
50         vm_offset_t     free_elements;
51         vm_size_t       cur_size;       /* current memory utilization */
52         vm_size_t       max_size;       /* how large can this zone grow */
53         vm_size_t       elem_size;      /* size of an element */
54         vm_size_t       alloc_size;     /* size used for more memory */
55         char            *zone_name;     /* a name for the zone */
56         unsigned int
57         /* boolean_t */ exhaustible :1, /* (F) merely return if empty? */
58         /* boolean_t */ collectable :1, /* (F) garbage collect empty pages */
59         /* boolean_t */ expandable :1,  /* (T) expand zone (with message)? */
60         /* boolean_t */ allows_foreign :1,/* (F) allow non-zalloc space */
61         /* boolean_t */ doing_alloc :1, /* is zone expanding now? */
62         /* boolean_t */ waiting :1,     /* is thread waiting for expansion? */
63         /* boolean_t */ async_pending :1;       /* asynchronous allocation pending? */
64         struct zone_hack *      next_zone;      /* Link for all-zones list */
65         /*
66          * more fields follow, but we don't need them. We only need
67          * offset from the beginning of struct zone to ->next_zone
68          * field: it allows us to scan the list of all zones.
69          */
70 };
71
72 decl_simple_lock_data(extern, all_zones_lock)
73
74 /*
75  * returns true iff zone with name @name already exists.
76  *
77  * XXX nikita: this function is defined in this file only because there is no
78  * better place to put it in.
79  */
80 zone_t cfs_find_zone(const char *name)
81 {
82         struct zone_hack *scan;
83
84         /* from osfmk/kern/zalloc.c */
85         extern zone_t first_zone;
86
87         LASSERT(name != NULL);
88
89         simple_lock(&all_zones_lock);
90         for (scan = (struct zone_hack *)first_zone;
91              scan != NULL; scan = scan->next_zone) {
92                 if (!strcmp(scan->zone_name, name))
93                         break;
94         }
95         simple_unlock(&all_zones_lock);
96         return((zone_t)scan);
97 }
98
99 /*
100  * our wrapper around kern/zalloc.c:zinit()
101  *
102  * Creates copy of name and calls zinit() to do real work. Needed because zone
103  * survives kext unloading, so that @name cannot be just static string
104  * embedded into kext image.
105  */
106 zone_t cfs_zinit(vm_size_t size, vm_size_t max, int alloc, const char *name)
107 {
108         char *cname;
109
110         cname = _MALLOC(strlen(name) + 1, M_TEMP, M_WAITOK);
111         LASSERT(cname != NULL);
112         return zinit(size, max, alloc, strcpy(cname, name));
113 }
114
115 cfs_mem_cache_t *
116 cfs_mem_cache_create (const char *name, size_t objsize, size_t off, unsigned long arg1,
117                 void (*arg2)(void *, cfs_mem_cache_t *, unsigned long),
118                 void (*arg3)(void *, cfs_mem_cache_t *, unsigned long))
119 {
120         cfs_mem_cache_t *new = NULL;
121
122         MALLOC(new, cfs_mem_cache_t *, objsize, M_TEMP, M_WAITOK|M_ZERO);
123         if (new == NULL){
124                 CERROR("cfs_mem_cache created fail!\n");
125                 return NULL;
126         }
127         new->size = objsize;
128         CFS_INIT_LIST_HEAD(&new->link);
129         strncpy(new->name, name, 1 + strlen(name));
130         new->zone = cfs_find_zone(name);
131         if (new->zone == NULL) {
132                 new->zone = cfs_zinit (objsize, KMEM_MAX_ZONE * objsize, 0, name);
133                 if (new->zone == NULL) {
134                         CERROR("zone create fault!\n");
135                         FREE (new, M_TEMP);
136                         return NULL;
137                 }
138         }
139         return new;
140 }
141
142 int
143 cfs_mem_cache_destroy (cfs_mem_cache_t *cachep)
144 {
145         FREE (cachep, M_TEMP);
146         return 0;
147 }
148
149 void *
150 cfs_mem_cache_alloc (cfs_mem_cache_t *cachep, int flags)
151 {
152         return (void *)zalloc(cachep->zone);
153 }
154
155 void
156 cfs_mem_cache_free (cfs_mem_cache_t *cachep, void *objp)
157 {
158         zfree (cachep->zone, (vm_address_t)objp);
159 }
160
161 /* ---------------------------------------------------------------------------
162  * Page operations
163  *
164  * --------------------------------------------------------------------------- */
165
166 /*
167  * "Raw" pages
168  */
169
170 extern vm_map_t zone_map;
171 static inline vm_map_t page_map(struct xnu_raw_page *pg)
172 {
173         LASSERT(pg != NULL);
174
175         return pg->order == 0 ? zone_map : kernel_map;
176 }
177
178 static int raw_page_init(struct xnu_raw_page *pg)
179 {
180         vm_size_t size = (1UL << pg->order) * PAGE_SIZE;
181         int upl_flags = UPL_SET_INTERNAL |
182                 UPL_SET_LITE | UPL_SET_IO_WIRE | UPL_COPYOUT_FROM;
183         int     kr = 0;
184
185         /* XXX is it necessary? */
186         kr = vm_map_get_upl(page_map(pg),
187                             pg->virtual, &size, &pg->upl, 0, 0, &upl_flags, 0);
188         return kr;
189 }
190
191 static void raw_page_done(struct xnu_raw_page *pg)
192 {
193         ubc_upl_abort(pg->upl, UPL_ABORT_FREE_ON_EMPTY);
194         return;
195 }
196
197 static struct xnu_page_ops raw_page_ops;
198 static struct xnu_page_ops *page_ops[XNU_PAGE_NTYPES] = {
199         [XNU_PAGE_RAW] = &raw_page_ops
200 };
201
202 static int page_type_is_valid(cfs_page_t *page)
203 {
204         LASSERT(page != NULL);
205         return 0 <= page->type && page->type < XNU_PAGE_NTYPES;
206 }
207
208 static int page_is_raw(cfs_page_t *page)
209 {
210         return page->type == XNU_PAGE_RAW;
211 }
212
213 static struct xnu_raw_page *as_raw(cfs_page_t *page)
214 {
215         LASSERT(page_is_raw(page));
216         return list_entry(page, struct xnu_raw_page, header);
217 }
218
219 static void *raw_page_address(cfs_page_t *pg)
220 {
221         return (void *)as_raw(pg)->virtual;
222 }
223
224 static void *raw_page_map(cfs_page_t *pg)
225 {
226         return (void *)as_raw(pg)->virtual;
227 }
228
229 static void raw_page_unmap(cfs_page_t *pg)
230 {
231 }
232
233 static struct xnu_page_ops raw_page_ops = {
234         .page_map       = raw_page_map,
235         .page_unmap     = raw_page_unmap,
236         .page_address   = raw_page_address
237 };
238
239
240 extern vm_size_t kalloc_max;
241 extern vm_size_t kalloc_max_prerounded;
242 extern int first_k_zone;
243 extern struct zone *k_zone[16];
244 extern vm_offset_t zalloc_canblock( register zone_t, boolean_t );
245 extern vm_map_t zone_map;
246
247 static inline vm_address_t
248 page_zone_alloc(int flags, int order)
249 {
250         register int zindex;
251         register vm_size_t allocsize;
252         vm_size_t size = (1UL << order) * PAGE_SIZE;
253         vm_address_t    addr;
254         kern_return_t   kr;
255
256         assert(order >= 0);
257         if (size > PAGE_SIZE){
258                 /* XXX Liang:
259                  * zalloc_canblock() call kernel_memory_allocate to allocate
260                  * pages, kernel_memory_allocate cannot guarantee contig pages!
261                  * So any request bigger then PAGE_SIZE should not call zalloc()
262                  *
263                  * NB. kmem_alloc_contig could be very slow!!!! Anyway, I dont
264                  * know what will happen if order >= 1 :-(
265                  * */
266                 CDEBUG(D_MALLOC, "Allocate contig pages!\n");
267                 kr = kmem_alloc_contig(kernel_map, &addr, size, 0, 0);
268                 if (kr)
269                         return 0;
270                 return addr;
271         }
272         allocsize = KALLOC_MINSIZE;
273         zindex = first_k_zone;
274         while (allocsize < size) {
275                 allocsize <<= 1;
276                 zindex++;
277         }
278         assert(allocsize < kalloc_max);
279         if (flags & M_NOWAIT != 0)
280                 addr = zalloc_canblock(k_zone[zindex], FALSE);
281         else
282                 addr = zalloc_canblock(k_zone[zindex], TRUE);
283         return addr;
284 }
285
286 /* Allocate a "page", actually upl of darwin */
287 struct xnu_raw_page *alloc_raw_pages(u_int32_t flags, u_int32_t order)
288 {
289         kern_return_t   kr;
290         vm_size_t size = (1UL << order) * PAGE_SIZE;
291         u_int32_t mflags = 0;
292         struct xnu_raw_page *pg;
293
294         if (flags & CFS_ALLOC_ATOMIC != 0)
295                 mflags |= M_NOWAIT;
296         else
297                 mflags |= M_WAITOK;
298         if (flags & CFS_ALLOC_ZERO != 0)
299                 mflags |= M_ZERO;
300
301         MALLOC (pg, struct xnu_raw_page *, sizeof *pg, M_TEMP, mflags);
302         if (pg == NULL)
303                 return NULL;
304         pg->header.type = XNU_PAGE_RAW;
305         pg->order = order;
306         cfs_set_page_count(&pg->header, 1);
307         pg->virtual = page_zone_alloc(flags, order);
308         if (!pg->virtual)
309                 /*
310                  * XXX nikita: Liang, shouldn't pg be freed here?
311                  */
312                 return NULL;
313
314         kr = raw_page_init(pg);
315         if (kr != 0) {
316                 size = (1UL << order) * PAGE_SIZE;
317                 kmem_free(page_map(pg), pg->virtual, size);
318                 return NULL;
319         }
320         return pg;
321 }
322
323 /* Free a "page" */
324 void free_raw_pages(struct xnu_raw_page *pg, u_int32_t order)
325 {
326         vm_size_t size = (1UL << order) * PAGE_SIZE;
327
328         if (!atomic_dec_and_test(&pg->count))
329                 return;
330         raw_page_done(pg);
331         kmem_free(page_map(pg), pg->virtual, size);
332         FREE(pg, M_TEMP);
333 }
334
335 cfs_page_t *cfs_alloc_pages(u_int32_t flags, u_int32_t order)
336 {
337         return &alloc_raw_pages(flags, order)->header;
338 }
339
340 cfs_page_t *cfs_alloc_page(u_int32_t flags)
341 {
342         return cfs_alloc_pages(flags, 0);
343 }
344
345 void cfs_free_pages(cfs_page_t *pages, int order)
346 {
347         free_raw_pages(as_raw(pages), order);
348 }
349
350 void cfs_free_page(cfs_page_t *page)
351 {
352         cfs_free_pages(page, 0);
353 }
354
355 void cfs_get_page(cfs_page_t *p)
356 {
357         atomic_inc(&as_raw(p)->count);
358 }
359
360 int cfs_put_page_testzero(cfs_page_t *p)
361 {
362         return atomic_dec_and_test(&as_raw(p)->count);
363 }
364
365 int cfs_page_count(cfs_page_t *p)
366 {
367         return atomic_read(&as_raw(p)->count);
368 }
369
370 void cfs_set_page_count(cfs_page_t *p, int v)
371 {
372         atomic_set(&as_raw(p)->count, v);
373 }
374
375 /*
376  * Generic page operations
377  */
378
379 void *cfs_page_address(cfs_page_t *pg)
380 {
381         LASSERT(page_type_is_valid(pg));
382         return page_ops[pg->type]->page_address(pg);
383 }
384
385 void *cfs_kmap(cfs_page_t *pg)
386 {
387         LASSERT(page_type_is_valid(pg));
388         return page_ops[pg->type]->page_map(pg);
389 }
390
391 void cfs_kunmap(cfs_page_t *pg)
392 {
393         LASSERT(page_type_is_valid(pg));
394         return page_ops[pg->type]->page_unmap(pg);
395 }
396
397 void xnu_page_ops_register(int type, struct xnu_page_ops *ops)
398 {
399         LASSERT(0 <= type && type < XNU_PAGE_NTYPES);
400         LASSERT(ops != NULL);
401         LASSERT(page_ops[type] == NULL);
402
403         page_ops[type] = ops;
404 }
405
406 void xnu_page_ops_unregister(int type)
407 {
408         LASSERT(0 <= type && type < XNU_PAGE_NTYPES);
409         LASSERT(page_ops[type] != NULL);
410
411         page_ops[type] = NULL;
412 }
413
414 /*
415  * Portable memory allocator API
416  */
417 #ifdef HAVE_GET_PREEMPTION_LEVEL
418 extern int get_preemption_level(void);
419 #else
420 #define get_preemption_level() (0)
421 #endif
422
423 void *cfs_alloc(size_t nr_bytes, u_int32_t flags)
424 {
425         int mflags;
426
427         mflags = 0;
428         if (flags & CFS_ALLOC_ATOMIC != 0) {
429                 mflags |= 0 /* M_NOWAIT */;
430         } else {
431                 LASSERT(get_preemption_level() == 0);
432                 mflags |= M_WAITOK;
433         }
434
435         if (flags & CFS_ALLOC_ZERO != 0)
436                 mflags |= M_ZERO;
437
438         return _MALLOC(nr_bytes, M_TEMP, mflags);
439 }
440
441 void cfs_free(void *addr)
442 {
443         return _FREE(addr, M_TEMP);
444 }
445
446 void *cfs_alloc_large(size_t nr_bytes)
447 {
448         LASSERT(get_preemption_level() == 0);
449         return _MALLOC(nr_bytes, M_TEMP, M_WAITOK);
450 }
451
452 void  cfs_free_large(void *addr)
453 {
454         return _FREE(addr, M_TEMP);
455 }