diff options
Diffstat (limited to 'drivers/scsi/scsi_lib.c')
-rw-r--r-- | drivers/scsi/scsi_lib.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 7b0f9a3810d2..57453ca09700 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 | } |
2352 | EXPORT_SYMBOL_GPL(scsi_target_unblock); | 2352 | EXPORT_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 | */ | ||
2363 | void *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 | } | ||
2398 | EXPORT_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 | */ | ||
2405 | void scsi_kunmap_atomic_sg(void *virt) | ||
2406 | { | ||
2407 | kunmap_atomic(virt, KM_BIO_SRC_IRQ); | ||
2408 | } | ||
2409 | EXPORT_SYMBOL(scsi_kunmap_atomic_sg); | ||