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