/* * copy data from device into scatter/gather buffer */ static int fill_from_dev_buffer(struct scsi_cmnd *cmd, const void *buf) { int k, req_len, act_len, len, active; void *kaddr; struct scatterlist *sgpnt; unsigned int buflen; buflen = scsi_bufflen(cmd); if (!buflen) return 0; if (!scsi_sglist(cmd)) return -1; active = 1; req_len = act_len = 0; scsi_for_each_sg(cmd, sgpnt, scsi_sg_count(cmd), k) { if (active) { kaddr = kmap_atomic(sg_page(sgpnt), KM_IRQ0); len = sgpnt->length; if ((req_len + len) > buflen) { active = 0; len = buflen - req_len; } memcpy(kaddr + sgpnt->offset, buf + req_len, len); flush_kernel_dcache_page(sg_page(sgpnt)); kunmap_atomic(kaddr, KM_IRQ0); act_len += len; } req_len += sgpnt->length; } scsi_set_resid(cmd, req_len - act_len); return 0; }
/** * sg_copy_buffer - Copy data between a linear buffer and an SG list * @sgl: The SG list * @nents: Number of SG entries * @buf: Where to copy from * @buflen: The number of bytes to copy * @to_buffer: transfer direction (non zero == from an sg list to a * buffer, 0 == from a buffer to an sg list * * Returns the number of copied bytes. * **/ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, size_t buflen, int to_buffer) { unsigned int offset = 0; struct sg_mapping_iter miter; unsigned long flags; sg_miter_start(&miter, sgl, nents, SG_MITER_ATOMIC); local_irq_save(flags); while (sg_miter_next(&miter) && offset < buflen) { unsigned int len; len = min(miter.length, buflen - offset); if (to_buffer) memcpy(buf + offset, miter.addr, len); else { memcpy(miter.addr, buf + offset, len); flush_kernel_dcache_page(miter.page); } offset += len; } sg_miter_stop(&miter); local_irq_restore(flags); return offset; }
/* * Unmap the current page in abd_miter. * Pass 1 to atmoic if you want to use kmap_atomic. * This can be safely called when the aiter has already exhausted, in which * case this does nothing. */ static void abd_miter_unmap_x(struct abd_miter *aiter, int atomic) { void *paddr; if (!aiter->nents) return; ASSERT(aiter->addr); if (aiter->is_linear) { if (atomic) pagefault_enable(); } else { paddr = aiter->addr - aiter->offset; if (atomic) { if (aiter->rw == ABD_MITER_W) flush_kernel_dcache_page(sg_page(aiter->sg)); zfs_kunmap_atomic(paddr, (aiter->km_type ? KM_USER1 : KM_USER0)); } else { kunmap(sg_page(aiter->sg)); } } aiter->addr = NULL; }
static void at91_mci_post_dma_read(struct at91mci_host *host) { struct mmc_command *cmd; struct mmc_data *data; unsigned int len, i, size; unsigned *dmabuf = host->buffer; pr_debug("post dma read\n"); cmd = host->cmd; if (!cmd) { pr_debug("no command\n"); return; } data = cmd->data; if (!data) { pr_debug("no data\n"); return; } size = data->blksz * data->blocks; len = data->sg_len; at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); for (i = 0; i < len; i++) { struct scatterlist *sg; int amount; unsigned int *sgbuffer; sg = &data->sg[i]; sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; amount = min(size, sg->length); size -= amount; if (cpu_is_at91rm9200()) { int index; for (index = 0; index < (amount / 4); index++) sgbuffer[index] = swab32(*dmabuf++); } else { char *tmpv = (char *)dmabuf; memcpy(sgbuffer, tmpv, amount); tmpv += amount; dmabuf = (unsigned *)tmpv; } flush_kernel_dcache_page(sg_page(sg)); kunmap_atomic(sgbuffer); data->bytes_xfered += amount; if (size == 0) break; } pr_debug("post dma read done\n"); }
/** * sg_copy_buffer - Copy data between a linear buffer and an SG list * @sgl: The SG list * @nents: Number of SG entries * @buf: Where to copy from * @buflen: The number of bytes to copy * @to_buffer: transfer direction (non zero == from an sg list to a * buffer, 0 == from a buffer to an sg list * * Returns the number of copied bytes. * **/ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, size_t buflen, int to_buffer) { struct scatterlist *sg; size_t buf_off = 0; int i; unsigned long flags; local_irq_save(flags); for_each_sg(sgl, sg, nents, i) { struct page *page; int n = 0; unsigned int sg_off = sg->offset; unsigned int sg_copy = sg->length; if (sg_copy > buflen) sg_copy = buflen; buflen -= sg_copy; while (sg_copy > 0) { unsigned int page_copy; void *p; page_copy = PAGE_SIZE - sg_off; if (page_copy > sg_copy) page_copy = sg_copy; page = nth_page(sg_page(sg), n); p = kmap_atomic(page, KM_BIO_SRC_IRQ); if (to_buffer) memcpy(buf + buf_off, p + sg_off, page_copy); else { memcpy(p + sg_off, buf + buf_off, page_copy); flush_kernel_dcache_page(page); } kunmap_atomic(p, KM_BIO_SRC_IRQ); buf_off += page_copy; sg_off += page_copy; if (sg_off == PAGE_SIZE) { sg_off = 0; n++; } sg_copy -= page_copy; } if (!buflen) break; } local_irq_restore(flags); return buf_off; }
/** * sg_copy_buffer - Copy data between a linear buffer and an SG list * @sgl: The SG list * @nents: Number of SG entries * @buf: Where to copy from * @buflen: The number of bytes to copy * @to_buffer: transfer direction (non zero == from an sg list to a * buffer, 0 == from a buffer to an sg list * * Returns the number of copied bytes. * **/ static size_t vtl_sg_copy_user(struct scatterlist *sgl, unsigned int nents, __user void *buf, size_t buflen, int to_buffer) { struct scatterlist *sg; size_t buf_off = 0; int i; int ret; for_each_sg(sgl, sg, nents, i) { struct page *page; int n = 0; unsigned int sg_off = sg->offset; unsigned int sg_copy = sg->length; if (sg_copy > buflen) sg_copy = buflen; buflen -= sg_copy; while (sg_copy > 0) { unsigned int page_copy; void *p; page_copy = PAGE_SIZE - sg_off; if (page_copy > sg_copy) page_copy = sg_copy; page = nth_page(sg_page(sg), n); p = kmap_atomic(page, KM_BIO_SRC_IRQ); if (to_buffer) ret = copy_to_user(buf + buf_off, p + sg_off, page_copy); else { ret = copy_from_user(p + sg_off, buf + buf_off, page_copy); flush_kernel_dcache_page(page); } kunmap_atomic(p, KM_BIO_SRC_IRQ); buf_off += page_copy; sg_off += page_copy; if (sg_off == PAGE_SIZE) { sg_off = 0; n++; } sg_copy -= page_copy; } if (!buflen) break; } return buf_off; }
void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte) { struct page *page = pte_page(pte); if (VALID_PAGE(page) && page->mapping && test_bit(PG_dcache_dirty, &page->flags)) { flush_kernel_dcache_page(page_address(page)); clear_bit(PG_dcache_dirty, &page->flags); } }
/* * Handle after a dma read */ static void nuc970_emmc_post_dma_read(struct nuc970_emmc_host *host) { struct mmc_command *cmd; struct mmc_data *data; unsigned int len, i, size; unsigned *dmabuf = host->buffer; cmd = host->cmd; if (!cmd) { nuc970_emmc_debug("no command\n"); return; } data = cmd->data; if (!data) { nuc970_emmc_debug("no data\n"); return; } size = data->blksz * data->blocks; len = data->sg_len; for (i = 0; i < len; i++) { struct scatterlist *sg; int amount; unsigned int *sgbuffer; sg = &data->sg[i]; sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; amount = min(size, sg->length); size -= amount; { char *tmpv = (char *)dmabuf; memcpy(sgbuffer, tmpv, amount); tmpv += amount; dmabuf = (unsigned *)tmpv; } flush_kernel_dcache_page(sg_page(sg)); kunmap_atomic(sgbuffer); data->bytes_xfered += amount; if (size == 0) break; } }
static size_t vtl_sg_copy_user(struct scatterlist *sgl, unsigned int nents, __user void *buf, size_t buflen, int to_buffer) { unsigned int offset = 0; struct sg_mapping_iter miter; /* Do not use SG_MITER_ATOMIC flag on the sg_miter_start() call */ unsigned int sg_flags = 0; unsigned int rem; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) if (to_buffer) sg_flags |= SG_MITER_FROM_SG; else sg_flags |= SG_MITER_TO_SG; #endif sg_miter_start(&miter, sgl, nents, sg_flags); while (sg_miter_next(&miter) && offset < buflen) { unsigned int len; len = min(miter.length, buflen - offset); if (to_buffer) rem = copy_to_user(buf + offset, miter.addr, len); else { rem = copy_from_user(miter.addr, buf + offset, len); flush_kernel_dcache_page(miter.page); } if (rem) printk(KERN_DEBUG "mhvtl: %s(): " "copy_%s_user() failed, rem %ld, buf 0x%llx, " "miter.addr 0x%llx, len %d\n", __func__, (to_buffer) ? "to" : "from", (long)rem, (long long unsigned int)(buf + offset), (long long unsigned int)miter.addr, len); offset += len; } sg_miter_stop(&miter); return offset; }
static int tbio_transfer(struct request *req, struct tbio_device *dev) { unsigned int i = 0, offset = 0; char *buf; unsigned long flags; size_t size; struct bio_vec *bv; struct req_iterator iter; size = blk_rq_cur_bytes(req); prk_info("bio req of size %zu:", size); offset = blk_rq_pos(req) * 512; rq_for_each_segment(bv, req, iter) { size = bv->bv_len; prk_info("%s bio(%u), segs(%u) sect(%u) pos(%lu) off(%u)", (bio_data_dir(iter.bio) == READ) ? "READ" : "WRITE", i, bio_segments(iter.bio), bio_sectors(iter.bio), iter.bio->bi_sector, offset); if (get_capacity(req->rq_disk) * 512 < offset) { prk_info("Error, small capacity %zu, offset %u", get_capacity(req->rq_disk) * 512, offset); continue; } buf = bvec_kmap_irq(bv, &flags); if (bio_data_dir(iter.bio) == WRITE) memcpy(dev->data + offset, buf, size); else memcpy(buf, dev->data + offset, size); offset += size; flush_kernel_dcache_page(bv->bv_page); bvec_kunmap_irq(buf, &flags); ++i; }
/** * sg_miter_stop - stop mapping iteration * @miter: sg mapping iter to be stopped * * Description: * Stops mapping iterator @miter. @miter should have been started * started using sg_miter_start(). A stopped iteration can be * resumed by calling sg_miter_next() on it. This is useful when * resources (kmap) need to be released during iteration. * * Context: * IRQ disabled if the SG_MITER_ATOMIC is set. Don't care otherwise. */ void sg_miter_stop(struct sg_mapping_iter *miter) { WARN_ON(miter->consumed > miter->length); /* drop resources from the last iteration */ if (miter->addr) { miter->__offset += miter->consumed; if (miter->__flags & SG_MITER_TO_SG) flush_kernel_dcache_page(miter->page); if (miter->__flags & SG_MITER_ATOMIC) { WARN_ON(!irqs_disabled()); kunmap_atomic(miter->addr, KM_BIO_SRC_IRQ); } else kunmap(miter->page); miter->page = NULL; miter->addr = NULL; miter->length = 0; miter->consumed = 0; } }
void sg_miter_stop(struct sg_mapping_iter *miter) { WARN_ON(miter->consumed > miter->length); if (miter->addr) { miter->__offset += miter->consumed; if ((miter->__flags & SG_MITER_TO_SG) && !PageSlab(miter->page)) flush_kernel_dcache_page(miter->page); if (miter->__flags & SG_MITER_ATOMIC) { WARN_ON(!irqs_disabled()); kunmap_atomic(miter->addr); } else kunmap(miter->page); miter->page = NULL; miter->addr = NULL; miter->length = 0; miter->consumed = 0; } }
int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, struct request *rsp) { u8 *req_data = NULL, *resp_data = NULL, *buf; struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); int error = -EINVAL; /* eight is the minimum size for request and response frames */ if (blk_rq_bytes(req) < 8 || blk_rq_bytes(rsp) < 8) goto out; if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE || bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) { shost_printk(KERN_ERR, shost, "SMP request/response frame crosses page boundary"); goto out; } req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL); /* make sure frame can always be built ... we copy * back only the requested length */ resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL); if (!req_data || !resp_data) { error = -ENOMEM; goto out; } local_irq_disable(); buf = kmap_atomic(bio_page(req->bio), KM_USER0) + bio_offset(req->bio); memcpy(req_data, buf, blk_rq_bytes(req)); kunmap_atomic(buf - bio_offset(req->bio), KM_USER0); local_irq_enable(); if (req_data[0] != SMP_REQUEST) goto out; /* always succeeds ... even if we can't process the request * the result is in the response frame */ error = 0; /* set up default don't know response */ resp_data[0] = SMP_RESPONSE; resp_data[1] = req_data[1]; resp_data[2] = SMP_RESP_FUNC_UNK; switch (req_data[1]) { case SMP_REPORT_GENERAL: req->resid_len -= 8; rsp->resid_len -= 32; resp_data[2] = SMP_RESP_FUNC_ACC; resp_data[9] = sas_ha->num_phys; break; case SMP_REPORT_MANUF_INFO: req->resid_len -= 8; rsp->resid_len -= 64; resp_data[2] = SMP_RESP_FUNC_ACC; memcpy(resp_data + 12, shost->hostt->name, SAS_EXPANDER_VENDOR_ID_LEN); memcpy(resp_data + 20, "libsas virt phy", SAS_EXPANDER_PRODUCT_ID_LEN); break; case SMP_READ_GPIO_REG: /* FIXME: need GPIO support in the transport class */ break; case SMP_DISCOVER: req->resid_len -= 16; if ((int)req->resid_len < 0) { req->resid_len = 0; error = -EINVAL; goto out; } rsp->resid_len -= 56; sas_host_smp_discover(sas_ha, resp_data, req_data[9]); break; case SMP_REPORT_PHY_ERR_LOG: /* FIXME: could implement this with additional * libsas callbacks providing the HW supports it */ break; case SMP_REPORT_PHY_SATA: req->resid_len -= 16; if ((int)req->resid_len < 0) { req->resid_len = 0; error = -EINVAL; goto out; } rsp->resid_len -= 60; sas_report_phy_sata(sas_ha, resp_data, req_data[9]); break; case SMP_REPORT_ROUTE_INFO: /* Can't implement; hosts have no routes */ break; case SMP_WRITE_GPIO_REG: /* FIXME: need GPIO support in the transport class */ break; case SMP_CONF_ROUTE_INFO: /* Can't implement; hosts have no routes */ break; case SMP_PHY_CONTROL: req->resid_len -= 44; if ((int)req->resid_len < 0) { req->resid_len = 0; error = -EINVAL; goto out; } rsp->resid_len -= 8; sas_phy_control(sas_ha, req_data[9], req_data[10], req_data[32] >> 4, req_data[33] >> 4, resp_data); break; case SMP_PHY_TEST_FUNCTION: /* FIXME: should this be implemented? */ break; default: /* probably a 2.0 function */ break; } local_irq_disable(); buf = kmap_atomic(bio_page(rsp->bio), KM_USER0) + bio_offset(rsp->bio); memcpy(buf, resp_data, blk_rq_bytes(rsp)); flush_kernel_dcache_page(bio_page(rsp->bio)); kunmap_atomic(buf - bio_offset(rsp->bio), KM_USER0); local_irq_enable(); out: kfree(req_data); kfree(resp_data); return error; }