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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
29  */
30 
31 #include <sys/conf.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/modctl.h>
35 
36 #include <stmf_defines.h>
37 #include <fct_defines.h>
38 #include <stmf.h>
39 #include <portif.h>
40 #include <fct.h>
41 #include <qlt.h>
42 #include <qlt_dma.h>
43 
44 /*
45  *  Local Function Prototypes.
46  */
47 static void
48 qlt_dma_free_handles(qlt_state_t *qlt, qlt_dma_handle_t *first_handle);
49 
50 #define	BUF_COUNT_2K		2048
51 #define	BUF_COUNT_8K		512
52 #define	BUF_COUNT_64K		256
53 #define	BUF_COUNT_128K		1024
54 #define	BUF_COUNT_256K		8
55 
56 #define	QLT_DMEM_MAX_BUF_SIZE	(4 * 65536)
57 #define	QLT_DMEM_NBUCKETS	5
58 static qlt_dmem_bucket_t bucket2K	= { 2048, BUF_COUNT_2K },
59 			bucket8K	= { 8192, BUF_COUNT_8K },
60 			bucket64K	= { 65536, BUF_COUNT_64K },
61 			bucket128k	= { (2 * 65536), BUF_COUNT_128K },
62 			bucket256k	= { (4 * 65536), BUF_COUNT_256K };
63 
64 static qlt_dmem_bucket_t *dmem_buckets[] = { &bucket2K, &bucket8K,
65 			&bucket64K, &bucket128k, &bucket256k, NULL };
66 static ddi_device_acc_attr_t acc;
67 static ddi_dma_attr_t qlt_scsi_dma_attr = {
68 	DMA_ATTR_V0,		/* dma_attr_version */
69 	0,			/* low DMA address range */
70 	0xffffffffffffffff,	/* high DMA address range */
71 	0xffffffff,		/* DMA counter register */
72 	8192,			/* DMA address alignment */
73 	0xff,			/* DMA burstsizes */
74 	1,			/* min effective DMA size */
75 	0xffffffff,		/* max DMA xfer size */
76 	0xffffffff,		/* segment boundary */
77 	1,			/* s/g list length */
78 	1,			/* granularity of device */
79 	0			/* DMA transfer flags */
80 };
81 
82 fct_status_t
83 qlt_dmem_init(qlt_state_t *qlt)
84 {
85 	qlt_dmem_bucket_t	*p;
86 	qlt_dmem_bctl_t		*bctl, *bc;
87 	qlt_dmem_bctl_t		*prev;
88 	int			ndx, i;
89 	uint32_t		total_mem;
90 	uint8_t			*addr;
91 	uint8_t			*host_addr;
92 	uint64_t		dev_addr;
93 	ddi_dma_cookie_t	cookie;
94 	uint32_t		ncookie;
95 	uint32_t		bsize;
96 	size_t			len;
97 
98 	if (qlt->qlt_bucketcnt[0] != 0) {
99 		bucket2K.dmem_nbufs = qlt->qlt_bucketcnt[0];
100 	}
101 	if (qlt->qlt_bucketcnt[1] != 0) {
102 		bucket8K.dmem_nbufs = qlt->qlt_bucketcnt[1];
103 	}
104 	if (qlt->qlt_bucketcnt[2] != 0) {
105 		bucket64K.dmem_nbufs = qlt->qlt_bucketcnt[2];
106 	}
107 	if (qlt->qlt_bucketcnt[3] != 0) {
108 		bucket128k.dmem_nbufs = qlt->qlt_bucketcnt[3];
109 	}
110 	if (qlt->qlt_bucketcnt[4] != 0) {
111 		bucket256k.dmem_nbufs = qlt->qlt_bucketcnt[4];
112 	}
113 
114 	bsize = sizeof (dmem_buckets);
115 	ndx = (int)(bsize / sizeof (void *));
116 	/*
117 	 * The reason it is ndx - 1 everywhere is becasue the last bucket
118 	 * pointer is NULL.
119 	 */
120 	qlt->dmem_buckets = (qlt_dmem_bucket_t **)kmem_zalloc(bsize +
121 	    ((ndx - 1) * (int)sizeof (qlt_dmem_bucket_t)), KM_SLEEP);
122 	for (i = 0; i < (ndx - 1); i++) {
123 		qlt->dmem_buckets[i] = (qlt_dmem_bucket_t *)
124 		    ((uint8_t *)qlt->dmem_buckets + bsize +
125 		    (i * (int)sizeof (qlt_dmem_bucket_t)));
126 		bcopy(dmem_buckets[i], qlt->dmem_buckets[i],
127 		    sizeof (qlt_dmem_bucket_t));
128 	}
129 	bzero(&acc, sizeof (acc));
130 	acc.devacc_attr_version = DDI_DEVICE_ATTR_V0;
131 	acc.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
132 	acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
133 	for (ndx = 0; (p = qlt->dmem_buckets[ndx]) != NULL; ndx++) {
134 		bctl = (qlt_dmem_bctl_t *)kmem_zalloc(p->dmem_nbufs *
135 		    sizeof (qlt_dmem_bctl_t), KM_NOSLEEP);
136 		if (bctl == NULL) {
137 			EL(qlt, "bctl==NULL\n");
138 			goto alloc_bctl_failed;
139 		}
140 		p->dmem_bctls_mem = bctl;
141 		mutex_init(&p->dmem_lock, NULL, MUTEX_DRIVER, NULL);
142 		if ((i = ddi_dma_alloc_handle(qlt->dip, &qlt_scsi_dma_attr,
143 		    DDI_DMA_SLEEP, 0, &p->dmem_dma_handle)) != DDI_SUCCESS) {
144 			EL(qlt, "ddi_dma_alloc_handle status=%xh\n", i);
145 			goto alloc_handle_failed;
146 		}
147 
148 		total_mem = p->dmem_buf_size * p->dmem_nbufs;
149 
150 		if ((i = ddi_dma_mem_alloc(p->dmem_dma_handle, total_mem, &acc,
151 		    DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0, (caddr_t *)&addr,
152 		    &len, &p->dmem_acc_handle)) != DDI_SUCCESS) {
153 			EL(qlt, "ddi_dma_mem_alloc status=%xh\n", i);
154 			goto mem_alloc_failed;
155 		}
156 
157 		if ((i = ddi_dma_addr_bind_handle(p->dmem_dma_handle, NULL,
158 		    (caddr_t)addr, total_mem, DDI_DMA_RDWR | DDI_DMA_STREAMING,
159 		    DDI_DMA_DONTWAIT, 0, &cookie, &ncookie)) != DDI_SUCCESS) {
160 			EL(qlt, "ddi_dma_addr_bind_handle status=%xh\n", i);
161 			goto addr_bind_handle_failed;
162 		}
163 		if (ncookie != 1) {
164 			EL(qlt, "ncookie=%d\n", ncookie);
165 			goto dmem_init_failed;
166 		}
167 
168 		p->dmem_host_addr = host_addr = addr;
169 		p->dmem_dev_addr = dev_addr = (uint64_t)cookie.dmac_laddress;
170 		bsize = p->dmem_buf_size;
171 		p->dmem_bctl_free_list = bctl;
172 		p->dmem_nbufs_free = p->dmem_nbufs;
173 		for (i = 0; i < p->dmem_nbufs; i++) {
174 			stmf_data_buf_t	*db;
175 			prev = bctl;
176 			bctl->bctl_bucket = p;
177 			bctl->bctl_buf = db = stmf_alloc(STMF_STRUCT_DATA_BUF,
178 			    0, 0);
179 			db->db_port_private = bctl;
180 			db->db_sglist[0].seg_addr = host_addr;
181 			bctl->bctl_dev_addr = dev_addr;
182 			db->db_sglist[0].seg_length = db->db_buf_size = bsize;
183 			db->db_sglist_length = 1;
184 			host_addr += bsize;
185 			dev_addr += bsize;
186 			bctl++;
187 			prev->bctl_next = bctl;
188 		}
189 		prev->bctl_next = NULL;
190 	}
191 
192 	return (QLT_SUCCESS);
193 
194 dmem_failure_loop:;
195 	bc = bctl;
196 	while (bc) {
197 		stmf_free(bc->bctl_buf);
198 		bc = bc->bctl_next;
199 	}
200 dmem_init_failed:;
201 	(void) ddi_dma_unbind_handle(p->dmem_dma_handle);
202 addr_bind_handle_failed:;
203 	ddi_dma_mem_free(&p->dmem_acc_handle);
204 mem_alloc_failed:;
205 	ddi_dma_free_handle(&p->dmem_dma_handle);
206 alloc_handle_failed:;
207 	kmem_free(p->dmem_bctls_mem, p->dmem_nbufs * sizeof (qlt_dmem_bctl_t));
208 	mutex_destroy(&p->dmem_lock);
209 alloc_bctl_failed:;
210 	if (--ndx >= 0) {
211 		p = qlt->dmem_buckets[ndx];
212 		bctl = p->dmem_bctl_free_list;
213 		goto dmem_failure_loop;
214 	}
215 	kmem_free(qlt->dmem_buckets, sizeof (dmem_buckets) +
216 	    ((sizeof (dmem_buckets)/sizeof (void *))
217 	    *sizeof (qlt_dmem_bucket_t)));
218 	qlt->dmem_buckets = NULL;
219 
220 	return (QLT_FAILURE);
221 }
222 
223 void
224 qlt_dma_handle_pool_init(qlt_state_t *qlt)
225 {
226 	qlt_dma_handle_pool_t *pool;
227 
228 	pool = kmem_zalloc(sizeof (*pool), KM_SLEEP);
229 	mutex_init(&pool->pool_lock, NULL, MUTEX_DRIVER, NULL);
230 	qlt->qlt_dma_handle_pool = pool;
231 }
232 
233 void
234 qlt_dma_handle_pool_fini(qlt_state_t *qlt)
235 {
236 	qlt_dma_handle_pool_t	*pool;
237 	qlt_dma_handle_t	*handle, *next_handle;
238 
239 	pool = qlt->qlt_dma_handle_pool;
240 	mutex_enter(&pool->pool_lock);
241 	/*
242 	 * XXX Need to wait for free == total elements
243 	 * XXX Not sure how other driver shutdown stuff is done.
244 	 */
245 	ASSERT(pool->num_free == pool->num_total);
246 	if (pool->num_free != pool->num_total)
247 		cmn_err(CE_WARN,
248 		    "num_free %d != num_total %d\n",
249 		    pool->num_free, pool->num_total);
250 	handle = pool->free_list;
251 	while (handle) {
252 		next_handle = handle->next;
253 		kmem_free(handle, sizeof (*handle));
254 		handle = next_handle;
255 	}
256 	qlt->qlt_dma_handle_pool = NULL;
257 	mutex_destroy(&pool->pool_lock);
258 	kmem_free(pool, sizeof (*pool));
259 }
260 
261 void
262 qlt_dmem_fini(qlt_state_t *qlt)
263 {
264 	qlt_dmem_bucket_t *p;
265 	qlt_dmem_bctl_t *bctl;
266 	int ndx;
267 
268 	for (ndx = 0; (p = qlt->dmem_buckets[ndx]) != NULL; ndx++) {
269 		bctl = p->dmem_bctl_free_list;
270 		while (bctl) {
271 			stmf_free(bctl->bctl_buf);
272 			bctl = bctl->bctl_next;
273 		}
274 		bctl = p->dmem_bctl_free_list;
275 		(void) ddi_dma_unbind_handle(p->dmem_dma_handle);
276 		ddi_dma_mem_free(&p->dmem_acc_handle);
277 		ddi_dma_free_handle(&p->dmem_dma_handle);
278 		kmem_free(p->dmem_bctls_mem,
279 		    p->dmem_nbufs * sizeof (qlt_dmem_bctl_t));
280 		mutex_destroy(&p->dmem_lock);
281 	}
282 	kmem_free(qlt->dmem_buckets, sizeof (dmem_buckets) +
283 	    (((sizeof (dmem_buckets)/sizeof (void *))-1)*
284 	    sizeof (qlt_dmem_bucket_t)));
285 	qlt->dmem_buckets = NULL;
286 }
287 
288 stmf_data_buf_t *
289 qlt_dmem_alloc(fct_local_port_t *port, uint32_t size, uint32_t *pminsize,
290     uint32_t flags)
291 {
292 	return (qlt_i_dmem_alloc((qlt_state_t *)
293 	    port->port_fca_private, size, pminsize,
294 	    flags));
295 }
296 
297 /* ARGSUSED */
298 stmf_data_buf_t *
299 qlt_i_dmem_alloc(qlt_state_t *qlt, uint32_t size, uint32_t *pminsize,
300     uint32_t flags)
301 {
302 	qlt_dmem_bucket_t	*p;
303 	qlt_dmem_bctl_t 	*bctl;
304 	int			i;
305 	uint32_t		size_possible = 0;
306 
307 	if (size > QLT_DMEM_MAX_BUF_SIZE) {
308 		goto qlt_try_partial_alloc;
309 	}
310 
311 	/* 1st try to do a full allocation */
312 	for (i = 0; (p = qlt->dmem_buckets[i]) != NULL; i++) {
313 		if (p->dmem_buf_size >= size) {
314 			if (p->dmem_nbufs_free) {
315 				mutex_enter(&p->dmem_lock);
316 				bctl = p->dmem_bctl_free_list;
317 				if (bctl == NULL) {
318 					mutex_exit(&p->dmem_lock);
319 					continue;
320 				}
321 				p->dmem_bctl_free_list =
322 				    bctl->bctl_next;
323 				p->dmem_nbufs_free--;
324 				qlt->qlt_bufref[i]++;
325 				mutex_exit(&p->dmem_lock);
326 				bctl->bctl_buf->db_data_size = size;
327 				return (bctl->bctl_buf);
328 			} else {
329 				qlt->qlt_bumpbucket++;
330 			}
331 		}
332 	}
333 
334 qlt_try_partial_alloc:
335 
336 	qlt->qlt_pmintry++;
337 
338 	/* Now go from high to low */
339 	for (i = QLT_DMEM_NBUCKETS - 1; i >= 0; i--) {
340 		p = qlt->dmem_buckets[i];
341 		if (p->dmem_nbufs_free == 0)
342 			continue;
343 		if (!size_possible) {
344 			size_possible = p->dmem_buf_size;
345 		}
346 		if (*pminsize > p->dmem_buf_size) {
347 			/* At this point we know the request is failing. */
348 			if (size_possible) {
349 				/*
350 				 * This caller is asking too much. We already
351 				 * know what we can give, so get out.
352 				 */
353 				break;
354 			} else {
355 				/*
356 				 * Lets continue to find out and tell what
357 				 * we can give.
358 				 */
359 				continue;
360 			}
361 		}
362 		mutex_enter(&p->dmem_lock);
363 		if (*pminsize <= p->dmem_buf_size) {
364 			bctl = p->dmem_bctl_free_list;
365 			if (bctl == NULL) {
366 				/* Someone took it. */
367 				size_possible = 0;
368 				mutex_exit(&p->dmem_lock);
369 				continue;
370 			}
371 			p->dmem_bctl_free_list = bctl->bctl_next;
372 			p->dmem_nbufs_free--;
373 			mutex_exit(&p->dmem_lock);
374 			bctl->bctl_buf->db_data_size = p->dmem_buf_size;
375 			qlt->qlt_pmin_ok++;
376 			return (bctl->bctl_buf);
377 		}
378 	}
379 
380 	*pminsize = size_possible;
381 
382 	return (NULL);
383 }
384 
385 /* ARGSUSED */
386 void
387 qlt_i_dmem_free(qlt_state_t *qlt, stmf_data_buf_t *dbuf)
388 {
389 	qlt_dmem_free(0, dbuf);
390 }
391 
392 /* ARGSUSED */
393 void
394 qlt_dmem_free(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf)
395 {
396 	qlt_dmem_bctl_t		*bctl;
397 	qlt_dmem_bucket_t	*p;
398 
399 	ASSERT((dbuf->db_flags & DB_LU_DATA_BUF) == 0);
400 
401 	bctl = (qlt_dmem_bctl_t *)dbuf->db_port_private;
402 	p = bctl->bctl_bucket;
403 	mutex_enter(&p->dmem_lock);
404 	bctl->bctl_next = p->dmem_bctl_free_list;
405 	p->dmem_bctl_free_list = bctl;
406 	p->dmem_nbufs_free++;
407 	mutex_exit(&p->dmem_lock);
408 }
409 
410 void
411 qlt_dmem_dma_sync(stmf_data_buf_t *dbuf, uint_t sync_type)
412 {
413 	qlt_dmem_bctl_t		*bctl;
414 	qlt_dma_sgl_t		*qsgl;
415 	qlt_dmem_bucket_t	*p;
416 	qlt_dma_handle_t	*th;
417 	int			rv;
418 
419 	if (dbuf->db_flags & DB_LU_DATA_BUF) {
420 		/*
421 		 * go through ddi handle list
422 		 */
423 		qsgl = (qlt_dma_sgl_t *)dbuf->db_port_private;
424 		th = qsgl->handle_list;
425 		while (th) {
426 			rv = ddi_dma_sync(th->dma_handle,
427 			    0, 0, sync_type);
428 			if (rv != DDI_SUCCESS) {
429 				cmn_err(CE_WARN, "ddi_dma_sync FAILED\n");
430 			}
431 			th = th->next;
432 		}
433 	} else {
434 		bctl = (qlt_dmem_bctl_t *)dbuf->db_port_private;
435 		p = bctl->bctl_bucket;
436 		(void) ddi_dma_sync(p->dmem_dma_handle, (off_t)
437 		    (bctl->bctl_dev_addr - p->dmem_dev_addr),
438 		    dbuf->db_data_size, sync_type);
439 	}
440 }
441 
442 /*
443  * A very lite version of ddi_dma_addr_bind_handle()
444  */
445 uint64_t
446 qlt_ddi_vtop(caddr_t vaddr)
447 {
448 	uint64_t offset, paddr;
449 	pfn_t pfn;
450 
451 	pfn = hat_getpfnum(kas.a_hat, vaddr);
452 	ASSERT(pfn != PFN_INVALID && pfn != PFN_SUSPENDED);
453 	offset = ((uintptr_t)vaddr) & MMU_PAGEOFFSET;
454 	paddr = mmu_ptob(pfn);
455 	return (paddr+offset);
456 }
457 
458 static ddi_dma_attr_t 	qlt_sgl_dma_attr = {
459 	DMA_ATTR_V0,		/* dma_attr_version */
460 	0,				/* low DMA address range */
461 	0xffffffffffffffff,		/* high DMA address range */
462 	0xffffffff,			/* DMA counter register */
463 	64,				/* DMA address alignment */
464 	0xff,			/* DMA burstsizes */
465 	1,				/* min effective DMA size */
466 	0xffffffff,			/* max DMA xfer size */
467 	0xffffffff,			/* segment boundary */
468 	QLT_DMA_SG_LIST_LENGTH,	/* s/g list length */
469 	1,				/* granularity of device */
470 	0				/* DMA transfer flags */
471 };
472 
473 /*
474  * Allocate a qlt_dma_handle container and fill it with a ddi_dma_handle
475  */
476 static qlt_dma_handle_t *
477 qlt_dma_alloc_handle(qlt_state_t *qlt)
478 {
479 	ddi_dma_handle_t ddi_handle;
480 	qlt_dma_handle_t *qlt_handle;
481 	int rv;
482 
483 	rv = ddi_dma_alloc_handle(qlt->dip, &qlt_sgl_dma_attr,
484 	    DDI_DMA_SLEEP, 0, &ddi_handle);
485 	if (rv != DDI_SUCCESS) {
486 		EL(qlt, "ddi_dma_alloc_handle status=%xh\n", rv);
487 		return (NULL);
488 	}
489 	qlt_handle = kmem_zalloc(sizeof (qlt_dma_handle_t), KM_SLEEP);
490 	qlt_handle->dma_handle = ddi_handle;
491 	return (qlt_handle);
492 }
493 
494 /*
495  * Allocate a list of qlt_dma_handle containers from the free list
496  */
497 static qlt_dma_handle_t *
498 qlt_dma_alloc_handle_list(qlt_state_t *qlt, int handle_count)
499 {
500 	qlt_dma_handle_pool_t	*pool;
501 	qlt_dma_handle_t	*tmp_handle, *first_handle, *last_handle;
502 	int i;
503 
504 	/*
505 	 * Make sure the free list can satisfy the request.
506 	 * Once the free list is primed, it should satisfy most requests.
507 	 * XXX Should there be a limit on pool size?
508 	 */
509 	pool = qlt->qlt_dma_handle_pool;
510 	mutex_enter(&pool->pool_lock);
511 	while (handle_count > pool->num_free) {
512 		mutex_exit(&pool->pool_lock);
513 		if ((tmp_handle = qlt_dma_alloc_handle(qlt)) == NULL)
514 			return (NULL);
515 		mutex_enter(&pool->pool_lock);
516 		tmp_handle->next = pool->free_list;
517 		pool->free_list = tmp_handle;
518 		pool->num_free++;
519 		pool->num_total++;
520 	}
521 
522 	/*
523 	 * The free list lock is held and the list is large enough to
524 	 * satisfy this request. Run down the freelist and snip off
525 	 * the number of elements needed for this request.
526 	 */
527 	first_handle = pool->free_list;
528 	tmp_handle = first_handle;
529 	for (i = 0; i < handle_count; i++) {
530 		last_handle = tmp_handle;
531 		tmp_handle = tmp_handle->next;
532 	}
533 	pool->free_list = tmp_handle;
534 	pool->num_free -= handle_count;
535 	mutex_exit(&pool->pool_lock);
536 	last_handle->next = NULL;	/* sanity */
537 	return (first_handle);
538 }
539 
540 /*
541  * Return a list of qlt_dma_handle containers to the free list.
542  */
543 static void
544 qlt_dma_free_handles(qlt_state_t *qlt, qlt_dma_handle_t *first_handle)
545 {
546 	qlt_dma_handle_pool_t *pool;
547 	qlt_dma_handle_t *tmp_handle, *last_handle;
548 	int rv, handle_count;
549 
550 	/*
551 	 * Traverse the list and unbind the handles
552 	 */
553 	ASSERT(first_handle);
554 	tmp_handle = first_handle;
555 	handle_count = 0;
556 	while (tmp_handle != NULL) {
557 		last_handle = tmp_handle;
558 		/*
559 		 * If the handle is bound, unbind the handle so it can be
560 		 * reused. It may not be bound if there was a bind failure.
561 		 */
562 		if (tmp_handle->num_cookies != 0) {
563 			rv = ddi_dma_unbind_handle(tmp_handle->dma_handle);
564 			ASSERT(rv == DDI_SUCCESS);
565 			tmp_handle->num_cookies = 0;
566 			tmp_handle->num_cookies_fetched = 0;
567 		}
568 		tmp_handle = tmp_handle->next;
569 		handle_count++;
570 	}
571 	/*
572 	 * Insert this list into the free list
573 	 */
574 	pool = qlt->qlt_dma_handle_pool;
575 	mutex_enter(&pool->pool_lock);
576 	last_handle->next = pool->free_list;
577 	pool->free_list = first_handle;
578 	pool->num_free += handle_count;
579 	mutex_exit(&pool->pool_lock);
580 }
581 
582 /*
583  * cookies produced by mapping this dbuf
584  */
585 uint16_t
586 qlt_get_cookie_count(stmf_data_buf_t *dbuf)
587 {
588 	qlt_dma_sgl_t *qsgl = dbuf->db_port_private;
589 
590 	ASSERT(dbuf->db_flags & DB_LU_DATA_BUF);
591 	return (qsgl->cookie_count);
592 }
593 
594 ddi_dma_cookie_t
595 *qlt_get_cookie_array(stmf_data_buf_t *dbuf)
596 {
597 	qlt_dma_sgl_t *qsgl = dbuf->db_port_private;
598 
599 	ASSERT(dbuf->db_flags & DB_LU_DATA_BUF);
600 
601 	if (qsgl->cookie_prefetched)
602 		return (&qsgl->cookie[0]);
603 	else
604 		return (NULL);
605 }
606 
607 /*
608  * Wrapper around ddi_dma_nextcookie that hides the ddi_dma_handle usage.
609  */
610 void
611 qlt_ddi_dma_nextcookie(stmf_data_buf_t *dbuf, ddi_dma_cookie_t *cookiep)
612 {
613 	qlt_dma_sgl_t *qsgl = dbuf->db_port_private;
614 
615 	ASSERT(dbuf->db_flags & DB_LU_DATA_BUF);
616 
617 	if (qsgl->cookie_prefetched) {
618 		ASSERT(qsgl->cookie_next_fetch < qsgl->cookie_count);
619 		*cookiep = qsgl->cookie[qsgl->cookie_next_fetch++];
620 	} else {
621 		qlt_dma_handle_t *fetch;
622 		qlt_dma_handle_t *FETCH_DONE = (qlt_dma_handle_t *)0xbad;
623 
624 		ASSERT(qsgl->handle_list != NULL);
625 		ASSERT(qsgl->handle_next_fetch != FETCH_DONE);
626 
627 		fetch = qsgl->handle_next_fetch;
628 		if (fetch->num_cookies_fetched == 0) {
629 			*cookiep = fetch->first_cookie;
630 		} else {
631 			ddi_dma_nextcookie(fetch->dma_handle, cookiep);
632 		}
633 		if (++fetch->num_cookies_fetched == fetch->num_cookies) {
634 			if (fetch->next == NULL)
635 				qsgl->handle_next_fetch = FETCH_DONE;
636 			else
637 				qsgl->handle_next_fetch = fetch->next;
638 		}
639 	}
640 }
641 
642 /*
643  * Set this flag to fetch the DDI dma cookies from the handles here and
644  * store them in the port private area of the dbuf. This will allow
645  * faster access to the cookies in qlt_xfer_scsi_data() at the expense of
646  * an extra copy. If the qlt->req_lock is hot, this may help.
647  */
648 int qlt_sgl_prefetch = 0;
649 
650 /*ARGSUSED*/
651 stmf_status_t
652 qlt_dma_setup_dbuf(fct_local_port_t *port, stmf_data_buf_t *dbuf,
653     uint32_t flags)
654 {
655 	qlt_state_t		*qlt = port->port_fca_private;
656 	qlt_dma_sgl_t		*qsgl;
657 	struct stmf_sglist_ent	*sglp;
658 	qlt_dma_handle_t	*handle_list, *th;
659 	int			i, rv;
660 	ddi_dma_cookie_t	*cookie_p;
661 	int			cookie_count, numbufs;
662 	int			prefetch;
663 	size_t			qsize;
664 
665 	/*
666 	 * psuedo code:
667 	 * get dma handle list from cache - one per sglist entry
668 	 * foreach sglist entry
669 	 *	bind dma handle to sglist vaddr
670 	 * allocate space for DMA state to store in db_port_private
671 	 * fill in port private object
672 	 * if prefetching
673 	 *	move all dma cookies into db_port_private
674 	 */
675 	dbuf->db_port_private = NULL;
676 	numbufs = dbuf->db_sglist_length;
677 	handle_list = qlt_dma_alloc_handle_list(qlt, numbufs);
678 	if (handle_list == NULL) {
679 		EL(qlt, "handle_list==NULL\n");
680 		return (STMF_FAILURE);
681 	}
682 	/*
683 	 * Loop through sglist and bind each entry to a handle
684 	 */
685 	th = handle_list;
686 	sglp = &dbuf->db_sglist[0];
687 	cookie_count = 0;
688 	for (i = 0; i < numbufs; i++, sglp++) {
689 
690 		/*
691 		 * Bind this sgl entry to a DDI dma handle
692 		 */
693 		if ((rv = ddi_dma_addr_bind_handle(
694 		    th->dma_handle,
695 		    NULL,
696 		    (caddr_t)(sglp->seg_addr),
697 		    (size_t)sglp->seg_length,
698 		    DDI_DMA_RDWR | DDI_DMA_STREAMING,
699 		    DDI_DMA_DONTWAIT,
700 		    NULL,
701 		    &th->first_cookie,
702 		    &th->num_cookies)) != DDI_DMA_MAPPED) {
703 			cmn_err(CE_NOTE, "ddi_dma_addr_bind_handle %d", rv);
704 			qlt_dma_free_handles(qlt, handle_list);
705 			return (STMF_FAILURE);
706 		}
707 
708 		/*
709 		 * Add to total cookie count
710 		 */
711 		cookie_count += th->num_cookies;
712 		if (cookie_count > QLT_DMA_SG_LIST_LENGTH) {
713 			/*
714 			 * Request exceeds HBA limit
715 			 */
716 			qlt_dma_free_handles(qlt, handle_list);
717 			return (STMF_FAILURE);
718 		}
719 		/* move to next ddi_dma_handle */
720 		th = th->next;
721 	}
722 
723 	/*
724 	 * Allocate our port private object for DMA mapping state.
725 	 */
726 	prefetch =  qlt_sgl_prefetch;
727 	qsize = sizeof (qlt_dma_sgl_t);
728 	if (prefetch) {
729 		/* one extra ddi_dma_cookie allocated for alignment padding */
730 		qsize += cookie_count * sizeof (ddi_dma_cookie_t);
731 	}
732 	qsgl = kmem_alloc(qsize, KM_SLEEP);
733 	/*
734 	 * Fill in the sgl
735 	 */
736 	dbuf->db_port_private = qsgl;
737 	qsgl->qsize = qsize;
738 	qsgl->handle_count = dbuf->db_sglist_length;
739 	qsgl->cookie_prefetched = prefetch;
740 	qsgl->cookie_count = cookie_count;
741 	qsgl->cookie_next_fetch = 0;
742 	qsgl->handle_list = handle_list;
743 	qsgl->handle_next_fetch = handle_list;
744 	if (prefetch) {
745 		/*
746 		 * traverse handle list and move cookies to db_port_private
747 		 */
748 		th = handle_list;
749 		cookie_p = &qsgl->cookie[0];
750 		for (i = 0; i < numbufs; i++) {
751 			uint_t cc = th->num_cookies;
752 
753 			*cookie_p++ = th->first_cookie;
754 			while (--cc > 0) {
755 				ddi_dma_nextcookie(th->dma_handle, cookie_p++);
756 			}
757 			th->num_cookies_fetched = th->num_cookies;
758 			th = th->next;
759 		}
760 	}
761 
762 	return (STMF_SUCCESS);
763 }
764 
765 void
766 qlt_dma_teardown_dbuf(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf)
767 {
768 	qlt_state_t		*qlt = fds->fds_fca_private;
769 	qlt_dma_sgl_t		*qsgl = dbuf->db_port_private;
770 
771 	ASSERT(qlt);
772 	ASSERT(qsgl);
773 	ASSERT(dbuf->db_flags & DB_LU_DATA_BUF);
774 
775 	/*
776 	 * unbind and free the dma handles
777 	 */
778 	if (qsgl->handle_list) {
779 		/* go through ddi handle list */
780 		qlt_dma_free_handles(qlt, qsgl->handle_list);
781 	}
782 	kmem_free(qsgl, qsgl->qsize);
783 }
784 
785 uint8_t
786 qlt_get_iocb_count(uint32_t cookie_count)
787 {
788 	uint32_t	cnt, cont_segs;
789 	uint8_t		iocb_count;
790 
791 	iocb_count = 1;
792 	cnt = CMD7_2400_DATA_SEGMENTS;
793 	cont_segs = CONT_A64_DATA_SEGMENTS;
794 
795 	if (cookie_count > cnt) {
796 		cnt = cookie_count - cnt;
797 		iocb_count = (uint8_t)(iocb_count + cnt / cont_segs);
798 		if (cnt % cont_segs) {
799 			iocb_count++;
800 		}
801 	}
802 	return (iocb_count);
803 }
804