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

mmc: sdio: Use multiple scatter/gather list

Before this patch, we always used only single sg entry for SDIO transfer.
This patch switches to using multiple sg entries. In the case of dwmci,
it supports only up to 4KB size per single sg entry. So if we want to
transfer more than 4KB, we should send more than 1 command.

When we tested before applying this patch, it took around 335 us for
5K(5120) bytes transfer with dwmci controller. After applying this patch,
it takes 242 us for 5K bytes. So this patch makes around 38% performance
improvement for 5K bytes transfer. If the transfer size is bigger, then
the performance improvement ratio will be increased.

Signed-off-by: Kyoungil Kim <ki0351.kim@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>

authored by

Kyoungil Kim and committed by
Chris Ball
968a64ea 8fee476b

+31 -11
+3 -7
drivers/mmc/core/sdio_io.c
··· 188 188 */ 189 189 static inline unsigned int sdio_max_byte_size(struct sdio_func *func) 190 190 { 191 - unsigned mval = min(func->card->host->max_seg_size, 192 - func->card->host->max_blk_size); 191 + unsigned mval = func->card->host->max_blk_size; 193 192 194 193 if (mmc_blksz_for_byte_mode(func->card)) 195 194 mval = min(mval, func->cur_blksize); ··· 310 311 /* Do the bulk of the transfer using block mode (if supported). */ 311 312 if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) { 312 313 /* Blocks per command is limited by host count, host transfer 313 - * size (we only use a single sg entry) and the maximum for 314 - * IO_RW_EXTENDED of 511 blocks. */ 315 - max_blocks = min(func->card->host->max_blk_count, 316 - func->card->host->max_seg_size / func->cur_blksize); 317 - max_blocks = min(max_blocks, 511u); 314 + * size and the maximum for IO_RW_EXTENDED of 511 blocks. */ 315 + max_blocks = min(func->card->host->max_blk_count, 511u); 318 316 319 317 while (remainder >= func->cur_blksize) { 320 318 unsigned blocks;
+28 -4
drivers/mmc/core/sdio_ops.c
··· 124 124 struct mmc_request mrq = {NULL}; 125 125 struct mmc_command cmd = {0}; 126 126 struct mmc_data data = {0}; 127 - struct scatterlist sg; 127 + struct scatterlist sg, *sg_ptr; 128 + struct sg_table sgtable; 129 + unsigned int nents, left_size, i; 130 + unsigned int seg_size = card->host->max_seg_size; 128 131 129 132 BUG_ON(!card); 130 133 BUG_ON(fn > 7); ··· 155 152 /* Code in host drivers/fwk assumes that "blocks" always is >=1 */ 156 153 data.blocks = blocks ? blocks : 1; 157 154 data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; 158 - data.sg = &sg; 159 - data.sg_len = 1; 160 155 161 - sg_init_one(&sg, buf, data.blksz * data.blocks); 156 + left_size = data.blksz * data.blocks; 157 + nents = (left_size - 1) / seg_size + 1; 158 + if (nents > 1) { 159 + if (sg_alloc_table(&sgtable, nents, GFP_KERNEL)) 160 + return -ENOMEM; 161 + 162 + data.sg = sgtable.sgl; 163 + data.sg_len = nents; 164 + 165 + for_each_sg(data.sg, sg_ptr, data.sg_len, i) { 166 + sg_set_page(sg_ptr, virt_to_page(buf + (i * seg_size)), 167 + min(seg_size, left_size), 168 + offset_in_page(buf + (i * seg_size))); 169 + left_size = left_size - seg_size; 170 + } 171 + } else { 172 + data.sg = &sg; 173 + data.sg_len = 1; 174 + 175 + sg_init_one(&sg, buf, left_size); 176 + } 162 177 163 178 mmc_set_data_timeout(&data, card); 164 179 165 180 mmc_wait_for_req(card->host, &mrq); 181 + 182 + if (nents > 1) 183 + sg_free_table(&sgtable); 166 184 167 185 if (cmd.error) 168 186 return cmd.error;