- Support for Async. Event Notifications added

- Implemented "plug-and-play" notifications about new devices in security groups and changed size of a device both through AENs and Unit Attentions

 - New command SCST_USER_DEVICE_CAPACITY_CHANGED added to scst_user interface to notify SCST core that the corresponding device has changed its capacity

 - New command "resync_size" added to scst_vdisk proc interface to tell scst_vdisk to reread size of the corresponding device.

 - Docs update

 - Minor fixes and cleanups



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@697 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2009-03-17 17:56:28 +00:00
parent 244775bb35
commit 019760a88c
15 changed files with 993 additions and 265 deletions

View File

@@ -576,6 +576,9 @@ subdirectories "vdisk" and "vcdrom". They have similar layout:
* "close NAME" - closes device "NAME".
* "resync_size NAME" - refreshes size of device "NAME". Intended to be
used after device resize.
* "change NAME [PATH]" - changes a virtual CD in the VDISK CDROM.
By default, if neither BLOCKIO, nor NULLIO option is supplied, FILEIO

View File

@@ -515,6 +515,9 @@ subdirectories "vdisk" and "vcdrom". They have similar layout:
* "close NAME" - closes device "NAME".
* "resync_size NAME" - refreshes size of device "NAME". Intended to be
used after device resize.
* "change NAME [PATH]" - changes a virtual CD in the VDISK CDROM.
By default, if neither BLOCKIO, nor NULLIO option is supplied, FILEIO

View File

@@ -269,8 +269,36 @@ enum scst_exec_context {
#define SCST_PREPROCESS_STATUS_NEED_THREAD 4
/*************************************************************
** Allowed return codes for xmit_response(), rdy_to_xfer(),
** report_aen()
** Values for AEN functions
*************************************************************/
/*
* SCSI Asynchronous Event. Parameter contains SCSI sense
* (Unit Attention). AENs generated only for 2 the following UAs:
* CAPACITY DATA HAS CHANGED and REPORTED LUNS DATA HAS CHANGED.
* Other UAs reported regularly as CHECK CONDITION status,
* because it doesn't look safe to report them using AENs, since
* reporting using AENs opens delivery race windows even in case of
* untagged commands.
*/
#define SCST_AEN_SCSI 0
/*************************************************************
** Allowed return/status codes for report_aen() callback and
** scst_set_aen_delivery_status() function
*************************************************************/
/* Success */
#define SCST_AEN_RES_SUCCESS 0
/* Not supported */
#define SCST_AEN_RES_NOT_SUPPORTED -1
/* Failure */
#define SCST_AEN_RES_FAILED -2
/*************************************************************
** Allowed return codes for xmit_response(), rdy_to_xfer()
*************************************************************/
/* Success */
@@ -408,6 +436,21 @@ enum scst_exec_context {
(__flags), NULL, NULL)
#endif
/*************************************************************
** Vlaid_mask constants for scst_analyze_sense()
*************************************************************/
#define SCST_SENSE_KEY_VALID 1
#define SCST_SENSE_ASC_VALID 2
#define SCST_SENSE_ASCQ_VALID 4
#define SCST_SENSE_ASCx_VALID (SCST_SENSE_ASC_VALID | \
SCST_SENSE_ASCQ_VALID)
#define SCST_SENSE_ALL_VALID (SCST_SENSE_KEY_VALID | \
SCST_SENSE_ASC_VALID | \
SCST_SENSE_ASCQ_VALID)
/*************************************************************
* TYPES
*************************************************************/
@@ -422,6 +465,7 @@ struct scst_dev_type;
struct scst_acg;
struct scst_acg_dev;
struct scst_acn;
struct scst_aen;
/*
* SCST uses 64-bit numbers to represent LUN's internally. The value
@@ -665,17 +709,17 @@ struct scst_tgt_template {
int (*release) (struct scst_tgt *tgt);
/*
* This function is used for Asynchronous Event Notification.
* It is the responsibility of the driver to notify any/all
* initiators about the Asynchronous Event reported.
* Returns one of the SCST_TGT_RES_* constants.
* This function is used for Asynchronous Event Notifications.
*
* Returns one of the SCST_AEN_RES_* constants.
* After AEN is sent, target driver must call scst_aen_done() and,
* optionally, scst_set_aen_delivery_status().
*
* This command is expected to be NON-BLOCKING, but can sleep.
*
* MUST HAVE if low-level protocol supports AEN
*
* ToDo
* MUST HAVE, if low-level protocol supports AENs.
*/
int (*report_aen) (int mgmt_fn, const uint8_t *lun, int lun_len);
int (*report_aen) (struct scst_aen *aen);
/*
* Those functions can be used to export the driver's statistics and
@@ -1598,10 +1642,33 @@ struct scst_acn {
struct scst_tgt_dev_UA {
/* List entry in tgt_dev->UA_list */
struct list_head UA_list_entry;
/* Set if UA is global for session */
unsigned int global_UA:1;
/* Unit Attention sense */
uint8_t UA_sense_buffer[SCST_SENSE_BUFFERSIZE];
};
/* Used to deliver AENs */
struct scst_aen {
int event_fn; /* AEN fn */
struct scst_session *sess; /* corresponding session */
uint64_t lun; /* corresponding LUN in SCSI form */
union {
/* SCSI AEN data */
struct {
int aen_sense_len;
uint8_t aen_sense[SCST_STANDARD_SENSE_LEN];
};
};
/* Keeps status of AEN's delivery to remote initiator */
int delivery_status;
};
#ifndef smp_mb__after_set_bit
/* There is no smp_mb__after_set_bit() in the kernel */
#define smp_mb__after_set_bit() smp_mb()
@@ -1922,6 +1989,11 @@ void scst_set_busy(struct scst_cmd *cmd);
*/
void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq);
/*
* Notifies SCST core that dev changed its capacity
*/
void scst_capacity_data_changed(struct scst_device *dev);
/*
* Finds a command based on the supplied tag comparing it with one
* that previously set by scst_cmd_set_tag().
@@ -2340,8 +2412,8 @@ static inline void scst_clear_may_need_dma_sync(struct scst_cmd *cmd)
}
/*
* Get/clear functions for cmd's delivery_status. It is one of
* SCST_CMD_DELIVERY_* constants, it specifies the status of the
* Get/set functions for cmd's delivery_status. It is one of
* SCST_CMD_DELIVERY_* constants. It specifies the status of the
* command's delivery to initiator.
*/
static inline int scst_get_delivery_status(struct scst_cmd *cmd)
@@ -2393,6 +2465,60 @@ void scst_prepare_async_mcmd(struct scst_mgmt_cmd *mcmd);
*/
void scst_async_mcmd_completed(struct scst_mgmt_cmd *mcmd, int status);
/* Returns AEN's fn */
static inline int scst_aen_get_event_fn(struct scst_aen *aen)
{
return aen->event_fn;
}
/* Returns AEN's session */
static inline struct scst_session *scst_aen_get_sess(struct scst_aen *aen)
{
return aen->sess;
}
/* Returns AEN's LUN */
static inline uint64_t scst_aen_get_lun(struct scst_aen *aen)
{
return aen->lun;
}
/* Returns SCSI AEN's sense */
static inline const uint8_t *scst_aen_get_sense(struct scst_aen *aen)
{
return aen->aen_sense;
}
/* Returns SCSI AEN's sense length */
static inline int scst_aen_get_sense_len(struct scst_aen *aen)
{
return aen->aen_sense_len;
}
/*
* Get/set functions for AEN's delivery_status. It is one of
* SCST_AEN_RES_* constants. It specifies the status of the
* command's delivery to initiator.
*/
static inline int scst_get_aen_delivery_status(struct scst_aen *aen)
{
return aen->delivery_status;
}
static inline void scst_set_aen_delivery_status(struct scst_aen *aen,
int status)
{
aen->delivery_status = status;
}
/*
* Notifies SCST that the driver has sent the AEN and it
* can be freed now. Don't forget to set the delivery status, if it
* isn't success, using scst_set_aen_delivery_status() before calling
* this function.
*/
void scst_aen_done(struct scst_aen *aen);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
static inline struct page *sg_page(struct scatterlist *sg)
@@ -2410,6 +2536,13 @@ static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
memset(sgl, 0, sizeof(*sgl) * nents);
}
static inline void sg_init_one(struct scatterlist *sg, const void *buf,
unsigned int buflen)
{
sg_init_table(sg, 1);
sg_set_buf(sg, buf, buflen);
}
static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
{
sg->page = page;
@@ -2612,8 +2745,15 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic);
int scst_alloc_set_sense(struct scst_cmd *cmd, int atomic,
const uint8_t *sense, unsigned int len);
void scst_set_sense(uint8_t *buffer, int len, int key,
int asc, int ascq);
void scst_set_sense(uint8_t *buffer, int len, int key, int asc, int ascq);
/*
* Returnes true if sense matches to (key, asc, ascq) and false otherwise.
* Valid_mask is one or several SCST_SENSE_*_VALID constants setting valid
* (key, asc, ascq) values.
*/
bool scst_analyze_sense(const uint8_t *sense, int len, unsigned int valid_mask,
int key, int asc, int ascq);
/*
* Returnes a pseudo-random number for debugging purposes. Available only in

View File

@@ -29,6 +29,12 @@
/* Max size of CDB */
#define SCST_MAX_CDB_SIZE 16
/*
* Size of sense sufficient to carry standard sense data.
* Warning! It's allocated on stack!
*/
#define SCST_STANDARD_SENSE_LEN 17
/* Max size of sense */
#define SCST_SENSE_BUFFERSIZE 96
@@ -156,8 +162,8 @@ static inline int scst_is_ua_sense(const uint8_t *sense)
#define scst_sense_not_ready NOT_READY, 0x04, 0x10
#define scst_sense_invalid_message ILLEGAL_REQUEST, 0x49, 0
#define scst_sense_cleared_by_another_ini_UA UNIT_ATTENTION, 0x2F, 0
#define SCST_STANDARD_SENSE_LEN 14
#define scst_sense_capacity_data_changed UNIT_ATTENTION, 0x2A, 0x9
#define scst_sense_reported_luns_data_changed UNIT_ATTENTION, 0x3F, 0xE
/*************************************************************
* SCSI opcodes not listed anywhere else

View File

@@ -242,6 +242,7 @@ struct scst_user_reply_cmd {
#define SCST_USER_REPLY_AND_GET_CMD _IOWR('u', 5, struct scst_user_get_cmd)
#define SCST_USER_REPLY_CMD _IOW('u', 6, struct scst_user_reply_cmd)
#define SCST_USER_FLUSH_CACHE _IO('u', 7)
#define SCST_USER_DEVICE_CAPACITY_CHANGED _IO('u', 8)
/* Values for scst_user_get_cmd.subcode */
#define SCST_USER_ATTACH_SESS \

View File

@@ -68,9 +68,8 @@ static int cdrom_attach(struct scst_device *dev)
const int buffer_size = 512;
uint8_t *buffer = NULL;
int retries;
unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE];
unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
enum dma_data_direction data_dir;
unsigned char *sbuff;
struct cdrom_params *params;
TRACE_ENTRY();
@@ -106,16 +105,15 @@ static int cdrom_attach(struct scst_device *dev)
while (1) {
memset(buffer, 0, buffer_size);
data_dir = SCST_DATA_READ;
sbuff = sense_buffer;
TRACE_DBG("%s", "Doing READ_CAPACITY");
res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
buffer_size, sbuff,
buffer_size, sense_buffer,
SCST_GENERIC_CDROM_REG_TIMEOUT, 3, 0);
TRACE_DBG("READ_CAPACITY done: %x", res);
if ((res == 0) || (sbuff[2] != UNIT_ATTENTION))
if ((res == 0) || (sense_buffer[2] != UNIT_ATTENTION))
break;
if (!--retries) {
@@ -137,7 +135,7 @@ static int cdrom_attach(struct scst_device *dev)
TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)",
sector_size, dev->scsi_dev->scsi_level, SCSI_2);
} else {
TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE);
TRACE_BUFFER("Sense set", sense_buffer, sizeof(sense_buffer));
params->block_shift = CDROM_DEF_BLOCK_SHIFT;
}

View File

@@ -144,9 +144,8 @@ static int disk_attach(struct scst_device *dev)
const int buffer_size = 512;
uint8_t *buffer = NULL;
int retries;
unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE];
unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
enum dma_data_direction data_dir;
unsigned char *sbuff;
struct disk_params *params;
TRACE_ENTRY();
@@ -182,16 +181,16 @@ static int disk_attach(struct scst_device *dev)
while (1) {
memset(buffer, 0, buffer_size);
data_dir = SCST_DATA_READ;
sbuff = sense_buffer;
TRACE_DBG("%s", "Doing READ_CAPACITY");
res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
buffer_size, sbuff,
buffer_size, sense_buffer,
SCST_GENERIC_DISK_REG_TIMEOUT, 3, 0);
TRACE_DBG("READ_CAPACITY done: %x", res);
if (!res || (sbuff[12] != 0x28 && sbuff[12] != 0x29))
if (!res || (sense_buffer[12] != 0x28 &&
sense_buffer[12] != 0x29))
break;
if (!--retries) {
PRINT_ERROR("UA not clear after %d retries",
@@ -209,7 +208,7 @@ static int disk_attach(struct scst_device *dev)
params->block_shift =
scst_calc_block_shift(sector_size);
} else {
TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE);
TRACE_BUFFER("Sense set", sense_buffer, sizeof(sense_buffer));
res = -ENODEV;
goto out_free_buf;
}

View File

@@ -144,9 +144,8 @@ static int modisk_attach(struct scst_device *dev)
const int buffer_size = 512;
uint8_t *buffer = NULL;
int retries;
unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE];
unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
enum dma_data_direction data_dir;
unsigned char *sbuff;
struct modisk_params *params;
TRACE_ENTRY();
@@ -196,16 +195,15 @@ static int modisk_attach(struct scst_device *dev)
while (1) {
memset(buffer, 0, buffer_size);
data_dir = SCST_DATA_READ;
sbuff = sense_buffer;
TRACE_DBG("%s", "Doing READ_CAPACITY");
res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
buffer_size, sbuff,
buffer_size, sense_buffer,
SCST_GENERIC_MODISK_REG_TIMEOUT, 3, 0);
TRACE_DBG("READ_CAPACITY done: %x", res);
if (!res || (sbuff[2] != UNIT_ATTENTION))
if (!res || (sense_buffer[2] != UNIT_ATTENTION))
break;
if (!--retries) {
@@ -226,9 +224,9 @@ static int modisk_attach(struct scst_device *dev)
TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)",
sector_size, dev->scsi_dev->scsi_level, SCSI_2);
} else {
TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE);
TRACE_BUFFER("Sense set", sense_buffer, sizeof(sense_buffer));
if (sbuff[2] != NOT_READY) {
if (sense_buffer[2] != NOT_READY) {
res = -ENODEV;
goto out_free_buf;
}

View File

@@ -336,7 +336,17 @@ static int tape_done(struct scst_cmd *cmd)
else if ((status == SAM_STAT_CHECK_CONDITION) &&
SCST_SENSE_VALID(cmd->sense)) {
struct tape_params *params;
TRACE_DBG("%s", "Extended sense");
TRACE_DBG("Extended sense %x", cmd->sense[0] & 0x7F);
if ((cmd->sense[0] & 0x7F) != 0x70) {
PRINT_ERROR("Sense format 0x%x is not supported",
cmd->sense[0] & 0x7F);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
if (opcode == READ_6 && !(cmd->cdb[1] & SILI_BIT) &&
(cmd->sense[2] & 0xe0)) {
/* EOF, EOM, or ILI */
@@ -374,6 +384,7 @@ static int tape_done(struct scst_cmd *cmd)
}
}
out:
TRACE_DBG("cmd->is_send_status=%x, cmd->resp_data_len=%d, "
"res=%d", cmd->is_send_status, cmd->resp_data_len, res);

View File

@@ -186,6 +186,7 @@ static int dev_user_register_dev(struct file *file,
const struct scst_user_dev_desc *dev_desc);
static int dev_user_unregister_dev(struct file *file);
static int dev_user_flush_cache(struct file *file);
static int dev_user_capacity_changed(struct file *file);
static int __dev_user_set_opt(struct scst_user_dev *dev,
const struct scst_user_opt *opt);
static int dev_user_set_opt(struct file *file, const struct scst_user_opt *opt);
@@ -1862,6 +1863,11 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
res = dev_user_get_opt(file, (void __user *)arg);
break;
case SCST_USER_DEVICE_CAPACITY_CHANGED:
TRACE_DBG("%s", "CAPACITY_CHANGED");
res = dev_user_capacity_changed(file);
break;
default:
PRINT_ERROR("Invalid ioctl cmd %x", cmd);
res = -EINVAL;
@@ -2765,6 +2771,33 @@ out:
return res;
}
static int dev_user_capacity_changed(struct file *file)
{
int res;
struct scst_user_dev *dev;
TRACE_ENTRY();
mutex_lock(&dev_priv_mutex);
dev = (struct scst_user_dev *)file->private_data;
res = dev_user_check_reg(dev);
if (res != 0) {
mutex_unlock(&dev_priv_mutex);
goto out;
}
down_read(&dev->dev_rwsem);
mutex_unlock(&dev_priv_mutex);
scst_capacity_data_changed(dev->sdev);
up_read(&dev->dev_rwsem);
out:
TRACE_EXIT_RES(res);
return res;
}
static int __dev_user_set_opt(struct scst_user_dev *dev,
const struct scst_user_opt *opt)
{

View File

@@ -93,6 +93,8 @@ static struct scst_proc_log vdisk_proc_local_trace_tbl[] =
#define VDISK_NAME "vdisk"
#define VCDROM_NAME "vcdrom"
#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
@@ -361,9 +363,9 @@ static struct scst_dev_type vcdrom_devtype = VCDROM_TYPE;
static struct scst_vdisk_thr nullio_thr_data;
static char *vdisk_proc_help_string =
"echo \"open|close NAME [FILE_NAME [BLOCK_SIZE] [WRITE_THROUGH "
"READ_ONLY O_DIRECT NULLIO NV_CACHE BLOCKIO]]\" >/proc/scsi_tgt/"
VDISK_NAME "/" VDISK_NAME "\n";
"echo \"open|close|resync_size NAME [FILE_NAME [BLOCK_SIZE] "
"[WRITE_THROUGH READ_ONLY O_DIRECT NULLIO NV_CACHE BLOCKIO]]\" "
">/proc/scsi_tgt/" VDISK_NAME "/" VDISK_NAME "\n";
static char *vcdrom_proc_help_string =
"echo \"open|change|close NAME [FILE_NAME]\" "
@@ -407,6 +409,76 @@ static struct file *vdisk_open(const struct scst_vdisk_dev *virt_dev)
return fd;
}
/**************************************************************
* Function: vdisk_get_file_size
*
* Argument:
*
* Returns : 0 on success and file size in *file_size,
* error code otherwise
*
* Description:
*************************************************************/
static int vdisk_get_check_file_size(const char *file_name, bool blockio,
loff_t *file_size)
{
struct inode *inode;
int res = 0;
struct file *fd;
TRACE_ENTRY();
*file_size = 0;
fd = filp_open(file_name, O_LARGEFILE | O_RDONLY, 0600);
if (IS_ERR(fd)) {
res = PTR_ERR(fd);
PRINT_ERROR("filp_open(%s) returned error %d", file_name, res);
goto out;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
if ((fd->f_op == NULL) ||
(fd->f_op->readv == NULL) ||
(fd->f_op->writev == NULL)) {
#else
if ((fd->f_op == NULL) ||
(fd->f_op->aio_read == NULL) ||
(fd->f_op->aio_write == NULL)) {
#endif
PRINT_ERROR("%s", "Wrong f_op or FS doesn't have required "
"capabilities");
res = -EINVAL;
goto out_close;
}
inode = fd->f_dentry->d_inode;
if (blockio && !S_ISBLK(inode->i_mode)) {
PRINT_ERROR("File %s is NOT a block device", file_name);
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:
TRACE_EXIT_RES(res);
return res;
}
/**************************************************************
* Function: vdisk_attach
*
@@ -420,7 +492,6 @@ static int vdisk_attach(struct scst_device *dev)
{
int res = 0;
loff_t err;
struct file *fd;
struct scst_vdisk_dev *virt_dev = NULL, *vv;
struct list_head *vd;
@@ -462,56 +533,12 @@ static int vdisk_attach(struct scst_device *dev)
if (!virt_dev->cdrom_empty) {
if (virt_dev->nullio)
err = 3LL*1024*1024*1024*1024/2;
err = VDISK_NULLIO_SIZE;
else {
struct inode *inode;
fd = vdisk_open(virt_dev);
if (IS_ERR(fd)) {
res = PTR_ERR(fd);
PRINT_ERROR("filp_open(%s) returned error %d",
virt_dev->file_name, res);
res = vdisk_get_check_file_size(virt_dev->file_name,
virt_dev->blockio, &err);
if (res != 0)
goto out;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
if ((fd->f_op == NULL) ||
(fd->f_op->readv == NULL) ||
(fd->f_op->writev == NULL)) {
#else
if ((fd->f_op == NULL) ||
(fd->f_op->aio_read == NULL) ||
(fd->f_op->aio_write == NULL)) {
#endif
PRINT_ERROR("%s", "Wrong f_op or FS doesn't "
"have required capabilities");
res = -EINVAL;
filp_close(fd, NULL);
goto out;
}
inode = fd->f_dentry->d_inode;
if (virt_dev->blockio && !S_ISBLK(inode->i_mode)) {
PRINT_ERROR("File %s is NOT a block device",
virt_dev->file_name);
res = -EINVAL;
filp_close(fd, NULL);
goto out;
}
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;
filp_close(fd, NULL);
goto out;
}
err = inode->i_size;
filp_close(fd, NULL);
}
virt_dev->file_size = err;
TRACE_DBG("size of file: %lld", (long long unsigned int)err);
@@ -2777,6 +2804,46 @@ static void vdisk_report_registering(const char *type,
return;
}
/* scst_vdisk_mutex supposed to be held */
static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev)
{
loff_t err;
int res = 0;
if (!virt_dev->nullio) {
res = vdisk_get_check_file_size(virt_dev->file_name,
virt_dev->blockio, &err);
if (res != 0)
goto out;
} else
err = VDISK_NULLIO_SIZE;
res = scst_suspend_activity(true);
if (res != 0)
goto out;
virt_dev->file_size = err;
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;
}
/*
* Called when a file in the /proc/VDISK_NAME/VDISK_NAME is written
*/
@@ -2810,6 +2877,9 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset,
action = 0;
} else if (!strncmp("open ", p, 5)) {
p += 5;
action = 1;
} else if (!strncmp("resync_size ", p, 12)) {
p += 12;
action = 2;
} else {
PRINT_ERROR("Unknown action \"%s\"", p);
@@ -2834,12 +2904,11 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset,
goto out_up;
}
if (action) {
if (action == 1) {
/* open */
virt_dev = NULL;
list_for_each_entry(vv, &vdisk_dev_list,
vdisk_dev_list_entry)
{
vdisk_dev_list_entry) {
if (strcmp(vv->name, name) == 0) {
virt_dev = vv;
break;
@@ -2992,11 +3061,10 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset,
"vdisk_dev_list", virt_dev->name,
virt_dev->file_name, virt_dev->virt_id,
virt_dev->block_size);
} else { /* close */
} else if (action == 0) { /* close */
virt_dev = NULL;
list_for_each_entry(vv, &vdisk_dev_list,
vdisk_dev_list_entry)
{
vdisk_dev_list_entry) {
if (strcmp(vv->name, name) == 0) {
virt_dev = vv;
break;
@@ -3016,6 +3084,24 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset,
kfree(virt_dev->file_name);
kfree(virt_dev);
} else { /* resync_size */
virt_dev = NULL;
list_for_each_entry(vv, &vdisk_dev_list,
vdisk_dev_list_entry) {
if (strcmp(vv->name, name) == 0) {
virt_dev = vv;
break;
}
}
if (virt_dev == NULL) {
PRINT_ERROR("Device %s not found", name);
res = -EINVAL;
goto out_up;
}
res = vdisk_resync_size(virt_dev);
if (res != 0)
goto out_up;
}
res = length;
@@ -3169,9 +3255,7 @@ out:
/* scst_vdisk_mutex supposed to be held */
static int vcdrom_change(char *p, char *name)
{
struct file *fd;
loff_t err;
mm_segment_t old_fs;
struct scst_vdisk_dev *virt_dev, *vv;
char *file_name, *fn, *old_fn;
int len;
@@ -3179,8 +3263,7 @@ static int vcdrom_change(char *p, char *name)
virt_dev = NULL;
list_for_each_entry(vv, &vcdrom_dev_list,
vdisk_dev_list_entry)
{
vdisk_dev_list_entry) {
if (strcmp(vv->name, name) == 0) {
virt_dev = vv;
break;
@@ -3225,45 +3308,17 @@ static int vcdrom_change(char *p, char *name)
strncpy(fn, file_name, len);
virt_dev->file_name = fn;
fd = vdisk_open(virt_dev);
if (IS_ERR(fd)) {
res = PTR_ERR(fd);
PRINT_ERROR("filp_open(%s) returned an error %d",
virt_dev->file_name, res);
res = vdisk_get_check_file_size(virt_dev->file_name,
virt_dev->blockio, &err);
if (res != 0)
goto out_free;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
if ((fd->f_op == NULL) || (fd->f_op->readv == NULL)) {
#else
if ((fd->f_op == NULL) || (fd->f_op->aio_read == NULL)) {
#endif
PRINT_ERROR("%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("llseek %s returned an error %d",
virt_dev->file_name, res);
goto out_free;
}
} else {
len = 0;
err = 0;
fn = NULL;
virt_dev->file_name = fn;
virt_dev->file_name = NULL;
}
if (virt_dev->nullio)
err = VDISK_NULLIO_SIZE;
res = scst_suspend_activity(true);
if (res != 0)

View File

@@ -37,8 +37,10 @@
static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev);
static void scst_check_internal_sense(struct scst_device *dev, int result,
uint8_t *sense, int sense_len);
static void __scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
const uint8_t *sense, int sense_len, int flags);
static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev,
const uint8_t *sense, int sense_len, int head);
const uint8_t *sense, int sense_len, int flags);
static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev);
static void scst_release_space(struct scst_cmd *cmd);
static void scst_sess_free_tgt_devs(struct scst_session *sess);
@@ -61,7 +63,8 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic)
TRACE_ENTRY();
sBUG_ON(cmd->sense != NULL);
if (cmd->sense != NULL)
goto memzero;
cmd->sense = mempool_alloc(scst_sense_mempool, gfp_mask);
if (cmd->sense == NULL) {
@@ -71,6 +74,7 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic)
goto out;
}
memzero:
memset(cmd->sense, 0, SCST_SENSE_BUFFERSIZE);
out:
@@ -146,20 +150,55 @@ out:
}
EXPORT_SYMBOL(scst_set_cmd_error);
void scst_set_sense(uint8_t *buffer, int len, int key,
int asc, int ascq)
void scst_set_sense(uint8_t *buffer, int len, int key, int asc, int ascq)
{
sBUG_ON(len < SCST_STANDARD_SENSE_LEN);
memset(buffer, 0, len);
buffer[0] = 0x70; /* Error Code */
buffer[2] = key; /* Sense Key */
buffer[7] = 0x0a; /* Additional Sense Length */
buffer[12] = asc; /* ASC */
buffer[13] = ascq; /* ASCQ */
TRACE_BUFFER("Sense set", buffer, len);
return;
}
EXPORT_SYMBOL(scst_set_sense);
bool scst_analyze_sense(const uint8_t *sense, int len, unsigned int valid_mask,
int key, int asc, int ascq)
{
bool res = false;
if (len < 14)
goto out;
/* Error Code */
if (sense[0] != 0x70)
goto out;
/* Sense Key */
if ((valid_mask & SCST_SENSE_KEY_VALID) && (sense[2] != key))
goto out;
/* ASC */
if ((valid_mask & SCST_SENSE_ASC_VALID) && (sense[12] != asc))
goto out;
/* ASCQ */
if ((valid_mask & SCST_SENSE_ASCQ_VALID) && (sense[13] != ascq))
goto out;
res = true;
out:
TRACE_EXIT_RES((int)res);
return res;
}
EXPORT_SYMBOL(scst_analyze_sense);
static void scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense,
unsigned int len)
{
@@ -207,7 +246,7 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
asc, ascq);
/* Protect sess_tgt_dev_list_hash */
mutex_lock(&scst_mutex);
mutex_lock(&scst_mutex);
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
struct list_head *sess_tgt_dev_list_head =
@@ -219,15 +258,14 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
spin_lock_bh(&tgt_dev->tgt_dev_lock);
if (!list_empty(&tgt_dev->UA_list)) {
struct scst_tgt_dev_UA *ua;
uint8_t *sense;
ua = list_entry(tgt_dev->UA_list.next,
typeof(*ua), UA_list_entry);
sense = ua->UA_sense_buffer;
if ((sense[2] == UNIT_ATTENTION) &&
(sense[12] == 0x29) &&
(sense[13] == 0)) {
scst_set_sense(sense,
if (scst_analyze_sense(ua->UA_sense_buffer,
sizeof(ua->UA_sense_buffer),
SCST_SENSE_ALL_VALID,
SCST_LOAD_SENSE(scst_sense_reset_UA))) {
scst_set_sense(ua->UA_sense_buffer,
sizeof(ua->UA_sense_buffer),
key, asc, ascq);
} else
@@ -240,13 +278,307 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
}
}
mutex_unlock(&scst_mutex);
mutex_unlock(&scst_mutex);
TRACE_EXIT();
return;
}
EXPORT_SYMBOL(scst_set_initial_UA);
static struct scst_aen *scst_alloc_aen(struct scst_tgt_dev *tgt_dev)
{
struct scst_aen *aen;
TRACE_ENTRY();
aen = mempool_alloc(scst_aen_mempool, GFP_KERNEL);
if (aen == NULL) {
PRINT_ERROR("AEN memory allocation failed. Corresponding "
"event notification will not be performed (initiator "
"%s)", tgt_dev->sess->initiator_name);
goto out;
}
memset(aen, 0, sizeof(*aen));
aen->sess = tgt_dev->sess;
scst_sess_get(aen->sess);
aen->lun = scst_pack_lun(tgt_dev->lun);
out:
TRACE_EXIT_HRES((unsigned long)aen);
return aen;
};
static void scst_free_aen(struct scst_aen *aen)
{
TRACE_ENTRY();
scst_sess_put(aen->sess);
mempool_free(aen, scst_aen_mempool);
TRACE_EXIT();
return;
};
/* No locks */
void scst_capacity_data_changed(struct scst_device *dev)
{
struct scst_tgt_dev *tgt_dev;
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
TRACE_ENTRY();
if (dev->type != TYPE_DISK) {
TRACE_MGMT_DBG("Device type %d isn't for CAPACITY DATA "
"CHANGED UA", dev->type);
goto out;
}
TRACE_MGMT_DBG("CAPACITY DATA CHANGED (dev %p)", dev);
scst_set_sense(sense_buffer, sizeof(sense_buffer),
SCST_LOAD_SENSE(scst_sense_capacity_data_changed));
mutex_lock(&scst_mutex);
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt;
if (tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
aen = scst_alloc_aen(tgt_dev);
if (aen == NULL)
goto queue_ua;
aen->event_fn = SCST_AEN_SCSI;
aen->aen_sense_len = SCST_STANDARD_SENSE_LEN;
scst_set_sense(aen->aen_sense, aen->aen_sense_len,
SCST_LOAD_SENSE(scst_sense_capacity_data_changed));
TRACE_DBG("Calling target's %s report_aen(%p)",
tgtt->name, aen);
rc = tgtt->report_aen(aen);
TRACE_DBG("Target's %s report_aen(%p) returned %d",
tgtt->name, aen, rc);
if (rc == SCST_AEN_RES_SUCCESS)
continue;
scst_free_aen(aen);
}
queue_ua:
TRACE_MGMT_DBG("Queuing CAPACITY DATA CHANGED UA (tgt_dev %p)",
tgt_dev);
scst_check_set_UA(tgt_dev, sense_buffer,
sizeof(sense_buffer), 0);
}
mutex_unlock(&scst_mutex);
out:
TRACE_EXIT();
return;
}
EXPORT_SYMBOL(scst_capacity_data_changed);
static inline bool scst_is_report_luns_changed_type(int type)
{
switch (type) {
case TYPE_DISK:
case TYPE_TAPE:
case TYPE_PRINTER:
case TYPE_PROCESSOR:
case TYPE_WORM:
case TYPE_ROM:
case TYPE_SCANNER:
case TYPE_MOD:
case TYPE_MEDIUM_CHANGER:
case TYPE_RAID:
case TYPE_ENCLOSURE:
return true;
default:
return false;
}
}
/* scst_mutex supposed to be held */
void scst_queue_report_luns_changed_UA(struct scst_session *sess, int flags)
{
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
struct list_head *shead;
struct scst_tgt_dev *tgt_dev;
int i;
TRACE_ENTRY();
scst_set_sense(sense_buffer, sizeof(sense_buffer),
SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed));
TRACE_MGMT_DBG("Queuing REPORTED LUNS DATA CHANGED UA "
"(sess %p)", sess);
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
shead = &sess->sess_tgt_dev_list_hash[i];
list_for_each_entry(tgt_dev, shead,
sess_tgt_dev_list_entry) {
spin_lock_bh(&tgt_dev->tgt_dev_lock);
}
}
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
shead = &sess->sess_tgt_dev_list_hash[i];
list_for_each_entry(tgt_dev, shead,
sess_tgt_dev_list_entry) {
if (!scst_is_report_luns_changed_type(
tgt_dev->dev->type))
continue;
__scst_check_set_UA(tgt_dev, sense_buffer,
sizeof(sense_buffer),
flags | SCST_SET_UA_FLAG_GLOBAL);
}
}
for (i = TGT_DEV_HASH_SIZE-1; i >= 0; i--) {
shead = &sess->sess_tgt_dev_list_hash[i];
list_for_each_entry_reverse(tgt_dev,
shead, sess_tgt_dev_list_entry) {
spin_unlock_bh(&tgt_dev->tgt_dev_lock);
}
}
TRACE_EXIT();
return;
}
/* The activity supposed to be suspended and scst_mutex held */
void scst_report_luns_changed(struct scst_acg *acg)
{
struct scst_session *sess;
TRACE_ENTRY();
TRACE_MGMT_DBG("REPORTED LUNS DATA CHANGED (acg %s)", acg->acg_name);
list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
int i;
struct list_head *shead;
struct scst_tgt_dev *tgt_dev;
struct scst_tgt_template *tgtt = sess->tgt->tgtt;
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
shead = &sess->sess_tgt_dev_list_hash[i];
list_for_each_entry(tgt_dev, shead,
sess_tgt_dev_list_entry) {
if (scst_is_report_luns_changed_type(
tgt_dev->dev->type))
goto found;
}
}
TRACE_MGMT_DBG("Not found a device capable REPORTED "
"LUNS DATA CHANGED UA (sess %p)", sess);
continue;
found:
if (tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
aen = scst_alloc_aen(tgt_dev);
if (aen == NULL)
goto queue_ua;
aen->event_fn = SCST_AEN_SCSI;
aen->aen_sense_len = SCST_STANDARD_SENSE_LEN;
scst_set_sense(aen->aen_sense, aen->aen_sense_len,
SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed));
TRACE_DBG("Calling target's %s report_aen(%p)",
tgtt->name, aen);
rc = tgtt->report_aen(aen);
TRACE_DBG("Target's %s report_aen(%p) returned %d",
tgtt->name, aen, rc);
if (rc == SCST_AEN_RES_SUCCESS)
continue;
scst_free_aen(aen);
}
queue_ua:
scst_queue_report_luns_changed_UA(sess, 0);
}
TRACE_EXIT();
return;
}
void scst_aen_done(struct scst_aen *aen)
{
TRACE_ENTRY();
TRACE_MGMT_DBG("AEN %p (fn %d) done (initiator %s)", aen,
aen->event_fn, aen->sess->initiator_name);
if (aen->delivery_status == SCST_AEN_RES_SUCCESS)
goto out_free;
if (aen->event_fn != SCST_AEN_SCSI)
goto out_free;
TRACE_MGMT_DBG("Delivery of SCSI AEN failed (initiator %s)",
aen->sess->initiator_name);
if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len,
SCST_SENSE_ALL_VALID,
SCST_LOAD_SENSE(
scst_sense_reported_luns_data_changed))) {
mutex_lock(&scst_mutex);
scst_queue_report_luns_changed_UA(aen->sess,
SCST_SET_UA_FLAG_AT_HEAD);
mutex_unlock(&scst_mutex);
} else if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len,
SCST_SENSE_ALL_VALID,
SCST_LOAD_SENSE(scst_sense_capacity_data_changed))) {
/* tgt_dev might get dead, so we need to reseek it */
struct list_head *shead;
struct scst_tgt_dev *tgt_dev;
uint64_t lun;
lun = scst_unpack_lun((uint8_t *)&aen->lun, sizeof(aen->lun));
mutex_lock(&scst_mutex);
shead = &aen->sess->sess_tgt_dev_list_hash[HASH_VAL(lun)];
list_for_each_entry(tgt_dev, shead,
sess_tgt_dev_list_entry) {
if (tgt_dev->lun == lun) {
TRACE_MGMT_DBG("Queuing CAPACITY DATA CHANGED "
"UA (tgt_dev %p)", tgt_dev);
scst_check_set_UA(tgt_dev, aen->aen_sense,
aen->aen_sense_len,
SCST_SET_UA_FLAG_AT_HEAD);
break;
}
}
mutex_unlock(&scst_mutex);
} else
PRINT_ERROR("%s", "Unknown SCSI AEN");
out_free:
scst_free_aen(aen);
TRACE_EXIT();
return;
}
EXPORT_SYMBOL(scst_aen_done);
int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd)
{
int res;
@@ -274,12 +606,15 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd)
res = SCST_CMD_STATE_XMIT_RESP;
break;
case SCST_CMD_STATE_PREPROCESS_DONE:
case SCST_CMD_STATE_PREPARE_SPACE:
case SCST_CMD_STATE_RDY_TO_XFER:
case SCST_CMD_STATE_DATA_WAIT:
case SCST_CMD_STATE_TGT_PRE_EXEC:
case SCST_CMD_STATE_SEND_FOR_EXEC:
case SCST_CMD_STATE_LOCAL_EXEC:
case SCST_CMD_STATE_REAL_EXEC:
case SCST_CMD_STATE_REAL_EXECUTING:
res = SCST_CMD_STATE_PRE_DEV_DONE;
break;
@@ -559,6 +894,7 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess,
struct list_head *sess_tgt_dev_list_head;
struct scst_tgt_template *vtt = sess->tgt->tgtt;
int rc, i;
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
TRACE_ENTRY();
@@ -652,11 +988,9 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess,
&tgt_dev->tgt_dev_flags);
}
spin_lock_bh(&scst_temp_UA_lock);
scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA),
scst_set_sense(sense_buffer, sizeof(sense_buffer),
SCST_LOAD_SENSE(scst_sense_reset_UA));
scst_alloc_set_UA(tgt_dev, scst_temp_UA, sizeof(scst_temp_UA), 0);
spin_unlock_bh(&scst_temp_UA_lock);
scst_alloc_set_UA(tgt_dev, sense_buffer, sizeof(sense_buffer), 0);
tm_dbg_init_tgt_dev(tgt_dev, acg_dev);
@@ -726,11 +1060,11 @@ void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA)
spin_unlock_bh(&tgt_dev->tgt_dev_lock);
if (queue_UA) {
spin_lock_bh(&scst_temp_UA_lock);
scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA),
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
scst_set_sense(sense_buffer, sizeof(sense_buffer),
SCST_LOAD_SENSE(scst_sense_nexus_loss_UA));
scst_check_set_UA(tgt_dev, scst_temp_UA, sizeof(scst_temp_UA), 0);
spin_unlock_bh(&scst_temp_UA_lock);
scst_check_set_UA(tgt_dev, sense_buffer,
sizeof(sense_buffer), 0);
}
TRACE_EXIT();
@@ -879,24 +1213,24 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
&tmp_tgt_dev_list);
}
out:
if (res == 0) {
if (dev->virt_name != NULL) {
PRINT_INFO("Added device %s to group %s (LUN %lld, "
"rd_only %d)", dev->virt_name, acg->acg_name,
(long long unsigned int)lun,
read_only);
} else {
PRINT_INFO("Added device %d:%d:%d:%d to group %s (LUN "
"%lld, rd_only %d)",
dev->scsi_dev->host->host_no,
dev->scsi_dev->channel, dev->scsi_dev->id,
dev->scsi_dev->lun, acg->acg_name,
(long long unsigned int)lun,
read_only);
}
scst_report_luns_changed(acg);
if (dev->virt_name != NULL) {
PRINT_INFO("Added device %s to group %s (LUN %lld, "
"rd_only %d)", dev->virt_name, acg->acg_name,
(long long unsigned int)lun,
read_only);
} else {
PRINT_INFO("Added device %d:%d:%d:%d to group %s (LUN "
"%lld, rd_only %d)",
dev->scsi_dev->host->host_no,
dev->scsi_dev->channel, dev->scsi_dev->id,
dev->scsi_dev->lun, acg->acg_name,
(long long unsigned int)lun,
read_only);
}
out:
TRACE_EXIT_RES(res);
return res;
@@ -938,19 +1272,19 @@ int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev)
}
scst_free_acg_dev(acg_dev);
out:
if (res == 0) {
if (dev->virt_name != NULL) {
PRINT_INFO("Removed device %s from group %s",
dev->virt_name, acg->acg_name);
} else {
PRINT_INFO("Removed device %d:%d:%d:%d from group %s",
dev->scsi_dev->host->host_no,
dev->scsi_dev->channel, dev->scsi_dev->id,
dev->scsi_dev->lun, acg->acg_name);
}
scst_report_luns_changed(acg);
if (dev->virt_name != NULL) {
PRINT_INFO("Removed device %s from group %s",
dev->virt_name, acg->acg_name);
} else {
PRINT_INFO("Removed device %d:%d:%d:%d from group %s",
dev->scsi_dev->host->host_no,
dev->scsi_dev->channel, dev->scsi_dev->id,
dev->scsi_dev->lun, acg->acg_name);
}
out:
TRACE_EXIT_RES(res);
return res;
}
@@ -1078,9 +1412,8 @@ out:
int scst_prepare_request_sense(struct scst_cmd *orig_cmd)
{
int res = 0;
#define sbuf_size 252
static const uint8_t request_sense[6] =
{ REQUEST_SENSE, 0, 0, 0, sbuf_size, 0 };
{ REQUEST_SENSE, 0, 0, 0, SCST_SENSE_BUFFERSIZE, 0 };
struct scst_cmd *rs_cmd;
TRACE_ENTRY();
@@ -1092,7 +1425,8 @@ int scst_prepare_request_sense(struct scst_cmd *orig_cmd)
orig_cmd->sense = NULL;
}
rs_cmd = scst_create_prepare_internal_cmd(orig_cmd, sbuf_size);
rs_cmd = scst_create_prepare_internal_cmd(orig_cmd,
SCST_SENSE_BUFFERSIZE);
if (rs_cmd == NULL)
goto out_error;
@@ -1100,7 +1434,7 @@ int scst_prepare_request_sense(struct scst_cmd *orig_cmd)
rs_cmd->cdb_len = sizeof(request_sense);
rs_cmd->data_direction = SCST_DATA_READ;
rs_cmd->expected_data_direction = rs_cmd->data_direction;
rs_cmd->expected_transfer_len = sbuf_size;
rs_cmd->expected_transfer_len = SCST_SENSE_BUFFERSIZE;
rs_cmd->expected_values_set = 1;
TRACE(TRACE_MGMT_MINOR, "Adding REQUEST SENSE cmd %p to head of active "
@@ -1117,7 +1451,6 @@ out:
out_error:
res = -1;
goto out;
#undef sbuf_size
}
static void scst_complete_request_sense(struct scst_cmd *req_cmd)
@@ -1246,7 +1579,7 @@ static void scst_send_release(struct scst_device *dev)
{
struct scsi_device *scsi_dev;
unsigned char cdb[6];
unsigned char *sense;
uint8_t sense[SCSI_SENSE_BUFFERSIZE];
int rc, i;
TRACE_ENTRY();
@@ -1254,9 +1587,6 @@ static void scst_send_release(struct scst_device *dev)
if (dev->scsi_dev == NULL)
goto out;
/* We can't afford missing RELEASE due to memory shortage */
sense = kmalloc(SCST_SENSE_BUFFERSIZE, GFP_KERNEL|__GFP_NOFAIL);
scsi_dev = dev->scsi_dev;
for (i = 0; i < 5; i++) {
@@ -1265,7 +1595,7 @@ static void scst_send_release(struct scst_device *dev)
cdb[1] = (scsi_dev->scsi_level <= SCSI_2) ?
((scsi_dev->lun << 5) & 0xe0) : 0;
memset(sense, 0, SCST_SENSE_BUFFERSIZE);
memset(sense, 0, sizeof(sense));
TRACE(TRACE_DEBUG | TRACE_SCSI, "%s", "Sending RELEASE req to "
"SCSI mid-level");
@@ -1277,15 +1607,12 @@ static void scst_send_release(struct scst_device *dev)
break;
} else {
PRINT_ERROR("RELEASE failed: %d", rc);
PRINT_BUFFER("RELEASE sense", sense,
SCST_SENSE_BUFFERSIZE);
scst_check_internal_sense(dev, rc,
sense, SCST_SENSE_BUFFERSIZE);
PRINT_BUFFER("RELEASE sense", sense, sizeof(sense));
scst_check_internal_sense(dev, rc, sense,
sizeof(sense));
}
}
kfree(sense);
out:
TRACE_EXIT();
return;
@@ -2117,6 +2444,19 @@ out:
}
EXPORT_SYMBOL(scst_get_cdb_info);
/* Packs SCST LUN back to SCSI form using peripheral device addressing method */
uint64_t scst_pack_lun(const uint64_t lun)
{
uint64_t res;
uint16_t *p = (uint16_t *)&res;
res = lun;
*p = cpu_to_be16(*p);
TRACE_EXIT_HRES((unsigned long)res);
return res;
}
/*
* Routine to extract a lun number from an 8-byte LUN structure
* in network byte order (BE).
@@ -2670,7 +3010,7 @@ int scst_obtain_device_parameters(struct scst_device *dev)
int res = 0, i;
uint8_t cmd[16];
uint8_t buffer[4+0x0A];
uint8_t sense_buffer[SCST_SENSE_BUFFERSIZE];
uint8_t sense_buffer[SCSI_SENSE_BUFFERSIZE];
TRACE_ENTRY();
@@ -2741,7 +3081,10 @@ int scst_obtain_device_parameters(struct scst_device *dev)
if (
#endif
SCST_SENSE_VALID(sense_buffer)) {
if (sense_buffer[2] == ILLEGAL_REQUEST) {
if (scst_analyze_sense(sense_buffer,
sizeof(sense_buffer),
SCST_SENSE_KEY_VALID,
ILLEGAL_REQUEST, 0, 0)) {
TRACE(TRACE_SCSI|TRACE_MGMT_MINOR,
"Device %d:%d:%d:%d doesn't"
" support control mode page,"
@@ -2759,7 +3102,10 @@ int scst_obtain_device_parameters(struct scst_device *dev)
dev->has_own_order_mgmt);
res = 0;
goto out;
} else if (sense_buffer[2] == NOT_READY) {
} else if (scst_analyze_sense(sense_buffer,
sizeof(sense_buffer),
SCST_SENSE_KEY_VALID,
NOT_READY, 0, 0)) {
TRACE(TRACE_SCSI,
"Device %d:%d:%d:%d not ready",
dev->scsi_dev->host->host_no,
@@ -2863,13 +3209,11 @@ void scst_process_reset(struct scst_device *dev,
}
if (setUA) {
/* BH already off */
spin_lock(&scst_temp_UA_lock);
scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA),
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
scst_set_sense(sense_buffer, sizeof(sense_buffer),
SCST_LOAD_SENSE(scst_sense_reset_UA));
scst_dev_check_set_local_UA(dev, exclude_cmd, scst_temp_UA,
sizeof(scst_temp_UA));
spin_unlock(&scst_temp_UA_lock);
scst_dev_check_set_local_UA(dev, exclude_cmd, sense_buffer,
sizeof(sense_buffer));
}
TRACE_EXIT();
@@ -2878,8 +3222,10 @@ void scst_process_reset(struct scst_device *dev,
int scst_set_pending_UA(struct scst_cmd *cmd)
{
int res = 0;
int res = 0, i;
struct scst_tgt_dev_UA *UA_entry;
bool first = true, global_unlock = false;
struct scst_session *sess = cmd->sess;
TRACE_ENTRY();
@@ -2887,6 +3233,7 @@ int scst_set_pending_UA(struct scst_cmd *cmd)
spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock);
again:
/* UA list could be cleared behind us, so retest */
if (list_empty(&cmd->tgt_dev->UA_list)) {
TRACE_DBG("%s",
@@ -2901,6 +3248,28 @@ int scst_set_pending_UA(struct scst_cmd *cmd)
TRACE_DBG("next %p UA_entry %p",
cmd->tgt_dev->UA_list.next, UA_entry);
if (UA_entry->global_UA && first) {
TRACE_MGMT_DBG("Global UA %p detected", UA_entry);
spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock);
mutex_lock(&scst_mutex);
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
struct list_head *sess_tgt_dev_list_head =
&sess->sess_tgt_dev_list_hash[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
sess_tgt_dev_list_entry) {
spin_lock_bh(&tgt_dev->tgt_dev_lock);
}
}
first = false;
global_unlock = true;
goto again;
}
scst_set_cmd_error_sense(cmd, UA_entry->UA_sense_buffer,
sizeof(UA_entry->UA_sense_buffer));
@@ -2908,6 +3277,33 @@ int scst_set_pending_UA(struct scst_cmd *cmd)
list_del(&UA_entry->UA_list_entry);
if (UA_entry->global_UA) {
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
struct list_head *sess_tgt_dev_list_head =
&sess->sess_tgt_dev_list_hash[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
sess_tgt_dev_list_entry) {
struct scst_tgt_dev_UA *ua;
list_for_each_entry(ua, &tgt_dev->UA_list,
UA_list_entry) {
if (ua->global_UA &&
memcmp(ua->UA_sense_buffer,
UA_entry->UA_sense_buffer,
sizeof(ua->UA_sense_buffer)) == 0) {
TRACE_MGMT_DBG("Freeing not "
"needed global UA %p",
ua);
list_del(&ua->UA_list_entry);
mempool_free(ua, scst_ua_mempool);
break;
}
}
}
}
}
mempool_free(UA_entry, scst_ua_mempool);
if (list_empty(&cmd->tgt_dev->UA_list)) {
@@ -2915,20 +3311,32 @@ int scst_set_pending_UA(struct scst_cmd *cmd)
&cmd->tgt_dev->tgt_dev_flags);
}
out_unlock:
if (global_unlock) {
for (i = TGT_DEV_HASH_SIZE-1; i >= 0; i--) {
struct list_head *sess_tgt_dev_list_head =
&sess->sess_tgt_dev_list_hash[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_reverse(tgt_dev, sess_tgt_dev_list_head,
sess_tgt_dev_list_entry) {
spin_unlock_bh(&tgt_dev->tgt_dev_lock);
}
}
mutex_unlock(&scst_mutex);
spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock);
}
spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock);
out:
TRACE_EXIT_RES(res);
return res;
out_unlock:
spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock);
goto out;
}
/* Called under tgt_dev_lock and BH off */
static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev,
const uint8_t *sense, int sense_len, int head)
const uint8_t *sense, int sense_len, int flags)
{
struct scst_tgt_dev_UA *UA_entry = NULL;
@@ -2944,6 +3352,10 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev,
}
memset(UA_entry, 0, sizeof(*UA_entry));
UA_entry->global_UA = (flags & SCST_SET_UA_FLAG_GLOBAL) != 0;
if (UA_entry->global_UA)
TRACE_MGMT_DBG("Queuing global UA %p", UA_entry);
if (sense_len > (int)sizeof(UA_entry->UA_sense_buffer))
sense_len = sizeof(UA_entry->UA_sense_buffer);
memcpy(UA_entry->UA_sense_buffer, sense, sense_len);
@@ -2952,7 +3364,7 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev,
TRACE_MGMT_DBG("Adding new UA to tgt_dev %p", tgt_dev);
if (head)
if (flags & SCST_SET_UA_FLAG_AT_HEAD)
list_add(&UA_entry->UA_list_entry, &tgt_dev->UA_list);
else
list_add_tail(&UA_entry->UA_list_entry, &tgt_dev->UA_list);
@@ -2962,20 +3374,19 @@ out:
return;
}
void scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
const uint8_t *sense, int sense_len, int head)
/* tgt_dev_lock supposed to be held and BH off */
static void __scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
const uint8_t *sense, int sense_len, int flags)
{
int skip_UA = 0;
struct scst_tgt_dev_UA *UA_entry_tmp;
int len = min((int)sizeof(UA_entry_tmp->UA_sense_buffer), sense_len);
TRACE_ENTRY();
spin_lock_bh(&tgt_dev->tgt_dev_lock);
list_for_each_entry(UA_entry_tmp, &tgt_dev->UA_list,
UA_list_entry) {
if (memcmp(sense, UA_entry_tmp->UA_sense_buffer,
sense_len) == 0) {
if (memcmp(sense, UA_entry_tmp->UA_sense_buffer, len) == 0) {
TRACE_MGMT_DBG("%s", "UA already exists");
skip_UA = 1;
break;
@@ -2983,8 +3394,19 @@ void scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
}
if (skip_UA == 0)
scst_alloc_set_UA(tgt_dev, sense, sense_len, head);
scst_alloc_set_UA(tgt_dev, sense, len, flags);
TRACE_EXIT();
return;
}
void scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
const uint8_t *sense, int sense_len, int flags)
{
TRACE_ENTRY();
spin_lock_bh(&tgt_dev->tgt_dev_lock);
__scst_check_set_UA(tgt_dev, sense, sense_len, flags);
spin_unlock_bh(&tgt_dev->tgt_dev_lock);
TRACE_EXIT();
@@ -3021,7 +3443,8 @@ void __scst_dev_check_set_UA(struct scst_device *dev,
TRACE(TRACE_MGMT_MINOR, "Processing UA dev %p", dev);
/* Check for reset UA */
if (sense[12] == SCST_SENSE_ASC_UA_RESET)
if (scst_analyze_sense(sense, sense_len, SCST_SENSE_ASC_VALID,
0, SCST_SENSE_ASC_UA_RESET, 0))
scst_process_reset(dev,
(exclude != NULL) ? exclude->sess : NULL,
exclude, NULL, false);
@@ -3044,7 +3467,7 @@ static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev)
TRACE_MGMT_DBG("Clearing UA for tgt_dev lun %lld",
(long long unsigned int)tgt_dev->lun);
list_del(&UA_entry->UA_list_entry);
kfree(UA_entry);
mempool_free(UA_entry, scst_ua_mempool);
}
INIT_LIST_HEAD(&tgt_dev->UA_list);
clear_bit(SCST_TGT_DEV_UA_PENDING, &tgt_dev->tgt_dev_flags);

View File

@@ -85,6 +85,8 @@ static struct kmem_cache *scst_ua_cachep;
mempool_t *scst_ua_mempool;
static struct kmem_cache *scst_sense_cachep;
mempool_t *scst_sense_mempool;
static struct kmem_cache *scst_aen_cachep;
mempool_t *scst_aen_mempool;
struct kmem_cache *scst_tgtd_cachep;
struct kmem_cache *scst_sess_cachep;
struct kmem_cache *scst_acgd_cachep;
@@ -133,13 +135,6 @@ static int suspend_count;
static int scst_virt_dev_last_id; /* protected by scst_mutex */
/*
* This buffer and lock are intended to avoid memory allocation, which
* could fail in improper places.
*/
spinlock_t scst_temp_UA_lock;
uint8_t scst_temp_UA[SCST_SENSE_BUFFERSIZE];
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
#if defined(CONFIG_BLOCK) && defined(SCST_ALLOC_IO_CONTEXT_EXPORTED)
static struct io_context *scst_ioc;
@@ -1765,8 +1760,6 @@ static int __init init_scst(void)
mutex_init(&scst_suspend_mutex);
INIT_LIST_HEAD(&scst_cmd_lists_list);
scst_virt_dev_last_id = 1;
spin_lock_init(&scst_temp_UA_lock);
spin_lock_init(&scst_main_cmd_lists.cmd_list_lock);
INIT_LIST_HEAD(&scst_main_cmd_lists.active_cmd_list);
init_waitqueue_head(&scst_main_cmd_lists.cmd_list_waitQ);
@@ -1807,7 +1800,8 @@ static int __init init_scst(void)
INIT_CACHEP(scst_sense_cachep, scst_sense,
out_destroy_ua_cache);
}
INIT_CACHEP(scst_cmd_cachep, scst_cmd, out_destroy_sense_cache);
INIT_CACHEP(scst_aen_cachep, scst_aen, out_destroy_sense_cache);
INIT_CACHEP(scst_cmd_cachep, scst_cmd, out_destroy_aen_cache);
INIT_CACHEP(scst_sess_cachep, scst_session, out_destroy_cmd_cache);
INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev, out_destroy_sess_cache);
INIT_CACHEP(scst_acgd_cachep, scst_acg_dev, out_destroy_tgt_cache);
@@ -1843,6 +1837,13 @@ static int __init init_scst(void)
goto out_destroy_ua_mempool;
}
scst_aen_mempool = mempool_create(100, mempool_alloc_slab,
mempool_free_slab, scst_aen_cachep);
if (scst_aen_mempool == NULL) {
res = -ENOMEM;
goto out_destroy_sense_mempool;
}
if (scst_max_cmd_mem == 0) {
struct sysinfo si;
si_meminfo(&si);
@@ -1870,7 +1871,7 @@ static int __init init_scst(void)
res = scst_sgv_pools_init(
((uint64_t)scst_max_cmd_mem << 10) >> (PAGE_SHIFT - 10), 0);
if (res != 0)
goto out_destroy_sense_mempool;
goto out_destroy_aen_mempool;
scst_default_acg = scst_alloc_add_acg(SCST_DEFAULT_ACG_NAME);
if (scst_default_acg == NULL) {
@@ -1925,6 +1926,9 @@ out_free_acg:
out_destroy_sgv_pool:
scst_sgv_pools_deinit();
out_destroy_aen_mempool:
mempool_destroy(scst_aen_mempool);
out_destroy_sense_mempool:
mempool_destroy(scst_sense_mempool);
@@ -1949,6 +1953,9 @@ out_destroy_sess_cache:
out_destroy_cmd_cache:
kmem_cache_destroy(scst_cmd_cachep);
out_destroy_aen_cache:
kmem_cache_destroy(scst_aen_cachep);
out_destroy_sense_cache:
kmem_cache_destroy(scst_sense_cachep);
@@ -1987,11 +1994,13 @@ static void __exit exit_scst(void)
mempool_destroy(scst_mgmt_stub_mempool);
mempool_destroy(scst_ua_mempool);
mempool_destroy(scst_sense_mempool);
mempool_destroy(scst_aen_mempool);
DEINIT_CACHEP(scst_mgmt_cachep);
DEINIT_CACHEP(scst_mgmt_stub_cachep);
DEINIT_CACHEP(scst_ua_cachep);
DEINIT_CACHEP(scst_sense_cachep);
DEINIT_CACHEP(scst_aen_cachep);
DEINIT_CACHEP(scst_cmd_cachep);
DEINIT_CACHEP(scst_sess_cachep);
DEINIT_CACHEP(scst_tgtd_cachep);

View File

@@ -126,6 +126,7 @@ extern mempool_t *scst_mgmt_mempool;
extern mempool_t *scst_mgmt_stub_mempool;
extern mempool_t *scst_ua_mempool;
extern mempool_t *scst_sense_mempool;
extern mempool_t *scst_aen_mempool;
extern struct kmem_cache *scst_cmd_cachep;
extern struct kmem_cache *scst_sess_cachep;
@@ -190,9 +191,6 @@ extern int scst_cmd_threads_count(void);
extern int __scst_add_cmd_threads(int num);
extern void __scst_del_cmd_threads(int num);
extern spinlock_t scst_temp_UA_lock;
extern uint8_t scst_temp_UA[SCST_SENSE_BUFFERSIZE];
extern struct scst_dev_type scst_null_devtype;
extern struct scst_cmd *__scst_check_deferred_commands(
@@ -326,6 +324,7 @@ enum scst_sg_copy_dir {
};
void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir);
uint64_t scst_pack_lun(const uint64_t lun);
uint64_t scst_unpack_lun(const uint8_t *lun, int len);
struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(gfp_t gfp_mask);
@@ -357,10 +356,15 @@ static inline void scst_dev_check_set_UA(struct scst_device *dev,
void scst_dev_check_set_local_UA(struct scst_device *dev,
struct scst_cmd *exclude, const uint8_t *sense, int sense_len);
#define SCST_SET_UA_FLAG_AT_HEAD 1
#define SCST_SET_UA_FLAG_GLOBAL 2
void scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
const uint8_t *sense, int sense_len, int head);
const uint8_t *sense, int sense_len, int flags);
int scst_set_pending_UA(struct scst_cmd *cmd);
void scst_report_luns_changed(struct scst_acg *acg);
void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
int other_ini, int call_dev_task_mgmt_fn);
void scst_process_reset(struct scst_device *dev,

View File

@@ -1321,13 +1321,12 @@ static void scst_cmd_done(void *data, char *sense, int result, int resid)
if (cmd == NULL)
goto out;
scst_do_cmd_done(cmd, result, sense, SCST_SENSE_BUFFERSIZE, resid);
scst_do_cmd_done(cmd, result, sense, SCSI_SENSE_BUFFERSIZE, resid);
cmd->state = SCST_CMD_STATE_PRE_DEV_DONE;
scst_proccess_redirect_cmd(cmd,
scst_optimize_post_exec_context(cmd, scst_estimate_context()),
0);
scst_optimize_post_exec_context(cmd, scst_estimate_context()), 0);
out:
TRACE_EXIT();
@@ -1490,6 +1489,37 @@ inc_dev_cnt:
out_compl:
cmd->completed = 1;
/* Clear left sense_reported_luns_data_changed UA, if any. */
mutex_lock(&scst_mutex); /* protect sess_tgt_dev_list_hash */
for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
struct list_head *sess_tgt_dev_list_head =
&cmd->sess->sess_tgt_dev_list_hash[i];
list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
sess_tgt_dev_list_entry) {
struct scst_tgt_dev_UA *ua;
spin_lock_bh(&tgt_dev->tgt_dev_lock);
list_for_each_entry(ua, &tgt_dev->UA_list,
UA_list_entry) {
if (scst_analyze_sense(ua->UA_sense_buffer,
sizeof(ua->UA_sense_buffer),
SCST_SENSE_ALL_VALID,
SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed))) {
TRACE_MGMT_DBG("Freeing not needed "
"REPORTED LUNS DATA CHANGED UA "
"%p", ua);
list_del(&ua->UA_list_entry);
mempool_free(ua, scst_ua_mempool);
break;
}
}
spin_unlock_bh(&tgt_dev->tgt_dev_lock);
}
}
mutex_unlock(&scst_mutex);
out_done:
/* Report the result */
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
@@ -2275,7 +2305,10 @@ static int scst_check_sense(struct scst_cmd *cmd)
/* Check Unit Attention Sense Key */
if (scst_is_ua_sense(cmd->sense)) {
if (cmd->sense[12] == SCST_SENSE_ASC_UA_RESET) {
if (scst_analyze_sense(cmd->sense,
SCST_SENSE_BUFFERSIZE,
SCST_SENSE_ASC_VALID,
0, SCST_SENSE_ASC_UA_RESET, 0)) {
if (cmd->double_ua_possible) {
TRACE(TRACE_MGMT_MINOR, "Double UA "
"detected for device %p", dev);
@@ -2501,10 +2534,11 @@ static int scst_pre_dev_done(struct scst_cmd *cmd)
(cmd->status == SAM_STAT_CHECK_CONDITION) &&
SCST_SENSE_VALID(cmd->sense) &&
scst_is_ua_sense(cmd->sense) &&
(cmd->sense[12] == 0x2a) && (cmd->sense[13] == 0x01)) {
TRACE(TRACE_SCSI,
"MODE PARAMETERS CHANGED UA (lun %lld)",
(long long unsigned int)cmd->lun);
scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE,
SCST_SENSE_ASCx_VALID,
0, 0x2a, 0x01)) {
TRACE(TRACE_SCSI, "MODE PARAMETERS CHANGED UA (lun "
"%lld)", (long long unsigned int)cmd->lun);
cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS;
goto out;
}
@@ -2529,6 +2563,8 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
(cmd->cdb[0] == MODE_SELECT_10) ||
(cmd->cdb[0] == LOG_SELECT))) {
struct scst_device *dev = cmd->dev;
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
if (atomic && (dev->scsi_dev != NULL)) {
TRACE_DBG("%s", "MODE/LOG SELECT: thread "
"context required");
@@ -2541,19 +2577,17 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
(long long unsigned int)cmd->lun);
spin_lock_bh(&dev->dev_lock);
spin_lock(&scst_temp_UA_lock);
if (cmd->cdb[0] == LOG_SELECT) {
scst_set_sense(scst_temp_UA,
sizeof(scst_temp_UA),
scst_set_sense(sense_buffer,
sizeof(sense_buffer),
UNIT_ATTENTION, 0x2a, 0x02);
} else {
scst_set_sense(scst_temp_UA,
sizeof(scst_temp_UA),
scst_set_sense(sense_buffer,
sizeof(sense_buffer),
UNIT_ATTENTION, 0x2a, 0x01);
}
scst_dev_check_set_local_UA(dev, cmd, scst_temp_UA,
sizeof(scst_temp_UA));
spin_unlock(&scst_temp_UA_lock);
scst_dev_check_set_local_UA(dev, cmd, sense_buffer,
sizeof(sense_buffer));
spin_unlock_bh(&dev->dev_lock);
if (dev->scsi_dev != NULL)
@@ -2562,11 +2596,20 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
} else if ((cmd->status == SAM_STAT_CHECK_CONDITION) &&
SCST_SENSE_VALID(cmd->sense) &&
scst_is_ua_sense(cmd->sense) &&
(((cmd->sense[12] == 0x2a) && (cmd->sense[13] == 0x01)) ||
(cmd->sense[12] == 0x29) /* reset */ ||
(cmd->sense[12] == 0x28) /* medium changed */ ||
/* mode parameters changed */
(scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE,
SCST_SENSE_ASCx_VALID,
0, 0x2a, 0x01) ||
scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE,
SCST_SENSE_ASC_VALID,
0, 0x29, 0) /* reset */ ||
scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE,
SCST_SENSE_ASC_VALID,
0, 0x28, 0) /* medium changed */ ||
/* cleared by another ini (just in case) */
(cmd->sense[12] == 0x2F))) {
scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE,
SCST_SENSE_ASC_VALID,
0, 0x2F, 0))) {
if (atomic) {
TRACE_DBG("Possible parameters changed UA %x: "
"thread context required", cmd->sense[12]);
@@ -2934,7 +2977,8 @@ static int scst_finish_cmd(struct scst_cmd *cmd)
TRACE_MGMT_DBG("Requeuing UA for delivery failed cmd "
"%p", cmd);
scst_check_set_UA(cmd->tgt_dev, cmd->sense,
SCST_SENSE_BUFFERSIZE, 1);
SCST_SENSE_BUFFERSIZE,
SCST_SET_UA_FLAG_AT_HEAD);
}
}
@@ -4192,14 +4236,15 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
mutex_unlock(&scst_mutex);
if (!dev->tas) {
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
scst_set_sense(sense_buffer, sizeof(sense_buffer),
SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA));
list_for_each_entry(tgt_dev, &UA_tgt_devs,
extra_tgt_dev_list_entry) {
spin_lock_bh(&scst_temp_UA_lock);
scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA),
SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA));
scst_check_set_UA(tgt_dev, scst_temp_UA,
sizeof(scst_temp_UA), 0);
spin_unlock_bh(&scst_temp_UA_lock);
extra_tgt_dev_list_entry) {
scst_check_set_UA(tgt_dev, sense_buffer,
sizeof(sense_buffer), 0);
}
}