Newer
Older
/* bind iSCSI connection and socket */
tcp_conn->sock = sock;
/* setup Socket parameters */
sk = sock->sk;
sk->sk_reuse = 1;
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
/* FIXME: disable Nagle's algorithm */
/*
* Intercept TCP callbacks for sendfile like receive
* processing.
*/
conn->recv_lock = &sk->sk_callback_lock;
iscsi_conn_set_callbacks(conn);
tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
/*
* set receive state machine into initial state
*/
tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
}
return 0;
}
iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
struct iscsi_r2t_info *r2t;
/* flush ctask's r2t queues */
while (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*)))
__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
__iscsi_ctask_cleanup(conn, ctask);
iscsi_tcp_suspend_conn_rx(struct iscsi_conn *conn)
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct sock *sk;
if (!tcp_conn->sock)
return;
sk = tcp_conn->sock->sk;
write_lock_bh(&sk->sk_callback_lock);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
write_unlock_bh(&sk->sk_callback_lock);
iscsi_tcp_terminate_conn(struct iscsi_conn *conn)
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
if (!tcp_conn->sock)
return;
sock_hold(tcp_conn->sock->sk);
sock_put(tcp_conn->sock->sk);
sock_release(tcp_conn->sock);
tcp_conn->sock = NULL;
conn->recv_lock = NULL;
/* called with host lock */
iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask,
char *data, uint32_t data_size)
struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
iscsi_buf_init_virt(&tcp_mtask->headbuf, (char*)mtask->hdr,
sizeof(struct iscsi_hdr));
tcp_mtask->xmstate = XMSTATE_IMM_HDR;
if (mtask->data_count)
iscsi_buf_init_iov(&tcp_mtask->sendbuf, (char*)mtask->data,
mtask->data_count);
}
static int
iscsi_r2tpool_alloc(struct iscsi_session *session)
{
int i;
int cmd_i;
/*
* initialize per-task: R2T pool and xmit queue
*/
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
/*
* pre-allocated x4 as much r2ts to handle race when
* target acks DataOut faster than we data_xmit() queues
* could replenish r2tqueue.
*/
/* R2T pool */
if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4,
(void***)&tcp_ctask->r2ts,
sizeof(struct iscsi_r2t_info))) {
goto r2t_alloc_fail;
}
/* R2T xmit queue */
tcp_ctask->r2tqueue = kfifo_alloc(
session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
iscsi_pool_free(&tcp_ctask->r2tpool,
(void**)tcp_ctask->r2ts);
goto r2t_alloc_fail;
}
/*
* number of
* Data-Out PDU's within R2T-sequence can be quite big;
* using mempool
*/
tcp_ctask->datapool = mempool_create_slab_pool(ISCSI_DTASK_DEFAULT_MAX,
taskcache);
if (tcp_ctask->datapool == NULL) {
kfifo_free(tcp_ctask->r2tqueue);
iscsi_pool_free(&tcp_ctask->r2tpool,
(void**)tcp_ctask->r2ts);
goto r2t_alloc_fail;
}
INIT_LIST_HEAD(&tcp_ctask->dataqueue);
}
return 0;
r2t_alloc_fail:
for (i = 0; i < cmd_i; i++) {
struct iscsi_cmd_task *ctask = session->cmds[i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
mempool_destroy(tcp_ctask->datapool);
kfifo_free(tcp_ctask->r2tqueue);
iscsi_pool_free(&tcp_ctask->r2tpool,
(void**)tcp_ctask->r2ts);
}
return -ENOMEM;
}
static void
iscsi_r2tpool_free(struct iscsi_session *session)
{
int i;
for (i = 0; i < session->cmds_max; i++) {
struct iscsi_cmd_task *ctask = session->cmds[i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
mempool_destroy(tcp_ctask->datapool);
kfifo_free(tcp_ctask->r2tqueue);
iscsi_pool_free(&tcp_ctask->r2tpool,
(void**)tcp_ctask->r2ts);
}
}
static int
iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
uint32_t value)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
switch(param) {
case ISCSI_PARAM_MAX_RECV_DLENGTH: {
char *saveptr = tcp_conn->data;
if (tcp_conn->data_size >= value) {
conn->max_recv_dlength = value;
break;
}
spin_lock_bh(&session->lock);
if (conn->stop_stage == STOP_CONN_RECOVER)
flags = GFP_ATOMIC;
spin_unlock_bh(&session->lock);
if (value <= PAGE_SIZE)
tcp_conn->data = kmalloc(value, flags);
tcp_conn->data = (void*)__get_free_pages(flags,
get_order(value));
if (tcp_conn->data == NULL) {
tcp_conn->data = saveptr;
return -ENOMEM;
}
if (tcp_conn->data_size <= PAGE_SIZE)
kfree(saveptr);
else
free_pages((unsigned long)saveptr,
get_order(tcp_conn->data_size));
conn->max_recv_dlength = value;
tcp_conn->data_size = value;
}
break;
case ISCSI_PARAM_MAX_XMIT_DLENGTH:
conn->max_xmit_dlength = value;
break;
case ISCSI_PARAM_HDRDGST_EN:
conn->hdrdgst_en = value;
tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
if (conn->hdrdgst_en) {
tcp_conn->hdr_size += sizeof(__u32);
if (!tcp_conn->tx_tfm)
tcp_conn->tx_tfm = crypto_alloc_tfm("crc32c",
0);
if (!tcp_conn->tx_tfm)
return -ENOMEM;
if (!tcp_conn->rx_tfm)
tcp_conn->rx_tfm = crypto_alloc_tfm("crc32c",
0);
if (!tcp_conn->rx_tfm) {
crypto_free_tfm(tcp_conn->tx_tfm);
return -ENOMEM;
}
} else {
if (tcp_conn->tx_tfm)
crypto_free_tfm(tcp_conn->tx_tfm);
if (tcp_conn->rx_tfm)
crypto_free_tfm(tcp_conn->rx_tfm);
}
break;
case ISCSI_PARAM_DATADGST_EN:
conn->datadgst_en = value;
if (conn->datadgst_en) {
if (!tcp_conn->data_tx_tfm)
tcp_conn->data_tx_tfm =
crypto_alloc_tfm("crc32c", 0);
if (!tcp_conn->data_tx_tfm)
return -ENOMEM;
if (!tcp_conn->data_rx_tfm)
tcp_conn->data_rx_tfm =
crypto_alloc_tfm("crc32c", 0);
if (!tcp_conn->data_rx_tfm) {
crypto_free_tfm(tcp_conn->data_tx_tfm);
return -ENOMEM;
}
} else {
if (tcp_conn->data_tx_tfm)
crypto_free_tfm(tcp_conn->data_tx_tfm);
if (tcp_conn->data_rx_tfm)
crypto_free_tfm(tcp_conn->data_rx_tfm);
tcp_conn->sendpage = conn->datadgst_en ?
sock_no_sendpage : tcp_conn->sock->ops->sendpage;
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
break;
case ISCSI_PARAM_INITIAL_R2T_EN:
session->initial_r2t_en = value;
break;
case ISCSI_PARAM_MAX_R2T:
if (session->max_r2t == roundup_pow_of_two(value))
break;
iscsi_r2tpool_free(session);
session->max_r2t = value;
if (session->max_r2t & (session->max_r2t - 1))
session->max_r2t = roundup_pow_of_two(session->max_r2t);
if (iscsi_r2tpool_alloc(session))
return -ENOMEM;
break;
case ISCSI_PARAM_IMM_DATA_EN:
session->imm_data_en = value;
break;
case ISCSI_PARAM_FIRST_BURST:
session->first_burst = value;
break;
case ISCSI_PARAM_MAX_BURST:
session->max_burst = value;
break;
case ISCSI_PARAM_PDU_INORDER_EN:
session->pdu_inorder_en = value;
break;
case ISCSI_PARAM_DATASEQ_INORDER_EN:
session->dataseq_inorder_en = value;
break;
case ISCSI_PARAM_ERL:
session->erl = value;
break;
case ISCSI_PARAM_IFMARKER_EN:
BUG_ON(value);
session->ifmarker_en = value;
break;
case ISCSI_PARAM_OFMARKER_EN:
BUG_ON(value);
session->ofmarker_en = value;
break;
case ISCSI_PARAM_EXP_STATSN:
conn->exp_statsn = value;
break;
default:
break;
}
return 0;
}
static int
iscsi_session_get_param(struct iscsi_cls_session *cls_session,
enum iscsi_param param, uint32_t *value)
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
switch(param) {
case ISCSI_PARAM_INITIAL_R2T_EN:
*value = session->initial_r2t_en;
break;
case ISCSI_PARAM_MAX_R2T:
*value = session->max_r2t;
break;
case ISCSI_PARAM_IMM_DATA_EN:
*value = session->imm_data_en;
break;
case ISCSI_PARAM_FIRST_BURST:
*value = session->first_burst;
break;
case ISCSI_PARAM_MAX_BURST:
*value = session->max_burst;
break;
case ISCSI_PARAM_PDU_INORDER_EN:
*value = session->pdu_inorder_en;
break;
case ISCSI_PARAM_DATASEQ_INORDER_EN:
*value = session->dataseq_inorder_en;
break;
case ISCSI_PARAM_ERL:
*value = session->erl;
break;
case ISCSI_PARAM_IFMARKER_EN:
*value = session->ifmarker_en;
break;
case ISCSI_PARAM_OFMARKER_EN:
*value = session->ofmarker_en;
break;
default:
}
return 0;
}
iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, uint32_t *value)
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct inet_sock *inet;
switch(param) {
case ISCSI_PARAM_MAX_RECV_DLENGTH:
*value = conn->max_recv_dlength;
break;
case ISCSI_PARAM_MAX_XMIT_DLENGTH:
*value = conn->max_xmit_dlength;
break;
case ISCSI_PARAM_HDRDGST_EN:
*value = conn->hdrdgst_en;
break;
case ISCSI_PARAM_DATADGST_EN:
*value = conn->datadgst_en;
break;
case ISCSI_PARAM_CONN_PORT:
mutex_lock(&conn->xmitmutex);
mutex_unlock(&conn->xmitmutex);
return -EINVAL;
}
inet = inet_sk(tcp_conn->sock->sk);
*value = be16_to_cpu(inet->dport);
mutex_unlock(&conn->xmitmutex);
case ISCSI_PARAM_EXP_STATSN:
*value = conn->exp_statsn;
break;
}
return 0;
}
static int
iscsi_conn_get_str_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct sock *sk;
struct inet_sock *inet;
struct ipv6_pinfo *np;
int len = 0;
switch (param) {
case ISCSI_PARAM_CONN_ADDRESS:
mutex_lock(&conn->xmitmutex);
mutex_unlock(&conn->xmitmutex);
return -EINVAL;
}
if (sk->sk_family == PF_INET) {
inet = inet_sk(sk);
len = sprintf(buf, "%u.%u.%u.%u\n",
NIPQUAD(inet->daddr));
} else {
np = inet6_sk(sk);
len = sprintf(buf,
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(np->daddr));
}
mutex_unlock(&conn->xmitmutex);
break;
default:
return -EINVAL;
}
return len;
}
iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
stats->txdata_octets = conn->txdata_octets;
stats->rxdata_octets = conn->rxdata_octets;
stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
stats->dataout_pdus = conn->dataout_pdus_cnt;
stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
stats->datain_pdus = conn->datain_pdus_cnt;
stats->r2t_pdus = conn->r2t_pdus_cnt;
stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
stats->custom_length = 3;
strcpy(stats->custom[0].desc, "tx_sendpage_failures");
stats->custom[0].value = tcp_conn->sendpage_failures_cnt;
strcpy(stats->custom[1].desc, "rx_discontiguous_hdr");
stats->custom[1].value = tcp_conn->discontiguous_hdr_cnt;
strcpy(stats->custom[2].desc, "eh_abort_cnt");
stats->custom[2].value = conn->eh_abort_cnt;
}
static struct iscsi_cls_session *
iscsi_tcp_session_create(struct iscsi_transport *iscsit,
struct scsi_transport_template *scsit,
uint32_t initial_cmdsn, uint32_t *hostno)
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
uint32_t hn;
int cmd_i;
cls_session = iscsi_session_setup(iscsit, scsit,
sizeof(struct iscsi_tcp_cmd_task),
sizeof(struct iscsi_tcp_mgmt_task),
initial_cmdsn, &hn);
if (!cls_session)
return NULL;
*hostno = hn;
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
session = class_to_transport_session(cls_session);
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
ctask->hdr = &tcp_ctask->hdr;
}
for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
mtask->hdr = &tcp_mtask->hdr;
}
if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
goto r2tpool_alloc_fail;
return cls_session;
r2tpool_alloc_fail:
iscsi_session_teardown(cls_session);
return NULL;
}
static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
{
struct iscsi_session *session = class_to_transport_session(cls_session);
struct iscsi_data_task *dtask, *n;
int cmd_i;
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
list_for_each_entry_safe(dtask, n, &tcp_ctask->dataqueue,
item) {
list_del(&dtask->item);
mempool_free(dtask, tcp_ctask->datapool);
}
}
iscsi_r2tpool_free(class_to_transport_session(cls_session));
iscsi_session_teardown(cls_session);
static struct scsi_host_template iscsi_sht = {
.name = "iSCSI Initiator over TCP/IP, v."
ISCSI_VERSION_STR,
.queuecommand = iscsi_queuecommand,
.change_queue_depth = iscsi_change_queue_depth,
.can_queue = ISCSI_XMIT_CMDS_MAX - 1,
.sg_tablesize = ISCSI_SG_TABLESIZE,
.cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
.eh_abort_handler = iscsi_eh_abort,
.eh_host_reset_handler = iscsi_eh_host_reset,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "iscsi_tcp",
.this_id = -1,
};
static struct iscsi_transport iscsi_tcp_transport = {
.owner = THIS_MODULE,
.name = "tcp",
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
| CAP_DATADGST,
.param_mask = ISCSI_MAX_RECV_DLENGTH |
ISCSI_MAX_XMIT_DLENGTH |
ISCSI_HDRDGST_EN |
ISCSI_DATADGST_EN |
ISCSI_INITIAL_R2T_EN |
ISCSI_MAX_R2T |
ISCSI_IMM_DATA_EN |
ISCSI_FIRST_BURST |
ISCSI_MAX_BURST |
ISCSI_PDU_INORDER_EN |
ISCSI_DATASEQ_INORDER_EN |
ISCSI_ERL |
ISCSI_CONN_PORT |
ISCSI_CONN_ADDRESS |
ISCSI_EXP_STATSN,
.host_template = &iscsi_sht,
.conndata_size = sizeof(struct iscsi_conn),
.max_conn = 1,
.max_cmd_len = ISCSI_TCP_MAX_CMD_LEN,
/* session management */
.create_session = iscsi_tcp_session_create,
.destroy_session = iscsi_tcp_session_destroy,
/* connection management */
.create_conn = iscsi_tcp_conn_create,
.bind_conn = iscsi_tcp_conn_bind,
.destroy_conn = iscsi_tcp_conn_destroy,
.set_param = iscsi_conn_set_param,
.get_conn_param = iscsi_conn_get_param,
.get_conn_str_param = iscsi_conn_get_str_param,
.get_session_param = iscsi_session_get_param,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
/* these are called as part of conn recovery */
.suspend_conn_recv = iscsi_tcp_suspend_conn_rx,
.terminate_conn = iscsi_tcp_terminate_conn,
/* IO */
.send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_conn_get_stats,
.init_cmd_task = iscsi_tcp_cmd_init,
.init_mgmt_task = iscsi_tcp_mgmt_init,
.xmit_cmd_task = iscsi_tcp_ctask_xmit,
.xmit_mgmt_task = iscsi_tcp_mtask_xmit,
.cleanup_cmd_task = iscsi_tcp_cleanup_ctask,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
};
static int __init
iscsi_tcp_init(void)
{
if (iscsi_max_lun < 1) {
printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n",
iscsi_max_lun);
return -EINVAL;
}
iscsi_tcp_transport.max_lun = iscsi_max_lun;
taskcache = kmem_cache_create("iscsi_taskcache",
sizeof(struct iscsi_data_task), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!taskcache)
return -ENOMEM;
if (!iscsi_register_transport(&iscsi_tcp_transport))
kmem_cache_destroy(taskcache);
}
static void __exit
iscsi_tcp_exit(void)
{
iscsi_unregister_transport(&iscsi_tcp_transport);
kmem_cache_destroy(taskcache);
}
module_init(iscsi_tcp_init);
module_exit(iscsi_tcp_exit);