uipc_mvec.c revision 168496
1/**************************************************************************
2 *
3 * Copyright (c) 2007, Kip Macy kmacy@freebsd.org
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 *    this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 *
29 ***************************************************************************/
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/sys/dev/cxgb/sys/uipc_mvec.c 168496 2007-04-08 15:59:07Z kmacy $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/lock.h>
38#include <sys/malloc.h>
39#include <sys/mbuf.h>
40#include <sys/ktr.h>
41
42#include <machine/bus.h>
43
44#include <dev/cxgb/sys/mvec.h>
45
46int
47_m_explode(struct mbuf *m)
48{
49        int i, offset, type;
50        void *cl;
51        struct mbuf *m0, *head = NULL;
52        struct mbuf_vec *mv;
53
54        mv = mtomv(m);
55        for (i = mv->mv_count + mv->mv_first - 1;
56             i > mv->mv_first; i--) {
57                cl = mv->mv_vec[i].mi_base;
58                if ((m0 = m_get(M_NOWAIT, MT_DATA)) == NULL) {
59                        m_freem(head);
60                        return (ENOMEM);
61                }
62		m0->m_flags = 0;
63		type = mbuf_vec_get_type(mv, i);
64		m_cljset(m0, (uint8_t *)cl, type);
65		m0->m_len = mv->mv_vec[i].mi_size;
66
67		offset = mv->mv_vec[i].mi_offset;
68
69		if (offset)
70			m_adj(m, offset);
71
72		m0->m_next = head;
73		m->m_len -= m0->m_len;
74		head = m0;
75	}
76	offset = mv->mv_vec[0].mi_offset;
77	cl = mv->mv_vec[0].mi_base;
78	type = mbuf_vec_get_type(mv, 0);
79	m->m_flags &= ~(M_IOVEC);
80	m_cljset(m, cl, type);
81	if (offset)
82		m_adj(m, offset);
83	m->m_next = head;
84
85	return (0);
86}
87
88#define MAX_BUFS 36
89
90int
91_m_collapse(struct mbuf *m, int maxbufs, struct mbuf **mnew)
92{
93	struct mbuf *m0, *lvec[MAX_BUFS];
94	struct mbuf **mnext, **vec = &lvec[0];
95	struct mbuf *mhead = NULL;
96	struct mbuf_vec *mv;
97	int i, j, max;
98
99	if (maxbufs > MAX_BUFS)
100		if ((vec = malloc(maxbufs * sizeof(struct mbuf *),
101			    M_DEVBUF, M_NOWAIT)) == NULL)
102			return (ENOMEM);
103
104	m0 = m;
105	for (i = 0; i < maxbufs; i++) {
106		if (m0 == NULL)
107			goto batch;
108		vec[i] = m0;
109		m0 = m0->m_next;
110	}
111
112	if (i == maxbufs)
113		return (EFBIG);
114batch:
115	max = i;
116	i = 0;
117	m0 = NULL;
118	mnext = NULL;
119	while (i < max) {
120		if ((vec[i]->m_flags & M_EXT) == 0) {
121			m0 = m_get(M_NOWAIT, MT_DATA);
122		} else {
123			m0 = vec[i];
124			m0->m_flags = (vec[i]->m_flags & ~M_EXT);
125		}
126		m0->m_flags |= M_IOVEC;
127		if (m0 == NULL)
128			goto m_getfail;
129		if (i == 0)
130			mhead = m0;
131		if (mnext)
132			*mnext = m0;
133		mv = mtomv(m0);
134		mv->mv_count = mv->mv_first = 0;
135		for (j = 0; j < MAX_MBUF_IOV; j++, i++) {
136			if (vec[i]->m_flags & M_EXT) {
137				mv->mv_vec[j].mi_base = vec[i]->m_ext.ext_buf;
138				mv->mv_vec[j].mi_offset =
139				    (vec[i]->m_ext.ext_buf - vec[i]->m_data);
140				mv->mv_vec[j].mi_size = vec[i]->m_ext.ext_size;
141				mv->mv_vec[j].mi_flags = vec[i]->m_ext.ext_type;
142			} else {
143				mv->mv_vec[j].mi_base = (caddr_t)vec[i];
144				mv->mv_vec[j].mi_offset =
145				    ((caddr_t)vec[i] - vec[i]->m_data);
146				mv->mv_vec[j].mi_size = MSIZE;
147				mv->mv_vec[j].mi_flags = EXT_MBUF;
148			}
149		}
150		mnext = &m0->m_next;
151	}
152
153	mhead->m_flags |= (m0->m_flags & M_PKTHDR);
154	*mnew = mhead;
155	return (0);
156
157m_getfail:
158	m0 = mhead;
159	while (mhead) {
160		mhead = m0->m_next;
161		uma_zfree(zone_mbuf, m0);
162	}
163	return (ENOMEM);
164}
165
166void
167mb_free_vec(struct mbuf *m)
168{
169	struct mbuf_vec *mv;
170	int i;
171
172	KASSERT((m->m_flags & (M_EXT|M_IOVEC)) == M_IOVEC,
173	    ("%s: M_IOVEC not set", __func__));
174
175	mv = mtomv(m);
176	KASSERT(mv->mv_count <= MAX_MBUF_IOV,
177	    ("%s: mi_count too large %d", __func__, mv->mv_count));
178
179	for (i = mv->mv_first; i < mv->mv_count; i++) {
180		uma_zone_t zone = NULL;
181		int *refcnt;
182		int type = mbuf_vec_get_type(mv, i);
183		void *cl = mv->mv_vec[i].mi_base;
184		int size = mv->mv_vec[i].mi_size;
185
186		zone = m_getzone(size);
187		refcnt = uma_find_refcnt(zone, cl);
188		if (*refcnt != 1 && atomic_fetchadd_int(refcnt, -1) != 1)
189			continue;
190
191		switch (type) {
192		case EXT_PACKET:	/* The packet zone is special. */
193			if (*refcnt == 0)
194				*refcnt = 1;
195			uma_zfree(zone_pack, m);
196			return;		/* Job done. */
197		case EXT_CLUSTER:
198		case EXT_JUMBOP:
199		case EXT_JUMBO9:
200		case EXT_JUMBO16:
201			uma_zfree(zone, cl);
202			continue;
203		case EXT_SFBUF:
204			*refcnt = 0;
205			uma_zfree(zone_ext_refcnt, __DEVOLATILE(u_int *,
206				refcnt));
207			/* FALLTHROUGH */
208		case EXT_EXTREF:
209#ifdef notyet
210			KASSERT(m->m_ext.ext_free != NULL,
211				("%s: ext_free not set", __func__));
212			(*(m->m_ext.ext_free))(m->m_ext.ext_buf,
213			    m->m_ext.ext_args);
214#endif
215			/*
216			 * XXX
217			 */
218			panic("unsupported mbuf_vec type: %d\n", type);
219			break;
220		default:
221			KASSERT(m->m_ext.ext_type == 0,
222				("%s: unknown ext_type", __func__));
223
224		}
225	}
226	/*
227	 * Free this mbuf back to the mbuf zone with all m_ext
228	 * information purged.
229	 */
230	m->m_flags &= ~M_IOVEC;
231	uma_zfree(zone_mbuf, m);
232}
233
234struct mvec_sg_cb_arg {
235	int error;
236	bus_dma_segment_t seg;
237	int nseg;
238};
239
240struct bus_dma_tag {
241	bus_dma_tag_t	  parent;
242	bus_size_t	  alignment;
243	bus_size_t	  boundary;
244	bus_addr_t	  lowaddr;
245	bus_addr_t	  highaddr;
246	bus_dma_filter_t *filter;
247	void		 *filterarg;
248	bus_size_t	  maxsize;
249	u_int		  nsegments;
250	bus_size_t	  maxsegsz;
251	int		  flags;
252	int		  ref_count;
253	int		  map_count;
254	bus_dma_lock_t	 *lockfunc;
255	void		 *lockfuncarg;
256	bus_dma_segment_t *segments;
257	struct bounce_zone *bounce_zone;
258};
259
260static void
261mvec_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
262{
263	struct mvec_sg_cb_arg *cb_arg = arg;
264
265	cb_arg->error = error;
266	cb_arg->seg = segs[0];
267	cb_arg->nseg = nseg;
268
269}
270
271int
272bus_dmamap_load_mvec_sg(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0,
273                        bus_dma_segment_t *segs, int *nsegs, int flags)
274{
275	int error;
276	struct mbuf_vec *mv;
277	struct mvec_sg_cb_arg cb_arg;
278
279	M_ASSERTPKTHDR(m0);
280
281	flags |= BUS_DMA_NOWAIT;
282	*nsegs = 0;
283	error = 0;
284	if (m0->m_pkthdr.len <=
285	    dmat->maxsize) {
286		struct mbuf *m;
287
288		for (m = m0; m != NULL && error == 0; m = m->m_next) {
289			int count, first, i;
290			if (!(m->m_len > 0))
291				continue;
292
293			mv = mtomv(m);
294			count = mv->mv_count;
295			first = mv->mv_first;
296			for (i = first; i < count; i++) {
297				void *data = mv->mv_vec[i].mi_base;
298				int size = mv->mv_vec[i].mi_size;
299
300				cb_arg.seg = *segs;
301				error = bus_dmamap_load(dmat, map,
302				    data, size, mvec_cb, &cb_arg, flags);
303				segs++;
304				*nsegs++;
305				if (error || cb_arg.error)
306					goto err_out;
307			}
308		}
309	} else {
310		error = EINVAL;
311	}
312
313	/* XXX FIXME: Having to increment nsegs is really annoying */
314	++*nsegs;
315	CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
316	    __func__, dmat, dmat->flags, error, *nsegs);
317	return (error);
318
319err_out:
320	if (cb_arg.error)
321		return (cb_arg.error);
322
323	return (error);
324}
325