1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Copyright 2014-2015 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _IDM_IMPL_H_
#define _IDM_IMPL_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/avl.h>
#include <sys/socket_impl.h>
#include <sys/taskq_impl.h>
/*
* IDM lock order:
*
* idm_taskid_table_lock, idm_task_t.idt_mutex
*/
#define CF_LOGIN_READY 0x00000001
#define CF_INITIAL_LOGIN 0x00000002
#define CF_ERROR 0x80000000
typedef enum {
CONN_TYPE_INI = 1,
CONN_TYPE_TGT
} idm_conn_type_t;
/*
* Watchdog interval in seconds
*/
#define IDM_WD_INTERVAL 5
/*
* Timeout period before the client "keepalive" callback is invoked in
* seconds if the connection is idle.
*/
#define IDM_TRANSPORT_KEEPALIVE_IDLE_TIMEOUT 20
/*
* Timeout period before a TRANSPORT_FAIL event is generated in seconds
* if the connection is idle.
*/
#define IDM_TRANSPORT_FAIL_IDLE_TIMEOUT 30
/*
* IDM reference count structure. Audit code is shamelessly adapted
* from CIFS server.
*/
#define REFCNT_AUDIT_STACK_DEPTH 16
#define REFCNT_AUDIT_BUF_MAX_REC 16
typedef struct {
uint32_t anr_refcnt;
int anr_depth;
pc_t anr_stack[REFCNT_AUDIT_STACK_DEPTH];
} refcnt_audit_record_t;
typedef struct {
int anb_index;
int anb_max_index;
refcnt_audit_record_t anb_records[REFCNT_AUDIT_BUF_MAX_REC];
} refcnt_audit_buf_t;
#define REFCNT_AUDIT(_rf_) { \
refcnt_audit_record_t *anr; \
\
anr = (_rf_)->ir_audit_buf.anb_records; \
anr += (_rf_)->ir_audit_buf.anb_index; \
(_rf_)->ir_audit_buf.anb_index++; \
(_rf_)->ir_audit_buf.anb_index &= \
(_rf_)->ir_audit_buf.anb_max_index; \
anr->anr_refcnt = (_rf_)->ir_refcnt; \
anr->anr_depth = getpcstack(anr->anr_stack, \
REFCNT_AUDIT_STACK_DEPTH); \
}
struct idm_refcnt_s;
typedef void (idm_refcnt_cb_t)(void *ref_obj);
typedef enum {
REF_NOWAIT,
REF_WAIT_SYNC,
REF_WAIT_ASYNC
} idm_refcnt_wait_t;
typedef struct idm_refcnt_s {
int ir_refcnt;
void *ir_referenced_obj;
idm_refcnt_wait_t ir_waiting;
kmutex_t ir_mutex;
kcondvar_t ir_cv;
idm_refcnt_cb_t *ir_cb;
refcnt_audit_buf_t ir_audit_buf;
} idm_refcnt_t;
/*
* connection parameters - These parameters would be populated at
* connection create, or during key-value negotiation at login
*/
typedef struct idm_conn_params_s {
uint32_t max_recv_dataseglen;
uint32_t max_xmit_dataseglen;
uint32_t conn_login_max;
uint32_t conn_login_interval;
boolean_t nonblock_socket;
} idm_conn_param_t;
typedef struct idm_svc_s {
list_node_t is_list_node;
kmutex_t is_mutex;
kcondvar_t is_cv;
kmutex_t is_count_mutex;
kcondvar_t is_count_cv;
idm_refcnt_t is_refcnt;
int is_online;
/* transport-specific service components */
void *is_so_svc;
void *is_iser_svc;
idm_svc_req_t is_svc_req;
} idm_svc_t;
#define ISCSI_MAX_TSIH_LEN 6 /* 0x%04x */
#define ISCSI_MAX_ISID_LEN ISCSI_ISID_LEN * 2
typedef struct idm_conn_s {
list_node_t ic_list_node;
void *ic_handle;
idm_refcnt_t ic_refcnt;
idm_svc_t *ic_svc_binding; /* Target conn. only */
idm_sockaddr_t ic_ini_dst_addr;
struct sockaddr_storage ic_laddr; /* conn local address */
struct sockaddr_storage ic_raddr; /* conn remote address */
/*
* the target_name, initiator_name, initiator session
* identifier and target session identifying handle
* are only used for target connections.
*/
char ic_target_name[ISCSI_MAX_NAME_LEN + 1];
char ic_initiator_name[ISCSI_MAX_NAME_LEN + 1];
char ic_tsih[ISCSI_MAX_TSIH_LEN + 1];
char ic_isid[ISCSI_MAX_ISID_LEN + 1];
idm_conn_state_t ic_state;
idm_conn_state_t ic_last_state;
sm_audit_buf_t ic_state_audit;
kmutex_t ic_state_mutex;
kcondvar_t ic_state_cv;
uint32_t ic_state_flags;
timeout_id_t ic_state_timeout;
struct idm_conn_s *ic_reinstate_conn; /* For conn reinst. */
struct idm_conn_s *ic_logout_conn; /* For other conn logout */
taskq_t *ic_state_taskq;
int ic_pdu_events;
boolean_t ic_login_info_valid;
boolean_t ic_rdma_extensions;
uint16_t ic_login_cid;
kmutex_t ic_mutex;
kcondvar_t ic_cv;
idm_status_t ic_conn_sm_status;
boolean_t ic_ffp;
boolean_t ic_keepalive;
uint32_t ic_internal_cid;
uint32_t ic_conn_flags;
idm_conn_type_t ic_conn_type;
idm_conn_ops_t ic_conn_ops;
idm_transport_ops_t *ic_transport_ops;
idm_transport_type_t ic_transport_type;
int ic_transport_hdrlen;
void *ic_transport_private;
idm_conn_param_t ic_conn_params;
/*
* Save client callback to interpose idm callback
*/
idm_pdu_cb_t *ic_client_callback;
clock_t ic_timestamp;
} idm_conn_t;
#define IDM_CONN_HEADER_DIGEST 0x00000001
#define IDM_CONN_DATA_DIGEST 0x00000002
#define IDM_CONN_USE_SCOREBOARD 0x00000004
#define IDM_CONN_ISINI(ICI_IC) ((ICI_IC)->ic_conn_type == CONN_TYPE_INI)
#define IDM_CONN_ISTGT(ICI_IC) ((ICI_IC)->ic_conn_type == CONN_TYPE_TGT)
/*
* An IDM target task can transfer data using multiple buffers. The task
* will maintain a list of buffers, and each buffer will contain the relative
* offset of the transfer and a pointer to the next buffer in the list.
*
* Note on client private data:
* idt_private is intended to be a pointer to some sort of client-
* specific state.
*
* idt_client_handle is a more generic client-private piece of data that can
* be used by the client for the express purpose of task lookup. The driving
* use case for this is for the client to store the initiator task tag for
* a given task so that it may be more easily retrieved for task management.
*
* The key take away here is that clients should never call
* idm_task_find_by_handle in the performance path.
*
* An initiator will require only one buffer per task, the offset will be 0.
*/
typedef struct idm_task_s {
idm_conn_t *idt_ic; /* Associated connection */
/* connection type is in idt_ic->ic_conn_type */
kmutex_t idt_mutex;
void *idt_private; /* Client private data */
uintptr_t idt_client_handle; /* Client private */
uint32_t idt_tt; /* Task tag */
uint32_t idt_r2t_ttt; /* R2T Target Task tag */
idm_task_state_t idt_state;
idm_refcnt_t idt_refcnt;
/*
* Statistics
*/
int idt_tx_to_ini_start;
int idt_tx_to_ini_done;
int idt_rx_from_ini_start;
int idt_rx_from_ini_done;
int idt_tx_bytes; /* IDM_CONN_USE_SCOREBOARD */
int idt_rx_bytes; /* IDM_CONN_USE_SCOREBOARD */
uint32_t idt_exp_datasn; /* expected datasn */
uint32_t idt_exp_rttsn; /* expected rttsn */
list_t idt_inbufv; /* chunks of IN buffers */
list_t idt_outbufv; /* chunks of OUT buffers */
/*
* Transport header, which describes this tasks remote tagged buffer
*/
int idt_transport_hdrlen;
void *idt_transport_hdr;
uint32_t idt_flags; /* phase collapse */
} idm_task_t;
int idm_task_constructor(void *task_void, void *arg, int flags);
void idm_task_destructor(void *task_void, void *arg);
#define IDM_TASKIDS_MAX 16384
#define IDM_BUF_MAGIC 0x49425546 /* "IBUF" */
#define IDM_TASK_PHASECOLLAPSE_REQ 0x00000001 /* request phase collapse */
#define IDM_TASK_PHASECOLLAPSE_SUCCESS 0x00000002 /* phase collapse success */
/* Protect with task mutex */
typedef struct idm_buf_s {
uint32_t idb_magic; /* "IBUF" */
/*
* Note: idm_tx_link *must* be the second element in the list for
* proper TX PDU ordering.
*/
list_node_t idm_tx_link; /* link in a list of TX objects */
list_node_t idb_buflink; /* link in a multi-buffer data xfer */
idm_conn_t *idb_ic; /* Associated connection */
void *idb_buf; /* data */
uint64_t idb_buflen; /* length of buffer */
size_t idb_bufoffset; /* offset in a multi-buffer xfer */
boolean_t idb_bufalloc; /* true if alloc'd in idm_buf_alloc */
/*
* DataPDUInOrder=Yes, so to track that the PDUs in a sequence are sent
* in continuously increasing address order, check that offsets for a
* single buffer xfer are in order.
*/
uint32_t idb_exp_offset;
size_t idb_xfer_len; /* Current requested xfer len */
void *idb_buf_private; /* transport-specific buf handle */
void *idb_reg_private; /* transport-specific reg handle */
void *idb_bufptr; /* transport-specific bcopy pointer */
boolean_t idb_bufbcopy; /* true if bcopy required */
idm_buf_cb_t *idb_buf_cb; /* Data Completion Notify, tgt only */
void *idb_cb_arg; /* Client private data */
idm_task_t *idb_task_binding;
timespec_t idb_xfer_start;
timespec_t idb_xfer_done;
boolean_t idb_in_transport;
boolean_t idb_tx_thread; /* Sockets only */
iscsi_hdr_t idb_data_hdr_tmpl; /* Sockets only */
idm_status_t idb_status;
} idm_buf_t;
typedef enum {
BP_CHECK_QUICK,
BP_CHECK_THOROUGH,
BP_CHECK_ASSERT
} idm_bufpat_check_type_t;
#define BUFPAT_MATCH(bc_bufpat, bc_idb) \
((bufpat->bufpat_idb == bc_idb) && \
(bufpat->bufpat_bufmagic == IDM_BUF_MAGIC))
typedef struct idm_bufpat_s {
void *bufpat_idb;
uint32_t bufpat_bufmagic;
uint32_t bufpat_offset;
} idm_bufpat_t;
#define PDU_MAX_IOVLEN 12
#define IDM_PDU_MAGIC 0x49504455 /* "IPDU" */
typedef struct idm_pdu_s {
uint32_t isp_magic; /* "IPDU" */
/*
* Internal - Order is vital. idm_tx_link *must* be the second
* element in this structure for proper TX PDU ordering.
*/
list_node_t idm_tx_link;
list_node_t isp_client_lnd;
idm_conn_t *isp_ic; /* Must be set */
iscsi_hdr_t *isp_hdr;
uint_t isp_hdrlen;
uint8_t *isp_data;
uint_t isp_datalen;
/* Transport header */
void *isp_transport_hdr;
uint32_t isp_transport_hdrlen;
void *isp_transport_private;
/*
* isp_data is used for sending SCSI status, NOP, text, scsi and
* non-scsi data. Data is received using isp_iov and isp_iovlen
* to support data over multiple buffers.
*/
void *isp_private;
idm_pdu_cb_t *isp_callback;
idm_status_t isp_status;
/*
* The following four elements are only used in
* idm_sorecv_scsidata() currently.
*/
struct iovec isp_iov[PDU_MAX_IOVLEN];
int isp_iovlen;
idm_buf_t *isp_sorx_buf;
/* Implementation data for idm_pdu_alloc and sorx PDU cache */
uint32_t isp_flags;
uint_t isp_hdrbuflen;
uint_t isp_databuflen;
hrtime_t isp_queue_time;
/* Taskq dispatching state for deferred PDU */
taskq_ent_t isp_tqent;
} idm_pdu_t;
/*
* This "generic" object is used when removing an item from the ic_tx_list
* in order to determine whether it's an idm_pdu_t or an idm_buf_t
*/
typedef struct {
uint32_t idm_tx_obj_magic;
/*
* idm_tx_link *must* be the second element in this structure.
*/
list_node_t idm_tx_link;
} idm_tx_obj_t;
#define IDM_PDU_OPCODE(PDU) \
((PDU)->isp_hdr->opcode & ISCSI_OPCODE_MASK)
#define IDM_PDU_ALLOC 0x00000001
#define IDM_PDU_ADDL_HDR 0x00000002
#define IDM_PDU_ADDL_DATA 0x00000004
#define IDM_PDU_LOGIN_TX 0x00000008
#define IDM_PDU_SET_STATSN 0x00000010
#define IDM_PDU_ADVANCE_STATSN 0x00000020
#define OSD_EXT_CDB_AHSLEN (200 - 15)
#define BIDI_AHS_LENGTH 5
/*
* Additional Header Segment (AHS)
* AHS is only valid for SCSI Requests and contains SCSI CDB information
* which doesn't fit in the standard 16 byte area of the PDU. Commonly
* this only holds true for OSD device commands.
*
* IDM_SORX_CACHE_ASHLEN is the amount of memory which is preallocated in bytes.
* When used in the header the AHS length is stored as the number of 4-byte
* words; so IDM_SORX_WIRE_ASHLEN is IDM_SORX_CACHE_ASHLEN in words.
*/
#define IDM_SORX_CACHE_AHSLEN \
((OSD_EXT_CDB_AHSLEN + 3) + (BIDI_AHS_LENGTH + 3))
#define IDM_SORX_WIRE_AHSLEN (IDM_SORX_CACHE_AHSLEN / sizeof (uint32_t))
#define IDM_SORX_CACHE_HDRLEN (sizeof (iscsi_hdr_t) + IDM_SORX_CACHE_AHSLEN)
/*
* ID pool
*/
#define IDM_IDPOOL_MAGIC 0x4944504C /* IDPL */
#define IDM_IDPOOL_MIN_SIZE 64 /* Number of IDs to begin with */
#define IDM_IDPOOL_MAX_SIZE 64 * 1024
typedef struct idm_idpool {
uint32_t id_magic;
kmutex_t id_mutex;
uint8_t *id_pool;
uint32_t id_size;
uint8_t id_bit;
uint8_t id_bit_idx;
uint32_t id_idx;
uint32_t id_idx_msk;
uint32_t id_free_counter;
uint32_t id_max_free_counter;
} idm_idpool_t;
/*
* Global IDM state structure
*/
typedef struct {
kmutex_t idm_global_mutex;
taskq_t *idm_global_taskq;
kthread_t *idm_wd_thread;
kt_did_t idm_wd_thread_did;
boolean_t idm_wd_thread_running;
kcondvar_t idm_wd_cv;
list_t idm_tgt_svc_list;
kcondvar_t idm_tgt_svc_cv;
list_t idm_tgt_conn_list;
int idm_tgt_conn_count;
list_t idm_ini_conn_list;
kmem_cache_t *idm_buf_cache;
kmem_cache_t *idm_task_cache;
krwlock_t idm_taskid_table_lock;
idm_task_t **idm_taskid_table;
uint32_t idm_taskid_next;
uint32_t idm_taskid_max;
idm_idpool_t idm_conn_id_pool;
kmem_cache_t *idm_sotx_pdu_cache;
kmem_cache_t *idm_sorx_pdu_cache;
kmem_cache_t *idm_so_128k_buf_cache;
} idm_global_t;
idm_global_t idm; /* Global state */
int
idm_idpool_create(idm_idpool_t *pool);
void
idm_idpool_destroy(idm_idpool_t *pool);
int
idm_idpool_alloc(idm_idpool_t *pool, uint16_t *id);
void
idm_idpool_free(idm_idpool_t *pool, uint16_t id);
void
idm_pdu_rx(idm_conn_t *ic, idm_pdu_t *pdu);
void
idm_pdu_tx_forward(idm_conn_t *ic, idm_pdu_t *pdu);
boolean_t
idm_pdu_rx_forward_ffp(idm_conn_t *ic, idm_pdu_t *pdu);
void
idm_pdu_rx_forward(idm_conn_t *ic, idm_pdu_t *pdu);
void
idm_pdu_tx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu);
void
idm_pdu_rx_protocol_error(idm_conn_t *ic, idm_pdu_t *pdu);
void idm_parse_login_rsp(idm_conn_t *ic, idm_pdu_t *logout_req_pdu,
boolean_t rx);
void idm_parse_logout_req(idm_conn_t *ic, idm_pdu_t *logout_req_pdu,
boolean_t rx);
void idm_parse_logout_rsp(idm_conn_t *ic, idm_pdu_t *login_rsp_pdu,
boolean_t rx);
idm_status_t idm_svc_conn_create(idm_svc_t *is, idm_transport_type_t type,
idm_conn_t **ic_result);
void idm_svc_conn_destroy(idm_conn_t *ic);
idm_status_t idm_ini_conn_finish(idm_conn_t *ic);
idm_status_t idm_tgt_conn_finish(idm_conn_t *ic);
idm_conn_t *idm_conn_create_common(idm_conn_type_t conn_type,
idm_transport_type_t tt, idm_conn_ops_t *conn_ops);
void idm_conn_destroy_common(idm_conn_t *ic);
void idm_conn_close(idm_conn_t *ic);
uint32_t idm_cid_alloc(void);
void idm_cid_free(uint32_t cid);
uint32_t idm_crc32c(void *address, unsigned long length);
uint32_t idm_crc32c_continued(void *address, unsigned long length,
uint32_t crc);
void idm_listbuf_insert(list_t *lst, idm_buf_t *buf);
idm_conn_t *idm_lookup_conn(uint8_t *isid, uint16_t tsih, uint16_t cid);
#ifdef __cplusplus
}
#endif
#endif /* _IDM_IMPL_H_ */
|