Merge branch 'dev' of https://github.com/smartxworks/fio
[fio.git] / engines / libiscsi.c
1 /*
2  * libiscsi engine
3  *
4  * this engine read/write iscsi lun with libiscsi.
5  */
6
7
8 #include "../fio.h"
9 #include "../optgroup.h"
10
11 #include <stdlib.h>
12 #include <iscsi/iscsi.h>
13 #include <iscsi/scsi-lowlevel.h>
14 #include <poll.h>
15
16 struct iscsi_lun;
17 struct iscsi_info;
18
19 struct iscsi_task {
20         struct scsi_task        *scsi_task;
21         struct iscsi_lun        *iscsi_lun;
22         struct io_u             *io_u;
23 };
24
25 struct iscsi_lun {
26         struct iscsi_info       *iscsi_info;
27         struct iscsi_context    *iscsi;
28         struct iscsi_url        *url;
29         int                      block_size;
30         uint64_t                 num_blocks;
31 };
32
33 struct iscsi_info {
34         struct iscsi_lun        **luns;
35         int                       nr_luns;
36         struct pollfd            *pfds;
37         struct iscsi_task       **complete_events;
38         int                       nr_events;
39 };
40
41 struct iscsi_options {
42         void    *pad;
43         char    *initiator;
44 };
45
46 static struct fio_option options[] = {
47         {
48                 .name     = "initiator",
49                 .lname    = "initiator",
50                 .type     = FIO_OPT_STR_STORE,
51                 .off1     = offsetof(struct iscsi_options, initiator),
52                 .def      = "iqn.2019-04.org.fio:fio",
53                 .help     = "initiator name",
54                 .category = FIO_OPT_C_ENGINE,
55                 .group    = FIO_OPT_G_ISCSI,
56         },
57
58         {
59                 .name = NULL,
60         },
61 };
62
63 static int fio_iscsi_setup_lun(struct iscsi_info *iscsi_info,
64                                char *initiator, struct fio_file *f, int i)
65 {
66         struct iscsi_lun                *iscsi_lun  = NULL;
67         struct scsi_task                *task       = NULL;
68         struct scsi_readcapacity16      *rc16       = NULL;
69         int                              ret        = 0;
70
71         iscsi_lun = malloc(sizeof(struct iscsi_lun));
72         memset(iscsi_lun, 0, sizeof(struct iscsi_lun));
73
74         iscsi_lun->iscsi_info = iscsi_info;
75
76         iscsi_lun->url = iscsi_parse_full_url(NULL, f->file_name);
77         if (iscsi_lun->url == NULL) {
78                 log_err("iscsi: failed to parse url: %s\n", f->file_name);
79                 ret = EINVAL;
80                 goto out;
81         }
82
83         iscsi_lun->iscsi = iscsi_create_context(initiator);
84         if (iscsi_lun->iscsi == NULL) {
85                 log_err("iscsi: failed to create iscsi context.\n");
86                 ret = 1;
87                 goto out;
88         }
89
90         if (iscsi_set_targetname(iscsi_lun->iscsi, iscsi_lun->url->target)) {
91                 log_err("iscsi: failed to set target name.\n");
92                 ret = EINVAL;
93                 goto out;
94         }
95
96         if (iscsi_set_session_type(iscsi_lun->iscsi, ISCSI_SESSION_NORMAL) != 0) {
97                 log_err("iscsi: failed to set session type.\n");
98                 ret = EINVAL;
99                 goto out;
100         }
101
102         if (iscsi_set_header_digest(iscsi_lun->iscsi,
103                                     ISCSI_HEADER_DIGEST_NONE_CRC32C) != 0) {
104                 log_err("iscsi: failed to set header digest.\n");
105                 ret = EINVAL;
106                 goto out;
107         }
108
109         if (iscsi_full_connect_sync(iscsi_lun->iscsi,
110                                     iscsi_lun->url->portal,
111                                     iscsi_lun->url->lun)) {
112                 log_err("sicsi: failed to connect to LUN : %s\n",
113                         iscsi_get_error(iscsi_lun->iscsi));
114                 ret = EINVAL;
115                 goto out;
116         }
117
118         task = iscsi_readcapacity16_sync(iscsi_lun->iscsi, iscsi_lun->url->lun);
119         if (task == NULL || task->status != SCSI_STATUS_GOOD) {
120                 log_err("iscsi: failed to send readcapacity command: %s\n",
121                         iscsi_get_error(iscsi_lun->iscsi));
122                 ret = EINVAL;
123                 goto out;
124         }
125
126         rc16 = scsi_datain_unmarshall(task);
127         if (rc16 == NULL) {
128                 log_err("iscsi: failed to unmarshal readcapacity16 data.\n");
129                 ret = EINVAL;
130                 goto out;
131         }
132
133         iscsi_lun->block_size = rc16->block_length;
134         iscsi_lun->num_blocks = rc16->returned_lba + 1;
135
136         scsi_free_scsi_task(task);
137         task = NULL;
138
139         f->real_file_size = iscsi_lun->num_blocks * iscsi_lun->block_size;
140         f->engine_data    = iscsi_lun;
141
142         iscsi_info->luns[i]    = iscsi_lun;
143         iscsi_info->pfds[i].fd = iscsi_get_fd(iscsi_lun->iscsi);
144
145 out:
146         if (task) {
147                 scsi_free_scsi_task(task);
148         }
149
150         if (ret && iscsi_lun) {
151                 if (iscsi_lun->iscsi != NULL) {
152                         if (iscsi_is_logged_in(iscsi_lun->iscsi)) {
153                                 iscsi_logout_sync(iscsi_lun->iscsi);
154                         }
155                         iscsi_destroy_context(iscsi_lun->iscsi);
156                 }
157                 free(iscsi_lun);
158         }
159
160         return ret;
161 }
162
163 static int fio_iscsi_setup(struct thread_data *td)
164 {
165         struct iscsi_options    *options    = td->eo;
166         struct iscsi_info       *iscsi_info = NULL;
167         int                      ret        = 0;
168         struct fio_file         *f;
169         int                      i;
170
171         iscsi_info          = malloc(sizeof(struct iscsi_info));
172         iscsi_info->nr_luns = td->o.nr_files;
173         iscsi_info->luns    = calloc(iscsi_info->nr_luns, sizeof(struct iscsi_lun*));
174         iscsi_info->pfds    = calloc(iscsi_info->nr_luns, sizeof(struct pollfd));
175
176         iscsi_info->nr_events       = 0;
177         iscsi_info->complete_events = calloc(td->o.iodepth, sizeof(struct iscsi_task*));
178
179         td->io_ops_data = iscsi_info;
180
181         for_each_file(td, f, i) {
182                 ret = fio_iscsi_setup_lun(iscsi_info, options->initiator, f, i);
183                 if (ret < 0) break;
184         }
185
186         return ret;
187 }
188
189 static int fio_iscsi_init(struct thread_data *td) {
190         return 0;
191 }
192
193 static void fio_iscsi_cleanup_lun(struct iscsi_lun *iscsi_lun) {
194         if (iscsi_lun->iscsi != NULL) {
195                 if (iscsi_is_logged_in(iscsi_lun->iscsi)) {
196                         iscsi_logout_sync(iscsi_lun->iscsi);
197                 }
198                 iscsi_destroy_context(iscsi_lun->iscsi);
199         }
200         free(iscsi_lun);
201 }
202
203 static void fio_iscsi_cleanup(struct thread_data *td)
204 {
205         struct iscsi_info *iscsi_info = td->io_ops_data;
206
207         for (int i = 0; i < iscsi_info->nr_luns; i++) {
208                 if (iscsi_info->luns[i]) {
209                         fio_iscsi_cleanup_lun(iscsi_info->luns[i]);
210                         iscsi_info->luns[i] = NULL;
211                 }
212         }
213
214         free(iscsi_info->luns);
215         free(iscsi_info->pfds);
216         free(iscsi_info->complete_events);
217         free(iscsi_info);
218 }
219
220 static int fio_iscsi_prep(struct thread_data *td, struct io_u *io_u)
221 {
222         return 0;
223 }
224
225 static int fio_iscsi_open_file(struct thread_data *td, struct fio_file *f)
226 {
227         return 0;
228 }
229
230 static int fio_iscsi_close_file(struct thread_data *td, struct fio_file *f)
231 {
232         return 0;
233 }
234
235 static void iscsi_cb(struct iscsi_context *iscsi, int status,
236                      void *command_data, void *private_data)
237 {
238         struct iscsi_task       *iscsi_task = (struct iscsi_task*)private_data;
239         struct iscsi_lun        *iscsi_lun  = iscsi_task->iscsi_lun;
240         struct iscsi_info       *iscsi_info = iscsi_lun->iscsi_info;
241         struct io_u             *io_u       = iscsi_task->io_u;
242
243         if (status == SCSI_STATUS_GOOD) {
244                 io_u->error = 0;
245         } else {
246                 log_err("iscsi: request failed with error %s.\n",
247                         iscsi_get_error(iscsi_lun->iscsi));
248
249                 io_u->error = 1;
250                 io_u->resid = io_u->xfer_buflen;
251         }
252
253         iscsi_info->complete_events[iscsi_info->nr_events] = iscsi_task;
254         iscsi_info->nr_events++;
255 }
256
257 static enum fio_q_status fio_iscsi_queue(struct thread_data *td,
258                                          struct io_u *io_u)
259 {
260         struct iscsi_lun        *iscsi_lun  = io_u->file->engine_data;
261         struct scsi_task        *scsi_task  = NULL;
262         struct iscsi_task       *iscsi_task = malloc(sizeof(struct iscsi_task));
263         int                      ret        = -1;
264
265         if (io_u->ddir == DDIR_READ || io_u->ddir == DDIR_WRITE) {
266                 if (io_u->offset % iscsi_lun->block_size != 0) {
267                         log_err("iscsi: offset is not align to block size.\n");
268                         ret = -1;
269                         goto out;
270                 }
271
272                 if (io_u->xfer_buflen % iscsi_lun->block_size != 0) {
273                         log_err("iscsi: buflen is not align to block size.\n");
274                         ret = -1;
275                         goto out;
276                 }
277         }
278
279         if (io_u->ddir == DDIR_READ) {
280                 scsi_task = scsi_cdb_read16(io_u->offset / iscsi_lun->block_size,
281                                             io_u->xfer_buflen,
282                                             iscsi_lun->block_size,
283                                             0, 0, 0, 0, 0);
284                 ret = scsi_task_add_data_in_buffer(scsi_task, io_u->xfer_buflen,
285                                                    io_u->xfer_buf);
286                 if (ret < 0) {
287                         log_err("iscsi: failed to add data in buffer.\n");
288                         goto out;
289                 }
290         } else if (io_u->ddir == DDIR_WRITE) {
291                 scsi_task = scsi_cdb_write16(io_u->offset / iscsi_lun->block_size,
292                                              io_u->xfer_buflen,
293                                              iscsi_lun->block_size,
294                                              0, 0, 0, 0, 0);
295                 ret = scsi_task_add_data_out_buffer(scsi_task, io_u->xfer_buflen,
296                                                     io_u->xfer_buf);
297                 if (ret < 0) {
298                         log_err("iscsi: failed to add data out buffer.\n");
299                         goto out;
300                 }
301         } else if (ddir_sync(io_u->ddir)) {
302                 scsi_task = scsi_cdb_synchronizecache16(
303                         0, iscsi_lun->num_blocks * iscsi_lun->block_size, 0, 0);
304         } else {
305                 log_err("iscsi: invalid I/O operation: %d\n", io_u->ddir);
306                 ret = EINVAL;
307                 goto out;
308         }
309
310         iscsi_task->scsi_task = scsi_task;
311         iscsi_task->iscsi_lun = iscsi_lun;
312         iscsi_task->io_u      = io_u;
313
314         ret = iscsi_scsi_command_async(iscsi_lun->iscsi, iscsi_lun->url->lun,
315                                        scsi_task, iscsi_cb, NULL, iscsi_task);
316         if (ret < 0) {
317                 log_err("iscsi: failed to send scsi command.\n");
318                 goto out;
319         }
320
321         return FIO_Q_QUEUED;
322
323 out:
324         if (iscsi_task) {
325                 free(iscsi_task);
326         }
327
328         if (scsi_task) {
329                 scsi_free_scsi_task(scsi_task);
330         }
331
332         if (ret) {
333                 io_u->error = ret;
334         }
335         return FIO_Q_COMPLETED;
336 }
337
338 static int fio_iscsi_getevents(struct thread_data *td, unsigned int min,
339                                unsigned int max, const struct timespec *t)
340 {
341         struct iscsi_info       *iscsi_info = td->io_ops_data;
342         int                      ret        = 0;
343
344         iscsi_info->nr_events = 0;
345
346         while (iscsi_info->nr_events < min) {
347                 for (int i = 0; i < iscsi_info->nr_luns; i++) {
348                         int events = iscsi_which_events(iscsi_info->luns[i]->iscsi);
349                         iscsi_info->pfds[i].events = events;
350                 }
351
352                 ret = poll(iscsi_info->pfds, iscsi_info->nr_luns, -1);
353                 if (ret < 0) {
354                         log_err("iscsi: failed to poll events: %s.\n",
355                                 strerror(errno));
356                         break;
357                 }
358
359                 for (int i = 0; i < iscsi_info->nr_luns; i++) {
360                         ret = iscsi_service(iscsi_info->luns[i]->iscsi,
361                                             iscsi_info->pfds[i].revents);
362                         assert(ret >= 0);
363                 }
364         }
365
366         return ret < 0 ? ret : iscsi_info->nr_events;
367 }
368
369 static struct io_u *fio_iscsi_event(struct thread_data *td, int event)
370 {
371         struct iscsi_info       *iscsi_info = (struct iscsi_info*)td->io_ops_data;
372         struct iscsi_task       *iscsi_task = iscsi_info->complete_events[event];
373         struct io_u             *io_u       = iscsi_task->io_u;
374
375         iscsi_info->complete_events[event] = NULL;
376
377         scsi_free_scsi_task(iscsi_task->scsi_task);
378         free(iscsi_task);
379
380         return io_u;
381 }
382
383 static struct ioengine_ops ioengine_iscsi = {
384         .name               = "libiscsi",
385         .version            = FIO_IOOPS_VERSION,
386         .flags              = FIO_SYNCIO | FIO_DISKLESSIO | FIO_NODISKUTIL,
387         .setup              = fio_iscsi_setup,
388         .init               = fio_iscsi_init,
389         .prep               = fio_iscsi_prep,
390         .queue              = fio_iscsi_queue,
391         .getevents          = fio_iscsi_getevents,
392         .event              = fio_iscsi_event,
393         .cleanup            = fio_iscsi_cleanup,
394         .open_file          = fio_iscsi_open_file,
395         .close_file         = fio_iscsi_close_file,
396         .option_struct_size = sizeof(struct iscsi_options),
397         .options            = options,
398 };
399
400 static void fio_init fio_iscsi_register(void)
401 {
402         register_ioengine(&ioengine_iscsi);
403 }
404
405 static void fio_exit fio_iscsi_unregister(void)
406 {
407         unregister_ioengine(&ioengine_iscsi);
408 }