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