Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: storage: use sg_miter_* APIs to access scsi buffer

We have sg_miter_* APIs for accessing scsi sg buffer, so
use them to make code clean and bug free.

Cc: Matthew Dharm <mdharm-usb@one-eyed-alien.net>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Ming Lei and committed by
Greg Kroah-Hartman
e5fc70d5 0d6077f8

+24 -52
+24 -52
drivers/usb/storage/protocol.c
··· 135 135 unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr, 136 136 unsigned int *offset, enum xfer_buf_dir dir) 137 137 { 138 - unsigned int cnt; 138 + unsigned int cnt = 0; 139 139 struct scatterlist *sg = *sgptr; 140 + struct sg_mapping_iter miter; 141 + unsigned int nents = scsi_sg_count(srb); 140 142 141 - /* We have to go through the list one entry 142 - * at a time. Each s-g entry contains some number of pages, and 143 - * each page has to be kmap()'ed separately. If the page is already 144 - * in kernel-addressable memory then kmap() will return its address. 145 - * If the page is not directly accessible -- such as a user buffer 146 - * located in high memory -- then kmap() will map it to a temporary 147 - * position in the kernel's virtual address space. 148 - */ 149 - 150 - if (!sg) 143 + if (sg) 144 + nents = sg_nents(sg); 145 + else 151 146 sg = scsi_sglist(srb); 152 147 153 - /* This loop handles a single s-g list entry, which may 154 - * include multiple pages. Find the initial page structure 155 - * and the starting offset within the page, and update 156 - * the *offset and **sgptr values for the next loop. 157 - */ 158 - cnt = 0; 159 - while (cnt < buflen && sg) { 160 - struct page *page = sg_page(sg) + 161 - ((sg->offset + *offset) >> PAGE_SHIFT); 162 - unsigned int poff = (sg->offset + *offset) & (PAGE_SIZE-1); 163 - unsigned int sglen = sg->length - *offset; 148 + sg_miter_start(&miter, sg, nents, dir == FROM_XFER_BUF ? 149 + SG_MITER_FROM_SG: SG_MITER_TO_SG); 164 150 165 - if (sglen > buflen - cnt) { 151 + if (!sg_miter_skip(&miter, *offset)) 152 + return cnt; 166 153 167 - /* Transfer ends within this s-g entry */ 168 - sglen = buflen - cnt; 169 - *offset += sglen; 154 + while (sg_miter_next(&miter) && cnt < buflen) { 155 + unsigned int len = min(miter.length, buflen - cnt); 156 + 157 + if (dir == FROM_XFER_BUF) 158 + memcpy(buffer + cnt, miter.addr, len); 159 + else 160 + memcpy(miter.addr, buffer + cnt, len); 161 + 162 + if (*offset + len < miter.piter.sg->length) { 163 + *offset += len; 164 + *sgptr = miter.piter.sg; 170 165 } else { 171 - 172 - /* Transfer continues to next s-g entry */ 173 166 *offset = 0; 174 - sg = sg_next(sg); 167 + *sgptr = sg_next(miter.piter.sg); 175 168 } 176 - 177 - /* Transfer the data for all the pages in this 178 - * s-g entry. For each page: call kmap(), do the 179 - * transfer, and call kunmap() immediately after. */ 180 - while (sglen > 0) { 181 - unsigned int plen = min(sglen, (unsigned int) 182 - PAGE_SIZE - poff); 183 - unsigned char *ptr = kmap(page); 184 - 185 - if (dir == TO_XFER_BUF) 186 - memcpy(ptr + poff, buffer + cnt, plen); 187 - else 188 - memcpy(buffer + cnt, ptr + poff, plen); 189 - kunmap(page); 190 - 191 - /* Start at the beginning of the next page */ 192 - poff = 0; 193 - ++page; 194 - cnt += plen; 195 - sglen -= plen; 196 - } 169 + cnt += len; 197 170 } 198 - *sgptr = sg; 171 + sg_miter_stop(&miter); 199 172 200 - /* Return the amount actually transferred */ 201 173 return cnt; 202 174 } 203 175 EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf);