lkml.org 
[lkml]   [2010]   [Sep]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 13/17]: SCST vdisk dev handler
    This patch contains SCST vdisk dev handler. This dev handler allows
    to create virtual disks and CDROMs from files on file system.

    Signed-off-by: Vladislav Bolkhovitin <vst@vlnb.net>
    ---
    scst_vdisk.c | 3966 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    1 file changed, 3966 insertions(+)

    diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_vdisk.c linux-2.6.35/drivers/scst/dev_handlers/scst_vdisk.c
    --- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_vdisk.c
    +++ linux-2.6.35/drivers/scst/dev_handlers/scst_vdisk.c
    @@ -0,0 +1,3966 @@
    +/*
    + * scst_vdisk.c
    + *
    + * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net>
    + * Copyright (C) 2004 - 2005 Leonid Stoljar
    + * Copyright (C) 2007 Ming Zhang <blackmagic02881 at gmail dot com>
    + * Copyright (C) 2007 Ross Walker <rswwalker at hotmail dot com>
    + * Copyright (C) 2007 - 2010 ID7 Ltd.
    + *
    + * SCSI disk (type 0) and CDROM (type 5) dev handler using files
    + * on file systems or block devices (VDISK)
    + *
    + * 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/file.h>
    +#include <linux/fs.h>
    +#include <linux/string.h>
    +#include <linux/types.h>
    +#include <linux/unistd.h>
    +#include <linux/smp_lock.h>
    +#include <linux/spinlock.h>
    +#include <linux/init.h>
    +#include <linux/uio.h>
    +#include <linux/list.h>
    +#include <linux/ctype.h>
    +#include <linux/writeback.h>
    +#include <linux/vmalloc.h>
    +#include <asm/atomic.h>
    +#include <linux/kthread.h>
    +#include <linux/sched.h>
    +#include <linux/version.h>
    +#include <asm/div64.h>
    +#include <asm/unaligned.h>
    +#include <linux/slab.h>
    +#include <linux/bio.h>
    +
    +#define LOG_PREFIX "dev_vdisk"
    +
    +#include <scst/scst.h>
    +
    +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
    +
    +#define TRACE_ORDER 0x80000000
    +
    +static struct scst_trace_log vdisk_local_trace_tbl[] = {
    + { TRACE_ORDER, "order" },
    + { 0, NULL }
    +};
    +#define trace_log_tbl vdisk_local_trace_tbl
    +
    +#define VDISK_TRACE_TLB_HELP ", order"
    +
    +#endif
    +
    +#include "scst_dev_handler.h"
    +
    +/* 8 byte ASCII Vendor */
    +#define SCST_FIO_VENDOR "SCST_FIO"
    +#define SCST_BIO_VENDOR "SCST_BIO"
    +/* 4 byte ASCII Product Revision Level - left aligned */
    +#define SCST_FIO_REV " 210"
    +
    +#define MAX_USN_LEN (20+1) /* For '\0' */
    +
    +#define INQ_BUF_SZ 128
    +#define EVPD 0x01
    +#define CMDDT 0x02
    +
    +#define MSENSE_BUF_SZ 256
    +#define DBD 0x08 /* disable block descriptor */
    +#define WP 0x80 /* write protect */
    +#define DPOFUA 0x10 /* DPOFUA bit */
    +#define WCE 0x04 /* write cache enable */
    +
    +#define PF 0x10 /* page format */
    +#define SP 0x01 /* save pages */
    +#define PS 0x80 /* parameter saveable */
    +
    +#define BYTE 8
    +#define DEF_DISK_BLOCKSIZE_SHIFT 9
    +#define DEF_DISK_BLOCKSIZE (1 << DEF_DISK_BLOCKSIZE_SHIFT)
    +#define DEF_CDROM_BLOCKSIZE_SHIFT 11
    +#define DEF_CDROM_BLOCKSIZE (1 << DEF_CDROM_BLOCKSIZE_SHIFT)
    +#define DEF_SECTORS 56
    +#define DEF_HEADS 255
    +#define LEN_MEM (32 * 1024)
    +#define DEF_RD_ONLY 0
    +#define DEF_WRITE_THROUGH 0
    +#define DEF_NV_CACHE 0
    +#define DEF_O_DIRECT 0
    +#define DEF_REMOVABLE 0
    +
    +#define VDISK_NULLIO_SIZE (3LL*1024*1024*1024*1024/2)
    +
    +#define DEF_TST SCST_CONTR_MODE_SEP_TASK_SETS
    +/*
    + * Since we can't control backstorage device's reordering, we have to always
    + * report unrestricted reordering.
    + */
    +#define DEF_QUEUE_ALG_WT SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER
    +#define DEF_QUEUE_ALG SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER
    +#define DEF_SWP 0
    +#define DEF_TAS 0
    +
    +#define DEF_DSENSE SCST_CONTR_MODE_FIXED_SENSE
    +
    +static unsigned int random_values[256] = {
    + 9862592UL, 3744545211UL, 2348289082UL, 4036111983UL,
    + 435574201UL, 3110343764UL, 2383055570UL, 1826499182UL,
    + 4076766377UL, 1549935812UL, 3696752161UL, 1200276050UL,
    + 3878162706UL, 1783530428UL, 2291072214UL, 125807985UL,
    + 3407668966UL, 547437109UL, 3961389597UL, 969093968UL,
    + 56006179UL, 2591023451UL, 1849465UL, 1614540336UL,
    + 3699757935UL, 479961779UL, 3768703953UL, 2529621525UL,
    + 4157893312UL, 3673555386UL, 4091110867UL, 2193909423UL,
    + 2800464448UL, 3052113233UL, 450394455UL, 3424338713UL,
    + 2113709130UL, 4082064373UL, 3708640918UL, 3841182218UL,
    + 3141803315UL, 1032476030UL, 1166423150UL, 1169646901UL,
    + 2686611738UL, 575517645UL, 2829331065UL, 1351103339UL,
    + 2856560215UL, 2402488288UL, 867847666UL, 8524618UL,
    + 704790297UL, 2228765657UL, 231508411UL, 1425523814UL,
    + 2146764591UL, 1287631730UL, 4142687914UL, 3879884598UL,
    + 729945311UL, 310596427UL, 2263511876UL, 1983091134UL,
    + 3500916580UL, 1642490324UL, 3858376049UL, 695342182UL,
    + 780528366UL, 1372613640UL, 1100993200UL, 1314818946UL,
    + 572029783UL, 3775573540UL, 776262915UL, 2684520905UL,
    + 1007252738UL, 3505856396UL, 1974886670UL, 3115856627UL,
    + 4194842288UL, 2135793908UL, 3566210707UL, 7929775UL,
    + 1321130213UL, 2627281746UL, 3587067247UL, 2025159890UL,
    + 2587032000UL, 3098513342UL, 3289360258UL, 130594898UL,
    + 2258149812UL, 2275857755UL, 3966929942UL, 1521739999UL,
    + 4191192765UL, 958953550UL, 4153558347UL, 1011030335UL,
    + 524382185UL, 4099757640UL, 498828115UL, 2396978754UL,
    + 328688935UL, 826399828UL, 3174103611UL, 3921966365UL,
    + 2187456284UL, 2631406787UL, 3930669674UL, 4282803915UL,
    + 1776755417UL, 374959755UL, 2483763076UL, 844956392UL,
    + 2209187588UL, 3647277868UL, 291047860UL, 3485867047UL,
    + 2223103546UL, 2526736133UL, 3153407604UL, 3828961796UL,
    + 3355731910UL, 2322269798UL, 2752144379UL, 519897942UL,
    + 3430536488UL, 1801511593UL, 1953975728UL, 3286944283UL,
    + 1511612621UL, 1050133852UL, 409321604UL, 1037601109UL,
    + 3352316843UL, 4198371381UL, 617863284UL, 994672213UL,
    + 1540735436UL, 2337363549UL, 1242368492UL, 665473059UL,
    + 2330728163UL, 3443103219UL, 2291025133UL, 3420108120UL,
    + 2663305280UL, 1608969839UL, 2278959931UL, 1389747794UL,
    + 2226946970UL, 2131266900UL, 3856979144UL, 1894169043UL,
    + 2692697628UL, 3797290626UL, 3248126844UL, 3922786277UL,
    + 343705271UL, 3739749888UL, 2191310783UL, 2962488787UL,
    + 4119364141UL, 1403351302UL, 2984008923UL, 3822407178UL,
    + 1932139782UL, 2323869332UL, 2793574182UL, 1852626483UL,
    + 2722460269UL, 1136097522UL, 1005121083UL, 1805201184UL,
    + 2212824936UL, 2979547931UL, 4133075915UL, 2585731003UL,
    + 2431626071UL, 134370235UL, 3763236829UL, 1171434827UL,
    + 2251806994UL, 1289341038UL, 3616320525UL, 392218563UL,
    + 1544502546UL, 2993937212UL, 1957503701UL, 3579140080UL,
    + 4270846116UL, 2030149142UL, 1792286022UL, 366604999UL,
    + 2625579499UL, 790898158UL, 770833822UL, 815540197UL,
    + 2747711781UL, 3570468835UL, 3976195842UL, 1257621341UL,
    + 1198342980UL, 1860626190UL, 3247856686UL, 351473955UL,
    + 993440563UL, 340807146UL, 1041994520UL, 3573925241UL,
    + 480246395UL, 2104806831UL, 1020782793UL, 3362132583UL,
    + 2272911358UL, 3440096248UL, 2356596804UL, 259492703UL,
    + 3899500740UL, 252071876UL, 2177024041UL, 4284810959UL,
    + 2775999888UL, 2653420445UL, 2876046047UL, 1025771859UL,
    + 1994475651UL, 3564987377UL, 4112956647UL, 1821511719UL,
    + 3113447247UL, 455315102UL, 1585273189UL, 2311494568UL,
    + 774051541UL, 1898115372UL, 2637499516UL, 247231365UL,
    + 1475014417UL, 803585727UL, 3911097303UL, 1714292230UL,
    + 476579326UL, 2496900974UL, 3397613314UL, 341202244UL,
    + 807790202UL, 4221326173UL, 499979741UL, 1301488547UL,
    + 1056807896UL, 3525009458UL, 1174811641UL, 3049738746UL,
    +};
    +
    +struct scst_vdisk_dev {
    + uint32_t block_size;
    + uint64_t nblocks;
    + int block_shift;
    + loff_t file_size; /* in bytes */
    +
    + /*
    + * This lock can be taken on both SIRQ and thread context, but in
    + * all cases for each particular instance it's taken consistenly either
    + * on SIRQ or thread context. Mix of them is forbidden.
    + */
    + spinlock_t flags_lock;
    +
    + /*
    + * Below flags are protected by flags_lock or suspended activity
    + * with scst_vdisk_mutex.
    + */
    + unsigned int rd_only:1;
    + unsigned int wt_flag:1;
    + unsigned int nv_cache:1;
    + unsigned int o_direct_flag:1;
    + unsigned int media_changed:1;
    + unsigned int prevent_allow_medium_removal:1;
    + unsigned int nullio:1;
    + unsigned int blockio:1;
    + unsigned int cdrom_empty:1;
    + unsigned int removable:1;
    +
    + int virt_id;
    + char name[16+1]; /* Name of the virtual device,
    + must be <= SCSI Model + 1 */
    + char *filename; /* File name, protected by
    + scst_mutex and suspended activities */
    + uint16_t command_set_version;
    + unsigned int t10_dev_id_set:1; /* true if t10_dev_id manually set */
    + char t10_dev_id[16+8+2]; /* T10 device ID */
    + char usn[MAX_USN_LEN];
    + struct scst_device *dev;
    + struct list_head vdev_list_entry;
    +
    + struct scst_dev_type *vdev_devt;
    +};
    +
    +struct scst_vdisk_thr {
    + struct scst_thr_data_hdr hdr;
    + struct file *fd;
    + struct block_device *bdev;
    + struct iovec *iv;
    + int iv_count;
    +};
    +
    +/* Context RA patch supposed to be applied on the kernel */
    +#define DEF_NUM_THREADS 8
    +static int num_threads = DEF_NUM_THREADS;
    +
    +module_param_named(num_threads, num_threads, int, S_IRUGO);
    +MODULE_PARM_DESC(num_threads, "vdisk threads count");
    +
    +static int vdisk_attach(struct scst_device *dev);
    +static void vdisk_detach(struct scst_device *dev);
    +static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev);
    +static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev);
    +static int vdisk_parse(struct scst_cmd *);
    +static int vdisk_do_job(struct scst_cmd *cmd);
    +static int vcdrom_parse(struct scst_cmd *);
    +static int vcdrom_exec(struct scst_cmd *cmd);
    +static void vdisk_exec_read(struct scst_cmd *cmd,
    + struct scst_vdisk_thr *thr, loff_t loff);
    +static void vdisk_exec_write(struct scst_cmd *cmd,
    + struct scst_vdisk_thr *thr, loff_t loff);
    +static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
    + u64 lba_start, int write);
    +static int blockio_flush(struct block_device *bdev);
    +static void vdisk_exec_verify(struct scst_cmd *cmd,
    + struct scst_vdisk_thr *thr, loff_t loff);
    +static void vdisk_exec_read_capacity(struct scst_cmd *cmd);
    +static void vdisk_exec_read_capacity16(struct scst_cmd *cmd);
    +static void vdisk_exec_inquiry(struct scst_cmd *cmd);
    +static void vdisk_exec_request_sense(struct scst_cmd *cmd);
    +static void vdisk_exec_mode_sense(struct scst_cmd *cmd);
    +static void vdisk_exec_mode_select(struct scst_cmd *cmd);
    +static void vdisk_exec_log(struct scst_cmd *cmd);
    +static void vdisk_exec_read_toc(struct scst_cmd *cmd);
    +static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd);
    +static int vdisk_fsync(struct scst_vdisk_thr *thr, loff_t loff,
    + loff_t len, struct scst_cmd *cmd, struct scst_device *dev);
    +static ssize_t vdisk_add_fileio_device(const char *device_name, char *params);
    +static ssize_t vdisk_add_blockio_device(const char *device_name, char *params);
    +static ssize_t vdisk_add_nullio_device(const char *device_name, char *params);
    +static ssize_t vdisk_del_device(const char *device_name);
    +static ssize_t vcdrom_add_device(const char *device_name, char *params);
    +static ssize_t vcdrom_del_device(const char *device_name);
    +static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
    + struct scst_tgt_dev *tgt_dev);
    +static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name);
    +
    +/** SYSFS **/
    +
    +static ssize_t vdev_sysfs_size_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdev_sysfs_filename_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj,
    + struct kobj_attribute *attr, const char *buf, size_t count);
    +static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj,
    + struct kobj_attribute *attr, const char *buf, size_t count);
    +static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +static ssize_t vdev_sysfs_usn_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf);
    +
    +static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj,
    + struct kobj_attribute *attr, const char *buf, size_t count);
    +
    +static struct kobj_attribute vdev_size_attr =
    + __ATTR(size_mb, S_IRUGO, vdev_sysfs_size_show, NULL);
    +static struct kobj_attribute vdisk_blocksize_attr =
    + __ATTR(blocksize, S_IRUGO, vdisk_sysfs_blocksize_show, NULL);
    +static struct kobj_attribute vdisk_rd_only_attr =
    + __ATTR(read_only, S_IRUGO, vdisk_sysfs_rd_only_show, NULL);
    +static struct kobj_attribute vdisk_wt_attr =
    + __ATTR(write_through, S_IRUGO, vdisk_sysfs_wt_show, NULL);
    +static struct kobj_attribute vdisk_nv_cache_attr =
    + __ATTR(nv_cache, S_IRUGO, vdisk_sysfs_nv_cache_show, NULL);
    +static struct kobj_attribute vdisk_o_direct_attr =
    + __ATTR(o_direct, S_IRUGO, vdisk_sysfs_o_direct_show, NULL);
    +static struct kobj_attribute vdisk_removable_attr =
    + __ATTR(removable, S_IRUGO, vdisk_sysfs_removable_show, NULL);
    +static struct kobj_attribute vdisk_filename_attr =
    + __ATTR(filename, S_IRUGO, vdev_sysfs_filename_show, NULL);
    +static struct kobj_attribute vdisk_resync_size_attr =
    + __ATTR(resync_size, S_IWUSR, NULL, vdisk_sysfs_resync_size_store);
    +static struct kobj_attribute vdev_t10_dev_id_attr =
    + __ATTR(t10_dev_id, S_IWUSR|S_IRUGO, vdev_sysfs_t10_dev_id_show,
    + vdev_sysfs_t10_dev_id_store);
    +static struct kobj_attribute vdev_usn_attr =
    + __ATTR(usn, S_IRUGO, vdev_sysfs_usn_show, NULL);
    +
    +static struct kobj_attribute vcdrom_filename_attr =
    + __ATTR(filename, S_IRUGO|S_IWUSR, vdev_sysfs_filename_show,
    + vcdrom_sysfs_filename_store);
    +
    +static const struct attribute *vdisk_fileio_attrs[] = {
    + &vdev_size_attr.attr,
    + &vdisk_blocksize_attr.attr,
    + &vdisk_rd_only_attr.attr,
    + &vdisk_wt_attr.attr,
    + &vdisk_nv_cache_attr.attr,
    + &vdisk_o_direct_attr.attr,
    + &vdisk_removable_attr.attr,
    + &vdisk_filename_attr.attr,
    + &vdisk_resync_size_attr.attr,
    + &vdev_t10_dev_id_attr.attr,
    + &vdev_usn_attr.attr,
    + NULL,
    +};
    +
    +static const struct attribute *vdisk_blockio_attrs[] = {
    + &vdev_size_attr.attr,
    + &vdisk_blocksize_attr.attr,
    + &vdisk_rd_only_attr.attr,
    + &vdisk_nv_cache_attr.attr,
    + &vdisk_removable_attr.attr,
    + &vdisk_filename_attr.attr,
    + &vdisk_resync_size_attr.attr,
    + &vdev_t10_dev_id_attr.attr,
    + &vdev_usn_attr.attr,
    + NULL,
    +};
    +
    +static const struct attribute *vdisk_nullio_attrs[] = {
    + &vdev_size_attr.attr,
    + &vdisk_blocksize_attr.attr,
    + &vdisk_rd_only_attr.attr,
    + &vdisk_removable_attr.attr,
    + &vdev_t10_dev_id_attr.attr,
    + &vdev_usn_attr.attr,
    + NULL,
    +};
    +
    +static const struct attribute *vcdrom_attrs[] = {
    + &vdev_size_attr.attr,
    + &vcdrom_filename_attr.attr,
    + &vdev_t10_dev_id_attr.attr,
    + &vdev_usn_attr.attr,
    + NULL,
    +};
    +
    +/* Protects vdisks addition/deletion and related activities, like search */
    +static DEFINE_MUTEX(scst_vdisk_mutex);
    +static DEFINE_RWLOCK(vdisk_t10_dev_id_rwlock);
    +
    +/* Protected by scst_vdisk_mutex */
    +static LIST_HEAD(vdev_list);
    +
    +static struct kmem_cache *vdisk_thr_cachep;
    +
    +/*
    + * Be careful changing "name" field, since it is the name of the corresponding
    + * /sys/kernel/scst_tgt entry, hence a part of user space ABI.
    + */
    +
    +static struct scst_dev_type vdisk_file_devtype = {
    + .name = "vdisk_fileio",
    + .type = TYPE_DISK,
    + .exec_sync = 1,
    + .threads_num = -1,
    + .parse_atomic = 1,
    + .dev_done_atomic = 1,
    + .attach = vdisk_attach,
    + .detach = vdisk_detach,
    + .attach_tgt = vdisk_attach_tgt,
    + .detach_tgt = vdisk_detach_tgt,
    + .parse = vdisk_parse,
    + .exec = vdisk_do_job,
    + .task_mgmt_fn = vdisk_task_mgmt_fn,
    + .add_device = vdisk_add_fileio_device,
    + .del_device = vdisk_del_device,
    + .dev_attrs = vdisk_fileio_attrs,
    + .add_device_parameters = "filename, blocksize, write_through, "
    + "nv_cache, o_direct, read_only, removable",
    +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
    + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
    + .trace_flags = &trace_flag,
    + .trace_tbl = vdisk_local_trace_tbl,
    + .trace_tbl_help = VDISK_TRACE_TLB_HELP,
    +#endif
    +};
    +
    +static struct kmem_cache *blockio_work_cachep;
    +
    +static struct scst_dev_type vdisk_blk_devtype = {
    + .name = "vdisk_blockio",
    + .type = TYPE_DISK,
    + .threads_num = 1,
    + .parse_atomic = 1,
    + .dev_done_atomic = 1,
    + .attach = vdisk_attach,
    + .detach = vdisk_detach,
    + .attach_tgt = vdisk_attach_tgt,
    + .detach_tgt = vdisk_detach_tgt,
    + .parse = vdisk_parse,
    + .exec = vdisk_do_job,
    + .task_mgmt_fn = vdisk_task_mgmt_fn,
    + .add_device = vdisk_add_blockio_device,
    + .del_device = vdisk_del_device,
    + .dev_attrs = vdisk_blockio_attrs,
    + .add_device_parameters = "filename, blocksize, nv_cache, read_only, "
    + "removable",
    +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
    + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
    + .trace_flags = &trace_flag,
    + .trace_tbl = vdisk_local_trace_tbl,
    + .trace_tbl_help = VDISK_TRACE_TLB_HELP,
    +#endif
    +};
    +
    +static struct scst_dev_type vdisk_null_devtype = {
    + .name = "vdisk_nullio",
    + .type = TYPE_DISK,
    + .threads_num = 0,
    + .parse_atomic = 1,
    + .dev_done_atomic = 1,
    + .attach = vdisk_attach,
    + .detach = vdisk_detach,
    + .attach_tgt = vdisk_attach_tgt,
    + .detach_tgt = vdisk_detach_tgt,
    + .parse = vdisk_parse,
    + .exec = vdisk_do_job,
    + .task_mgmt_fn = vdisk_task_mgmt_fn,
    + .add_device = vdisk_add_nullio_device,
    + .del_device = vdisk_del_device,
    + .dev_attrs = vdisk_nullio_attrs,
    + .add_device_parameters = "blocksize, read_only, removable",
    +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
    + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
    + .trace_flags = &trace_flag,
    + .trace_tbl = vdisk_local_trace_tbl,
    + .trace_tbl_help = VDISK_TRACE_TLB_HELP,
    +#endif
    +};
    +
    +static struct scst_dev_type vcdrom_devtype = {
    + .name = "vcdrom",
    + .type = TYPE_ROM,
    + .exec_sync = 1,
    + .threads_num = -1,
    + .parse_atomic = 1,
    + .dev_done_atomic = 1,
    + .attach = vdisk_attach,
    + .detach = vdisk_detach,
    + .attach_tgt = vdisk_attach_tgt,
    + .detach_tgt = vdisk_detach_tgt,
    + .parse = vcdrom_parse,
    + .exec = vcdrom_exec,
    + .task_mgmt_fn = vdisk_task_mgmt_fn,
    + .add_device = vcdrom_add_device,
    + .del_device = vcdrom_del_device,
    + .dev_attrs = vcdrom_attrs,
    + .add_device_parameters = NULL,
    +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
    + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
    + .trace_flags = &trace_flag,
    + .trace_tbl = vdisk_local_trace_tbl,
    + .trace_tbl_help = VDISK_TRACE_TLB_HELP,
    +#endif
    +};
    +
    +static struct scst_vdisk_thr nullio_thr_data;
    +
    +static const char *vdev_get_filename(const struct scst_vdisk_dev *virt_dev)
    +{
    + if (virt_dev->filename != NULL)
    + return virt_dev->filename;
    + else
    + return "none";
    +}
    +
    +/* Returns fd, use IS_ERR(fd) to get error status */
    +static struct file *vdev_open_fd(const struct scst_vdisk_dev *virt_dev)
    +{
    + int open_flags = 0;
    + struct file *fd;
    +
    + if (virt_dev->dev->rd_only)
    + open_flags |= O_RDONLY;
    + else
    + open_flags |= O_RDWR;
    + if (virt_dev->o_direct_flag)
    + open_flags |= O_DIRECT;
    + if (virt_dev->wt_flag && !virt_dev->nv_cache)
    + open_flags |= O_SYNC;
    + TRACE_DBG("Opening file %s, flags 0x%x",
    + virt_dev->filename, open_flags);
    + fd = filp_open(virt_dev->filename, O_LARGEFILE | open_flags, 0600);
    + return fd;
    +}
    +
    +static void vdisk_blockio_check_flush_support(struct scst_vdisk_dev *virt_dev)
    +{
    + struct inode *inode;
    + struct file *fd;
    +
    + if (!virt_dev->blockio || virt_dev->rd_only || virt_dev->nv_cache)
    + goto out;
    +
    + fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600);
    + if (IS_ERR(fd)) {
    + PRINT_ERROR("filp_open(%s) returned error %ld",
    + virt_dev->filename, PTR_ERR(fd));
    + goto out;
    + }
    +
    + inode = fd->f_dentry->d_inode;
    +
    + if (!S_ISBLK(inode->i_mode)) {
    + PRINT_ERROR("%s is NOT a block device", virt_dev->filename);
    + goto out_close;
    + }
    +
    + if (blockio_flush(inode->i_bdev) != 0) {
    + PRINT_WARNING("Device %s doesn't support barriers, switching "
    + "to NV_CACHE mode. Read README for more details.",
    + virt_dev->filename);
    + virt_dev->nv_cache = 1;
    + }
    +
    +out_close:
    + filp_close(fd, NULL);
    +
    +out:
    + return;
    +}
    +
    +/* Returns 0 on success and file size in *file_size, error code otherwise */
    +static int vdisk_get_file_size(const char *filename, bool blockio,
    + loff_t *file_size)
    +{
    + struct inode *inode;
    + int res = 0;
    + struct file *fd;
    +
    + *file_size = 0;
    +
    + fd = filp_open(filename, O_LARGEFILE | O_RDONLY, 0600);
    + if (IS_ERR(fd)) {
    + res = PTR_ERR(fd);
    + PRINT_ERROR("filp_open(%s) returned error %d", filename, res);
    + goto out;
    + }
    +
    + inode = fd->f_dentry->d_inode;
    +
    + if (blockio && !S_ISBLK(inode->i_mode)) {
    + PRINT_ERROR("File %s is NOT a block device", filename);
    + res = -EINVAL;
    + goto out_close;
    + }
    +
    + if (S_ISREG(inode->i_mode))
    + /* Nothing to do */;
    + else if (S_ISBLK(inode->i_mode))
    + inode = inode->i_bdev->bd_inode;
    + else {
    + res = -EINVAL;
    + goto out_close;
    + }
    +
    + *file_size = inode->i_size;
    +
    +out_close:
    + filp_close(fd, NULL);
    +
    +out:
    + return res;
    +}
    +
    +static int vdisk_attach(struct scst_device *dev)
    +{
    + int res = 0;
    + loff_t err;
    + struct scst_vdisk_dev *virt_dev = NULL, *vv;
    +
    + TRACE_DBG("virt_id %d (%s)", dev->virt_id, dev->virt_name);
    +
    + if (dev->virt_id == 0) {
    + PRINT_ERROR("%s", "Not a virtual device");
    + res = -EINVAL;
    + goto out;
    + }
    +
    + /*
    + * scst_vdisk_mutex must be already taken before
    + * scst_register_virtual_device()
    + */
    + list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
    + if (strcmp(vv->name, dev->virt_name) == 0) {
    + virt_dev = vv;
    + break;
    + }
    + }
    +
    + if (virt_dev == NULL) {
    + PRINT_ERROR("Device %s not found", dev->virt_name);
    + res = -EINVAL;
    + goto out;
    + }
    +
    + virt_dev->dev = dev;
    +
    + dev->rd_only = virt_dev->rd_only;
    +
    + if (!virt_dev->cdrom_empty) {
    + if (virt_dev->nullio)
    + err = VDISK_NULLIO_SIZE;
    + else {
    + res = vdisk_get_file_size(virt_dev->filename,
    + virt_dev->blockio, &err);
    + if (res != 0)
    + goto out;
    + }
    + virt_dev->file_size = err;
    +
    + TRACE_DBG("size of file: %lld", (long long unsigned int)err);
    +
    + vdisk_blockio_check_flush_support(virt_dev);
    + } else
    + virt_dev->file_size = 0;
    +
    + virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
    +
    + if (!virt_dev->cdrom_empty) {
    + PRINT_INFO("Attached SCSI target virtual %s %s "
    + "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld,"
    + " cyln=%lld%s)",
    + (dev->type == TYPE_DISK) ? "disk" : "cdrom",
    + virt_dev->name, vdev_get_filename(virt_dev),
    + virt_dev->file_size >> 20, virt_dev->block_size,
    + (long long unsigned int)virt_dev->nblocks,
    + (long long unsigned int)virt_dev->nblocks/64/32,
    + virt_dev->nblocks < 64*32
    + ? " !WARNING! cyln less than 1" : "");
    + } else {
    + PRINT_INFO("Attached empty SCSI target virtual cdrom %s",
    + virt_dev->name);
    + }
    +
    + dev->dh_priv = virt_dev;
    +
    + dev->tst = DEF_TST;
    + dev->d_sense = DEF_DSENSE;
    + if (virt_dev->wt_flag && !virt_dev->nv_cache)
    + dev->queue_alg = DEF_QUEUE_ALG_WT;
    + else
    + dev->queue_alg = DEF_QUEUE_ALG;
    + dev->swp = DEF_SWP;
    + dev->tas = DEF_TAS;
    +
    +out:
    + return res;
    +}
    +
    +/* scst_mutex supposed to be held */
    +static void vdisk_detach(struct scst_device *dev)
    +{
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + TRACE_DBG("virt_id %d", dev->virt_id);
    +
    + PRINT_INFO("Detached virtual device %s (\"%s\")",
    + virt_dev->name, vdev_get_filename(virt_dev));
    +
    + /* virt_dev will be freed by the caller */
    + dev->dh_priv = NULL;
    + return;
    +}
    +
    +static void vdisk_free_thr_data(struct scst_thr_data_hdr *d)
    +{
    + struct scst_vdisk_thr *thr =
    + container_of(d, struct scst_vdisk_thr, hdr);
    +
    + if (thr->fd)
    + filp_close(thr->fd, NULL);
    +
    + kfree(thr->iv);
    +
    + kmem_cache_free(vdisk_thr_cachep, thr);
    + return;
    +}
    +
    +static struct scst_vdisk_thr *vdisk_init_thr_data(
    + struct scst_tgt_dev *tgt_dev)
    +{
    + struct scst_vdisk_thr *res;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)tgt_dev->dev->dh_priv;
    +
    + EXTRACHECKS_BUG_ON(virt_dev->nullio);
    +
    + res = kmem_cache_zalloc(vdisk_thr_cachep, GFP_KERNEL);
    + if (res == NULL) {
    + TRACE(TRACE_OUT_OF_MEM, "%s", "Unable to allocate struct "
    + "scst_vdisk_thr");
    + goto out;
    + }
    +
    + if (!virt_dev->cdrom_empty) {
    + res->fd = vdev_open_fd(virt_dev);
    + if (IS_ERR(res->fd)) {
    + PRINT_ERROR("filp_open(%s) returned an error %ld",
    + virt_dev->filename, PTR_ERR(res->fd));
    + goto out_free;
    + }
    + if (virt_dev->blockio)
    + res->bdev = res->fd->f_dentry->d_inode->i_bdev;
    + else
    + res->bdev = NULL;
    + } else
    + res->fd = NULL;
    +
    + scst_add_thr_data(tgt_dev, &res->hdr, vdisk_free_thr_data);
    +
    +out:
    + return res;
    +
    +out_free:
    + kmem_cache_free(vdisk_thr_cachep, res);
    + res = NULL;
    + goto out;
    +}
    +
    +static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev)
    +{
    + int res = 0;
    +
    + /* Nothing to do */
    + return res;
    +}
    +
    +static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev)
    +{
    +
    + scst_del_all_thr_data(tgt_dev);
    + return;
    +}
    +
    +static int vdisk_do_job(struct scst_cmd *cmd)
    +{
    + int rc, res;
    + uint64_t lba_start = 0;
    + loff_t data_len = 0;
    + uint8_t *cdb = cmd->cdb;
    + int opcode = cdb[0];
    + loff_t loff;
    + struct scst_device *dev = cmd->dev;
    + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)dev->dh_priv;
    + struct scst_thr_data_hdr *d;
    + struct scst_vdisk_thr *thr = NULL;
    + int fua = 0;
    +
    + switch (cmd->queue_type) {
    + case SCST_CMD_QUEUE_ORDERED:
    + TRACE(TRACE_ORDER, "ORDERED cmd %p (op %x)", cmd, cmd->cdb[0]);
    + break;
    + case SCST_CMD_QUEUE_HEAD_OF_QUEUE:
    + TRACE(TRACE_ORDER, "HQ cmd %p (op %x)", cmd, cmd->cdb[0]);
    + break;
    + default:
    + break;
    + }
    +
    + rc = scst_check_local_events(cmd);
    + if (unlikely(rc != 0))
    + goto out_done;
    +
    + cmd->status = 0;
    + cmd->msg_status = 0;
    + cmd->host_status = DID_OK;
    + cmd->driver_status = 0;
    +
    + if (!virt_dev->nullio) {
    + d = scst_find_thr_data(tgt_dev);
    + if (unlikely(d == NULL)) {
    + thr = vdisk_init_thr_data(tgt_dev);
    + if (thr == NULL) {
    + scst_set_busy(cmd);
    + goto out_compl;
    + }
    + scst_thr_data_get(&thr->hdr);
    + } else
    + thr = container_of(d, struct scst_vdisk_thr, hdr);
    + } else {
    + thr = &nullio_thr_data;
    + scst_thr_data_get(&thr->hdr);
    + }
    +
    + switch (opcode) {
    + case READ_6:
    + case WRITE_6:
    + case VERIFY_6:
    + lba_start = (((cdb[1] & 0x1f) << (BYTE * 2)) +
    + (cdb[2] << (BYTE * 1)) +
    + (cdb[3] << (BYTE * 0)));
    + data_len = cmd->bufflen;
    + break;
    + case READ_10:
    + case READ_12:
    + case WRITE_10:
    + case WRITE_12:
    + case VERIFY:
    + case WRITE_VERIFY:
    + case WRITE_VERIFY_12:
    + case VERIFY_12:
    + lba_start |= ((u64)cdb[2]) << 24;
    + lba_start |= ((u64)cdb[3]) << 16;
    + lba_start |= ((u64)cdb[4]) << 8;
    + lba_start |= ((u64)cdb[5]);
    + data_len = cmd->bufflen;
    + break;
    + case READ_16:
    + case WRITE_16:
    + case WRITE_VERIFY_16:
    + case VERIFY_16:
    + lba_start |= ((u64)cdb[2]) << 56;
    + lba_start |= ((u64)cdb[3]) << 48;
    + lba_start |= ((u64)cdb[4]) << 40;
    + lba_start |= ((u64)cdb[5]) << 32;
    + lba_start |= ((u64)cdb[6]) << 24;
    + lba_start |= ((u64)cdb[7]) << 16;
    + lba_start |= ((u64)cdb[8]) << 8;
    + lba_start |= ((u64)cdb[9]);
    + data_len = cmd->bufflen;
    + break;
    + case SYNCHRONIZE_CACHE:
    + lba_start |= ((u64)cdb[2]) << 24;
    + lba_start |= ((u64)cdb[3]) << 16;
    + lba_start |= ((u64)cdb[4]) << 8;
    + lba_start |= ((u64)cdb[5]);
    + data_len = ((cdb[7] << (BYTE * 1)) + (cdb[8] << (BYTE * 0)))
    + << virt_dev->block_shift;
    + if (data_len == 0)
    + data_len = virt_dev->file_size -
    + ((loff_t)lba_start << virt_dev->block_shift);
    + break;
    + }
    +
    + loff = (loff_t)lba_start << virt_dev->block_shift;
    + TRACE_DBG("cmd %p, lba_start %lld, loff %lld, data_len %lld", cmd,
    + (long long unsigned int)lba_start,
    + (long long unsigned int)loff,
    + (long long unsigned int)data_len);
    + if (unlikely(loff < 0) || unlikely(data_len < 0) ||
    + unlikely((loff + data_len) > virt_dev->file_size)) {
    + PRINT_INFO("Access beyond the end of the device "
    + "(%lld of %lld, len %lld)",
    + (long long unsigned int)loff,
    + (long long unsigned int)virt_dev->file_size,
    + (long long unsigned int)data_len);
    + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
    + scst_sense_block_out_range_error));
    + goto out_compl;
    + }
    +
    + switch (opcode) {
    + case WRITE_10:
    + case WRITE_12:
    + case WRITE_16:
    + fua = (cdb[1] & 0x8);
    + if (fua) {
    + TRACE(TRACE_ORDER, "FUA: loff=%lld, "
    + "data_len=%lld", (long long unsigned int)loff,
    + (long long unsigned int)data_len);
    + }
    + break;
    + }
    +
    + switch (opcode) {
    + case READ_6:
    + case READ_10:
    + case READ_12:
    + case READ_16:
    + if (virt_dev->blockio) {
    + blockio_exec_rw(cmd, thr, lba_start, 0);
    + goto out_thr;
    + } else
    + vdisk_exec_read(cmd, thr, loff);
    + break;
    + case WRITE_6:
    + case WRITE_10:
    + case WRITE_12:
    + case WRITE_16:
    + {
    + if (virt_dev->blockio) {
    + blockio_exec_rw(cmd, thr, lba_start, 1);
    + goto out_thr;
    + } else
    + vdisk_exec_write(cmd, thr, loff);
    + /* O_SYNC flag is used for WT devices */
    + if (fua)
    + vdisk_fsync(thr, loff, data_len, cmd, dev);
    + break;
    + }
    + case WRITE_VERIFY:
    + case WRITE_VERIFY_12:
    + case WRITE_VERIFY_16:
    + {
    + /* ToDo: BLOCKIO VERIFY */
    + vdisk_exec_write(cmd, thr, loff);
    + /* O_SYNC flag is used for WT devices */
    + if (scsi_status_is_good(cmd->status))
    + vdisk_exec_verify(cmd, thr, loff);
    + break;
    + }
    + case SYNCHRONIZE_CACHE:
    + {
    + int immed = cdb[1] & 0x2;
    + TRACE(TRACE_ORDER, "SYNCHRONIZE_CACHE: "
    + "loff=%lld, data_len=%lld, immed=%d",
    + (long long unsigned int)loff,
    + (long long unsigned int)data_len, immed);
    + if (immed) {
    + scst_cmd_get(cmd); /* to protect dev */
    + cmd->completed = 1;
    + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT,
    + SCST_CONTEXT_SAME);
    + vdisk_fsync(thr, loff, data_len, NULL, dev);
    + /* ToDo: vdisk_fsync() error processing */
    + scst_cmd_put(cmd);
    + goto out_thr;
    + } else {
    + vdisk_fsync(thr, loff, data_len, cmd, dev);
    + break;
    + }
    + }
    + case VERIFY_6:
    + case VERIFY:
    + case VERIFY_12:
    + case VERIFY_16:
    + vdisk_exec_verify(cmd, thr, loff);
    + break;
    + case MODE_SENSE:
    + case MODE_SENSE_10:
    + vdisk_exec_mode_sense(cmd);
    + break;
    + case MODE_SELECT:
    + case MODE_SELECT_10:
    + vdisk_exec_mode_select(cmd);
    + break;
    + case LOG_SELECT:
    + case LOG_SENSE:
    + vdisk_exec_log(cmd);
    + break;
    + case ALLOW_MEDIUM_REMOVAL:
    + vdisk_exec_prevent_allow_medium_removal(cmd);
    + break;
    + case READ_TOC:
    + vdisk_exec_read_toc(cmd);
    + break;
    + case START_STOP:
    + vdisk_fsync(thr, 0, virt_dev->file_size, cmd, dev);
    + break;
    + case RESERVE:
    + case RESERVE_10:
    + case RELEASE:
    + case RELEASE_10:
    + case TEST_UNIT_READY:
    + break;
    + case INQUIRY:
    + vdisk_exec_inquiry(cmd);
    + break;
    + case REQUEST_SENSE:
    + vdisk_exec_request_sense(cmd);
    + break;
    + case READ_CAPACITY:
    + vdisk_exec_read_capacity(cmd);
    + break;
    + case SERVICE_ACTION_IN:
    + if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16) {
    + vdisk_exec_read_capacity16(cmd);
    + break;
    + }
    + /* else go through */
    + case REPORT_LUNS:
    + default:
    + TRACE_DBG("Invalid opcode %d", opcode);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_opcode));
    + }
    +
    +out_compl:
    + cmd->completed = 1;
    +
    +out_done:
    + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
    +
    +out_thr:
    + if (likely(thr != NULL))
    + scst_thr_data_put(&thr->hdr);
    +
    + res = SCST_EXEC_COMPLETED;
    + return res;
    +}
    +
    +static int vdisk_get_block_shift(struct scst_cmd *cmd)
    +{
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + return virt_dev->block_shift;
    +}
    +
    +static int vdisk_parse(struct scst_cmd *cmd)
    +{
    + scst_sbc_generic_parse(cmd, vdisk_get_block_shift);
    + return SCST_CMD_STATE_DEFAULT;
    +}
    +
    +static int vcdrom_parse(struct scst_cmd *cmd)
    +{
    + scst_cdrom_generic_parse(cmd, vdisk_get_block_shift);
    + return SCST_CMD_STATE_DEFAULT;
    +}
    +
    +static int vcdrom_exec(struct scst_cmd *cmd)
    +{
    + int res = SCST_EXEC_COMPLETED;
    + int opcode = cmd->cdb[0];
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    +
    + cmd->status = 0;
    + cmd->msg_status = 0;
    + cmd->host_status = DID_OK;
    + cmd->driver_status = 0;
    +
    + if (virt_dev->cdrom_empty && (opcode != INQUIRY)) {
    + TRACE_DBG("%s", "CDROM empty");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_not_ready));
    + goto out_done;
    + }
    +
    + if (virt_dev->media_changed && scst_is_ua_command(cmd)) {
    + spin_lock(&virt_dev->flags_lock);
    + if (virt_dev->media_changed) {
    + virt_dev->media_changed = 0;
    + TRACE_DBG("%s", "Reporting media changed");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_medium_changed_UA));
    + spin_unlock(&virt_dev->flags_lock);
    + goto out_done;
    + }
    + spin_unlock(&virt_dev->flags_lock);
    + }
    +
    + res = vdisk_do_job(cmd);
    +
    +out:
    + return res;
    +
    +out_done:
    + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
    + goto out;
    +}
    +
    +static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name)
    +{
    + unsigned int dev_id_num, i;
    +
    + for (dev_id_num = 0, i = 0; i < strlen(virt_dev_name); i++) {
    + unsigned int rv = random_values[(int)(virt_dev_name[i])];
    + /* Do some rotating of the bits */
    + dev_id_num ^= ((rv << i) | (rv >> (32 - i)));
    + }
    +
    + return ((uint64_t)scst_get_setup_id() << 32) | dev_id_num;
    +}
    +
    +static void vdisk_exec_inquiry(struct scst_cmd *cmd)
    +{
    + int32_t length, i, resp_len = 0;
    + uint8_t *address;
    + uint8_t *buf;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    +
    + /* ToDo: Performance Boost:
    + * 1. remove kzalloc, buf
    + * 2. do all checks before touching *address
    + * 3. zero *address
    + * 4. write directly to *address
    + */
    +
    + buf = kzalloc(INQ_BUF_SZ, GFP_KERNEL);
    + if (buf == NULL) {
    + scst_set_busy(cmd);
    + goto out;
    + }
    +
    + length = scst_get_buf_first(cmd, &address);
    + TRACE_DBG("length %d", length);
    + if (unlikely(length <= 0)) {
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_first() failed: %d", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + }
    + goto out_free;
    + }
    +
    + if (cmd->cdb[1] & CMDDT) {
    + TRACE_DBG("%s", "INQUIRY: CMDDT is unsupported");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out_put;
    + }
    +
    + buf[0] = cmd->dev->type; /* type dev */
    + if (virt_dev->removable)
    + buf[1] = 0x80; /* removable */
    + /* Vital Product */
    + if (cmd->cdb[1] & EVPD) {
    + if (0 == cmd->cdb[2]) {
    + /* supported vital product data pages */
    + buf[3] = 3;
    + buf[4] = 0x0; /* this page */
    + buf[5] = 0x80; /* unit serial number */
    + buf[6] = 0x83; /* device identification */
    + if (virt_dev->dev->type == TYPE_DISK) {
    + buf[3] += 1;
    + buf[7] = 0xB0; /* block limits */
    + }
    + resp_len = buf[3] + 4;
    + } else if (0x80 == cmd->cdb[2]) {
    + /* unit serial number */
    + int usn_len = strlen(virt_dev->usn);
    + buf[1] = 0x80;
    + buf[3] = usn_len;
    + strncpy(&buf[4], virt_dev->usn, usn_len);
    + resp_len = buf[3] + 4;
    + } else if (0x83 == cmd->cdb[2]) {
    + /* device identification */
    + int num = 4;
    +
    + buf[1] = 0x83;
    + /* T10 vendor identifier field format (faked) */
    + buf[num + 0] = 0x2; /* ASCII */
    + buf[num + 1] = 0x1; /* Vendor ID */
    + if (virt_dev->blockio)
    + memcpy(&buf[num + 4], SCST_BIO_VENDOR, 8);
    + else
    + memcpy(&buf[num + 4], SCST_FIO_VENDOR, 8);
    +
    + read_lock_bh(&vdisk_t10_dev_id_rwlock);
    + i = strlen(virt_dev->t10_dev_id);
    + memcpy(&buf[num + 12], virt_dev->t10_dev_id, i);
    + read_unlock_bh(&vdisk_t10_dev_id_rwlock);
    +
    + buf[num + 3] = 8 + i;
    + num += buf[num + 3];
    +
    + num += 4;
    +
    + /*
    + * Relative target port identifier
    + */
    + buf[num + 0] = 0x01; /* binary */
    + /* Relative target port id */
    + buf[num + 1] = 0x10 | 0x04;
    +
    + put_unaligned(cpu_to_be16(cmd->tgt->rel_tgt_id),
    + (__be16 *)&buf[num + 4 + 2]);
    +
    + buf[num + 3] = 4;
    + num += buf[num + 3];
    +
    + num += 4;
    +
    + /*
    + * IEEE id
    + */
    + buf[num + 0] = 0x01; /* binary */
    +
    + /* EUI-64 */
    + buf[num + 1] = 0x02;
    + buf[num + 2] = 0x00;
    + buf[num + 3] = 0x08;
    +
    + /* IEEE id */
    + buf[num + 4] = virt_dev->t10_dev_id[0];
    + buf[num + 5] = virt_dev->t10_dev_id[1];
    + buf[num + 6] = virt_dev->t10_dev_id[2];
    +
    + /* IEEE ext id */
    + buf[num + 7] = virt_dev->t10_dev_id[3];
    + buf[num + 8] = virt_dev->t10_dev_id[4];
    + buf[num + 9] = virt_dev->t10_dev_id[5];
    + buf[num + 10] = virt_dev->t10_dev_id[6];
    + buf[num + 11] = virt_dev->t10_dev_id[7];
    + num += buf[num + 3];
    +
    + resp_len = num;
    + buf[2] = (resp_len >> 8) & 0xFF;
    + buf[3] = resp_len & 0xFF;
    + resp_len += 4;
    + } else if ((0xB0 == cmd->cdb[2]) &&
    + (virt_dev->dev->type == TYPE_DISK)) {
    + /* block limits */
    + int max_transfer;
    + buf[1] = 0xB0;
    + buf[3] = 0x1C;
    + /* Optimal transfer granuality is PAGE_SIZE */
    + put_unaligned(cpu_to_be16(max_t(int,
    + PAGE_SIZE/virt_dev->block_size, 1)),
    + (uint16_t *)&buf[6]);
    + /* Max transfer len is min of sg limit and 8M */
    + max_transfer = min_t(int,
    + cmd->tgt_dev->max_sg_cnt << PAGE_SHIFT,
    + 8*1024*1024) / virt_dev->block_size;
    + put_unaligned(cpu_to_be32(max_transfer),
    + (uint32_t *)&buf[8]);
    + /*
    + * Let's have optimal transfer len 1MB. Better to not
    + * set it at all, because we don't have such limit,
    + * but some initiators may not understand that (?).
    + * From other side, too big transfers are not optimal,
    + * because SGV cache supports only <4M buffers.
    + */
    + put_unaligned(cpu_to_be32(min_t(int,
    + max_transfer,
    + 1*1024*1024 / virt_dev->block_size)),
    + (uint32_t *)&buf[12]);
    + resp_len = buf[3] + 4;
    + } else {
    + TRACE_DBG("INQUIRY: Unsupported EVPD page %x",
    + cmd->cdb[2]);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out_put;
    + }
    + } else {
    + int len, num;
    +
    + if (cmd->cdb[2] != 0) {
    + TRACE_DBG("INQUIRY: Unsupported page %x", cmd->cdb[2]);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out_put;
    + }
    +
    + buf[2] = 5; /* Device complies to SPC-3 */
    + buf[3] = 0x12; /* HiSup + data in format specified in SPC */
    + buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */
    + buf[6] = 1; /* MultiP 1 */
    + buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */
    +
    + /*
    + * 8 byte ASCII Vendor Identification of the target
    + * - left aligned.
    + */
    + if (virt_dev->blockio)
    + memcpy(&buf[8], SCST_BIO_VENDOR, 8);
    + else
    + memcpy(&buf[8], SCST_FIO_VENDOR, 8);
    +
    + /*
    + * 16 byte ASCII Product Identification of the target - left
    + * aligned.
    + */
    + memset(&buf[16], ' ', 16);
    + len = min(strlen(virt_dev->name), (size_t)16);
    + memcpy(&buf[16], virt_dev->name, len);
    +
    + /*
    + * 4 byte ASCII Product Revision Level of the target - left
    + * aligned.
    + */
    + memcpy(&buf[32], SCST_FIO_REV, 4);
    +
    + /** Version descriptors **/
    +
    + buf[4] += 58 - 36;
    + num = 0;
    +
    + /* SAM-3 T10/1561-D revision 14 */
    + buf[58 + num] = 0x0;
    + buf[58 + num + 1] = 0x76;
    + num += 2;
    +
    + /* Physical transport */
    + if (cmd->tgtt->get_phys_transport_version != NULL) {
    + uint16_t v = cmd->tgtt->get_phys_transport_version(cmd->tgt);
    + if (v != 0) {
    + *((__be16 *)&buf[58 + num]) = cpu_to_be16(v);
    + num += 2;
    + }
    + }
    +
    + /* SCSI transport */
    + if (cmd->tgtt->get_scsi_transport_version != NULL) {
    + *((__be16 *)&buf[58 + num]) =
    + cpu_to_be16(cmd->tgtt->get_scsi_transport_version(cmd->tgt));
    + num += 2;
    + }
    +
    + /* SPC-3 T10/1416-D revision 23 */
    + buf[58 + num] = 0x3;
    + buf[58 + num + 1] = 0x12;
    + num += 2;
    +
    + /* Device command set */
    + if (virt_dev->command_set_version != 0) {
    + *((__be16 *)&buf[58 + num]) =
    + cpu_to_be16(virt_dev->command_set_version);
    + num += 2;
    + }
    +
    + buf[4] += num;
    + resp_len = buf[4] + 5;
    + }
    +
    + BUG_ON(resp_len >= INQ_BUF_SZ);
    +
    + if (length > resp_len)
    + length = resp_len;
    + memcpy(address, buf, length);
    +
    +out_put:
    + scst_put_buf(cmd, address);
    + if (length < cmd->resp_data_len)
    + scst_set_resp_data_len(cmd, length);
    +
    +out_free:
    + kfree(buf);
    +
    +out:
    + return;
    +}
    +
    +static void vdisk_exec_request_sense(struct scst_cmd *cmd)
    +{
    + int32_t length, sl;
    + uint8_t *address;
    + uint8_t b[SCST_STANDARD_SENSE_LEN];
    +
    + sl = scst_set_sense(b, sizeof(b), cmd->dev->d_sense,
    + SCST_LOAD_SENSE(scst_sense_no_sense));
    +
    + length = scst_get_buf_first(cmd, &address);
    + TRACE_DBG("length %d", length);
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_first() failed: %d)", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out;
    + }
    +
    + length = min(sl, length);
    + memcpy(address, b, length);
    + scst_set_resp_data_len(cmd, length);
    +
    + scst_put_buf(cmd, address);
    +
    +out:
    + return;
    +}
    +
    +/*
    + * <<Following mode pages info copied from ST318451LW with some corrections>>
    + *
    + * ToDo: revise them
    + */
    +static int vdisk_err_recov_pg(unsigned char *p, int pcontrol,
    + struct scst_vdisk_dev *virt_dev)
    +{ /* Read-Write Error Recovery page for mode_sense */
    + const unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
    + 5, 0, 0xff, 0xff};
    +
    + memcpy(p, err_recov_pg, sizeof(err_recov_pg));
    + if (1 == pcontrol)
    + memset(p + 2, 0, sizeof(err_recov_pg) - 2);
    + return sizeof(err_recov_pg);
    +}
    +
    +static int vdisk_disconnect_pg(unsigned char *p, int pcontrol,
    + struct scst_vdisk_dev *virt_dev)
    +{ /* Disconnect-Reconnect page for mode_sense */
    + const unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
    + 0, 0, 0, 0, 0, 0, 0, 0};
    +
    + memcpy(p, disconnect_pg, sizeof(disconnect_pg));
    + if (1 == pcontrol)
    + memset(p + 2, 0, sizeof(disconnect_pg) - 2);
    + return sizeof(disconnect_pg);
    +}
    +
    +static int vdisk_rigid_geo_pg(unsigned char *p, int pcontrol,
    + struct scst_vdisk_dev *virt_dev)
    +{
    + unsigned char geo_m_pg[] = {0x04, 0x16, 0, 0, 0, DEF_HEADS, 0, 0,
    + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    + 0x3a, 0x98/* 15K RPM */, 0, 0};
    + int32_t ncyl, n, rem;
    + uint64_t dividend;
    +
    + memcpy(p, geo_m_pg, sizeof(geo_m_pg));
    + /*
    + * Divide virt_dev->nblocks by (DEF_HEADS * DEF_SECTORS) and store
    + * the quotient in ncyl and the remainder in rem.
    + */
    + dividend = virt_dev->nblocks;
    + rem = do_div(dividend, DEF_HEADS * DEF_SECTORS);
    + ncyl = dividend;
    + if (rem != 0)
    + ncyl++;
    + memcpy(&n, p + 2, sizeof(u32));
    + n = n | ((__force u32)cpu_to_be32(ncyl) >> 8);
    + memcpy(p + 2, &n, sizeof(u32));
    + if (1 == pcontrol)
    + memset(p + 2, 0, sizeof(geo_m_pg) - 2);
    + return sizeof(geo_m_pg);
    +}
    +
    +static int vdisk_format_pg(unsigned char *p, int pcontrol,
    + struct scst_vdisk_dev *virt_dev)
    +{ /* Format device page for mode_sense */
    + const unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
    + 0, 0, 0, 0, 0, 0, 0, 0,
    + 0, 0, 0, 0, 0x40, 0, 0, 0};
    +
    + memcpy(p, format_pg, sizeof(format_pg));
    + p[10] = (DEF_SECTORS >> 8) & 0xff;
    + p[11] = DEF_SECTORS & 0xff;
    + p[12] = (virt_dev->block_size >> 8) & 0xff;
    + p[13] = virt_dev->block_size & 0xff;
    + if (1 == pcontrol)
    + memset(p + 2, 0, sizeof(format_pg) - 2);
    + return sizeof(format_pg);
    +}
    +
    +static int vdisk_caching_pg(unsigned char *p, int pcontrol,
    + struct scst_vdisk_dev *virt_dev)
    +{ /* Caching page for mode_sense */
    + const unsigned char caching_pg[] = {0x8, 18, 0x10, 0, 0xff, 0xff, 0, 0,
    + 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
    +
    + memcpy(p, caching_pg, sizeof(caching_pg));
    + p[2] |= !(virt_dev->wt_flag || virt_dev->nv_cache) ? WCE : 0;
    + if (1 == pcontrol)
    + memset(p + 2, 0, sizeof(caching_pg) - 2);
    + return sizeof(caching_pg);
    +}
    +
    +static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol,
    + struct scst_vdisk_dev *virt_dev)
    +{ /* Control mode page for mode_sense */
    + const unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0,
    + 0, 0, 0x2, 0x4b};
    +
    + memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
    + switch (pcontrol) {
    + case 0:
    + p[2] |= virt_dev->dev->tst << 5;
    + p[2] |= virt_dev->dev->d_sense << 2;
    + p[3] |= virt_dev->dev->queue_alg << 4;
    + p[4] |= virt_dev->dev->swp << 3;
    + p[5] |= virt_dev->dev->tas << 6;
    + break;
    + case 1:
    + memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
    +#if 0 /*
    + * It's too early to implement it, since we can't control the
    + * backstorage device parameters. ToDo
    + */
    + p[2] |= 7 << 5; /* TST */
    + p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */
    +#endif
    + p[2] |= 1 << 2; /* D_SENSE */
    + p[4] |= 1 << 3; /* SWP */
    + p[5] |= 1 << 6; /* TAS */
    + break;
    + case 2:
    + p[2] |= DEF_TST << 5;
    + p[2] |= DEF_DSENSE << 2;
    + if (virt_dev->wt_flag || virt_dev->nv_cache)
    + p[3] |= DEF_QUEUE_ALG_WT << 4;
    + else
    + p[3] |= DEF_QUEUE_ALG << 4;
    + p[4] |= DEF_SWP << 3;
    + p[5] |= DEF_TAS << 6;
    + break;
    + default:
    + BUG();
    + }
    + return sizeof(ctrl_m_pg);
    +}
    +
    +static int vdisk_iec_m_pg(unsigned char *p, int pcontrol,
    + struct scst_vdisk_dev *virt_dev)
    +{ /* Informational Exceptions control mode page for mode_sense */
    + const unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
    + 0, 0, 0x0, 0x0};
    + memcpy(p, iec_m_pg, sizeof(iec_m_pg));
    + if (1 == pcontrol)
    + memset(p + 2, 0, sizeof(iec_m_pg) - 2);
    + return sizeof(iec_m_pg);
    +}
    +
    +static void vdisk_exec_mode_sense(struct scst_cmd *cmd)
    +{
    + int32_t length;
    + uint8_t *address;
    + uint8_t *buf;
    + struct scst_vdisk_dev *virt_dev;
    + uint32_t blocksize;
    + uint64_t nblocks;
    + unsigned char dbd, type;
    + int pcontrol, pcode, subpcode;
    + unsigned char dev_spec;
    + int msense_6, offset = 0, len;
    + unsigned char *bp;
    +
    + buf = kzalloc(MSENSE_BUF_SZ, GFP_KERNEL);
    + if (buf == NULL) {
    + scst_set_busy(cmd);
    + goto out;
    + }
    +
    + virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + blocksize = virt_dev->block_size;
    + nblocks = virt_dev->nblocks;
    +
    + type = cmd->dev->type; /* type dev */
    + dbd = cmd->cdb[1] & DBD;
    + pcontrol = (cmd->cdb[2] & 0xc0) >> 6;
    + pcode = cmd->cdb[2] & 0x3f;
    + subpcode = cmd->cdb[3];
    + msense_6 = (MODE_SENSE == cmd->cdb[0]);
    + dev_spec = (virt_dev->dev->rd_only ||
    + cmd->tgt_dev->acg_dev->rd_only) ? WP : 0;
    +
    + if (!virt_dev->blockio)
    + dev_spec |= DPOFUA;
    +
    + length = scst_get_buf_first(cmd, &address);
    + if (unlikely(length <= 0)) {
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_first() failed: %d", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + }
    + goto out_free;
    + }
    +
    + if (0x3 == pcontrol) {
    + TRACE_DBG("%s", "MODE SENSE: Saving values not supported");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_saving_params_unsup));
    + goto out_put;
    + }
    +
    + if (msense_6) {
    + buf[1] = type;
    + buf[2] = dev_spec;
    + offset = 4;
    + } else {
    + buf[2] = type;
    + buf[3] = dev_spec;
    + offset = 8;
    + }
    +
    + if (0 != subpcode) {
    + /* TODO: Control Extension page */
    + TRACE_DBG("%s", "MODE SENSE: Only subpage 0 is supported");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out_put;
    + }
    +
    + if (!dbd) {
    + /* Create block descriptor */
    + buf[offset - 1] = 0x08; /* block descriptor length */
    + if (nblocks >> 32) {
    + buf[offset + 0] = 0xFF;
    + buf[offset + 1] = 0xFF;
    + buf[offset + 2] = 0xFF;
    + buf[offset + 3] = 0xFF;
    + } else {
    + /* num blks */
    + buf[offset + 0] = (nblocks >> (BYTE * 3)) & 0xFF;
    + buf[offset + 1] = (nblocks >> (BYTE * 2)) & 0xFF;
    + buf[offset + 2] = (nblocks >> (BYTE * 1)) & 0xFF;
    + buf[offset + 3] = (nblocks >> (BYTE * 0)) & 0xFF;
    + }
    + buf[offset + 4] = 0; /* density code */
    + buf[offset + 5] = (blocksize >> (BYTE * 2)) & 0xFF;/* blklen */
    + buf[offset + 6] = (blocksize >> (BYTE * 1)) & 0xFF;
    + buf[offset + 7] = (blocksize >> (BYTE * 0)) & 0xFF;
    +
    + offset += 8; /* increment offset */
    + }
    +
    + bp = buf + offset;
    +
    + switch (pcode) {
    + case 0x1: /* Read-Write error recovery page, direct access */
    + len = vdisk_err_recov_pg(bp, pcontrol, virt_dev);
    + break;
    + case 0x2: /* Disconnect-Reconnect page, all devices */
    + len = vdisk_disconnect_pg(bp, pcontrol, virt_dev);
    + break;
    + case 0x3: /* Format device page, direct access */
    + len = vdisk_format_pg(bp, pcontrol, virt_dev);
    + break;
    + case 0x4: /* Rigid disk geometry */
    + len = vdisk_rigid_geo_pg(bp, pcontrol, virt_dev);
    + break;
    + case 0x8: /* Caching page, direct access */
    + len = vdisk_caching_pg(bp, pcontrol, virt_dev);
    + break;
    + case 0xa: /* Control Mode page, all devices */
    + len = vdisk_ctrl_m_pg(bp, pcontrol, virt_dev);
    + break;
    + case 0x1c: /* Informational Exceptions Mode page, all devices */
    + len = vdisk_iec_m_pg(bp, pcontrol, virt_dev);
    + break;
    + case 0x3f: /* Read all Mode pages */
    + len = vdisk_err_recov_pg(bp, pcontrol, virt_dev);
    + len += vdisk_disconnect_pg(bp + len, pcontrol, virt_dev);
    + len += vdisk_format_pg(bp + len, pcontrol, virt_dev);
    + len += vdisk_caching_pg(bp + len, pcontrol, virt_dev);
    + len += vdisk_ctrl_m_pg(bp + len, pcontrol, virt_dev);
    + len += vdisk_iec_m_pg(bp + len, pcontrol, virt_dev);
    + len += vdisk_rigid_geo_pg(bp + len, pcontrol, virt_dev);
    + break;
    + default:
    + TRACE_DBG("MODE SENSE: Unsupported page %x", pcode);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out_put;
    + }
    +
    + offset += len;
    +
    + if (msense_6)
    + buf[0] = offset - 1;
    + else {
    + buf[0] = ((offset - 2) >> 8) & 0xff;
    + buf[1] = (offset - 2) & 0xff;
    + }
    +
    + if (offset > length)
    + offset = length;
    + memcpy(address, buf, offset);
    +
    +out_put:
    + scst_put_buf(cmd, address);
    + if (offset < cmd->resp_data_len)
    + scst_set_resp_data_len(cmd, offset);
    +
    +out_free:
    + kfree(buf);
    +
    +out:
    + return;
    +}
    +
    +static int vdisk_set_wt(struct scst_vdisk_dev *virt_dev, int wt)
    +{
    + int res = 0;
    +
    + if ((virt_dev->wt_flag == wt) || virt_dev->nullio || virt_dev->nv_cache)
    + goto out;
    +
    + spin_lock(&virt_dev->flags_lock);
    + virt_dev->wt_flag = wt;
    + spin_unlock(&virt_dev->flags_lock);
    +
    + scst_dev_del_all_thr_data(virt_dev->dev);
    +
    +out:
    + return res;
    +}
    +
    +static void vdisk_ctrl_m_pg_select(unsigned char *p,
    + struct scst_vdisk_dev *virt_dev)
    +{
    + struct scst_device *dev = virt_dev->dev;
    + int old_swp = dev->swp, old_tas = dev->tas, old_dsense = dev->d_sense;
    +
    +#if 0
    + /* Not implemented yet, see comment in vdisk_ctrl_m_pg() */
    + dev->tst = p[2] >> 5;
    + dev->queue_alg = p[3] >> 4;
    +#endif
    + dev->swp = (p[4] & 0x8) >> 3;
    + dev->tas = (p[5] & 0x40) >> 6;
    + dev->d_sense = (p[2] & 0x4) >> 2;
    +
    + PRINT_INFO("Device %s: new control mode page parameters: SWP %x "
    + "(was %x), TAS %x (was %x), D_SENSE %d (was %d)",
    + virt_dev->name, dev->swp, old_swp, dev->tas, old_tas,
    + dev->d_sense, old_dsense);
    + return;
    +}
    +
    +static void vdisk_exec_mode_select(struct scst_cmd *cmd)
    +{
    + int32_t length;
    + uint8_t *address;
    + struct scst_vdisk_dev *virt_dev;
    + int mselect_6, offset;
    +
    + virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + mselect_6 = (MODE_SELECT == cmd->cdb[0]);
    +
    + length = scst_get_buf_first(cmd, &address);
    + if (unlikely(length <= 0)) {
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_first() failed: %d", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + }
    + goto out;
    + }
    +
    + if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) {
    + TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: Unsupported "
    + "value(s) of PF and/or SP bits (cdb[1]=%x)",
    + cmd->cdb[1]);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out_put;
    + }
    +
    + if (mselect_6)
    + offset = 4;
    + else
    + offset = 8;
    +
    + if (address[offset - 1] == 8) {
    + offset += 8;
    + } else if (address[offset - 1] != 0) {
    + PRINT_ERROR("%s", "MODE SELECT: Wrong parameters list "
    + "lenght");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list));
    + goto out_put;
    + }
    +
    + while (length > offset + 2) {
    + if (address[offset] & PS) {
    + PRINT_ERROR("%s", "MODE SELECT: Illegal PS bit");
    + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
    + scst_sense_invalid_field_in_parm_list));
    + goto out_put;
    + }
    + if ((address[offset] & 0x3f) == 0x8) {
    + /* Caching page */
    + if (address[offset + 1] != 18) {
    + PRINT_ERROR("%s", "MODE SELECT: Invalid "
    + "caching page request");
    + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
    + scst_sense_invalid_field_in_parm_list));
    + goto out_put;
    + }
    + if (vdisk_set_wt(virt_dev,
    + (address[offset + 2] & WCE) ? 0 : 1) != 0) {
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out_put;
    + }
    + break;
    + } else if ((address[offset] & 0x3f) == 0xA) {
    + /* Control page */
    + if (address[offset + 1] != 0xA) {
    + PRINT_ERROR("%s", "MODE SELECT: Invalid "
    + "control page request");
    + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
    + scst_sense_invalid_field_in_parm_list));
    + goto out_put;
    + }
    + vdisk_ctrl_m_pg_select(&address[offset], virt_dev);
    + } else {
    + PRINT_ERROR("MODE SELECT: Invalid request %x",
    + address[offset] & 0x3f);
    + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
    + scst_sense_invalid_field_in_parm_list));
    + goto out_put;
    + }
    + offset += address[offset + 1];
    + }
    +
    +out_put:
    + scst_put_buf(cmd, address);
    +
    +out:
    + return;
    +}
    +
    +static void vdisk_exec_log(struct scst_cmd *cmd)
    +{
    +
    + /* No log pages are supported */
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + return;
    +}
    +
    +static void vdisk_exec_read_capacity(struct scst_cmd *cmd)
    +{
    + int32_t length;
    + uint8_t *address;
    + struct scst_vdisk_dev *virt_dev;
    + uint32_t blocksize;
    + uint64_t nblocks;
    + uint8_t buffer[8];
    +
    + virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + blocksize = virt_dev->block_size;
    + nblocks = virt_dev->nblocks;
    +
    + if ((cmd->cdb[8] & 1) == 0) {
    + uint64_t lba = be64_to_cpu(get_unaligned((__be64 *)&cmd->cdb[2]));
    + if (lba != 0) {
    + TRACE_DBG("PMI zero and LBA not zero (cmd %p)", cmd);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out;
    + }
    + }
    +
    + /* Last block on the virt_dev is (nblocks-1) */
    + memset(buffer, 0, sizeof(buffer));
    + if (nblocks >> 32) {
    + buffer[0] = 0xFF;
    + buffer[1] = 0xFF;
    + buffer[2] = 0xFF;
    + buffer[3] = 0xFF;
    + } else {
    + buffer[0] = ((nblocks - 1) >> (BYTE * 3)) & 0xFF;
    + buffer[1] = ((nblocks - 1) >> (BYTE * 2)) & 0xFF;
    + buffer[2] = ((nblocks - 1) >> (BYTE * 1)) & 0xFF;
    + buffer[3] = ((nblocks - 1) >> (BYTE * 0)) & 0xFF;
    + }
    + buffer[4] = (blocksize >> (BYTE * 3)) & 0xFF;
    + buffer[5] = (blocksize >> (BYTE * 2)) & 0xFF;
    + buffer[6] = (blocksize >> (BYTE * 1)) & 0xFF;
    + buffer[7] = (blocksize >> (BYTE * 0)) & 0xFF;
    +
    + length = scst_get_buf_first(cmd, &address);
    + if (unlikely(length <= 0)) {
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_first() failed: %d", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + }
    + goto out;
    + }
    +
    + length = min_t(int, length, sizeof(buffer));
    +
    + memcpy(address, buffer, length);
    +
    + scst_put_buf(cmd, address);
    +
    + if (length < cmd->resp_data_len)
    + scst_set_resp_data_len(cmd, length);
    +
    +out:
    + return;
    +}
    +
    +static void vdisk_exec_read_capacity16(struct scst_cmd *cmd)
    +{
    + int32_t length;
    + uint8_t *address;
    + struct scst_vdisk_dev *virt_dev;
    + uint32_t blocksize;
    + uint64_t nblocks;
    + uint8_t buffer[32];
    +
    + virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + blocksize = virt_dev->block_size;
    + nblocks = virt_dev->nblocks - 1;
    +
    + if ((cmd->cdb[14] & 1) == 0) {
    + uint64_t lba = be64_to_cpu(get_unaligned((__be64 *)&cmd->cdb[2]));
    + if (lba != 0) {
    + TRACE_DBG("PMI zero and LBA not zero (cmd %p)", cmd);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out;
    + }
    + }
    +
    + memset(buffer, 0, sizeof(buffer));
    +
    + buffer[0] = nblocks >> 56;
    + buffer[1] = (nblocks >> 48) & 0xFF;
    + buffer[2] = (nblocks >> 40) & 0xFF;
    + buffer[3] = (nblocks >> 32) & 0xFF;
    + buffer[4] = (nblocks >> 24) & 0xFF;
    + buffer[5] = (nblocks >> 16) & 0xFF;
    + buffer[6] = (nblocks >> 8) & 0xFF;
    + buffer[7] = nblocks & 0xFF;
    +
    + buffer[8] = (blocksize >> (BYTE * 3)) & 0xFF;
    + buffer[9] = (blocksize >> (BYTE * 2)) & 0xFF;
    + buffer[10] = (blocksize >> (BYTE * 1)) & 0xFF;
    + buffer[11] = (blocksize >> (BYTE * 0)) & 0xFF;
    +
    + switch (blocksize) {
    + case 512:
    + buffer[13] = 3;
    + break;
    + case 1024:
    + buffer[13] = 2;
    + break;
    + case 2048:
    + buffer[13] = 1;
    + break;
    + case 4096:
    + default:
    + buffer[13] = 0;
    + break;
    + }
    +
    + length = scst_get_buf_first(cmd, &address);
    + if (unlikely(length <= 0)) {
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_first() failed: %d", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + }
    + goto out;
    + }
    +
    + length = min_t(int, length, sizeof(buffer));
    +
    + memcpy(address, buffer, length);
    +
    + scst_put_buf(cmd, address);
    +
    + if (length < cmd->resp_data_len)
    + scst_set_resp_data_len(cmd, length);
    +
    +out:
    + return;
    +}
    +
    +static void vdisk_exec_read_toc(struct scst_cmd *cmd)
    +{
    + int32_t length, off = 0;
    + uint8_t *address;
    + struct scst_vdisk_dev *virt_dev;
    + uint32_t nblocks;
    + uint8_t buffer[4+8+8] = { 0x00, 0x0a, 0x01, 0x01, 0x00, 0x14,
    + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
    +
    + if (cmd->dev->type != TYPE_ROM) {
    + PRINT_ERROR("%s", "READ TOC for non-CDROM device");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_opcode));
    + goto out;
    + }
    +
    + if (cmd->cdb[2] & 0x0e/*Format*/) {
    + PRINT_ERROR("%s", "READ TOC: invalid requested data format");
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out;
    + }
    +
    + if ((cmd->cdb[6] != 0 && (cmd->cdb[2] & 0x01)) ||
    + (cmd->cdb[6] > 1 && cmd->cdb[6] != 0xAA)) {
    + PRINT_ERROR("READ TOC: invalid requested track number %x",
    + cmd->cdb[6]);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
    + goto out;
    + }
    +
    + length = scst_get_buf_first(cmd, &address);
    + if (unlikely(length <= 0)) {
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_first() failed: %d", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + }
    + goto out;
    + }
    +
    + virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + /* ToDo when you have > 8TB ROM device. */
    + nblocks = (uint32_t)virt_dev->nblocks;
    +
    + /* Header */
    + memset(buffer, 0, sizeof(buffer));
    + buffer[2] = 0x01; /* First Track/Session */
    + buffer[3] = 0x01; /* Last Track/Session */
    + off = 4;
    + if (cmd->cdb[6] <= 1) {
    + /* Fistr TOC Track Descriptor */
    + /* ADDR 0x10 - Q Sub-channel encodes current position data
    + CONTROL 0x04 - Data track, recoreded uninterrupted */
    + buffer[off+1] = 0x14;
    + /* Track Number */
    + buffer[off+2] = 0x01;
    + off += 8;
    + }
    + if (!(cmd->cdb[2] & 0x01)) {
    + /* Lead-out area TOC Track Descriptor */
    + buffer[off+1] = 0x14;
    + /* Track Number */
    + buffer[off+2] = 0xAA;
    + /* Track Start Address */
    + buffer[off+4] = (nblocks >> (BYTE * 3)) & 0xFF;
    + buffer[off+5] = (nblocks >> (BYTE * 2)) & 0xFF;
    + buffer[off+6] = (nblocks >> (BYTE * 1)) & 0xFF;
    + buffer[off+7] = (nblocks >> (BYTE * 0)) & 0xFF;
    + off += 8;
    + }
    +
    + buffer[1] = off - 2; /* Data Length */
    +
    + if (off > length)
    + off = length;
    + memcpy(address, buffer, off);
    +
    + scst_put_buf(cmd, address);
    +
    + if (off < cmd->resp_data_len)
    + scst_set_resp_data_len(cmd, off);
    +
    +out:
    + return;
    +}
    +
    +static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd)
    +{
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    +
    + TRACE_DBG("PERSIST/PREVENT 0x%02x", cmd->cdb[4]);
    +
    + spin_lock(&virt_dev->flags_lock);
    + virt_dev->prevent_allow_medium_removal = cmd->cdb[4] & 0x01 ? 1 : 0;
    + spin_unlock(&virt_dev->flags_lock);
    +
    + return;
    +}
    +
    +static int vdisk_fsync(struct scst_vdisk_thr *thr, loff_t loff,
    + loff_t len, struct scst_cmd *cmd, struct scst_device *dev)
    +{
    + int res = 0;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)dev->dh_priv;
    + struct file *file;
    +
    + /* Hopefully, the compiler will generate the single comparison */
    + if (virt_dev->nv_cache || virt_dev->wt_flag ||
    + virt_dev->o_direct_flag || virt_dev->nullio)
    + goto out;
    +
    + if (virt_dev->blockio) {
    + res = blockio_flush(thr->bdev);
    + goto out;
    + }
    +
    + file = thr->fd;
    +
    +#if 0 /* For sparse files we might need to sync metadata as well */
    + res = generic_write_sync(file, loff, len);
    +#else
    + res = filemap_write_and_wait_range(file->f_mapping, loff, len);
    +#endif
    + if (unlikely(res != 0)) {
    + PRINT_ERROR("sync range failed (%d)", res);
    + if (cmd != NULL) {
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_write_error));
    + }
    + }
    +
    +out:
    + return res;
    +}
    +
    +static struct iovec *vdisk_alloc_iv(struct scst_cmd *cmd,
    + struct scst_vdisk_thr *thr)
    +{
    + int iv_count;
    +
    + iv_count = min_t(int, scst_get_buf_count(cmd), UIO_MAXIOV);
    + if (iv_count > thr->iv_count) {
    + kfree(thr->iv);
    + /* It can't be called in atomic context */
    + thr->iv = kmalloc(sizeof(*thr->iv) * iv_count, GFP_KERNEL);
    + if (thr->iv == NULL) {
    + PRINT_ERROR("Unable to allocate iv (%d)", iv_count);
    + scst_set_busy(cmd);
    + goto out;
    + }
    + thr->iv_count = iv_count;
    + }
    +
    +out:
    + return thr->iv;
    +}
    +
    +static void vdisk_exec_read(struct scst_cmd *cmd,
    + struct scst_vdisk_thr *thr, loff_t loff)
    +{
    + mm_segment_t old_fs;
    + loff_t err;
    + ssize_t length, full_len;
    + uint8_t __user *address;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + struct file *fd = thr->fd;
    + struct iovec *iv;
    + int iv_count, i;
    + bool finished = false;
    +
    + if (virt_dev->nullio)
    + goto out;
    +
    + iv = vdisk_alloc_iv(cmd, thr);
    + if (iv == NULL)
    + goto out;
    +
    + length = scst_get_buf_first(cmd, (uint8_t __force **)&address);
    + if (unlikely(length < 0)) {
    + PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out;
    + }
    +
    + old_fs = get_fs();
    + set_fs(get_ds());
    +
    + while (1) {
    + iv_count = 0;
    + full_len = 0;
    + i = -1;
    + while (length > 0) {
    + full_len += length;
    + i++;
    + iv_count++;
    + iv[i].iov_base = address;
    + iv[i].iov_len = length;
    + if (iv_count == UIO_MAXIOV)
    + break;
    + length = scst_get_buf_next(cmd,
    + (uint8_t __force **)&address);
    + }
    + if (length == 0) {
    + finished = true;
    + if (unlikely(iv_count == 0))
    + break;
    + } else if (unlikely(length < 0)) {
    + PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out_set_fs;
    + }
    +
    + TRACE_DBG("(iv_count %d, full_len %zd)", iv_count, full_len);
    + /* SEEK */
    + if (fd->f_op->llseek)
    + err = fd->f_op->llseek(fd, loff, 0/*SEEK_SET*/);
    + else
    + err = default_llseek(fd, loff, 0/*SEEK_SET*/);
    + if (err != loff) {
    + PRINT_ERROR("lseek trouble %lld != %lld",
    + (long long unsigned int)err,
    + (long long unsigned int)loff);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out_set_fs;
    + }
    +
    + /* READ */
    + err = vfs_readv(fd, (struct iovec __force __user *)iv, iv_count,
    + &fd->f_pos);
    +
    + if ((err < 0) || (err < full_len)) {
    + PRINT_ERROR("readv() returned %lld from %zd",
    + (long long unsigned int)err,
    + full_len);
    + if (err == -EAGAIN)
    + scst_set_busy(cmd);
    + else {
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_read_error));
    + }
    + goto out_set_fs;
    + }
    +
    + for (i = 0; i < iv_count; i++)
    + scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
    +
    + if (finished)
    + break;
    +
    + loff += full_len;
    + length = scst_get_buf_next(cmd, (uint8_t __force **)&address);
    + };
    +
    + set_fs(old_fs);
    +
    +out:
    + return;
    +
    +out_set_fs:
    + set_fs(old_fs);
    + for (i = 0; i < iv_count; i++)
    + scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
    + goto out;
    +}
    +
    +static void vdisk_exec_write(struct scst_cmd *cmd,
    + struct scst_vdisk_thr *thr, loff_t loff)
    +{
    + mm_segment_t old_fs;
    + loff_t err;
    + ssize_t length, full_len, saved_full_len;
    + uint8_t __user *address;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + struct file *fd = thr->fd;
    + struct iovec *iv, *eiv;
    + int i, iv_count, eiv_count;
    + bool finished = false;
    +
    + if (virt_dev->nullio)
    + goto out;
    +
    + iv = vdisk_alloc_iv(cmd, thr);
    + if (iv == NULL)
    + goto out;
    +
    + length = scst_get_buf_first(cmd, (uint8_t __force **)&address);
    + if (unlikely(length < 0)) {
    + PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out;
    + }
    +
    + old_fs = get_fs();
    + set_fs(get_ds());
    +
    + while (1) {
    + iv_count = 0;
    + full_len = 0;
    + i = -1;
    + while (length > 0) {
    + full_len += length;
    + i++;
    + iv_count++;
    + iv[i].iov_base = address;
    + iv[i].iov_len = length;
    + if (iv_count == UIO_MAXIOV)
    + break;
    + length = scst_get_buf_next(cmd,
    + (uint8_t __force **)&address);
    + }
    + if (length == 0) {
    + finished = true;
    + if (unlikely(iv_count == 0))
    + break;
    + } else if (unlikely(length < 0)) {
    + PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out_set_fs;
    + }
    +
    + saved_full_len = full_len;
    + eiv = iv;
    + eiv_count = iv_count;
    +restart:
    + TRACE_DBG("writing(eiv_count %d, full_len %zd)", eiv_count, full_len);
    +
    + /* SEEK */
    + if (fd->f_op->llseek)
    + err = fd->f_op->llseek(fd, loff, 0 /*SEEK_SET */);
    + else
    + err = default_llseek(fd, loff, 0 /*SEEK_SET */);
    + if (err != loff) {
    + PRINT_ERROR("lseek trouble %lld != %lld",
    + (long long unsigned int)err,
    + (long long unsigned int)loff);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out_set_fs;
    + }
    +
    + /* WRITE */
    + err = vfs_writev(fd, (struct iovec __force __user *)eiv, eiv_count,
    + &fd->f_pos);
    +
    + if (err < 0) {
    + PRINT_ERROR("write() returned %lld from %zd",
    + (long long unsigned int)err,
    + full_len);
    + if (err == -EAGAIN)
    + scst_set_busy(cmd);
    + else {
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_write_error));
    + }
    + goto out_set_fs;
    + } else if (err < full_len) {
    + /*
    + * Probably that's wrong, but sometimes write() returns
    + * value less, than requested. Let's restart.
    + */
    + int e = eiv_count;
    + TRACE_MGMT_DBG("write() returned %d from %zd "
    + "(iv_count=%d)", (int)err, full_len,
    + eiv_count);
    + if (err == 0) {
    + PRINT_INFO("Suspicious: write() returned 0 from "
    + "%zd (iv_count=%d)", full_len, eiv_count);
    + }
    + full_len -= err;
    + for (i = 0; i < e; i++) {
    + if ((long long)eiv->iov_len < err) {
    + err -= eiv->iov_len;
    + eiv++;
    + eiv_count--;
    + } else {
    + eiv->iov_base =
    + (uint8_t __force __user *)eiv->iov_base + err;
    + eiv->iov_len -= err;
    + break;
    + }
    + }
    + goto restart;
    + }
    +
    + for (i = 0; i < iv_count; i++)
    + scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
    +
    + if (finished)
    + break;
    +
    + loff += saved_full_len;
    + length = scst_get_buf_next(cmd, (uint8_t __force **)&address);
    + }
    +
    + set_fs(old_fs);
    +
    +out:
    + return;
    +
    +out_set_fs:
    + set_fs(old_fs);
    + for (i = 0; i < iv_count; i++)
    + scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
    + goto out;
    +}
    +
    +struct scst_blockio_work {
    + atomic_t bios_inflight;
    + struct scst_cmd *cmd;
    +};
    +
    +static inline void blockio_check_finish(struct scst_blockio_work *blockio_work)
    +{
    + /* Decrement the bios in processing, and if zero signal completion */
    + if (atomic_dec_and_test(&blockio_work->bios_inflight)) {
    + blockio_work->cmd->completed = 1;
    + blockio_work->cmd->scst_cmd_done(blockio_work->cmd,
    + SCST_CMD_STATE_DEFAULT, scst_estimate_context());
    + kmem_cache_free(blockio_work_cachep, blockio_work);
    + }
    + return;
    +}
    +
    +static void blockio_endio(struct bio *bio, int error)
    +{
    + struct scst_blockio_work *blockio_work = bio->bi_private;
    +
    + if (unlikely(!bio_flagged(bio, BIO_UPTODATE))) {
    + if (error == 0) {
    + PRINT_ERROR("Not up to date bio with error 0 for "
    + "cmd %p, returning -EIO", blockio_work->cmd);
    + error = -EIO;
    + }
    + }
    +
    + if (unlikely(error != 0)) {
    + static DEFINE_SPINLOCK(blockio_endio_lock);
    +
    + PRINT_ERROR("cmd %p returned error %d", blockio_work->cmd,
    + error);
    +
    + /* To protect from several bios finishing simultaneously */
    + spin_lock_bh(&blockio_endio_lock);
    +
    + if (bio->bi_rw & (1 << BIO_RW))
    + scst_set_cmd_error(blockio_work->cmd,
    + SCST_LOAD_SENSE(scst_sense_write_error));
    + else
    + scst_set_cmd_error(blockio_work->cmd,
    + SCST_LOAD_SENSE(scst_sense_read_error));
    +
    + spin_unlock_bh(&blockio_endio_lock);
    + }
    +
    + blockio_check_finish(blockio_work);
    +
    + bio_put(bio);
    + return;
    +}
    +
    +static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
    + u64 lba_start, int write)
    +{
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + struct block_device *bdev = thr->bdev;
    + struct request_queue *q = bdev_get_queue(bdev);
    + int length, max_nr_vecs = 0;
    + uint8_t *address;
    + struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
    + int need_new_bio;
    + struct scst_blockio_work *blockio_work;
    + int bios = 0;
    +
    + if (virt_dev->nullio)
    + goto out;
    +
    + /* Allocate and initialize blockio_work struct */
    + blockio_work = kmem_cache_alloc(blockio_work_cachep, GFP_KERNEL);
    + if (blockio_work == NULL)
    + goto out_no_mem;
    +
    + blockio_work->cmd = cmd;
    +
    + if (q)
    + max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_PAGES);
    + else
    + max_nr_vecs = 1;
    +
    + need_new_bio = 1;
    +
    + length = scst_get_buf_first(cmd, &address);
    + while (length > 0) {
    + int len, bytes, off, thislen;
    + uint8_t *addr;
    + u64 lba_start0;
    +
    + addr = address;
    + off = offset_in_page(addr);
    + len = length;
    + thislen = 0;
    + lba_start0 = lba_start;
    +
    + while (len > 0) {
    + int rc;
    + struct page *page = virt_to_page(addr);
    +
    + if (need_new_bio) {
    + bio = bio_kmalloc(GFP_KERNEL, max_nr_vecs);
    + if (!bio) {
    + PRINT_ERROR("Failed to create bio "
    + "for data segment %d (cmd %p)",
    + cmd->get_sg_buf_entry_num, cmd);
    + goto out_no_bio;
    + }
    +
    + bios++;
    + need_new_bio = 0;
    + bio->bi_end_io = blockio_endio;
    + bio->bi_sector = lba_start0 <<
    + (virt_dev->block_shift - 9);
    + bio->bi_bdev = bdev;
    + bio->bi_private = blockio_work;
    + /*
    + * Better to fail fast w/o any local recovery
    + * and retries.
    + */
    +#ifdef BIO_RW_FAILFAST
    + bio->bi_rw |= (1 << BIO_RW_FAILFAST);
    +#else
    + bio->bi_rw |= (1 << BIO_RW_FAILFAST_DEV) |
    + (1 << BIO_RW_FAILFAST_TRANSPORT) |
    + (1 << BIO_RW_FAILFAST_DRIVER);
    +#endif
    +#if 0 /* It could be win, but could be not, so a performance study is needed */
    + bio->bi_rw |= 1 << BIO_RW_SYNC;
    +#endif
    + if (!hbio)
    + hbio = tbio = bio;
    + else
    + tbio = tbio->bi_next = bio;
    + }
    +
    + bytes = min_t(unsigned int, len, PAGE_SIZE - off);
    +
    + rc = bio_add_page(bio, page, bytes, off);
    + if (rc < bytes) {
    + BUG_ON(rc != 0);
    + need_new_bio = 1;
    + lba_start0 += thislen >> virt_dev->block_shift;
    + thislen = 0;
    + continue;
    + }
    +
    + addr += PAGE_SIZE;
    + thislen += bytes;
    + len -= bytes;
    + off = 0;
    + }
    +
    + lba_start += length >> virt_dev->block_shift;
    +
    + scst_put_buf(cmd, address);
    + length = scst_get_buf_next(cmd, &address);
    + }
    +
    + /* +1 to prevent erroneous too early command completion */
    + atomic_set(&blockio_work->bios_inflight, bios+1);
    +
    + while (hbio) {
    + bio = hbio;
    + hbio = hbio->bi_next;
    + bio->bi_next = NULL;
    + submit_bio((write != 0), bio);
    + }
    +
    + if (q && q->unplug_fn)
    + q->unplug_fn(q);
    +
    + blockio_check_finish(blockio_work);
    +
    +out:
    + return;
    +
    +out_no_bio:
    + while (hbio) {
    + bio = hbio;
    + hbio = hbio->bi_next;
    + bio_put(bio);
    + }
    + kmem_cache_free(blockio_work_cachep, blockio_work);
    +
    +out_no_mem:
    + scst_set_busy(cmd);
    + goto out;
    +}
    +
    +static int blockio_flush(struct block_device *bdev)
    +{
    + int res = 0;
    +
    + res = blkdev_issue_flush(bdev, GFP_KERNEL, NULL, BLKDEV_IFL_WAIT);
    + if (res != 0)
    + PRINT_ERROR("blkdev_issue_flush() failed: %d", res);
    + return res;
    +}
    +
    +static void vdisk_exec_verify(struct scst_cmd *cmd,
    + struct scst_vdisk_thr *thr, loff_t loff)
    +{
    + mm_segment_t old_fs;
    + loff_t err;
    + ssize_t length, len_mem = 0;
    + uint8_t *address_sav, *address;
    + int compare;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)cmd->dev->dh_priv;
    + struct file *fd = thr->fd;
    + uint8_t *mem_verify = NULL;
    +
    + if (vdisk_fsync(thr, loff, cmd->bufflen, cmd, cmd->dev) != 0)
    + goto out;
    +
    + /*
    + * Until the cache is cleared prior the verifying, there is not
    + * much point in this code. ToDo.
    + *
    + * Nevertherless, this code is valuable if the data have not read
    + * from the file/disk yet.
    + */
    +
    + /* SEEK */
    + old_fs = get_fs();
    + set_fs(get_ds());
    +
    + if (!virt_dev->nullio) {
    + if (fd->f_op->llseek)
    + err = fd->f_op->llseek(fd, loff, 0/*SEEK_SET*/);
    + else
    + err = default_llseek(fd, loff, 0/*SEEK_SET*/);
    + if (err != loff) {
    + PRINT_ERROR("lseek trouble %lld != %lld",
    + (long long unsigned int)err,
    + (long long unsigned int)loff);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out_set_fs;
    + }
    + }
    +
    + mem_verify = vmalloc(LEN_MEM);
    + if (mem_verify == NULL) {
    + PRINT_ERROR("Unable to allocate memory %d for verify",
    + LEN_MEM);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + goto out_set_fs;
    + }
    +
    + length = scst_get_buf_first(cmd, &address);
    + address_sav = address;
    + if (!length && cmd->data_len) {
    + length = cmd->data_len;
    + compare = 0;
    + } else
    + compare = 1;
    +
    + while (length > 0) {
    + len_mem = (length > LEN_MEM) ? LEN_MEM : length;
    + TRACE_DBG("Verify: length %zd - len_mem %zd", length, len_mem);
    +
    + if (!virt_dev->nullio)
    + err = vfs_read(fd, (char __force __user *)mem_verify,
    + len_mem, &fd->f_pos);
    + else
    + err = len_mem;
    + if ((err < 0) || (err < len_mem)) {
    + PRINT_ERROR("verify() returned %lld from %zd",
    + (long long unsigned int)err, len_mem);
    + if (err == -EAGAIN)
    + scst_set_busy(cmd);
    + else {
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_read_error));
    + }
    + if (compare)
    + scst_put_buf(cmd, address_sav);
    + goto out_set_fs;
    + }
    + if (compare && memcmp(address, mem_verify, len_mem) != 0) {
    + TRACE_DBG("Verify: error memcmp length %zd", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_miscompare_error));
    + scst_put_buf(cmd, address_sav);
    + goto out_set_fs;
    + }
    + length -= len_mem;
    + address += len_mem;
    + if (compare && length <= 0) {
    + scst_put_buf(cmd, address_sav);
    + length = scst_get_buf_next(cmd, &address);
    + address_sav = address;
    + }
    + }
    +
    + if (length < 0) {
    + PRINT_ERROR("scst_get_buf_() failed: %zd", length);
    + scst_set_cmd_error(cmd,
    + SCST_LOAD_SENSE(scst_sense_hardw_error));
    + }
    +
    +out_set_fs:
    + set_fs(old_fs);
    + if (mem_verify)
    + vfree(mem_verify);
    +
    +out:
    + return;
    +}
    +
    +static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
    + struct scst_tgt_dev *tgt_dev)
    +{
    +
    + if ((mcmd->fn == SCST_LUN_RESET) || (mcmd->fn == SCST_TARGET_RESET)) {
    + /* Restore default values */
    + struct scst_device *dev = tgt_dev->dev;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + dev->tst = DEF_TST;
    + dev->d_sense = DEF_DSENSE;
    + if (virt_dev->wt_flag && !virt_dev->nv_cache)
    + dev->queue_alg = DEF_QUEUE_ALG_WT;
    + else
    + dev->queue_alg = DEF_QUEUE_ALG;
    + dev->swp = DEF_SWP;
    + dev->tas = DEF_TAS;
    +
    + spin_lock(&virt_dev->flags_lock);
    + virt_dev->prevent_allow_medium_removal = 0;
    + spin_unlock(&virt_dev->flags_lock);
    + } else if (mcmd->fn == SCST_PR_ABORT_ALL) {
    + struct scst_device *dev = tgt_dev->dev;
    + struct scst_vdisk_dev *virt_dev =
    + (struct scst_vdisk_dev *)dev->dh_priv;
    + spin_lock(&virt_dev->flags_lock);
    + virt_dev->prevent_allow_medium_removal = 0;
    + spin_unlock(&virt_dev->flags_lock);
    + }
    + return SCST_DEV_TM_NOT_COMPLETED;
    +}
    +
    +static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev)
    +{
    + char buf[128];
    + int i, j;
    +
    + i = snprintf(buf, sizeof(buf), "Registering virtual %s device %s ",
    + virt_dev->vdev_devt->name, virt_dev->name);
    + j = i;
    +
    + if (virt_dev->wt_flag)
    + i += snprintf(&buf[i], sizeof(buf) - i, "(WRITE_THROUGH");
    +
    + if (virt_dev->nv_cache)
    + i += snprintf(&buf[i], sizeof(buf) - i, "%sNV_CACHE",
    + (j == i) ? "(" : ", ");
    +
    + if (virt_dev->rd_only)
    + i += snprintf(&buf[i], sizeof(buf) - i, "%sREAD_ONLY",
    + (j == i) ? "(" : ", ");
    +
    + if (virt_dev->o_direct_flag)
    + i += snprintf(&buf[i], sizeof(buf) - i, "%sO_DIRECT",
    + (j == i) ? "(" : ", ");
    +
    + if (virt_dev->nullio)
    + i += snprintf(&buf[i], sizeof(buf) - i, "%sNULLIO",
    + (j == i) ? "(" : ", ");
    +
    + if (virt_dev->blockio)
    + i += snprintf(&buf[i], sizeof(buf) - i, "%sBLOCKIO",
    + (j == i) ? "(" : ", ");
    +
    + if (virt_dev->removable)
    + i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE",
    + (j == i) ? "(" : ", ");
    +
    + if (j == i)
    + PRINT_INFO("%s", buf);
    + else
    + PRINT_INFO("%s)", buf);
    +
    + return;
    +}
    +
    +static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev)
    +{
    + loff_t file_size;
    + int res = 0;
    +
    + BUG_ON(virt_dev->nullio);
    +
    + res = vdisk_get_file_size(virt_dev->filename,
    + virt_dev->blockio, &file_size);
    + if (res != 0)
    + goto out;
    +
    + if (file_size == virt_dev->file_size) {
    + PRINT_INFO("Size of virtual disk %s remained the same",
    + virt_dev->name);
    + goto out;
    + }
    +
    + res = scst_suspend_activity(true);
    + if (res != 0)
    + goto out;
    +
    + virt_dev->file_size = file_size;
    + virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
    +
    + scst_dev_del_all_thr_data(virt_dev->dev);
    +
    + PRINT_INFO("New size of SCSI target virtual disk %s "
    + "(fs=%lldMB, bs=%d, nblocks=%lld, cyln=%lld%s)",
    + virt_dev->name, virt_dev->file_size >> 20,
    + virt_dev->block_size,
    + (long long unsigned int)virt_dev->nblocks,
    + (long long unsigned int)virt_dev->nblocks/64/32,
    + virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
    + "than 1" : "");
    +
    + scst_capacity_data_changed(virt_dev->dev);
    +
    + scst_resume_activity();
    +
    +out:
    + return res;
    +}
    +
    +static int vdev_create(struct scst_dev_type *devt,
    + const char *name, struct scst_vdisk_dev **res_virt_dev)
    +{
    + int res = 0;
    + struct scst_vdisk_dev *virt_dev;
    + uint64_t dev_id_num;
    + int dev_id_len;
    + char dev_id_str[17];
    + int32_t i;
    +
    + virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
    + if (virt_dev == NULL) {
    + PRINT_ERROR("Allocation of virtual device %s failed",
    + devt->name);
    + res = -ENOMEM;
    + goto out;
    + }
    +
    + spin_lock_init(&virt_dev->flags_lock);
    + virt_dev->vdev_devt = devt;
    +
    + virt_dev->rd_only = DEF_RD_ONLY;
    + virt_dev->removable = DEF_REMOVABLE;
    +
    + virt_dev->block_size = DEF_DISK_BLOCKSIZE;
    + virt_dev->block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
    +
    + if (strlen(name) >= sizeof(virt_dev->name)) {
    + PRINT_ERROR("Name %s is too long (max allowed %zd)", name,
    + sizeof(virt_dev->name)-1);
    + res = -EINVAL;
    + goto out_free;
    + }
    + strcpy(virt_dev->name, name);
    +
    + dev_id_num = vdisk_gen_dev_id_num(virt_dev->name);
    + dev_id_len = scnprintf(dev_id_str, sizeof(dev_id_str), "%llx",
    + dev_id_num);
    +
    + i = strlen(virt_dev->name) + 1; /* for ' ' */
    + memset(virt_dev->t10_dev_id, ' ', i + dev_id_len);
    + memcpy(virt_dev->t10_dev_id, virt_dev->name, i-1);
    + memcpy(virt_dev->t10_dev_id + i, dev_id_str, dev_id_len);
    + TRACE_DBG("t10_dev_id %s", virt_dev->t10_dev_id);
    +
    + scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", dev_id_num);
    + TRACE_DBG("usn %s", virt_dev->usn);
    +
    + *res_virt_dev = virt_dev;
    +
    +out:
    + return res;
    +
    +out_free:
    + kfree(virt_dev);
    + goto out;
    +}
    +
    +static void vdev_destroy(struct scst_vdisk_dev *virt_dev)
    +{
    + kfree(virt_dev->filename);
    + kfree(virt_dev);
    + return;
    +}
    +
    +/* scst_vdisk_mutex supposed to be held */
    +static struct scst_vdisk_dev *vdev_find(const char *name)
    +{
    + struct scst_vdisk_dev *res, *vv;
    +
    + res = NULL;
    + list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
    + if (strcmp(vv->name, name) == 0) {
    + res = vv;
    + break;
    + }
    + }
    + return res;
    +}
    +
    +static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev,
    + char *params, const char *allowed_params[])
    +{
    + int res = 0;
    + unsigned long val;
    + char *param, *p, *pp;
    +
    + while (1) {
    + param = scst_get_next_token_str(&params);
    + if (param == NULL)
    + break;
    +
    + p = scst_get_next_lexem(&param);
    + if (*p == '\0') {
    + PRINT_ERROR("Syntax error at %s (device %s)",
    + param, virt_dev->name);
    + res = -EINVAL;
    + goto out;
    + }
    +
    + if (allowed_params != NULL) {
    + const char **a = allowed_params;
    + bool allowed = false;
    +
    + while (*a != NULL) {
    + if (!strcasecmp(*a, p)) {
    + allowed = true;
    + break;
    + }
    + a++;
    + }
    +
    + if (!allowed) {
    + PRINT_ERROR("Unknown parameter %s (device %s)", p,
    + virt_dev->name);
    + res = -EINVAL;
    + goto out;
    + }
    + }
    +
    + pp = scst_get_next_lexem(&param);
    + if (*pp == '\0') {
    + PRINT_ERROR("Parameter %s value missed for device %s",
    + p, virt_dev->name);
    + res = -EINVAL;
    + goto out;
    + }
    +
    + if (scst_get_next_lexem(&param)[0] != '\0') {
    + PRINT_ERROR("Too many parameter's %s values (device %s)",
    + p, virt_dev->name);
    + res = -EINVAL;
    + goto out;
    + }
    +
    + if (!strcasecmp("filename", p)) {
    + if (*pp != '/') {
    + PRINT_ERROR("Filename %s must be global "
    + "(device %s)", pp, virt_dev->name);
    + res = -EINVAL;
    + goto out;
    + }
    +
    + virt_dev->filename = kstrdup(pp, GFP_KERNEL);
    + if (virt_dev->filename == NULL) {
    + PRINT_ERROR("Unable to duplicate file name %s "
    + "(device %s)", pp, virt_dev->name);
    + res = -ENOMEM;
    + goto out;
    + }
    + continue;
    + }
    +
    + res = strict_strtoul(pp, 0, &val);
    + if (res != 0) {
    + PRINT_ERROR("strict_strtoul() for %s failed: %d "
    + "(device %s)", pp, res, virt_dev->name);
    + goto out;
    + }
    +
    + if (!strcasecmp("write_through", p)) {
    + virt_dev->wt_flag = val;
    + TRACE_DBG("WRITE THROUGH %d", virt_dev->wt_flag);
    + } else if (!strcasecmp("nv_cache", p)) {
    + virt_dev->nv_cache = val;
    + TRACE_DBG("NON-VOLATILE CACHE %d", virt_dev->nv_cache);
    + } else if (!strcasecmp("o_direct", p)) {
    +#if 0
    + virt_dev->o_direct_flag = val;
    + TRACE_DBG("O_DIRECT %d", virt_dev->o_direct_flag);
    +#else
    + PRINT_INFO("O_DIRECT flag doesn't currently"
    + " work, ignoring it, use fileio_tgt "
    + "in O_DIRECT mode instead (device %s)", virt_dev->name);
    +#endif
    + } else if (!strcasecmp("read_only", p)) {
    + virt_dev->rd_only = val;
    + TRACE_DBG("READ ONLY %d", virt_dev->rd_only);
    + } else if (!strcasecmp("removable", p)) {
    + virt_dev->removable = val;
    + TRACE_DBG("REMOVABLE %d", virt_dev->removable);
    + } else if (!strcasecmp("blocksize", p)) {
    + virt_dev->block_size = val;
    + virt_dev->block_shift = scst_calc_block_shift(
    + virt_dev->block_size);
    + if (virt_dev->block_shift < 9) {
    + res = -EINVAL;
    + goto out;
    + }
    + TRACE_DBG("block_size %d, block_shift %d",
    + virt_dev->block_size,
    + virt_dev->block_shift);
    + } else {
    + PRINT_ERROR("Unknown parameter %s (device %s)", p,
    + virt_dev->name);
    + res = -EINVAL;
    + goto out;
    + }
    + }
    +
    +out:
    + return res;
    +}
    +
    +/* scst_vdisk_mutex supposed to be held */
    +static int vdev_fileio_add_device(const char *device_name, char *params)
    +{
    + int res = 0;
    + struct scst_vdisk_dev *virt_dev;
    +
    + res = vdev_create(&vdisk_file_devtype, device_name, &virt_dev);
    + if (res != 0)
    + goto out;
    +
    + virt_dev->command_set_version = 0x04C0; /* SBC-3 */
    +
    + virt_dev->wt_flag = DEF_WRITE_THROUGH;
    + virt_dev->nv_cache = DEF_NV_CACHE;
    + virt_dev->o_direct_flag = DEF_O_DIRECT;
    +
    + res = vdev_parse_add_dev_params(virt_dev, params, NULL);
    + if (res != 0)
    + goto out_destroy;
    +
    + if (virt_dev->rd_only && (virt_dev->wt_flag || virt_dev->nv_cache)) {
    + PRINT_ERROR("Write options on read only device %s",
    + virt_dev->name);
    + res = -EINVAL;
    + goto out_destroy;
    + }
    +
    + if (virt_dev->filename == NULL) {
    + PRINT_ERROR("File name required (device %s)", virt_dev->name);
    + res = -EINVAL;
    + goto out_destroy;
    + }
    +
    + list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
    +
    + vdisk_report_registering(virt_dev);
    +
    + virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
    + virt_dev->name);
    + if (virt_dev->virt_id < 0) {
    + res = virt_dev->virt_id;
    + goto out_del;
    + }
    +
    + TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
    + virt_dev->virt_id);
    +
    +out:
    + return res;
    +
    +out_del:
    + list_del(&virt_dev->vdev_list_entry);
    +
    +out_destroy:
    + vdev_destroy(virt_dev);
    + goto out;
    +}
    +
    +/* scst_vdisk_mutex supposed to be held */
    +static int vdev_blockio_add_device(const char *device_name, char *params)
    +{
    + int res = 0;
    + const char *allowed_params[] = { "filename", "read_only", "removable",
    + "blocksize", "nv_cache", NULL };
    + struct scst_vdisk_dev *virt_dev;
    +
    + res = vdev_create(&vdisk_blk_devtype, device_name, &virt_dev);
    + if (res != 0)
    + goto out;
    +
    + virt_dev->command_set_version = 0x04C0; /* SBC-3 */
    +
    + virt_dev->blockio = 1;
    +
    + res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
    + if (res != 0)
    + goto out_destroy;
    +
    + if (virt_dev->filename == NULL) {
    + PRINT_ERROR("File name required (device %s)", virt_dev->name);
    + res = -EINVAL;
    + goto out_destroy;
    + }
    +
    + list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
    +
    + vdisk_report_registering(virt_dev);
    +
    + virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
    + virt_dev->name);
    + if (virt_dev->virt_id < 0) {
    + res = virt_dev->virt_id;
    + goto out_del;
    + }
    +
    + TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
    + virt_dev->virt_id);
    +
    +out:
    + return res;
    +
    +out_del:
    + list_del(&virt_dev->vdev_list_entry);
    +
    +out_destroy:
    + vdev_destroy(virt_dev);
    + goto out;
    +}
    +
    +/* scst_vdisk_mutex supposed to be held */
    +static int vdev_nullio_add_device(const char *device_name, char *params)
    +{
    + int res = 0;
    + const char *allowed_params[] = { "read_only", "removable",
    + "blocksize", NULL };
    + struct scst_vdisk_dev *virt_dev;
    +
    + res = vdev_create(&vdisk_null_devtype, device_name, &virt_dev);
    + if (res != 0)
    + goto out;
    +
    + virt_dev->command_set_version = 0x04C0; /* SBC-3 */
    +
    + virt_dev->nullio = 1;
    +
    + res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
    + if (res != 0)
    + goto out_destroy;
    +
    + list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
    +
    + vdisk_report_registering(virt_dev);
    +
    + virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
    + virt_dev->name);
    + if (virt_dev->virt_id < 0) {
    + res = virt_dev->virt_id;
    + goto out_del;
    + }
    +
    + TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
    + virt_dev->virt_id);
    +
    +out:
    + return res;
    +
    +out_del:
    + list_del(&virt_dev->vdev_list_entry);
    +
    +out_destroy:
    + vdev_destroy(virt_dev);
    + goto out;
    +}
    +
    +static ssize_t vdisk_add_fileio_device(const char *device_name, char *params)
    +{
    + int res;
    +
    + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
    + res = -EINTR;
    + goto out;
    + }
    +
    + res = vdev_fileio_add_device(device_name, params);
    +
    + mutex_unlock(&scst_vdisk_mutex);
    +
    +out:
    + return res;
    +}
    +
    +static ssize_t vdisk_add_blockio_device(const char *device_name, char *params)
    +{
    + int res;
    +
    + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
    + res = -EINTR;
    + goto out;
    + }
    +
    + res = vdev_blockio_add_device(device_name, params);
    +
    + mutex_unlock(&scst_vdisk_mutex);
    +
    +out:
    + return res;
    +
    +}
    +
    +static ssize_t vdisk_add_nullio_device(const char *device_name, char *params)
    +{
    + int res;
    +
    + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
    + res = -EINTR;
    + goto out;
    + }
    +
    + res = vdev_nullio_add_device(device_name, params);
    +
    + mutex_unlock(&scst_vdisk_mutex);
    +
    +out:
    + return res;
    +
    +}
    +
    +/* scst_vdisk_mutex supposed to be held */
    +static void vdev_del_device(struct scst_vdisk_dev *virt_dev)
    +{
    +
    + scst_unregister_virtual_device(virt_dev->virt_id);
    +
    + list_del(&virt_dev->vdev_list_entry);
    +
    + PRINT_INFO("Virtual device %s unregistered", virt_dev->name);
    + TRACE_DBG("virt_id %d unregistered", virt_dev->virt_id);
    +
    + vdev_destroy(virt_dev);
    +
    + return;
    +}
    +
    +static ssize_t vdisk_del_device(const char *device_name)
    +{
    + int res = 0;
    + struct scst_vdisk_dev *virt_dev;
    +
    + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
    + res = -EINTR;
    + goto out;
    + }
    +
    + virt_dev = vdev_find(device_name);
    + if (virt_dev == NULL) {
    + PRINT_ERROR("Device %s not found", device_name);
    + res = -EINVAL;
    + goto out_unlock;
    + }
    +
    + vdev_del_device(virt_dev);
    +
    +out_unlock:
    + mutex_unlock(&scst_vdisk_mutex);
    +
    +out:
    + return res;
    +}
    +
    +/* scst_vdisk_mutex supposed to be held */
    +static ssize_t __vcdrom_add_device(const char *device_name, char *params)
    +{
    + int res = 0;
    + const char *allowed_params[] = { NULL }; /* no params */
    + struct scst_vdisk_dev *virt_dev;
    +
    + res = vdev_create(&vcdrom_devtype, device_name, &virt_dev);
    + if (res != 0)
    + goto out;
    +
    + virt_dev->command_set_version = 0x02A0; /* MMC-3 */
    +
    + virt_dev->rd_only = 1;
    + virt_dev->removable = 1;
    + virt_dev->cdrom_empty = 1;
    +
    + virt_dev->block_size = DEF_CDROM_BLOCKSIZE;
    + virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT;
    +
    + res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
    + if (res != 0)
    + goto out_destroy;
    +
    + list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
    +
    + vdisk_report_registering(virt_dev);
    +
    + virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
    + virt_dev->name);
    + if (virt_dev->virt_id < 0) {
    + res = virt_dev->virt_id;
    + goto out_del;
    + }
    +
    + TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
    + virt_dev->virt_id);
    +
    +out:
    + return res;
    +
    +out_del:
    + list_del(&virt_dev->vdev_list_entry);
    +
    +out_destroy:
    + vdev_destroy(virt_dev);
    + goto out;
    +}
    +
    +static ssize_t vcdrom_add_device(const char *device_name, char *params)
    +{
    + int res;
    +
    + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
    + res = -EINTR;
    + goto out;
    + }
    +
    + res = __vcdrom_add_device(device_name, params);
    +
    + mutex_unlock(&scst_vdisk_mutex);
    +
    +out:
    + return res;
    +
    +}
    +
    +static ssize_t vcdrom_del_device(const char *device_name)
    +{
    + int res = 0;
    + struct scst_vdisk_dev *virt_dev;
    +
    + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
    + res = -EINTR;
    + goto out;
    + }
    +
    + virt_dev = vdev_find(device_name);
    + if (virt_dev == NULL) {
    + PRINT_ERROR("Device %s not found", device_name);
    + res = -EINVAL;
    + goto out_unlock;
    + }
    +
    + vdev_del_device(virt_dev);
    +
    +out_unlock:
    + mutex_unlock(&scst_vdisk_mutex);
    +
    +out:
    + return res;
    +}
    +
    +static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
    + char *buffer)
    +{
    + loff_t err;
    + char *old_fn, *p, *pp;
    + const char *filename = NULL;
    + int length = strlen(buffer);
    + int res = 0;
    +
    + p = buffer;
    +
    + while (isspace(*p) && *p != '\0')
    + p++;
    + filename = p;
    + p = &buffer[length-1];
    + pp = &buffer[length];
    + while (isspace(*p) && (*p != '\0')) {
    + pp = p;
    + p--;
    + }
    + *pp = '\0';
    +
    + res = scst_suspend_activity(true);
    + if (res != 0)
    + goto out;
    +
    + /* To sync with detach*() functions */
    + mutex_lock(&scst_mutex);
    +
    + if (*filename == '\0') {
    + virt_dev->cdrom_empty = 1;
    + TRACE_DBG("%s", "No media");
    + } else if (*filename != '/') {
    + PRINT_ERROR("File path \"%s\" is not absolute", filename);
    + res = -EINVAL;
    + goto out_unlock;
    + } else
    + virt_dev->cdrom_empty = 0;
    +
    + old_fn = virt_dev->filename;
    +
    + if (!virt_dev->cdrom_empty) {
    + int len = strlen(filename) + 1;
    + char *fn = kmalloc(len, GFP_KERNEL);
    + if (fn == NULL) {
    + TRACE(TRACE_OUT_OF_MEM, "%s",
    + "Allocation of filename failed");
    + res = -ENOMEM;
    + goto out_unlock;
    + }
    +
    + strlcpy(fn, filename, len);
    + virt_dev->filename = fn;
    +
    + res = vdisk_get_file_size(virt_dev->filename,
    + virt_dev->blockio, &err);
    + if (res != 0)
    + goto out_free_fn;
    + } else {
    + err = 0;
    + virt_dev->filename = NULL;
    + }
    +
    + if (virt_dev->prevent_allow_medium_removal) {
    + PRINT_ERROR("Prevent medium removal for "
    + "virtual device with name %s", virt_dev->name);
    + res = -EINVAL;
    + goto out_free_fn;
    + }
    +
    + virt_dev->file_size = err;
    + virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
    + if (!virt_dev->cdrom_empty)
    + virt_dev->media_changed = 1;
    +
    + mutex_unlock(&scst_mutex);
    +
    + scst_dev_del_all_thr_data(virt_dev->dev);
    +
    + if (!virt_dev->cdrom_empty) {
    + PRINT_INFO("Changed SCSI target virtual cdrom %s "
    + "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld,"
    + " cyln=%lld%s)", virt_dev->name,
    + vdev_get_filename(virt_dev),
    + virt_dev->file_size >> 20, virt_dev->block_size,
    + (long long unsigned int)virt_dev->nblocks,
    + (long long unsigned int)virt_dev->nblocks/64/32,
    + virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
    + "than 1" : "");
    + } else {
    + PRINT_INFO("Removed media from SCSI target virtual cdrom %s",
    + virt_dev->name);
    + }
    +
    + kfree(old_fn);
    +
    +out_resume:
    + scst_resume_activity();
    +
    +out:
    + return res;
    +
    +out_free_fn:
    + kfree(virt_dev->filename);
    + virt_dev->filename = old_fn;
    +
    +out_unlock:
    + mutex_unlock(&scst_mutex);
    + goto out_resume;
    +}
    +
    +static int vcdrom_sysfs_process_filename_store(struct scst_sysfs_work_item *work)
    +{
    + int res;
    + struct scst_device *dev = work->dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + /* It's safe, since we taken dev_kobj and dh_priv NULLed in attach() */
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + res = vcdrom_change(virt_dev, work->buf);
    +
    + kobject_put(&dev->dev_kobj);
    + return res;
    +}
    +
    +static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj,
    + struct kobj_attribute *attr, const char *buf, size_t count)
    +{
    + int res;
    + char *i_buf;
    + struct scst_sysfs_work_item *work;
    + struct scst_device *dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    +
    + i_buf = kmalloc(count+1, GFP_KERNEL);
    + if (i_buf == NULL) {
    + PRINT_ERROR("Unable to alloc intermediate buffer with size %zd",
    + count+1);
    + res = -ENOMEM;
    + goto out;
    + }
    + memcpy(i_buf, buf, count);
    + i_buf[count] = '\0';
    +
    + res = scst_alloc_sysfs_work(vcdrom_sysfs_process_filename_store, &work);
    + if (res != 0)
    + goto out_free;
    +
    + work->buf = i_buf;
    + work->dev = dev;
    +
    + kobject_get(&dev->dev_kobj);
    +
    + res = scst_sysfs_queue_wait_work(work);
    + if (res == 0)
    + res = count;
    +
    +out:
    + return res;
    +
    +out_free:
    + kfree(i_buf);
    + goto out;
    +}
    +
    +static ssize_t vdev_sysfs_size_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%lld\n", virt_dev->file_size / 1024 / 1024);
    + return pos;
    +}
    +
    +static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%d\n%s", (int)virt_dev->block_size,
    + (virt_dev->block_size == DEF_DISK_BLOCKSIZE) ? "" :
    + SCST_SYSFS_KEY_MARK "\n");
    + return pos;
    +}
    +
    +static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%d\n%s", virt_dev->rd_only ? 1 : 0,
    + (virt_dev->rd_only == DEF_RD_ONLY) ? "" :
    + SCST_SYSFS_KEY_MARK "");
    + return pos;
    +}
    +
    +static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%d\n%s", virt_dev->wt_flag ? 1 : 0,
    + (virt_dev->wt_flag == DEF_WRITE_THROUGH) ? "" :
    + SCST_SYSFS_KEY_MARK "");
    + return pos;
    +}
    +
    +static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%d\n%s", virt_dev->nv_cache ? 1 : 0,
    + (virt_dev->nv_cache == DEF_NV_CACHE) ? "" :
    + SCST_SYSFS_KEY_MARK "");
    + return pos;
    +}
    +
    +static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%d\n%s", virt_dev->o_direct_flag ? 1 : 0,
    + (virt_dev->o_direct_flag == DEF_O_DIRECT) ? "" :
    + SCST_SYSFS_KEY_MARK "");
    + return pos;
    +}
    +
    +static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%d\n", virt_dev->removable ? 1 : 0);
    +
    + if ((virt_dev->dev->type != TYPE_ROM) &&
    + (virt_dev->removable != DEF_REMOVABLE))
    + pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK);
    + return pos;
    +}
    +
    +static int vdev_sysfs_process_get_filename(struct scst_sysfs_work_item *work)
    +{
    + int res = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = work->dev;
    +
    + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
    + res = -EINTR;
    + goto out_put;
    + }
    +
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + if (virt_dev == NULL)
    + goto out_unlock;
    +
    + if (virt_dev->filename != NULL)
    + work->res_buf = kasprintf(GFP_KERNEL, "%s\n%s\n",
    + vdev_get_filename(virt_dev), SCST_SYSFS_KEY_MARK);
    + else
    + work->res_buf = kasprintf(GFP_KERNEL, "%s\n",
    + vdev_get_filename(virt_dev));
    +
    +out_unlock:
    + mutex_unlock(&scst_vdisk_mutex);
    +
    +out_put:
    + kobject_put(&dev->dev_kobj);
    + return res;
    +}
    +
    +static ssize_t vdev_sysfs_filename_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int res = 0;
    + struct scst_device *dev;
    + struct scst_sysfs_work_item *work;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    +
    + res = scst_alloc_sysfs_work(vdev_sysfs_process_get_filename, &work);
    + if (res != 0)
    + goto out;
    +
    + work->dev = dev;
    +
    + kobject_get(&dev->dev_kobj);
    +
    + scst_sysfs_work_get(work);
    +
    + res = scst_sysfs_queue_wait_work(work);
    + if (res != 0)
    + goto out_put;
    +
    + res = snprintf(buf, SCST_SYSFS_BLOCK_SIZE, "%s\n", work->res_buf);
    +
    +out_put:
    + scst_sysfs_work_put(work);
    +
    +out:
    + return res;
    +}
    +
    +static int vdisk_sysfs_process_resync_size_store(
    + struct scst_sysfs_work_item *work)
    +{
    + int res;
    + struct scst_device *dev = work->dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + /* It's safe, since we taken dev_kobj and dh_priv NULLed in attach() */
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + res = vdisk_resync_size(virt_dev);
    +
    + kobject_put(&dev->dev_kobj);
    + return res;
    +}
    +
    +static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj,
    + struct kobj_attribute *attr, const char *buf, size_t count)
    +{
    + int res;
    + struct scst_device *dev;
    + struct scst_sysfs_work_item *work;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    +
    + res = scst_alloc_sysfs_work(vdisk_sysfs_process_resync_size_store,
    + &work);
    + if (res != 0)
    + goto out;
    +
    + work->dev = dev;
    +
    + kobject_get(&dev->dev_kobj);
    +
    + res = scst_sysfs_queue_wait_work(work);
    + if (res == 0)
    + res = count;
    +
    +out:
    + return res;
    +}
    +
    +static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj,
    + struct kobj_attribute *attr, const char *buf, size_t count)
    +{
    + int res, i;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + write_lock_bh(&vdisk_t10_dev_id_rwlock);
    +
    + if ((count > sizeof(virt_dev->t10_dev_id)) ||
    + ((count == sizeof(virt_dev->t10_dev_id)) &&
    + (buf[count-1] != '\n'))) {
    + PRINT_ERROR("T10 device id is too long (max %zd "
    + "characters)", sizeof(virt_dev->t10_dev_id)-1);
    + res = -EINVAL;
    + goto out_unlock;
    + }
    +
    + memset(virt_dev->t10_dev_id, 0, sizeof(virt_dev->t10_dev_id));
    + memcpy(virt_dev->t10_dev_id, buf, count);
    +
    + i = 0;
    + while (i < sizeof(virt_dev->t10_dev_id)) {
    + if (virt_dev->t10_dev_id[i] == '\n') {
    + virt_dev->t10_dev_id[i] = '\0';
    + break;
    + }
    + i++;
    + }
    +
    + virt_dev->t10_dev_id_set = 1;
    +
    + res = count;
    +
    + PRINT_INFO("T10 device id for device %s changed to %s", virt_dev->name,
    + virt_dev->t10_dev_id);
    +
    +out_unlock:
    + write_unlock_bh(&vdisk_t10_dev_id_rwlock);
    + return res;
    +}
    +
    +static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + read_lock_bh(&vdisk_t10_dev_id_rwlock);
    + pos = sprintf(buf, "%s\n%s", virt_dev->t10_dev_id,
    + virt_dev->t10_dev_id_set ? SCST_SYSFS_KEY_MARK "\n" : "");
    + read_unlock_bh(&vdisk_t10_dev_id_rwlock);
    + return pos;
    +}
    +
    +static ssize_t vdev_sysfs_usn_show(struct kobject *kobj,
    + struct kobj_attribute *attr, char *buf)
    +{
    + int pos = 0;
    + struct scst_device *dev;
    + struct scst_vdisk_dev *virt_dev;
    +
    + dev = container_of(kobj, struct scst_device, dev_kobj);
    + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
    +
    + pos = sprintf(buf, "%s\n", virt_dev->usn);
    + return pos;
    +}
    +
    +static int __init init_scst_vdisk(struct scst_dev_type *devtype)
    +{
    + int res = 0;
    +
    + devtype->module = THIS_MODULE;
    +
    + res = scst_register_virtual_dev_driver(devtype);
    + if (res < 0)
    + goto out;
    +
    +out:
    + return res;
    +
    +}
    +
    +static void exit_scst_vdisk(struct scst_dev_type *devtype)
    +{
    +
    + mutex_lock(&scst_vdisk_mutex);
    + while (1) {
    + struct scst_vdisk_dev *virt_dev;
    +
    + if (list_empty(&vdev_list))
    + break;
    +
    + virt_dev = list_entry(vdev_list.next, typeof(*virt_dev),
    + vdev_list_entry);
    +
    + vdev_del_device(virt_dev);
    + }
    + mutex_unlock(&scst_vdisk_mutex);
    +
    + scst_unregister_virtual_dev_driver(devtype);
    + return;
    +}
    +
    +static int __init init_scst_vdisk_driver(void)
    +{
    + int res;
    +
    + vdisk_thr_cachep = KMEM_CACHE(scst_vdisk_thr, SCST_SLAB_FLAGS);
    + if (vdisk_thr_cachep == NULL) {
    + res = -ENOMEM;
    + goto out;
    + }
    +
    + blockio_work_cachep = KMEM_CACHE(scst_blockio_work, SCST_SLAB_FLAGS);
    + if (blockio_work_cachep == NULL) {
    + res = -ENOMEM;
    + goto out_free_vdisk_cache;
    + }
    +
    + if (num_threads < 1) {
    + PRINT_ERROR("num_threads can not be less than 1, use "
    + "default %d", DEF_NUM_THREADS);
    + num_threads = DEF_NUM_THREADS;
    + }
    +
    + vdisk_file_devtype.threads_num = num_threads;
    + vcdrom_devtype.threads_num = num_threads;
    +
    + atomic_set(&nullio_thr_data.hdr.ref, 1); /* never destroy it */
    +
    + res = init_scst_vdisk(&vdisk_file_devtype);
    + if (res != 0)
    + goto out_free_slab;
    +
    + res = init_scst_vdisk(&vdisk_blk_devtype);
    + if (res != 0)
    + goto out_free_vdisk;
    +
    + res = init_scst_vdisk(&vdisk_null_devtype);
    + if (res != 0)
    + goto out_free_blk;
    +
    + res = init_scst_vdisk(&vcdrom_devtype);
    + if (res != 0)
    + goto out_free_null;
    +
    +out:
    + return res;
    +
    +out_free_null:
    + exit_scst_vdisk(&vdisk_null_devtype);
    +
    +out_free_blk:
    + exit_scst_vdisk(&vdisk_blk_devtype);
    +
    +out_free_vdisk:
    + exit_scst_vdisk(&vdisk_file_devtype);
    +
    +out_free_slab:
    + kmem_cache_destroy(blockio_work_cachep);
    +
    +out_free_vdisk_cache:
    + kmem_cache_destroy(vdisk_thr_cachep);
    + goto out;
    +}
    +
    +static void __exit exit_scst_vdisk_driver(void)
    +{
    + exit_scst_vdisk(&vdisk_null_devtype);
    + exit_scst_vdisk(&vdisk_blk_devtype);
    + exit_scst_vdisk(&vdisk_file_devtype);
    + exit_scst_vdisk(&vcdrom_devtype);
    +
    + kmem_cache_destroy(blockio_work_cachep);
    + kmem_cache_destroy(vdisk_thr_cachep);
    +}
    +
    +module_init(init_scst_vdisk_driver);
    +module_exit(exit_scst_vdisk_driver);
    +
    +MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
    +MODULE_LICENSE("GPL");
    +MODULE_DESCRIPTION("SCSI disk (type 0) and CDROM (type 5) dev handler for "
    + "SCST using files on file systems or block devices");
    +MODULE_VERSION(SCST_VERSION_STRING);


    \
     
     \ /
      Last update: 2010-09-14 16:51    [W:0.257 / U:89.416 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site