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