mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-19 03:31:26 +00:00
Merge branch 'svn-trunk'
Conflicts: www/contributing.html
This commit is contained in:
@@ -336,6 +336,50 @@ You can see description of possible subcommands in section
|
||||
<ref id="subcommands" name="subcommands">.
|
||||
|
||||
|
||||
<sect1> SCST_USER_REPLY_AND_GET_MULTI
|
||||
|
||||
<p>
|
||||
SCST_USER_REPLY_AND_GET_MULTI allows at one call reply on the multiple
|
||||
subcommands from SCST and get the multiple next subcommands.
|
||||
|
||||
Its argument is defined as:
|
||||
|
||||
<verb>
|
||||
struct scst_user_get_multi {
|
||||
aligned_u64 preplies;
|
||||
int16_t replies_cnt;
|
||||
int16_t replies_done;
|
||||
int16_t cmds_cnt;
|
||||
struct scst_user_get_cmd cmds[0];
|
||||
},
|
||||
</verb>
|
||||
|
||||
where:
|
||||
|
||||
<itemize>
|
||||
|
||||
<item> <bf/preplies/ - pointer to array of replies with size
|
||||
<it/replies_cnt/. See SCST_USER_REPLY_CMD for description of struct
|
||||
scst_user_reply_cmd fields
|
||||
|
||||
<item> <bf/replies_cnt/ - number of entries in <it/preplies/ array. If 0,
|
||||
there are no replies
|
||||
|
||||
<item> <bf/replies_done/ - returns how many replies were processed by SCST. If there are
|
||||
unprocessed replies, the user space device handler must retry the
|
||||
unprocessed replies.
|
||||
|
||||
<item> <bf/cmds_cnt/ - on entry: number of available entries in <it/cmds/ array; on exit -
|
||||
number of valid subcommands in <it/cmds/ array
|
||||
|
||||
<item> <bf/cmds/ - returned array of subcommands
|
||||
|
||||
</itemize>
|
||||
|
||||
Returns 0 on success or -1 in case of error, and errno is set
|
||||
appropriately.
|
||||
|
||||
|
||||
<sect1> SCST_USER_REPLY_CMD
|
||||
|
||||
<p>
|
||||
|
||||
@@ -303,6 +303,15 @@ union scst_user_prealloc_buffer {
|
||||
struct scst_user_prealloc_buffer_out out;
|
||||
};
|
||||
|
||||
struct scst_user_get_multi {
|
||||
aligned_u64 preplies; /* in */
|
||||
int16_t replies_cnt; /* in */
|
||||
int16_t replies_done; /* out */
|
||||
int16_t cmds_cnt; /* in/out */
|
||||
int16_t pad;
|
||||
struct scst_user_get_cmd cmds[0]; /* out */
|
||||
};
|
||||
|
||||
#define SCST_USER_REGISTER_DEVICE _IOW('u', 1, struct scst_user_dev_desc)
|
||||
#define SCST_USER_UNREGISTER_DEVICE _IO('u', 2)
|
||||
#define SCST_USER_SET_OPTIONS _IOW('u', 3, struct scst_user_opt)
|
||||
@@ -313,6 +322,7 @@ union scst_user_prealloc_buffer {
|
||||
#define SCST_USER_DEVICE_CAPACITY_CHANGED _IO('u', 8)
|
||||
#define SCST_USER_GET_EXTENDED_CDB _IOWR('u', 9, struct scst_user_get_ext_cdb)
|
||||
#define SCST_USER_PREALLOC_BUFFER _IOWR('u', 10, union scst_user_prealloc_buffer)
|
||||
#define SCST_USER_REPLY_AND_GET_MULTI _IOWR('u', 11, struct scst_user_get_multi)
|
||||
|
||||
/* Values for scst_user_get_cmd.subcode */
|
||||
#define SCST_USER_ATTACH_SESS \
|
||||
|
||||
@@ -217,7 +217,6 @@ static int dev_usr_parse(struct scst_cmd *cmd);
|
||||
static struct kmem_cache *user_dev_cachep;
|
||||
|
||||
static struct kmem_cache *user_cmd_cachep;
|
||||
static struct kmem_cache *user_get_cmd_cachep;
|
||||
|
||||
static const struct file_operations dev_user_fops = {
|
||||
.poll = dev_user_poll,
|
||||
@@ -1908,18 +1907,18 @@ again:
|
||||
return u;
|
||||
}
|
||||
|
||||
static inline int test_cmd_threads(struct scst_user_dev *dev)
|
||||
static inline int test_cmd_threads(struct scst_user_dev *dev, bool can_block)
|
||||
{
|
||||
int res = !list_empty(&dev->udev_cmd_threads.active_cmd_list) ||
|
||||
!list_empty(&dev->ready_cmd_list) ||
|
||||
!dev->blocking || dev->cleanup_done ||
|
||||
!can_block || !dev->blocking || dev->cleanup_done ||
|
||||
signal_pending(current);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Called under udev_cmd_threads.cmd_list_lock and IRQ off */
|
||||
static int dev_user_get_next_cmd(struct scst_user_dev *dev,
|
||||
struct scst_user_cmd **ucmd)
|
||||
struct scst_user_cmd **ucmd, bool can_block)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
@@ -1927,7 +1926,7 @@ static int dev_user_get_next_cmd(struct scst_user_dev *dev,
|
||||
|
||||
while (1) {
|
||||
wait_event_locked(dev->udev_cmd_threads.cmd_list_waitQ,
|
||||
test_cmd_threads(dev), lock_irq,
|
||||
test_cmd_threads(dev, can_block), lock_irq,
|
||||
dev->udev_cmd_threads.cmd_list_lock);
|
||||
|
||||
dev_user_process_scst_commands(dev);
|
||||
@@ -1936,7 +1935,7 @@ static int dev_user_get_next_cmd(struct scst_user_dev *dev,
|
||||
if (*ucmd != NULL)
|
||||
break;
|
||||
|
||||
if (!dev->blocking || dev->cleanup_done) {
|
||||
if (!can_block || !dev->blocking || dev->cleanup_done) {
|
||||
res = -EAGAIN;
|
||||
TRACE_DBG("No ready commands, returning %d", res);
|
||||
break;
|
||||
@@ -1953,13 +1952,66 @@ static int dev_user_get_next_cmd(struct scst_user_dev *dev,
|
||||
return res;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
static int dev_user_get_cmd_to_user(struct scst_user_dev *dev,
|
||||
void __user *where, bool can_block)
|
||||
{
|
||||
int res;
|
||||
struct scst_user_cmd *ucmd;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
again:
|
||||
res = dev_user_get_next_cmd(dev, &ucmd, can_block);
|
||||
if (res == 0) {
|
||||
int len, rc;
|
||||
/*
|
||||
* A misbehaving user space handler can make ucmd to get dead
|
||||
* immediately after we released the lock, which can lead to
|
||||
* copy of dead data to the user space, which can lead to a
|
||||
* leak of sensitive information.
|
||||
*/
|
||||
if (unlikely(ucmd_get_check(ucmd))) {
|
||||
/* Oops, this ucmd is already being destroyed. Retry. */
|
||||
goto again;
|
||||
}
|
||||
spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
|
||||
EXTRACHECKS_BUG_ON(ucmd->user_cmd_payload_len == 0);
|
||||
|
||||
len = ucmd->user_cmd_payload_len;
|
||||
TRACE_DBG("ucmd %p (user_cmd %p), payload_len %d (len %d)",
|
||||
ucmd, &ucmd->user_cmd, ucmd->user_cmd_payload_len, len);
|
||||
TRACE_BUFFER("UCMD", &ucmd->user_cmd, len);
|
||||
rc = copy_to_user(where, &ucmd->user_cmd, len);
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Copy to user failed (%d), requeuing ucmd "
|
||||
"%p back to head of ready cmd list", res, ucmd);
|
||||
res = -EFAULT;
|
||||
/* Requeue ucmd back */
|
||||
spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
list_add(&ucmd->ready_cmd_list_entry,
|
||||
&dev->ready_cmd_list);
|
||||
spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
}
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
else
|
||||
ucmd->user_cmd_payload_len = 0;
|
||||
#endif
|
||||
ucmd_put(ucmd);
|
||||
} else
|
||||
spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dev_user_reply_get_cmd(struct file *file, void __user *arg)
|
||||
{
|
||||
int res = 0, rc;
|
||||
struct scst_user_dev *dev;
|
||||
struct scst_user_get_cmd *cmd;
|
||||
struct scst_user_reply_cmd *reply;
|
||||
struct scst_user_cmd *ucmd;
|
||||
struct scst_user_reply_cmd reply;
|
||||
uint64_t ureply;
|
||||
|
||||
TRACE_ENTRY();
|
||||
@@ -1982,79 +2034,126 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg)
|
||||
TRACE_DBG("ureply %lld (dev %s)", (unsigned long long int)ureply,
|
||||
dev->name);
|
||||
|
||||
cmd = kmem_cache_alloc(user_get_cmd_cachep, GFP_KERNEL);
|
||||
if (unlikely(cmd == NULL)) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ureply != 0) {
|
||||
unsigned long u = (unsigned long)ureply;
|
||||
reply = (struct scst_user_reply_cmd *)cmd;
|
||||
rc = copy_from_user(reply, (void __user *)u, sizeof(*reply));
|
||||
rc = copy_from_user(&reply, (void __user *)u, sizeof(reply));
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Failed to copy %d user's bytes", rc);
|
||||
res = -EFAULT;
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_BUFFER("Reply", reply, sizeof(*reply));
|
||||
TRACE_BUFFER("Reply", &reply, sizeof(reply));
|
||||
|
||||
res = dev_user_process_reply(dev, reply);
|
||||
res = dev_user_process_reply(dev, &reply);
|
||||
if (unlikely(res < 0))
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
kmem_cache_free(user_get_cmd_cachep, cmd);
|
||||
res = dev_user_get_cmd_to_user(dev, arg, true);
|
||||
|
||||
spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
again:
|
||||
res = dev_user_get_next_cmd(dev, &ucmd);
|
||||
if (res == 0) {
|
||||
int len;
|
||||
/*
|
||||
* A misbehaving user space handler can make ucmd to get dead
|
||||
* immediately after we released the lock, which can lead to
|
||||
* copy of dead data to the user space, which can lead to a
|
||||
* leak of sensitive information.
|
||||
*/
|
||||
if (unlikely(ucmd_get_check(ucmd))) {
|
||||
/* Oops, this ucmd is already being destroyed. Retry. */
|
||||
goto again;
|
||||
}
|
||||
spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
EXTRACHECKS_BUG_ON(ucmd->user_cmd_payload_len == 0);
|
||||
static int dev_user_reply_get_multi(struct file *file, void __user *arg)
|
||||
{
|
||||
int res = 0, rc;
|
||||
struct scst_user_dev *dev;
|
||||
struct scst_user_reply_cmd __user *replies;
|
||||
int16_t i, replies_cnt, replies_done = 0, cmds_cnt = 0;
|
||||
|
||||
len = ucmd->user_cmd_payload_len;
|
||||
TRACE_DBG("ucmd %p (user_cmd %p), payload_len %d (len %d)",
|
||||
ucmd, &ucmd->user_cmd, ucmd->user_cmd_payload_len, len);
|
||||
TRACE_BUFFER("UCMD", &ucmd->user_cmd, len);
|
||||
rc = copy_to_user(arg, &ucmd->user_cmd, len);
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev = (struct scst_user_dev *)file->private_data;
|
||||
res = dev_user_check_reg(dev);
|
||||
if (unlikely(res != 0))
|
||||
goto out;
|
||||
|
||||
res = get_user(replies_cnt, (int16_t __user *)
|
||||
&((struct scst_user_get_multi *)arg)->replies_cnt);
|
||||
if (unlikely(res < 0)) {
|
||||
PRINT_ERROR("%s", "Unable to get replies_cnt");
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = get_user(cmds_cnt, (int16_t __user *)
|
||||
&((struct scst_user_get_multi *)arg)->cmds_cnt);
|
||||
if (unlikely(res < 0)) {
|
||||
PRINT_ERROR("%s", "Unable to get cmds_cnt");
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_DBG("replies %d, space %d (dev %s)",
|
||||
replies_cnt, cmds_cnt, dev->name);
|
||||
|
||||
if (replies_cnt == 0)
|
||||
goto get_cmds;
|
||||
|
||||
/* get_user() can't be used with 64-bit values on x86_32 */
|
||||
rc = copy_from_user(&replies, (uint64_t __user *)
|
||||
&((struct scst_user_get_multi __user *)arg)->preplies, sizeof(&replies));
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("%s", "Unable to get preply");
|
||||
res = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < replies_cnt; i++) {
|
||||
struct scst_user_reply_cmd reply;
|
||||
|
||||
rc = copy_from_user(&reply, &replies[i], sizeof(reply));
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Copy to user failed (%d), requeuing ucmd "
|
||||
"%p back to head of ready cmd list", rc, ucmd);
|
||||
PRINT_ERROR("Unable to get reply %d", i);
|
||||
res = -EFAULT;
|
||||
/* Requeue ucmd back */
|
||||
spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
list_add(&ucmd->ready_cmd_list_entry,
|
||||
&dev->ready_cmd_list);
|
||||
spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
goto out_part_replies_done;
|
||||
}
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
else
|
||||
ucmd->user_cmd_payload_len = 0;
|
||||
#endif
|
||||
ucmd_put(ucmd);
|
||||
} else
|
||||
spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
|
||||
TRACE_BUFFER("Reply", &reply, sizeof(reply));
|
||||
|
||||
res = dev_user_process_reply(dev, &reply);
|
||||
if (unlikely(res < 0))
|
||||
goto out_part_replies_done;
|
||||
|
||||
replies_done++;
|
||||
}
|
||||
|
||||
TRACE_DBG("Returning %d replies_done", replies_done);
|
||||
res = put_user(replies_done, (int16_t __user *)
|
||||
&((struct scst_user_get_multi *)arg)->replies_done);
|
||||
if (unlikely(res < 0))
|
||||
goto out;
|
||||
|
||||
get_cmds:
|
||||
for (i = 0; i < cmds_cnt; i++) {
|
||||
res = dev_user_get_cmd_to_user(dev,
|
||||
&((struct scst_user_get_multi __user *)arg)->cmds[i], i == 0);
|
||||
if (res != 0) {
|
||||
if ((res == -EAGAIN) && (i > 0))
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_DBG("Returning %d cmds_ret", i);
|
||||
rc = put_user(i, (int16_t __user *)
|
||||
&((struct scst_user_get_multi *)arg)->cmds_cnt);
|
||||
if (unlikely(rc < 0)) {
|
||||
res = rc; /* this error is more important */
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_free:
|
||||
kmem_cache_free(user_get_cmd_cachep, cmd);
|
||||
out_part_replies_done:
|
||||
TRACE_DBG("Partial returning %d replies_done", replies_done);
|
||||
put_user(replies_done, (int16_t __user *)
|
||||
&((struct scst_user_get_multi *)arg)->replies_done);
|
||||
rc = put_user(0, (int16_t __user *)
|
||||
&((struct scst_user_get_multi *)arg)->cmds_cnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2076,6 +2175,11 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
|
||||
res = dev_user_reply_cmd(file, (void __user *)arg);
|
||||
break;
|
||||
|
||||
case SCST_USER_REPLY_AND_GET_MULTI:
|
||||
TRACE_DBG("%s", "REPLY_AND_GET_MULTI");
|
||||
res = dev_user_reply_get_multi(file, (void __user *)arg);
|
||||
break;
|
||||
|
||||
case SCST_USER_GET_EXTENDED_CDB:
|
||||
TRACE_DBG("%s", "GET_EXTENDED_CDB");
|
||||
res = dev_user_get_ext_cdb(file, (void __user *)arg);
|
||||
@@ -3512,7 +3616,7 @@ static int dev_user_process_cleanup(struct scst_user_dev *dev)
|
||||
|
||||
spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
|
||||
|
||||
rc = dev_user_get_next_cmd(dev, &ucmd);
|
||||
rc = dev_user_get_next_cmd(dev, &ucmd, false);
|
||||
if (rc == 0)
|
||||
dev_user_unjam_cmd(ucmd, 1, NULL);
|
||||
|
||||
@@ -3754,18 +3858,11 @@ static int __init init_scst_user(void)
|
||||
goto out_dev_cache;
|
||||
}
|
||||
|
||||
user_get_cmd_cachep = KMEM_CACHE(max_get_reply,
|
||||
SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);
|
||||
if (user_get_cmd_cachep == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out_cache;
|
||||
}
|
||||
|
||||
dev_user_devtype.module = THIS_MODULE;
|
||||
|
||||
res = scst_register_virtual_dev_driver(&dev_user_devtype);
|
||||
if (res < 0)
|
||||
goto out_cache1;
|
||||
goto out_cache;
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
res = scst_dev_handler_build_std_proc(&dev_user_devtype);
|
||||
@@ -3845,9 +3942,6 @@ out_proc:
|
||||
out_unreg:
|
||||
scst_unregister_dev_driver(&dev_user_devtype);
|
||||
|
||||
out_cache1:
|
||||
kmem_cache_destroy(user_get_cmd_cachep);
|
||||
|
||||
out_cache:
|
||||
kmem_cache_destroy(user_cmd_cachep);
|
||||
|
||||
@@ -3879,7 +3973,6 @@ static void __exit exit_scst_user(void)
|
||||
#endif
|
||||
scst_unregister_virtual_dev_driver(&dev_user_devtype);
|
||||
|
||||
kmem_cache_destroy(user_get_cmd_cachep);
|
||||
kmem_cache_destroy(user_cmd_cachep);
|
||||
kmem_cache_destroy(user_dev_cachep);
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#
|
||||
# SCSI target mid-level makefile
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2007 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
# Copyright (C) 2007 - 2015 SanDisk 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, 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
|
||||
|
||||
@@ -48,6 +48,26 @@ static void exec_read(struct vdisk_cmd *vcmd, loff_t loff);
|
||||
static void exec_write(struct vdisk_cmd *vcmd, loff_t loff);
|
||||
static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff);
|
||||
|
||||
static int open_dev_fd(struct vdisk_dev *dev)
|
||||
{
|
||||
int res;
|
||||
int open_flags = O_LARGEFILE;
|
||||
|
||||
if (dev->rd_only_flag)
|
||||
open_flags |= O_RDONLY;
|
||||
else
|
||||
open_flags |= O_RDWR;
|
||||
if (dev->o_direct_flag)
|
||||
open_flags |= O_DIRECT;
|
||||
if (dev->wt_flag)
|
||||
open_flags |= O_DSYNC;
|
||||
|
||||
TRACE_DBG("Opening file %s, flags 0x%x", dev->file_name, open_flags);
|
||||
res = open(dev->file_name, open_flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void set_cmd_error_status(struct scst_user_scsi_cmd_reply_exec *reply,
|
||||
int status)
|
||||
{
|
||||
@@ -400,7 +420,7 @@ static int do_exec(struct vdisk_cmd *vcmd)
|
||||
break;
|
||||
case REPORT_LUNS:
|
||||
default:
|
||||
TRACE_DBG("Invalid opcode %d", opcode);
|
||||
TRACE_DBG("Invalid opcode 0x%x", opcode);
|
||||
set_cmd_error(vcmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
break;
|
||||
}
|
||||
@@ -602,35 +622,92 @@ reply:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int open_dev_fd(struct vdisk_dev *dev)
|
||||
static int process_cmd(struct vdisk_cmd *vcmd)
|
||||
{
|
||||
int res;
|
||||
int open_flags = O_LARGEFILE;
|
||||
struct scst_user_get_cmd *cmd = vcmd->cmd;
|
||||
struct scst_user_reply_cmd *reply = vcmd->reply;
|
||||
int res = 0;
|
||||
|
||||
if (dev->rd_only_flag)
|
||||
open_flags |= O_RDONLY;
|
||||
else
|
||||
open_flags |= O_RDWR;
|
||||
if (dev->o_direct_flag)
|
||||
open_flags |= O_DIRECT;
|
||||
if (dev->wt_flag)
|
||||
open_flags |= O_DSYNC;
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("Opening file %s, flags 0x%x", dev->file_name, open_flags);
|
||||
res = open(dev->file_name, open_flags);
|
||||
TRACE_BUFFER("Received cmd", &cmd, sizeof(cmd));
|
||||
|
||||
switch(cmd->subcode) {
|
||||
case SCST_USER_EXEC:
|
||||
if (cmd->exec_cmd.data_direction & SCST_DATA_WRITE) {
|
||||
TRACE_BUFFER("Received cmd data",
|
||||
(void *)(unsigned long)cmd->exec_cmd.pbuf,
|
||||
cmd->exec_cmd.bufflen);
|
||||
}
|
||||
res = do_exec(vcmd);
|
||||
if ((reply->exec_reply.resp_data_len != 0) && (res != 150)) {
|
||||
TRACE_BUFFER("Reply data",
|
||||
(void *)(unsigned long)reply->exec_reply.pbuf,
|
||||
reply->exec_reply.resp_data_len);
|
||||
}
|
||||
break;
|
||||
|
||||
case SCST_USER_ALLOC_MEM:
|
||||
res = do_alloc_mem(vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_PARSE:
|
||||
res = do_parse(vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_ON_CACHED_MEM_FREE:
|
||||
res = do_cached_mem_free(vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_ON_FREE_CMD:
|
||||
res = do_on_free_cmd(vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_TASK_MGMT_RECEIVED:
|
||||
res = do_tm(vcmd, 0);
|
||||
break;
|
||||
|
||||
case SCST_USER_TASK_MGMT_DONE:
|
||||
res = do_tm(vcmd, 1);
|
||||
#if DEBUG_TM_FN_IGNORE
|
||||
if (dev->debug_tm_ignore) {
|
||||
sleep(15);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SCST_USER_ATTACH_SESS:
|
||||
case SCST_USER_DETACH_SESS:
|
||||
res = do_sess(vcmd);
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT_ERROR("Unknown or wrong cmd subcode %x",
|
||||
cmd->subcode);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void *main_loop(void *arg)
|
||||
{
|
||||
int res = 0;
|
||||
int res = 0, i, j;
|
||||
struct vdisk_dev *dev = (struct vdisk_dev *)arg;
|
||||
struct scst_user_get_cmd cmd;
|
||||
struct scst_user_reply_cmd reply;
|
||||
struct vdisk_cmd vcmd = { -1, &cmd, dev, &reply, {0}};
|
||||
int scst_usr_fd = dev->scst_usr_fd;
|
||||
struct pollfd pl;
|
||||
#define MULTI_CMDS_CNT 128
|
||||
struct {
|
||||
struct scst_user_reply_cmd replies[MULTI_CMDS_CNT];
|
||||
struct scst_user_get_multi multi_cmd;
|
||||
struct scst_user_get_cmd cmds[MULTI_CMDS_CNT];
|
||||
} multi;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -647,6 +724,9 @@ void *main_loop(void *arg)
|
||||
pl.events = POLLIN;
|
||||
|
||||
cmd.preply = 0;
|
||||
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
|
||||
multi.multi_cmd.replies_cnt = 0;
|
||||
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
|
||||
|
||||
while(1) {
|
||||
#ifdef DEBUG_TM_IGNORE_ALL
|
||||
@@ -661,38 +741,50 @@ void *main_loop(void *arg)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
res = ioctl(scst_usr_fd, SCST_USER_REPLY_AND_GET_CMD, &cmd);
|
||||
if (use_multi) {
|
||||
TRACE_DBG("preplies %p (first: %p), replies_cnt %d, "
|
||||
"replies_done %d, cmds_cnt %d", (void *)multi.multi_cmd.preplies,
|
||||
&multi.replies[0], multi.multi_cmd.replies_cnt,
|
||||
multi.multi_cmd.replies_done, multi.multi_cmd.cmds_cnt);
|
||||
res = ioctl(scst_usr_fd, SCST_USER_REPLY_AND_GET_MULTI, &multi.multi_cmd);
|
||||
} else
|
||||
res = ioctl(scst_usr_fd, SCST_USER_REPLY_AND_GET_CMD, &cmd);
|
||||
if (res != 0) {
|
||||
res = errno;
|
||||
switch(res) {
|
||||
case ESRCH:
|
||||
case EBUSY:
|
||||
TRACE_MGMT_DBG("SCST_USER_REPLY_AND_GET_CMD returned "
|
||||
"%d (%s)", res, strerror(res));
|
||||
TRACE_MGMT_DBG("SCST_USER returned %d (%s)", res, strerror(res));
|
||||
cmd.preply = 0;
|
||||
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
|
||||
multi.multi_cmd.replies_cnt = 0;
|
||||
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
|
||||
case EINTR:
|
||||
continue;
|
||||
case EAGAIN:
|
||||
TRACE_DBG("SCST_USER_REPLY_AND_GET_CMD returned "
|
||||
"EAGAIN (%d)", res);
|
||||
TRACE_DBG("SCST_USER returned EAGAIN (%d)", res);
|
||||
cmd.preply = 0;
|
||||
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
|
||||
multi.multi_cmd.replies_cnt = 0;
|
||||
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
|
||||
if (dev->non_blocking)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
default:
|
||||
PRINT_ERROR("SCST_USER_REPLY_AND_GET_CMD failed: "
|
||||
"%s (%d)", strerror(res), res);
|
||||
PRINT_ERROR("SCST_USER failed: %s (%d)", strerror(res), res);
|
||||
#if 1
|
||||
cmd.preply = 0;
|
||||
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
|
||||
multi.multi_cmd.replies_cnt = 0;
|
||||
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
|
||||
continue;
|
||||
#else
|
||||
goto out_close;
|
||||
#endif
|
||||
}
|
||||
again_poll:
|
||||
res = poll(&pl, 1, 2000);
|
||||
res = poll(&pl, 1, -1);
|
||||
if (res > 0)
|
||||
continue;
|
||||
else if (res == 0)
|
||||
@@ -718,74 +810,47 @@ again_poll:
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_BUFFER("Received cmd", &cmd, sizeof(cmd));
|
||||
|
||||
switch(cmd.subcode) {
|
||||
case SCST_USER_EXEC:
|
||||
if (cmd.exec_cmd.data_direction & SCST_DATA_WRITE) {
|
||||
TRACE_BUFFER("Received cmd data",
|
||||
(void *)(unsigned long)cmd.exec_cmd.pbuf,
|
||||
cmd.exec_cmd.bufflen);
|
||||
if (use_multi) {
|
||||
if (multi.multi_cmd.replies_done < multi.multi_cmd.replies_cnt) {
|
||||
TRACE_MGMT_DBG("replies_done %d < replies_cnt %d (dev %s)",
|
||||
multi.multi_cmd.replies_done, multi.multi_cmd.replies_cnt, dev->name);
|
||||
multi.multi_cmd.preplies = (uint64_t)&multi.replies[multi.multi_cmd.replies_done];
|
||||
multi.multi_cmd.replies_cnt = multi.multi_cmd.replies_cnt - multi.multi_cmd.replies_done;
|
||||
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
|
||||
continue;
|
||||
}
|
||||
res = do_exec(&vcmd);
|
||||
TRACE_DBG("cmds_cnt %d", multi.multi_cmd.cmds_cnt);
|
||||
multi.multi_cmd.preplies = (uint64_t)&multi.replies[0];
|
||||
for (i = 0, j = 0; i < multi.multi_cmd.cmds_cnt; i++, j++) {
|
||||
vcmd.cmd = &multi.cmds[i];
|
||||
vcmd.reply = &multi.replies[j];
|
||||
res = process_cmd(&vcmd);
|
||||
#ifdef DEBUG_TM_IGNORE
|
||||
if (res == 150) {
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (res != 0)
|
||||
goto out_close;
|
||||
TRACE_BUFFER("Sending reply", vcmd.reply, sizeof(reply));
|
||||
}
|
||||
multi.multi_cmd.replies_cnt = j;
|
||||
multi.multi_cmd.cmds_cnt = MULTI_CMDS_CNT;
|
||||
} else {
|
||||
res = process_cmd(&vcmd);
|
||||
#ifdef DEBUG_TM_IGNORE
|
||||
if (res == 150) {
|
||||
cmd.preply = 0;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (reply.exec_reply.resp_data_len != 0) {
|
||||
TRACE_BUFFER("Reply data",
|
||||
(void *)(unsigned long)reply.exec_reply.pbuf,
|
||||
reply.exec_reply.resp_data_len);
|
||||
}
|
||||
break;
|
||||
if (res != 0)
|
||||
goto out_close;
|
||||
|
||||
case SCST_USER_ALLOC_MEM:
|
||||
res = do_alloc_mem(&vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_PARSE:
|
||||
res = do_parse(&vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_ON_CACHED_MEM_FREE:
|
||||
res = do_cached_mem_free(&vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_ON_FREE_CMD:
|
||||
res = do_on_free_cmd(&vcmd);
|
||||
break;
|
||||
|
||||
case SCST_USER_TASK_MGMT_RECEIVED:
|
||||
res = do_tm(&vcmd, 0);
|
||||
break;
|
||||
|
||||
case SCST_USER_TASK_MGMT_DONE:
|
||||
res = do_tm(&vcmd, 1);
|
||||
#if DEBUG_TM_FN_IGNORE
|
||||
if (dev->debug_tm_ignore) {
|
||||
sleep(15);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case SCST_USER_ATTACH_SESS:
|
||||
case SCST_USER_DETACH_SESS:
|
||||
res = do_sess(&vcmd);
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT_ERROR("Unknown or wrong cmd subcode %x",
|
||||
cmd.subcode);
|
||||
goto out_close;
|
||||
cmd.preply = (unsigned long)&reply;
|
||||
TRACE_BUFFER("Sending reply", &reply, sizeof(reply));
|
||||
}
|
||||
|
||||
if (res != 0)
|
||||
goto out_close;
|
||||
|
||||
cmd.preply = (unsigned long)&reply;
|
||||
TRACE_BUFFER("Sending reply", &reply, sizeof(reply));
|
||||
}
|
||||
|
||||
out_close:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <scst_user.h>
|
||||
|
||||
@@ -116,6 +117,7 @@ struct vdisk_cmd
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
extern int vdisk_ID;
|
||||
extern bool use_multi;
|
||||
|
||||
uint32_t crc32buf(const char *buf, size_t len);
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ static int debug_tm_ignore;
|
||||
#endif
|
||||
static int non_blocking, sgv_shared, sgv_single_alloc_pages, sgv_purge_interval;
|
||||
static int sgv_disable_clustered_pool, prealloc_buffers_num, prealloc_buffer_size;
|
||||
bool use_multi = true;
|
||||
|
||||
static void *(*alloc_fn)(size_t size) = align_alloc;
|
||||
|
||||
@@ -118,6 +119,7 @@ static struct option const long_options[] =
|
||||
{"sgv_disable_clustered_pool", no_argument, 0, 'D'},
|
||||
{"prealloc_buffers", required_argument, 0, 'R'},
|
||||
{"prealloc_buffer_size", required_argument, 0, 'Z'},
|
||||
{"multi_cmd", required_argument, 0, 'M'},
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
{"debug", required_argument, 0, 'd'},
|
||||
#endif
|
||||
@@ -156,6 +158,7 @@ static void usage(void)
|
||||
printf(" -D, --sgv_disable_clustered_pool Disable clustered SGV pool\n");
|
||||
printf(" -R, --prealloc_buffers=n Prealloc n buffers\n");
|
||||
printf(" -Z, --prealloc_buffer_size=n Sets the size in KB of each prealloced buffer\n");
|
||||
printf(" -M, --multi_cmd=v Use or not multi-commands processing (default: 1)\n");
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
printf(" -d, --debug=level Debug tracing level\n");
|
||||
#endif
|
||||
@@ -507,7 +510,7 @@ int main(int argc, char **argv)
|
||||
|
||||
memset(devs, 0, sizeof(devs));
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "+b:e:trongluF:I:cp:f:m:d:vsS:P:hDR:Z:",
|
||||
while ((ch = getopt_long(argc, argv, "+b:e:trongluF:I:cp:f:m:d:vsS:P:hDR:Z:M:",
|
||||
long_options, &longindex)) >= 0) {
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
@@ -579,6 +582,9 @@ int main(int argc, char **argv)
|
||||
case 'Z':
|
||||
prealloc_buffer_size = atoi(optarg) * 1024;
|
||||
break;
|
||||
case 'M':
|
||||
use_multi = atoi(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
if (strncmp(optarg, "all", 3) == 0)
|
||||
memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
|
||||
@@ -712,6 +718,9 @@ int main(int argc, char **argv)
|
||||
alloc_fn = malloc;
|
||||
}
|
||||
|
||||
if (!use_multi)
|
||||
PRINT_INFO(" %s", "Using SCST_USER_REPLY_AND_GET_CMD");
|
||||
|
||||
#if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
|
||||
if (debug_tm_ignore)
|
||||
PRINT_INFO(" %s", "DEBUG_TM_IGNORE");
|
||||
@@ -744,7 +753,7 @@ int main(int argc, char **argv)
|
||||
PRINT_ERROR("sigaction() failed: %s",
|
||||
strerror(res));
|
||||
goto out_done;
|
||||
}
|
||||
}
|
||||
|
||||
res = alarm(flush_interval);
|
||||
if (res != 0) {
|
||||
|
||||
Reference in New Issue
Block a user