diff --git a/scst/include/scst.h b/scst/include/scst.h index ad5ad043f..708f46818 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -3401,10 +3401,10 @@ static inline int __scst_get_buf(struct scst_cmd *cmd, int sg_cnt, int res = 0; struct scatterlist *sg = cmd->get_sg_buf_cur_sg_entry; - *buf = NULL; - - if (cmd->get_sg_buf_entry_num >= sg_cnt) + if (cmd->get_sg_buf_entry_num >= sg_cnt) { + *buf = NULL; goto out; + } if (unlikely(sg_is_chain(sg))) sg = sg_chain_ptr(sg); @@ -3490,6 +3490,94 @@ static inline void scst_put_sg_buf(struct scst_cmd *cmd, void *buf, /* Nothing to do */ } +/* + * Functions for access to the commands data (SG) page. Should be used + * instead of direct access. Returns the buffer length for success, 0 for EOD, + * negative error code otherwise. + * + * "Page" argument returns the starting page, "offset" - offset in it. + * + * The "put" function "puts" the buffer. It should be always be used, because + * in future may need to do some additional operations. + */ +static inline int __scst_get_sg_page(struct scst_cmd *cmd, int sg_cnt, + struct page **page, int *offset) +{ + int res = 0; + struct scatterlist *sg = cmd->get_sg_buf_cur_sg_entry; + + if (cmd->get_sg_buf_entry_num >= sg_cnt) { + *page = NULL; + *offset = 0; + goto out; + } + + if (unlikely(sg_is_chain(sg))) + sg = sg_chain_ptr(sg); + + *page = sg_page(sg); + *offset = sg->offset; + res = sg->length; + + cmd->get_sg_buf_entry_num++; + cmd->get_sg_buf_cur_sg_entry = ++sg; + +out: + return res; +} + +static inline int scst_get_sg_page_first(struct scst_cmd *cmd, + struct page **page, int *offset) +{ + if (unlikely(cmd->sg == NULL)) { + *page = NULL; + *offset = 0; + return 0; + } + cmd->get_sg_buf_entry_num = 0; + cmd->get_sg_buf_cur_sg_entry = cmd->sg; + cmd->may_need_dma_sync = 1; + return __scst_get_sg_page(cmd, cmd->sg_cnt, page, offset); +} + +static inline int scst_get_sg_page_next(struct scst_cmd *cmd, + struct page **page, int *offset) +{ + return __scst_get_sg_page(cmd, cmd->sg_cnt, page, offset); +} + +static inline void scst_put_sg_page(struct scst_cmd *cmd, + struct page *page, int offset) +{ + /* Nothing to do */ +} + +static inline int scst_get_out_sg_page_first(struct scst_cmd *cmd, + struct page **page, int *offset) +{ + if (unlikely(cmd->out_sg == NULL)) { + *page = NULL; + *offset = 0; + return 0; + } + cmd->get_sg_buf_entry_num = 0; + cmd->get_sg_buf_cur_sg_entry = cmd->out_sg; + cmd->may_need_dma_sync = 1; + return __scst_get_sg_page(cmd, cmd->out_sg_cnt, page, offset); +} + +static inline int scst_get_out_sg_page_next(struct scst_cmd *cmd, + struct page **page, int *offset) +{ + return __scst_get_sg_page(cmd, cmd->out_sg_cnt, page, offset); +} + +static inline void scst_put_out_sg_page(struct scst_cmd *cmd, + struct page *page, int offset) +{ + /* Nothing to do */ +} + /* * Returns approximate higher rounded buffers count that * scst_get_buf_[first|next]() return. diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 3e61891b0..b01e5bc36 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -2641,8 +2641,8 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr, (struct scst_vdisk_dev *)cmd->dev->dh_priv; struct block_device *bdev = thr->bdev; struct request_queue *q = bdev_get_queue(bdev); - int length, max_nr_vecs = 0; - uint8_t *address; + int length, max_nr_vecs = 0, offset; + struct page *page; struct bio *bio = NULL, *hbio = NULL, *tbio = NULL; int need_new_bio; struct scst_blockio_work *blockio_work; @@ -2667,21 +2667,20 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr, need_new_bio = 1; - length = scst_get_buf_first(cmd, &address); + length = scst_get_sg_page_first(cmd, &page, &offset); while (length > 0) { int len, bytes, off, thislen; - uint8_t *addr; + struct page *pg; u64 lba_start0; - addr = address; - off = offset_in_page(addr); + pg = page; len = length; + off = offset; thislen = 0; lba_start0 = lba_start; while (len > 0) { int rc; - struct page *page = virt_to_page(addr); if (need_new_bio) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) @@ -2725,7 +2724,7 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr, bytes = min_t(unsigned int, len, PAGE_SIZE - off); - rc = bio_add_page(bio, page, bytes, off); + rc = bio_add_page(bio, pg, bytes, off); if (rc < bytes) { sBUG_ON(rc != 0); need_new_bio = 1; @@ -2734,7 +2733,7 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr, continue; } - addr += PAGE_SIZE; + pg++; thislen += bytes; len -= bytes; off = 0; @@ -2742,8 +2741,8 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr, lba_start += length >> virt_dev->block_shift; - scst_put_buf(cmd, address); - length = scst_get_buf_next(cmd, &address); + scst_put_sg_page(cmd, page, offset); + length = scst_get_sg_page_next(cmd, &page, &offset); } /* +1 to prevent erroneous too early command completion */