#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/hardirq.h>
+#include <linux/scatterlist.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#define SG_MEMPOOL_NR ARRAY_SIZE(scsi_sg_pools)
#define SG_MEMPOOL_SIZE 2
+/*
+ * The maximum number of SG segments that we will put inside a scatterlist
+ * (unless chaining is used). Should ideally fit inside a single page, to
+ * avoid a higher order allocation.
+ */
+#define SCSI_MAX_SG_SEGMENTS 128
+
struct scsi_host_sg_pool {
size_t size;
- char *name;
+ char *name;
struct kmem_cache *slab;
mempool_t *pool;
};
-#if (SCSI_MAX_PHYS_SEGMENTS < 32)
-#error SCSI_MAX_PHYS_SEGMENTS is too small
-#endif
-
-#define SP(x) { x, "sgpool-" #x }
+#define SP(x) { x, "sgpool-" #x }
static struct scsi_host_sg_pool scsi_sg_pools[] = {
SP(8),
SP(16),
+#if (SCSI_MAX_SG_SEGMENTS > 16)
SP(32),
-#if (SCSI_MAX_PHYS_SEGMENTS > 32)
+#if (SCSI_MAX_SG_SEGMENTS > 32)
SP(64),
-#if (SCSI_MAX_PHYS_SEGMENTS > 64)
+#if (SCSI_MAX_SG_SEGMENTS > 64)
SP(128),
-#if (SCSI_MAX_PHYS_SEGMENTS > 128)
- SP(256),
-#if (SCSI_MAX_PHYS_SEGMENTS > 256)
-#error SCSI_MAX_PHYS_SEGMENTS is too large
-#endif
#endif
#endif
#endif
-};
+};
#undef SP
static void scsi_run_queue(struct request_queue *q);
*
* returns the req->errors value which is the scsi_cmnd result
* field.
- **/
+ */
int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
unsigned char *sense, int timeout, int retries, int flags)
/**
* scsi_req_map_sg - map a scatterlist into a request
* @rq: request to fill
- * @sg: scatterlist
+ * @sgl: scatterlist
* @nsegs: number of elements
* @bufflen: len of buffer
* @gfp: memory allocation flags
struct request_queue *q = rq->q;
int nr_pages = (bufflen + sgl[0].offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
unsigned int data_len = bufflen, len, bytes, off;
+ struct scatterlist *sg;
struct page *page;
struct bio *bio = NULL;
int i, err, nr_vecs = 0;
- for (i = 0; i < nsegs; i++) {
- page = sgl[i].page;
- off = sgl[i].offset;
- len = sgl[i].length;
+ for_each_sg(sgl, sg, nsegs, i) {
+ page = sg_page(sg);
+ off = sg->offset;
+ len = sg->length;
+ data_len += len;
while (len > 0 && data_len > 0) {
/*
* @sdev: scsi device
* @cmd: scsi command
* @cmd_len: length of scsi cdb
- * @data_direction: data direction
+ * @data_direction: DMA_TO_DEVICE, DMA_FROM_DEVICE, or DMA_NONE
* @buffer: data buffer (this can be a kernel buffer or scatterlist)
* @bufflen: len of buffer
* @use_sg: if buffer is a scatterlist this is the number of elements
* @timeout: request timeout in seconds
* @retries: number of times to retry request
- * @flags: or into request flags
- **/
+ * @privdata: data passed to done()
+ * @done: callback function when done
+ * @gfp: memory allocation flags
+ */
int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd,
int cmd_len, int data_direction, void *buffer, unsigned bufflen,
int use_sg, int timeout, int retries, void *privdata,
{
cmd->serial_number = 0;
cmd->resid = 0;
- memset(cmd->sense_buffer, 0, sizeof cmd->sense_buffer);
+ memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
if (cmd->cmd_len == 0)
cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
}
struct Scsi_Host *shost = sdev->host;
unsigned long flags;
- if (sdev->single_lun)
+ if (scsi_target(sdev)->single_lun)
scsi_single_lun_run(sdev);
spin_lock_irqsave(shost->host_lock, flags);
return NULL;
}
-struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask)
-{
- struct scsi_host_sg_pool *sgp;
- struct scatterlist *sgl;
+/*
+ * Like SCSI_MAX_SG_SEGMENTS, but for archs that have sg chaining. This limit
+ * is totally arbitrary, a setting of 2048 will get you at least 8mb ios.
+ */
+#define SCSI_MAX_SG_CHAIN_SEGMENTS 2048
- BUG_ON(!cmd->use_sg);
+static inline unsigned int scsi_sgtable_index(unsigned short nents)
+{
+ unsigned int index;
- switch (cmd->use_sg) {
+ switch (nents) {
case 1 ... 8:
- cmd->sglist_len = 0;
+ index = 0;
break;
case 9 ... 16:
- cmd->sglist_len = 1;
+ index = 1;
break;
+#if (SCSI_MAX_SG_SEGMENTS > 16)
case 17 ... 32:
- cmd->sglist_len = 2;
+ index = 2;
break;
-#if (SCSI_MAX_PHYS_SEGMENTS > 32)
+#if (SCSI_MAX_SG_SEGMENTS > 32)
case 33 ... 64:
- cmd->sglist_len = 3;
+ index = 3;
break;
-#if (SCSI_MAX_PHYS_SEGMENTS > 64)
+#if (SCSI_MAX_SG_SEGMENTS > 64)
case 65 ... 128:
- cmd->sglist_len = 4;
- break;
-#if (SCSI_MAX_PHYS_SEGMENTS > 128)
- case 129 ... 256:
- cmd->sglist_len = 5;
+ index = 4;
break;
#endif
#endif
#endif
default:
- return NULL;
+ printk(KERN_ERR "scsi: bad segment count=%d\n", nents);
+ BUG();
}
- sgp = scsi_sg_pools + cmd->sglist_len;
- sgl = mempool_alloc(sgp->pool, gfp_mask);
- return sgl;
+ return index;
+}
+
+struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+{
+ struct scsi_host_sg_pool *sgp;
+ struct scatterlist *sgl, *prev, *ret;
+ unsigned int index;
+ int this, left;
+
+ BUG_ON(!cmd->use_sg);
+
+ left = cmd->use_sg;
+ ret = prev = NULL;
+ do {
+ this = left;
+ if (this > SCSI_MAX_SG_SEGMENTS) {
+ this = SCSI_MAX_SG_SEGMENTS - 1;
+ index = SG_MEMPOOL_NR - 1;
+ } else
+ index = scsi_sgtable_index(this);
+
+ left -= this;
+
+ sgp = scsi_sg_pools + index;
+
+ sgl = mempool_alloc(sgp->pool, gfp_mask);
+ if (unlikely(!sgl))
+ goto enomem;
+
+ sg_init_table(sgl, sgp->size);
+
+ /*
+ * first loop through, set initial index and return value
+ */
+ if (!ret)
+ ret = sgl;
+
+ /*
+ * chain previous sglist, if any. we know the previous
+ * sglist must be the biggest one, or we would not have
+ * ended up doing another loop.
+ */
+ if (prev)
+ sg_chain(prev, SCSI_MAX_SG_SEGMENTS, sgl);
+
+ /*
+ * if we have nothing left, mark the last segment as
+ * end-of-list
+ */
+ if (!left)
+ sg_mark_end(&sgl[this - 1]);
+
+ /*
+ * don't allow subsequent mempool allocs to sleep, it would
+ * violate the mempool principle.
+ */
+ gfp_mask &= ~__GFP_WAIT;
+ gfp_mask |= __GFP_HIGH;
+ prev = sgl;
+ } while (left);
+
+ /*
+ * ->use_sg may get modified after dma mapping has potentially
+ * shrunk the number of segments, so keep a copy of it for free.
+ */
+ cmd->__use_sg = cmd->use_sg;
+ return ret;
+enomem:
+ if (ret) {
+ /*
+ * Free entries chained off ret. Since we were trying to
+ * allocate another sglist, we know that all entries are of
+ * the max size.
+ */
+ sgp = scsi_sg_pools + SG_MEMPOOL_NR - 1;
+ prev = ret;
+ ret = &ret[SCSI_MAX_SG_SEGMENTS - 1];
+
+ while ((sgl = sg_chain_ptr(ret)) != NULL) {
+ ret = &sgl[SCSI_MAX_SG_SEGMENTS - 1];
+ mempool_free(sgl, sgp->pool);
+ }
+
+ mempool_free(prev, sgp->pool);
+ }
+ return NULL;
}
EXPORT_SYMBOL(scsi_alloc_sgtable);
-void scsi_free_sgtable(struct scatterlist *sgl, int index)
+void scsi_free_sgtable(struct scsi_cmnd *cmd)
{
+ struct scatterlist *sgl = cmd->request_buffer;
struct scsi_host_sg_pool *sgp;
- BUG_ON(index >= SG_MEMPOOL_NR);
+ /*
+ * if this is the biggest size sglist, check if we have
+ * chained parts we need to free
+ */
+ if (cmd->__use_sg > SCSI_MAX_SG_SEGMENTS) {
+ unsigned short this, left;
+ struct scatterlist *next;
+ unsigned int index;
+
+ left = cmd->__use_sg - (SCSI_MAX_SG_SEGMENTS - 1);
+ next = sg_chain_ptr(&sgl[SCSI_MAX_SG_SEGMENTS - 1]);
+ while (left && next) {
+ sgl = next;
+ this = left;
+ if (this > SCSI_MAX_SG_SEGMENTS) {
+ this = SCSI_MAX_SG_SEGMENTS - 1;
+ index = SG_MEMPOOL_NR - 1;
+ } else
+ index = scsi_sgtable_index(this);
+
+ left -= this;
+
+ sgp = scsi_sg_pools + index;
+
+ if (left)
+ next = sg_chain_ptr(&sgl[sgp->size - 1]);
+
+ mempool_free(sgl, sgp->pool);
+ }
+
+ /*
+ * Restore original, will be freed below
+ */
+ sgl = cmd->request_buffer;
+ sgp = scsi_sg_pools + SG_MEMPOOL_NR - 1;
+ } else
+ sgp = scsi_sg_pools + scsi_sgtable_index(cmd->__use_sg);
- sgp = scsi_sg_pools + index;
mempool_free(sgl, sgp->pool);
}
static void scsi_release_buffers(struct scsi_cmnd *cmd)
{
if (cmd->use_sg)
- scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len);
+ scsi_free_sgtable(cmd);
/*
* Zero these out. They now point to freed memory, and it is
*
* Returns: 0 on success
* BLKPREP_DEFER if the failure is retryable
- * BLKPREP_KILL if the failure is fatal
*/
static int scsi_init_io(struct scsi_cmnd *cmd)
{
struct request *req = cmd->request;
- struct scatterlist *sgpnt;
int count;
/*
/*
* If sg table allocation fails, requeue request later.
*/
- sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC);
- if (unlikely(!sgpnt)) {
+ cmd->request_buffer = scsi_alloc_sgtable(cmd, GFP_ATOMIC);
+ if (unlikely(!cmd->request_buffer)) {
scsi_unprep_request(req);
return BLKPREP_DEFER;
}
req->buffer = NULL;
- cmd->request_buffer = (char *) sgpnt;
if (blk_pc_request(req))
cmd->request_bufflen = req->data_len;
else
* each segment.
*/
count = blk_rq_map_sg(req->q, req, cmd->request_buffer);
- if (likely(count <= cmd->use_sg)) {
- cmd->use_sg = count;
- return BLKPREP_OK;
- }
-
- printk(KERN_ERR "Incorrect number of segments after building list\n");
- printk(KERN_ERR "counted %d, received %d\n", count, cmd->use_sg);
- printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors,
- req->current_nr_sectors);
-
- return BLKPREP_KILL;
+ BUG_ON(count > cmd->use_sg);
+ cmd->use_sg = count;
+ return BLKPREP_OK;
}
static struct scsi_cmnd *scsi_get_cmd_from_req(struct scsi_device *sdev,
}
EXPORT_SYMBOL(scsi_prep_return);
-static int scsi_prep_fn(struct request_queue *q, struct request *req)
+int scsi_prep_fn(struct request_queue *q, struct request *req)
{
struct scsi_device *sdev = q->queuedata;
int ret = BLKPREP_KILL;
if (!scsi_host_queue_ready(q, shost, sdev))
goto not_ready;
- if (sdev->single_lun) {
+ if (scsi_target(sdev)->single_lun) {
if (scsi_target(sdev)->starget_sdev_user &&
scsi_target(sdev)->starget_sdev_user != sdev)
goto not_ready;
if (!q)
return NULL;
+ /*
+ * this limit is imposed by hardware restrictions
+ */
blk_queue_max_hw_segments(q, shost->sg_tablesize);
- blk_queue_max_phys_segments(q, SCSI_MAX_PHYS_SEGMENTS);
+
+ /*
+ * In the future, sg chaining support will be mandatory and this
+ * ifdef can then go away. Right now we don't have all archs
+ * converted, so better keep it safe.
+ */
+#ifdef ARCH_HAS_SG_CHAIN
+ if (shost->use_sg_chaining)
+ blk_queue_max_phys_segments(q, SCSI_MAX_SG_CHAIN_SEGMENTS);
+ else
+ blk_queue_max_phys_segments(q, SCSI_MAX_SG_SEGMENTS);
+#else
+ blk_queue_max_phys_segments(q, SCSI_MAX_SG_SEGMENTS);
+#endif
+
blk_queue_max_sectors(q, shost->max_sectors);
blk_queue_bounce_limit(q, scsi_calculate_bounce_limit(shost));
blk_queue_segment_boundary(q, shost->dma_boundary);
if (!shost->use_clustering)
clear_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
+
+ /*
+ * set a reasonable default alignment on word boundaries: the
+ * host and device may alter it using
+ * blk_queue_update_dma_alignment() later.
+ */
+ blk_queue_dma_alignment(q, 0x03);
+
return q;
}
EXPORT_SYMBOL(__scsi_alloc_queue);
* @timeout: command timeout
* @retries: number of retries before failing
* @data: returns a structure abstracting the mode header data
- * @sense: place to put sense data (or NULL if no sense to be collected).
+ * @sshdr: place to put sense data (or NULL if no sense to be collected).
* must be SCSI_SENSE_BUFFERSIZE big.
*
* Returns zero if successful; negative error number or scsi
EXPORT_SYMBOL_GPL(scsi_mode_select);
/**
- * scsi_mode_sense - issue a mode sense, falling back from 10 to
- * six bytes if necessary.
+ * scsi_mode_sense - issue a mode sense, falling back from 10 to six bytes if necessary.
* @sdev: SCSI device to be queried
* @dbd: set if mode sense will allow block descriptors to be returned
* @modepage: mode page being requested
* @timeout: command timeout
* @retries: number of retries before failing
* @data: returns a structure abstracting the mode header data
- * @sense: place to put sense data (or NULL if no sense to be collected).
+ * @sshdr: place to put sense data (or NULL if no sense to be collected).
* must be SCSI_SENSE_BUFFERSIZE big.
*
* Returns zero if unsuccessful, or the header offset (either 4
* or 8 depending on whether a six or ten byte command was
* issued) if successful.
- **/
+ */
int
scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
unsigned char *buffer, int len, int timeout, int retries,
}
EXPORT_SYMBOL(scsi_mode_sense);
+/**
+ * scsi_test_unit_ready - test if unit is ready
+ * @sdev: scsi device to change the state of.
+ * @timeout: command timeout
+ * @retries: number of retries before failing
+ * @sshdr_external: Optional pointer to struct scsi_sense_hdr for
+ * returning sense. Make sure that this is cleared before passing
+ * in.
+ *
+ * Returns zero if unsuccessful or an error if TUR failed. For
+ * removable media, a return of NOT_READY or UNIT_ATTENTION is
+ * translated to success, with the ->changed flag updated.
+ **/
int
-scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries)
+scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
+ struct scsi_sense_hdr *sshdr_external)
{
char cmd[] = {
TEST_UNIT_READY, 0, 0, 0, 0, 0,
};
- struct scsi_sense_hdr sshdr;
+ struct scsi_sense_hdr *sshdr;
int result;
-
- result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr,
- timeout, retries);
+
+ if (!sshdr_external)
+ sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
+ else
+ sshdr = sshdr_external;
+
+ /* try to eat the UNIT_ATTENTION if there are enough retries */
+ do {
+ result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr,
+ timeout, retries);
+ } while ((driver_byte(result) & DRIVER_SENSE) &&
+ sshdr && sshdr->sense_key == UNIT_ATTENTION &&
+ --retries);
+
+ if (!sshdr)
+ /* could not allocate sense buffer, so can't process it */
+ return result;
if ((driver_byte(result) & DRIVER_SENSE) && sdev->removable) {
- if ((scsi_sense_valid(&sshdr)) &&
- ((sshdr.sense_key == UNIT_ATTENTION) ||
- (sshdr.sense_key == NOT_READY))) {
+ if ((scsi_sense_valid(sshdr)) &&
+ ((sshdr->sense_key == UNIT_ATTENTION) ||
+ (sshdr->sense_key == NOT_READY))) {
sdev->changed = 1;
result = 0;
}
}
+ if (!sshdr_external)
+ kfree(sshdr);
return result;
}
EXPORT_SYMBOL(scsi_test_unit_ready);
/**
- * scsi_device_set_state - Take the given device through the device
- * state model.
+ * scsi_device_set_state - Take the given device through the device state model.
* @sdev: scsi device to change the state of.
* @state: state to change to.
*
* Returns zero if unsuccessful or an error if the requested
* transition is illegal.
- **/
+ */
int
scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
{
}
EXPORT_SYMBOL(scsi_device_set_state);
+/**
+ * sdev_evt_emit - emit a single SCSI device uevent
+ * @sdev: associated SCSI device
+ * @evt: event to emit
+ *
+ * Send a single uevent (scsi_event) to the associated scsi_device.
+ */
+static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt)
+{
+ int idx = 0;
+ char *envp[3];
+
+ switch (evt->evt_type) {
+ case SDEV_EVT_MEDIA_CHANGE:
+ envp[idx++] = "SDEV_MEDIA_CHANGE=1";
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+
+ envp[idx++] = NULL;
+
+ kobject_uevent_env(&sdev->sdev_gendev.kobj, KOBJ_CHANGE, envp);
+}
+
+/**
+ * sdev_evt_thread - send a uevent for each scsi event
+ * @work: work struct for scsi_device
+ *
+ * Dispatch queued events to their associated scsi_device kobjects
+ * as uevents.
+ */
+void scsi_evt_thread(struct work_struct *work)
+{
+ struct scsi_device *sdev;
+ LIST_HEAD(event_list);
+
+ sdev = container_of(work, struct scsi_device, event_work);
+
+ while (1) {
+ struct scsi_event *evt;
+ struct list_head *this, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdev->list_lock, flags);
+ list_splice_init(&sdev->event_list, &event_list);
+ spin_unlock_irqrestore(&sdev->list_lock, flags);
+
+ if (list_empty(&event_list))
+ break;
+
+ list_for_each_safe(this, tmp, &event_list) {
+ evt = list_entry(this, struct scsi_event, node);
+ list_del(&evt->node);
+ scsi_evt_emit(sdev, evt);
+ kfree(evt);
+ }
+ }
+}
+
+/**
+ * sdev_evt_send - send asserted event to uevent thread
+ * @sdev: scsi_device event occurred on
+ * @evt: event to send
+ *
+ * Assert scsi device event asynchronously.
+ */
+void sdev_evt_send(struct scsi_device *sdev, struct scsi_event *evt)
+{
+ unsigned long flags;
+
+ if (!test_bit(evt->evt_type, sdev->supported_events)) {
+ kfree(evt);
+ return;
+ }
+
+ spin_lock_irqsave(&sdev->list_lock, flags);
+ list_add_tail(&evt->node, &sdev->event_list);
+ schedule_work(&sdev->event_work);
+ spin_unlock_irqrestore(&sdev->list_lock, flags);
+}
+EXPORT_SYMBOL_GPL(sdev_evt_send);
+
+/**
+ * sdev_evt_alloc - allocate a new scsi event
+ * @evt_type: type of event to allocate
+ * @gfpflags: GFP flags for allocation
+ *
+ * Allocates and returns a new scsi_event.
+ */
+struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,
+ gfp_t gfpflags)
+{
+ struct scsi_event *evt = kzalloc(sizeof(struct scsi_event), gfpflags);
+ if (!evt)
+ return NULL;
+
+ evt->evt_type = evt_type;
+ INIT_LIST_HEAD(&evt->node);
+
+ /* evt_type-specific initialization, if any */
+ switch (evt_type) {
+ case SDEV_EVT_MEDIA_CHANGE:
+ default:
+ /* do nothing */
+ break;
+ }
+
+ return evt;
+}
+EXPORT_SYMBOL_GPL(sdev_evt_alloc);
+
+/**
+ * sdev_evt_send_simple - send asserted event to uevent thread
+ * @sdev: scsi_device event occurred on
+ * @evt_type: type of event to send
+ * @gfpflags: GFP flags for allocation
+ *
+ * Assert scsi device event asynchronously, given an event type.
+ */
+void sdev_evt_send_simple(struct scsi_device *sdev,
+ enum scsi_device_event evt_type, gfp_t gfpflags)
+{
+ struct scsi_event *evt = sdev_evt_alloc(evt_type, gfpflags);
+ if (!evt) {
+ sdev_printk(KERN_ERR, sdev, "event %d eaten due to OOM\n",
+ evt_type);
+ return;
+ }
+
+ sdev_evt_send(sdev, evt);
+}
+EXPORT_SYMBOL_GPL(sdev_evt_send_simple);
+
/**
* scsi_device_quiesce - Block user issued commands.
* @sdev: scsi device to quiesce.
* Must be called with user context, may sleep.
*
* Returns zero if unsuccessful or an error if not.
- **/
+ */
int
scsi_device_quiesce(struct scsi_device *sdev)
{
* queues.
*
* Must be called with user context, may sleep.
- **/
+ */
void
scsi_device_resume(struct scsi_device *sdev)
{
EXPORT_SYMBOL(scsi_target_resume);
/**
- * scsi_internal_device_block - internal function to put a device
- * temporarily into the SDEV_BLOCK state
+ * scsi_internal_device_block - internal function to put a device temporarily into the SDEV_BLOCK state
* @sdev: device to block
*
* Block request made by scsi lld's to temporarily stop all
* state, all commands are deferred until the scsi lld reenables
* the device with scsi_device_unblock or device_block_tmo fires.
* This routine assumes the host_lock is held on entry.
- **/
+ */
int
scsi_internal_device_block(struct scsi_device *sdev)
{
* (which must be a legal transition) allowing the midlayer to
* goose the queue for this device. This routine assumes the
* host_lock is held upon entry.
- **/
+ */
int
scsi_internal_device_unblock(struct scsi_device *sdev)
{
/**
* scsi_kmap_atomic_sg - find and atomically map an sg-elemnt
- * @sg: scatter-gather list
+ * @sgl: scatter-gather list
* @sg_count: number of segments in sg
* @offset: offset in bytes into sg, on return offset into the mapped area
* @len: bytes to map, on return number of bytes mapped
*
* Returns virtual address of the start of the mapped page
*/
-void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count,
+void *scsi_kmap_atomic_sg(struct scatterlist *sgl, int sg_count,
size_t *offset, size_t *len)
{
int i;
size_t sg_len = 0, len_complete = 0;
+ struct scatterlist *sg;
struct page *page;
WARN_ON(!irqs_disabled());
- for (i = 0; i < sg_count; i++) {
+ for_each_sg(sgl, sg, sg_count, i) {
len_complete = sg_len; /* Complete sg-entries */
- sg_len += sg[i].length;
+ sg_len += sg->length;
if (sg_len > *offset)
break;
}
}
/* Offset starting from the beginning of first page in this sg-entry */
- *offset = *offset - len_complete + sg[i].offset;
+ *offset = *offset - len_complete + sg->offset;
/* Assumption: contiguous pages can be accessed as "page + i" */
- page = nth_page(sg[i].page, (*offset >> PAGE_SHIFT));
+ page = nth_page(sg_page(sg), (*offset >> PAGE_SHIFT));
*offset &= ~PAGE_MASK;
/* Bytes in this sg-entry from *offset to the end of the page */
EXPORT_SYMBOL(scsi_kmap_atomic_sg);
/**
- * scsi_kunmap_atomic_sg - atomically unmap a virtual address, previously
- * mapped with scsi_kmap_atomic_sg
+ * scsi_kunmap_atomic_sg - atomically unmap a virtual address, previously mapped with scsi_kmap_atomic_sg
* @virt: virtual address to be unmapped
*/
void scsi_kunmap_atomic_sg(void *virt)