Files
scst/iscsi-scst/kernel/config.c
Gleb Chesnokov 0e8fdad5e4 scst: Use sysfs_emit/sysfs_emit_at instead of scnprintf()
Replace scnprintf() with sysfs_emit() and sysfs_emit_at() in sysfs show handlers.

These helper functions are specifically designed for sysfs output, providing safer
handling of buffer lengths and consistency across kernel sysfs interfaces.

This patch does not change any functionality.
2025-05-19 20:46:54 +03:00

1061 lines
22 KiB
C

/*
* Copyright (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
* Copyright (C) 2007 - 2018 Vladislav Bolkhovitin
* Copyright (C) 2007 - 2018 Western Digital Corporation
*
* 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.
*
* 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/module.h>
#include "iscsi_trace_flag.h"
#include "iscsi.h"
/* Protected by target_mgmt_mutex */
int ctr_open_state;
/* Protected by target_mgmt_mutex */
static LIST_HEAD(iscsi_attrs_list);
static ssize_t iscsi_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
ssize_t ret = 0;
TRACE_ENTRY();
ret += sysfs_emit_at(buf, ret, "%s\n", ISCSI_VERSION_STRING);
#ifdef CONFIG_SCST_EXTRACHECKS
ret += sysfs_emit_at(buf, ret, "EXTRACHECKS\n");
#endif
#ifdef CONFIG_SCST_TRACING
ret += sysfs_emit_at(buf, ret, "TRACING\n");
#endif
#ifdef CONFIG_SCST_DEBUG
ret += sysfs_emit_at(buf, ret, "DEBUG\n");
#endif
#ifdef CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES
ret += sysfs_emit_at(buf, ret, "DEBUG_DIGEST_FAILURES\n");
#endif
TRACE_EXIT();
return ret;
}
static struct kobj_attribute iscsi_version_attr =
__ATTR(version, 0444, iscsi_version_show, NULL);
static ssize_t iscsi_open_state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
ssize_t ret;
switch (ctr_open_state) {
case ISCSI_CTR_OPEN_STATE_CLOSED:
ret = sysfs_emit(buf, "closed\n");
break;
case ISCSI_CTR_OPEN_STATE_OPEN:
ret = sysfs_emit(buf, "open\n");
break;
case ISCSI_CTR_OPEN_STATE_CLOSING:
ret = sysfs_emit(buf, "closing\n");
break;
default:
ret = sysfs_emit(buf, "unknown\n");
break;
}
return ret;
}
static struct kobj_attribute iscsi_open_state_attr =
__ATTR(open_state, 0444, iscsi_open_state_show, NULL);
const struct attribute *iscsi_attrs[] = {
&iscsi_version_attr.attr,
&iscsi_open_state_attr.attr,
NULL,
};
/* target_mgmt_mutex supposed to be locked */
static int add_conn(void __user *ptr)
{
int err, rc;
struct iscsi_session *session;
struct iscsi_kern_conn_info info;
struct iscsi_target *target;
TRACE_ENTRY();
rc = copy_from_user(&info, ptr, sizeof(info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out;
}
target = target_lookup_by_id(info.tid);
if (!target) {
PRINT_ERROR("Target %d not found", info.tid);
err = -ENOENT;
goto out;
}
mutex_lock(&target->target_mutex);
session = session_lookup(target, info.sid);
if (!session) {
PRINT_ERROR("Session %lld not found",
(unsigned long long)info.tid);
err = -ENOENT;
goto out_unlock;
}
err = __add_conn(session, &info);
out_unlock:
mutex_unlock(&target->target_mutex);
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int del_conn(void __user *ptr)
{
int err, rc;
struct iscsi_session *session;
struct iscsi_kern_conn_info info;
struct iscsi_target *target;
TRACE_ENTRY();
rc = copy_from_user(&info, ptr, sizeof(info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out;
}
target = target_lookup_by_id(info.tid);
if (!target) {
PRINT_ERROR("Target %d not found", info.tid);
err = -ENOENT;
goto out;
}
mutex_lock(&target->target_mutex);
session = session_lookup(target, info.sid);
if (!session) {
PRINT_ERROR("Session %llx not found",
(unsigned long long)info.sid);
err = -ENOENT;
goto out_unlock;
}
err = __del_conn(session, &info);
out_unlock:
mutex_unlock(&target->target_mutex);
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int add_session(void __user *ptr)
{
int err, rc;
struct iscsi_kern_session_info *info;
struct iscsi_target *target;
TRACE_ENTRY();
lockdep_assert_held(&target_mgmt_mutex);
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
PRINT_ERROR("Can't alloc info (size %zd)", sizeof(*info));
err = -ENOMEM;
goto out;
}
rc = copy_from_user(info, ptr, sizeof(*info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out_free;
}
info->initiator_name[sizeof(info->initiator_name) - 1] = '\0';
info->full_initiator_name[sizeof(info->full_initiator_name) - 1] = '\0';
target = target_lookup_by_id(info->tid);
if (!target) {
PRINT_ERROR("Target %d not found", info->tid);
err = -ENOENT;
goto out_free;
}
err = __add_session(target, info);
out_free:
kfree(info);
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int del_session(void __user *ptr)
{
int err, rc;
struct iscsi_kern_session_info *info;
struct iscsi_target *target;
TRACE_ENTRY();
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
PRINT_ERROR("Can't alloc info (size %zd)", sizeof(*info));
err = -ENOMEM;
goto out;
}
rc = copy_from_user(info, ptr, sizeof(*info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out_free;
}
#ifdef __COVERITY__
/* To suppress a Coverity "tainted scalar" complaint. */
if (info->initiator_name[sizeof(info->initiator_name) - 1]) {
err = -EINVAL;
goto out_free;
}
#endif
info->initiator_name[sizeof(info->initiator_name) - 1] = '\0';
target = target_lookup_by_id(info->tid);
if (!target) {
PRINT_ERROR("Target %d not found", info->tid);
err = -ENOENT;
goto out_free;
}
mutex_lock(&target->target_mutex);
err = __del_session(target, info->sid);
mutex_unlock(&target->target_mutex);
out_free:
kfree(info);
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int iscsi_params_config(void __user *ptr, int set)
{
int err, rc;
struct iscsi_kern_params_info info;
struct iscsi_target *target;
TRACE_ENTRY();
rc = copy_from_user(&info, ptr, sizeof(info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out;
}
target = target_lookup_by_id(info.tid);
if (!target) {
PRINT_ERROR("Target %d not found", info.tid);
err = -ENOENT;
goto out;
}
mutex_lock(&target->target_mutex);
err = iscsi_params_set(target, &info, set);
mutex_unlock(&target->target_mutex);
if (err < 0)
goto out;
if (!set) {
rc = copy_to_user(ptr, &info, sizeof(info));
if (rc != 0) {
PRINT_ERROR("Failed to copy to user %d bytes", rc);
err = -EFAULT;
goto out;
}
}
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int iscsi_initiator_allowed(void __user *ptr)
{
int err = 0, rc;
struct iscsi_kern_initiator_info cinfo;
struct iscsi_target *target;
TRACE_ENTRY();
rc = copy_from_user(&cinfo, ptr, sizeof(cinfo));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out;
}
#ifdef __COVERITY__
/* To suppress a Coverity "tainted scalar" complaint. */
if (cinfo.full_initiator_name[sizeof(cinfo.full_initiator_name) - 1]) {
err = -EINVAL;
goto out_free;
}
#endif
cinfo.full_initiator_name[sizeof(cinfo.full_initiator_name) - 1] = '\0';
target = target_lookup_by_id(cinfo.tid);
if (!target) {
PRINT_ERROR("Target %d not found", cinfo.tid);
err = -ENOENT;
goto out;
}
err = scst_initiator_has_luns(target->scst_tgt, cinfo.full_initiator_name);
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int mgmt_cmd_callback(void __user *ptr)
{
int err = 0, rc;
struct iscsi_kern_mgmt_cmd_res_info cinfo;
struct scst_sysfs_user_info *info;
TRACE_ENTRY();
rc = copy_from_user(&cinfo, ptr, sizeof(cinfo));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out;
}
cinfo.value[sizeof(cinfo.value) - 1] = '\0';
info = scst_sysfs_user_get_info(cinfo.cookie);
TRACE_DBG("cookie %u, info %p, result %d",
cinfo.cookie, info, cinfo.result);
if (!info) {
err = -EINVAL;
goto out;
}
info->info_status = 0;
if (cinfo.result != 0) {
info->info_status = cinfo.result;
goto out_complete;
}
switch (cinfo.req_cmd) {
case E_ENABLE_TARGET:
case E_DISABLE_TARGET:
{
struct iscsi_target *target;
target = target_lookup_by_id(cinfo.tid);
if (!target) {
PRINT_ERROR("Target %d not found", cinfo.tid);
err = -ENOENT;
goto out_status;
}
target->tgt_enabled = cinfo.req_cmd == E_ENABLE_TARGET;
break;
}
case E_GET_ATTR_VALUE:
info->data = kstrdup(cinfo.value, GFP_KERNEL);
if (!info->data) {
PRINT_ERROR("Can't duplicate value %s", cinfo.value);
info->info_status = -ENOMEM;
goto out_complete;
}
break;
}
out_complete:
complete(&info->info_completion);
out:
TRACE_EXIT_RES(err);
return err;
out_status:
info->info_status = err;
goto out_complete;
}
static ssize_t iscsi_attr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int pos;
struct iscsi_attr *tgt_attr;
void *value;
TRACE_ENTRY();
tgt_attr = container_of(attr, struct iscsi_attr, attr);
pos = iscsi_sysfs_send_event(tgt_attr->target ? tgt_attr->target->tid : 0,
E_GET_ATTR_VALUE, tgt_attr->name, NULL, &value);
if (pos != 0)
goto out;
pos = sysfs_emit(buf, "%s\n", (char *)value);
kfree(value);
out:
TRACE_EXIT_RES(pos);
return pos;
}
static ssize_t iscsi_attr_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,
size_t count)
{
int res;
char *buffer;
struct iscsi_attr *tgt_attr;
TRACE_ENTRY();
buffer = kzalloc(count + 1, GFP_KERNEL);
if (!buffer) {
res = -ENOMEM;
goto out;
}
memcpy(buffer, buf, count);
buffer[count] = '\0';
tgt_attr = container_of(attr, struct iscsi_attr, attr);
TRACE_DBG("attr %s, buffer %s", tgt_attr->attr.attr.name, buffer);
res = iscsi_sysfs_send_event(tgt_attr->target ? tgt_attr->target->tid : 0,
E_SET_ATTR_VALUE, tgt_attr->name, buffer, NULL);
kfree(buffer);
if (res == 0)
res = count;
out:
TRACE_EXIT_RES(res);
return res;
}
/*
* target_mgmt_mutex supposed to be locked. If target != 0, target_mutex
* supposed to be locked as well.
*/
int iscsi_add_attr(struct iscsi_target *target, const struct iscsi_kern_attr *attr_info)
{
int res = 0;
struct iscsi_attr *tgt_attr;
struct list_head *attrs_list;
const char *name;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key __key;
#endif
TRACE_ENTRY();
if (target) {
attrs_list = &target->attrs_list;
name = target->name;
} else {
attrs_list = &iscsi_attrs_list;
name = "global";
}
list_for_each_entry(tgt_attr, attrs_list, attrs_list_entry) {
/* Both for sure NULL-terminated */
if (strcmp(tgt_attr->name, attr_info->name) == 0) {
PRINT_ERROR("Attribute %s for %s already exist",
attr_info->name, name);
res = -EEXIST;
goto out;
}
}
TRACE_DBG("Adding %s's attr %s with mode %x", name,
attr_info->name, attr_info->mode);
tgt_attr = kzalloc(sizeof(*tgt_attr), GFP_KERNEL);
if (!tgt_attr) {
PRINT_ERROR("Unable to allocate user (size %zd)",
sizeof(*tgt_attr));
res = -ENOMEM;
goto out;
}
tgt_attr->target = target;
tgt_attr->name = kstrdup(attr_info->name, GFP_KERNEL);
if (!tgt_attr->name) {
PRINT_ERROR("Unable to allocate attr %s name/value (target %s)",
attr_info->name, name);
res = -ENOMEM;
goto out_free;
}
list_add(&tgt_attr->attrs_list_entry, attrs_list);
tgt_attr->attr.attr.name = tgt_attr->name;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
tgt_attr->attr.attr.key = &__key;
#endif
tgt_attr->attr.attr.mode = attr_info->mode & 0666;
tgt_attr->attr.show = iscsi_attr_show;
tgt_attr->attr.store = iscsi_attr_store;
TRACE_DBG("tgt_attr %p, attr %p", tgt_attr, &tgt_attr->attr.attr);
res = sysfs_create_file(target ? scst_sysfs_get_tgt_kobj(target->scst_tgt) :
scst_sysfs_get_tgtt_kobj(&iscsi_template),
&tgt_attr->attr.attr);
if (res != 0) {
PRINT_ERROR("Unable to create file '%s' for target '%s'",
tgt_attr->attr.attr.name, name);
goto out_del;
}
out:
TRACE_EXIT_RES(res);
return res;
out_del:
list_del(&tgt_attr->attrs_list_entry);
out_free:
kfree(tgt_attr->name);
kfree(tgt_attr);
goto out;
}
void __iscsi_del_attr(struct iscsi_target *target, struct iscsi_attr *tgt_attr)
{
TRACE_ENTRY();
TRACE_DBG("Deleting attr %s (target %s, tgt_attr %p, attr %p)",
tgt_attr->name, target ? target->name : "global",
tgt_attr, &tgt_attr->attr.attr);
list_del(&tgt_attr->attrs_list_entry);
sysfs_remove_file(target ? scst_sysfs_get_tgt_kobj(target->scst_tgt) :
scst_sysfs_get_tgtt_kobj(&iscsi_template), &tgt_attr->attr.attr);
kfree(tgt_attr->name);
kfree(tgt_attr);
TRACE_EXIT();
}
/*
* target_mgmt_mutex supposed to be locked. If target != 0, target_mutex
* supposed to be locked as well.
*/
static int iscsi_del_attr(struct iscsi_target *target, const char *attr_name)
{
int res = 0;
struct iscsi_attr *tgt_attr, *a;
struct list_head *attrs_list;
TRACE_ENTRY();
if (target)
attrs_list = &target->attrs_list;
else
attrs_list = &iscsi_attrs_list;
tgt_attr = NULL;
list_for_each_entry(a, attrs_list, attrs_list_entry) {
/* Both for sure NULL-terminated */
if (strcmp(a->name, attr_name) == 0) {
tgt_attr = a;
break;
}
}
if (!tgt_attr) {
PRINT_ERROR("attr %s not found (target %s)",
attr_name, target ? target->name : "global");
res = -ENOENT;
goto out;
}
__iscsi_del_attr(target, tgt_attr);
out:
TRACE_EXIT_RES(res);
return res;
}
/* target_mgmt_mutex supposed to be locked */
static int iscsi_attr_cmd(void __user *ptr, unsigned int cmd)
{
int rc, err = 0;
struct iscsi_kern_attr_info info;
struct iscsi_target *target;
struct scst_sysfs_user_info *i = NULL;
TRACE_ENTRY();
rc = copy_from_user(&info, ptr, sizeof(info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out;
}
info.attr.name[sizeof(info.attr.name) - 1] = '\0';
if (info.cookie != 0) {
i = scst_sysfs_user_get_info(info.cookie);
TRACE_DBG("cookie %u, uinfo %p", info.cookie, i);
if (!i) {
err = -EINVAL;
goto out;
}
}
target = target_lookup_by_id(info.tid);
if (target)
mutex_lock(&target->target_mutex);
switch (cmd) {
case ISCSI_ATTR_ADD:
err = iscsi_add_attr(target, &info.attr);
break;
case ISCSI_ATTR_DEL:
err = iscsi_del_attr(target, info.attr.name);
break;
default:
sBUG();
}
if (target)
mutex_unlock(&target->target_mutex);
if (i) {
i->info_status = err;
complete(&i->info_completion);
}
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int add_target(void __user *ptr)
{
int err, rc;
struct iscsi_kern_target_info *info;
struct scst_sysfs_user_info *uinfo;
TRACE_ENTRY();
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
PRINT_ERROR("Can't alloc info (size %zd)", sizeof(*info));
err = -ENOMEM;
goto out;
}
rc = copy_from_user(info, ptr, sizeof(*info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out_free;
}
if (target_lookup_by_id(info->tid)) {
PRINT_ERROR("Target %u already exist!", info->tid);
err = -EEXIST;
goto out_free;
}
info->name[sizeof(info->name) - 1] = '\0';
if (info->cookie != 0) {
uinfo = scst_sysfs_user_get_info(info->cookie);
TRACE_DBG("cookie %u, uinfo %p", info->cookie, uinfo);
if (!uinfo) {
err = -EINVAL;
goto out_free;
}
} else {
uinfo = NULL;
}
#ifdef __COVERITY__
/* To suppress a Coverity "tainted scalar" complaint (CID 344743). */
if (info->attrs_num > 65536) {
err = -EINVAL;
goto out_free;
}
#endif
err = __add_target(info);
if (uinfo) {
uinfo->info_status = err;
complete(&uinfo->info_completion);
}
out_free:
kfree(info);
out:
TRACE_EXIT_RES(err);
return err;
}
/* target_mgmt_mutex supposed to be locked */
static int del_target(void __user *ptr)
{
int err, rc;
struct iscsi_kern_target_info info;
struct scst_sysfs_user_info *uinfo;
TRACE_ENTRY();
rc = copy_from_user(&info, ptr, sizeof(info));
if (rc != 0) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
err = -EFAULT;
goto out;
}
info.name[sizeof(info.name) - 1] = '\0';
if (info.cookie != 0) {
uinfo = scst_sysfs_user_get_info(info.cookie);
TRACE_DBG("cookie %u, uinfo %p", info.cookie, uinfo);
if (!uinfo) {
err = -EINVAL;
goto out;
}
} else {
uinfo = NULL;
}
err = __del_target(info.tid);
if (uinfo) {
uinfo->info_status = err;
complete(&uinfo->info_completion);
}
out:
TRACE_EXIT_RES(err);
return err;
}
static int iscsi_register(void __user *arg)
{
struct iscsi_kern_register_info reg;
char ver[sizeof(ISCSI_SCST_INTERFACE_VERSION) + 1];
int res, rc;
TRACE_ENTRY();
rc = copy_from_user(&reg, arg, sizeof(reg));
if (rc != 0) {
PRINT_ERROR("%s", "Unable to get register info");
res = -EFAULT;
goto out;
}
rc = copy_from_user(ver, (void __user *)(unsigned long)reg.version, sizeof(ver));
if (rc != 0) {
PRINT_ERROR("%s", "Unable to get version string");
res = -EFAULT;
goto out;
}
ver[sizeof(ver) - 1] = '\0';
if (strcmp(ver, ISCSI_SCST_INTERFACE_VERSION) != 0) {
PRINT_ERROR("Incorrect version of user space %s (expected %s)",
ver, ISCSI_SCST_INTERFACE_VERSION);
res = -EINVAL;
goto out;
}
memset(&reg, 0, sizeof(reg));
reg.max_data_seg_len = ISCSI_CONN_IOV_MAX << PAGE_SHIFT;
/*
* In iSCSI all LUs in a session share queue depth, so let's not
* limit it too much for thousands of LUs VMware and other similar
* systems cases.
*/
#if 0
reg.max_queued_cmds = scst_get_max_lun_commands(NULL, NO_SUCH_LUN);
#else
reg.max_queued_cmds = MAX_NR_QUEUED_CMNDS;
#endif
res = 0;
rc = copy_to_user(arg, &reg, sizeof(reg));
if (rc != 0) {
PRINT_ERROR("Failed to copy to user %d bytes", rc);
res = -EFAULT;
goto out;
}
out:
TRACE_EXIT_RES(res);
return res;
}
static long iscsi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long err;
TRACE_ENTRY();
if (cmd == REGISTER_USERD) {
err = iscsi_register((void __user *)arg);
goto out;
}
err = mutex_lock_interruptible(&target_mgmt_mutex);
if (err < 0)
goto out;
switch (cmd) {
case ADD_TARGET:
err = add_target((void __user *)arg);
break;
case DEL_TARGET:
err = del_target((void __user *)arg);
break;
case ISCSI_ATTR_ADD:
case ISCSI_ATTR_DEL:
err = iscsi_attr_cmd((void __user *)arg, cmd);
break;
case MGMT_CMD_CALLBACK:
err = mgmt_cmd_callback((void __user *)arg);
break;
case ISCSI_INITIATOR_ALLOWED:
err = iscsi_initiator_allowed((void __user *)arg);
break;
case ADD_SESSION:
err = add_session((void __user *)arg);
break;
case DEL_SESSION:
err = del_session((void __user *)arg);
break;
case ISCSI_PARAM_SET:
err = iscsi_params_config((void __user *)arg, 1);
break;
case ISCSI_PARAM_GET:
err = iscsi_params_config((void __user *)arg, 0);
break;
case ADD_CONN:
err = add_conn((void __user *)arg);
break;
case DEL_CONN:
err = del_conn((void __user *)arg);
break;
default:
PRINT_ERROR("Invalid ioctl cmd %x", cmd);
err = -EINVAL;
goto out_unlock;
}
out_unlock:
mutex_unlock(&target_mgmt_mutex);
out:
TRACE_EXIT_RES(err);
return err;
}
static int iscsi_open(struct inode *inode, struct file *file)
{
bool already;
mutex_lock(&target_mgmt_mutex);
already = (ctr_open_state != ISCSI_CTR_OPEN_STATE_CLOSED);
if (!already)
ctr_open_state = ISCSI_CTR_OPEN_STATE_OPEN;
mutex_unlock(&target_mgmt_mutex);
if (already) {
PRINT_WARNING("Attempt to second open the control device!");
return -EBUSY;
}
return 0;
}
static int iscsi_release(struct inode *inode, struct file *filp)
{
struct iscsi_attr *attr, *t;
TRACE(TRACE_MGMT, "%s", "Releasing allocated resources");
mutex_lock(&target_mgmt_mutex);
ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSING;
mutex_unlock(&target_mgmt_mutex);
target_del_all();
mutex_lock(&target_mgmt_mutex);
list_for_each_entry_safe(attr, t, &iscsi_attrs_list, attrs_list_entry)
__iscsi_del_attr(NULL, attr);
ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSED;
mutex_unlock(&target_mgmt_mutex);
return 0;
}
const struct file_operations ctr_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = iscsi_ioctl,
.compat_ioctl = iscsi_ioctl,
.open = iscsi_open,
.release = iscsi_release,
};
#ifdef CONFIG_SCST_DEBUG
static void iscsi_dump_char(int ch, unsigned char *text, int *pos)
{
int i = *pos;
if (ch < 0) {
while ((i % 16) != 0) {
pr_cont(" ");
text[i] = ' ';
i++;
if ((i % 16) == 0)
pr_cont(" | %.16s |\n", text);
else if ((i % 4) == 0)
pr_cont(" |");
}
i = 0;
goto out;
}
text[i] = (ch < 0x20 || (ch >= 0x80 && ch <= 0xa0)) ? ' ' : ch;
pr_cont(" %02x", ch);
i++;
if ((i % 16) == 0) {
pr_cont(" | %.16s |\n", text);
i = 0;
} else if ((i % 4) == 0) {
pr_cont(" |");
}
out:
*pos = i;
}
void iscsi_dump_pdu(struct iscsi_pdu *pdu)
{
unsigned char text[16];
int pos = 0;
if (trace_flag & TRACE_D_DUMP_PDU) {
unsigned char *buf;
int i;
buf = (void *)&pdu->bhs;
pr_debug("BHS: (%p,%zd)\n", buf, sizeof(pdu->bhs));
for (i = 0; i < (int)sizeof(pdu->bhs); i++)
iscsi_dump_char(*buf++, text, &pos);
iscsi_dump_char(-1, text, &pos);
buf = (void *)pdu->ahs;
pr_debug("AHS: (%p,%d)\n", buf, pdu->ahssize);
for (i = 0; i < pdu->ahssize; i++)
iscsi_dump_char(*buf++, text, &pos);
iscsi_dump_char(-1, text, &pos);
pr_debug("Data: (%d)\n", pdu->datasize);
}
}
unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(struct iscsi_cmnd *cmnd)
{
unsigned long flag;
if (cmnd->cmd_req)
cmnd = cmnd->cmd_req;
if (!cmnd->scst_cmd) {
flag = TRACE_MGMT_DEBUG;
} else {
int status = scst_cmd_get_status(cmnd->scst_cmd);
if (status == SAM_STAT_TASK_SET_FULL || status == SAM_STAT_BUSY)
flag = TRACE_FLOW_CONTROL;
else
flag = TRACE_MGMT_DEBUG;
}
return flag;
}
#endif /* CONFIG_SCST_DEBUG */