1 // SPDX-License-Identifier: GPL-2.0-only << 2 /* 1 /* 3 * Copyright (C) 2015 Robert Jarzmik <robert.j 2 * Copyright (C) 2015 Robert Jarzmik <robert.jarzmik@free.fr> 4 * 3 * 5 * Scatterlist splitting helpers. 4 * Scatterlist splitting helpers. >> 5 * >> 6 * This source code is licensed under the GNU General Public License, >> 7 * Version 2. See the file COPYING for more details. 6 */ 8 */ 7 9 8 #include <linux/scatterlist.h> 10 #include <linux/scatterlist.h> 9 #include <linux/slab.h> 11 #include <linux/slab.h> 10 12 11 struct sg_splitter { 13 struct sg_splitter { 12 struct scatterlist *in_sg0; 14 struct scatterlist *in_sg0; 13 int nents; 15 int nents; 14 off_t skip_sg0; 16 off_t skip_sg0; 15 unsigned int length_last_sg; 17 unsigned int length_last_sg; 16 18 17 struct scatterlist *out_sg; 19 struct scatterlist *out_sg; 18 }; 20 }; 19 21 20 static int sg_calculate_split(struct scatterli 22 static int sg_calculate_split(struct scatterlist *in, int nents, int nb_splits, 21 off_t skip, cons 23 off_t skip, const size_t *sizes, 22 struct sg_splitt 24 struct sg_splitter *splitters, bool mapped) 23 { 25 { 24 int i; 26 int i; 25 unsigned int sglen; 27 unsigned int sglen; 26 size_t size = sizes[0], len; 28 size_t size = sizes[0], len; 27 struct sg_splitter *curr = splitters; 29 struct sg_splitter *curr = splitters; 28 struct scatterlist *sg; 30 struct scatterlist *sg; 29 31 30 for (i = 0; i < nb_splits; i++) { 32 for (i = 0; i < nb_splits; i++) { 31 splitters[i].in_sg0 = NULL; 33 splitters[i].in_sg0 = NULL; 32 splitters[i].nents = 0; 34 splitters[i].nents = 0; 33 } 35 } 34 36 35 for_each_sg(in, sg, nents, i) { 37 for_each_sg(in, sg, nents, i) { 36 sglen = mapped ? sg_dma_len(sg 38 sglen = mapped ? sg_dma_len(sg) : sg->length; 37 if (skip > sglen) { 39 if (skip > sglen) { 38 skip -= sglen; 40 skip -= sglen; 39 continue; 41 continue; 40 } 42 } 41 43 42 len = min_t(size_t, size, sgle 44 len = min_t(size_t, size, sglen - skip); 43 if (!curr->in_sg0) { 45 if (!curr->in_sg0) { 44 curr->in_sg0 = sg; 46 curr->in_sg0 = sg; 45 curr->skip_sg0 = skip; 47 curr->skip_sg0 = skip; 46 } 48 } 47 size -= len; 49 size -= len; 48 curr->nents++; 50 curr->nents++; 49 curr->length_last_sg = len; 51 curr->length_last_sg = len; 50 52 51 while (!size && (skip + len < 53 while (!size && (skip + len < sglen) && (--nb_splits > 0)) { 52 curr++; 54 curr++; 53 size = *(++sizes); 55 size = *(++sizes); 54 skip += len; 56 skip += len; 55 len = min_t(size_t, si 57 len = min_t(size_t, size, sglen - skip); 56 58 57 curr->in_sg0 = sg; 59 curr->in_sg0 = sg; 58 curr->skip_sg0 = skip; 60 curr->skip_sg0 = skip; 59 curr->nents = 1; 61 curr->nents = 1; 60 curr->length_last_sg = 62 curr->length_last_sg = len; 61 size -= len; 63 size -= len; 62 } 64 } 63 skip = 0; 65 skip = 0; 64 66 65 if (!size && --nb_splits > 0) 67 if (!size && --nb_splits > 0) { 66 curr++; 68 curr++; 67 size = *(++sizes); 69 size = *(++sizes); 68 } 70 } 69 71 70 if (!nb_splits) 72 if (!nb_splits) 71 break; 73 break; 72 } 74 } 73 75 74 return (size || !splitters[0].in_sg0) 76 return (size || !splitters[0].in_sg0) ? -EINVAL : 0; 75 } 77 } 76 78 77 static void sg_split_phys(struct sg_splitter * 79 static void sg_split_phys(struct sg_splitter *splitters, const int nb_splits) 78 { 80 { 79 int i, j; 81 int i, j; 80 struct scatterlist *in_sg, *out_sg; 82 struct scatterlist *in_sg, *out_sg; 81 struct sg_splitter *split; 83 struct sg_splitter *split; 82 84 83 for (i = 0, split = splitters; i < nb_ 85 for (i = 0, split = splitters; i < nb_splits; i++, split++) { 84 in_sg = split->in_sg0; 86 in_sg = split->in_sg0; 85 out_sg = split->out_sg; 87 out_sg = split->out_sg; 86 for (j = 0; j < split->nents; 88 for (j = 0; j < split->nents; j++, out_sg++) { 87 *out_sg = *in_sg; 89 *out_sg = *in_sg; 88 if (!j) { 90 if (!j) { 89 out_sg->offset 91 out_sg->offset += split->skip_sg0; 90 out_sg->length 92 out_sg->length -= split->skip_sg0; 91 } else { 93 } else { 92 out_sg->offset 94 out_sg->offset = 0; 93 } 95 } 94 sg_dma_address(out_sg) 96 sg_dma_address(out_sg) = 0; 95 sg_dma_len(out_sg) = 0 97 sg_dma_len(out_sg) = 0; 96 in_sg = sg_next(in_sg) 98 in_sg = sg_next(in_sg); 97 } 99 } 98 out_sg[-1].length = split->len 100 out_sg[-1].length = split->length_last_sg; 99 sg_mark_end(out_sg - 1); 101 sg_mark_end(out_sg - 1); 100 } 102 } 101 } 103 } 102 104 103 static void sg_split_mapped(struct sg_splitter 105 static void sg_split_mapped(struct sg_splitter *splitters, const int nb_splits) 104 { 106 { 105 int i, j; 107 int i, j; 106 struct scatterlist *in_sg, *out_sg; 108 struct scatterlist *in_sg, *out_sg; 107 struct sg_splitter *split; 109 struct sg_splitter *split; 108 110 109 for (i = 0, split = splitters; i < nb_ 111 for (i = 0, split = splitters; i < nb_splits; i++, split++) { 110 in_sg = split->in_sg0; 112 in_sg = split->in_sg0; 111 out_sg = split->out_sg; 113 out_sg = split->out_sg; 112 for (j = 0; j < split->nents; 114 for (j = 0; j < split->nents; j++, out_sg++) { 113 sg_dma_address(out_sg) 115 sg_dma_address(out_sg) = sg_dma_address(in_sg); 114 sg_dma_len(out_sg) = s 116 sg_dma_len(out_sg) = sg_dma_len(in_sg); 115 if (!j) { 117 if (!j) { 116 sg_dma_address 118 sg_dma_address(out_sg) += split->skip_sg0; 117 sg_dma_len(out 119 sg_dma_len(out_sg) -= split->skip_sg0; 118 } 120 } 119 in_sg = sg_next(in_sg) 121 in_sg = sg_next(in_sg); 120 } 122 } 121 sg_dma_len(--out_sg) = split-> 123 sg_dma_len(--out_sg) = split->length_last_sg; 122 } 124 } 123 } 125 } 124 126 125 /** 127 /** 126 * sg_split - split a scatterlist into several 128 * sg_split - split a scatterlist into several scatterlists 127 * @in: the input sg list 129 * @in: the input sg list 128 * @in_mapped_nents: the result of a dma_map_s 130 * @in_mapped_nents: the result of a dma_map_sg(in, ...), or 0 if not mapped. 129 * @skip: the number of bytes to skip in the i 131 * @skip: the number of bytes to skip in the input sg list 130 * @nb_splits: the number of desired sg output 132 * @nb_splits: the number of desired sg outputs 131 * @split_sizes: the respective size of each o 133 * @split_sizes: the respective size of each output sg list in bytes 132 * @out: an array where to store the allocated 134 * @out: an array where to store the allocated output sg lists 133 * @out_mapped_nents: the resulting sg lists m 135 * @out_mapped_nents: the resulting sg lists mapped number of sg entries. Might 134 * be NULL if sglist not al 136 * be NULL if sglist not already mapped (in_mapped_nents = 0) 135 * @gfp_mask: the allocation flag 137 * @gfp_mask: the allocation flag 136 * 138 * 137 * This function splits the input sg list into 139 * This function splits the input sg list into nb_splits sg lists, which are 138 * allocated and stored into out. 140 * allocated and stored into out. 139 * The @in is split into : 141 * The @in is split into : 140 * - @out[0], which covers bytes [@skip .. @s 142 * - @out[0], which covers bytes [@skip .. @skip + @split_sizes[0] - 1] of @in 141 * - @out[1], which covers bytes [@skip + spl 143 * - @out[1], which covers bytes [@skip + split_sizes[0] .. 142 * @skip + @sp 144 * @skip + @split_sizes[0] + @split_sizes[1] -1] 143 * etc ... 145 * etc ... 144 * It will be the caller's duty to kfree() out 146 * It will be the caller's duty to kfree() out array members. 145 * 147 * 146 * Returns 0 upon success, or error code 148 * Returns 0 upon success, or error code 147 */ 149 */ 148 int sg_split(struct scatterlist *in, const int 150 int sg_split(struct scatterlist *in, const int in_mapped_nents, 149 const off_t skip, const int nb_sp 151 const off_t skip, const int nb_splits, 150 const size_t *split_sizes, 152 const size_t *split_sizes, 151 struct scatterlist **out, int *ou 153 struct scatterlist **out, int *out_mapped_nents, 152 gfp_t gfp_mask) 154 gfp_t gfp_mask) 153 { 155 { 154 int i, ret; 156 int i, ret; 155 struct sg_splitter *splitters; 157 struct sg_splitter *splitters; 156 158 157 splitters = kcalloc(nb_splits, sizeof( 159 splitters = kcalloc(nb_splits, sizeof(*splitters), gfp_mask); 158 if (!splitters) 160 if (!splitters) 159 return -ENOMEM; 161 return -ENOMEM; 160 162 161 ret = sg_calculate_split(in, sg_nents( 163 ret = sg_calculate_split(in, sg_nents(in), nb_splits, skip, split_sizes, 162 splitters, false); 164 splitters, false); 163 if (ret < 0) 165 if (ret < 0) 164 goto err; 166 goto err; 165 167 166 ret = -ENOMEM; 168 ret = -ENOMEM; 167 for (i = 0; i < nb_splits; i++) { 169 for (i = 0; i < nb_splits; i++) { 168 splitters[i].out_sg = kmalloc_ 170 splitters[i].out_sg = kmalloc_array(splitters[i].nents, 169 171 sizeof(struct scatterlist), 170 172 gfp_mask); 171 if (!splitters[i].out_sg) 173 if (!splitters[i].out_sg) 172 goto err; 174 goto err; 173 } 175 } 174 176 175 /* 177 /* 176 * The order of these 3 calls is impor 178 * The order of these 3 calls is important and should be kept. 177 */ 179 */ 178 sg_split_phys(splitters, nb_splits); 180 sg_split_phys(splitters, nb_splits); 179 if (in_mapped_nents) { !! 181 ret = sg_calculate_split(in, in_mapped_nents, nb_splits, skip, 180 ret = sg_calculate_split(in, i !! 182 split_sizes, splitters, true); 181 split !! 183 if (ret < 0) 182 if (ret < 0) !! 184 goto err; 183 goto err; !! 185 sg_split_mapped(splitters, nb_splits); 184 sg_split_mapped(splitters, nb_ << 185 } << 186 186 187 for (i = 0; i < nb_splits; i++) { 187 for (i = 0; i < nb_splits; i++) { 188 out[i] = splitters[i].out_sg; 188 out[i] = splitters[i].out_sg; 189 if (out_mapped_nents) 189 if (out_mapped_nents) 190 out_mapped_nents[i] = 190 out_mapped_nents[i] = splitters[i].nents; 191 } 191 } 192 192 193 kfree(splitters); 193 kfree(splitters); 194 return 0; 194 return 0; 195 195 196 err: 196 err: 197 for (i = 0; i < nb_splits; i++) 197 for (i = 0; i < nb_splits; i++) 198 kfree(splitters[i].out_sg); 198 kfree(splitters[i].out_sg); 199 kfree(splitters); 199 kfree(splitters); 200 return ret; 200 return ret; 201 } 201 } 202 EXPORT_SYMBOL(sg_split); 202 EXPORT_SYMBOL(sg_split); 203 203
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.