e8fb9087deb8b819b3d33f4369f9ba66278d8de0
[deb_dpdk.git] / lib / librte_eal / bsdapp / contigmem / contigmem.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/param.h>
38 #include <sys/bio.h>
39 #include <sys/bus.h>
40 #include <sys/conf.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43 #include <sys/module.h>
44 #include <sys/proc.h>
45 #include <sys/rwlock.h>
46 #include <sys/systm.h>
47 #include <sys/sysctl.h>
48
49 #include <machine/bus.h>
50
51 #include <vm/vm.h>
52 #include <vm/pmap.h>
53 #include <vm/vm_param.h>
54 #include <vm/vm_object.h>
55 #include <vm/vm_page.h>
56 #include <vm/vm_pager.h>
57 #include <vm/vm_phys.h>
58
59 struct contigmem_buffer {
60         void           *addr;
61         int             refcnt;
62         struct mtx      mtx;
63 };
64
65 struct contigmem_vm_handle {
66         int             buffer_index;
67 };
68
69 static int              contigmem_load(void);
70 static int              contigmem_unload(void);
71 static int              contigmem_physaddr(SYSCTL_HANDLER_ARGS);
72
73 static d_mmap_single_t  contigmem_mmap_single;
74 static d_open_t         contigmem_open;
75 static d_close_t        contigmem_close;
76
77 static int              contigmem_num_buffers = RTE_CONTIGMEM_DEFAULT_NUM_BUFS;
78 static int64_t          contigmem_buffer_size = RTE_CONTIGMEM_DEFAULT_BUF_SIZE;
79
80 static eventhandler_tag contigmem_eh_tag;
81 static struct contigmem_buffer contigmem_buffers[RTE_CONTIGMEM_MAX_NUM_BUFS];
82 static struct cdev     *contigmem_cdev = NULL;
83 static int              contigmem_refcnt;
84
85 TUNABLE_INT("hw.contigmem.num_buffers", &contigmem_num_buffers);
86 TUNABLE_QUAD("hw.contigmem.buffer_size", &contigmem_buffer_size);
87
88 static SYSCTL_NODE(_hw, OID_AUTO, contigmem, CTLFLAG_RD, 0, "contigmem");
89
90 SYSCTL_INT(_hw_contigmem, OID_AUTO, num_buffers, CTLFLAG_RD,
91         &contigmem_num_buffers, 0, "Number of contigmem buffers allocated");
92 SYSCTL_QUAD(_hw_contigmem, OID_AUTO, buffer_size, CTLFLAG_RD,
93         &contigmem_buffer_size, 0, "Size of each contiguous buffer");
94 SYSCTL_INT(_hw_contigmem, OID_AUTO, num_references, CTLFLAG_RD,
95         &contigmem_refcnt, 0, "Number of references to contigmem");
96
97 static SYSCTL_NODE(_hw_contigmem, OID_AUTO, physaddr, CTLFLAG_RD, 0,
98         "physaddr");
99
100 MALLOC_DEFINE(M_CONTIGMEM, "contigmem", "contigmem(4) allocations");
101
102 static int contigmem_modevent(module_t mod, int type, void *arg)
103 {
104         int error = 0;
105
106         switch (type) {
107         case MOD_LOAD:
108                 error = contigmem_load();
109                 break;
110         case MOD_UNLOAD:
111                 error = contigmem_unload();
112                 break;
113         default:
114                 break;
115         }
116
117         return error;
118 }
119
120 moduledata_t contigmem_mod = {
121         "contigmem",
122         (modeventhand_t)contigmem_modevent,
123         0
124 };
125
126 DECLARE_MODULE(contigmem, contigmem_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
127 MODULE_VERSION(contigmem, 1);
128
129 static struct cdevsw contigmem_ops = {
130         .d_name         = "contigmem",
131         .d_version      = D_VERSION,
132         .d_flags        = D_TRACKCLOSE,
133         .d_mmap_single  = contigmem_mmap_single,
134         .d_open         = contigmem_open,
135         .d_close        = contigmem_close,
136 };
137
138 static int
139 contigmem_load()
140 {
141         char index_string[8], description[32];
142         int  i, error = 0;
143         void *addr;
144
145         if (contigmem_num_buffers > RTE_CONTIGMEM_MAX_NUM_BUFS) {
146                 printf("%d buffers requested is greater than %d allowed\n",
147                                 contigmem_num_buffers, RTE_CONTIGMEM_MAX_NUM_BUFS);
148                 error = EINVAL;
149                 goto error;
150         }
151
152         if (contigmem_buffer_size < PAGE_SIZE ||
153                         (contigmem_buffer_size & (contigmem_buffer_size - 1)) != 0) {
154                 printf("buffer size 0x%lx is not greater than PAGE_SIZE and "
155                                 "power of two\n", contigmem_buffer_size);
156                 error = EINVAL;
157                 goto error;
158         }
159
160         for (i = 0; i < contigmem_num_buffers; i++) {
161                 addr = contigmalloc(contigmem_buffer_size, M_CONTIGMEM, M_ZERO,
162                         0, BUS_SPACE_MAXADDR, contigmem_buffer_size, 0);
163                 if (addr == NULL) {
164                         printf("contigmalloc failed for buffer %d\n", i);
165                         error = ENOMEM;
166                         goto error;
167                 }
168
169                 printf("%2u: virt=%p phys=%p\n", i, addr,
170                         (void *)pmap_kextract((vm_offset_t)addr));
171
172                 mtx_init(&contigmem_buffers[i].mtx, "contigmem", NULL, MTX_DEF);
173                 contigmem_buffers[i].addr = addr;
174                 contigmem_buffers[i].refcnt = 0;
175
176                 snprintf(index_string, sizeof(index_string), "%d", i);
177                 snprintf(description, sizeof(description),
178                                 "phys addr for buffer %d", i);
179                 SYSCTL_ADD_PROC(NULL,
180                                 &SYSCTL_NODE_CHILDREN(_hw_contigmem, physaddr), OID_AUTO,
181                                 index_string, CTLTYPE_U64 | CTLFLAG_RD,
182                                 (void *)(uintptr_t)i, 0, contigmem_physaddr, "LU",
183                                 description);
184         }
185
186         contigmem_cdev = make_dev_credf(0, &contigmem_ops, 0, NULL, UID_ROOT,
187                         GID_WHEEL, 0600, "contigmem");
188
189         return 0;
190
191 error:
192         for (i = 0; i < contigmem_num_buffers; i++) {
193                 if (contigmem_buffers[i].addr != NULL)
194                         contigfree(contigmem_buffers[i].addr,
195                                 contigmem_buffer_size, M_CONTIGMEM);
196                 if (mtx_initialized(&contigmem_buffers[i].mtx))
197                         mtx_destroy(&contigmem_buffers[i].mtx);
198         }
199
200         return error;
201 }
202
203 static int
204 contigmem_unload()
205 {
206         int i;
207
208         if (contigmem_refcnt > 0)
209                 return EBUSY;
210
211         if (contigmem_cdev != NULL)
212                 destroy_dev(contigmem_cdev);
213
214         if (contigmem_eh_tag != NULL)
215                 EVENTHANDLER_DEREGISTER(process_exit, contigmem_eh_tag);
216
217         for (i = 0; i < RTE_CONTIGMEM_MAX_NUM_BUFS; i++) {
218                 if (contigmem_buffers[i].addr != NULL)
219                         contigfree(contigmem_buffers[i].addr,
220                                 contigmem_buffer_size, M_CONTIGMEM);
221                 if (mtx_initialized(&contigmem_buffers[i].mtx))
222                         mtx_destroy(&contigmem_buffers[i].mtx);
223         }
224
225         return 0;
226 }
227
228 static int
229 contigmem_physaddr(SYSCTL_HANDLER_ARGS)
230 {
231         uint64_t        physaddr;
232         int             index = (int)(uintptr_t)arg1;
233
234         physaddr = (uint64_t)vtophys(contigmem_buffers[index].addr);
235         return sysctl_handle_64(oidp, &physaddr, 0, req);
236 }
237
238 static int
239 contigmem_open(struct cdev *cdev, int fflags, int devtype,
240                 struct thread *td)
241 {
242
243         atomic_add_int(&contigmem_refcnt, 1);
244
245         return 0;
246 }
247
248 static int
249 contigmem_close(struct cdev *cdev, int fflags, int devtype,
250                 struct thread *td)
251 {
252
253         atomic_subtract_int(&contigmem_refcnt, 1);
254
255         return 0;
256 }
257
258 static int
259 contigmem_cdev_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
260                 vm_ooffset_t foff, struct ucred *cred, u_short *color)
261 {
262         struct contigmem_vm_handle *vmh = handle;
263         struct contigmem_buffer *buf;
264
265         buf = &contigmem_buffers[vmh->buffer_index];
266
267         atomic_add_int(&contigmem_refcnt, 1);
268
269         mtx_lock(&buf->mtx);
270         if (buf->refcnt == 0)
271                 memset(buf->addr, 0, contigmem_buffer_size);
272         buf->refcnt++;
273         mtx_unlock(&buf->mtx);
274
275         return 0;
276 }
277
278 static void
279 contigmem_cdev_pager_dtor(void *handle)
280 {
281         struct contigmem_vm_handle *vmh = handle;
282         struct contigmem_buffer *buf;
283
284         buf = &contigmem_buffers[vmh->buffer_index];
285
286         mtx_lock(&buf->mtx);
287         buf->refcnt--;
288         mtx_unlock(&buf->mtx);
289
290         free(vmh, M_CONTIGMEM);
291
292         atomic_subtract_int(&contigmem_refcnt, 1);
293 }
294
295 static int
296 contigmem_cdev_pager_fault(vm_object_t object, vm_ooffset_t offset, int prot,
297                 vm_page_t *mres)
298 {
299         vm_paddr_t paddr;
300         vm_page_t m_paddr, page;
301         vm_memattr_t memattr, memattr1;
302
303         memattr = object->memattr;
304
305         VM_OBJECT_WUNLOCK(object);
306
307         paddr = offset;
308
309         m_paddr = vm_phys_paddr_to_vm_page(paddr);
310         if (m_paddr != NULL) {
311                 memattr1 = pmap_page_get_memattr(m_paddr);
312                 if (memattr1 != memattr)
313                         memattr = memattr1;
314         }
315
316         if (((*mres)->flags & PG_FICTITIOUS) != 0) {
317                 /*
318                  * If the passed in result page is a fake page, update it with
319                  * the new physical address.
320                  */
321                 page = *mres;
322                 VM_OBJECT_WLOCK(object);
323                 vm_page_updatefake(page, paddr, memattr);
324         } else {
325                 vm_page_t mret;
326                 /*
327                  * Replace the passed in reqpage page with our own fake page and
328                  * free up the original page.
329                  */
330                 page = vm_page_getfake(paddr, memattr);
331                 VM_OBJECT_WLOCK(object);
332                 mret = vm_page_replace(page, object, (*mres)->pindex);
333                 KASSERT(mret == *mres,
334                     ("invalid page replacement, old=%p, ret=%p", *mres, mret));
335                 vm_page_lock(mret);
336                 vm_page_free(mret);
337                 vm_page_unlock(mret);
338                 *mres = page;
339         }
340
341         page->valid = VM_PAGE_BITS_ALL;
342
343         return VM_PAGER_OK;
344 }
345
346 static struct cdev_pager_ops contigmem_cdev_pager_ops = {
347         .cdev_pg_ctor = contigmem_cdev_pager_ctor,
348         .cdev_pg_dtor = contigmem_cdev_pager_dtor,
349         .cdev_pg_fault = contigmem_cdev_pager_fault,
350 };
351
352 static int
353 contigmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
354                 struct vm_object **obj, int nprot)
355 {
356         struct contigmem_vm_handle *vmh;
357         uint64_t buffer_index;
358
359         /*
360          * The buffer index is encoded in the offset.  Divide the offset by
361          *  PAGE_SIZE to get the index of the buffer requested by the user
362          *  app.
363          */
364         buffer_index = *offset / PAGE_SIZE;
365         if (buffer_index >= contigmem_num_buffers)
366                 return EINVAL;
367
368         if (size > contigmem_buffer_size)
369                 return EINVAL;
370
371         vmh = malloc(sizeof(*vmh), M_CONTIGMEM, M_NOWAIT | M_ZERO);
372         if (vmh == NULL)
373                 return ENOMEM;
374         vmh->buffer_index = buffer_index;
375
376         *offset = (vm_ooffset_t)vtophys(contigmem_buffers[buffer_index].addr);
377         *obj = cdev_pager_allocate(vmh, OBJT_DEVICE, &contigmem_cdev_pager_ops,
378                         size, nprot, *offset, curthread->td_ucred);
379
380         return 0;
381 }