1 | /* $NetBSD: ixgbe_netbsd.c,v 1.3 2015/02/04 09:05:53 msaitoh Exp $ */ |
2 | /* |
3 | * Copyright (c) 2011 The NetBSD Foundation, Inc. |
4 | * All rights reserved. |
5 | * |
6 | * This code is derived from software contributed to The NetBSD Foundation |
7 | * by Coyote Point Systems, Inc. |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer in the |
16 | * documentation and/or other materials provided with the distribution. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
19 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
20 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
21 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
22 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
28 | * POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | #include <sys/param.h> |
31 | |
32 | #include <sys/atomic.h> |
33 | #include <sys/bus.h> |
34 | #include <sys/condvar.h> |
35 | #include <sys/cpu.h> |
36 | #include <sys/kmem.h> |
37 | #include <sys/mbuf.h> |
38 | #include <sys/mutex.h> |
39 | #include <sys/queue.h> |
40 | #include <sys/workqueue.h> |
41 | |
42 | #include "ixgbe_netbsd.h" |
43 | |
44 | void |
45 | ixgbe_dma_tag_destroy(ixgbe_dma_tag_t *dt) |
46 | { |
47 | kmem_free(dt, sizeof(*dt)); |
48 | } |
49 | |
50 | int |
51 | ixgbe_dma_tag_create(bus_dma_tag_t dmat, bus_size_t alignment, |
52 | bus_size_t boundary, bus_size_t maxsize, int nsegments, |
53 | bus_size_t maxsegsize, int flags, ixgbe_dma_tag_t **dtp) |
54 | { |
55 | ixgbe_dma_tag_t *dt; |
56 | |
57 | *dtp = NULL; |
58 | |
59 | if ((dt = kmem_zalloc(sizeof(*dt), KM_SLEEP)) == NULL) |
60 | return ENOMEM; |
61 | |
62 | dt->dt_dmat = dmat; |
63 | dt->dt_alignment = alignment; |
64 | dt->dt_boundary = boundary; |
65 | dt->dt_maxsize = maxsize; |
66 | dt->dt_nsegments = nsegments; |
67 | dt->dt_maxsegsize = maxsegsize; |
68 | dt->dt_flags = flags; |
69 | *dtp = dt; |
70 | |
71 | return 0; |
72 | } |
73 | |
74 | void |
75 | ixgbe_dmamap_destroy(ixgbe_dma_tag_t *dt, bus_dmamap_t dmam) |
76 | { |
77 | bus_dmamap_destroy(dt->dt_dmat, dmam); |
78 | } |
79 | |
80 | void |
81 | ixgbe_dmamap_sync(ixgbe_dma_tag_t *dt, bus_dmamap_t dmam, int ops) |
82 | { |
83 | bus_dmamap_sync(dt->dt_dmat, dmam, 0, dt->dt_maxsize, ops); |
84 | } |
85 | |
86 | void |
87 | ixgbe_dmamap_unload(ixgbe_dma_tag_t *dt, bus_dmamap_t dmam) |
88 | { |
89 | bus_dmamap_unload(dt->dt_dmat, dmam); |
90 | } |
91 | |
92 | int |
93 | ixgbe_dmamap_create(ixgbe_dma_tag_t *dt, int flags, bus_dmamap_t *dmamp) |
94 | { |
95 | return bus_dmamap_create(dt->dt_dmat, dt->dt_maxsize, dt->dt_nsegments, |
96 | dt->dt_maxsegsize, dt->dt_boundary, flags, dmamp); |
97 | } |
98 | |
99 | static void |
100 | ixgbe_putext(ixgbe_extmem_t *em) |
101 | { |
102 | ixgbe_extmem_head_t *eh = em->em_head; |
103 | |
104 | mutex_enter(&eh->eh_mtx); |
105 | |
106 | TAILQ_INSERT_HEAD(&eh->eh_freelist, em, em_link); |
107 | |
108 | mutex_exit(&eh->eh_mtx); |
109 | |
110 | return; |
111 | } |
112 | |
113 | static ixgbe_extmem_t * |
114 | ixgbe_getext(ixgbe_extmem_head_t *eh, size_t size) |
115 | { |
116 | ixgbe_extmem_t *em; |
117 | |
118 | mutex_enter(&eh->eh_mtx); |
119 | |
120 | TAILQ_FOREACH(em, &eh->eh_freelist, em_link) { |
121 | if (em->em_size >= size) |
122 | break; |
123 | } |
124 | |
125 | if (em != NULL) |
126 | TAILQ_REMOVE(&eh->eh_freelist, em, em_link); |
127 | |
128 | mutex_exit(&eh->eh_mtx); |
129 | |
130 | return em; |
131 | } |
132 | |
133 | static ixgbe_extmem_t * |
134 | ixgbe_newext(ixgbe_extmem_head_t *eh, bus_dma_tag_t dmat, size_t size) |
135 | { |
136 | ixgbe_extmem_t *em; |
137 | int nseg, rc; |
138 | |
139 | em = kmem_zalloc(sizeof(*em), KM_SLEEP); |
140 | |
141 | if (em == NULL) |
142 | return NULL; |
143 | |
144 | rc = bus_dmamem_alloc(dmat, size, PAGE_SIZE, 0, &em->em_seg, 1, &nseg, |
145 | BUS_DMA_WAITOK); |
146 | |
147 | if (rc != 0) |
148 | goto post_zalloc_err; |
149 | |
150 | rc = bus_dmamem_map(dmat, &em->em_seg, 1, size, &em->em_vaddr, |
151 | BUS_DMA_WAITOK); |
152 | |
153 | if (rc != 0) |
154 | goto post_dmamem_err; |
155 | |
156 | em->em_dmat = dmat; |
157 | em->em_size = size; |
158 | em->em_head = eh; |
159 | |
160 | return em; |
161 | post_dmamem_err: |
162 | bus_dmamem_free(dmat, &em->em_seg, 1); |
163 | post_zalloc_err: |
164 | kmem_free(em, sizeof(*em)); |
165 | return NULL; |
166 | } |
167 | |
168 | void |
169 | ixgbe_jcl_reinit(ixgbe_extmem_head_t *eh, bus_dma_tag_t dmat, int nbuf, |
170 | size_t size) |
171 | { |
172 | int i; |
173 | ixgbe_extmem_t *em; |
174 | |
175 | if (!eh->eh_initialized) { |
176 | TAILQ_INIT(&eh->eh_freelist); |
177 | mutex_init(&eh->eh_mtx, MUTEX_DEFAULT, IPL_NET); |
178 | eh->eh_initialized = true; |
179 | } |
180 | |
181 | while ((em = ixgbe_getext(eh, 0)) != NULL) { |
182 | KASSERT(em->em_vaddr != NULL); |
183 | bus_dmamem_unmap(dmat, em->em_vaddr, em->em_size); |
184 | bus_dmamem_free(dmat, &em->em_seg, 1); |
185 | memset(em, 0, sizeof(*em)); |
186 | kmem_free(em, sizeof(*em)); |
187 | } |
188 | |
189 | for (i = 0; i < nbuf; i++) { |
190 | if ((em = ixgbe_newext(eh, dmat, size)) == NULL) { |
191 | printf("%s: only %d of %d jumbo buffers allocated\n" , |
192 | __func__, i, nbuf); |
193 | break; |
194 | } |
195 | ixgbe_putext(em); |
196 | } |
197 | } |
198 | |
199 | static void |
200 | ixgbe_jcl_free(struct mbuf *m, void *buf, size_t size, void *arg) |
201 | { |
202 | ixgbe_extmem_t *em = arg; |
203 | |
204 | KASSERT(em->em_size == size); |
205 | |
206 | ixgbe_putext(em); |
207 | /* this is an abstraction violation, but it does not lead to a |
208 | * double-free |
209 | */ |
210 | if (__predict_true(m != NULL)) { |
211 | KASSERT(m->m_type != MT_FREE); |
212 | m->m_type = MT_FREE; |
213 | pool_cache_put(mb_cache, m); |
214 | } |
215 | } |
216 | |
217 | /* XXX need to wait for the system to finish with each jumbo mbuf and |
218 | * free it before detaching the driver from the device. |
219 | */ |
220 | struct mbuf * |
221 | ixgbe_getjcl(ixgbe_extmem_head_t *eh, int nowait /* M_DONTWAIT */, |
222 | int type /* MT_DATA */, int flags /* M_PKTHDR */, size_t size) |
223 | { |
224 | ixgbe_extmem_t *em; |
225 | struct mbuf *m; |
226 | |
227 | if ((flags & M_PKTHDR) != 0) |
228 | m = m_gethdr(nowait, type); |
229 | else |
230 | m = m_get(nowait, type); |
231 | |
232 | if (m == NULL) |
233 | return NULL; |
234 | |
235 | em = ixgbe_getext(eh, size); |
236 | if (em == NULL) { |
237 | m_freem(m); |
238 | return NULL; |
239 | } |
240 | |
241 | MEXTADD(m, em->em_vaddr, em->em_size, M_DEVBUF, &ixgbe_jcl_free, em); |
242 | |
243 | if ((m->m_flags & M_EXT) == 0) { |
244 | ixgbe_putext(em); |
245 | m_freem(m); |
246 | return NULL; |
247 | } |
248 | |
249 | return m; |
250 | } |
251 | |