Newer
Older
mutex_lock(&conn->xmitmutex);
spin_lock_bh(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN) {
if (session->state == ISCSI_STATE_TERMINATE) {
spin_unlock_bh(&session->lock);
mutex_unlock(&conn->xmitmutex);
goto failed;
}
spin_unlock_bh(&session->lock);
} else {
struct iscsi_tm *hdr = &conn->tmhdr;
/*
* Still LOGGED_IN...
*/
if (!ctask->sc || sc->SCp.phase != session->age) {
/*
* 1) ctask completed before time out. But session
* is still ok => Happy Retry.
* 2) session was re-open during time out of ctask.
*/
spin_unlock_bh(&session->lock);
mutex_unlock(&conn->xmitmutex);
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
goto success;
}
conn->tmabort_state = TMABORT_INITIAL;
spin_unlock_bh(&session->lock);
/*
* ctask timed out but session is OK
* ERL=0 requires task mgmt abort to be issued on each
* failed command. requests must be serialized.
*/
memset(hdr, 0, sizeof(struct iscsi_tm));
hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
hdr->flags = ISCSI_TM_FUNC_ABORT_TASK;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
memcpy(hdr->lun, ctask->hdr.lun, sizeof(hdr->lun));
hdr->rtt = ctask->hdr.itt;
hdr->refcmdsn = ctask->hdr.cmdsn;
rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr,
NULL, 0);
if (rc) {
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
debug_scsi("abort sent failure [itt 0x%x]", ctask->itt);
} else {
struct iscsi_r2t_info *r2t;
/*
* TMF abort vs. TMF response race logic
*/
spin_lock_bh(&session->lock);
ctask->mtask = (struct iscsi_mgmt_task *)
session->mgmt_cmds[(hdr->itt & ITT_MASK) -
ISCSI_MGMT_ITT_OFFSET];
/*
* have to flush r2tqueue to avoid r2t leaks
*/
while (__kfifo_get(ctask->r2tqueue, (void*)&r2t,
sizeof(void*))) {
__kfifo_put(ctask->r2tpool.queue, (void*)&r2t,
sizeof(void*));
}
if (conn->tmabort_state == TMABORT_INITIAL) {
conn->tmfcmd_pdus_cnt++;
conn->tmabort_timer.expires = 3*HZ + jiffies;
conn->tmabort_timer.function =
iscsi_tmabort_timedout;
conn->tmabort_timer.data = (unsigned long)ctask;
add_timer(&conn->tmabort_timer);
debug_scsi("abort sent [itt 0x%x]", ctask->itt);
} else {
if (!ctask->sc ||
conn->tmabort_state == TMABORT_SUCCESS) {
conn->tmabort_state = TMABORT_INITIAL;
spin_unlock_bh(&session->lock);
mutex_unlock(&conn->xmitmutex);
goto success;
}
conn->tmabort_state = TMABORT_INITIAL;
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
}
spin_unlock_bh(&session->lock);
}
}
mutex_unlock(&conn->xmitmutex);
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
/*
* block eh thread until:
*
* 1) abort response;
* 2) abort timeout;
* 3) session re-opened;
* 4) session terminated;
*/
for (;;) {
int p_state = session->state;
rc = wait_event_interruptible(conn->ehwait,
(p_state == ISCSI_STATE_LOGGED_IN ?
(session->state == ISCSI_STATE_TERMINATE ||
conn->tmabort_state != TMABORT_INITIAL) :
(session->state == ISCSI_STATE_TERMINATE ||
session->state == ISCSI_STATE_LOGGED_IN)));
if (rc) {
/* shutdown.. */
session->state = ISCSI_STATE_TERMINATE;
goto failed;
}
if (signal_pending(current))
flush_signals(current);
if (session->state == ISCSI_STATE_TERMINATE)
goto failed;
spin_lock_bh(&session->lock);
if (sc->SCp.phase == session->age &&
(conn->tmabort_state == TMABORT_TIMEDOUT ||
conn->tmabort_state == TMABORT_FAILED)) {
conn->tmabort_state = TMABORT_INITIAL;
if (!ctask->sc) {
/*
* ctask completed before tmf abort response or
* time out.
* But session is still ok => Happy Retry.
*/
spin_unlock_bh(&session->lock);
break;
}
spin_unlock_bh(&session->lock);
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
continue;
}
spin_unlock_bh(&session->lock);
break;
}
success:
debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
rc = SUCCESS;
goto exit;
failed:
debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
rc = FAILED;
exit:
del_timer_sync(&conn->tmabort_timer);
mutex_lock(&conn->xmitmutex);
if (conn->sock) {
struct sock *sk = conn->sock->sk;
write_lock_bh(&sk->sk_callback_lock);
iscsi_ctask_cleanup(conn, ctask);
write_unlock_bh(&sk->sk_callback_lock);
}
mutex_unlock(&conn->xmitmutex);
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
return rc;
}
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];
/*
* 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(&ctask->r2tpool, session->max_r2t * 4,
(void***)&ctask->r2ts, sizeof(struct iscsi_r2t_info))) {
goto r2t_alloc_fail;
}
/* R2T xmit queue */
ctask->r2tqueue = kfifo_alloc(
session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
if (ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
iscsi_pool_free(&ctask->r2tpool, (void**)ctask->r2ts);
goto r2t_alloc_fail;
}
/*
* number of
* Data-Out PDU's within R2T-sequence can be quite big;
* using mempool
*/
ctask->datapool = mempool_create(ISCSI_DTASK_DEFAULT_MAX,
mempool_alloc_slab, mempool_free_slab, taskcache);
if (ctask->datapool == NULL) {
kfifo_free(ctask->r2tqueue);
iscsi_pool_free(&ctask->r2tpool, (void**)ctask->r2ts);
goto r2t_alloc_fail;
}
INIT_LIST_HEAD(&ctask->dataqueue);
}
return 0;
r2t_alloc_fail:
for (i = 0; i < cmd_i; i++) {
mempool_destroy(session->cmds[i]->datapool);
kfifo_free(session->cmds[i]->r2tqueue);
iscsi_pool_free(&session->cmds[i]->r2tpool,
(void**)session->cmds[i]->r2ts);
}
return -ENOMEM;
}
static void
iscsi_r2tpool_free(struct iscsi_session *session)
{
int i;
for (i = 0; i < session->cmds_max; i++) {
mempool_destroy(session->cmds[i]->datapool);
kfifo_free(session->cmds[i]->r2tqueue);
iscsi_pool_free(&session->cmds[i]->r2tpool,
(void**)session->cmds[i]->r2ts);
}
}
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;
static struct Scsi_Host *
iscsi_session_create(struct scsi_transport_template *scsit,
uint32_t initial_cmdsn)
struct Scsi_Host *shost;
struct iscsi_session *session;
shost = iscsi_transport_create_session(scsit, &iscsi_tcp_transport);
if (!shost)
return NULL;
session = iscsi_hostdata(shost->hostdata);
memset(session, 0, sizeof(struct iscsi_session));
session->host = shost;
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
session->state = ISCSI_STATE_LOGGED_IN;
session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
session->cmds_max = ISCSI_XMIT_CMDS_MAX;
session->cmdsn = initial_cmdsn;
session->exp_cmdsn = initial_cmdsn + 1;
session->max_cmdsn = initial_cmdsn + 1;
session->max_r2t = 1;
/* initialize SCSI PDU commands pool */
if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
(void***)&session->cmds, sizeof(struct iscsi_cmd_task)))
goto cmdpool_alloc_fail;
/* pre-format cmds pool with ITT */
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++)
session->cmds[cmd_i]->itt = cmd_i;
spin_lock_init(&session->lock);
INIT_LIST_HEAD(&session->connections);
/* initialize immediate command pool */
if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max,
(void***)&session->mgmt_cmds, sizeof(struct iscsi_mgmt_task)))
goto mgmtpool_alloc_fail;
/* pre-format immediate cmds pool with ITT */
for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
session->mgmt_cmds[cmd_i]->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i;
session->mgmt_cmds[cmd_i]->data = kmalloc(
DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH, GFP_KERNEL);
if (!session->mgmt_cmds[cmd_i]->data) {
int j;
for (j = 0; j < cmd_i; j++)
kfree(session->mgmt_cmds[j]->data);
goto immdata_alloc_fail;
}
}
if (iscsi_r2tpool_alloc(session))
goto r2tpool_alloc_fail;
return shost;
r2tpool_alloc_fail:
for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++)
kfree(session->mgmt_cmds[cmd_i]->data);
iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
immdata_alloc_fail:
mgmtpool_alloc_fail:
iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
cmdpool_alloc_fail:
return NULL;
}
static void
iscsi_session_destroy(struct Scsi_Host *shost)
struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
int cmd_i;
struct iscsi_data_task *dtask, *n;
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
list_for_each_entry_safe(dtask, n, &ctask->dataqueue, item) {
list_del(&dtask->item);
mempool_free(dtask, ctask->datapool);
}
}
for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++)
kfree(session->mgmt_cmds[cmd_i]->data);
iscsi_r2tpool_free(session);
iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
iscsi_transport_destroy_session(shost);
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
}
static int
iscsi_conn_set_param(iscsi_connh_t connh, enum iscsi_param param,
uint32_t value)
{
struct iscsi_conn *conn = iscsi_ptr(connh);
struct iscsi_session *session = conn->session;
spin_lock_bh(&session->lock);
if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE &&
conn->stop_stage != STOP_CONN_RECOVER) {
printk(KERN_ERR "iscsi_tcp: can not change parameter [%d]\n",
param);
spin_unlock_bh(&session->lock);
return 0;
}
spin_unlock_bh(&session->lock);
switch(param) {
case ISCSI_PARAM_MAX_RECV_DLENGTH: {
char *saveptr = conn->data;
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
if (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)
conn->data = kmalloc(value, flags);
else
conn->data = (void*)__get_free_pages(flags,
get_order(value));
if (conn->data == NULL) {
conn->data = saveptr;
return -ENOMEM;
}
if (conn->data_size <= PAGE_SIZE)
kfree(saveptr);
else
free_pages((unsigned long)saveptr,
get_order(conn->data_size));
conn->max_recv_dlength = value;
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;
conn->hdr_size = sizeof(struct iscsi_hdr);
if (conn->hdrdgst_en) {
conn->hdr_size += sizeof(__u32);
if (!conn->tx_tfm)
conn->tx_tfm = crypto_alloc_tfm("crc32c", 0);
if (!conn->tx_tfm)
return -ENOMEM;
if (!conn->rx_tfm)
conn->rx_tfm = crypto_alloc_tfm("crc32c", 0);
if (!conn->rx_tfm) {
crypto_free_tfm(conn->tx_tfm);
return -ENOMEM;
}
} else {
if (conn->tx_tfm)
crypto_free_tfm(conn->tx_tfm);
if (conn->rx_tfm)
crypto_free_tfm(conn->rx_tfm);
}
break;
case ISCSI_PARAM_DATADGST_EN:
conn->datadgst_en = value;
if (conn->datadgst_en) {
if (!conn->data_tx_tfm)
conn->data_tx_tfm =
crypto_alloc_tfm("crc32c", 0);
if (!conn->data_tx_tfm)
return -ENOMEM;
if (!conn->data_rx_tfm)
conn->data_rx_tfm =
crypto_alloc_tfm("crc32c", 0);
if (!conn->data_rx_tfm) {
crypto_free_tfm(conn->data_tx_tfm);
return -ENOMEM;
}
} else {
if (conn->data_tx_tfm)
crypto_free_tfm(conn->data_tx_tfm);
if (conn->data_rx_tfm)
crypto_free_tfm(conn->data_rx_tfm);
}
conn->sendpage = conn->datadgst_en ?
sock_no_sendpage : conn->sock->ops->sendpage;
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
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;
default:
break;
}
return 0;
}
static int
iscsi_session_get_param(struct Scsi_Host *shost,
enum iscsi_param param, uint32_t *value)
struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
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 ISCSI_ERR_PARAM_NOT_FOUND;
}
return 0;
}
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
static int
iscsi_conn_get_param(void *data, enum iscsi_param param, uint32_t *value)
{
struct iscsi_conn *conn = data;
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;
default:
return ISCSI_ERR_PARAM_NOT_FOUND;
}
return 0;
}
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
static void
iscsi_conn_get_stats(iscsi_connh_t connh, struct iscsi_stats *stats)
{
struct iscsi_conn *conn = iscsi_ptr(connh);
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 = conn->sendpage_failures_cnt;
strcpy(stats->custom[1].desc, "rx_discontiguous_hdr");
stats->custom[1].value = conn->discontiguous_hdr_cnt;
strcpy(stats->custom[2].desc, "eh_abort_cnt");
stats->custom[2].value = conn->eh_abort_cnt;
}
static int
iscsi_conn_send_pdu(iscsi_connh_t connh, struct iscsi_hdr *hdr, char *data,
uint32_t data_size)
{
struct iscsi_conn *conn = iscsi_ptr(connh);
int rc;
mutex_lock(&conn->xmitmutex);
rc = iscsi_conn_send_generic(conn, hdr, data, data_size);
mutex_unlock(&conn->xmitmutex);
return rc;
}
static struct iscsi_transport iscsi_tcp_transport = {
.owner = THIS_MODULE,
.name = "tcp",
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
| CAP_DATADGST,
.host_template = &iscsi_sht,
.hostdata_size = sizeof(struct iscsi_session),
.conndata_size = sizeof(struct iscsi_conn),
.max_conn = 1,
.max_cmd_len = ISCSI_TCP_MAX_CMD_LEN,
.create_session = iscsi_session_create,
.destroy_session = iscsi_session_destroy,
.create_conn = iscsi_conn_create,
.bind_conn = iscsi_conn_bind,
.destroy_conn = iscsi_conn_destroy,
.set_param = iscsi_conn_set_param,
.get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param,
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_conn_stop,
.send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_conn_get_stats,
};
static int __init
iscsi_tcp_init(void)
{
if (iscsi_max_lun < 1) {
printk(KERN_ERR "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 | SLAB_NO_REAP, 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);