0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/scatterlist.h>
0009 #include <linux/slab.h>
0010
0011 struct sg_splitter {
0012 struct scatterlist *in_sg0;
0013 int nents;
0014 off_t skip_sg0;
0015 unsigned int length_last_sg;
0016
0017 struct scatterlist *out_sg;
0018 };
0019
0020 static int sg_calculate_split(struct scatterlist *in, int nents, int nb_splits,
0021 off_t skip, const size_t *sizes,
0022 struct sg_splitter *splitters, bool mapped)
0023 {
0024 int i;
0025 unsigned int sglen;
0026 size_t size = sizes[0], len;
0027 struct sg_splitter *curr = splitters;
0028 struct scatterlist *sg;
0029
0030 for (i = 0; i < nb_splits; i++) {
0031 splitters[i].in_sg0 = NULL;
0032 splitters[i].nents = 0;
0033 }
0034
0035 for_each_sg(in, sg, nents, i) {
0036 sglen = mapped ? sg_dma_len(sg) : sg->length;
0037 if (skip > sglen) {
0038 skip -= sglen;
0039 continue;
0040 }
0041
0042 len = min_t(size_t, size, sglen - skip);
0043 if (!curr->in_sg0) {
0044 curr->in_sg0 = sg;
0045 curr->skip_sg0 = skip;
0046 }
0047 size -= len;
0048 curr->nents++;
0049 curr->length_last_sg = len;
0050
0051 while (!size && (skip + len < sglen) && (--nb_splits > 0)) {
0052 curr++;
0053 size = *(++sizes);
0054 skip += len;
0055 len = min_t(size_t, size, sglen - skip);
0056
0057 curr->in_sg0 = sg;
0058 curr->skip_sg0 = skip;
0059 curr->nents = 1;
0060 curr->length_last_sg = len;
0061 size -= len;
0062 }
0063 skip = 0;
0064
0065 if (!size && --nb_splits > 0) {
0066 curr++;
0067 size = *(++sizes);
0068 }
0069
0070 if (!nb_splits)
0071 break;
0072 }
0073
0074 return (size || !splitters[0].in_sg0) ? -EINVAL : 0;
0075 }
0076
0077 static void sg_split_phys(struct sg_splitter *splitters, const int nb_splits)
0078 {
0079 int i, j;
0080 struct scatterlist *in_sg, *out_sg;
0081 struct sg_splitter *split;
0082
0083 for (i = 0, split = splitters; i < nb_splits; i++, split++) {
0084 in_sg = split->in_sg0;
0085 out_sg = split->out_sg;
0086 for (j = 0; j < split->nents; j++, out_sg++) {
0087 *out_sg = *in_sg;
0088 if (!j) {
0089 out_sg->offset += split->skip_sg0;
0090 out_sg->length -= split->skip_sg0;
0091 } else {
0092 out_sg->offset = 0;
0093 }
0094 sg_dma_address(out_sg) = 0;
0095 sg_dma_len(out_sg) = 0;
0096 in_sg = sg_next(in_sg);
0097 }
0098 out_sg[-1].length = split->length_last_sg;
0099 sg_mark_end(out_sg - 1);
0100 }
0101 }
0102
0103 static void sg_split_mapped(struct sg_splitter *splitters, const int nb_splits)
0104 {
0105 int i, j;
0106 struct scatterlist *in_sg, *out_sg;
0107 struct sg_splitter *split;
0108
0109 for (i = 0, split = splitters; i < nb_splits; i++, split++) {
0110 in_sg = split->in_sg0;
0111 out_sg = split->out_sg;
0112 for (j = 0; j < split->nents; j++, out_sg++) {
0113 sg_dma_address(out_sg) = sg_dma_address(in_sg);
0114 sg_dma_len(out_sg) = sg_dma_len(in_sg);
0115 if (!j) {
0116 sg_dma_address(out_sg) += split->skip_sg0;
0117 sg_dma_len(out_sg) -= split->skip_sg0;
0118 }
0119 in_sg = sg_next(in_sg);
0120 }
0121 sg_dma_len(--out_sg) = split->length_last_sg;
0122 }
0123 }
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148 int sg_split(struct scatterlist *in, const int in_mapped_nents,
0149 const off_t skip, const int nb_splits,
0150 const size_t *split_sizes,
0151 struct scatterlist **out, int *out_mapped_nents,
0152 gfp_t gfp_mask)
0153 {
0154 int i, ret;
0155 struct sg_splitter *splitters;
0156
0157 splitters = kcalloc(nb_splits, sizeof(*splitters), gfp_mask);
0158 if (!splitters)
0159 return -ENOMEM;
0160
0161 ret = sg_calculate_split(in, sg_nents(in), nb_splits, skip, split_sizes,
0162 splitters, false);
0163 if (ret < 0)
0164 goto err;
0165
0166 ret = -ENOMEM;
0167 for (i = 0; i < nb_splits; i++) {
0168 splitters[i].out_sg = kmalloc_array(splitters[i].nents,
0169 sizeof(struct scatterlist),
0170 gfp_mask);
0171 if (!splitters[i].out_sg)
0172 goto err;
0173 }
0174
0175
0176
0177
0178 sg_split_phys(splitters, nb_splits);
0179 if (in_mapped_nents) {
0180 ret = sg_calculate_split(in, in_mapped_nents, nb_splits, skip,
0181 split_sizes, splitters, true);
0182 if (ret < 0)
0183 goto err;
0184 sg_split_mapped(splitters, nb_splits);
0185 }
0186
0187 for (i = 0; i < nb_splits; i++) {
0188 out[i] = splitters[i].out_sg;
0189 if (out_mapped_nents)
0190 out_mapped_nents[i] = splitters[i].nents;
0191 }
0192
0193 kfree(splitters);
0194 return 0;
0195
0196 err:
0197 for (i = 0; i < nb_splits; i++)
0198 kfree(splitters[i].out_sg);
0199 kfree(splitters);
0200 return ret;
0201 }
0202 EXPORT_SYMBOL(sg_split);