1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 QLogic Corporation.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
29  * Use is subject to license terms.
30  */
31 
32 #include <sys/conf.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/modctl.h>
36 
37 #include <stmf_defines.h>
38 #include <fct_defines.h>
39 #include <stmf.h>
40 #include <portif.h>
41 #include <fct.h>
42 #include <qlt.h>
43 #include <qlt_dma.h>
44 
45 #define	BUF_COUNT_2K		2048
46 #define	BUF_COUNT_8K		512
47 #define	BUF_COUNT_64K		256
48 #define	BUF_COUNT_128K		1024
49 #define	BUF_COUNT_256K		8
50 
51 #define	QLT_DMEM_MAX_BUF_SIZE	(4 * 65536)
52 #define	QLT_DMEM_NBUCKETS	5
53 static qlt_dmem_bucket_t bucket2K	= { 2048, BUF_COUNT_2K },
54 			bucket8K	= { 8192, BUF_COUNT_8K },
55 			bucket64K	= { 65536, BUF_COUNT_64K },
56 			bucket128k	= { (2 * 65536), BUF_COUNT_128K },
57 			bucket256k	= { (4 * 65536), BUF_COUNT_256K };
58 
59 static qlt_dmem_bucket_t *dmem_buckets[] = { &bucket2K, &bucket8K,
60 			&bucket64K, &bucket128k, &bucket256k, NULL };
61 static ddi_device_acc_attr_t acc;
62 static ddi_dma_attr_t qlt_scsi_dma_attr = {
63 	DMA_ATTR_V0,		/* dma_attr_version */
64 	0,			/* low DMA address range */
65 	0xffffffffffffffff,	/* high DMA address range */
66 	0xffffffff,		/* DMA counter register */
67 	8192,			/* DMA address alignment */
68 	0xff,			/* DMA burstsizes */
69 	1,			/* min effective DMA size */
70 	0xffffffff,		/* max DMA xfer size */
71 	0xffffffff,		/* segment boundary */
72 	1,			/* s/g list length */
73 	1,			/* granularity of device */
74 	0			/* DMA transfer flags */
75 };
76 
77 fct_status_t
78 qlt_dmem_init(qlt_state_t *qlt)
79 {
80 	qlt_dmem_bucket_t *p;
81 	qlt_dmem_bctl_t *bctl, *bc;
82 	qlt_dmem_bctl_t *prev;
83 	int ndx, i;
84 	uint32_t total_mem;
85 	uint8_t *addr;
86 	uint8_t *host_addr;
87 	uint64_t dev_addr;
88 	ddi_dma_cookie_t cookie;
89 	uint32_t ncookie;
90 	uint32_t bsize;
91 	size_t len;
92 
93 	if (qlt->qlt_bucketcnt[0] != 0) {
94 		bucket2K.dmem_nbufs = qlt->qlt_bucketcnt[0];
95 	}
96 	if (qlt->qlt_bucketcnt[1] != 0) {
97 		bucket8K.dmem_nbufs = qlt->qlt_bucketcnt[1];
98 	}
99 	if (qlt->qlt_bucketcnt[2] != 0) {
100 		bucket64K.dmem_nbufs = qlt->qlt_bucketcnt[2];
101 	}
102 	if (qlt->qlt_bucketcnt[3] != 0) {
103 		bucket128k.dmem_nbufs = qlt->qlt_bucketcnt[3];
104 	}
105 	if (qlt->qlt_bucketcnt[4] != 0) {
106 		bucket256k.dmem_nbufs = qlt->qlt_bucketcnt[4];
107 	}
108 
109 	bsize = sizeof (dmem_buckets);
110 	ndx = (int)(bsize / sizeof (void *));
111 	/*
112 	 * The reason it is ndx - 1 everywhere is becasue the last bucket
113 	 * pointer is NULL.
114 	 */
115 	qlt->dmem_buckets = (qlt_dmem_bucket_t **)kmem_zalloc(bsize +
116 	    ((ndx - 1) * (int)sizeof (qlt_dmem_bucket_t)), KM_SLEEP);
117 	for (i = 0; i < (ndx - 1); i++) {
118 		qlt->dmem_buckets[i] = (qlt_dmem_bucket_t *)
119 		    ((uint8_t *)qlt->dmem_buckets + bsize +
120 		    (i * (int)sizeof (qlt_dmem_bucket_t)));
121 		bcopy(dmem_buckets[i], qlt->dmem_buckets[i],
122 		    sizeof (qlt_dmem_bucket_t));
123 	}
124 	bzero(&acc, sizeof (acc));
125 	acc.devacc_attr_version = DDI_DEVICE_ATTR_V0;
126 	acc.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
127 	acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
128 	for (ndx = 0; (p = qlt->dmem_buckets[ndx]) != NULL; ndx++) {
129 		bctl = (qlt_dmem_bctl_t *)kmem_zalloc(p->dmem_nbufs *
130 		    sizeof (qlt_dmem_bctl_t), KM_NOSLEEP);
131 		if (bctl == NULL) {
132 			EL(qlt, "bctl==NULL\n");
133 			goto alloc_bctl_failed;
134 		}
135 		p->dmem_bctls_mem = bctl;
136 		mutex_init(&p->dmem_lock, NULL, MUTEX_DRIVER, NULL);
137 		if ((i = ddi_dma_alloc_handle(qlt->dip, &qlt_scsi_dma_attr,
138 		    DDI_DMA_SLEEP, 0, &p->dmem_dma_handle)) != DDI_SUCCESS) {
139 			EL(qlt, "ddi_dma_alloc_handle status=%xh\n", i);
140 			goto alloc_handle_failed;
141 		}
142 
143 		total_mem = p->dmem_buf_size * p->dmem_nbufs;
144 
145 		if ((i = ddi_dma_mem_alloc(p->dmem_dma_handle, total_mem, &acc,
146 		    DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0, (caddr_t *)&addr,
147 		    &len, &p->dmem_acc_handle)) != DDI_SUCCESS) {
148 			EL(qlt, "ddi_dma_mem_alloc status=%xh\n", i);
149 			goto mem_alloc_failed;
150 		}
151 
152 		if ((i = ddi_dma_addr_bind_handle(p->dmem_dma_handle, NULL,
153 		    (caddr_t)addr, total_mem, DDI_DMA_RDWR | DDI_DMA_STREAMING,
154 		    DDI_DMA_DONTWAIT, 0, &cookie, &ncookie)) != DDI_SUCCESS) {
155 			EL(qlt, "ddi_dma_addr_bind_handle status=%xh\n", i);
156 			goto addr_bind_handle_failed;
157 		}
158 		if (ncookie != 1) {
159 			EL(qlt, "ncookie=%d\n", ncookie);
160 			goto dmem_init_failed;
161 		}
162 
163 		p->dmem_host_addr = host_addr = addr;
164 		p->dmem_dev_addr = dev_addr = (uint64_t)cookie.dmac_laddress;
165 		bsize = p->dmem_buf_size;
166 		p->dmem_bctl_free_list = bctl;
167 		p->dmem_nbufs_free = p->dmem_nbufs;
168 		for (i = 0; i < p->dmem_nbufs; i++) {
169 			stmf_data_buf_t *db;
170 			prev = bctl;
171 			bctl->bctl_bucket = p;
172 			bctl->bctl_buf = db = stmf_alloc(STMF_STRUCT_DATA_BUF,
173 			    0, 0);
174 			db->db_port_private = bctl;
175 			db->db_sglist[0].seg_addr = host_addr;
176 			bctl->bctl_dev_addr = dev_addr;
177 			db->db_sglist[0].seg_length = db->db_buf_size = bsize;
178 			db->db_sglist_length = 1;
179 			host_addr += bsize;
180 			dev_addr += bsize;
181 			bctl++;
182 			prev->bctl_next = bctl;
183 		}
184 		prev->bctl_next = NULL;
185 	}
186 
187 	return (QLT_SUCCESS);
188 
189 dmem_failure_loop:;
190 	bc = bctl;
191 	while (bc) {
192 		stmf_free(bc->bctl_buf);
193 		bc = bc->bctl_next;
194 	}
195 dmem_init_failed:;
196 	(void) ddi_dma_unbind_handle(p->dmem_dma_handle);
197 addr_bind_handle_failed:;
198 	ddi_dma_mem_free(&p->dmem_acc_handle);
199 mem_alloc_failed:;
200 	ddi_dma_free_handle(&p->dmem_dma_handle);
201 alloc_handle_failed:;
202 	kmem_free(p->dmem_bctls_mem, p->dmem_nbufs * sizeof (qlt_dmem_bctl_t));
203 	mutex_destroy(&p->dmem_lock);
204 alloc_bctl_failed:;
205 	if (--ndx >= 0) {
206 		p = qlt->dmem_buckets[ndx];
207 		bctl = p->dmem_bctl_free_list;
208 		goto dmem_failure_loop;
209 	}
210 	kmem_free(qlt->dmem_buckets, sizeof (dmem_buckets) +
211 	    ((sizeof (dmem_buckets)/sizeof (void *))
212 	    *sizeof (qlt_dmem_bucket_t)));
213 	qlt->dmem_buckets = NULL;
214 
215 	return (QLT_FAILURE);
216 }
217 
218 void
219 qlt_dmem_fini(qlt_state_t *qlt)
220 {
221 	qlt_dmem_bucket_t *p;
222 	qlt_dmem_bctl_t *bctl;
223 	int ndx;
224 
225 	for (ndx = 0; (p = qlt->dmem_buckets[ndx]) != NULL; ndx++) {
226 		bctl = p->dmem_bctl_free_list;
227 		while (bctl) {
228 			stmf_free(bctl->bctl_buf);
229 			bctl = bctl->bctl_next;
230 		}
231 		bctl = p->dmem_bctl_free_list;
232 		(void) ddi_dma_unbind_handle(p->dmem_dma_handle);
233 		ddi_dma_mem_free(&p->dmem_acc_handle);
234 		ddi_dma_free_handle(&p->dmem_dma_handle);
235 		kmem_free(p->dmem_bctls_mem,
236 		    p->dmem_nbufs * sizeof (qlt_dmem_bctl_t));
237 		mutex_destroy(&p->dmem_lock);
238 	}
239 	kmem_free(qlt->dmem_buckets, sizeof (dmem_buckets) +
240 	    (((sizeof (dmem_buckets)/sizeof (void *))-1)*
241 	    sizeof (qlt_dmem_bucket_t)));
242 	qlt->dmem_buckets = NULL;
243 }
244 
245 stmf_data_buf_t *
246 qlt_dmem_alloc(fct_local_port_t *port, uint32_t size, uint32_t *pminsize,
247     uint32_t flags)
248 {
249 	return (qlt_i_dmem_alloc((qlt_state_t *)
250 	    port->port_fca_private, size, pminsize,
251 	    flags));
252 }
253 
254 /* ARGSUSED */
255 stmf_data_buf_t *
256 qlt_i_dmem_alloc(qlt_state_t *qlt, uint32_t size, uint32_t *pminsize,
257 					uint32_t flags)
258 {
259 	qlt_dmem_bucket_t	*p;
260 	qlt_dmem_bctl_t 	*bctl;
261 	int			i;
262 	uint32_t		size_possible = 0;
263 
264 	if (size > QLT_DMEM_MAX_BUF_SIZE) {
265 		goto qlt_try_partial_alloc;
266 	}
267 
268 	/* 1st try to do a full allocation */
269 	for (i = 0; (p = qlt->dmem_buckets[i]) != NULL; i++) {
270 		if (p->dmem_buf_size >= size) {
271 			if (p->dmem_nbufs_free) {
272 				mutex_enter(&p->dmem_lock);
273 				bctl = p->dmem_bctl_free_list;
274 				if (bctl == NULL) {
275 					mutex_exit(&p->dmem_lock);
276 					continue;
277 				}
278 				p->dmem_bctl_free_list =
279 				    bctl->bctl_next;
280 				p->dmem_nbufs_free--;
281 				qlt->qlt_bufref[i]++;
282 				mutex_exit(&p->dmem_lock);
283 				bctl->bctl_buf->db_data_size = size;
284 				return (bctl->bctl_buf);
285 			} else {
286 				qlt->qlt_bumpbucket++;
287 			}
288 		}
289 	}
290 
291 qlt_try_partial_alloc:
292 
293 	qlt->qlt_pmintry++;
294 
295 	/* Now go from high to low */
296 	for (i = QLT_DMEM_NBUCKETS - 1; i >= 0; i--) {
297 		p = qlt->dmem_buckets[i];
298 		if (p->dmem_nbufs_free == 0)
299 			continue;
300 		if (!size_possible) {
301 			size_possible = p->dmem_buf_size;
302 		}
303 		if (*pminsize > p->dmem_buf_size) {
304 			/* At this point we know the request is failing. */
305 			if (size_possible) {
306 				/*
307 				 * This caller is asking too much. We already
308 				 * know what we can give, so get out.
309 				 */
310 				break;
311 			} else {
312 				/*
313 				 * Lets continue to find out and tell what
314 				 * we can give.
315 				 */
316 				continue;
317 			}
318 		}
319 		mutex_enter(&p->dmem_lock);
320 		if (*pminsize <= p->dmem_buf_size) {
321 			bctl = p->dmem_bctl_free_list;
322 			if (bctl == NULL) {
323 				/* Someone took it. */
324 				size_possible = 0;
325 				mutex_exit(&p->dmem_lock);
326 				continue;
327 			}
328 			p->dmem_bctl_free_list = bctl->bctl_next;
329 			p->dmem_nbufs_free--;
330 			mutex_exit(&p->dmem_lock);
331 			bctl->bctl_buf->db_data_size = p->dmem_buf_size;
332 			qlt->qlt_pmin_ok++;
333 			return (bctl->bctl_buf);
334 		}
335 	}
336 
337 	*pminsize = size_possible;
338 
339 	return (NULL);
340 }
341 
342 /* ARGSUSED */
343 void
344 qlt_i_dmem_free(qlt_state_t *qlt, stmf_data_buf_t *dbuf)
345 {
346 	qlt_dmem_free(0, dbuf);
347 }
348 
349 /* ARGSUSED */
350 void
351 qlt_dmem_free(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf)
352 {
353 	qlt_dmem_bctl_t *bctl = (qlt_dmem_bctl_t *)dbuf->db_port_private;
354 	qlt_dmem_bucket_t *p = bctl->bctl_bucket;
355 
356 	mutex_enter(&p->dmem_lock);
357 	bctl->bctl_next = p->dmem_bctl_free_list;
358 	p->dmem_bctl_free_list = bctl;
359 	p->dmem_nbufs_free++;
360 	mutex_exit(&p->dmem_lock);
361 }
362 
363 void
364 qlt_dmem_dma_sync(stmf_data_buf_t *dbuf, uint_t sync_type)
365 {
366 	qlt_dmem_bctl_t *bctl = (qlt_dmem_bctl_t *)dbuf->db_port_private;
367 	qlt_dmem_bucket_t *p = bctl->bctl_bucket;
368 
369 	(void) ddi_dma_sync(p->dmem_dma_handle, (off_t)
370 	    (bctl->bctl_dev_addr - p->dmem_dev_addr),
371 	    dbuf->db_data_size, sync_type);
372 }
373