dmaengine: at_hdmac: Start transfer for cyclic channels in issue_pending
[platform/kernel/linux-rpi.git] / drivers / md / dm-unstripe.c
1 /*
2  * Copyright (C) 2017 Intel Corporation.
3  *
4  * This file is released under the GPL.
5  */
6
7 #include "dm.h"
8
9 #include <linux/module.h>
10
11 struct unstripe_c {
12         struct dm_dev *dev;
13         sector_t physical_start;
14
15         uint32_t stripes;
16
17         uint32_t unstripe;
18         sector_t unstripe_width;
19         sector_t unstripe_offset;
20
21         uint32_t chunk_size;
22         u8 chunk_shift;
23 };
24
25 #define DM_MSG_PREFIX "unstriped"
26
27 static void cleanup_unstripe(struct unstripe_c *uc, struct dm_target *ti)
28 {
29         if (uc->dev)
30                 dm_put_device(ti, uc->dev);
31         kfree(uc);
32 }
33
34 /*
35  * Contruct an unstriped mapping.
36  * <number of stripes> <chunk size> <stripe #> <dev_path> <offset>
37  */
38 static int unstripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
39 {
40         struct unstripe_c *uc;
41         sector_t tmp_len;
42         unsigned long long start;
43         char dummy;
44
45         if (argc != 5) {
46                 ti->error = "Invalid number of arguments";
47                 return -EINVAL;
48         }
49
50         uc = kzalloc(sizeof(*uc), GFP_KERNEL);
51         if (!uc) {
52                 ti->error = "Memory allocation for unstriped context failed";
53                 return -ENOMEM;
54         }
55
56         if (kstrtouint(argv[0], 10, &uc->stripes) || !uc->stripes) {
57                 ti->error = "Invalid stripe count";
58                 goto err;
59         }
60
61         if (kstrtouint(argv[1], 10, &uc->chunk_size) || !uc->chunk_size) {
62                 ti->error = "Invalid chunk_size";
63                 goto err;
64         }
65
66         if (kstrtouint(argv[2], 10, &uc->unstripe)) {
67                 ti->error = "Invalid stripe number";
68                 goto err;
69         }
70
71         if (uc->unstripe > uc->stripes && uc->stripes > 1) {
72                 ti->error = "Please provide stripe between [0, # of stripes]";
73                 goto err;
74         }
75
76         if (dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), &uc->dev)) {
77                 ti->error = "Couldn't get striped device";
78                 goto err;
79         }
80
81         if (sscanf(argv[4], "%llu%c", &start, &dummy) != 1 || start != (sector_t)start) {
82                 ti->error = "Invalid striped device offset";
83                 goto err;
84         }
85         uc->physical_start = start;
86
87         uc->unstripe_offset = uc->unstripe * uc->chunk_size;
88         uc->unstripe_width = (uc->stripes - 1) * uc->chunk_size;
89         uc->chunk_shift = is_power_of_2(uc->chunk_size) ? fls(uc->chunk_size) - 1 : 0;
90
91         tmp_len = ti->len;
92         if (sector_div(tmp_len, uc->chunk_size)) {
93                 ti->error = "Target length not divisible by chunk size";
94                 goto err;
95         }
96
97         if (dm_set_target_max_io_len(ti, uc->chunk_size)) {
98                 ti->error = "Failed to set max io len";
99                 goto err;
100         }
101
102         ti->private = uc;
103         return 0;
104 err:
105         cleanup_unstripe(uc, ti);
106         return -EINVAL;
107 }
108
109 static void unstripe_dtr(struct dm_target *ti)
110 {
111         struct unstripe_c *uc = ti->private;
112
113         cleanup_unstripe(uc, ti);
114 }
115
116 static sector_t map_to_core(struct dm_target *ti, struct bio *bio)
117 {
118         struct unstripe_c *uc = ti->private;
119         sector_t sector = bio->bi_iter.bi_sector;
120         sector_t tmp_sector = sector;
121
122         /* Shift us up to the right "row" on the stripe */
123         if (uc->chunk_shift)
124                 tmp_sector >>= uc->chunk_shift;
125         else
126                 sector_div(tmp_sector, uc->chunk_size);
127
128         sector += uc->unstripe_width * tmp_sector;
129
130         /* Account for what stripe we're operating on */
131         return sector + uc->unstripe_offset;
132 }
133
134 static int unstripe_map(struct dm_target *ti, struct bio *bio)
135 {
136         struct unstripe_c *uc = ti->private;
137
138         bio_set_dev(bio, uc->dev->bdev);
139         bio->bi_iter.bi_sector = map_to_core(ti, bio) + uc->physical_start;
140
141         return DM_MAPIO_REMAPPED;
142 }
143
144 static void unstripe_status(struct dm_target *ti, status_type_t type,
145                             unsigned int status_flags, char *result, unsigned int maxlen)
146 {
147         struct unstripe_c *uc = ti->private;
148         unsigned int sz = 0;
149
150         switch (type) {
151         case STATUSTYPE_INFO:
152                 break;
153
154         case STATUSTYPE_TABLE:
155                 DMEMIT("%d %llu %d %s %llu",
156                        uc->stripes, (unsigned long long)uc->chunk_size, uc->unstripe,
157                        uc->dev->name, (unsigned long long)uc->physical_start);
158                 break;
159
160         case STATUSTYPE_IMA:
161                 *result = '\0';
162                 break;
163         }
164 }
165
166 static int unstripe_iterate_devices(struct dm_target *ti,
167                                     iterate_devices_callout_fn fn, void *data)
168 {
169         struct unstripe_c *uc = ti->private;
170
171         return fn(ti, uc->dev, uc->physical_start, ti->len, data);
172 }
173
174 static void unstripe_io_hints(struct dm_target *ti,
175                                struct queue_limits *limits)
176 {
177         struct unstripe_c *uc = ti->private;
178
179         limits->chunk_sectors = uc->chunk_size;
180 }
181
182 static struct target_type unstripe_target = {
183         .name = "unstriped",
184         .version = {1, 1, 0},
185         .features = DM_TARGET_NOWAIT,
186         .module = THIS_MODULE,
187         .ctr = unstripe_ctr,
188         .dtr = unstripe_dtr,
189         .map = unstripe_map,
190         .status = unstripe_status,
191         .iterate_devices = unstripe_iterate_devices,
192         .io_hints = unstripe_io_hints,
193 };
194
195 static int __init dm_unstripe_init(void)
196 {
197         return dm_register_target(&unstripe_target);
198 }
199
200 static void __exit dm_unstripe_exit(void)
201 {
202         dm_unregister_target(&unstripe_target);
203 }
204
205 module_init(dm_unstripe_init);
206 module_exit(dm_unstripe_exit);
207
208 MODULE_DESCRIPTION(DM_NAME " unstriped target");
209 MODULE_ALIAS("dm-unstriped");
210 MODULE_AUTHOR("Scott Bauer <scott.bauer@intel.com>");
211 MODULE_LICENSE("GPL");