aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOr Gerlitz <ogerlitz@voltaire.com>2009-11-12 14:32:27 -0500
committerRoland Dreier <rolandd@cisco.com>2009-11-12 14:32:27 -0500
commitc1ccaf2478f84c2665cf57f981db143aa582d646 (patch)
tree20a529401839912380e6ca0f67aafff4dba77384
parent91d3f9bacdb4950d2f79fe2ba296aa249f60d06c (diff)
IB/iser: Rewrite SG handling for RDMA logic
After dma-mapping an SG list provided by the SCSI midlayer, iser has to make sure the mapped SG is "aligned for RDMA" in the sense that its possible to produce one mapping in the HCA IOMMU which represents the whole SG. Next, the mapped SG is formatted for registration with the HCA. This patch re-writes the logic that does the above, to make it clearer and simpler. It also fixes a bug in the being aligned for RDMA checks, where a "start" check wasn't done but rather only "end" check. Signed-off-by: Alexander Nezhinsky <alexandern@voltaire.com> Signed-off-by: Or Gerlitz <ogerlitz@voltaire.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c122
1 files changed, 56 insertions, 66 deletions
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index b9453d068e9d..274c883ef3ea 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -209,6 +209,8 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
209 mem_copy->copy_buf = NULL; 209 mem_copy->copy_buf = NULL;
210} 210}
211 211
212#define IS_4K_ALIGNED(addr) ((((unsigned long)addr) & ~MASK_4K) == 0)
213
212/** 214/**
213 * iser_sg_to_page_vec - Translates scatterlist entries to physical addresses 215 * iser_sg_to_page_vec - Translates scatterlist entries to physical addresses
214 * and returns the length of resulting physical address array (may be less than 216 * and returns the length of resulting physical address array (may be less than
@@ -221,62 +223,52 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
221 * where --few fragments of the same page-- are present in the SG as 223 * where --few fragments of the same page-- are present in the SG as
222 * consecutive elements. Also, it handles one entry SG. 224 * consecutive elements. Also, it handles one entry SG.
223 */ 225 */
226
224static int iser_sg_to_page_vec(struct iser_data_buf *data, 227static int iser_sg_to_page_vec(struct iser_data_buf *data,
225 struct iser_page_vec *page_vec, 228 struct iser_page_vec *page_vec,
226 struct ib_device *ibdev) 229 struct ib_device *ibdev)
227{ 230{
228 struct scatterlist *sgl = (struct scatterlist *)data->buf; 231 struct scatterlist *sg, *sgl = (struct scatterlist *)data->buf;
229 struct scatterlist *sg; 232 u64 start_addr, end_addr, page, chunk_start = 0;
230 u64 first_addr, last_addr, page;
231 int end_aligned;
232 unsigned int cur_page = 0;
233 unsigned long total_sz = 0; 233 unsigned long total_sz = 0;
234 int i; 234 unsigned int dma_len;
235 int i, new_chunk, cur_page, last_ent = data->dma_nents - 1;
235 236
236 /* compute the offset of first element */ 237 /* compute the offset of first element */
237 page_vec->offset = (u64) sgl[0].offset & ~MASK_4K; 238 page_vec->offset = (u64) sgl[0].offset & ~MASK_4K;
238 239
240 new_chunk = 1;
241 cur_page = 0;
239 for_each_sg(sgl, sg, data->dma_nents, i) { 242 for_each_sg(sgl, sg, data->dma_nents, i) {
240 unsigned int dma_len = ib_sg_dma_len(ibdev, sg); 243 start_addr = ib_sg_dma_address(ibdev, sg);
241 244 if (new_chunk)
245 chunk_start = start_addr;
246 dma_len = ib_sg_dma_len(ibdev, sg);
247 end_addr = start_addr + dma_len;
242 total_sz += dma_len; 248 total_sz += dma_len;
243 249
244 first_addr = ib_sg_dma_address(ibdev, sg); 250 /* collect page fragments until aligned or end of SG list */
245 last_addr = first_addr + dma_len; 251 if (!IS_4K_ALIGNED(end_addr) && i < last_ent) {
246 252 new_chunk = 0;
247 end_aligned = !(last_addr & ~MASK_4K); 253 continue;
248
249 /* continue to collect page fragments till aligned or SG ends */
250 while (!end_aligned && (i + 1 < data->dma_nents)) {
251 sg = sg_next(sg);
252 i++;
253 dma_len = ib_sg_dma_len(ibdev, sg);
254 total_sz += dma_len;
255 last_addr = ib_sg_dma_address(ibdev, sg) + dma_len;
256 end_aligned = !(last_addr & ~MASK_4K);
257 } 254 }
258 255 new_chunk = 1;
259 /* handle the 1st page in the 1st DMA element */ 256
260 if (cur_page == 0) { 257 /* address of the first page in the contiguous chunk;
261 page = first_addr & MASK_4K; 258 masking relevant for the very first SG entry,
262 page_vec->pages[cur_page] = page; 259 which might be unaligned */
263 cur_page++; 260 page = chunk_start & MASK_4K;
261 do {
262 page_vec->pages[cur_page++] = page;
264 page += SIZE_4K; 263 page += SIZE_4K;
265 } else 264 } while (page < end_addr);
266 page = first_addr;
267
268 for (; page < last_addr; page += SIZE_4K) {
269 page_vec->pages[cur_page] = page;
270 cur_page++;
271 }
272
273 } 265 }
266
274 page_vec->data_size = total_sz; 267 page_vec->data_size = total_sz;
275 iser_dbg("page_vec->data_size:%d cur_page %d\n", page_vec->data_size,cur_page); 268 iser_dbg("page_vec->data_size:%d cur_page %d\n", page_vec->data_size,cur_page);
276 return cur_page; 269 return cur_page;
277} 270}
278 271
279#define IS_4K_ALIGNED(addr) ((((unsigned long)addr) & ~MASK_4K) == 0)
280 272
281/** 273/**
282 * iser_data_buf_aligned_len - Tries to determine the maximal correctly aligned 274 * iser_data_buf_aligned_len - Tries to determine the maximal correctly aligned
@@ -284,42 +276,40 @@ static int iser_sg_to_page_vec(struct iser_data_buf *data,
284 * the number of entries which are aligned correctly. Supports the case where 276 * the number of entries which are aligned correctly. Supports the case where
285 * consecutive SG elements are actually fragments of the same physcial page. 277 * consecutive SG elements are actually fragments of the same physcial page.
286 */ 278 */
287static unsigned int iser_data_buf_aligned_len(struct iser_data_buf *data, 279static int iser_data_buf_aligned_len(struct iser_data_buf *data,
288 struct ib_device *ibdev) 280 struct ib_device *ibdev)
289{ 281{
290 struct scatterlist *sgl, *sg; 282 struct scatterlist *sgl, *sg, *next_sg = NULL;
291 u64 end_addr, next_addr; 283 u64 start_addr, end_addr;
292 int i, cnt; 284 int i, ret_len, start_check = 0;
293 unsigned int ret_len = 0; 285
286 if (data->dma_nents == 1)
287 return 1;
294 288
295 sgl = (struct scatterlist *)data->buf; 289 sgl = (struct scatterlist *)data->buf;
290 start_addr = ib_sg_dma_address(ibdev, sgl);
296 291
297 cnt = 0;
298 for_each_sg(sgl, sg, data->dma_nents, i) { 292 for_each_sg(sgl, sg, data->dma_nents, i) {
299 /* iser_dbg("Checking sg iobuf [%d]: phys=0x%08lX " 293 if (start_check && !IS_4K_ALIGNED(start_addr))
300 "offset: %ld sz: %ld\n", i, 294 break;
301 (unsigned long)sg_phys(sg), 295
302 (unsigned long)sg->offset, 296 next_sg = sg_next(sg);
303 (unsigned long)sg->length); */ 297 if (!next_sg)
304 end_addr = ib_sg_dma_address(ibdev, sg) + 298 break;
305 ib_sg_dma_len(ibdev, sg); 299
306 /* iser_dbg("Checking sg iobuf end address " 300 end_addr = start_addr + ib_sg_dma_len(ibdev, sg);
307 "0x%08lX\n", end_addr); */ 301 start_addr = ib_sg_dma_address(ibdev, next_sg);
308 if (i + 1 < data->dma_nents) { 302
309 next_addr = ib_sg_dma_address(ibdev, sg_next(sg)); 303 if (end_addr == start_addr) {
310 /* are i, i+1 fragments of the same page? */ 304 start_check = 0;
311 if (end_addr == next_addr) { 305 continue;
312 cnt++; 306 } else
313 continue; 307 start_check = 1;
314 } else if (!IS_4K_ALIGNED(end_addr)) { 308
315 ret_len = cnt + 1; 309 if (!IS_4K_ALIGNED(end_addr))
316 break; 310 break;
317 }
318 }
319 cnt++;
320 } 311 }
321 if (i == data->dma_nents) 312 ret_len = (next_sg) ? i : i+1;
322 ret_len = cnt; /* loop ended */
323 iser_dbg("Found %d aligned entries out of %d in sg:0x%p\n", 313 iser_dbg("Found %d aligned entries out of %d in sg:0x%p\n",
324 ret_len, data->dma_nents, data); 314 ret_len, data->dma_nents, data);
325 return ret_len; 315 return ret_len;