1 /* 2 * Copyright 2004-2008, Haiku, Inc. All RightsReserved. 3 * Copyright 2002-2003, Thomas Kurschel. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 /*! 9 Creates temporary Scatter/Gather table if the peripheral 10 driver has provided a simple pointer only. 11 */ 12 13 #include "scsi_internal.h" 14 #include "KernelExport_ext.h" 15 16 #include <string.h> 17 #include <iovec.h> 18 19 #include <algorithm> 20 21 22 static locked_pool_cookie temp_sg_pool; 23 24 25 static bool 26 fill_temp_sg(scsi_ccb *ccb) 27 { 28 status_t res; 29 scsi_bus_info *bus = ccb->bus; 30 uint32 dma_boundary = bus->dma_params.dma_boundary; 31 uint32 max_sg_block_size = bus->dma_params.max_sg_block_size; 32 uint32 max_sg_blocks = std::min(bus->dma_params.max_sg_blocks, 33 (uint32)MAX_TEMP_SG_FRAGMENTS); 34 iovec vec = { 35 ccb->data, 36 ccb->data_length 37 }; 38 size_t num_entries; 39 size_t mapped_len; 40 physical_entry *temp_sg = (physical_entry *)ccb->sg_list; 41 42 res = get_iovec_memory_map(&vec, 1, 0, ccb->data_length, temp_sg, max_sg_blocks, 43 &num_entries, &mapped_len); 44 45 if (res != B_OK) { 46 SHOW_ERROR(2, "cannot create temporary S/G list for IO request (%s)", strerror(res)); 47 return false; 48 } 49 50 if (mapped_len != ccb->data_length) 51 goto too_complex; 52 53 if (dma_boundary != ~(uint32)0 || ccb->data_length > max_sg_block_size) { 54 // S/G list may not be controller-compatible: 55 // we have to split offending entries 56 SHOW_FLOW(3, "Checking violation of dma boundary 0x%" B_PRIx32 57 " and entry size 0x%" B_PRIx32, dma_boundary, max_sg_block_size); 58 59 for (uint32 cur_idx = 0; cur_idx < num_entries; ++cur_idx) { 60 addr_t max_len; 61 62 // calculate space upto next dma boundary crossing 63 max_len = (dma_boundary + 1) - 64 (temp_sg[cur_idx].address & dma_boundary); 65 // restrict size per sg item 66 max_len = std::min(max_len, (addr_t)max_sg_block_size); 67 68 SHOW_FLOW(4, "addr=%#" B_PRIxPHYSADDR ", size=%" B_PRIxPHYSADDR 69 ", max_len=%" B_PRIxADDR ", idx=%" B_PRId32 ", num=%" 70 B_PRIuSIZE, temp_sg[cur_idx].address, temp_sg[cur_idx].size, 71 max_len, cur_idx, num_entries); 72 73 if (max_len < temp_sg[cur_idx].size) { 74 // split sg block 75 if (++num_entries > max_sg_blocks) 76 goto too_complex; 77 78 memmove(&temp_sg[cur_idx + 1], &temp_sg[cur_idx], 79 (num_entries - 1 - cur_idx) * sizeof(physical_entry)); 80 81 temp_sg[cur_idx].size = max_len; 82 temp_sg[cur_idx + 1].address 83 = temp_sg[cur_idx + 1].address + max_len; 84 temp_sg[cur_idx + 1].size -= max_len; 85 } 86 } 87 } 88 89 ccb->sg_count = num_entries; 90 91 return true; 92 93 too_complex: 94 SHOW_ERROR( 2, "S/G list to complex for IO request (max %d entries)", 95 MAX_TEMP_SG_FRAGMENTS ); 96 97 return false; 98 } 99 100 101 /** create temporary SG for request */ 102 103 bool 104 create_temp_sg(scsi_ccb *ccb) 105 { 106 physical_entry *temp_sg; 107 status_t res; 108 109 SHOW_FLOW(3, "ccb=%p, data=%p, data_length=%" B_PRIu32, ccb, ccb->data, 110 ccb->data_length); 111 112 ccb->sg_list = temp_sg = (physical_entry*)locked_pool->alloc(temp_sg_pool); 113 if (temp_sg == NULL) { 114 SHOW_ERROR0(2, "cannot allocate memory for IO request!"); 115 return false; 116 } 117 118 res = lock_memory(ccb->data, ccb->data_length, B_DMA_IO 119 | ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0)); 120 121 if (res != B_OK) { 122 SHOW_ERROR(2, "cannot lock memory for IO request (%s)", strerror(res)); 123 goto err; 124 } 125 126 if (fill_temp_sg(ccb)) 127 // this is the success path 128 return true; 129 130 unlock_memory(ccb->data, ccb->data_length, B_DMA_IO 131 | ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0)); 132 133 err: 134 locked_pool->free(temp_sg_pool, temp_sg); 135 return false; 136 } 137 138 139 /** cleanup temporary SG list */ 140 141 void 142 uninit_temp_sg(void) 143 { 144 locked_pool->destroy(temp_sg_pool); 145 } 146 147 148 /** destroy SG list buffer */ 149 150 void 151 cleanup_tmp_sg(scsi_ccb *ccb) 152 { 153 status_t res; 154 155 SHOW_FLOW(3, "ccb=%p, data=%p, data_length=%" B_PRId32, 156 ccb, ccb->data, ccb->data_length); 157 158 res = unlock_memory(ccb->data, ccb->data_length, B_DMA_IO 159 | ((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_IN ? B_READ_DEVICE : 0)); 160 161 if (res != B_OK) { 162 SHOW_FLOW0(3, "Cannot unlock previously locked memory!"); 163 panic("Cannot unlock previously locked memory!"); 164 } 165 166 locked_pool->free(temp_sg_pool, (physical_entry *)ccb->sg_list); 167 168 // restore previous state 169 ccb->sg_list = NULL; 170 } 171 172 173 /** create SG list buffer */ 174 175 int 176 init_temp_sg(void) 177 { 178 temp_sg_pool = locked_pool->create( 179 MAX_TEMP_SG_FRAGMENTS * sizeof(physical_entry), 180 sizeof(physical_entry) - 1, 0, 181 B_PAGE_SIZE, MAX_TEMP_SG_LISTS, 1, 182 "scsi_temp_sg_pool", B_CONTIGUOUS, NULL, NULL, NULL); 183 184 if (temp_sg_pool == NULL) 185 return B_NO_MEMORY; 186 187 return B_OK; 188 } 189 190