SCST looks to be quite stable (for beta) and useful. It supports disks
(SCSI type 0), tapes (type 1), processor (type 3), CDROM's (type 5), MO
disks (type 7), medium changers (type 8) and RAID controller (type 0xC).
-There are also FILEIO and "performance" device handlers. In addition,
-starting from version 0.9.3 advanced per-initiator access and devices
-visibility management is added, so different initiators could see
-different set of devices with different access permissions. See below
-for details.
+There are also FILEIO and "performance" device handlers. In addition, it
+supports advanced per-initiator access and devices visibility
+management, so different initiators could see different set of devices
+with different access permissions. See below for details.
-This is more or less stable (but still beta) version.
+This is quite stable (but still beta) version.
Tested mostly on "vanilla" 2.6.17.8 kernel from kernel.org.
exec() method skip (pretend to execute) all READ and WRITE operations
and thus provide a way for direct link performance measurements without
overhead of actual data transferring from/to underlying SCSI device.
-Starting from 0.9.3 these handlers are incorporated inside of
-corresponding device handler for real device and could be assigned on
-run-time via "assign" command in "/proc/scsi_tgt/scsi_tgt" (see below).
NOTE: Since "perf" device handlers on READ operations don't touch the
==== commands' data buffer, it is returned to remote initiators as it
are seen remotely. There must be LUN 0 in each security group, i.e. LUs
numeration must not start from, e.g., 1.
-IMPORTANT: without loading appropriate device handler, corresponding devices
+IMPORTANT: Without loading appropriate device handler, corresponding devices
========= will be invisible for remote initiators, which could lead to holes
in the LUN addressing, so automatic device scanning by remote SCSI
mid-level could not notice the devices. Therefore you will have
'echo "scsi add-single-device A 0 0 B" >/proc/scsi/scsi',
where A - is the host number, B - LUN.
-IMPORTANT 1: In the current version simultaneous access to local SCSI
-=========== devices via standard high-level SCSI drivers (sd, st, sg,
- etc.) and SCST's target drivers is unsupported. Especially
- it is important for execution via sg and st commands that
- change the state of devices and their parameters, because
- that could lead to data corruption. If any such command
- is done, at least related device handler driver(s) must be
- restarted. For block devices READ/WRITE commands using direct
- disk handler look to be safe.
+IMPORTANT: In the current version simultaneous access to local SCSI devices
+========= via standard high-level SCSI drivers (sd, st, sg, etc.) and
+ SCST's target drivers is unsupported. Especially it is
+ important for execution via sg and st commands that change
+ the state of devices and their parameters, because that could
+ lead to data corruption. If any such command is done, at
+ least related device handler driver(s) must be restarted. For
+ block devices READ/WRITE commands using direct disk handler
+ look to be safe.
To uninstall, type 'make uninstall'. It is not implemented for 2.6
kernels.
information of currently open device files. On write it supports the
following command:
- * "open NAME PATH [FLAGS]" - opens file "PATH" as device "NAME" with
- flags "FLAGS. Possible flags:
+ * "open NAME PATH [BLOCK_SIZE] [FLAGS]" - opens file "PATH" as
+ device "NAME" with block size "BLOCK_SIZE" bytes with flags
+ "FLAGS". The block size must be power of 2 and >= 512 bytes
+ Default is 512. Possible flags:
- WRITE_THROUGH - write back caching disabled
- NULLIO - in this mode no real IO will be done, but success will be
returned. Intended to be used for performance measurements at the same
way as "*_perf" handlers.
+
+ - NV_CACHE - enables "non-volatile cache" mode. In this mode it is
+ assumed that the target has GOOD uninterruptable power supply
+ and software/hardware bug free, i.e. all data from the target's
+ cache are guaranteed sooner or later to go to the media, hence
+ all data synchronization with media operations, like
+ SYNCHRONIZE_CACHE, are ignored (BTW, so violating SCSI standard)
+ in order to bring a bit more performance. Use with extreme
+ caution, since in this mode after a crash of the target
+ journaled file systems don't guarantee the consistency after
+ journal recovery, therefore manual fsck MUST be ran. The main
+ intent for it is to determine the performance impact caused by
+ the cache synchronization. Note, that since usually the journal
+ barrier protection (see "IMPORTANT" below) turned off, enabling
+ NV_CACHE could change nothing, since no data synchronization
+ with media operations will go from the initiator.
* "close NAME" - closes device "NAME".
For example, "echo "open disk1 /vdisks/disk1" >/proc/scsi_tgt/disk_fileio/disk_fileio"
will open file /vdisks/disk1 as virtual FILEIO disk with name "disk1".
-IMPORTANT: by default for performance reasons FILEIO devices use write back
-========= caching policy, so if you care about the consistence of file systems,
- laying over them, and your data you must supply your target
- server with some king of UPS or disable write back caching
- via WRITE_THROUGH flag. The FS joutnaling over write back
- caching enabled devices doesn't protect from power failures
- on the target side, therefore even after successful journal
- rollback you very much risk to loose your data.
+IMPORTANT: By default for performance reasons FILEIO devices use write back
+========= caching policy. This is generally safe from the consistence of
+ journaled file systems, laying over them, point of view, but
+ your unsaved cached data will be lost in case of
+ power/hardware/software faulure, so you must supply your
+ target server with some kind of UPS or disable write back
+ caching using WRITE_THROUGH flag. You also should note, that
+ the file systems journaling over write back caching enabled
+ devices works reliably *ONLY* if it uses some kind of data
+ protection barriers (i.e. after writing journaling data some
+ kind of synchronization with media operations will be used),
+ otherwise, because of possible reordering in the cache, even
+ after successful journal rollback you very much risk to loose
+ your data on the FS. On Linux initiators for EXT3 and
+ ReiserFS file systems the barrier protection could be turned
+ on using "barrier=1" and "barrier=flush" mount options
+ correspondingly. Note, that usually it turned off by default
+ and the status of barriers usage isn't reported anywhere in
+ the system logs as well as there is no way to know it on the
+ mounted file system (at least we don't know how). Also note
+ that on some real-life workloads write through caching might
+ perform better, than write back one with barrier protection
+ turned on.
+
+IMPORTANT: Many disk and partition table mananagement utilities don't support
+========= block sizes >512 bytes, therefore make sure that your favorite one
+ supports it. Also, if you export disk file or device with
+ another block size, than one, with which it was already
+ divided on partitions, you could get various weird things
+ like utilities hang up or other unexpected behaviour. Thus, to
+ be sure, zero the exported file or device before the first
+ access to it from the remote initiator with another block size.
Performance
-----------
that in some cases it could lead to 5-10 times less performance, than
expected.
+IMPORTANT: If you use on initiator some versions of Windows (at least W2K)
+========= you can't get good write performance for FILEIO devices with
+ default 512 bytes block sizes. You could get about 10% of
+ the expected one. This is because of "unusual" write access
+ pattern, with which Windows'es write data and which is
+ (simplifying) incompatible with how Linux page cache works.
+ With 4096 bytes block sizes for FILEIO devices the write
+ performance will be as expected.
+
Just for reference: we had with 0.9.2 and "old" Qlogic driver on 2.4.2x
kernel, where we did carefull performance study, aggregate throuhput
about 390 Mb/sec from 2 qla2300 cards sitting on different 64-bit PCI
The target computer configuration was not very modern for the moment:
something like 2x1GHz Intel P3 Xeon CPUs. You can estimate the
memory/PCI speed from that. CPU load was ~5%, there were ~30K IRQ/sec
-and no additional SCST related context switches. Version 0.9.3 at the
-same setup will usually have 1 CS/cmd for buffer allocation, so the will
-be about 5-10K CS/sec. This will be fixed in the next version, when
-sgv_pool is integrated.
+and no additional SCST related context switches.
Credits
-------
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/ctype.h>
+#include <linux/writeback.h>
#include <asm/atomic.h>
#define LOG_PREFIX "dev_fileio"
#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 */
loff_t file_size; /* in bytes */
unsigned int rd_only_flag: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 cdrom_empty:1;
int virt_id;
char name[16+1]; /* Name of virtual device,
must be <= SCSI Model + 1 */
int iv_count;
struct list_head fdev_cmd_list;
wait_queue_head_t fdev_waitQ;
+ struct scst_fileio_dev *virt_dev;
atomic_t threads_count;
struct semaphore shutdown_mutex;
struct list_head ftgt_list_entry;
static void fileio_exec_mode_select(struct scst_cmd *cmd);
static void fileio_exec_read_toc(struct scst_cmd *cmd);
static void fileio_exec_prevent_allow_medium_removal(struct scst_cmd *cmd);
-static int fileio_fsync(struct scst_cmd *cmd, uint64_t lba_start, uint32_t number_of_blocks);
+static int fileio_fsync(struct scst_fileio_tgt_dev *ftgt_dev,
+ loff_t loff, loff_t len, struct scst_cmd *cmd);
static int disk_fileio_proc(char *buffer, char **start, off_t offset,
int length, int *eof, struct scst_dev_type *dev_type, int inout);
static int cdrom_fileio_proc(char *buffer, char **start, off_t offset,
static struct scst_dev_type cdrom_devtype_fileio = CDROM_TYPE_FILEIO;
static char *disk_fileio_proc_help_string =
- "echo \"open|close NAME [FILE_NAME [WRITE_THROUGH READ_ONLY "
- "O_DIRECT NULLIO]]\" >/proc/scsi_tgt/" DISK_FILEIO_NAME "/"
- DISK_FILEIO_NAME "\n";
+ "echo \"open|close NAME [FILE_NAME [BLOCK_SIZE] [WRITE_THROUGH "
+ "READ_ONLY O_DIRECT NULLIO NV_CACHE]]\" >/proc/scsi_tgt/"
+ DISK_FILEIO_NAME "/" DISK_FILEIO_NAME "\n";
static char *cdrom_fileio_proc_help_string =
"echo \"open|change|close NAME [FILE_NAME]\" "
if (dev->handler->type == TYPE_ROM)
virt_dev->rd_only_flag = 1;
- fd = fileio_open(virt_dev);
- if (IS_ERR(fd)) {
- res = PTR_ERR(fd);
- PRINT_ERROR_PR("filp_open(%s) returned an error %d",
- virt_dev->file_name, res);
- goto out;
- }
+ if (!virt_dev->cdrom_empty) {
+ fd = fileio_open(virt_dev);
+ if (IS_ERR(fd)) {
+ res = PTR_ERR(fd);
+ PRINT_ERROR_PR("filp_open(%s) returned an error %d",
+ virt_dev->file_name, res);
+ goto out;
+ }
- if ((fd->f_op == NULL) || (fd->f_op->readv == NULL) ||
- (fd->f_op->writev == NULL))
- {
- PRINT_ERROR_PR("%s", "Wrong f_op or FS doesn't have required "
- "capabilities");
- res = -EINVAL;
- goto out_close_file;
- }
+ if ((fd->f_op == NULL) || (fd->f_op->readv == NULL) ||
+ (fd->f_op->writev == NULL))
+ {
+ PRINT_ERROR_PR("%s", "Wrong f_op or FS doesn't have "
+ "required capabilities");
+ res = -EINVAL;
+ goto out_close_file;
+ }
- /* seek to end */
- old_fs = get_fs();
- set_fs(get_ds());
- if (fd->f_op->llseek) {
- err = fd->f_op->llseek(fd, 0, 2/*SEEK_END*/);
- } else {
- err = default_llseek(fd, 0, 2/*SEEK_END*/);
- }
- set_fs(old_fs);
- if (err < 0) {
- res = err;
- PRINT_ERROR_PR("llseek %s returned an error %d",
- virt_dev->file_name, res);
- goto out_close_file;
- }
- virt_dev->file_size = err;
- TRACE_DBG("size of file: %Ld", (uint64_t)err);
+ /* seek to end */
+ old_fs = get_fs();
+ set_fs(get_ds());
+ if (fd->f_op->llseek) {
+ err = fd->f_op->llseek(fd, 0, 2/*SEEK_END*/);
+ } else {
+ err = default_llseek(fd, 0, 2/*SEEK_END*/);
+ }
+ set_fs(old_fs);
+ if (err < 0) {
+ res = err;
+ PRINT_ERROR_PR("llseek %s returned an error %d",
+ virt_dev->file_name, res);
+ goto out_close_file;
+ }
+ virt_dev->file_size = err;
+ TRACE_DBG("size of file: %Ld", (uint64_t)err);
+
+ filp_close(fd, NULL);
+ } else
+ virt_dev->file_size = 0;
- filp_close(fd, NULL);
-
if (dev->handler->type == TYPE_DISK) {
- virt_dev->block_size = DEF_DISK_BLOCKSIZE;
- virt_dev->block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
- virt_dev->nblocks = virt_dev->file_size >> DEF_DISK_BLOCKSIZE_SHIFT;
+ virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
} else {
virt_dev->block_size = DEF_CDROM_BLOCKSIZE;
virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT;
virt_dev->nblocks = virt_dev->file_size >> DEF_CDROM_BLOCKSIZE_SHIFT;
}
- PRINT_INFO_PR("Attached SCSI target virtual %s %s "
+
+ if (!virt_dev->cdrom_empty) {
+ PRINT_INFO_PR("Attached SCSI target virtual %s %s "
"(file=\"%s\", fs=%LdMB, bs=%d, nblocks=%Ld, cyln=%Ld%s)",
(dev->handler->type == TYPE_DISK) ? "disk" : "cdrom",
virt_dev->name, virt_dev->file_name,
virt_dev->file_size >> 20, virt_dev->block_size,
virt_dev->nblocks, virt_dev->nblocks/64/32,
virt_dev->nblocks < 64*32 ? " !WARNING! cyln less than 1" : "");
+ } else {
+ PRINT_INFO_PR("Attached empty SCSI target virtual cdrom %s",
+ virt_dev->name);
+ }
dev->tgt_dev_specific = virt_dev;
static void fileio_do_job(struct scst_cmd *cmd)
{
- uint64_t lba_start = 0;
- uint32_t number_of_blocks;
+ uint64_t lba_start;
+ loff_t data_len;
int opcode = cmd->cdb[0];
loff_t loff;
struct scst_device *dev = cmd->dev;
struct scst_fileio_dev *virt_dev =
(struct scst_fileio_dev *)dev->tgt_dev_specific;
+ int fua = 0;
TRACE_ENTRY();
lba_start = (((cmd->cdb[1] & 0x1f) << (BYTE * 2)) +
(cmd->cdb[2] << (BYTE * 1)) +
(cmd->cdb[3] << (BYTE * 0)));
+ data_len = cmd->bufflen;
break;
case READ_10:
case READ_12:
case WRITE_VERIFY:
case WRITE_VERIFY_12:
case VERIFY_12:
+ lba_start = be32_to_cpu(*(u32 *)&cmd->cdb[2]);
+ data_len = cmd->bufflen;
+ break;
case SYNCHRONIZE_CACHE:
lba_start = be32_to_cpu(*(u32 *)&cmd->cdb[2]);
+ data_len = ((cmd->cdb[7] << (BYTE * 1)) +
+ (cmd->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;
case READ_16:
case WRITE_16:
case WRITE_VERIFY_16:
case VERIFY_16:
- lba_start = be64_to_cpu(*(u64 *)&cmd->cdb[2]);
+ lba_start = be64_to_cpu(*(u64*)&cmd->cdb[2]);
+ data_len = cmd->bufflen;
break;
+ default:
+ lba_start = 0;
+ data_len = 0;
}
loff = (loff_t)lba_start << virt_dev->block_shift;
- TRACE_DBG("cmd %p, lba_start %Ld, loff %Ld", cmd, lba_start,
- (uint64_t)loff);
- if (unlikely(loff < 0) ||
- unlikely((loff + cmd->bufflen) > virt_dev->file_size)) {
+ TRACE_DBG("cmd %p, lba_start %Ld, loff %Ld, data_len %Ld", cmd,
+ lba_start, (uint64_t)loff, (uint64_t)data_len);
+ if (unlikely(loff < 0) || unlikely(data_len < 0) ||
+ unlikely((loff + data_len) > virt_dev->file_size)) {
PRINT_INFO_PR("Access beyond the end of the device "
- "(%lld of %lld, len %zd)", (uint64_t)loff,
- (uint64_t)virt_dev->file_size, cmd->bufflen);
+ "(%lld of %lld, len %Ld)", (uint64_t)loff,
+ (uint64_t)virt_dev->file_size, (uint64_t)data_len);
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
scst_sense_block_out_range_error));
goto done;
}
-
+
+ switch (opcode) {
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ fua = (cmd->cdb[1] & 0x8) && !virt_dev->wt_flag;
+ if (cmd->cdb[1] & 0x8) {
+ TRACE(TRACE_SCSI, "FUA(%d): loff=%Ld, "
+ "data_len=%Ld", fua, (uint64_t)loff,
+ (uint64_t)data_len);
+ }
+ break;
+ }
+
switch (opcode) {
case READ_6:
case READ_10:
case WRITE_12:
case WRITE_16:
if (likely(!virt_dev->rd_only_flag)) {
- fileio_exec_write(cmd, loff);
-#if 0 /* instead, O_SYNC flag is used */
- if ((cmd->status == 0) && virt_dev->wt_flag) {
- number_of_blocks = cmd->bufflen >>
- virt_dev->block_shift;
- fileio_fsync(cmd, lba_start, number_of_blocks);
+ int do_fsync = 0;
+ struct scst_fileio_tgt_dev *ftgt_dev =
+ (struct scst_fileio_tgt_dev*)
+ cmd->tgt_dev->tgt_dev_specific;
+ if ((cmd->queue_type == SCST_CMD_QUEUE_ORDERED) &&
+ !virt_dev->wt_flag) {
+ TRACE(TRACE_SCSI/*|TRACE_SPECIAL*/, "ORDERED WRITE: "
+ "loff=%Ld, data_len=%Ld", (uint64_t)loff,
+ (uint64_t)data_len);
+ do_fsync = 1;
+ if (fileio_fsync(ftgt_dev, 0, 0, cmd) != 0)
+ goto done;
}
-#endif
- }
- else {
+ fileio_exec_write(cmd, loff);
+ /* O_SYNC flag is used for wt_flag devices */
+ if (do_fsync || fua)
+ fileio_fsync(ftgt_dev, loff, data_len, cmd);
+ } else {
TRACE_DBG("%s", "Attempt to write to read-only device");
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_data_protect));
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
if (likely(!virt_dev->rd_only_flag)) {
- fileio_exec_write(cmd, loff);
-#if 0 /* instead, O_SYNC flag is used */
- if ((cmd->status == 0) && virt_dev->wt_flag) {
- number_of_blocks = cmd->bufflen >>
- virt_dev->block_shift;
- fileio_fsync(cmd, lba_start, number_of_blocks);
+ int do_fsync = 0;
+ struct scst_fileio_tgt_dev *ftgt_dev =
+ (struct scst_fileio_tgt_dev*)
+ cmd->tgt_dev->tgt_dev_specific;
+ if ((cmd->queue_type == SCST_CMD_QUEUE_ORDERED) &&
+ !virt_dev->wt_flag) {
+ TRACE(TRACE_SCSI/*|TRACE_SPECIAL*/, "ORDERED "
+ "WRITE_VERIFY: loff=%Ld, data_len=%Ld",
+ (uint64_t)loff, (uint64_t)data_len);
+ do_fsync = 1;
+ if (fileio_fsync(ftgt_dev, 0, 0, cmd) != 0)
+ goto done;
}
-#endif
+ fileio_exec_write(cmd, loff);
+ /* O_SYNC flag is used for wt_flag devices */
if (cmd->status == 0)
fileio_exec_verify(cmd, loff);
- }
- else {
+ else if (do_fsync)
+ fileio_fsync(ftgt_dev, loff, data_len, cmd);
+ } else {
TRACE_DBG("%s", "Attempt to write to read-only device");
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_data_protect));
}
break;
case SYNCHRONIZE_CACHE:
- /* ToDo: IMMED bit */
- number_of_blocks = ((cmd->cdb[7] << (BYTE * 1)) +
- (cmd->cdb[8] << (BYTE * 0)));
- fileio_fsync(cmd, lba_start, number_of_blocks);
- break;
+ {
+ int immed = cmd->cdb[1] & 0x2;
+ struct scst_fileio_tgt_dev *ftgt_dev =
+ (struct scst_fileio_tgt_dev*)
+ cmd->tgt_dev->tgt_dev_specific;
+ TRACE(TRACE_SCSI, "SYNCHRONIZE_CACHE: "
+ "loff=%Ld, data_len=%Ld, immed=%d", (uint64_t)loff,
+ (uint64_t)data_len, immed);
+ if (immed) {
+ scst_get();
+ cmd->completed = 1;
+ cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
+ /* cmd is dead here */
+ fileio_fsync(ftgt_dev, loff, data_len, NULL);
+ /* ToDo: fileio_fsync() error processing */
+ scst_put();
+ goto out;
+ } else {
+ fileio_fsync(ftgt_dev, loff, data_len, cmd);
+ break;
+ }
+ }
case VERIFY_6:
case VERIFY:
case VERIFY_12:
done_uncompl:
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
+out:
TRACE_EXIT();
return;
}
init_waitqueue_head(&ftgt_dev->fdev_waitQ);
atomic_set(&ftgt_dev->threads_count, 0);
init_MUTEX_LOCKED(&ftgt_dev->shutdown_mutex);
-
- ftgt_dev->fd = fileio_open(virt_dev);
- if (IS_ERR(ftgt_dev->fd)) {
- res = PTR_ERR(ftgt_dev->fd);
- PRINT_ERROR_PR("filp_open(%s) returned an error %d",
- virt_dev->file_name, res);
- goto out_free;
- }
+ ftgt_dev->virt_dev = virt_dev;
+
+ if (!virt_dev->cdrom_empty) {
+ ftgt_dev->fd = fileio_open(virt_dev);
+ if (IS_ERR(ftgt_dev->fd)) {
+ res = PTR_ERR(ftgt_dev->fd);
+ PRINT_ERROR_PR("filp_open(%s) returned an error %d",
+ virt_dev->file_name, res);
+ goto out_free;
+ }
+ } else
+ ftgt_dev->fd = NULL;
/*
* Only ONE thread must be run here, otherwise the commands could
return res;
out_free_close:
- filp_close(ftgt_dev->fd, NULL);
+ if (ftgt_dev->fd)
+ filp_close(ftgt_dev->fd, NULL);
out_free:
TRACE_MEM("kfree ftgt_dev: %p", ftgt_dev);
wake_up_all(&ftgt_dev->fdev_waitQ);
down(&ftgt_dev->shutdown_mutex);
- filp_close(ftgt_dev->fd, NULL);
+ if (ftgt_dev->fd)
+ filp_close(ftgt_dev->fd, NULL);
if (ftgt_dev->iv != NULL) {
TRACE_MEM("kfree ftgt_dev->iv: %p", ftgt_dev->iv);
case REPORT_LUNS:
def:
default:
- TRACE_DBG("Wrong opcode 0x%02x", opcode);
+ TRACE_DBG("Invalid opcode 0x%02x", opcode);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
}
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;
+ }
+
/*
* No protection is necessary, because media_changed set only
* in suspended state and exec() is serialized
if (virt_dev->media_changed && (cmd->cdb[0] != INQUIRY) &&
(cmd->cdb[0] != REQUEST_SENSE) && (cmd->cdb[0] != REPORT_LUNS)) {
virt_dev->media_changed = 0;
+ TRACE_DBG("%s", "Reporting media changed");
scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(scst_sense_medium_changed_UA));
+ SCST_LOAD_SENSE(scst_sense_medium_changed_UA));
goto out;
}
break;
case REPORT_LUNS:
default:
- TRACE_DBG("Wrong opcode 0x%02x", opcode);
+ TRACE_DBG("Invalid opcode 0x%02x", opcode);
scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(scst_sense_invalid_opcode));
+ SCST_LOAD_SENSE(scst_sense_invalid_opcode));
}
out:
length = scst_get_buf_first(cmd, &address);
TRACE_DBG("length %d", length);
if (unlikely(length <= 0)) {
+ PRINT_ERROR_PR("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[3] = num + 12 - 4;
} else {
- /* Illegal request, invalid field in cdb */
+ 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 {
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;
static int fileio_err_recov_pg(unsigned char *p, int pcontrol,
struct scst_fileio_dev *virt_dev)
{ /* Read-Write Error Recovery page for mode_sense */
- unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
- 5, 0, 0xff, 0xff};
+ 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)
static int fileio_disconnect_pg(unsigned char *p, int pcontrol,
struct scst_fileio_dev *virt_dev)
{ /* Disconnect-Reconnect page for mode_sense */
- unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0};
+ 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)
static int fileio_format_pg(unsigned char *p, int pcontrol,
struct scst_fileio_dev *virt_dev)
{ /* Format device page for mode_sense */
- 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};
+ 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_PER >> 8) & 0xff;
static int fileio_caching_pg(unsigned char *p, int pcontrol,
struct scst_fileio_dev *virt_dev)
{ /* Caching page for mode_sense */
- unsigned char caching_pg[] = {0x8, 18, 0x10, 0, 0xff, 0xff, 0, 0,
- 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
+ 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};
- caching_pg[2] |= !(virt_dev->wt_flag) ? WCE : 0;
memcpy(p, caching_pg, sizeof(caching_pg));
+ p[2] |= !(virt_dev->wt_flag) ? WCE : 0;
if (1 == pcontrol)
memset(p + 2, 0, sizeof(caching_pg) - 2);
return sizeof(caching_pg);
static int fileio_ctrl_m_pg(unsigned char *p, int pcontrol,
struct scst_fileio_dev *virt_dev)
{ /* Control mode page for mode_sense */
- unsigned char ctrl_m_pg[] = {0xa, 0xa, 0x22, 0, 0, 0x40, 0, 0,
- 0, 0, 0x2, 0x4b};
+ const unsigned char ctrl_m_pg[] = {0xa, 0xa, 0x22, 0, 0, 0x40, 0, 0,
+ 0, 0, 0x2, 0x4b};
memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
+ if (!virt_dev->wt_flag)
+ p[3] |= 0x10; /* Enable unrestricted reordering */
if (1 == pcontrol)
memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
return sizeof(ctrl_m_pg);
static int fileio_iec_m_pg(unsigned char *p, int pcontrol,
struct scst_fileio_dev *virt_dev)
{ /* Informational Exceptions control mode page for mode_sense */
- unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
- 0, 0, 0x0, 0x0};
+ 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);
pcode = cmd->cdb[2] & 0x3f;
subpcode = cmd->cdb[3];
msense_6 = (MODE_SENSE == cmd->cdb[0]);
- dev_spec = virt_dev->rd_only_flag ? WP : 0;
+ dev_spec = (virt_dev->rd_only_flag ? WP : 0) | DPOFUA;
length = scst_get_buf_first(cmd, &address);
if (unlikely(length <= 0)) {
+ PRINT_ERROR_PR("scst_get_buf_first() failed: %d", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out_free;
memset(buf, 0, sizeof(buf));
- if (0x3 == pcontrol) { /* Saving values not supported */
+ 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 (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;
offset += len;
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;
res = 0; /* ?? ToDo */
goto out_resume;
}
- filp_close(ftgt_dev->fd, NULL);
+ if (ftgt_dev->fd)
+ filp_close(ftgt_dev->fd, NULL);
ftgt_dev->fd = fd;
}
up(&virt_dev->ftgt_list_mutex);
length = scst_get_buf_first(cmd, &address);
if (unlikely(length <= 0)) {
+ PRINT_ERROR_PR("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)) {
+ PRINT_ERROR_PR("MODE SELECT: PF and/or SP are wrongly set "
+ "(cdb[1]=%x)", cmd->cdb[1]);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out_put;
if (address[offset - 1] == 8) {
offset += 8;
} else if (address[offset - 1] != 0) {
+ PRINT_ERROR_PR("%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) {
- scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(
+ PRINT_ERROR_PR("%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) {
- scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(
+ PRINT_ERROR_PR("%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;
}
length = scst_get_buf_first(cmd, &address);
if (unlikely(length <= 0)) {
+ PRINT_ERROR_PR("scst_get_buf_first() failed: %d", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
length = scst_get_buf_first(cmd, &address);
if (unlikely(length <= 0)) {
+ PRINT_ERROR_PR("scst_get_buf_first() failed: %d", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
TRACE_ENTRY();
if (cmd->dev->handler->type != TYPE_ROM) {
+ PRINT_ERROR_PR("%s", "READ TOC for non-CDROM device");
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
goto out;
}
- if ((cmd->cdb[1] & 0x02/*TIME*/) ||
- (cmd->cdb[2] & 0x0e/*Format*/) ||
- (cmd->cdb[6] != 0 && (cmd->cdb[2] & 0x01)) ||
+ if (cmd->cdb[2] & 0x0e/*Format*/) {
+ PRINT_ERROR_PR("%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_PR("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_put;
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out;
}
length = scst_get_buf_first(cmd, &address);
if (unlikely(length <= 0)) {
+ PRINT_ERROR_PR("scst_get_buf_first() failed: %d", length);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
/* Header */
memset(buffer, 0, sizeof(buffer));
buffer[2] = 0x01; /* First Track/Session */
- buffer[3] = 0x01; /* Last Track/Session */
+ buffer[3] = 0x01; /* Last Track/Session */
off = 4;
if (cmd->cdb[6] <= 1)
{
}
if (!(cmd->cdb[2] & 0x01))
{
- /* Lead-out area TOC Track Descriptor */
+ /* Lead-out area TOC Track Descriptor */
buffer[off+1] = 0x14;
buffer[off+2] = 0xAA; /* Track Number */
buffer[off+4] = (nblocks >> (BYTE * 3)) & 0xFF; /* Track Start Address */
buffer[1] = off - 2; /* Data Length */
- memcpy(address, buffer, length < off ? length : off);
+ memcpy(address, buffer, (length < off) ? length : off);
-out_put:
scst_put_buf(cmd, address);
out:
if (cmd->dev->handler->type == TYPE_ROM)
virt_dev->prevent_allow_medium_removal =
cmd->cdb[4] & 0x01 ? 1 : 0;
- else
+ else {
+ PRINT_ERROR_PR("%s", "Prevent allow medium removal for "
+ "non-CDROM device");
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
+ }
return;
}
-static int fileio_fsync(struct scst_cmd *cmd, uint64_t lba_start, uint32_t number_of_blocks)
+static int fileio_fsync(struct scst_fileio_tgt_dev *ftgt_dev,
+ loff_t loff, loff_t len, struct scst_cmd *cmd)
{
- /* Mostly borrowed from sys_fsync() */
- struct address_space *mapping;
- int ret = 0, err;
- struct scst_fileio_tgt_dev *ftgt_dev =
- (struct scst_fileio_tgt_dev *)cmd->tgt_dev->tgt_dev_specific;
+ int res = 0;
struct file *file = ftgt_dev->fd;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct address_space *mapping = file->f_mapping;
TRACE_ENTRY();
- mapping = file->f_mapping;
-
- ret = -EINVAL;
- if (!file->f_op || !file->f_op->fsync) {
- /* Why? We can still call filemap_fdatawrite */
+ if (ftgt_dev->virt_dev->nv_cache)
goto out;
+
+ res = sync_page_range(inode, mapping, loff, len);
+ if (unlikely(res != 0)) {
+ PRINT_ERROR_PR("sync_page_range() failed (%d)", res);
+ if (cmd != NULL) {
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_write_error));
+ }
}
- /* We need to protect against concurrent writers.. */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
- mutex_lock(&mapping->host->i_mutex);
-#else
- down(&mapping->host->i_sem);
-#endif
- current->flags |= PF_SYNCWRITE;
- ret = filemap_fdatawrite(mapping);
- err = file->f_op->fsync(file, file->f_dentry, 0);
- if (!ret)
- ret = err;
- err = filemap_fdatawait(mapping);
- if (!ret)
- ret = err;
- current->flags &= ~PF_SYNCWRITE;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
- mutex_unlock(&mapping->host->i_mutex);
-#else
- up(&mapping->host->i_sem);
-#endif
+ /* ToDo: flush the device cache, if needed */
out:
- if (ret != 0)
- scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(scst_sense_hardw_error));
-
- /* ToDo: flush the device cache */
-
- TRACE_EXIT_RES(ret);
- return ret;
+ TRACE_EXIT_RES(res);
+ return res;
}
static struct iovec *fileio_alloc_iv(struct scst_cmd *cmd,
scst_set_busy(cmd);
else {
scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(scst_sense_hardw_error));
+ SCST_LOAD_SENSE(scst_sense_read_error));
}
goto out_set_fs;
}
scst_set_busy(cmd);
else {
scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(scst_sense_hardw_error));
+ SCST_LOAD_SENSE(scst_sense_write_error));
}
goto out_set_fs;
} else if (err < full_len) {
static void fileio_exec_verify(struct scst_cmd *cmd, loff_t loff)
{
- uint32_t number_of_blocks;
- struct scst_fileio_dev *virt_dev =
- (struct scst_fileio_dev *)cmd->dev->tgt_dev_specific;
mm_segment_t old_fs;
loff_t err;
ssize_t length, len_mem = 0;
TRACE_ENTRY();
- if (!virt_dev->wt_flag) {
- number_of_blocks = cmd->bufflen >> virt_dev->block_shift;
- fileio_fsync(cmd, loff, number_of_blocks);
- }
+ if (fileio_fsync(ftgt_dev, loff, cmd->bufflen, cmd) != 0)
+ goto out;
/*
* Until the cache is cleared prior the verifying, there is not
scst_set_busy(cmd);
else {
scst_set_cmd_error(cmd,
- SCST_LOAD_SENSE(scst_sense_hardw_error));
+ SCST_LOAD_SENSE(scst_sense_read_error));
}
scst_put_buf(cmd, address_sav);
goto out_set_fs;
if (mem_verify)
vfree(mem_verify);
+out:
TRACE_EXIT();
return;
}
}
if (inout == 0) { /* read */
- size = scnprintf(buffer, length, "%-17s %-9s %-8s %s\n",
+ size = scnprintf(buffer, length, "%-17s %-12s %-15s %s\n",
"Name", "Size(MB)", "Options", "File name");
if (fileio_proc_update_size(size, &len, &begin, &pos, &offset)) {
res = len;
{
int c;
size = scnprintf(buffer + len, length - len,
- "%-17s %-10d", virt_dev->name,
+ "%-17s %-13d", virt_dev->name,
(uint32_t)(virt_dev->file_size >> 20));
if (fileio_proc_update_size(size, &len, &begin, &pos,
&offset)) {
goto out_up;
}
}
+ if (virt_dev->nv_cache) {
+ size = scnprintf(buffer + len, length - len,
+ c ? ",NV" : "NV");
+ c += size;
+ if (fileio_proc_update_size(size, &len, &begin,
+ &pos, &offset)) {
+ res = len;
+ goto out_up;
+ }
+ }
if (virt_dev->rd_only_flag) {
size = scnprintf(buffer + len, length - len,
c ? ",RO" : "RO");
goto out_up;
}
}
- while (c < 9) {
+ while (c < 16) {
size = scnprintf(buffer + len, length - len, " ");
if (fileio_proc_update_size(size, &len, &begin, &pos,
&offset)) {
*eof = 1;
}
else { /* write */
- /*
- * Usage:
- * echo "open|close NAME FILE_NAME [WRITE_THROUGH \
- * READ_ONLY O_DIRECT NULLIO]" >
- * /proc/scsi_tgt/DISK_FILEIO_NAME/DISK_FILEIO_NAME
- */
+ uint32_t block_size = DEF_DISK_BLOCKSIZE;
+ int block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
p = buffer;
if (p[strlen(p) - 1] == '\n') {
p[strlen(p) - 1] = '\0';
while (isspace(*p) && *p != '\0')
p++;
+
+ if (isdigit(*p)) {
+ char *pp;
+ uint32_t t;
+ block_size = simple_strtoul(p, &pp, 0);
+ p = pp;
+ if ((*p != '\0') && !isspace(*p)) {
+ PRINT_ERROR_PR("Parse error: \"%s\"", p);
+ res = -EINVAL;
+ goto out_free_vdev;
+ }
+ while (isspace(*p) && *p != '\0')
+ p++;
+
+ t = block_size;
+ block_shift = 0;
+ while(1) {
+ if ((t & 1) != 0)
+ break;
+ t >>= 1;
+ block_shift++;
+ }
+ if (block_shift < 9) {
+ PRINT_ERROR_PR("Wrong block size %d",
+ block_size);
+ res = -EINVAL;
+ goto out_free_vdev;
+ }
+ }
+ virt_dev->block_size = block_size;
+ virt_dev->block_shift = block_shift;
while (*p != '\0') {
if (!strncmp("WRITE_THROUGH", p, 13)) {
p += 13;
virt_dev->wt_flag = 1;
TRACE_DBG("%s", "WRITE_THROUGH");
+ } else if (!strncmp("NV_CACHE", p, 8)) {
+ p += 8;
+ virt_dev->nv_cache = 1;
+ TRACE_DBG("%s", "NON-VOLATILE CACHE");
} else if (!strncmp("READ_ONLY", p, 9)) {
p += 9;
virt_dev->rd_only_flag = 1;
res = virt_dev->virt_id;
goto out_free_vpath;
}
- TRACE_DBG("Added virt_dev (name %s file name %s id %d) "
- "to disk_fileio_dev_list", virt_dev->name,
- virt_dev->file_name, virt_dev->virt_id);
+ TRACE_DBG("Added virt_dev (name %s, file name %s, "
+ "id %d, block size %d) to "
+ "disk_fileio_dev_list", virt_dev->name,
+ virt_dev->file_name, virt_dev->virt_id,
+ virt_dev->block_size);
} else { /* close */
virt_dev = NULL;
list_for_each_entry(vv, &disk_fileio_dev_list,
char *file_name;
int len;
int res = 0;
+ int cdrom_empty;
virt_dev = NULL;
list_for_each_entry(vv, &cdrom_fileio_dev_list,
p++;
*p++ = '\0';
if (*file_name == '\0') {
- PRINT_ERROR_PR("%s", "File name required");
- res = -EINVAL;
- goto out;
+ cdrom_empty = 1;
+ TRACE_DBG("%s", "No media");
} else if (*file_name != '/') {
PRINT_ERROR_PR("File path \"%s\" is not "
"absolute", file_name);
res = -EINVAL;
goto out;
- }
+ } else
+ cdrom_empty = 0;
virt_dev = fileio_alloc_dev();
if (virt_dev == NULL) {
res = -ENOMEM;
goto out;
}
+ virt_dev->cdrom_empty = cdrom_empty;
strcpy(virt_dev->name, name);
- len = strlen(file_name) + 1;
- virt_dev->file_name = kmalloc(len, GFP_KERNEL);
- TRACE_MEM("kmalloc(GFP_KERNEL) for file_name (%d): %p",
- len, virt_dev->file_name);
- if (virt_dev->file_name == NULL) {
- TRACE(TRACE_OUT_OF_MEM, "%s",
- "Allocation of file_name failed");
- res = -ENOMEM;
- goto out_free_vdev;
+ if (!virt_dev->cdrom_empty) {
+ len = strlen(file_name) + 1;
+ virt_dev->file_name = kmalloc(len, GFP_KERNEL);
+ TRACE_MEM("kmalloc(GFP_KERNEL) for file_name (%d): %p",
+ len, virt_dev->file_name);
+ if (virt_dev->file_name == NULL) {
+ TRACE(TRACE_OUT_OF_MEM, "%s",
+ "Allocation of file_name failed");
+ res = -ENOMEM;
+ goto out_free_vdev;
+ }
+ strncpy(virt_dev->file_name, file_name, len);
}
- strncpy(virt_dev->file_name, file_name, len);
list_add_tail(&virt_dev->fileio_dev_list_entry,
&cdrom_fileio_dev_list);
list_del(&virt_dev->fileio_dev_list_entry);
- TRACE_MEM("kfree for file_name: %p", virt_dev->file_name);
- kfree(virt_dev->file_name);
+ if (virt_dev->file_name) {
+ TRACE_MEM("kfree for file_name: %p", virt_dev->file_name);
+ kfree(virt_dev->file_name);
+ }
TRACE_MEM("kfree for virt_dev: %p", virt_dev);
kfree(virt_dev);
p++;
*p++ = '\0';
if (*file_name == '\0') {
- PRINT_ERROR_PR("%s", "File name required");
- res = -EINVAL;
- goto out;
+ virt_dev->cdrom_empty = 1;
+ TRACE_DBG("%s", "No media");
} else if (*file_name != '/') {
PRINT_ERROR_PR("File path \"%s\" is not "
"absolute", file_name);
res = -EINVAL;
goto out;
- }
-
- len = strlen(file_name) + 1;
- fn = kmalloc(len, GFP_KERNEL);
- TRACE_MEM("kmalloc(GFP_KERNEL) for file_name (%d): %p",
- len, fn);
- if (fn == NULL) {
- TRACE(TRACE_OUT_OF_MEM, "%s",
- "Allocation of file_name failed");
- res = -ENOMEM;
- goto out;
- }
+ } else
+ virt_dev->cdrom_empty = 0;
old_fn = virt_dev->file_name;
- virt_dev->file_name = fn;
- strncpy(virt_dev->file_name, file_name, len);
-
- fd = fileio_open(virt_dev);
- if (IS_ERR(fd)) {
- res = PTR_ERR(fd);
- PRINT_ERROR_PR("filp_open(%s) returned an error %d",
- virt_dev->file_name, res);
- goto out_free;
- }
- if ((fd->f_op == NULL) || (fd->f_op->readv == NULL))
- {
- PRINT_ERROR_PR("%s", "Wrong f_op or FS doesn't"
- " have required capabilities");
- res = -EINVAL;
- filp_close(fd, NULL);
- goto out_free;
- }
- /* seek to end */
- old_fs = get_fs();
- set_fs(get_ds());
- if (fd->f_op->llseek) {
- err = fd->f_op->llseek(fd, 0, 2/*SEEK_END*/);
+ if (!virt_dev->cdrom_empty) {
+ len = strlen(file_name) + 1;
+ fn = kmalloc(len, GFP_KERNEL);
+ TRACE_MEM("kmalloc(GFP_KERNEL) for file_name (%d): %p",
+ len, fn);
+ if (fn == NULL) {
+ TRACE(TRACE_OUT_OF_MEM, "%s",
+ "Allocation of file_name failed");
+ res = -ENOMEM;
+ goto out;
+ }
+
+ strncpy(fn, file_name, len);
+ virt_dev->file_name = fn;
+
+ fd = fileio_open(virt_dev);
+ if (IS_ERR(fd)) {
+ res = PTR_ERR(fd);
+ PRINT_ERROR_PR("filp_open(%s) returned an error %d",
+ virt_dev->file_name, res);
+ goto out_free;
+ }
+ if ((fd->f_op == NULL) || (fd->f_op->readv == NULL)) {
+ PRINT_ERROR_PR("%s", "Wrong f_op or FS doesn't "
+ "have required capabilities");
+ res = -EINVAL;
+ filp_close(fd, NULL);
+ goto out_free;
+ }
+
+ /* seek to end */
+ old_fs = get_fs();
+ set_fs(get_ds());
+ if (fd->f_op->llseek) {
+ err = fd->f_op->llseek(fd, 0, 2/*SEEK_END*/);
+ } else {
+ err = default_llseek(fd, 0, 2/*SEEK_END*/);
+ }
+ set_fs(old_fs);
+ filp_close(fd, NULL);
+ if (err < 0) {
+ res = err;
+ PRINT_ERROR_PR("llseek %s returned an error %d",
+ virt_dev->file_name, res);
+ goto out_free;
+ }
} else {
- err = default_llseek(fd, 0, 2/*SEEK_END*/);
- }
- set_fs(old_fs);
- filp_close(fd, NULL);
- if (err < 0) {
- res = err;
- PRINT_ERROR_PR("llseek %s returned an error %d",
- virt_dev->file_name, res);
- goto out_free;
+ len = 0;
+ err = 0;
+ fn = NULL;
+ virt_dev->file_name = fn;
}
scst_suspend_activity();
virt_dev->file_size = err;
virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
- virt_dev->media_changed = 1;
- PRINT_INFO_PR("Changed SCSI target virtual cdrom %s "
- "(file=\"%s\", fs=%LdMB, bs=%d, nblocks=%Ld, cyln=%Ld%s)",
- virt_dev->name, virt_dev->file_name,
- virt_dev->file_size >> 20, virt_dev->block_size,
- virt_dev->nblocks, virt_dev->nblocks/64/32,
- virt_dev->nblocks < 64*32 ? " !WARNING! cyln less than 1" : "");
+ if (!virt_dev->cdrom_empty)
+ virt_dev->media_changed = 1;
down(&virt_dev->ftgt_list_mutex);
list_for_each_entry(ftgt_dev, &virt_dev->ftgt_list,
ftgt_list_entry)
{
- fd = fileio_open(virt_dev);
- if (IS_ERR(fd)) {
- res = PTR_ERR(fd);
- PRINT_ERROR_PR("filp_open(%s) returned an error %d, "
- "closing the device", virt_dev->file_name, res);
- up(&virt_dev->ftgt_list_mutex);
- goto out_err_resume;
- }
- filp_close(ftgt_dev->fd, NULL);
+ if (!virt_dev->cdrom_empty) {
+ fd = fileio_open(virt_dev);
+ if (IS_ERR(fd)) {
+ res = PTR_ERR(fd);
+ PRINT_ERROR_PR("filp_open(%s) returned an error %d, "
+ "closing the device", virt_dev->file_name, res);
+ up(&virt_dev->ftgt_list_mutex);
+ goto out_err_resume;
+ }
+ } else
+ fd = NULL;
+ if (ftgt_dev->fd)
+ filp_close(ftgt_dev->fd, NULL);
ftgt_dev->fd = fd;
}
up(&virt_dev->ftgt_list_mutex);
- TRACE_MEM("kfree for old_fn: %p", old_fn);
- kfree(old_fn);
+ if (!virt_dev->cdrom_empty) {
+ PRINT_INFO_PR("Changed SCSI target virtual cdrom %s "
+ "(file=\"%s\", fs=%LdMB, bs=%d, nblocks=%Ld, cyln=%Ld%s)",
+ virt_dev->name, virt_dev->file_name,
+ virt_dev->file_size >> 20, virt_dev->block_size,
+ virt_dev->nblocks, virt_dev->nblocks/64/32,
+ virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
+ "than 1" : "");
+ } else {
+ PRINT_INFO_PR("Removed media from SCSI target virtual cdrom %s",
+ virt_dev->name);
+ }
+
+ if (old_fn) {
+ TRACE_MEM("kfree for old_fn: %p", old_fn);
+ kfree(old_fn);
+ }
out_resume:
scst_resume_activity();
res = len;
}
else { /* write */
- /*
- * Usage:
- * echo "open|change|close NAME [FILE_NAME SIZE_IN_MB]" >
- * /proc/scsi_tgt/CDROM_FILEIO_NAME/CDROM_FILEIO_NAME
- */
p = buffer;
if (p[strlen(p) - 1] == '\n') {
p[strlen(p) - 1] = '\0';