Emulex Logo
OneCoreā„¢ Storage SDK Release 11.2
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ocs_xport.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011-2015, Emulex
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 /**
34  * @file
35  * iSCSI transport API
36  *
37  */
38 
39 #include "ocs.h"
40 #include "ocs_login.h"
41 
42 typedef struct ocs_xport_gen_arg_s {
43  ocs_xport_t *xport;
44  ocs_sem_t sem;
45  uint32_t status;
46  uint32_t ext_status;
48 
49 /**
50  * @brief allocate a transport object
51  *
52  * A transport object is allocated, and associated with a device instance.
53  *
54  * @param ocs pointer to device instance
55  *
56  * @return pointer to allocated transport object, or NULL if failed.
57  */
58 
59 ocs_xport_t *
60 ocs_xport_alloc(ocs_t *ocs)
61 {
62  ocs_xport_t *xport;
63 
64  ocs_assert(ocs, NULL);
65  xport = ocs_malloc(ocs, sizeof(*xport), OCS_M_ZERO);
66  if (xport != NULL) {
67  xport->ocs = ocs;
68  }
69  return xport;
70 }
71 
72 static void
73 ocs_xport_cxn_cb(void *ul_arg, int32_t status, uint32_t ext_status, void *params)
74 {
75  ocs_xport_gen_arg_t *arg = ul_arg;
76  arg->status = status;
77  arg->ext_status = ext_status;
78  ocs_sem_v(&arg->sem);
79 }
80 
81 
82 /**
83  * @brief Perform transport attach function
84  *
85  * Perform attach function, which for the FC transport makes a HAL call
86  * to bring up the link
87  *
88  * @param xport pointer to transport object
89  * @param cmd command to execute
90  *
91  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_PORT_ONLINE)
92  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_PORT_OFFLINE)
93  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_PORT_SHUTDOWN)
94  * ocs_xport_control(ocs_xport_t *xport, OCS_XPORT_POST_NODE_EVENT, ocs_node_t *node, ocs_sm_event_t, void *context)
95  *
96  * @return returns 0 for success, a negative error code value for failure.
97  */
98 
99 int32_t
100 ocs_xport_control(ocs_xport_t *xport, ocs_xport_ctrl_e cmd, ...)
101 {
102  int32_t rc = 0;
103  ocs_t *ocs;
104  va_list argp;
105  ocs_iscsi_t *iscsi;
106 
107  ocs_assert(xport, -1);
108  ocs = xport->ocs;
109  ocs_assert(xport->ocs, -1);
110  iscsi = &xport->ocs->iscsi;
111  ocs_assert(iscsi, -1);
112 
113  switch (cmd) {
114  case OCS_XPORT_CONNECT: {
115  ocs_hal_ip_address_t *remote_ip = NULL;
116  uint32_t remote_port = 0;
117  ocs_cxn_t *cxn = NULL;
118  ocs_cxn_t **cxn_arg = NULL;
119  ocs_xport_gen_arg_t *arg;
120  char ip_str[40];
121 
122  /* Retrieve arguments */
123  va_start(argp, cmd);
124  remote_ip = va_arg(argp, ocs_hal_ip_address_t*);
125  remote_port = va_arg(argp, uint32_t);
126  cxn_arg = va_arg(argp, ocs_cxn_t **);
127  va_end(argp);
128  ocs_assert(remote_ip, -1);
129  ocs_assert(remote_port, -1);
130 
132  remote_ip->ip_address,
133  ip_str, sizeof(ip_str));
134 
135  ocs_log_debug(iscsi->os, "CONNECT %s:%d\n", ip_str, remote_port);
136 
137  /*
138  * Reject the TCP connection if initiator functionality is not
139  * enabled.
140  */
141  if (!ocs->enable_ini) {
142  ocs_log_test(iscsi->os, "Reject connection request, no initiator functionality enabled\n");
143  return -1;
144  }
145 
146  arg = ocs_malloc(iscsi->os, sizeof(*arg), OCS_M_NOWAIT);
147  if (arg == NULL) {
148  ocs_log_err(ocs, "out of memory\n");
149  return -1;
150  }
151 
152  /* first allocate a connection
153  * this 'cxn' can have its own state machine, but we're not
154  * pushing it through the state machine. We're only using it
155  * to make HAL requests
156  */
157  cxn = ocs_cxn_alloc(iscsi, 0);
158  if (!cxn) {
159  ocs_log_err(iscsi->os, "Failed allocating a connection\n");
160  ocs_free(ocs, arg, sizeof(*arg));
161  return -1;
162  }
163 
164  ocs_sem_init(&arg->sem, 0, "connect");
165  ocs_snprintf(cxn->display_name,
166  sizeof(cxn->display_name),
167  "%s(%d)",
168  ip_str,
169  remote_port);
170 
171  cxn->no_idle_timer = TRUE;
172 
173  /* call HAL to make connection */
174  rc = ocs_hal_cxn_connect(iscsi->hal,
175  cxn,
176  remote_ip,
177  remote_port,
178  ocs->tcp_window_size,
179  ocs->tcp_window_scale,
181  arg);
182  if (rc == OCS_HAL_RTN_NO_RESOURCES) {
183  /* return error */
184  ocs_log_test(ocs, "connect failed, no resources\n");
185  ocs_sem_v(&arg->sem);
186  ocs_free(ocs, arg, sizeof(*arg));
187  } else if (rc) {
188  /* return error */
189  ocs_log_test(ocs, "connect failed, status=%d\n", rc);
190  ocs_sem_v(&arg->sem);
191  ocs_free(ocs, arg, sizeof(*arg));
192  } else {
193  /* wait till connection is complete */
194  rc = ocs_sem_p(&arg->sem, 10000000);
195  if (rc) {
196  ocs_log_test(ocs, "failed to connect within timeout\n");
197  /* Can't free the memory, the callback might still get invoked and use it
198  * TODO: add reference counting.
199  */
200  } else {
201  ocs_log_test(ocs, "done waiting connect, status=0x%x ext=0x%x\n",
202  arg->status, arg->ext_status);
203  rc = arg->status;
204  *cxn_arg = cxn;
205  ocs_free(ocs, arg, sizeof(*arg));
206  }
207  }
208 
209  /* If the connect failed we need to free the connection that
210  * we allocated.
211  */
212  if (rc) {
213  ocs_hal_cxn_free(iscsi->hal, cxn);
214  }
215 
216  break;
217  }
218  case OCS_XPORT_DISCONNECT: {
219  ocs_cxn_t *cxn = NULL;
220  uint32_t reset;
221  ocs_xport_gen_arg_t *arg;
222  ocs_io_t *io, *abort_io = NULL;
223  ocs_hal_rtn_e hal_rc;
224 
225  /* Retrieve arguments */
226  va_start(argp, cmd);
227  cxn = va_arg(argp, ocs_cxn_t*);
228  reset = va_arg(argp, uint32_t);
229  va_end(argp);
230  ocs_assert(cxn, -1);
231 
232  ocs_log_debug(iscsi->os, "DISCONNECT %s\n", cxn->display_name);
233 
234  /*
235  * If there are outstanding IOs, then we need to abort these
236  * before tearing down the connection.
237  */
238 
239  /* try to find one with a HAL IO allocated. */
240  ocs_node_lock(cxn);
241  ocs_list_foreach(&cxn->active_ios, io) {
242  if (io->hio != NULL) {
243  abort_io = io;
244  break;
245  }
246  }
247  ocs_node_unlock(cxn);
248  if (abort_io != NULL) {
249  ocs_log_debug(ocs, "Active IOs found, Issue abort on cxn\n");
250  hal_rc = ocs_hal_io_abort(iscsi->hal,
251  abort_io->hio,
254 
255  if (hal_rc != OCS_HAL_RTN_SUCCESS) {
256  ocs_log_test(ocs, "received rc %d aborting IOs\n", hal_rc);
257  } else {
258  uint32_t inters = 100; /* wait up to 1 second */
259  int32_t io_list_empty = FALSE;
260 
261  /* poll for IOs to complete */
262  do {
263  ocs_delay_usec(10000); // 10 ms
264  ocs_node_lock(cxn);
265  io_list_empty = ocs_list_empty(&cxn->active_ios);
266  ocs_node_unlock(cxn);
267  inters--;
268  } while(inters && !io_list_empty);
269  ocs_log_debug(ocs, "Active IOs list is %sEmpty\n",
270  (io_list_empty ? "" : "non-"));
271  }
272  } else {
273  ocs_log_debug(ocs, "No Active IOs found\n");
274  }
275 
276  hal_rc = ocs_iscsi_login_cxn_cleanup(cxn);
277  if (hal_rc != OCS_HAL_RTN_SUCCESS) {
278  ocs_log_test(ocs, "received rc %d for login cleanup\n", hal_rc);
279  }
280 
281 
282  /* now that the command aborts are in progress, close the connections. */
283  arg = ocs_malloc(ocs, sizeof(*arg), OCS_M_NOWAIT);
284  if (arg == NULL) {
285  ocs_log_err(ocs, "out of memory\n");
286  return -1;
287  }
288 
289  ocs_sem_init(&arg->sem, 0, "disconnect");
290 
291  /* call HAL to close connection */
292  rc = ocs_hal_cxn_close(iscsi->hal,
293  cxn,
294  reset,
296  arg);
297  if (rc == OCS_HAL_RTN_NO_RESOURCES) {
298  /* return error */
299  ocs_log_test(ocs, "disconnect failed, no resources\n");
300  ocs_sem_v(&arg->sem);
301  ocs_free(ocs, arg, sizeof(*arg));
302  } else if (rc) {
303  /* return error */
304  ocs_log_test(ocs, "disconnect failed, status=%d\n", rc);
305  ocs_sem_v(&arg->sem);
306  ocs_free(ocs, arg, sizeof(*arg));
307  } else {
308  /* wait till connection is complete */
309  rc = ocs_sem_p(&arg->sem, 10000000);
310  if (rc) {
311  ocs_log_test(ocs, "failed to disconnect within timeout\n");
312  /* Can't free the memory, the callback might still get invoked and use it
313  * TODO: add reference counting.
314  */
315  } else {
316  ocs_log_test(ocs, "done waiting for disconnect, status=0x%x ext=0x%x\n",
317  arg->status, arg->ext_status);
318  rc = arg->status;
319  ocs_free(ocs, arg, sizeof(*arg));
320  }
321  }
322  break;
323  }
324  case OCS_XPORT_UPLOAD: {
325  ocs_cxn_t *cxn = NULL;
326  ocs_xport_gen_arg_t *arg;
327  uint32_t reset;
328 
329  /* Retrieve arguments */
330  va_start(argp, cmd);
331  cxn = va_arg(argp, ocs_cxn_t*);
332  reset = va_arg(argp, uint32_t);
333  va_end(argp);
334  ocs_assert(cxn, -1);
335 
336  ocs_log_debug(iscsi->os, "UPLOAD %s\n", cxn->display_name);
337 
338  arg = ocs_malloc(ocs, sizeof(*arg), OCS_M_NOWAIT);
339  if (arg == NULL) {
340  ocs_log_err(ocs, "out of memory\n");
341  return -1;
342  }
343 
344  ocs_sem_init(&arg->sem, 0, "upload");
345 
346  /* call HAL to upload connection */
347  rc = ocs_hal_cxn_upload(iscsi->hal,
348  cxn,
349  !reset,
351  arg);
352  if (rc == OCS_HAL_RTN_NO_RESOURCES) {
353  /* return error */
354  ocs_log_test(ocs, "upload failed, no resources\n");
355  ocs_sem_v(&arg->sem);
356  ocs_free(ocs, arg, sizeof(*arg));
357  } else if (rc) {
358  /* return error */
359  ocs_log_test(ocs, "upload failed, status=%d\n", rc);
360  ocs_sem_v(&arg->sem);
361  ocs_free(ocs, arg, sizeof(*arg));
362  } else {
363  /* wait till connection is complete */
364  rc = ocs_sem_p(&arg->sem, 10000000);
365  if (rc) {
366  ocs_log_test(ocs, "failed to upload connection within timeout\n");
367  /* Can't free the memory, the callback might still get invoked and use it
368  * TODO: add reference counting.
369  */
370  } else {
371  ocs_log_test(ocs, "done waiting for upload, status=0x%x ext=0x%x\n",
372  arg->status, arg->ext_status);
373  rc = arg->status;
374  ocs_hal_cxn_free(iscsi->hal, cxn);
375  ocs_free(ocs, arg, sizeof(*arg));
376  }
377  }
378  break;
379  }
382 
383  /* Retrieve arguments */
384  va_start(argp, cmd);
385  cb = va_arg(argp, ocs_hal_unsol_cb_t);
386  va_end(argp);
387  ocs_assert(iscsi, -1);
388  ocs_assert(cb, -1);
389 
390  ocs_log_debug(iscsi->os, "REG_UNSOL_INI_CB\n");
391 
392  /*
393  * Reject the TCP connection if initiator functionality is not
394  * enabled.
395  */
396  if (!ocs->enable_ini) {
397  ocs_log_test(iscsi->os, "Reject function registration request, no initiator functionality enabled\n");
398  rc = -1;
399  } else {
400  /* set unsolicited initiator callback */
401  iscsi->unsol_ini = cb;
402  }
403  break;
404  }
405  default:
406  break;
407  }
408  return rc;
409 }
410 
411 /**
412  * @brief Return status on a link.
413  *
414  * @par Description
415  * Returns status information about a link.
416  *
417  * @param xport Pointer to transport object.
418  * @param cmd Command to execute.
419  * @param result Pointer to result value.
420  *
421  * ocs_xport_status(ocs_xport_t *xport, OCS_XPORT_PORT_STATUS)
422  *
423  * @return Returns 0 on success, or a negative error code value on failure.
424  */
425 int32_t
426 ocs_xport_status(ocs_xport_t *xport, ocs_xport_status_e cmd,
427  ocs_xport_stats_t *result)
428 {
429  int32_t rc = -1;
430  ocs_t *ocs = NULL;
431  ocs_xport_stats_t value;
432  ocs_hal_rtn_e hal_rc;
433 
434  ocs_assert(xport, -1);
435  ocs_assert(xport->ocs, -1);
436 
437  ocs = xport->ocs;
438 
439  switch (cmd) {
441  ocs_assert(result, -1);
442 
443  /* Determine port status based on link speed */
444  hal_rc = ocs_hal_get(&(ocs->hal), OCS_HAL_LINK_SPEED,
445  &value.value);
446  if (hal_rc == OCS_HAL_RTN_SUCCESS) {
447  if (value.value == 0) {
448  result->value = 0;
449  } else {
450  result->value = 1;
451  }
452  rc = 0;
453  }
454  break;
455  }
456 
457  return rc;
458 }
459 
460 /**
461  * @brief free a transport object
462  *
463  * The transport object is free'd
464  *
465  * @param xport pointer to transport object
466  *
467  * @return none
468  */
469 
470 void
471 ocs_xport_free(ocs_xport_t *xport)
472 {
473  if (xport) {
474  ocs_free(xport->ocs, xport, sizeof(*xport));
475  }
476 }
ocs_hal_rtn_e ocs_hal_cxn_connect(ocs_hal_t *hal, ocs_cxn_t *cxn, ocs_hal_ip_address_t *ipaddr, uint32_t tcp_port, uint16_t window_size, uint8_t window_scale, cxn_cb_t cxn_cb, void *ul_arg)
Establish a TCP connection from an initiator to a target.
Definition: ocs_hal.c:4551
ocs_hal_rtn_e ocs_hal_cxn_close(ocs_hal_t *hal, ocs_cxn_t *cxn, uint8_t sendRst, cxn_cb_t cxn_cb, void *ul_arg)
Invalidates a connection and optionally sends a TCP Reset.
Definition: ocs_hal.c:5038
ocs_hal_t * hal
Definition: ocs_iscsi.h:295
static int32_t ocs_list_empty(ocs_list_t *list)
Test if a list is empty.
Definition: ocs_list.h:125
uint8_t ip_address[16]
Definition: ocs_hal.h:261
ocs_cxn_t * ocs_cxn_alloc(ocs_iscsi_t *iscsi, uint32_t conn_hdl)
Allocates a connection and initializes the fields as required.
Definition: ocs_cxn.c:1666
ocs_hal_rtn_e
defines common HAL return statuses.
Definition: ocs_hal.h:78
int32_t ocs_xport_control(ocs_xport_t *xport, ocs_xport_ctrl_e cmd,...)
Perform transport attach function.
Definition: ocs_xport.c:100
ocs_ip_type_e ocs_iscsi_convert_to_ip_type(ocs_hal_ip_type_e ip_type)
Convert an HAL IP type to the iSCSI API value.
Definition: ocs_iscsi.c:327
ocs_hal_rtn_e ocs_hal_io_abort(ocs_hal_t *hal, ocs_hal_io_t *io, ocs_hal_abort_scope_e abort_scope, ocs_hal_abort_type_e abort_type)
Abort a currently running IO.
Definition: ocs_hal.c:6148
ocs_xport_t * xport
Definition: ocs_xport.c:43
uint32_t value
Definition: ocs_xport.h:59
int32_t(* ocs_hal_unsol_cb_t)(void *, ocs_cxn_t *, ocs_hal_rq_buffer_t *, uint32_t, ocs_hal_rq_buffer_t *, uint32_t, uint8_t)
HAL unsolicited callback.
Definition: ocs_hal.h:719
ocs_hal_rtn_e ocs_hal_get(ocs_hal_t *hal, ocs_hal_property_e prop, uint32_t *value)
Definition: ocs_hal.c:2666
iSCSI object
Definition: ocs_iscsi.h:293
ocs_hal_ip_type_e ip_type
Definition: ocs_hal.h:260
ocs_os_handle_t os
Definition: ocs_iscsi.h:294
ocs_xport_ctrl_e
Definition: ocs_xport.h:46
ocs_hal_rtn_e ocs_hal_cxn_free(ocs_hal_t *hal, ocs_cxn_t *cxn)
Frees a connection object for re-use.
Definition: ocs_hal.c:5141
ocs_xport_status_e
Definition: ocs_xport.h:54
static void ocs_xport_cxn_cb(void *ul_arg, int32_t status, uint32_t ext_status, void *params)
Definition: ocs_xport.c:73
void format_ip_address(ocs_ip_type_e ip_type, uint8_t *address, char *buffer, int size)
Format an IP address into a string.
Definition: ocs_iscsi.c:460
structure wrapping an IP address together with it's type.
Definition: ocs_hal.h:259
static void ocs_node_lock(ocs_node_t *node)
Definition: ocs_common.h:432
ocs_hal_unsol_cb_t unsol_ini
Definition: ocs_iscsi.h:339
static void ocs_node_unlock(ocs_node_t *node)
Definition: ocs_common.h:437
void ocs_xport_free(ocs_xport_t *xport)
free a transport object
Definition: ocs_xport.c:471
#define ocs_assert(cond,...)
Definition: ocs_debug.h:163
int32_t ocs_xport_status(ocs_xport_t *xport, ocs_xport_status_e cmd, ocs_xport_stats_t *result)
Return status on a link.
Definition: ocs_xport.c:426
#define ocs_list_foreach(list, item)
Iterate a linked list.
Definition: ocs_list.h:428
ocs_xport_t * ocs_xport_alloc(ocs_t *ocs)
allocate a transport object
Definition: ocs_xport.c:60
ocs_hal_rtn_e ocs_iscsi_login_cxn_cleanup(ocs_cxn_t *cxn)
Frees any retry objects for the connection.
Definition: ocs_login.c:277
uint32_t ext_status
Definition: ocs_xport.c:46
ocs_hal_rtn_e ocs_hal_cxn_upload(ocs_hal_t *hal, ocs_cxn_t *cxn, uint8_t graceful, cxn_cb_t cxn_cb, void *ul_arg)
uploads an incoming connection on a target.
Definition: ocs_hal.c:5096