/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009-2015 QLogic Corporation. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "qlt.h" #include "qlt_dma.h" /* * Local Function Prototypes. */ static void qlt_dma_free_handles(qlt_state_t *qlt, qlt_dma_handle_t *first_handle); #define BUF_COUNT_2K 2048 #define BUF_COUNT_8K 512 #define BUF_COUNT_64K 256 #define BUF_COUNT_128K 1024 #define BUF_COUNT_256K 8 #define QLT_DMEM_MAX_BUF_SIZE (4 * 65536) #define QLT_DMEM_NBUCKETS 5 static qlt_dmem_bucket_t bucket2K = { 2048, BUF_COUNT_2K }, bucket8K = { 8192, BUF_COUNT_8K }, bucket64K = { 65536, BUF_COUNT_64K }, bucket128k = { (2 * 65536), BUF_COUNT_128K }, bucket256k = { (4 * 65536), BUF_COUNT_256K }; static qlt_dmem_bucket_t *dmem_buckets[] = { &bucket2K, &bucket8K, &bucket64K, &bucket128k, &bucket256k, NULL }; static ddi_device_acc_attr_t acc; static ddi_dma_attr_t qlt_scsi_dma_attr = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* low DMA address range */ 0xffffffffffffffff, /* high DMA address range */ 0xffffffff, /* DMA counter register */ 8192, /* DMA address alignment */ 0xff, /* DMA burstsizes */ 1, /* min effective DMA size */ 0xffffffff, /* max DMA xfer size */ 0xffffffff, /* segment boundary */ 1, /* s/g list length */ 1, /* granularity of device */ 0 /* DMA transfer flags */ }; fct_status_t qlt_dmem_init(qlt_state_t *qlt) { qlt_dmem_bucket_t *p; qlt_dmem_bctl_t *bctl, *bc; qlt_dmem_bctl_t *prev; int ndx, i; uint32_t total_mem; uint8_t *addr; uint8_t *host_addr; uint64_t dev_addr; ddi_dma_cookie_t cookie; uint32_t ncookie; uint32_t bsize; size_t len; if (qlt->qlt_bucketcnt[0] != 0) { bucket2K.dmem_nbufs = qlt->qlt_bucketcnt[0]; } if (qlt->qlt_bucketcnt[1] != 0) { bucket8K.dmem_nbufs = qlt->qlt_bucketcnt[1]; } if (qlt->qlt_bucketcnt[2] != 0) { bucket64K.dmem_nbufs = qlt->qlt_bucketcnt[2]; } if (qlt->qlt_bucketcnt[3] != 0) { bucket128k.dmem_nbufs = qlt->qlt_bucketcnt[3]; } if (qlt->qlt_bucketcnt[4] != 0) { bucket256k.dmem_nbufs = qlt->qlt_bucketcnt[4]; } bsize = sizeof (dmem_buckets); ndx = (int)(bsize / sizeof (void *)); /* * The reason it is ndx - 1 everywhere is becasue the last bucket * pointer is NULL. */ qlt->dmem_buckets = (qlt_dmem_bucket_t **)kmem_zalloc(bsize + ((ndx - 1) * (int)sizeof (qlt_dmem_bucket_t)), KM_SLEEP); for (i = 0; i < (ndx - 1); i++) { qlt->dmem_buckets[i] = (qlt_dmem_bucket_t *) ((uint8_t *)qlt->dmem_buckets + bsize + (i * (int)sizeof (qlt_dmem_bucket_t))); bcopy(dmem_buckets[i], qlt->dmem_buckets[i], sizeof (qlt_dmem_bucket_t)); } bzero(&acc, sizeof (acc)); acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; acc.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; for (ndx = 0; (p = qlt->dmem_buckets[ndx]) != NULL; ndx++) { bctl = (qlt_dmem_bctl_t *)kmem_zalloc(p->dmem_nbufs * sizeof (qlt_dmem_bctl_t), KM_NOSLEEP); if (bctl == NULL) { EL(qlt, "bctl==NULL\n"); goto alloc_bctl_failed; } p->dmem_bctls_mem = bctl; mutex_init(&p->dmem_lock, NULL, MUTEX_DRIVER, NULL); if ((i = ddi_dma_alloc_handle(qlt->dip, &qlt_scsi_dma_attr, DDI_DMA_SLEEP, 0, &p->dmem_dma_handle)) != DDI_SUCCESS) { EL(qlt, "ddi_dma_alloc_handle status=%xh\n", i); goto alloc_handle_failed; } total_mem = p->dmem_buf_size * p->dmem_nbufs; if ((i = ddi_dma_mem_alloc(p->dmem_dma_handle, total_mem, &acc, DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0, (caddr_t *)&addr, &len, &p->dmem_acc_handle)) != DDI_SUCCESS) { EL(qlt, "ddi_dma_mem_alloc status=%xh\n", i); goto mem_alloc_failed; } if ((i = ddi_dma_addr_bind_handle(p->dmem_dma_handle, NULL, (caddr_t)addr, total_mem, DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, 0, &cookie, &ncookie)) != DDI_SUCCESS) { EL(qlt, "ddi_dma_addr_bind_handle status=%xh\n", i); goto addr_bind_handle_failed; } if (ncookie != 1) { EL(qlt, "ncookie=%d\n", ncookie); goto dmem_init_failed; } p->dmem_host_addr = host_addr = addr; p->dmem_dev_addr = dev_addr = (uint64_t)cookie.dmac_laddress; bsize = p->dmem_buf_size; p->dmem_bctl_free_list = bctl; p->dmem_nbufs_free = p->dmem_nbufs; for (i = 0; i < p->dmem_nbufs; i++) { stmf_data_buf_t *db; prev = bctl; bctl->bctl_bucket = p; bctl->bctl_buf = db = stmf_alloc(STMF_STRUCT_DATA_BUF, 0, 0); db->db_port_private = bctl; db->db_sglist[0].seg_addr = host_addr; bctl->bctl_dev_addr = dev_addr; db->db_sglist[0].seg_length = db->db_buf_size = bsize; db->db_sglist_length = 1; host_addr += bsize; dev_addr += bsize; bctl++; prev->bctl_next = bctl; } prev->bctl_next = NULL; } return (QLT_SUCCESS); dmem_failure_loop:; bc = bctl; while (bc) { stmf_free(bc->bctl_buf); bc = bc->bctl_next; } dmem_init_failed:; (void) ddi_dma_unbind_handle(p->dmem_dma_handle); addr_bind_handle_failed:; ddi_dma_mem_free(&p->dmem_acc_handle); mem_alloc_failed:; ddi_dma_free_handle(&p->dmem_dma_handle); alloc_handle_failed:; kmem_free(p->dmem_bctls_mem, p->dmem_nbufs * sizeof (qlt_dmem_bctl_t)); mutex_destroy(&p->dmem_lock); alloc_bctl_failed:; if (--ndx >= 0) { p = qlt->dmem_buckets[ndx]; bctl = p->dmem_bctl_free_list; goto dmem_failure_loop; } kmem_free(qlt->dmem_buckets, sizeof (dmem_buckets) + (((sizeof (dmem_buckets)/sizeof (void *))-1)* sizeof (qlt_dmem_bucket_t))); qlt->dmem_buckets = NULL; return (QLT_FAILURE); } void qlt_dma_handle_pool_init(qlt_state_t *qlt) { qlt_dma_handle_pool_t *pool; pool = kmem_zalloc(sizeof (*pool), KM_SLEEP); mutex_init(&pool->pool_lock, NULL, MUTEX_DRIVER, NULL); qlt->qlt_dma_handle_pool = pool; } void qlt_dma_handle_pool_fini(qlt_state_t *qlt) { qlt_dma_handle_pool_t *pool; qlt_dma_handle_t *handle, *next_handle; pool = qlt->qlt_dma_handle_pool; mutex_enter(&pool->pool_lock); /* * XXX Need to wait for free == total elements * XXX Not sure how other driver shutdown stuff is done. */ ASSERT(pool->num_free == pool->num_total); if (pool->num_free != pool->num_total) cmn_err(CE_WARN, "num_free %d != num_total %d\n", pool->num_free, pool->num_total); handle = pool->free_list; while (handle) { next_handle = handle->next; kmem_free(handle, sizeof (*handle)); handle = next_handle; } qlt->qlt_dma_handle_pool = NULL; mutex_destroy(&pool->pool_lock); kmem_free(pool, sizeof (*pool)); } void qlt_dmem_fini(qlt_state_t *qlt) { qlt_dmem_bucket_t *p; qlt_dmem_bctl_t *bctl; int ndx; for (ndx = 0; (p = qlt->dmem_buckets[ndx]) != NULL; ndx++) { bctl = p->dmem_bctl_free_list; while (bctl) { stmf_free(bctl->bctl_buf); bctl = bctl->bctl_next; } bctl = p->dmem_bctl_free_list; (void) ddi_dma_unbind_handle(p->dmem_dma_handle); ddi_dma_mem_free(&p->dmem_acc_handle); ddi_dma_free_handle(&p->dmem_dma_handle); kmem_free(p->dmem_bctls_mem, p->dmem_nbufs * sizeof (qlt_dmem_bctl_t)); mutex_destroy(&p->dmem_lock); } kmem_free(qlt->dmem_buckets, sizeof (dmem_buckets) + (((sizeof (dmem_buckets)/sizeof (void *))-1)* sizeof (qlt_dmem_bucket_t))); qlt->dmem_buckets = NULL; } stmf_data_buf_t * qlt_dmem_alloc(fct_local_port_t *port, uint32_t size, uint32_t *pminsize, uint32_t flags) { return (qlt_i_dmem_alloc((qlt_state_t *) port->port_fca_private, size, pminsize, flags)); } /* ARGSUSED */ stmf_data_buf_t * qlt_i_dmem_alloc(qlt_state_t *qlt, uint32_t size, uint32_t *pminsize, uint32_t flags) { qlt_dmem_bucket_t *p; qlt_dmem_bctl_t *bctl; int i; uint32_t size_possible = 0; if (size > QLT_DMEM_MAX_BUF_SIZE) { goto qlt_try_partial_alloc; } /* 1st try to do a full allocation */ for (i = 0; (p = qlt->dmem_buckets[i]) != NULL; i++) { if (p->dmem_buf_size >= size) { if (p->dmem_nbufs_free) { mutex_enter(&p->dmem_lock); bctl = p->dmem_bctl_free_list; if (bctl == NULL) { mutex_exit(&p->dmem_lock); continue; } p->dmem_bctl_free_list = bctl->bctl_next; p->dmem_nbufs_free--; qlt->qlt_bufref[i]++; mutex_exit(&p->dmem_lock); bctl->bctl_buf->db_data_size = size; return (bctl->bctl_buf); } else { qlt->qlt_bumpbucket++; } } } qlt_try_partial_alloc: qlt->qlt_pmintry++; /* Now go from high to low */ for (i = QLT_DMEM_NBUCKETS - 1; i >= 0; i--) { p = qlt->dmem_buckets[i]; if (p->dmem_nbufs_free == 0) continue; if (!size_possible) { size_possible = p->dmem_buf_size; } if (*pminsize > p->dmem_buf_size) { /* At this point we know the request is failing. */ if (size_possible) { /* * This caller is asking too much. We already * know what we can give, so get out. */ break; } else { /* * Lets continue to find out and tell what * we can give. */ continue; } } mutex_enter(&p->dmem_lock); if (*pminsize <= p->dmem_buf_size) { bctl = p->dmem_bctl_free_list; if (bctl == NULL) { /* Someone took it. */ size_possible = 0; mutex_exit(&p->dmem_lock); continue; } p->dmem_bctl_free_list = bctl->bctl_next; p->dmem_nbufs_free--; mutex_exit(&p->dmem_lock); bctl->bctl_buf->db_data_size = p->dmem_buf_size; qlt->qlt_pmin_ok++; return (bctl->bctl_buf); } } *pminsize = size_possible; return (NULL); } /* ARGSUSED */ void qlt_i_dmem_free(qlt_state_t *qlt, stmf_data_buf_t *dbuf) { qlt_dmem_free(0, dbuf); } /* ARGSUSED */ void qlt_dmem_free(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf) { qlt_dmem_bctl_t *bctl; qlt_dmem_bucket_t *p; ASSERT((dbuf->db_flags & DB_LU_DATA_BUF) == 0); bctl = (qlt_dmem_bctl_t *)dbuf->db_port_private; p = bctl->bctl_bucket; mutex_enter(&p->dmem_lock); bctl->bctl_next = p->dmem_bctl_free_list; p->dmem_bctl_free_list = bctl; p->dmem_nbufs_free++; mutex_exit(&p->dmem_lock); } void qlt_dmem_dma_sync(stmf_data_buf_t *dbuf, uint_t sync_type) { qlt_dmem_bctl_t *bctl; qlt_dma_sgl_t *qsgl; qlt_dmem_bucket_t *p; qlt_dma_handle_t *th; int rv; if (dbuf->db_flags & DB_LU_DATA_BUF) { /* * go through ddi handle list */ qsgl = (qlt_dma_sgl_t *)dbuf->db_port_private; th = qsgl->handle_list; while (th) { rv = ddi_dma_sync(th->dma_handle, 0, 0, sync_type); if (rv != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_dma_sync FAILED\n"); } th = th->next; } } else { bctl = (qlt_dmem_bctl_t *)dbuf->db_port_private; p = bctl->bctl_bucket; (void) ddi_dma_sync(p->dmem_dma_handle, (off_t) (bctl->bctl_dev_addr - p->dmem_dev_addr), dbuf->db_data_size, sync_type); } } /* * A very lite version of ddi_dma_addr_bind_handle() */ uint64_t qlt_ddi_vtop(caddr_t vaddr) { uint64_t offset, paddr; pfn_t pfn; pfn = hat_getpfnum(kas.a_hat, vaddr); ASSERT(pfn != PFN_INVALID && pfn != PFN_SUSPENDED); offset = ((uintptr_t)vaddr) & MMU_PAGEOFFSET; paddr = mmu_ptob(pfn); return (paddr+offset); } static ddi_dma_attr_t qlt_sgl_dma_attr = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* low DMA address range */ 0xffffffffffffffff, /* high DMA address range */ 0xffffffff, /* DMA counter register */ 64, /* DMA address alignment */ 0xff, /* DMA burstsizes */ 1, /* min effective DMA size */ 0xffffffff, /* max DMA xfer size */ 0xffffffff, /* segment boundary */ QLT_DMA_SG_LIST_LENGTH, /* s/g list length */ 1, /* granularity of device */ 0 /* DMA transfer flags */ }; /* * Allocate a qlt_dma_handle container and fill it with a ddi_dma_handle */ static qlt_dma_handle_t * qlt_dma_alloc_handle(qlt_state_t *qlt) { ddi_dma_handle_t ddi_handle; qlt_dma_handle_t *qlt_handle; int rv; rv = ddi_dma_alloc_handle(qlt->dip, &qlt_sgl_dma_attr, DDI_DMA_SLEEP, 0, &ddi_handle); if (rv != DDI_SUCCESS) { EL(qlt, "ddi_dma_alloc_handle status=%xh\n", rv); return (NULL); } qlt_handle = kmem_zalloc(sizeof (qlt_dma_handle_t), KM_SLEEP); qlt_handle->dma_handle = ddi_handle; return (qlt_handle); } /* * Allocate a list of qlt_dma_handle containers from the free list */ static qlt_dma_handle_t * qlt_dma_alloc_handle_list(qlt_state_t *qlt, int handle_count) { qlt_dma_handle_pool_t *pool; qlt_dma_handle_t *tmp_handle, *first_handle, *last_handle; int i; /* * Make sure the free list can satisfy the request. * Once the free list is primed, it should satisfy most requests. * XXX Should there be a limit on pool size? */ pool = qlt->qlt_dma_handle_pool; mutex_enter(&pool->pool_lock); while (handle_count > pool->num_free) { mutex_exit(&pool->pool_lock); if ((tmp_handle = qlt_dma_alloc_handle(qlt)) == NULL) return (NULL); mutex_enter(&pool->pool_lock); tmp_handle->next = pool->free_list; pool->free_list = tmp_handle; pool->num_free++; pool->num_total++; } /* * The free list lock is held and the list is large enough to * satisfy this request. Run down the freelist and snip off * the number of elements needed for this request. */ first_handle = pool->free_list; tmp_handle = first_handle; for (i = 0; i < handle_count; i++) { last_handle = tmp_handle; tmp_handle = tmp_handle->next; } pool->free_list = tmp_handle; pool->num_free -= handle_count; mutex_exit(&pool->pool_lock); last_handle->next = NULL; /* sanity */ return (first_handle); } /* * Return a list of qlt_dma_handle containers to the free list. */ static void qlt_dma_free_handles(qlt_state_t *qlt, qlt_dma_handle_t *first_handle) { qlt_dma_handle_pool_t *pool; qlt_dma_handle_t *tmp_handle, *last_handle; int rv, handle_count; /* * Traverse the list and unbind the handles */ ASSERT(first_handle); tmp_handle = first_handle; handle_count = 0; while (tmp_handle != NULL) { last_handle = tmp_handle; /* * If the handle is bound, unbind the handle so it can be * reused. It may not be bound if there was a bind failure. */ if (tmp_handle->num_cookies != 0) { rv = ddi_dma_unbind_handle(tmp_handle->dma_handle); ASSERT(rv == DDI_SUCCESS); if (rv == DDI_SUCCESS) { tmp_handle->num_cookies = 0; tmp_handle->num_cookies_fetched = 0; } } tmp_handle = tmp_handle->next; handle_count++; } /* * Insert this list into the free list */ pool = qlt->qlt_dma_handle_pool; mutex_enter(&pool->pool_lock); last_handle->next = pool->free_list; pool->free_list = first_handle; pool->num_free += handle_count; mutex_exit(&pool->pool_lock); } /* * cookies produced by mapping this dbuf */ uint16_t qlt_get_cookie_count(stmf_data_buf_t *dbuf) { qlt_dma_sgl_t *qsgl = dbuf->db_port_private; ASSERT(dbuf->db_flags & DB_LU_DATA_BUF); return (qsgl->cookie_count); } ddi_dma_cookie_t *qlt_get_cookie_array(stmf_data_buf_t *dbuf) { qlt_dma_sgl_t *qsgl = dbuf->db_port_private; ASSERT(dbuf->db_flags & DB_LU_DATA_BUF); if (qsgl->cookie_prefetched) return (&qsgl->cookie[0]); else return (NULL); } /* * Wrapper around ddi_dma_nextcookie that hides the ddi_dma_handle usage. */ void qlt_ddi_dma_nextcookie(stmf_data_buf_t *dbuf, ddi_dma_cookie_t *cookiep) { qlt_dma_sgl_t *qsgl = dbuf->db_port_private; ASSERT(dbuf->db_flags & DB_LU_DATA_BUF); if (qsgl->cookie_prefetched) { ASSERT(qsgl->cookie_next_fetch < qsgl->cookie_count); *cookiep = qsgl->cookie[qsgl->cookie_next_fetch++]; } else { qlt_dma_handle_t *fetch; qlt_dma_handle_t *FETCH_DONE = (qlt_dma_handle_t *)0xbad; ASSERT(qsgl->handle_list != NULL); ASSERT(qsgl->handle_next_fetch != FETCH_DONE); fetch = qsgl->handle_next_fetch; if (fetch->num_cookies_fetched == 0) { *cookiep = fetch->first_cookie; } else { ddi_dma_nextcookie(fetch->dma_handle, cookiep); } if (++fetch->num_cookies_fetched == fetch->num_cookies) { if (fetch->next == NULL) qsgl->handle_next_fetch = FETCH_DONE; else qsgl->handle_next_fetch = fetch->next; } } } /* * Set this flag to fetch the DDI dma cookies from the handles here and * store them in the port private area of the dbuf. This will allow * faster access to the cookies in qlt_xfer_scsi_data() at the expense of * an extra copy. If the qlt->req_lock is hot, this may help. */ uint16_t qlt_sgl_prefetch = 0; /*ARGSUSED*/ stmf_status_t qlt_dma_setup_dbuf(fct_local_port_t *port, stmf_data_buf_t *dbuf, uint32_t flags) { qlt_state_t *qlt = port->port_fca_private; qlt_dma_sgl_t *qsgl; struct stmf_sglist_ent *sglp; qlt_dma_handle_t *handle_list, *th; int i, rv; ddi_dma_cookie_t *cookie_p; int numbufs; uint16_t cookie_count; uint16_t prefetch; size_t qsize; /* * psuedo code: * get dma handle list from cache - one per sglist entry * foreach sglist entry * bind dma handle to sglist vaddr * allocate space for DMA state to store in db_port_private * fill in port private object * if prefetching * move all dma cookies into db_port_private */ dbuf->db_port_private = NULL; numbufs = dbuf->db_sglist_length; handle_list = qlt_dma_alloc_handle_list(qlt, numbufs); if (handle_list == NULL) { EL(qlt, "handle_list==NULL\n"); return (STMF_FAILURE); } /* * Loop through sglist and bind each entry to a handle */ th = handle_list; sglp = &dbuf->db_sglist[0]; cookie_count = 0; for (i = 0; i < numbufs; i++, sglp++) { /* * Bind this sgl entry to a DDI dma handle */ if ((rv = ddi_dma_addr_bind_handle( th->dma_handle, NULL, (caddr_t)(sglp->seg_addr), (size_t)sglp->seg_length, DDI_DMA_RDWR | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, &th->first_cookie, &th->num_cookies)) != DDI_DMA_MAPPED) { cmn_err(CE_NOTE, "ddi_dma_addr_bind_handle %d", rv); qlt_dma_free_handles(qlt, handle_list); return (STMF_FAILURE); } /* * Add to total cookie count */ cookie_count += th->num_cookies; if (cookie_count > QLT_DMA_SG_LIST_LENGTH) { /* * Request exceeds HBA limit */ qlt_dma_free_handles(qlt, handle_list); return (STMF_FAILURE); } /* move to next ddi_dma_handle */ th = th->next; } /* * Allocate our port private object for DMA mapping state. */ prefetch = qlt_sgl_prefetch; qsize = sizeof (qlt_dma_sgl_t); if (prefetch) { /* one extra ddi_dma_cookie allocated for alignment padding */ qsize += cookie_count * sizeof (ddi_dma_cookie_t); } qsgl = kmem_alloc(qsize, KM_SLEEP); /* * Fill in the sgl */ dbuf->db_port_private = qsgl; qsgl->qsize = qsize; qsgl->handle_count = dbuf->db_sglist_length; qsgl->cookie_prefetched = prefetch; qsgl->cookie_count = cookie_count; qsgl->cookie_next_fetch = 0; qsgl->handle_list = handle_list; qsgl->handle_next_fetch = handle_list; if (prefetch) { /* * traverse handle list and move cookies to db_port_private */ th = handle_list; cookie_p = &qsgl->cookie[0]; for (i = 0; i < numbufs; i++) { uint_t cc = th->num_cookies; *cookie_p++ = th->first_cookie; while (--cc > 0) { ddi_dma_nextcookie(th->dma_handle, cookie_p++); } th->num_cookies_fetched = th->num_cookies; th = th->next; } } return (STMF_SUCCESS); } void qlt_dma_teardown_dbuf(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf) { qlt_state_t *qlt = fds->fds_fca_private; qlt_dma_sgl_t *qsgl = dbuf->db_port_private; ASSERT(qlt); ASSERT(qsgl); ASSERT(dbuf->db_flags & DB_LU_DATA_BUF); /* * unbind and free the dma handles */ if (qsgl->handle_list) { /* go through ddi handle list */ qlt_dma_free_handles(qlt, qsgl->handle_list); } kmem_free(qsgl, qsgl->qsize); } uint8_t qlt_get_iocb_count(uint32_t cookie_count) { uint32_t cnt, cont_segs; uint8_t iocb_count; iocb_count = 1; cnt = CMD7_2400_DATA_SEGMENTS; cont_segs = CONT_A64_DATA_SEGMENTS; if (cookie_count > cnt) { cnt = cookie_count - cnt; iocb_count = (uint8_t)(iocb_count + cnt / cont_segs); if (cnt % cont_segs) { iocb_count++; } } return (iocb_count); }