diff options
-rw-r--r-- | include/linux/scatterlist.h | 5 | ||||
-rw-r--r-- | lib/Kconfig | 7 | ||||
-rw-r--r-- | lib/Makefile | 1 | ||||
-rw-r--r-- | lib/sg_split.c | 202 |
4 files changed, 215 insertions, 0 deletions
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 698e906ca730..556ec1ea2574 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h | |||
@@ -247,6 +247,11 @@ struct scatterlist *sg_next(struct scatterlist *); | |||
247 | struct scatterlist *sg_last(struct scatterlist *s, unsigned int); | 247 | struct scatterlist *sg_last(struct scatterlist *s, unsigned int); |
248 | void sg_init_table(struct scatterlist *, unsigned int); | 248 | void sg_init_table(struct scatterlist *, unsigned int); |
249 | void sg_init_one(struct scatterlist *, const void *, unsigned int); | 249 | void sg_init_one(struct scatterlist *, const void *, unsigned int); |
250 | int sg_split(struct scatterlist *in, const int in_mapped_nents, | ||
251 | const off_t skip, const int nb_splits, | ||
252 | const size_t *split_sizes, | ||
253 | struct scatterlist **out, int *out_mapped_nents, | ||
254 | gfp_t gfp_mask); | ||
250 | 255 | ||
251 | typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t); | 256 | typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t); |
252 | typedef void (sg_free_fn)(struct scatterlist *, unsigned int); | 257 | typedef void (sg_free_fn)(struct scatterlist *, unsigned int); |
diff --git a/lib/Kconfig b/lib/Kconfig index 3a2ef67db6c7..dc516164415a 100644 --- a/lib/Kconfig +++ b/lib/Kconfig | |||
@@ -521,6 +521,13 @@ config UCS2_STRING | |||
521 | 521 | ||
522 | source "lib/fonts/Kconfig" | 522 | source "lib/fonts/Kconfig" |
523 | 523 | ||
524 | config SG_SPLIT | ||
525 | def_bool n | ||
526 | help | ||
527 | Provides a heler to split scatterlists into chunks, each chunk being a | ||
528 | scatterlist. This should be selected by a driver or an API which | ||
529 | whishes to split a scatterlist amongst multiple DMA channel. | ||
530 | |||
524 | # | 531 | # |
525 | # sg chaining option | 532 | # sg chaining option |
526 | # | 533 | # |
diff --git a/lib/Makefile b/lib/Makefile index 6897b527581a..2ee6ea2e9b08 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -160,6 +160,7 @@ obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o | |||
160 | 160 | ||
161 | obj-$(CONFIG_GENERIC_NET_UTILS) += net_utils.o | 161 | obj-$(CONFIG_GENERIC_NET_UTILS) += net_utils.o |
162 | 162 | ||
163 | obj-$(CONFIG_SG_SPLIT) += sg_split.o | ||
163 | obj-$(CONFIG_STMP_DEVICE) += stmp_device.o | 164 | obj-$(CONFIG_STMP_DEVICE) += stmp_device.o |
164 | 165 | ||
165 | libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \ | 166 | libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \ |
diff --git a/lib/sg_split.c b/lib/sg_split.c new file mode 100644 index 000000000000..b063410c3593 --- /dev/null +++ b/lib/sg_split.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Robert Jarzmik <robert.jarzmik@free.fr> | ||
3 | * | ||
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. | ||
8 | */ | ||
9 | |||
10 | #include <linux/scatterlist.h> | ||
11 | #include <linux/slab.h> | ||
12 | |||
13 | struct sg_splitter { | ||
14 | struct scatterlist *in_sg0; | ||
15 | int nents; | ||
16 | off_t skip_sg0; | ||
17 | unsigned int length_last_sg; | ||
18 | |||
19 | struct scatterlist *out_sg; | ||
20 | }; | ||
21 | |||
22 | static int sg_calculate_split(struct scatterlist *in, int nents, int nb_splits, | ||
23 | off_t skip, const size_t *sizes, | ||
24 | struct sg_splitter *splitters, bool mapped) | ||
25 | { | ||
26 | int i; | ||
27 | unsigned int sglen; | ||
28 | size_t size = sizes[0], len; | ||
29 | struct sg_splitter *curr = splitters; | ||
30 | struct scatterlist *sg; | ||
31 | |||
32 | for (i = 0; i < nb_splits; i++) { | ||
33 | splitters[i].in_sg0 = NULL; | ||
34 | splitters[i].nents = 0; | ||
35 | } | ||
36 | |||
37 | for_each_sg(in, sg, nents, i) { | ||
38 | sglen = mapped ? sg_dma_len(sg) : sg->length; | ||
39 | if (skip > sglen) { | ||
40 | skip -= sglen; | ||
41 | continue; | ||
42 | } | ||
43 | |||
44 | len = min_t(size_t, size, sglen - skip); | ||
45 | if (!curr->in_sg0) { | ||
46 | curr->in_sg0 = sg; | ||
47 | curr->skip_sg0 = skip; | ||
48 | } | ||
49 | size -= len; | ||
50 | curr->nents++; | ||
51 | curr->length_last_sg = len; | ||
52 | |||
53 | while (!size && (skip + len < sglen) && (--nb_splits > 0)) { | ||
54 | curr++; | ||
55 | size = *(++sizes); | ||
56 | skip += len; | ||
57 | len = min_t(size_t, size, sglen - skip); | ||
58 | |||
59 | curr->in_sg0 = sg; | ||
60 | curr->skip_sg0 = skip; | ||
61 | curr->nents = 1; | ||
62 | curr->length_last_sg = len; | ||
63 | size -= len; | ||
64 | } | ||
65 | skip = 0; | ||
66 | |||
67 | if (!size && --nb_splits > 0) { | ||
68 | curr++; | ||
69 | size = *(++sizes); | ||
70 | } | ||
71 | |||
72 | if (!nb_splits) | ||
73 | break; | ||
74 | } | ||
75 | |||
76 | return (size || !splitters[0].in_sg0) ? -EINVAL : 0; | ||
77 | } | ||
78 | |||
79 | static void sg_split_phys(struct sg_splitter *splitters, const int nb_splits) | ||
80 | { | ||
81 | int i, j; | ||
82 | struct scatterlist *in_sg, *out_sg; | ||
83 | struct sg_splitter *split; | ||
84 | |||
85 | for (i = 0, split = splitters; i < nb_splits; i++, split++) { | ||
86 | in_sg = split->in_sg0; | ||
87 | out_sg = split->out_sg; | ||
88 | for (j = 0; j < split->nents; j++, out_sg++) { | ||
89 | *out_sg = *in_sg; | ||
90 | if (!j) { | ||
91 | out_sg->offset += split->skip_sg0; | ||
92 | out_sg->length -= split->skip_sg0; | ||
93 | } else { | ||
94 | out_sg->offset = 0; | ||
95 | } | ||
96 | sg_dma_address(out_sg) = 0; | ||
97 | sg_dma_len(out_sg) = 0; | ||
98 | in_sg = sg_next(in_sg); | ||
99 | } | ||
100 | out_sg[-1].length = split->length_last_sg; | ||
101 | sg_mark_end(out_sg - 1); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | static void sg_split_mapped(struct sg_splitter *splitters, const int nb_splits) | ||
106 | { | ||
107 | int i, j; | ||
108 | struct scatterlist *in_sg, *out_sg; | ||
109 | struct sg_splitter *split; | ||
110 | |||
111 | for (i = 0, split = splitters; i < nb_splits; i++, split++) { | ||
112 | in_sg = split->in_sg0; | ||
113 | out_sg = split->out_sg; | ||
114 | for (j = 0; j < split->nents; j++, out_sg++) { | ||
115 | sg_dma_address(out_sg) = sg_dma_address(in_sg); | ||
116 | sg_dma_len(out_sg) = sg_dma_len(in_sg); | ||
117 | if (!j) { | ||
118 | sg_dma_address(out_sg) += split->skip_sg0; | ||
119 | sg_dma_len(out_sg) -= split->skip_sg0; | ||
120 | } | ||
121 | in_sg = sg_next(in_sg); | ||
122 | } | ||
123 | sg_dma_len(--out_sg) = split->length_last_sg; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * sg_split - split a scatterlist into several scatterlists | ||
129 | * @in: the input sg list | ||
130 | * @in_mapped_nents: the result of a dma_map_sg(in, ...), or 0 if not mapped. | ||
131 | * @skip: the number of bytes to skip in the input sg list | ||
132 | * @nb_splits: the number of desired sg outputs | ||
133 | * @split_sizes: the respective size of each output sg list in bytes | ||
134 | * @out: an array where to store the allocated output sg lists | ||
135 | * @out_mapped_nents: the resulting sg lists mapped number of sg entries. Might | ||
136 | * be NULL if sglist not already mapped (in_mapped_nents = 0) | ||
137 | * @gfp_mask: the allocation flag | ||
138 | * | ||
139 | * This function splits the input sg list into nb_splits sg lists, which are | ||
140 | * allocated and stored into out. | ||
141 | * The @in is split into : | ||
142 | * - @out[0], which covers bytes [@skip .. @skip + @split_sizes[0] - 1] of @in | ||
143 | * - @out[1], which covers bytes [@skip + split_sizes[0] .. | ||
144 | * @skip + @split_sizes[0] + @split_sizes[1] -1] | ||
145 | * etc ... | ||
146 | * It will be the caller's duty to kfree() out array members. | ||
147 | * | ||
148 | * Returns 0 upon success, or error code | ||
149 | */ | ||
150 | int sg_split(struct scatterlist *in, const int in_mapped_nents, | ||
151 | const off_t skip, const int nb_splits, | ||
152 | const size_t *split_sizes, | ||
153 | struct scatterlist **out, int *out_mapped_nents, | ||
154 | gfp_t gfp_mask) | ||
155 | { | ||
156 | int i, ret; | ||
157 | struct sg_splitter *splitters; | ||
158 | |||
159 | splitters = kcalloc(nb_splits, sizeof(*splitters), gfp_mask); | ||
160 | if (!splitters) | ||
161 | return -ENOMEM; | ||
162 | |||
163 | ret = sg_calculate_split(in, sg_nents(in), nb_splits, skip, split_sizes, | ||
164 | splitters, false); | ||
165 | if (ret < 0) | ||
166 | goto err; | ||
167 | |||
168 | ret = -ENOMEM; | ||
169 | for (i = 0; i < nb_splits; i++) { | ||
170 | splitters[i].out_sg = kmalloc_array(splitters[i].nents, | ||
171 | sizeof(struct scatterlist), | ||
172 | gfp_mask); | ||
173 | if (!splitters[i].out_sg) | ||
174 | goto err; | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * The order of these 3 calls is important and should be kept. | ||
179 | */ | ||
180 | sg_split_phys(splitters, nb_splits); | ||
181 | ret = sg_calculate_split(in, in_mapped_nents, nb_splits, skip, | ||
182 | split_sizes, splitters, true); | ||
183 | if (ret < 0) | ||
184 | goto err; | ||
185 | sg_split_mapped(splitters, nb_splits); | ||
186 | |||
187 | for (i = 0; i < nb_splits; i++) { | ||
188 | out[i] = splitters[i].out_sg; | ||
189 | if (out_mapped_nents) | ||
190 | out_mapped_nents[i] = splitters[i].nents; | ||
191 | } | ||
192 | |||
193 | kfree(splitters); | ||
194 | return 0; | ||
195 | |||
196 | err: | ||
197 | for (i = 0; i < nb_splits; i++) | ||
198 | kfree(splitters[i].out_sg); | ||
199 | kfree(splitters); | ||
200 | return ret; | ||
201 | } | ||
202 | EXPORT_SYMBOL(sg_split); | ||