1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source. A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * This file is part of the Chelsio T4 support code.
14  *
15  * Copyright (C) 2011-2013 Chelsio Communications.  All rights reserved.
16  *
17  * This program is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
20  * release for licensing terms and conditions.
21  */
22 
23 #include <sys/ddi.h>
24 #include <sys/sunddi.h>
25 #include <sys/atomic.h>
26 #include <sys/types.h>
27 #include <sys/kmem.h>
28 
29 #include "osdep.h"
30 #include "shared.h"
31 
32 static int rxbuf_ctor(void *, void *, int);
33 static void rxbuf_dtor(void *, void *);
34 
35 int
cxgb_printf(dev_info_t * dip,int level,char * f,...)36 cxgb_printf(dev_info_t *dip, int level, char *f, ...)
37 {
38 	va_list list;
39 	char fmt[128];
40 	int rv;
41 
42 	rv = snprintf(fmt, sizeof (fmt), "%s%d: %s", ddi_driver_name(dip),
43 	    ddi_get_instance(dip), f);
44 	va_start(list, f);
45 	vcmn_err(level, fmt, list);
46 	va_end(list);
47 	return (rv);
48 }
49 
50 kmem_cache_t *
rxbuf_cache_create(struct rxbuf_cache_params * p)51 rxbuf_cache_create(struct rxbuf_cache_params *p)
52 {
53 	char name[32];
54 
55 	(void) snprintf(name, sizeof (name), "%s%d_rxbuf_cache",
56 	    ddi_driver_name(p->dip), ddi_get_instance(p->dip));
57 
58 	return kmem_cache_create(name, sizeof (struct rxbuf), CACHE_LINE,
59 	    rxbuf_ctor, rxbuf_dtor, NULL, p, NULL, 0);
60 }
61 
62 void
rxbuf_cache_destroy(kmem_cache_t * cache)63 rxbuf_cache_destroy(kmem_cache_t *cache)
64 {
65 	kmem_cache_destroy(cache);
66 }
67 
68 /*
69  * If ref_cnt is more than 1 then those many calls to rxbuf_free will
70  * have to be made before the rxb is released back to the kmem_cache.
71  */
72 struct rxbuf *
rxbuf_alloc(kmem_cache_t * cache,int kmflags,uint_t ref_cnt)73 rxbuf_alloc(kmem_cache_t *cache, int kmflags, uint_t ref_cnt)
74 {
75 	struct rxbuf *rxb;
76 
77 	ASSERT(ref_cnt > 0);
78 
79 	rxb = kmem_cache_alloc(cache, kmflags);
80 	if (rxb != NULL) {
81 		rxb->ref_cnt = ref_cnt;
82 		rxb->cache = cache;
83 	}
84 
85 	return (rxb);
86 }
87 
88 /*
89  * This is normally called via the rxb's freefunc, when an mblk referencing the
90  * rxb is freed.
91  */
92 void
rxbuf_free(struct rxbuf * rxb)93 rxbuf_free(struct rxbuf *rxb)
94 {
95 	if (atomic_dec_uint_nv(&rxb->ref_cnt) == 0)
96 		kmem_cache_free(rxb->cache, rxb);
97 }
98 
99 static int
rxbuf_ctor(void * arg1,void * arg2,int kmflag)100 rxbuf_ctor(void *arg1, void *arg2, int kmflag)
101 {
102 	struct rxbuf *rxb = arg1;
103 	struct rxbuf_cache_params *p = arg2;
104 	size_t real_len;
105 	ddi_dma_cookie_t cookie;
106 	uint_t ccount = 0;
107 	int (*callback)(caddr_t);
108 	int rc = ENOMEM;
109 
110 	if ((kmflag & KM_NOSLEEP) != 0)
111 		callback = DDI_DMA_DONTWAIT;
112 	else
113 		callback = DDI_DMA_SLEEP;
114 
115 	rc = ddi_dma_alloc_handle(p->dip, &p->dma_attr_rx, callback, 0,
116 	    &rxb->dhdl);
117 	if (rc != DDI_SUCCESS)
118 		return (rc == DDI_DMA_BADATTR ? EINVAL : ENOMEM);
119 
120 	rc = ddi_dma_mem_alloc(rxb->dhdl, p->buf_size, &p->acc_attr_rx,
121 	    DDI_DMA_STREAMING, callback, 0, &rxb->va, &real_len, &rxb->ahdl);
122 	if (rc != DDI_SUCCESS) {
123 		rc = ENOMEM;
124 		goto fail1;
125 	}
126 
127 	rc = ddi_dma_addr_bind_handle(rxb->dhdl, NULL, rxb->va, p->buf_size,
128 	    DDI_DMA_READ | DDI_DMA_STREAMING, NULL, NULL, &cookie, &ccount);
129 	if (rc != DDI_DMA_MAPPED) {
130 		if (rc == DDI_DMA_INUSE)
131 			rc = EBUSY;
132 		else if (rc == DDI_DMA_TOOBIG)
133 			rc = E2BIG;
134 		else
135 			rc = ENOMEM;
136 		goto fail2;
137 	}
138 
139 	if (ccount != 1) {
140 		rc = E2BIG;
141 		goto fail3;
142 	}
143 
144 	rxb->ref_cnt = 0;
145 	rxb->buf_size = p->buf_size;
146 	rxb->freefunc.free_arg = (caddr_t)rxb;
147 	rxb->freefunc.free_func = rxbuf_free;
148 	rxb->ba = cookie.dmac_laddress;
149 
150 	return (0);
151 
152 fail3:	(void) ddi_dma_unbind_handle(rxb->dhdl);
153 fail2:	ddi_dma_mem_free(&rxb->ahdl);
154 fail1:	ddi_dma_free_handle(&rxb->dhdl);
155 	return (rc);
156 }
157 
158 /* ARGSUSED */
159 static void
rxbuf_dtor(void * arg1,void * arg2)160 rxbuf_dtor(void *arg1, void *arg2)
161 {
162 	struct rxbuf *rxb = arg1;
163 
164 	(void) ddi_dma_unbind_handle(rxb->dhdl);
165 	ddi_dma_mem_free(&rxb->ahdl);
166 	ddi_dma_free_handle(&rxb->dhdl);
167 }
168