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