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