Merge branch 'dev' of https://github.com/smartxworks/fio
[fio.git] / engines / libiscsi.c
CommitLineData
247ef2aa
KZ
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
16struct iscsi_lun;
17struct iscsi_info;
18
19struct iscsi_task {
20 struct scsi_task *scsi_task;
21 struct iscsi_lun *iscsi_lun;
22 struct io_u *io_u;
23};
24
25struct 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
33struct 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
41struct iscsi_options {
42 void *pad;
43 char *initiator;
44};
45
46static 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
63static 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) {
b57f4fd4
KZ
120 log_err("iscsi: failed to send readcapacity command: %s\n",
121 iscsi_get_error(iscsi_lun->iscsi));
247ef2aa
KZ
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
145out:
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
163static 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
189static int fio_iscsi_init(struct thread_data *td) {
190 return 0;
191}
192
193static 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
203static 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
220static int fio_iscsi_prep(struct thread_data *td, struct io_u *io_u)
221{
222 return 0;
223}
224
225static int fio_iscsi_open_file(struct thread_data *td, struct fio_file *f)
226{
227 return 0;
228}
229
230static int fio_iscsi_close_file(struct thread_data *td, struct fio_file *f)
231{
232 return 0;
233}
234
235static 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
257static 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
323out:
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
338static 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
369static 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
383static 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
400static void fio_init fio_iscsi_register(void)
401{
402 register_ioengine(&ioengine_iscsi);
403}
404
405static void fio_exit fio_iscsi_unregister(void)
406{
407 unregister_ioengine(&ioengine_iscsi);
408}