aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_lib.c
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2006-04-02 15:57:43 -0400
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-04-14 17:45:27 -0400
commitcdb8c2a6d848deb9eeefffff42974478fbb51b8c (patch)
treefc862e003d35def8bc9d160180ec1b1440679a52 /drivers/scsi/scsi_lib.c
parent4c021dd136c0ad524e6d117296beafad2bf570c0 (diff)
[SCSI] dc395x: dynamically map scatter-gather for PIO
The current dc395x driver uses PIO to transfer up to 4 bytes which do not get transferred by DMA (under unclear circumstances). For this the driver uses page_address() which is broken on highmem. Apart from this the actual calculation of the virtual address is wrong (even without highmem). So, e.g., for reading it reads bytes from the driver to a wrong address and returns wrong data, I guess, for writing it would just output random data to the device. The proper fix, as suggested by many, is to dynamically map data using kmap_atomic(page, KM_BIO_SRC_IRQ) / kunmap_atomic(virt). The reason why it has not been done until now, although I've done some preliminary patches more than a year ago was that nobody interested in fixing this problem was able to reliably reproduce it. Now it changed - with the help from Sebastian Frei (CC'ed) I was able to trigger the PIO path. Thus, I was also able to test and debug it. There are 4 cases when PIO is used in dc395x - data-in / -out with and without scatter-gather. I was able to reproduce and test only data-in with and without SG. So, the data-out path is still untested, but it is also somewhat simpler than the data-in. Fredrik Roubert (also CC'ed) also had PIO triggering on his system, and in his case it was data-out without SG. It would be great if he could test the attached patch on his system, but even if he cannot, I would still request to apply the patch and just wait if anybody cries... Implementation: I put 2 new functions in scsi_lib.c and their declarations in scsi_cmnd.h. I exported them without _GPL, although, I don't feel strongly about that - not many drivers are likely to use them. But there is at least one more - I want to use them in tmscsim.c. Whether these are the right files for the functions and their declarations - not sure either. Actually, they are not scsi-specific, so, might go somewhere around other scattergather magic? They are not platform specific either, and most SG functions are defined under arch/*/... As these issues were discussed previously there were some more routines suggested to manipulate scattergather buffers, I think, some of them were needed around crypto code... So, might be a common place reasonable, like lib/scattergather.c? I am open here. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/scsi_lib.c')
-rw-r--r--drivers/scsi/scsi_lib.c57
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 7b0f9a3810d..57453ca0970 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2350,3 +2350,60 @@ scsi_target_unblock(struct device *dev)
2350 device_for_each_child(dev, NULL, target_unblock); 2350 device_for_each_child(dev, NULL, target_unblock);
2351} 2351}
2352EXPORT_SYMBOL_GPL(scsi_target_unblock); 2352EXPORT_SYMBOL_GPL(scsi_target_unblock);
2353
2354/**
2355 * scsi_kmap_atomic_sg - find and atomically map an sg-elemnt
2356 * @sg: scatter-gather list
2357 * @sg_count: number of segments in sg
2358 * @offset: offset in bytes into sg, on return offset into the mapped area
2359 * @len: bytes to map, on return number of bytes mapped
2360 *
2361 * Returns virtual address of the start of the mapped page
2362 */
2363void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
2364 size_t *offset, size_t *len)
2365{
2366 int i;
2367 size_t sg_len = 0, len_complete = 0;
2368 struct page *page;
2369
2370 for (i = 0; i < sg_count; i++) {
2371 len_complete = sg_len; /* Complete sg-entries */
2372 sg_len += sg[i].length;
2373 if (sg_len > *offset)
2374 break;
2375 }
2376
2377 if (unlikely(i == sg_count)) {
2378 printk(KERN_ERR "%s: Bytes in sg: %u, requested offset %u, elements %d\n",
2379 __FUNCTION__, sg_len, *offset, sg_count);
2380 WARN_ON(1);
2381 return NULL;
2382 }
2383
2384 /* Offset starting from the beginning of first page in this sg-entry */
2385 *offset = *offset - len_complete + sg[i].offset;
2386
2387 /* Assumption: contiguous pages can be accessed as "page + i" */
2388 page = nth_page(sg[i].page, (*offset >> PAGE_SHIFT));
2389 *offset &= ~PAGE_MASK;
2390
2391 /* Bytes in this sg-entry from *offset to the end of the page */
2392 sg_len = PAGE_SIZE - *offset;
2393 if (*len > sg_len)
2394 *len = sg_len;
2395
2396 return kmap_atomic(page, KM_BIO_SRC_IRQ);
2397}
2398EXPORT_SYMBOL(scsi_kmap_atomic_sg);
2399
2400/**
2401 * scsi_kunmap_atomic_sg - atomically unmap a virtual address, previously
2402 * mapped with scsi_kmap_atomic_sg
2403 * @virt: virtual address to be unmapped
2404 */
2405void scsi_kunmap_atomic_sg(void *virt)
2406{
2407 kunmap_atomic(virt, KM_BIO_SRC_IRQ);
2408}
2409EXPORT_SYMBOL(scsi_kunmap_atomic_sg);