mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-17 02:31:27 +00:00
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@408 d57e44dd-8a1f-0410-8b47-8ef2f437770f
300 lines
6.5 KiB
C
300 lines
6.5 KiB
C
/*
|
|
* Copyright (C) 2002 - 2003 Ardis Technolgies <roman@ardistech.com>
|
|
* Copyright (C) 2007 - 2008 Vladislav Bolkhovitin
|
|
* Copyright (C) 2007 - 2008 CMS Distribution Limited
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, version 2
|
|
* of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include "iscsi.h"
|
|
#include "digest.h"
|
|
|
|
#define MAX_NR_TARGETS (1UL << 30)
|
|
|
|
DEFINE_MUTEX(target_mgmt_mutex);
|
|
|
|
/* All 3 protected by target_mgmt_mutex */
|
|
static LIST_HEAD(target_list);
|
|
static u32 next_target_id;
|
|
static u32 nr_targets;
|
|
|
|
static struct iscsi_sess_param default_session_param = {
|
|
.initial_r2t = 1,
|
|
.immediate_data = 1,
|
|
.max_connections = 1,
|
|
.max_recv_data_length = 8192,
|
|
.max_xmit_data_length = 8192,
|
|
.max_burst_length = 262144,
|
|
.first_burst_length = 65536,
|
|
.default_wait_time = 2,
|
|
.default_retain_time = 20,
|
|
.max_outstanding_r2t = 1,
|
|
.data_pdu_inorder = 1,
|
|
.data_sequence_inorder = 1,
|
|
.error_recovery_level = 0,
|
|
.header_digest = DIGEST_NONE,
|
|
.data_digest = DIGEST_NONE,
|
|
.ofmarker = 0,
|
|
.ifmarker = 0,
|
|
.ofmarkint = 2048,
|
|
.ifmarkint = 2048,
|
|
};
|
|
|
|
static struct iscsi_trgt_param default_target_param = {
|
|
.queued_cmnds = DEFAULT_NR_QUEUED_CMNDS,
|
|
};
|
|
|
|
/* target_mgmt_mutex supposed to be locked */
|
|
struct iscsi_target *target_lookup_by_id(u32 id)
|
|
{
|
|
struct iscsi_target *target;
|
|
|
|
list_for_each_entry(target, &target_list, target_list_entry) {
|
|
if (target->tid == id)
|
|
return target;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* target_mgmt_mutex supposed to be locked */
|
|
static struct iscsi_target *target_lookup_by_name(char *name)
|
|
{
|
|
struct iscsi_target *target;
|
|
|
|
list_for_each_entry(target, &target_list, target_list_entry) {
|
|
if (!strcmp(target->name, name))
|
|
return target;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* target_mgmt_mutex supposed to be locked */
|
|
static int iscsi_target_create(struct target_info *info, u32 tid)
|
|
{
|
|
int err = -EINVAL, len;
|
|
char *name = info->name;
|
|
struct iscsi_target *target;
|
|
|
|
TRACE_MGMT_DBG("Creating target tid %u, name %s", tid, name);
|
|
|
|
len = strlen(name);
|
|
if (!len) {
|
|
PRINT_ERROR("The length of the target name is zero %u", tid);
|
|
goto out;
|
|
}
|
|
|
|
if (!try_module_get(THIS_MODULE)) {
|
|
PRINT_ERROR("Fail to get module %u", tid);
|
|
goto out;
|
|
}
|
|
|
|
target = kzalloc(sizeof(*target), GFP_KERNEL);
|
|
if (!target) {
|
|
err = -ENOMEM;
|
|
goto out_put;
|
|
}
|
|
|
|
target->tid = info->tid = tid;
|
|
|
|
memcpy(&target->trgt_sess_param, &default_session_param,
|
|
sizeof(default_session_param));
|
|
memcpy(&target->trgt_param, &default_target_param,
|
|
sizeof(default_target_param));
|
|
|
|
strncpy(target->name, name, sizeof(target->name) - 1);
|
|
|
|
mutex_init(&target->target_mutex);
|
|
INIT_LIST_HEAD(&target->session_list);
|
|
|
|
list_add(&target->target_list_entry, &target_list);
|
|
|
|
target->scst_tgt = scst_register(&iscsi_template, target->name);
|
|
if (!target->scst_tgt) {
|
|
PRINT_ERROR("%s", "scst_register() failed");
|
|
err = -EBUSY;
|
|
goto out_free;
|
|
}
|
|
|
|
return 0;
|
|
|
|
out_free:
|
|
kfree(target);
|
|
|
|
out_put:
|
|
module_put(THIS_MODULE);
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
/* target_mgmt_mutex supposed to be locked */
|
|
int target_add(struct target_info *info)
|
|
{
|
|
int err = -EEXIST;
|
|
u32 tid = info->tid;
|
|
|
|
if (nr_targets > MAX_NR_TARGETS) {
|
|
err = -EBUSY;
|
|
goto out;
|
|
}
|
|
|
|
if (target_lookup_by_name(info->name))
|
|
goto out;
|
|
|
|
if (tid && target_lookup_by_id(tid))
|
|
goto out;
|
|
|
|
if (!tid) {
|
|
do {
|
|
if (!++next_target_id)
|
|
++next_target_id;
|
|
} while (target_lookup_by_id(next_target_id));
|
|
|
|
tid = next_target_id;
|
|
}
|
|
|
|
err = iscsi_target_create(info, tid);
|
|
if (!err)
|
|
nr_targets++;
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static void target_destroy(struct iscsi_target *target)
|
|
{
|
|
TRACE_MGMT_DBG("Destroying target tid %u", target->tid);
|
|
|
|
scst_unregister(target->scst_tgt);
|
|
|
|
kfree(target);
|
|
|
|
module_put(THIS_MODULE);
|
|
}
|
|
|
|
/* target_mgmt_mutex supposed to be locked */
|
|
int target_del(u32 id)
|
|
{
|
|
struct iscsi_target *target;
|
|
int err;
|
|
|
|
target = target_lookup_by_id(id);
|
|
if (!target) {
|
|
err = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
mutex_lock(&target->target_mutex);
|
|
|
|
if (!list_empty(&target->session_list)) {
|
|
err = -EBUSY;
|
|
goto out_unlock;
|
|
}
|
|
|
|
list_del(&target->target_list_entry);
|
|
nr_targets--;
|
|
|
|
mutex_unlock(&target->target_mutex);
|
|
|
|
target_destroy(target);
|
|
return 0;
|
|
|
|
out_unlock:
|
|
mutex_unlock(&target->target_mutex);
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
void target_del_all(void)
|
|
{
|
|
struct iscsi_target *target, *t;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
TRACE_MGMT_DBG("%s", "Deleting all targets");
|
|
|
|
/* Complete brain damage, ToDo */
|
|
while (1) {
|
|
mutex_lock(&target_mgmt_mutex);
|
|
|
|
if (list_empty(&target_list))
|
|
break;
|
|
|
|
list_for_each_entry_safe(target, t, &target_list, target_list_entry) {
|
|
struct iscsi_session *session, *ts;
|
|
mutex_lock(&target->target_mutex);
|
|
if (!list_empty(&target->session_list)) {
|
|
TRACE_MGMT_DBG("Cleaning up target %p", target);
|
|
list_for_each_entry_safe(session, ts, &target->session_list,
|
|
session_list_entry) {
|
|
TRACE_MGMT_DBG("Cleaning up session %p", session);
|
|
if (!list_empty(&session->conn_list)) {
|
|
struct iscsi_conn *conn, *tc;
|
|
list_for_each_entry_safe(conn, tc,
|
|
&session->conn_list,
|
|
conn_list_entry) {
|
|
TRACE_MGMT_DBG("Mark conn %p "
|
|
"closing", conn);
|
|
__mark_conn_closed(conn,
|
|
ISCSI_CONN_ACTIVE_CLOSE|ISCSI_CONN_DELETING);
|
|
}
|
|
} else {
|
|
TRACE_MGMT_DBG("Freeing session %p "
|
|
"without connections", session);
|
|
session_del(target, session->sid);
|
|
}
|
|
}
|
|
mutex_unlock(&target->target_mutex);
|
|
} else {
|
|
TRACE_MGMT_DBG("Deleting target %p", target);
|
|
list_del(&target->target_list_entry);
|
|
nr_targets--;
|
|
mutex_unlock(&target->target_mutex);
|
|
target_destroy(target);
|
|
continue;
|
|
}
|
|
}
|
|
mutex_unlock(&target_mgmt_mutex);
|
|
msleep(100);
|
|
}
|
|
|
|
mutex_unlock(&target_mgmt_mutex);
|
|
|
|
TRACE_MGMT_DBG("%s", "Deleting all targets finished");
|
|
|
|
TRACE_EXIT();
|
|
return;
|
|
}
|
|
|
|
int iscsi_info_show(struct seq_file *seq, iscsi_show_info_t *func)
|
|
{
|
|
int err;
|
|
struct iscsi_target *target;
|
|
|
|
err = mutex_lock_interruptible(&target_mgmt_mutex);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
list_for_each_entry(target, &target_list, target_list_entry) {
|
|
seq_printf(seq, "tid:%u name:%s\n", target->tid, target->name);
|
|
|
|
mutex_lock(&target->target_mutex);
|
|
func(seq, target);
|
|
mutex_unlock(&target->target_mutex);
|
|
}
|
|
|
|
mutex_unlock(&target_mgmt_mutex);
|
|
|
|
return 0;
|
|
}
|