fc45cd27d8d3ddc2c350ad590629eef71c74576e
[fio.git] / dataplacement.c
1 /*
2  * Note: This is similar to a very basic setup
3  * of ZBD devices
4  *
5  * Specify fdp=1 (With char devices /dev/ng0n1)
6  */
7
8 #include <errno.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include "fio.h"
13 #include "file.h"
14
15 #include "pshared.h"
16 #include "dataplacement.h"
17
18 static int fdp_ruh_info(struct thread_data *td, struct fio_file *f,
19                         struct fio_ruhs_info *ruhs)
20 {
21         int ret = -EINVAL;
22
23         if (!td->io_ops) {
24                 log_err("fio: no ops set in fdp init?!\n");
25                 return ret;
26         }
27
28         if (td->io_ops->fdp_fetch_ruhs) {
29                 ret = td->io_ops->fdp_fetch_ruhs(td, f, ruhs);
30                 if (ret < 0) {
31                         td_verror(td, errno, "fdp fetch ruhs failed");
32                         log_err("%s: fdp fetch ruhs failed (%d)\n",
33                                 f->file_name, errno);
34                 }
35         } else {
36                 log_err("%s: engine (%s) lacks fetch ruhs\n",
37                         f->file_name, td->io_ops->name);
38         }
39
40         return ret;
41 }
42
43 static int init_ruh_info(struct thread_data *td, struct fio_file *f)
44 {
45         struct fio_ruhs_info *ruhs, *tmp;
46         uint32_t nr_ruhs;
47         int i, ret;
48
49         /* set up the data structure used for FDP to work with the supplied stream IDs */
50         if (td->o.dp_type == FIO_DP_STREAMS) {
51                 if (!td->o.dp_nr_ids) {
52                         log_err("fio: stream IDs must be provided for dataplacement=streams\n");
53                         return -EINVAL;
54                 }
55                 ruhs = scalloc(1, sizeof(*ruhs) + td->o.dp_nr_ids * sizeof(*ruhs->plis));
56                 if (!ruhs)
57                         return -ENOMEM;
58
59                 ruhs->nr_ruhs = td->o.dp_nr_ids;
60                 for (int i = 0; i < ruhs->nr_ruhs; i++)
61                         ruhs->plis[i] = td->o.dp_ids[i];
62
63                 f->ruhs_info = ruhs;
64                 return 0;
65         }
66
67         /*
68          * Since we don't know the actual number of ruhs. Only fetch the header.
69          * We will reallocate this buffer and then fetch all the ruhs again.
70          */
71         ruhs = calloc(1, sizeof(*ruhs));
72         ret = fdp_ruh_info(td, f, ruhs);
73         if (ret) {
74                 log_err("fio: ruh info failed for %s (%d)\n",
75                         f->file_name, -ret);
76                 goto out;
77         }
78
79         nr_ruhs = ruhs->nr_ruhs;
80         ruhs = realloc(ruhs, sizeof(*ruhs) + nr_ruhs * sizeof(*ruhs->plis));
81         if (!ruhs) {
82                 log_err("fio: ruhs buffer realloc failed for %s\n",
83                         f->file_name);
84                 ret = -ENOMEM;
85                 goto out;
86         }
87
88         ruhs->nr_ruhs = nr_ruhs;
89         ret = fdp_ruh_info(td, f, ruhs);
90         if (ret) {
91                 log_err("fio: ruh info failed for %s (%d)\n",
92                         f->file_name, -ret);
93                 goto out;
94         }
95
96         if (td->o.dp_nr_ids == 0) {
97                 if (ruhs->nr_ruhs > FIO_MAX_DP_IDS)
98                         ruhs->nr_ruhs = FIO_MAX_DP_IDS;
99         } else {
100                 for (i = 0; i < td->o.dp_nr_ids; i++) {
101                         if (td->o.dp_ids[i] >= ruhs->nr_ruhs) {
102                                 log_err("fio: for %s PID index %d must be smaller than %d\n",
103                                         f->file_name, td->o.dp_ids[i],
104                                         ruhs->nr_ruhs);
105                                 ret = -EINVAL;
106                                 goto out;
107                         }
108                 }
109                 ruhs->nr_ruhs = td->o.dp_nr_ids;
110         }
111
112         tmp = scalloc(1, sizeof(*tmp) + ruhs->nr_ruhs * sizeof(*tmp->plis));
113         if (!tmp) {
114                 ret = -ENOMEM;
115                 goto out;
116         }
117
118         if (td->o.dp_nr_ids == 0) {
119                 for (i = 0; i < ruhs->nr_ruhs; i++)
120                         tmp->plis[i] = ruhs->plis[i];
121
122                 tmp->nr_ruhs = ruhs->nr_ruhs;
123                 f->ruhs_info = tmp;
124                 free(ruhs);
125
126                 return 0;
127         }
128
129         tmp->nr_ruhs = td->o.dp_nr_ids;
130         for (i = 0; i < td->o.dp_nr_ids; i++)
131                 tmp->plis[i] = ruhs->plis[td->o.dp_ids[i]];
132         f->ruhs_info = tmp;
133 out:
134         free(ruhs);
135         return ret;
136 }
137
138 static int init_ruh_scheme(struct thread_data *td, struct fio_file *f)
139 {
140         struct fio_ruhs_scheme *ruh_scheme;
141         FILE *scheme_fp;
142         unsigned long long start, end;
143         uint16_t pli;
144         int ret = 0;
145
146         if (td->o.dp_id_select != FIO_DP_SCHEME)
147                 return 0;
148
149         /* Get the scheme from the file */
150         scheme_fp = fopen(td->o.dp_scheme_file, "r");
151
152         if (!scheme_fp) {
153                 log_err("fio: ruh scheme failed to open scheme file %s\n",
154                         td->o.dp_scheme_file);
155                 ret = -errno;
156                 goto out;
157         }
158
159         ruh_scheme = scalloc(1, sizeof(*ruh_scheme));
160         if (!ruh_scheme) {
161                 ret = -ENOMEM;
162                 goto out_with_close_fp;
163         }
164
165         for (int i = 0;
166                 i < DP_MAX_SCHEME_ENTRIES && fscanf(scheme_fp, "%llu,%llu,%hu\n", &start, &end, &pli) == 3;
167                 i++) {
168
169                 ruh_scheme->scheme_entries[i].start_offset = start;
170                 ruh_scheme->scheme_entries[i].end_offset = end;
171                 ruh_scheme->scheme_entries[i].pli = pli;
172                 ruh_scheme->nr_schemes++;
173         }
174
175         if (fscanf(scheme_fp, "%llu,%llu,%hu\n", &start, &end, &pli) == 3)
176                 log_info("fio: too many scheme entries in %s. Only the first %d scheme entries are applied\n",
177                          td->o.dp_scheme_file,
178                          DP_MAX_SCHEME_ENTRIES);
179
180         f->ruhs_scheme = ruh_scheme;
181
182 out_with_close_fp:
183         fclose(scheme_fp);
184 out:
185         return ret;
186 }
187
188 int dp_init(struct thread_data *td)
189 {
190         struct fio_file *f;
191         int i, ret = 0;
192
193         for_each_file(td, f, i) {
194                 ret = init_ruh_info(td, f);
195                 if (ret)
196                         break;
197
198                 ret = init_ruh_scheme(td, f);
199                 if (ret)
200                         break;
201         }
202         return ret;
203 }
204
205 void fdp_free_ruhs_info(struct fio_file *f)
206 {
207         if (!f->ruhs_info)
208                 return;
209         sfree(f->ruhs_info);
210         f->ruhs_info = NULL;
211
212         if (!f->ruhs_scheme)
213                 return;
214         sfree(f->ruhs_scheme);
215         f->ruhs_scheme = NULL;
216 }
217
218 void dp_fill_dspec_data(struct thread_data *td, struct io_u *io_u)
219 {
220         struct fio_file *f = io_u->file;
221         struct fio_ruhs_info *ruhs = f->ruhs_info;
222         int dspec;
223
224         if (!ruhs || io_u->ddir != DDIR_WRITE) {
225                 io_u->dtype = 0;
226                 io_u->dspec = 0;
227                 return;
228         }
229
230         if (td->o.dp_id_select == FIO_DP_RR) {
231                 if (ruhs->pli_loc >= ruhs->nr_ruhs)
232                         ruhs->pli_loc = 0;
233
234                 dspec = ruhs->plis[ruhs->pli_loc++];
235         } else if (td->o.dp_id_select == FIO_DP_SCHEME) {
236                 struct fio_ruhs_scheme *ruhs_scheme = f->ruhs_scheme;
237                 unsigned long long offset = io_u->offset;
238                 int i;
239
240                 for (i = 0; i < ruhs_scheme->nr_schemes; i++) {
241                         if (offset >= ruhs_scheme->scheme_entries[i].start_offset &&
242                             offset < ruhs_scheme->scheme_entries[i].end_offset) {
243                                 dspec = ruhs_scheme->scheme_entries[i].pli;
244                                 break;
245                         }
246                 }
247
248                 /*
249                  * If the write offset is not affected by any scheme entry,
250                  * 0(default RUH) will be assigned to dspec
251                  */
252                 if (i == ruhs_scheme->nr_schemes)
253                         dspec = 0;
254         } else {
255                 ruhs->pli_loc = rand_between(&td->fdp_state, 0, ruhs->nr_ruhs - 1);
256                 dspec = ruhs->plis[ruhs->pli_loc];
257         }
258
259         io_u->dtype = td->o.dp_type == FIO_DP_FDP ? FDP_DIR_DTYPE : STREAMS_DIR_DTYPE;
260         io_u->dspec = dspec;
261         dprint(FD_IO, "dtype set to 0x%x, dspec set to 0x%x\n", io_u->dtype, io_u->dspec);
262 }