diff options
Diffstat (limited to 'drivers/scsi/cxgb3i/cxgb3i_iscsi.c')
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i_iscsi.c | 1018 |
1 files changed, 0 insertions, 1018 deletions
diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c deleted file mode 100644 index 7b686abaae64..000000000000 --- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c +++ /dev/null | |||
@@ -1,1018 +0,0 @@ | |||
1 | /* cxgb3i_iscsi.c: Chelsio S3xx iSCSI driver. | ||
2 | * | ||
3 | * Copyright (c) 2008 Chelsio Communications, Inc. | ||
4 | * Copyright (c) 2008 Mike Christie | ||
5 | * Copyright (c) 2008 Red Hat, Inc. All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation. | ||
10 | * | ||
11 | * Written by: Karen Xie (kxie@chelsio.com) | ||
12 | */ | ||
13 | |||
14 | #include <linux/inet.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/crypto.h> | ||
17 | #include <linux/if_vlan.h> | ||
18 | #include <net/dst.h> | ||
19 | #include <net/tcp.h> | ||
20 | #include <scsi/scsi_cmnd.h> | ||
21 | #include <scsi/scsi_device.h> | ||
22 | #include <scsi/scsi_eh.h> | ||
23 | #include <scsi/scsi_host.h> | ||
24 | #include <scsi/scsi.h> | ||
25 | #include <scsi/iscsi_proto.h> | ||
26 | #include <scsi/libiscsi.h> | ||
27 | #include <scsi/scsi_transport_iscsi.h> | ||
28 | |||
29 | #include "cxgb3i.h" | ||
30 | #include "cxgb3i_pdu.h" | ||
31 | |||
32 | #ifdef __DEBUG_CXGB3I_TAG__ | ||
33 | #define cxgb3i_tag_debug cxgb3i_log_debug | ||
34 | #else | ||
35 | #define cxgb3i_tag_debug(fmt...) | ||
36 | #endif | ||
37 | |||
38 | #ifdef __DEBUG_CXGB3I_API__ | ||
39 | #define cxgb3i_api_debug cxgb3i_log_debug | ||
40 | #else | ||
41 | #define cxgb3i_api_debug(fmt...) | ||
42 | #endif | ||
43 | |||
44 | /* | ||
45 | * align pdu size to multiple of 512 for better performance | ||
46 | */ | ||
47 | #define align_pdu_size(n) do { n = (n) & (~511); } while (0) | ||
48 | |||
49 | static struct scsi_transport_template *cxgb3i_scsi_transport; | ||
50 | static struct scsi_host_template cxgb3i_host_template; | ||
51 | static struct iscsi_transport cxgb3i_iscsi_transport; | ||
52 | static unsigned char sw_tag_idx_bits; | ||
53 | static unsigned char sw_tag_age_bits; | ||
54 | |||
55 | static LIST_HEAD(cxgb3i_snic_list); | ||
56 | static DEFINE_RWLOCK(cxgb3i_snic_rwlock); | ||
57 | |||
58 | /** | ||
59 | * cxgb3i_adpater_find_by_tdev - find the cxgb3i_adapter structure via t3cdev | ||
60 | * @tdev: t3cdev pointer | ||
61 | */ | ||
62 | struct cxgb3i_adapter *cxgb3i_adapter_find_by_tdev(struct t3cdev *tdev) | ||
63 | { | ||
64 | struct cxgb3i_adapter *snic; | ||
65 | |||
66 | read_lock(&cxgb3i_snic_rwlock); | ||
67 | list_for_each_entry(snic, &cxgb3i_snic_list, list_head) { | ||
68 | if (snic->tdev == tdev) { | ||
69 | read_unlock(&cxgb3i_snic_rwlock); | ||
70 | return snic; | ||
71 | } | ||
72 | } | ||
73 | read_unlock(&cxgb3i_snic_rwlock); | ||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | static inline int adapter_update(struct cxgb3i_adapter *snic) | ||
78 | { | ||
79 | cxgb3i_log_info("snic 0x%p, t3dev 0x%p, updating.\n", | ||
80 | snic, snic->tdev); | ||
81 | return cxgb3i_adapter_ddp_info(snic->tdev, &snic->tag_format, | ||
82 | &snic->tx_max_size, | ||
83 | &snic->rx_max_size); | ||
84 | } | ||
85 | |||
86 | static int adapter_add(struct cxgb3i_adapter *snic) | ||
87 | { | ||
88 | struct t3cdev *t3dev = snic->tdev; | ||
89 | struct adapter *adapter = tdev2adap(t3dev); | ||
90 | int i, err; | ||
91 | |||
92 | snic->pdev = adapter->pdev; | ||
93 | snic->tag_format.sw_bits = sw_tag_idx_bits + sw_tag_age_bits; | ||
94 | |||
95 | err = cxgb3i_adapter_ddp_info(t3dev, &snic->tag_format, | ||
96 | &snic->tx_max_size, | ||
97 | &snic->rx_max_size); | ||
98 | if (err < 0) | ||
99 | return err; | ||
100 | |||
101 | for_each_port(adapter, i) { | ||
102 | snic->hba[i] = cxgb3i_hba_host_add(snic, adapter->port[i]); | ||
103 | if (!snic->hba[i]) | ||
104 | return -EINVAL; | ||
105 | } | ||
106 | snic->hba_cnt = adapter->params.nports; | ||
107 | |||
108 | /* add to the list */ | ||
109 | write_lock(&cxgb3i_snic_rwlock); | ||
110 | list_add_tail(&snic->list_head, &cxgb3i_snic_list); | ||
111 | write_unlock(&cxgb3i_snic_rwlock); | ||
112 | |||
113 | cxgb3i_log_info("t3dev 0x%p open, snic 0x%p, %u scsi hosts added.\n", | ||
114 | t3dev, snic, snic->hba_cnt); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * cxgb3i_adapter_open - init a s3 adapter structure and any h/w settings | ||
120 | * @t3dev: t3cdev adapter | ||
121 | */ | ||
122 | void cxgb3i_adapter_open(struct t3cdev *t3dev) | ||
123 | { | ||
124 | struct cxgb3i_adapter *snic = cxgb3i_adapter_find_by_tdev(t3dev); | ||
125 | int err; | ||
126 | |||
127 | if (snic) | ||
128 | err = adapter_update(snic); | ||
129 | else { | ||
130 | snic = kzalloc(sizeof(*snic), GFP_KERNEL); | ||
131 | if (snic) { | ||
132 | spin_lock_init(&snic->lock); | ||
133 | snic->tdev = t3dev; | ||
134 | err = adapter_add(snic); | ||
135 | } else | ||
136 | err = -ENOMEM; | ||
137 | } | ||
138 | |||
139 | if (err < 0) { | ||
140 | cxgb3i_log_info("snic 0x%p, f 0x%x, t3dev 0x%p open, err %d.\n", | ||
141 | snic, snic ? snic->flags : 0, t3dev, err); | ||
142 | if (snic) { | ||
143 | snic->flags &= ~CXGB3I_ADAPTER_FLAG_RESET; | ||
144 | cxgb3i_adapter_close(t3dev); | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * cxgb3i_adapter_close - release the resources held and cleanup h/w settings | ||
151 | * @t3dev: t3cdev adapter | ||
152 | */ | ||
153 | void cxgb3i_adapter_close(struct t3cdev *t3dev) | ||
154 | { | ||
155 | struct cxgb3i_adapter *snic = cxgb3i_adapter_find_by_tdev(t3dev); | ||
156 | int i; | ||
157 | |||
158 | if (!snic || snic->flags & CXGB3I_ADAPTER_FLAG_RESET) { | ||
159 | cxgb3i_log_info("t3dev 0x%p close, snic 0x%p, f 0x%x.\n", | ||
160 | t3dev, snic, snic ? snic->flags : 0); | ||
161 | return; | ||
162 | } | ||
163 | |||
164 | /* remove from the list */ | ||
165 | write_lock(&cxgb3i_snic_rwlock); | ||
166 | list_del(&snic->list_head); | ||
167 | write_unlock(&cxgb3i_snic_rwlock); | ||
168 | |||
169 | for (i = 0; i < snic->hba_cnt; i++) { | ||
170 | if (snic->hba[i]) { | ||
171 | cxgb3i_hba_host_remove(snic->hba[i]); | ||
172 | snic->hba[i] = NULL; | ||
173 | } | ||
174 | } | ||
175 | cxgb3i_log_info("t3dev 0x%p close, snic 0x%p, %u scsi hosts removed.\n", | ||
176 | t3dev, snic, snic->hba_cnt); | ||
177 | kfree(snic); | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * cxgb3i_hba_find_by_netdev - find the cxgb3i_hba structure via net_device | ||
182 | * @t3dev: t3cdev adapter | ||
183 | */ | ||
184 | static struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev) | ||
185 | { | ||
186 | struct cxgb3i_adapter *snic; | ||
187 | int i; | ||
188 | |||
189 | if (ndev->priv_flags & IFF_802_1Q_VLAN) | ||
190 | ndev = vlan_dev_real_dev(ndev); | ||
191 | |||
192 | read_lock(&cxgb3i_snic_rwlock); | ||
193 | list_for_each_entry(snic, &cxgb3i_snic_list, list_head) { | ||
194 | for (i = 0; i < snic->hba_cnt; i++) { | ||
195 | if (snic->hba[i]->ndev == ndev) { | ||
196 | read_unlock(&cxgb3i_snic_rwlock); | ||
197 | return snic->hba[i]; | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | read_unlock(&cxgb3i_snic_rwlock); | ||
202 | return NULL; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * cxgb3i_hba_host_add - register a new host with scsi/iscsi | ||
207 | * @snic: the cxgb3i adapter | ||
208 | * @ndev: associated net_device | ||
209 | */ | ||
210 | struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *snic, | ||
211 | struct net_device *ndev) | ||
212 | { | ||
213 | struct cxgb3i_hba *hba; | ||
214 | struct Scsi_Host *shost; | ||
215 | int err; | ||
216 | |||
217 | shost = iscsi_host_alloc(&cxgb3i_host_template, | ||
218 | sizeof(struct cxgb3i_hba), 1); | ||
219 | if (!shost) { | ||
220 | cxgb3i_log_info("snic 0x%p, ndev 0x%p, host_alloc failed.\n", | ||
221 | snic, ndev); | ||
222 | return NULL; | ||
223 | } | ||
224 | |||
225 | shost->transportt = cxgb3i_scsi_transport; | ||
226 | shost->max_lun = CXGB3I_MAX_LUN; | ||
227 | shost->max_id = CXGB3I_MAX_TARGET; | ||
228 | shost->max_channel = 0; | ||
229 | shost->max_cmd_len = 16; | ||
230 | |||
231 | hba = iscsi_host_priv(shost); | ||
232 | hba->snic = snic; | ||
233 | hba->ndev = ndev; | ||
234 | hba->shost = shost; | ||
235 | |||
236 | pci_dev_get(snic->pdev); | ||
237 | err = iscsi_host_add(shost, &snic->pdev->dev); | ||
238 | if (err) { | ||
239 | cxgb3i_log_info("snic 0x%p, ndev 0x%p, host_add failed.\n", | ||
240 | snic, ndev); | ||
241 | goto pci_dev_put; | ||
242 | } | ||
243 | |||
244 | cxgb3i_api_debug("shost 0x%p, hba 0x%p, no %u.\n", | ||
245 | shost, hba, shost->host_no); | ||
246 | |||
247 | return hba; | ||
248 | |||
249 | pci_dev_put: | ||
250 | pci_dev_put(snic->pdev); | ||
251 | scsi_host_put(shost); | ||
252 | return NULL; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * cxgb3i_hba_host_remove - de-register the host with scsi/iscsi | ||
257 | * @hba: the cxgb3i hba | ||
258 | */ | ||
259 | void cxgb3i_hba_host_remove(struct cxgb3i_hba *hba) | ||
260 | { | ||
261 | cxgb3i_api_debug("shost 0x%p, hba 0x%p, no %u.\n", | ||
262 | hba->shost, hba, hba->shost->host_no); | ||
263 | iscsi_host_remove(hba->shost); | ||
264 | pci_dev_put(hba->snic->pdev); | ||
265 | iscsi_host_free(hba->shost); | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * cxgb3i_ep_connect - establish TCP connection to target portal | ||
270 | * @shost: scsi host to use | ||
271 | * @dst_addr: target IP address | ||
272 | * @non_blocking: blocking or non-blocking call | ||
273 | * | ||
274 | * Initiates a TCP/IP connection to the dst_addr | ||
275 | */ | ||
276 | static struct iscsi_endpoint *cxgb3i_ep_connect(struct Scsi_Host *shost, | ||
277 | struct sockaddr *dst_addr, | ||
278 | int non_blocking) | ||
279 | { | ||
280 | struct iscsi_endpoint *ep; | ||
281 | struct cxgb3i_endpoint *cep; | ||
282 | struct cxgb3i_hba *hba = NULL; | ||
283 | struct s3_conn *c3cn = NULL; | ||
284 | int err = 0; | ||
285 | |||
286 | if (shost) | ||
287 | hba = iscsi_host_priv(shost); | ||
288 | |||
289 | cxgb3i_api_debug("shost 0x%p, hba 0x%p.\n", shost, hba); | ||
290 | |||
291 | c3cn = cxgb3i_c3cn_create(); | ||
292 | if (!c3cn) { | ||
293 | cxgb3i_log_info("ep connect OOM.\n"); | ||
294 | err = -ENOMEM; | ||
295 | goto release_conn; | ||
296 | } | ||
297 | |||
298 | err = cxgb3i_c3cn_connect(hba ? hba->ndev : NULL, c3cn, | ||
299 | (struct sockaddr_in *)dst_addr); | ||
300 | if (err < 0) { | ||
301 | cxgb3i_log_info("ep connect failed.\n"); | ||
302 | goto release_conn; | ||
303 | } | ||
304 | |||
305 | hba = cxgb3i_hba_find_by_netdev(c3cn->dst_cache->dev); | ||
306 | if (!hba) { | ||
307 | err = -ENOSPC; | ||
308 | cxgb3i_log_info("NOT going through cxgbi device.\n"); | ||
309 | goto release_conn; | ||
310 | } | ||
311 | |||
312 | if (shost && hba != iscsi_host_priv(shost)) { | ||
313 | err = -ENOSPC; | ||
314 | cxgb3i_log_info("Could not connect through request host%u\n", | ||
315 | shost->host_no); | ||
316 | goto release_conn; | ||
317 | } | ||
318 | |||
319 | if (c3cn_is_closing(c3cn)) { | ||
320 | err = -ENOSPC; | ||
321 | cxgb3i_log_info("ep connect unable to connect.\n"); | ||
322 | goto release_conn; | ||
323 | } | ||
324 | |||
325 | ep = iscsi_create_endpoint(sizeof(*cep)); | ||
326 | if (!ep) { | ||
327 | err = -ENOMEM; | ||
328 | cxgb3i_log_info("iscsi alloc ep, OOM.\n"); | ||
329 | goto release_conn; | ||
330 | } | ||
331 | cep = ep->dd_data; | ||
332 | cep->c3cn = c3cn; | ||
333 | cep->hba = hba; | ||
334 | |||
335 | cxgb3i_api_debug("ep 0x%p, 0x%p, c3cn 0x%p, hba 0x%p.\n", | ||
336 | ep, cep, c3cn, hba); | ||
337 | return ep; | ||
338 | |||
339 | release_conn: | ||
340 | cxgb3i_api_debug("conn 0x%p failed, release.\n", c3cn); | ||
341 | if (c3cn) | ||
342 | cxgb3i_c3cn_release(c3cn); | ||
343 | return ERR_PTR(err); | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * cxgb3i_ep_poll - polls for TCP connection establishement | ||
348 | * @ep: TCP connection (endpoint) handle | ||
349 | * @timeout_ms: timeout value in milli secs | ||
350 | * | ||
351 | * polls for TCP connect request to complete | ||
352 | */ | ||
353 | static int cxgb3i_ep_poll(struct iscsi_endpoint *ep, int timeout_ms) | ||
354 | { | ||
355 | struct cxgb3i_endpoint *cep = ep->dd_data; | ||
356 | struct s3_conn *c3cn = cep->c3cn; | ||
357 | |||
358 | if (!c3cn_is_established(c3cn)) | ||
359 | return 0; | ||
360 | cxgb3i_api_debug("ep 0x%p, c3cn 0x%p established.\n", ep, c3cn); | ||
361 | return 1; | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * cxgb3i_ep_disconnect - teardown TCP connection | ||
366 | * @ep: TCP connection (endpoint) handle | ||
367 | * | ||
368 | * teardown TCP connection | ||
369 | */ | ||
370 | static void cxgb3i_ep_disconnect(struct iscsi_endpoint *ep) | ||
371 | { | ||
372 | struct cxgb3i_endpoint *cep = ep->dd_data; | ||
373 | struct cxgb3i_conn *cconn = cep->cconn; | ||
374 | |||
375 | cxgb3i_api_debug("ep 0x%p, cep 0x%p.\n", ep, cep); | ||
376 | |||
377 | if (cconn && cconn->conn) { | ||
378 | /* | ||
379 | * stop the xmit path so the xmit_pdu function is | ||
380 | * not being called | ||
381 | */ | ||
382 | iscsi_suspend_tx(cconn->conn); | ||
383 | |||
384 | write_lock_bh(&cep->c3cn->callback_lock); | ||
385 | cep->c3cn->user_data = NULL; | ||
386 | cconn->cep = NULL; | ||
387 | write_unlock_bh(&cep->c3cn->callback_lock); | ||
388 | } | ||
389 | |||
390 | cxgb3i_api_debug("ep 0x%p, cep 0x%p, release c3cn 0x%p.\n", | ||
391 | ep, cep, cep->c3cn); | ||
392 | cxgb3i_c3cn_release(cep->c3cn); | ||
393 | iscsi_destroy_endpoint(ep); | ||
394 | } | ||
395 | |||
396 | /** | ||
397 | * cxgb3i_session_create - create a new iscsi session | ||
398 | * @cmds_max: max # of commands | ||
399 | * @qdepth: scsi queue depth | ||
400 | * @initial_cmdsn: initial iscsi CMDSN for this session | ||
401 | * | ||
402 | * Creates a new iSCSI session | ||
403 | */ | ||
404 | static struct iscsi_cls_session * | ||
405 | cxgb3i_session_create(struct iscsi_endpoint *ep, u16 cmds_max, u16 qdepth, | ||
406 | u32 initial_cmdsn) | ||
407 | { | ||
408 | struct cxgb3i_endpoint *cep; | ||
409 | struct cxgb3i_hba *hba; | ||
410 | struct Scsi_Host *shost; | ||
411 | struct iscsi_cls_session *cls_session; | ||
412 | struct iscsi_session *session; | ||
413 | |||
414 | if (!ep) { | ||
415 | cxgb3i_log_error("%s, missing endpoint.\n", __func__); | ||
416 | return NULL; | ||
417 | } | ||
418 | |||
419 | cep = ep->dd_data; | ||
420 | hba = cep->hba; | ||
421 | shost = hba->shost; | ||
422 | cxgb3i_api_debug("ep 0x%p, cep 0x%p, hba 0x%p.\n", ep, cep, hba); | ||
423 | BUG_ON(hba != iscsi_host_priv(shost)); | ||
424 | |||
425 | cls_session = iscsi_session_setup(&cxgb3i_iscsi_transport, shost, | ||
426 | cmds_max, 0, | ||
427 | sizeof(struct iscsi_tcp_task) + | ||
428 | sizeof(struct cxgb3i_task_data), | ||
429 | initial_cmdsn, ISCSI_MAX_TARGET); | ||
430 | if (!cls_session) | ||
431 | return NULL; | ||
432 | session = cls_session->dd_data; | ||
433 | if (iscsi_tcp_r2tpool_alloc(session)) | ||
434 | goto remove_session; | ||
435 | |||
436 | return cls_session; | ||
437 | |||
438 | remove_session: | ||
439 | iscsi_session_teardown(cls_session); | ||
440 | return NULL; | ||
441 | } | ||
442 | |||
443 | /** | ||
444 | * cxgb3i_session_destroy - destroys iscsi session | ||
445 | * @cls_session: pointer to iscsi cls session | ||
446 | * | ||
447 | * Destroys an iSCSI session instance and releases its all resources held | ||
448 | */ | ||
449 | static void cxgb3i_session_destroy(struct iscsi_cls_session *cls_session) | ||
450 | { | ||
451 | cxgb3i_api_debug("sess 0x%p.\n", cls_session); | ||
452 | iscsi_tcp_r2tpool_free(cls_session->dd_data); | ||
453 | iscsi_session_teardown(cls_session); | ||
454 | } | ||
455 | |||
456 | /** | ||
457 | * cxgb3i_conn_max_xmit_dlength -- calc the max. xmit pdu segment size | ||
458 | * @conn: iscsi connection | ||
459 | * check the max. xmit pdu payload, reduce it if needed | ||
460 | */ | ||
461 | static inline int cxgb3i_conn_max_xmit_dlength(struct iscsi_conn *conn) | ||
462 | |||
463 | { | ||
464 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
465 | struct cxgb3i_conn *cconn = tcp_conn->dd_data; | ||
466 | unsigned int max = max(512 * MAX_SKB_FRAGS, SKB_TX_HEADROOM); | ||
467 | |||
468 | max = min(cconn->hba->snic->tx_max_size, max); | ||
469 | if (conn->max_xmit_dlength) | ||
470 | conn->max_xmit_dlength = min(conn->max_xmit_dlength, max); | ||
471 | else | ||
472 | conn->max_xmit_dlength = max; | ||
473 | align_pdu_size(conn->max_xmit_dlength); | ||
474 | cxgb3i_api_debug("conn 0x%p, max xmit %u.\n", | ||
475 | conn, conn->max_xmit_dlength); | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | /** | ||
480 | * cxgb3i_conn_max_recv_dlength -- check the max. recv pdu segment size | ||
481 | * @conn: iscsi connection | ||
482 | * return 0 if the value is valid, < 0 otherwise. | ||
483 | */ | ||
484 | static inline int cxgb3i_conn_max_recv_dlength(struct iscsi_conn *conn) | ||
485 | { | ||
486 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
487 | struct cxgb3i_conn *cconn = tcp_conn->dd_data; | ||
488 | unsigned int max = cconn->hba->snic->rx_max_size; | ||
489 | |||
490 | align_pdu_size(max); | ||
491 | if (conn->max_recv_dlength) { | ||
492 | if (conn->max_recv_dlength > max) { | ||
493 | cxgb3i_log_error("MaxRecvDataSegmentLength %u too big." | ||
494 | " Need to be <= %u.\n", | ||
495 | conn->max_recv_dlength, max); | ||
496 | return -EINVAL; | ||
497 | } | ||
498 | conn->max_recv_dlength = min(conn->max_recv_dlength, max); | ||
499 | align_pdu_size(conn->max_recv_dlength); | ||
500 | } else | ||
501 | conn->max_recv_dlength = max; | ||
502 | cxgb3i_api_debug("conn 0x%p, max recv %u.\n", | ||
503 | conn, conn->max_recv_dlength); | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * cxgb3i_conn_create - create iscsi connection instance | ||
509 | * @cls_session: pointer to iscsi cls session | ||
510 | * @cid: iscsi cid | ||
511 | * | ||
512 | * Creates a new iSCSI connection instance for a given session | ||
513 | */ | ||
514 | static struct iscsi_cls_conn *cxgb3i_conn_create(struct iscsi_cls_session | ||
515 | *cls_session, u32 cid) | ||
516 | { | ||
517 | struct iscsi_cls_conn *cls_conn; | ||
518 | struct iscsi_conn *conn; | ||
519 | struct iscsi_tcp_conn *tcp_conn; | ||
520 | struct cxgb3i_conn *cconn; | ||
521 | |||
522 | cxgb3i_api_debug("sess 0x%p, cid %u.\n", cls_session, cid); | ||
523 | |||
524 | cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*cconn), cid); | ||
525 | if (!cls_conn) | ||
526 | return NULL; | ||
527 | conn = cls_conn->dd_data; | ||
528 | tcp_conn = conn->dd_data; | ||
529 | cconn = tcp_conn->dd_data; | ||
530 | |||
531 | cconn->conn = conn; | ||
532 | return cls_conn; | ||
533 | } | ||
534 | |||
535 | /** | ||
536 | * cxgb3i_conn_bind - binds iscsi sess, conn and endpoint together | ||
537 | * @cls_session: pointer to iscsi cls session | ||
538 | * @cls_conn: pointer to iscsi cls conn | ||
539 | * @transport_eph: 64-bit EP handle | ||
540 | * @is_leading: leading connection on this session? | ||
541 | * | ||
542 | * Binds together an iSCSI session, an iSCSI connection and a | ||
543 | * TCP connection. This routine returns error code if the TCP | ||
544 | * connection does not belong on the device iSCSI sess/conn is bound | ||
545 | */ | ||
546 | |||
547 | static int cxgb3i_conn_bind(struct iscsi_cls_session *cls_session, | ||
548 | struct iscsi_cls_conn *cls_conn, | ||
549 | u64 transport_eph, int is_leading) | ||
550 | { | ||
551 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
552 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
553 | struct cxgb3i_conn *cconn = tcp_conn->dd_data; | ||
554 | struct cxgb3i_adapter *snic; | ||
555 | struct iscsi_endpoint *ep; | ||
556 | struct cxgb3i_endpoint *cep; | ||
557 | struct s3_conn *c3cn; | ||
558 | int err; | ||
559 | |||
560 | ep = iscsi_lookup_endpoint(transport_eph); | ||
561 | if (!ep) | ||
562 | return -EINVAL; | ||
563 | |||
564 | /* setup ddp pagesize */ | ||
565 | cep = ep->dd_data; | ||
566 | c3cn = cep->c3cn; | ||
567 | snic = cep->hba->snic; | ||
568 | err = cxgb3i_setup_conn_host_pagesize(snic->tdev, c3cn->tid, 0); | ||
569 | if (err < 0) | ||
570 | return err; | ||
571 | |||
572 | cxgb3i_api_debug("ep 0x%p, cls sess 0x%p, cls conn 0x%p.\n", | ||
573 | ep, cls_session, cls_conn); | ||
574 | |||
575 | err = iscsi_conn_bind(cls_session, cls_conn, is_leading); | ||
576 | if (err) | ||
577 | return -EINVAL; | ||
578 | |||
579 | /* calculate the tag idx bits needed for this conn based on cmds_max */ | ||
580 | cconn->task_idx_bits = (__ilog2_u32(conn->session->cmds_max - 1)) + 1; | ||
581 | cxgb3i_api_debug("session cmds_max 0x%x, bits %u.\n", | ||
582 | conn->session->cmds_max, cconn->task_idx_bits); | ||
583 | |||
584 | read_lock(&c3cn->callback_lock); | ||
585 | c3cn->user_data = conn; | ||
586 | cconn->hba = cep->hba; | ||
587 | cconn->cep = cep; | ||
588 | cep->cconn = cconn; | ||
589 | read_unlock(&c3cn->callback_lock); | ||
590 | |||
591 | cxgb3i_conn_max_xmit_dlength(conn); | ||
592 | cxgb3i_conn_max_recv_dlength(conn); | ||
593 | |||
594 | spin_lock_bh(&conn->session->lock); | ||
595 | sprintf(conn->portal_address, "%pI4", &c3cn->daddr.sin_addr.s_addr); | ||
596 | conn->portal_port = ntohs(c3cn->daddr.sin_port); | ||
597 | spin_unlock_bh(&conn->session->lock); | ||
598 | |||
599 | /* init recv engine */ | ||
600 | iscsi_tcp_hdr_recv_prep(tcp_conn); | ||
601 | |||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | /** | ||
606 | * cxgb3i_conn_get_param - return iscsi connection parameter to caller | ||
607 | * @cls_conn: pointer to iscsi cls conn | ||
608 | * @param: parameter type identifier | ||
609 | * @buf: buffer pointer | ||
610 | * | ||
611 | * returns iSCSI connection parameters | ||
612 | */ | ||
613 | static int cxgb3i_conn_get_param(struct iscsi_cls_conn *cls_conn, | ||
614 | enum iscsi_param param, char *buf) | ||
615 | { | ||
616 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
617 | int len; | ||
618 | |||
619 | cxgb3i_api_debug("cls_conn 0x%p, param %d.\n", cls_conn, param); | ||
620 | |||
621 | switch (param) { | ||
622 | case ISCSI_PARAM_CONN_PORT: | ||
623 | spin_lock_bh(&conn->session->lock); | ||
624 | len = sprintf(buf, "%hu\n", conn->portal_port); | ||
625 | spin_unlock_bh(&conn->session->lock); | ||
626 | break; | ||
627 | case ISCSI_PARAM_CONN_ADDRESS: | ||
628 | spin_lock_bh(&conn->session->lock); | ||
629 | len = sprintf(buf, "%s\n", conn->portal_address); | ||
630 | spin_unlock_bh(&conn->session->lock); | ||
631 | break; | ||
632 | default: | ||
633 | return iscsi_conn_get_param(cls_conn, param, buf); | ||
634 | } | ||
635 | |||
636 | return len; | ||
637 | } | ||
638 | |||
639 | /** | ||
640 | * cxgb3i_conn_set_param - set iscsi connection parameter | ||
641 | * @cls_conn: pointer to iscsi cls conn | ||
642 | * @param: parameter type identifier | ||
643 | * @buf: buffer pointer | ||
644 | * @buflen: buffer length | ||
645 | * | ||
646 | * set iSCSI connection parameters | ||
647 | */ | ||
648 | static int cxgb3i_conn_set_param(struct iscsi_cls_conn *cls_conn, | ||
649 | enum iscsi_param param, char *buf, int buflen) | ||
650 | { | ||
651 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
652 | struct iscsi_session *session = conn->session; | ||
653 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
654 | struct cxgb3i_conn *cconn = tcp_conn->dd_data; | ||
655 | struct cxgb3i_adapter *snic = cconn->hba->snic; | ||
656 | struct s3_conn *c3cn = cconn->cep->c3cn; | ||
657 | int value, err = 0; | ||
658 | |||
659 | switch (param) { | ||
660 | case ISCSI_PARAM_HDRDGST_EN: | ||
661 | err = iscsi_set_param(cls_conn, param, buf, buflen); | ||
662 | if (!err && conn->hdrdgst_en) | ||
663 | err = cxgb3i_setup_conn_digest(snic->tdev, c3cn->tid, | ||
664 | conn->hdrdgst_en, | ||
665 | conn->datadgst_en, 0); | ||
666 | break; | ||
667 | case ISCSI_PARAM_DATADGST_EN: | ||
668 | err = iscsi_set_param(cls_conn, param, buf, buflen); | ||
669 | if (!err && conn->datadgst_en) | ||
670 | err = cxgb3i_setup_conn_digest(snic->tdev, c3cn->tid, | ||
671 | conn->hdrdgst_en, | ||
672 | conn->datadgst_en, 0); | ||
673 | break; | ||
674 | case ISCSI_PARAM_MAX_R2T: | ||
675 | sscanf(buf, "%d", &value); | ||
676 | if (value <= 0 || !is_power_of_2(value)) | ||
677 | return -EINVAL; | ||
678 | if (session->max_r2t == value) | ||
679 | break; | ||
680 | iscsi_tcp_r2tpool_free(session); | ||
681 | err = iscsi_set_param(cls_conn, param, buf, buflen); | ||
682 | if (!err && iscsi_tcp_r2tpool_alloc(session)) | ||
683 | return -ENOMEM; | ||
684 | case ISCSI_PARAM_MAX_RECV_DLENGTH: | ||
685 | err = iscsi_set_param(cls_conn, param, buf, buflen); | ||
686 | if (!err) | ||
687 | err = cxgb3i_conn_max_recv_dlength(conn); | ||
688 | break; | ||
689 | case ISCSI_PARAM_MAX_XMIT_DLENGTH: | ||
690 | err = iscsi_set_param(cls_conn, param, buf, buflen); | ||
691 | if (!err) | ||
692 | err = cxgb3i_conn_max_xmit_dlength(conn); | ||
693 | break; | ||
694 | default: | ||
695 | return iscsi_set_param(cls_conn, param, buf, buflen); | ||
696 | } | ||
697 | return err; | ||
698 | } | ||
699 | |||
700 | /** | ||
701 | * cxgb3i_host_set_param - configure host (adapter) related parameters | ||
702 | * @shost: scsi host pointer | ||
703 | * @param: parameter type identifier | ||
704 | * @buf: buffer pointer | ||
705 | */ | ||
706 | static int cxgb3i_host_set_param(struct Scsi_Host *shost, | ||
707 | enum iscsi_host_param param, | ||
708 | char *buf, int buflen) | ||
709 | { | ||
710 | struct cxgb3i_hba *hba = iscsi_host_priv(shost); | ||
711 | |||
712 | if (!hba->ndev) { | ||
713 | shost_printk(KERN_ERR, shost, "Could not set host param. " | ||
714 | "Netdev for host not set.\n"); | ||
715 | return -ENODEV; | ||
716 | } | ||
717 | |||
718 | cxgb3i_api_debug("param %d, buf %s.\n", param, buf); | ||
719 | |||
720 | switch (param) { | ||
721 | case ISCSI_HOST_PARAM_IPADDRESS: | ||
722 | { | ||
723 | __be32 addr = in_aton(buf); | ||
724 | cxgb3i_set_private_ipv4addr(hba->ndev, addr); | ||
725 | return 0; | ||
726 | } | ||
727 | case ISCSI_HOST_PARAM_HWADDRESS: | ||
728 | case ISCSI_HOST_PARAM_NETDEV_NAME: | ||
729 | /* ignore */ | ||
730 | return 0; | ||
731 | default: | ||
732 | return iscsi_host_set_param(shost, param, buf, buflen); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | /** | ||
737 | * cxgb3i_host_get_param - returns host (adapter) related parameters | ||
738 | * @shost: scsi host pointer | ||
739 | * @param: parameter type identifier | ||
740 | * @buf: buffer pointer | ||
741 | */ | ||
742 | static int cxgb3i_host_get_param(struct Scsi_Host *shost, | ||
743 | enum iscsi_host_param param, char *buf) | ||
744 | { | ||
745 | struct cxgb3i_hba *hba = iscsi_host_priv(shost); | ||
746 | int len = 0; | ||
747 | |||
748 | if (!hba->ndev) { | ||
749 | shost_printk(KERN_ERR, shost, "Could not set host param. " | ||
750 | "Netdev for host not set.\n"); | ||
751 | return -ENODEV; | ||
752 | } | ||
753 | |||
754 | cxgb3i_api_debug("hba %s, param %d.\n", hba->ndev->name, param); | ||
755 | |||
756 | switch (param) { | ||
757 | case ISCSI_HOST_PARAM_HWADDRESS: | ||
758 | len = sysfs_format_mac(buf, hba->ndev->dev_addr, 6); | ||
759 | break; | ||
760 | case ISCSI_HOST_PARAM_NETDEV_NAME: | ||
761 | len = sprintf(buf, "%s\n", hba->ndev->name); | ||
762 | break; | ||
763 | case ISCSI_HOST_PARAM_IPADDRESS: | ||
764 | { | ||
765 | __be32 addr; | ||
766 | |||
767 | addr = cxgb3i_get_private_ipv4addr(hba->ndev); | ||
768 | len = sprintf(buf, "%pI4", &addr); | ||
769 | break; | ||
770 | } | ||
771 | default: | ||
772 | return iscsi_host_get_param(shost, param, buf); | ||
773 | } | ||
774 | return len; | ||
775 | } | ||
776 | |||
777 | /** | ||
778 | * cxgb3i_conn_get_stats - returns iSCSI stats | ||
779 | * @cls_conn: pointer to iscsi cls conn | ||
780 | * @stats: pointer to iscsi statistic struct | ||
781 | */ | ||
782 | static void cxgb3i_conn_get_stats(struct iscsi_cls_conn *cls_conn, | ||
783 | struct iscsi_stats *stats) | ||
784 | { | ||
785 | struct iscsi_conn *conn = cls_conn->dd_data; | ||
786 | |||
787 | stats->txdata_octets = conn->txdata_octets; | ||
788 | stats->rxdata_octets = conn->rxdata_octets; | ||
789 | stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; | ||
790 | stats->dataout_pdus = conn->dataout_pdus_cnt; | ||
791 | stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; | ||
792 | stats->datain_pdus = conn->datain_pdus_cnt; | ||
793 | stats->r2t_pdus = conn->r2t_pdus_cnt; | ||
794 | stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; | ||
795 | stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; | ||
796 | stats->digest_err = 0; | ||
797 | stats->timeout_err = 0; | ||
798 | stats->custom_length = 1; | ||
799 | strcpy(stats->custom[0].desc, "eh_abort_cnt"); | ||
800 | stats->custom[0].value = conn->eh_abort_cnt; | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * cxgb3i_parse_itt - get the idx and age bits from a given tag | ||
805 | * @conn: iscsi connection | ||
806 | * @itt: itt tag | ||
807 | * @idx: task index, filled in by this function | ||
808 | * @age: session age, filled in by this function | ||
809 | */ | ||
810 | static void cxgb3i_parse_itt(struct iscsi_conn *conn, itt_t itt, | ||
811 | int *idx, int *age) | ||
812 | { | ||
813 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
814 | struct cxgb3i_conn *cconn = tcp_conn->dd_data; | ||
815 | struct cxgb3i_adapter *snic = cconn->hba->snic; | ||
816 | u32 tag = ntohl((__force u32) itt); | ||
817 | u32 sw_bits; | ||
818 | |||
819 | sw_bits = cxgb3i_tag_nonrsvd_bits(&snic->tag_format, tag); | ||
820 | if (idx) | ||
821 | *idx = sw_bits & ((1 << cconn->task_idx_bits) - 1); | ||
822 | if (age) | ||
823 | *age = (sw_bits >> cconn->task_idx_bits) & ISCSI_AGE_MASK; | ||
824 | |||
825 | cxgb3i_tag_debug("parse tag 0x%x/0x%x, sw 0x%x, itt 0x%x, age 0x%x.\n", | ||
826 | tag, itt, sw_bits, idx ? *idx : 0xFFFFF, | ||
827 | age ? *age : 0xFF); | ||
828 | } | ||
829 | |||
830 | /** | ||
831 | * cxgb3i_reserve_itt - generate tag for a give task | ||
832 | * @task: iscsi task | ||
833 | * @hdr_itt: tag, filled in by this function | ||
834 | * Set up ddp for scsi read tasks if possible. | ||
835 | */ | ||
836 | int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt) | ||
837 | { | ||
838 | struct scsi_cmnd *sc = task->sc; | ||
839 | struct iscsi_conn *conn = task->conn; | ||
840 | struct iscsi_session *sess = conn->session; | ||
841 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | ||
842 | struct cxgb3i_conn *cconn = tcp_conn->dd_data; | ||
843 | struct cxgb3i_adapter *snic = cconn->hba->snic; | ||
844 | struct cxgb3i_tag_format *tformat = &snic->tag_format; | ||
845 | u32 sw_tag = (sess->age << cconn->task_idx_bits) | task->itt; | ||
846 | u32 tag; | ||
847 | int err = -EINVAL; | ||
848 | |||
849 | if (sc && | ||
850 | (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) && | ||
851 | cxgb3i_sw_tag_usable(tformat, sw_tag)) { | ||
852 | struct s3_conn *c3cn = cconn->cep->c3cn; | ||
853 | struct cxgb3i_gather_list *gl; | ||
854 | |||
855 | gl = cxgb3i_ddp_make_gl(scsi_in(sc)->length, | ||
856 | scsi_in(sc)->table.sgl, | ||
857 | scsi_in(sc)->table.nents, | ||
858 | snic->pdev, | ||
859 | GFP_ATOMIC); | ||
860 | if (gl) { | ||
861 | tag = sw_tag; | ||
862 | err = cxgb3i_ddp_tag_reserve(snic->tdev, c3cn->tid, | ||
863 | tformat, &tag, | ||
864 | gl, GFP_ATOMIC); | ||
865 | if (err < 0) | ||
866 | cxgb3i_ddp_release_gl(gl, snic->pdev); | ||
867 | } | ||
868 | } | ||
869 | |||
870 | if (err < 0) | ||
871 | tag = cxgb3i_set_non_ddp_tag(tformat, sw_tag); | ||
872 | /* the itt need to sent in big-endian order */ | ||
873 | *hdr_itt = (__force itt_t)htonl(tag); | ||
874 | |||
875 | cxgb3i_tag_debug("new tag 0x%x/0x%x (itt 0x%x, age 0x%x).\n", | ||
876 | tag, *hdr_itt, task->itt, sess->age); | ||
877 | return 0; | ||
878 | } | ||
879 | |||
880 | /** | ||
881 | * cxgb3i_release_itt - release the tag for a given task | ||
882 | * @task: iscsi task | ||
883 | * @hdr_itt: tag | ||
884 | * If the tag is a ddp tag, release the ddp setup | ||
885 | */ | ||
886 | void cxgb3i_release_itt(struct iscsi_task *task, itt_t hdr_itt) | ||
887 | { | ||
888 | struct scsi_cmnd *sc = task->sc; | ||
889 | struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data; | ||
890 | struct cxgb3i_conn *cconn = tcp_conn->dd_data; | ||
891 | struct cxgb3i_adapter *snic = cconn->hba->snic; | ||
892 | struct cxgb3i_tag_format *tformat = &snic->tag_format; | ||
893 | u32 tag = ntohl((__force u32)hdr_itt); | ||
894 | |||
895 | cxgb3i_tag_debug("release tag 0x%x.\n", tag); | ||
896 | |||
897 | if (sc && | ||
898 | (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) && | ||
899 | cxgb3i_is_ddp_tag(tformat, tag)) | ||
900 | cxgb3i_ddp_tag_release(snic->tdev, tag); | ||
901 | } | ||
902 | |||
903 | /** | ||
904 | * cxgb3i_host_template -- Scsi_Host_Template structure | ||
905 | * used when registering with the scsi mid layer | ||
906 | */ | ||
907 | static struct scsi_host_template cxgb3i_host_template = { | ||
908 | .module = THIS_MODULE, | ||
909 | .name = "Chelsio S3xx iSCSI Initiator", | ||
910 | .proc_name = "cxgb3i", | ||
911 | .queuecommand = iscsi_queuecommand, | ||
912 | .change_queue_depth = iscsi_change_queue_depth, | ||
913 | .can_queue = CXGB3I_SCSI_HOST_QDEPTH, | ||
914 | .sg_tablesize = SG_ALL, | ||
915 | .max_sectors = 0xFFFF, | ||
916 | .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, | ||
917 | .eh_abort_handler = iscsi_eh_abort, | ||
918 | .eh_device_reset_handler = iscsi_eh_device_reset, | ||
919 | .eh_target_reset_handler = iscsi_eh_recover_target, | ||
920 | .target_alloc = iscsi_target_alloc, | ||
921 | .use_clustering = DISABLE_CLUSTERING, | ||
922 | .this_id = -1, | ||
923 | }; | ||
924 | |||
925 | static struct iscsi_transport cxgb3i_iscsi_transport = { | ||
926 | .owner = THIS_MODULE, | ||
927 | .name = "cxgb3i", | ||
928 | .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST | ||
929 | | CAP_DATADGST | CAP_DIGEST_OFFLOAD | | ||
930 | CAP_PADDING_OFFLOAD, | ||
931 | .param_mask = ISCSI_MAX_RECV_DLENGTH | | ||
932 | ISCSI_MAX_XMIT_DLENGTH | | ||
933 | ISCSI_HDRDGST_EN | | ||
934 | ISCSI_DATADGST_EN | | ||
935 | ISCSI_INITIAL_R2T_EN | | ||
936 | ISCSI_MAX_R2T | | ||
937 | ISCSI_IMM_DATA_EN | | ||
938 | ISCSI_FIRST_BURST | | ||
939 | ISCSI_MAX_BURST | | ||
940 | ISCSI_PDU_INORDER_EN | | ||
941 | ISCSI_DATASEQ_INORDER_EN | | ||
942 | ISCSI_ERL | | ||
943 | ISCSI_CONN_PORT | | ||
944 | ISCSI_CONN_ADDRESS | | ||
945 | ISCSI_EXP_STATSN | | ||
946 | ISCSI_PERSISTENT_PORT | | ||
947 | ISCSI_PERSISTENT_ADDRESS | | ||
948 | ISCSI_TARGET_NAME | ISCSI_TPGT | | ||
949 | ISCSI_USERNAME | ISCSI_PASSWORD | | ||
950 | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | | ||
951 | ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | | ||
952 | ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | | ||
953 | ISCSI_PING_TMO | ISCSI_RECV_TMO | | ||
954 | ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, | ||
955 | .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | | ||
956 | ISCSI_HOST_INITIATOR_NAME | ISCSI_HOST_NETDEV_NAME, | ||
957 | .get_host_param = cxgb3i_host_get_param, | ||
958 | .set_host_param = cxgb3i_host_set_param, | ||
959 | /* session management */ | ||
960 | .create_session = cxgb3i_session_create, | ||
961 | .destroy_session = cxgb3i_session_destroy, | ||
962 | .get_session_param = iscsi_session_get_param, | ||
963 | /* connection management */ | ||
964 | .create_conn = cxgb3i_conn_create, | ||
965 | .bind_conn = cxgb3i_conn_bind, | ||
966 | .destroy_conn = iscsi_tcp_conn_teardown, | ||
967 | .start_conn = iscsi_conn_start, | ||
968 | .stop_conn = iscsi_conn_stop, | ||
969 | .get_conn_param = cxgb3i_conn_get_param, | ||
970 | .set_param = cxgb3i_conn_set_param, | ||
971 | .get_stats = cxgb3i_conn_get_stats, | ||
972 | /* pdu xmit req. from user space */ | ||
973 | .send_pdu = iscsi_conn_send_pdu, | ||
974 | /* task */ | ||
975 | .init_task = iscsi_tcp_task_init, | ||
976 | .xmit_task = iscsi_tcp_task_xmit, | ||
977 | .cleanup_task = cxgb3i_conn_cleanup_task, | ||
978 | |||
979 | /* pdu */ | ||
980 | .alloc_pdu = cxgb3i_conn_alloc_pdu, | ||
981 | .init_pdu = cxgb3i_conn_init_pdu, | ||
982 | .xmit_pdu = cxgb3i_conn_xmit_pdu, | ||
983 | .parse_pdu_itt = cxgb3i_parse_itt, | ||
984 | |||
985 | /* TCP connect/disconnect */ | ||
986 | .ep_connect = cxgb3i_ep_connect, | ||
987 | .ep_poll = cxgb3i_ep_poll, | ||
988 | .ep_disconnect = cxgb3i_ep_disconnect, | ||
989 | /* Error recovery timeout call */ | ||
990 | .session_recovery_timedout = iscsi_session_recovery_timedout, | ||
991 | }; | ||
992 | |||
993 | int cxgb3i_iscsi_init(void) | ||
994 | { | ||
995 | sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1; | ||
996 | sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1; | ||
997 | cxgb3i_log_info("tag itt 0x%x, %u bits, age 0x%x, %u bits.\n", | ||
998 | ISCSI_ITT_MASK, sw_tag_idx_bits, | ||
999 | ISCSI_AGE_MASK, sw_tag_age_bits); | ||
1000 | |||
1001 | cxgb3i_scsi_transport = | ||
1002 | iscsi_register_transport(&cxgb3i_iscsi_transport); | ||
1003 | if (!cxgb3i_scsi_transport) { | ||
1004 | cxgb3i_log_error("Could not register cxgb3i transport.\n"); | ||
1005 | return -ENODEV; | ||
1006 | } | ||
1007 | cxgb3i_api_debug("cxgb3i transport 0x%p.\n", cxgb3i_scsi_transport); | ||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | void cxgb3i_iscsi_cleanup(void) | ||
1012 | { | ||
1013 | if (cxgb3i_scsi_transport) { | ||
1014 | cxgb3i_api_debug("cxgb3i transport 0x%p.\n", | ||
1015 | cxgb3i_scsi_transport); | ||
1016 | iscsi_unregister_transport(&cxgb3i_iscsi_transport); | ||
1017 | } | ||
1018 | } | ||