diff options
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/ulp/iser/iscsi_iser.c | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c new file mode 100644 index 000000000000..4c3f2de2a06e --- /dev/null +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c | |||
@@ -0,0 +1,790 @@ | |||
1 | /* | ||
2 | * iSCSI Initiator over iSER Data-Path | ||
3 | * | ||
4 | * Copyright (C) 2004 Dmitry Yusupov | ||
5 | * Copyright (C) 2004 Alex Aizman | ||
6 | * Copyright (C) 2005 Mike Christie | ||
7 | * Copyright (c) 2005, 2006 Voltaire, Inc. All rights reserved. | ||
8 | * maintained by openib-general@openib.org | ||
9 | * | ||
10 | * This software is available to you under a choice of one of two | ||
11 | * licenses. You may choose to be licensed under the terms of the GNU | ||
12 | * General Public License (GPL) Version 2, available from the file | ||
13 | * COPYING in the main directory of this source tree, or the | ||
14 | * OpenIB.org BSD license below: | ||
15 | * | ||
16 | * Redistribution and use in source and binary forms, with or | ||
17 | * without modification, are permitted provided that the following | ||
18 | * conditions are met: | ||
19 | * | ||
20 | * - Redistributions of source code must retain the above | ||
21 | * copyright notice, this list of conditions and the following | ||
22 | * disclaimer. | ||
23 | * | ||
24 | * - Redistributions in binary form must reproduce the above | ||
25 | * copyright notice, this list of conditions and the following | ||
26 | * disclaimer in the documentation and/or other materials | ||
27 | * provided with the distribution. | ||
28 | * | ||
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
30 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
31 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
32 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
33 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
34 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
35 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
36 | * SOFTWARE. | ||
37 | * | ||
38 | * Credits: | ||
39 | * Christoph Hellwig | ||
40 | * FUJITA Tomonori | ||
41 | * Arne Redlich | ||
42 | * Zhenyu Wang | ||
43 | * Modified by: | ||
44 | * Erez Zilber | ||
45 | * | ||
46 | * | ||
47 | * $Id: iscsi_iser.c 6965 2006-05-07 11:36:20Z ogerlitz $ | ||
48 | */ | ||
49 | |||
50 | #include <linux/types.h> | ||
51 | #include <linux/list.h> | ||
52 | #include <linux/hardirq.h> | ||
53 | #include <linux/kfifo.h> | ||
54 | #include <linux/blkdev.h> | ||
55 | #include <linux/init.h> | ||
56 | #include <linux/ioctl.h> | ||
57 | #include <linux/devfs_fs_kernel.h> | ||
58 | #include <linux/cdev.h> | ||
59 | #include <linux/in.h> | ||
60 | #include <linux/net.h> | ||
61 | #include <linux/scatterlist.h> | ||
62 | #include <linux/delay.h> | ||
63 | |||
64 | #include <net/sock.h> | ||
65 | |||
66 | #include <asm/uaccess.h> | ||
67 | |||
68 | #include <scsi/scsi_cmnd.h> | ||
69 | #include <scsi/scsi_device.h> | ||
70 | #include <scsi/scsi_eh.h> | ||
71 | #include <scsi/scsi_tcq.h> | ||
72 | #include <scsi/scsi_host.h> | ||
73 | #include <scsi/scsi.h> | ||
74 | #include <scsi/scsi_transport_iscsi.h> | ||
75 | |||
76 | #include "iscsi_iser.h" | ||
77 | |||
78 | static unsigned int iscsi_max_lun = 512; | ||
79 | module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); | ||
80 | |||
81 | int iser_debug_level = 0; | ||
82 | |||
83 | MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover " | ||
84 | "v" DRV_VER " (" DRV_DATE ")"); | ||
85 | MODULE_LICENSE("Dual BSD/GPL"); | ||
86 | MODULE_AUTHOR("Alex Nezhinsky, Dan Bar Dov, Or Gerlitz"); | ||
87 | |||
88 | module_param_named(debug_level, iser_debug_level, int, 0644); | ||
89 | MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0 (default:disabled)"); | ||
90 | |||
91 | struct iser_global ig; | ||
92 | |||
93 | void | ||
94 | iscsi_iser_recv(struct iscsi_conn *conn, | ||
95 | struct iscsi_hdr *hdr, char *rx_data, int rx_data_len) | ||
96 | { | ||
97 | int rc = 0; | ||
98 | uint32_t ret_itt; | ||
99 | int datalen; | ||
100 | int ahslen; | ||
101 | |||
102 | /* verify PDU length */ | ||
103 | datalen = ntoh24(hdr->dlength); | ||
104 | if (datalen != rx_data_len) { | ||
105 | printk(KERN_ERR "iscsi_iser: datalen %d (hdr) != %d (IB) \n", | ||
106 | datalen, rx_data_len); | ||
107 | rc = ISCSI_ERR_DATALEN; | ||
108 | goto error; | ||
109 | } | ||
110 | |||
111 | /* read AHS */ | ||
112 | ahslen = hdr->hlength * 4; | ||
113 | |||
114 | /* verify itt (itt encoding: age+cid+itt) */ | ||
115 | rc = iscsi_verify_itt(conn, hdr, &ret_itt); | ||
116 | |||
117 | if (!rc) | ||
118 | rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len); | ||
119 | |||
120 | if (rc && rc != ISCSI_ERR_NO_SCSI_CMD) | ||
121 | goto error; | ||
122 | |||
123 | return; | ||
124 | error: | ||
125 | iscsi_conn_failure(conn, rc); | ||
126 | } | ||
127 | |||
128 | |||
129 | /** | ||
130 | * iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands | ||
131 | * | ||
132 | **/ | ||
133 | static void | ||
134 | iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask) | ||
135 | { | ||
136 | struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data; | ||
137 | struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data; | ||
138 | struct scsi_cmnd *sc = ctask->sc; | ||
139 | |||
140 | iser_ctask->command_sent = 0; | ||
141 | iser_ctask->iser_conn = iser_conn; | ||
142 | |||
143 | if (sc->sc_data_direction == DMA_TO_DEVICE) { | ||
144 | BUG_ON(ctask->total_length == 0); | ||
145 | /* bytes to be sent via RDMA operations */ | ||
146 | iser_ctask->rdma_data_count = ctask->total_length - | ||
147 | ctask->imm_count - | ||
148 | ctask->unsol_count; | ||
149 | |||
150 | debug_scsi("cmd [itt %x total %d imm %d unsol_data %d " | ||
151 | "rdma_data %d]\n", | ||
152 | ctask->itt, ctask->total_length, ctask->imm_count, | ||
153 | ctask->unsol_count, iser_ctask->rdma_data_count); | ||
154 | } else | ||
155 | /* bytes to be sent via RDMA operations */ | ||
156 | iser_ctask->rdma_data_count = ctask->total_length; | ||
157 | |||
158 | iser_ctask_rdma_init(iser_ctask); | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * iscsi_mtask_xmit - xmit management(immediate) task | ||
163 | * @conn: iscsi connection | ||
164 | * @mtask: task management task | ||
165 | * | ||
166 | * Notes: | ||
167 | * The function can return -EAGAIN in which case caller must | ||
168 | * call it again later, or recover. '0' return code means successful | ||
169 | * xmit. | ||
170 | * | ||
171 | **/ | ||
172 | static int | ||
173 | iscsi_iser_mtask_xmit(struct iscsi_conn *conn, | ||
174 | struct iscsi_mgmt_task *mtask) | ||
175 | { | ||
176 | int error = 0; | ||
177 | |||
178 | debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt); | ||
179 | |||
180 | error = iser_send_control(conn, mtask); | ||
181 | |||
182 | /* since iser xmits control with zero copy, mtasks can not be recycled | ||
183 | * right after sending them. | ||
184 | * The recycling scheme is based on whether a response is expected | ||
185 | * - if yes, the mtask is recycled at iscsi_complete_pdu | ||
186 | * - if no, the mtask is recycled at iser_snd_completion | ||
187 | */ | ||
188 | if (error && error != -EAGAIN) | ||
189 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
190 | |||
191 | return error; | ||
192 | } | ||
193 | |||
194 | static int | ||
195 | iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn, | ||
196 | struct iscsi_cmd_task *ctask) | ||
197 | { | ||
198 | struct iscsi_data hdr; | ||
199 | int error = 0; | ||
200 | struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data; | ||
201 | |||
202 | /* Send data-out PDUs while there's still unsolicited data to send */ | ||
203 | while (ctask->unsol_count > 0) { | ||
204 | iscsi_prep_unsolicit_data_pdu(ctask, &hdr, | ||
205 | iser_ctask->rdma_data_count); | ||
206 | |||
207 | debug_scsi("Sending data-out: itt 0x%x, data count %d\n", | ||
208 | hdr.itt, ctask->data_count); | ||
209 | |||
210 | /* the buffer description has been passed with the command */ | ||
211 | /* Send the command */ | ||
212 | error = iser_send_data_out(conn, ctask, &hdr); | ||
213 | if (error) { | ||
214 | ctask->unsol_datasn--; | ||
215 | goto iscsi_iser_ctask_xmit_unsol_data_exit; | ||
216 | } | ||
217 | ctask->unsol_count -= ctask->data_count; | ||
218 | debug_scsi("Need to send %d more as data-out PDUs\n", | ||
219 | ctask->unsol_count); | ||
220 | } | ||
221 | |||
222 | iscsi_iser_ctask_xmit_unsol_data_exit: | ||
223 | return error; | ||
224 | } | ||
225 | |||
226 | static int | ||
227 | iscsi_iser_ctask_xmit(struct iscsi_conn *conn, | ||
228 | struct iscsi_cmd_task *ctask) | ||
229 | { | ||
230 | struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data; | ||
231 | int error = 0; | ||
232 | |||
233 | debug_scsi("ctask deq [cid %d itt 0x%x]\n", | ||
234 | conn->id, ctask->itt); | ||
235 | |||
236 | /* | ||
237 | * serialize with TMF AbortTask | ||
238 | */ | ||
239 | if (ctask->mtask) | ||
240 | return error; | ||
241 | |||
242 | /* Send the cmd PDU */ | ||
243 | if (!iser_ctask->command_sent) { | ||
244 | error = iser_send_command(conn, ctask); | ||
245 | if (error) | ||
246 | goto iscsi_iser_ctask_xmit_exit; | ||
247 | iser_ctask->command_sent = 1; | ||
248 | } | ||
249 | |||
250 | /* Send unsolicited data-out PDU(s) if necessary */ | ||
251 | if (ctask->unsol_count) | ||
252 | error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask); | ||
253 | |||
254 | iscsi_iser_ctask_xmit_exit: | ||
255 | if (error && error != -EAGAIN) | ||
256 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
257 | return error; | ||
258 | } | ||
259 | |||
260 | static void | ||
261 | iscsi_iser_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) | ||
262 | { | ||
263 | struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data; | ||
264 | |||
265 | if (iser_ctask->status == ISER_TASK_STATUS_STARTED) { | ||
266 | iser_ctask->status = ISER_TASK_STATUS_COMPLETED; | ||
267 | iser_ctask_rdma_finalize(iser_ctask); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | static struct iser_conn * | ||
272 | iscsi_iser_ib_conn_lookup(__u64 ep_handle) | ||
273 | { | ||
274 | struct iser_conn *ib_conn; | ||
275 | struct iser_conn *uib_conn = (struct iser_conn *)(unsigned long)ep_handle; | ||
276 | |||
277 | mutex_lock(&ig.connlist_mutex); | ||
278 | list_for_each_entry(ib_conn, &ig.connlist, conn_list) { | ||
279 | if (ib_conn == uib_conn) { | ||
280 | mutex_unlock(&ig.connlist_mutex); | ||
281 | return ib_conn; | ||
282 | } | ||
283 | } | ||
284 | mutex_unlock(&ig.connlist_mutex); | ||
285 | iser_err("no conn exists for eph %llx\n",(unsigned long long)ep_handle); | ||
286 | return NULL; | ||
287 | } | ||
288 | |||
289 | static struct iscsi_cls_conn * | ||
290 | iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) | ||
291 | { | ||
292 | struct iscsi_conn *conn; | ||
293 | struct iscsi_cls_conn *cls_conn; | ||
294 | struct iscsi_iser_conn *iser_conn; | ||
295 | |||
296 | cls_conn = iscsi_conn_setup(cls_session, conn_idx); | ||
297 | if (!cls_conn) | ||
298 | return NULL; | ||
299 | conn = cls_conn->dd_data; | ||
300 | |||
301 | /* | ||
302 | * due to issues with the login code re iser sematics | ||
303 | * this not set in iscsi_conn_setup - FIXME | ||
304 | */ | ||
305 | conn->max_recv_dlength = 128; | ||
306 | |||
307 | iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL); | ||
308 | if (!iser_conn) | ||
309 | goto conn_alloc_fail; | ||
310 | |||
311 | /* currently this is the only field which need to be initiated */ | ||
312 | rwlock_init(&iser_conn->lock); | ||
313 | |||
314 | conn->dd_data = iser_conn; | ||
315 | iser_conn->iscsi_conn = conn; | ||
316 | |||
317 | return cls_conn; | ||
318 | |||
319 | conn_alloc_fail: | ||
320 | iscsi_conn_teardown(cls_conn); | ||
321 | return NULL; | ||
322 | } | ||
323 | |||
324 | static void | ||
325 | iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn) | ||
326 | { | ||
327 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
328 | struct iscsi_iser_conn *iser_conn = conn->dd_data; | ||
329 | |||
330 | iscsi_conn_teardown(cls_conn); | ||
331 | kfree(iser_conn); | ||
332 | } | ||
333 | |||
334 | static int | ||
335 | iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session, | ||
336 | struct iscsi_cls_conn *cls_conn, uint64_t transport_eph, | ||
337 | int is_leading) | ||
338 | { | ||
339 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
340 | struct iscsi_iser_conn *iser_conn; | ||
341 | struct iser_conn *ib_conn; | ||
342 | int error; | ||
343 | |||
344 | error = iscsi_conn_bind(cls_session, cls_conn, is_leading); | ||
345 | if (error) | ||
346 | return error; | ||
347 | |||
348 | /* the transport ep handle comes from user space so it must be | ||
349 | * verified against the global ib connections list */ | ||
350 | ib_conn = iscsi_iser_ib_conn_lookup(transport_eph); | ||
351 | if (!ib_conn) { | ||
352 | iser_err("can't bind eph %llx\n", | ||
353 | (unsigned long long)transport_eph); | ||
354 | return -EINVAL; | ||
355 | } | ||
356 | /* binds the iSER connection retrieved from the previously | ||
357 | * connected ep_handle to the iSCSI layer connection. exchanges | ||
358 | * connection pointers */ | ||
359 | iser_err("binding iscsi conn %p to iser_conn %p\n",conn,ib_conn); | ||
360 | iser_conn = conn->dd_data; | ||
361 | ib_conn->iser_conn = iser_conn; | ||
362 | iser_conn->ib_conn = ib_conn; | ||
363 | |||
364 | conn->recv_lock = &iser_conn->lock; | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int | ||
370 | iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn) | ||
371 | { | ||
372 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
373 | int err; | ||
374 | |||
375 | err = iscsi_conn_start(cls_conn); | ||
376 | if (err) | ||
377 | return err; | ||
378 | |||
379 | return iser_conn_set_full_featured_mode(conn); | ||
380 | } | ||
381 | |||
382 | static void | ||
383 | iscsi_iser_conn_terminate(struct iscsi_conn *conn) | ||
384 | { | ||
385 | struct iscsi_iser_conn *iser_conn = conn->dd_data; | ||
386 | struct iser_conn *ib_conn = iser_conn->ib_conn; | ||
387 | |||
388 | BUG_ON(!ib_conn); | ||
389 | /* starts conn teardown process, waits until all previously * | ||
390 | * posted buffers get flushed, deallocates all conn resources */ | ||
391 | iser_conn_terminate(ib_conn); | ||
392 | iser_conn->ib_conn = NULL; | ||
393 | conn->recv_lock = NULL; | ||
394 | } | ||
395 | |||
396 | |||
397 | static struct iscsi_transport iscsi_iser_transport; | ||
398 | |||
399 | static struct iscsi_cls_session * | ||
400 | iscsi_iser_session_create(struct iscsi_transport *iscsit, | ||
401 | struct scsi_transport_template *scsit, | ||
402 | uint32_t initial_cmdsn, uint32_t *hostno) | ||
403 | { | ||
404 | struct iscsi_cls_session *cls_session; | ||
405 | struct iscsi_session *session; | ||
406 | int i; | ||
407 | uint32_t hn; | ||
408 | struct iscsi_cmd_task *ctask; | ||
409 | struct iscsi_mgmt_task *mtask; | ||
410 | struct iscsi_iser_cmd_task *iser_ctask; | ||
411 | struct iser_desc *desc; | ||
412 | |||
413 | cls_session = iscsi_session_setup(iscsit, scsit, | ||
414 | sizeof(struct iscsi_iser_cmd_task), | ||
415 | sizeof(struct iser_desc), | ||
416 | initial_cmdsn, &hn); | ||
417 | if (!cls_session) | ||
418 | return NULL; | ||
419 | |||
420 | *hostno = hn; | ||
421 | session = class_to_transport_session(cls_session); | ||
422 | |||
423 | /* libiscsi setup itts, data and pool so just set desc fields */ | ||
424 | for (i = 0; i < session->cmds_max; i++) { | ||
425 | ctask = session->cmds[i]; | ||
426 | iser_ctask = ctask->dd_data; | ||
427 | ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header; | ||
428 | } | ||
429 | |||
430 | for (i = 0; i < session->mgmtpool_max; i++) { | ||
431 | mtask = session->mgmt_cmds[i]; | ||
432 | desc = mtask->dd_data; | ||
433 | mtask->hdr = &desc->iscsi_header; | ||
434 | desc->data = mtask->data; | ||
435 | } | ||
436 | |||
437 | return cls_session; | ||
438 | } | ||
439 | |||
440 | static int | ||
441 | iscsi_iser_conn_set_param(struct iscsi_cls_conn *cls_conn, | ||
442 | enum iscsi_param param, uint32_t value) | ||
443 | { | ||
444 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
445 | struct iscsi_session *session = conn->session; | ||
446 | |||
447 | spin_lock_bh(&session->lock); | ||
448 | if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE && | ||
449 | conn->stop_stage != STOP_CONN_RECOVER) { | ||
450 | printk(KERN_ERR "iscsi_iser: can not change parameter [%d]\n", | ||
451 | param); | ||
452 | spin_unlock_bh(&session->lock); | ||
453 | return 0; | ||
454 | } | ||
455 | spin_unlock_bh(&session->lock); | ||
456 | |||
457 | switch (param) { | ||
458 | case ISCSI_PARAM_MAX_RECV_DLENGTH: | ||
459 | /* TBD */ | ||
460 | break; | ||
461 | case ISCSI_PARAM_MAX_XMIT_DLENGTH: | ||
462 | conn->max_xmit_dlength = value; | ||
463 | break; | ||
464 | case ISCSI_PARAM_HDRDGST_EN: | ||
465 | if (value) { | ||
466 | printk(KERN_ERR "DataDigest wasn't negotiated to None"); | ||
467 | return -EPROTO; | ||
468 | } | ||
469 | break; | ||
470 | case ISCSI_PARAM_DATADGST_EN: | ||
471 | if (value) { | ||
472 | printk(KERN_ERR "DataDigest wasn't negotiated to None"); | ||
473 | return -EPROTO; | ||
474 | } | ||
475 | break; | ||
476 | case ISCSI_PARAM_INITIAL_R2T_EN: | ||
477 | session->initial_r2t_en = value; | ||
478 | break; | ||
479 | case ISCSI_PARAM_IMM_DATA_EN: | ||
480 | session->imm_data_en = value; | ||
481 | break; | ||
482 | case ISCSI_PARAM_FIRST_BURST: | ||
483 | session->first_burst = value; | ||
484 | break; | ||
485 | case ISCSI_PARAM_MAX_BURST: | ||
486 | session->max_burst = value; | ||
487 | break; | ||
488 | case ISCSI_PARAM_PDU_INORDER_EN: | ||
489 | session->pdu_inorder_en = value; | ||
490 | break; | ||
491 | case ISCSI_PARAM_DATASEQ_INORDER_EN: | ||
492 | session->dataseq_inorder_en = value; | ||
493 | break; | ||
494 | case ISCSI_PARAM_ERL: | ||
495 | session->erl = value; | ||
496 | break; | ||
497 | case ISCSI_PARAM_IFMARKER_EN: | ||
498 | if (value) { | ||
499 | printk(KERN_ERR "IFMarker wasn't negotiated to No"); | ||
500 | return -EPROTO; | ||
501 | } | ||
502 | break; | ||
503 | case ISCSI_PARAM_OFMARKER_EN: | ||
504 | if (value) { | ||
505 | printk(KERN_ERR "OFMarker wasn't negotiated to No"); | ||
506 | return -EPROTO; | ||
507 | } | ||
508 | break; | ||
509 | default: | ||
510 | break; | ||
511 | } | ||
512 | |||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | static int | ||
517 | iscsi_iser_session_get_param(struct iscsi_cls_session *cls_session, | ||
518 | enum iscsi_param param, uint32_t *value) | ||
519 | { | ||
520 | struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); | ||
521 | struct iscsi_session *session = iscsi_hostdata(shost->hostdata); | ||
522 | |||
523 | switch (param) { | ||
524 | case ISCSI_PARAM_INITIAL_R2T_EN: | ||
525 | *value = session->initial_r2t_en; | ||
526 | break; | ||
527 | case ISCSI_PARAM_MAX_R2T: | ||
528 | *value = session->max_r2t; | ||
529 | break; | ||
530 | case ISCSI_PARAM_IMM_DATA_EN: | ||
531 | *value = session->imm_data_en; | ||
532 | break; | ||
533 | case ISCSI_PARAM_FIRST_BURST: | ||
534 | *value = session->first_burst; | ||
535 | break; | ||
536 | case ISCSI_PARAM_MAX_BURST: | ||
537 | *value = session->max_burst; | ||
538 | break; | ||
539 | case ISCSI_PARAM_PDU_INORDER_EN: | ||
540 | *value = session->pdu_inorder_en; | ||
541 | break; | ||
542 | case ISCSI_PARAM_DATASEQ_INORDER_EN: | ||
543 | *value = session->dataseq_inorder_en; | ||
544 | break; | ||
545 | case ISCSI_PARAM_ERL: | ||
546 | *value = session->erl; | ||
547 | break; | ||
548 | case ISCSI_PARAM_IFMARKER_EN: | ||
549 | *value = 0; | ||
550 | break; | ||
551 | case ISCSI_PARAM_OFMARKER_EN: | ||
552 | *value = 0; | ||
553 | break; | ||
554 | default: | ||
555 | return ISCSI_ERR_PARAM_NOT_FOUND; | ||
556 | } | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | static int | ||
562 | iscsi_iser_conn_get_param(struct iscsi_cls_conn *cls_conn, | ||
563 | enum iscsi_param param, uint32_t *value) | ||
564 | { | ||
565 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
566 | |||
567 | switch(param) { | ||
568 | case ISCSI_PARAM_MAX_RECV_DLENGTH: | ||
569 | *value = conn->max_recv_dlength; | ||
570 | break; | ||
571 | case ISCSI_PARAM_MAX_XMIT_DLENGTH: | ||
572 | *value = conn->max_xmit_dlength; | ||
573 | break; | ||
574 | case ISCSI_PARAM_HDRDGST_EN: | ||
575 | *value = 0; | ||
576 | break; | ||
577 | case ISCSI_PARAM_DATADGST_EN: | ||
578 | *value = 0; | ||
579 | break; | ||
580 | /*case ISCSI_PARAM_TARGET_RECV_DLENGTH: | ||
581 | *value = conn->target_recv_dlength; | ||
582 | break; | ||
583 | case ISCSI_PARAM_INITIATOR_RECV_DLENGTH: | ||
584 | *value = conn->initiator_recv_dlength; | ||
585 | break;*/ | ||
586 | default: | ||
587 | return ISCSI_ERR_PARAM_NOT_FOUND; | ||
588 | } | ||
589 | |||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | |||
594 | static void | ||
595 | iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats) | ||
596 | { | ||
597 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
598 | |||
599 | stats->txdata_octets = conn->txdata_octets; | ||
600 | stats->rxdata_octets = conn->rxdata_octets; | ||
601 | stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; | ||
602 | stats->dataout_pdus = conn->dataout_pdus_cnt; | ||
603 | stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; | ||
604 | stats->datain_pdus = conn->datain_pdus_cnt; /* always 0 */ | ||
605 | stats->r2t_pdus = conn->r2t_pdus_cnt; /* always 0 */ | ||
606 | stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; | ||
607 | stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; | ||
608 | stats->custom_length = 3; | ||
609 | strcpy(stats->custom[0].desc, "qp_tx_queue_full"); | ||
610 | stats->custom[0].value = 0; /* TB iser_conn->qp_tx_queue_full; */ | ||
611 | strcpy(stats->custom[1].desc, "fmr_map_not_avail"); | ||
612 | stats->custom[1].value = 0; /* TB iser_conn->fmr_map_not_avail */; | ||
613 | strcpy(stats->custom[2].desc, "eh_abort_cnt"); | ||
614 | stats->custom[2].value = conn->eh_abort_cnt; | ||
615 | } | ||
616 | |||
617 | static int | ||
618 | iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking, | ||
619 | __u64 *ep_handle) | ||
620 | { | ||
621 | int err; | ||
622 | struct iser_conn *ib_conn; | ||
623 | |||
624 | err = iser_conn_init(&ib_conn); | ||
625 | if (err) | ||
626 | goto out; | ||
627 | |||
628 | err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking); | ||
629 | if (!err) | ||
630 | *ep_handle = (__u64)(unsigned long)ib_conn; | ||
631 | |||
632 | out: | ||
633 | return err; | ||
634 | } | ||
635 | |||
636 | static int | ||
637 | iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms) | ||
638 | { | ||
639 | struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle); | ||
640 | int rc; | ||
641 | |||
642 | if (!ib_conn) | ||
643 | return -EINVAL; | ||
644 | |||
645 | rc = wait_event_interruptible_timeout(ib_conn->wait, | ||
646 | ib_conn->state == ISER_CONN_UP, | ||
647 | msecs_to_jiffies(timeout_ms)); | ||
648 | |||
649 | /* if conn establishment failed, return error code to iscsi */ | ||
650 | if (!rc && | ||
651 | (ib_conn->state == ISER_CONN_TERMINATING || | ||
652 | ib_conn->state == ISER_CONN_DOWN)) | ||
653 | rc = -1; | ||
654 | |||
655 | iser_err("ib conn %p rc = %d\n", ib_conn, rc); | ||
656 | |||
657 | if (rc > 0) | ||
658 | return 1; /* success, this is the equivalent of POLLOUT */ | ||
659 | else if (!rc) | ||
660 | return 0; /* timeout */ | ||
661 | else | ||
662 | return rc; /* signal */ | ||
663 | } | ||
664 | |||
665 | static void | ||
666 | iscsi_iser_ep_disconnect(__u64 ep_handle) | ||
667 | { | ||
668 | struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle); | ||
669 | |||
670 | if (!ib_conn) | ||
671 | return; | ||
672 | |||
673 | iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state); | ||
674 | |||
675 | iser_conn_terminate(ib_conn); | ||
676 | } | ||
677 | |||
678 | static struct scsi_host_template iscsi_iser_sht = { | ||
679 | .name = "iSCSI Initiator over iSER, v." DRV_VER, | ||
680 | .queuecommand = iscsi_queuecommand, | ||
681 | .can_queue = ISCSI_XMIT_CMDS_MAX - 1, | ||
682 | .sg_tablesize = ISCSI_ISER_SG_TABLESIZE, | ||
683 | .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN, | ||
684 | .eh_abort_handler = iscsi_eh_abort, | ||
685 | .eh_host_reset_handler = iscsi_eh_host_reset, | ||
686 | .use_clustering = DISABLE_CLUSTERING, | ||
687 | .proc_name = "iscsi_iser", | ||
688 | .this_id = -1, | ||
689 | }; | ||
690 | |||
691 | static struct iscsi_transport iscsi_iser_transport = { | ||
692 | .owner = THIS_MODULE, | ||
693 | .name = "iser", | ||
694 | .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T, | ||
695 | .param_mask = ISCSI_MAX_RECV_DLENGTH | | ||
696 | ISCSI_MAX_XMIT_DLENGTH | | ||
697 | ISCSI_HDRDGST_EN | | ||
698 | ISCSI_DATADGST_EN | | ||
699 | ISCSI_INITIAL_R2T_EN | | ||
700 | ISCSI_MAX_R2T | | ||
701 | ISCSI_IMM_DATA_EN | | ||
702 | ISCSI_FIRST_BURST | | ||
703 | ISCSI_MAX_BURST | | ||
704 | ISCSI_PDU_INORDER_EN | | ||
705 | ISCSI_DATASEQ_INORDER_EN, | ||
706 | .host_template = &iscsi_iser_sht, | ||
707 | .conndata_size = sizeof(struct iscsi_conn), | ||
708 | .max_lun = ISCSI_ISER_MAX_LUN, | ||
709 | .max_cmd_len = ISCSI_ISER_MAX_CMD_LEN, | ||
710 | /* session management */ | ||
711 | .create_session = iscsi_iser_session_create, | ||
712 | .destroy_session = iscsi_session_teardown, | ||
713 | /* connection management */ | ||
714 | .create_conn = iscsi_iser_conn_create, | ||
715 | .bind_conn = iscsi_iser_conn_bind, | ||
716 | .destroy_conn = iscsi_iser_conn_destroy, | ||
717 | .set_param = iscsi_iser_conn_set_param, | ||
718 | .get_conn_param = iscsi_iser_conn_get_param, | ||
719 | .get_session_param = iscsi_iser_session_get_param, | ||
720 | .start_conn = iscsi_iser_conn_start, | ||
721 | .stop_conn = iscsi_conn_stop, | ||
722 | /* these are called as part of conn recovery */ | ||
723 | .suspend_conn_recv = NULL, /* FIXME is/how this relvant to iser? */ | ||
724 | .terminate_conn = iscsi_iser_conn_terminate, | ||
725 | /* IO */ | ||
726 | .send_pdu = iscsi_conn_send_pdu, | ||
727 | .get_stats = iscsi_iser_conn_get_stats, | ||
728 | .init_cmd_task = iscsi_iser_cmd_init, | ||
729 | .xmit_cmd_task = iscsi_iser_ctask_xmit, | ||
730 | .xmit_mgmt_task = iscsi_iser_mtask_xmit, | ||
731 | .cleanup_cmd_task = iscsi_iser_cleanup_ctask, | ||
732 | /* recovery */ | ||
733 | .session_recovery_timedout = iscsi_session_recovery_timedout, | ||
734 | |||
735 | .ep_connect = iscsi_iser_ep_connect, | ||
736 | .ep_poll = iscsi_iser_ep_poll, | ||
737 | .ep_disconnect = iscsi_iser_ep_disconnect | ||
738 | }; | ||
739 | |||
740 | static int __init iser_init(void) | ||
741 | { | ||
742 | int err; | ||
743 | |||
744 | iser_dbg("Starting iSER datamover...\n"); | ||
745 | |||
746 | if (iscsi_max_lun < 1) { | ||
747 | printk(KERN_ERR "Invalid max_lun value of %u\n", iscsi_max_lun); | ||
748 | return -EINVAL; | ||
749 | } | ||
750 | |||
751 | iscsi_iser_transport.max_lun = iscsi_max_lun; | ||
752 | |||
753 | memset(&ig, 0, sizeof(struct iser_global)); | ||
754 | |||
755 | ig.desc_cache = kmem_cache_create("iser_descriptors", | ||
756 | sizeof (struct iser_desc), | ||
757 | 0, SLAB_HWCACHE_ALIGN, | ||
758 | NULL, NULL); | ||
759 | if (ig.desc_cache == NULL) | ||
760 | return -ENOMEM; | ||
761 | |||
762 | /* device init is called only after the first addr resolution */ | ||
763 | mutex_init(&ig.device_list_mutex); | ||
764 | INIT_LIST_HEAD(&ig.device_list); | ||
765 | mutex_init(&ig.connlist_mutex); | ||
766 | INIT_LIST_HEAD(&ig.connlist); | ||
767 | |||
768 | if (!iscsi_register_transport(&iscsi_iser_transport)) { | ||
769 | iser_err("iscsi_register_transport failed\n"); | ||
770 | err = -EINVAL; | ||
771 | goto register_transport_failure; | ||
772 | } | ||
773 | |||
774 | return 0; | ||
775 | |||
776 | register_transport_failure: | ||
777 | kmem_cache_destroy(ig.desc_cache); | ||
778 | |||
779 | return err; | ||
780 | } | ||
781 | |||
782 | static void __exit iser_exit(void) | ||
783 | { | ||
784 | iser_dbg("Removing iSER datamover...\n"); | ||
785 | iscsi_unregister_transport(&iscsi_iser_transport); | ||
786 | kmem_cache_destroy(ig.desc_cache); | ||
787 | } | ||
788 | |||
789 | module_init(iser_init); | ||
790 | module_exit(iser_exit); | ||