scst: Make pr path configurable

Make the path of the file in which persistent reservation information
is stored configurable via sysfs.

Signed-off-by: Bart Van Assche <bvanassche@acm.org> with some improvements and fixes



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5510 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2014-05-09 03:51:10 +00:00
parent b7f80e0566
commit f3820d5bc4
8 changed files with 412 additions and 81 deletions

View File

@@ -1043,6 +1043,12 @@ Each vdisk_fileio's device has the following attributes in
- size_mb - contains size of this virtual device in MB. - size_mb - contains size of this virtual device in MB.
- pr_file_name - Full path of the file or block device in which to store
persistent reservation information. The default value for this attribute is
/var/lib/scst/pr/${device_name}. Writing a new value into this sysfs
attribute is only allowed if the device is not exported. Modifying this
sysfs attribute causes the persistent reservation state to be reloaded.
- t10_dev_id - contains and allows to set T10 vendor specific - t10_dev_id - contains and allows to set T10 vendor specific
identifier for Device Identification VPD page (0x83) of INQUIRY data. identifier for Device Identification VPD page (0x83) of INQUIRY data.
By default VDISK handler always generates t10_dev_id for every new By default VDISK handler always generates t10_dev_id for every new

View File

@@ -2533,6 +2533,9 @@ struct scst_device {
/* True if persist through power loss is activated. */ /* True if persist through power loss is activated. */
unsigned short pr_aptpl:1; unsigned short pr_aptpl:1;
/* Whether or not pr_file_name has been modified via sysfs. */
unsigned int pr_file_name_is_set:1;
/* Persistent reservation type */ /* Persistent reservation type */
uint8_t pr_type; uint8_t pr_type;
@@ -2562,7 +2565,10 @@ struct scst_device {
struct scst_order_data dev_order_data; struct scst_order_data dev_order_data;
/* Persist through power loss files */ /*
* Where to save persistent reservation information. Protected by
* dev_pr_mutex.
*/
char *pr_file_name; char *pr_file_name;
char *pr_file_name1; char *pr_file_name1;
@@ -4700,7 +4706,10 @@ struct scst_sysfs_work_item {
}; };
struct { struct {
struct scst_device *dev; struct scst_device *dev;
int new_threads_num; union {
int new_threads_num;
bool default_val;
};
enum scst_dev_type_threads_pool_type new_threads_pool_type; enum scst_dev_type_threads_pool_type new_threads_pool_type;
}; };
struct scst_session *sess; struct scst_session *sess;
@@ -4743,6 +4752,9 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
void (*done)(void *data, char *sense, int result, int resid)); void (*done)(void *data, char *sense, int result, int resid));
#endif #endif
int scst_get_file_mode(const char *path);
bool scst_parent_dir_exists(const char *path);
struct scst_data_descriptor { struct scst_data_descriptor {
uint64_t sdd_lba; uint64_t sdd_lba;
uint64_t sdd_blocks; uint64_t sdd_blocks;

View File

@@ -3759,6 +3759,17 @@ void scst_free_device(struct scst_device *dev)
return; return;
} }
bool scst_device_is_exported(struct scst_device *dev)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
WARN_ON_ONCE(!dev->dev_tgt_dev_list.next);
return !list_empty(&dev->dev_tgt_dev_list);
}
/** /**
* scst_init_mem_lim - initialize memory limits structure * scst_init_mem_lim - initialize memory limits structure
* *
@@ -10143,6 +10154,55 @@ int scst_read_file_transactional(const char *name, const char *name1,
} }
EXPORT_SYMBOL_GPL(scst_read_file_transactional); EXPORT_SYMBOL_GPL(scst_read_file_transactional);
/*
* Return the file mode if @path exists or an error code if opening @path via
* filp_open() in read-only mode failed.
*/
int scst_get_file_mode(const char *path)
{
struct file *file;
int res;
file = filp_open(path, O_RDONLY, 0400);
if (IS_ERR(file)) {
res = PTR_ERR(file);
goto out;
}
res = file->f_dentry->d_inode->i_mode;
filp_close(file, NULL);
out:
return res;
}
EXPORT_SYMBOL(scst_get_file_mode);
/*
* Return true if either @path does not contain a slash or if the directory
* specified in @path exists.
*/
bool scst_parent_dir_exists(const char *path)
{
const char *last_slash = strrchr(path, '/');
const char *dir;
int dir_mode;
bool res = true;
if (last_slash && last_slash > path) {
dir = kasprintf(GFP_KERNEL, "%.*s", (int)(last_slash - path),
path);
if (dir) {
dir_mode = scst_get_file_mode(dir);
kfree(dir);
res = dir_mode >= 0 && S_ISDIR(dir_mode);
} else {
res = false;
}
}
return res;
}
EXPORT_SYMBOL(scst_parent_dir_exists);
static void __init scst_scsi_op_list_init(void) static void __init scst_scsi_op_list_init(void)
{ {
int i; int i;

View File

@@ -1198,13 +1198,13 @@ out:
#ifndef CONFIG_SCST_PROC #ifndef CONFIG_SCST_PROC
out_del_unlocked: out_del_unlocked:
mutex_lock(&scst_mutex); mutex_lock(&scst_mutex);
list_del(&dev->dev_list_entry); list_del_init(&dev->dev_list_entry);
mutex_unlock(&scst_mutex); mutex_unlock(&scst_mutex);
scst_free_device(dev); scst_free_device(dev);
goto out; goto out;
#else #else
out_del_locked: out_del_locked:
list_del(&dev->dev_list_entry); list_del_init(&dev->dev_list_entry);
#endif #endif
out_free_dev: out_free_dev:
@@ -1266,7 +1266,7 @@ static void scst_unregister_device(struct scsi_device *scsidp)
dev->dev_unregistering = 1; dev->dev_unregistering = 1;
list_del(&dev->dev_list_entry); list_del_init(&dev->dev_list_entry);
scst_dg_dev_remove_by_dev(dev); scst_dg_dev_remove_by_dev(dev);
@@ -1418,6 +1418,11 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler,
scst_virt_dev_last_id = 1; scst_virt_dev_last_id = 1;
} }
res = scst_pr_set_file_name(dev, NULL, "%s/%s", SCST_PR_DIR,
dev->virt_name);
if (res != 0)
goto out_free_dev;
res = scst_pr_init_dev(dev); res = scst_pr_init_dev(dev);
if (res != 0) if (res != 0)
goto out_free_dev; goto out_free_dev;
@@ -1516,7 +1521,7 @@ void scst_unregister_virtual_device(int id)
dev->dev_unregistering = 1; dev->dev_unregistering = 1;
list_del(&dev->dev_list_entry); list_del_init(&dev->dev_list_entry);
scst_pr_clear_dev(dev); scst_pr_clear_dev(dev);

View File

@@ -45,6 +45,7 @@
#endif #endif
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <stdarg.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
#include <linux/mount.h> #include <linux/mount.h>
@@ -74,6 +75,19 @@
#define isblank(c) ((c) == ' ' || (c) == '\t') #define isblank(c) ((c) == ' ' || (c) == '\t')
#endif #endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && defined(CONFIG_LOCKDEP)
#define scst_assert_pr_mutex_held(dev) \
do { \
if (dev->dev_list_entry.next && \
!list_empty(&dev->dev_list_entry)) \
lockdep_assert_held(&dev->dev_pr_mutex); \
} while (0);
#else
static inline void scst_assert_pr_mutex_held(struct scst_device *dev)
{
}
#endif
static inline int tid_size(const uint8_t *tid) static inline int tid_size(const uint8_t *tid)
{ {
sBUG_ON(tid == NULL); sBUG_ON(tid == NULL);
@@ -174,6 +188,8 @@ out_error:
static inline void scst_pr_set_holder(struct scst_device *dev, static inline void scst_pr_set_holder(struct scst_device *dev,
struct scst_dev_registrant *holder, uint8_t scope, uint8_t type) struct scst_dev_registrant *holder, uint8_t scope, uint8_t type)
{ {
scst_assert_pr_mutex_held(dev);
dev->pr_is_set = 1; dev->pr_is_set = 1;
dev->pr_scope = scope; dev->pr_scope = scope;
dev->pr_type = type; dev->pr_type = type;
@@ -190,6 +206,8 @@ static bool scst_pr_is_holder(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
if (!dev->pr_is_set) if (!dev->pr_is_set)
goto out; goto out;
@@ -209,6 +227,8 @@ out:
/* Must be called under dev_pr_mutex */ /* Must be called under dev_pr_mutex */
void scst_pr_dump_prs(struct scst_device *dev, bool force) void scst_pr_dump_prs(struct scst_device *dev, bool force)
{ {
scst_assert_pr_mutex_held(dev);
if (!force) { if (!force) {
#if defined(CONFIG_SCST_DEBUG) #if defined(CONFIG_SCST_DEBUG)
if ((trace_flag & TRACE_PRES) == 0) if ((trace_flag & TRACE_PRES) == 0)
@@ -265,6 +285,8 @@ static void scst_pr_find_registrants_list_all(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
TRACE_PR("Finding all registered records for device '%s' " TRACE_PR("Finding all registered records for device '%s' "
"with exclude reg key %016llx", "with exclude reg key %016llx",
dev->virt_name, be64_to_cpu(exclude_reg->key)); dev->virt_name, be64_to_cpu(exclude_reg->key));
@@ -291,6 +313,8 @@ static void scst_pr_find_registrants_list_key(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
TRACE_PR("Finding registrants for device '%s' with key %016llx", TRACE_PR("Finding registrants for device '%s' with key %016llx",
dev->virt_name, be64_to_cpu(key)); dev->virt_name, be64_to_cpu(key));
@@ -320,6 +344,8 @@ static struct scst_dev_registrant *scst_pr_find_reg(
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
list_for_each_entry(reg, &dev->dev_registrants_list, list_for_each_entry(reg, &dev->dev_registrants_list,
dev_registrants_list_entry) { dev_registrants_list_entry) {
if ((reg->rel_tgt_id == rel_tgt_id) && if ((reg->rel_tgt_id == rel_tgt_id) &&
@@ -338,6 +364,8 @@ static void scst_pr_clear_reservation(struct scst_device *dev)
{ {
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
WARN_ON(!dev->pr_is_set); WARN_ON(!dev->pr_is_set);
dev->pr_is_set = 0; dev->pr_is_set = 0;
@@ -355,6 +383,8 @@ static void scst_pr_clear_holder(struct scst_device *dev)
{ {
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
WARN_ON(!dev->pr_is_set); WARN_ON(!dev->pr_is_set);
if (dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG || if (dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG ||
@@ -382,6 +412,8 @@ static struct scst_dev_registrant *scst_pr_add_registrant(
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
sBUG_ON(dev == NULL); sBUG_ON(dev == NULL);
sBUG_ON(transport_id == NULL); sBUG_ON(transport_id == NULL);
@@ -465,6 +497,8 @@ static void scst_pr_remove_registrant(struct scst_device *dev,
{ {
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
TRACE_PR("Removing registrant %s/%d (reg %p, tgt_dev %p, key %016llx, " TRACE_PR("Removing registrant %s/%d (reg %p, tgt_dev %p, key %016llx, "
"dev %s)", debug_transport_id_to_initiator_name(reg->transport_id), "dev %s)", debug_transport_id_to_initiator_name(reg->transport_id),
reg->rel_tgt_id, reg, reg->tgt_dev, be64_to_cpu(reg->key), reg->rel_tgt_id, reg, reg->tgt_dev, be64_to_cpu(reg->key),
@@ -485,6 +519,18 @@ static void scst_pr_remove_registrant(struct scst_device *dev,
return; return;
} }
static void scst_pr_remove_registrants(struct scst_device *dev)
{
struct scst_dev_registrant *reg, *tmp_reg;
scst_assert_pr_mutex_held(dev);
list_for_each_entry_safe(reg, tmp_reg, &dev->dev_registrants_list,
dev_registrants_list_entry) {
scst_pr_remove_registrant(dev, reg);
}
}
/* Must be called under dev_pr_mutex */ /* Must be called under dev_pr_mutex */
static void scst_pr_send_ua_reg(struct scst_device *dev, static void scst_pr_send_ua_reg(struct scst_device *dev,
struct scst_dev_registrant *reg, struct scst_dev_registrant *reg,
@@ -494,6 +540,8 @@ static void scst_pr_send_ua_reg(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
scst_set_sense(ua, sizeof(ua), dev->d_sense, key, asc, ascq); scst_set_sense(ua, sizeof(ua), dev->d_sense, key, asc, ascq);
TRACE_PR("Queueing UA [%x %x %x]: registrant %s/%d (%p), tgt_dev %p, " TRACE_PR("Queueing UA [%x %x %x]: registrant %s/%d (%p), tgt_dev %p, "
@@ -517,6 +565,8 @@ static void scst_pr_send_ua_all(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
list_for_each_entry(reg, &dev->dev_registrants_list, list_for_each_entry(reg, &dev->dev_registrants_list,
dev_registrants_list_entry) { dev_registrants_list_entry) {
if (reg != exclude_reg) if (reg != exclude_reg)
@@ -537,6 +587,8 @@ static void scst_pr_abort_reg(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
if (reg->tgt_dev == NULL) { if (reg->tgt_dev == NULL) {
TRACE_PR("Registrant %s/%d (%p, key 0x%016llx) has no session", TRACE_PR("Registrant %s/%d (%p, key 0x%016llx) has no session",
debug_transport_id_to_initiator_name(reg->transport_id), debug_transport_id_to_initiator_name(reg->transport_id),
@@ -612,6 +664,10 @@ static int scst_pr_do_load_device_file(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
scst_pr_remove_registrants(dev);
old_fs = get_fs(); old_fs = get_fs();
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
@@ -767,10 +823,12 @@ out:
static int scst_pr_load_device_file(struct scst_device *dev) static int scst_pr_load_device_file(struct scst_device *dev)
{ {
int res; int res, rc;
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
if (dev->pr_file_name == NULL || dev->pr_file_name1 == NULL) { if (dev->pr_file_name == NULL || dev->pr_file_name1 == NULL) {
PRINT_ERROR("Invalid file paths for '%s'", dev->virt_name); PRINT_ERROR("Invalid file paths for '%s'", dev->virt_name);
res = -EINVAL; res = -EINVAL;
@@ -779,12 +837,20 @@ static int scst_pr_load_device_file(struct scst_device *dev)
res = scst_pr_do_load_device_file(dev, dev->pr_file_name); res = scst_pr_do_load_device_file(dev, dev->pr_file_name);
if (res == 0) if (res == 0)
goto out; goto out_dump;
else if (res == -ENOMEM) else if (res == -ENOMEM)
goto out; goto out;
res = scst_pr_do_load_device_file(dev, dev->pr_file_name1); rc = res;
res = scst_pr_do_load_device_file(dev, dev->pr_file_name1);
if (res != 0) {
if (res == -ENOENT)
res = rc;
goto out;
}
out_dump:
scst_pr_dump_prs(dev, false); scst_pr_dump_prs(dev, false);
out: out:
@@ -799,6 +865,8 @@ static void scst_pr_remove_device_files(struct scst_tgt_dev *tgt_dev)
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
res = dev->pr_file_name ? scst_remove_file(dev->pr_file_name) : -ENOENT; res = dev->pr_file_name ? scst_remove_file(dev->pr_file_name) : -ENOENT;
res = dev->pr_file_name1 ? scst_remove_file(dev->pr_file_name1) : -ENOENT; res = dev->pr_file_name1 ? scst_remove_file(dev->pr_file_name1) : -ENOENT;
@@ -821,6 +889,8 @@ void scst_pr_sync_device_file(struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
if ((dev->pr_aptpl == 0) || list_empty(&dev->dev_registrants_list)) { if ((dev->pr_aptpl == 0) || list_empty(&dev->dev_registrants_list)) {
scst_pr_remove_device_files(tgt_dev); scst_pr_remove_device_files(tgt_dev);
goto out; goto out;
@@ -1003,107 +1073,107 @@ write_error_close:
goto out_set_fs; goto out_set_fs;
} }
static int scst_pr_check_pr_path(void) #endif /* CONFIG_SCST_PROC */
/**
* scst_pr_set_file_name - set name of file in which to save PR information
* @dev: SCST device.
* @prev: If not NULL, the current path will be stored in *@prev. It is the
* responsibility of the caller to invoke kfree(*@prev) at an
* appropriate time.
* @fmt: Full path of the file in which to save PR info.
*
* This function must be called either while @dev is not on the device list
* or with scst_mutex held.
*/
int scst_pr_set_file_name(struct scst_device *dev, char **prev,
const char *fmt, ...)
{ {
int res; va_list args;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) char *pr_file_name = NULL, *bkp = NULL;
struct nameidata nd; int file_mode, res = -EINVAL;
#else
struct path path;
#endif
mm_segment_t old_fs = get_fs(); scst_assert_pr_mutex_held(dev);
TRACE_ENTRY(); sBUG_ON(!fmt);
set_fs(KERNEL_DS); res = -ENOMEM;
va_start(args, fmt);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) pr_file_name = kvasprintf(GFP_KERNEL, fmt, args);
res = path_lookup(SCST_PR_DIR, 0, &nd); va_end(args);
if (res == 0) if (!pr_file_name) {
scst_path_put(&nd); PRINT_ERROR("Unable to kvasprintf() new PR file name");
#else goto out;
res = kern_path(SCST_PR_DIR, 0, &path);
if (res == 0)
path_put(&path);
#endif
if (res != 0) {
PRINT_ERROR("Unable to find %s (err %d), you should create "
"this directory manually or reinstall SCST",
SCST_PR_DIR, res);
goto out_setfs;
} }
out_setfs: res = -EINVAL;
set_fs(old_fs); if (pr_file_name[0] != '/') {
PRINT_ERROR("PR file name must be absolute!");
goto out;
}
TRACE_EXIT_RES(res); file_mode = scst_get_file_mode(pr_file_name);
if (file_mode >= 0 && !S_ISREG(file_mode) && !S_ISBLK(file_mode)) {
PRINT_ERROR("PR file name must be file or block device!");
goto out;
}
res = -ENOENT;
if (!scst_parent_dir_exists(pr_file_name)) {
PRINT_ERROR("PR file name parent directory doesn't exist");
goto out;
}
res = -ENOMEM;
bkp = kasprintf(GFP_KERNEL, "%s.1", pr_file_name);
if (!bkp) {
PRINT_ERROR("Unable to kasprintf() backup PR file name");
goto out;
}
if (prev) {
*prev = dev->pr_file_name;
dev->pr_file_name = pr_file_name;
pr_file_name = NULL;
} else
swap(dev->pr_file_name, pr_file_name);
swap(dev->pr_file_name1, bkp);
res = 0;
out:
kfree(pr_file_name);
kfree(bkp);
return res; return res;
} }
#endif /* CONFIG_SCST_PROC */ /* Must be called under dev_pr_mutex or before dev is on the device list. */
/* Called under scst_mutex */
int scst_pr_init_dev(struct scst_device *dev) int scst_pr_init_dev(struct scst_device *dev)
{ {
int res = 0; int res = 0;
TRACE_ENTRY(); TRACE_ENTRY();
dev->pr_file_name = kasprintf(GFP_KERNEL, "%s/%s", SCST_PR_DIR, scst_assert_pr_mutex_held(dev);
dev->virt_name);
if (dev->pr_file_name == NULL) { sBUG_ON(!dev->pr_file_name || !dev->pr_file_name1);
PRINT_ERROR("Allocation of device '%s' file path failed",
dev->virt_name);
res = -ENOMEM;
goto out;
}
dev->pr_file_name1 = kasprintf(GFP_KERNEL, "%s/%s.1", SCST_PR_DIR,
dev->virt_name);
if (dev->pr_file_name1 == NULL) {
PRINT_ERROR("Allocation of device '%s' backup file path failed",
dev->virt_name);
res = -ENOMEM;
goto out_free_name;
}
#ifndef CONFIG_SCST_PROC #ifndef CONFIG_SCST_PROC
res = scst_pr_check_pr_path(); res = scst_pr_load_device_file(dev);
if (res == 0) { if (res == -ENOENT)
res = scst_pr_load_device_file(dev); res = 0;
if (res == -ENOENT)
res = 0;
}
#endif #endif
if (res != 0)
goto out_free_name1;
out:
TRACE_EXIT_RES(res); TRACE_EXIT_RES(res);
return res; return res;
out_free_name1:
kfree(dev->pr_file_name1);
dev->pr_file_name1 = NULL;
out_free_name:
kfree(dev->pr_file_name);
dev->pr_file_name = NULL;
goto out;
} }
/* Called under scst_mutex */ /* Called under scst_mutex */
void scst_pr_clear_dev(struct scst_device *dev) void scst_pr_clear_dev(struct scst_device *dev)
{ {
struct scst_dev_registrant *reg, *tmp_reg;
TRACE_ENTRY(); TRACE_ENTRY();
list_for_each_entry_safe(reg, tmp_reg, &dev->dev_registrants_list, scst_assert_pr_mutex_held(dev);
dev_registrants_list_entry) {
scst_pr_remove_registrant(dev, reg); scst_pr_remove_registrants(dev);
}
kfree(dev->pr_file_name); kfree(dev->pr_file_name);
kfree(dev->pr_file_name1); kfree(dev->pr_file_name1);
@@ -1198,6 +1268,8 @@ static int scst_pr_register_with_spec_i_pt(struct scst_cmd *cmd,
struct scst_dev_registrant *reg; struct scst_dev_registrant *reg;
uint8_t *transport_id; uint8_t *transport_id;
scst_assert_pr_mutex_held(cmd->dev);
action_key = get_unaligned((__be64 *)&buffer[8]); action_key = get_unaligned((__be64 *)&buffer[8]);
ext_size = get_unaligned_be32(&buffer[24]); ext_size = get_unaligned_be32(&buffer[24]);
@@ -1315,6 +1387,8 @@ static void scst_pr_unregister(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
TRACE_PR("Unregistering key %0llx", reg->key); TRACE_PR("Unregistering key %0llx", reg->key);
is_holder = scst_pr_is_holder(dev, reg); is_holder = scst_pr_is_holder(dev, reg);
@@ -1346,6 +1420,8 @@ static void scst_pr_unregister_all_tg_pt(struct scst_device *dev,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
/* /*
* We can't use scst_mutex here since the caller already holds * We can't use scst_mutex here since the caller already holds
* dev_pr_mutex. * dev_pr_mutex.
@@ -1388,6 +1464,8 @@ static int scst_pr_register_on_tgt_id(struct scst_cmd *cmd,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
TRACE_PR("rel_tgt_id %d, spec_i_pt %d", rel_tgt_id, spec_i_pt); TRACE_PR("rel_tgt_id %d, spec_i_pt %d", rel_tgt_id, spec_i_pt);
if (spec_i_pt) { if (spec_i_pt) {
@@ -1433,6 +1511,8 @@ static int scst_pr_register_all_tg_pt(struct scst_cmd *cmd, uint8_t *buffer,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
/* /*
* We can't use scst_mutex here because the caller already holds * We can't use scst_mutex here because the caller already holds
* dev_pr_mutex. * dev_pr_mutex.
@@ -1478,6 +1558,8 @@ static int __scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
if (all_tg_pt) { if (all_tg_pt) {
res = scst_pr_register_all_tg_pt(cmd, buffer, buffer_size, res = scst_pr_register_all_tg_pt(cmd, buffer, buffer_size,
spec_i_pt, &rollback_list); spec_i_pt, &rollback_list);
@@ -1524,6 +1606,8 @@ void scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size)
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
aptpl = buffer[20] & 0x01; aptpl = buffer[20] & 0x01;
spec_i_pt = (buffer[20] >> 3) & 0x01; spec_i_pt = (buffer[20] >> 3) & 0x01;
all_tg_pt = (buffer[20] >> 2) & 0x01; all_tg_pt = (buffer[20] >> 2) & 0x01;
@@ -1618,6 +1702,8 @@ void scst_pr_register_and_ignore(struct scst_cmd *cmd, uint8_t *buffer,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
aptpl = buffer[20] & 0x01; aptpl = buffer[20] & 0x01;
all_tg_pt = (buffer[20] >> 2) & 0x01; all_tg_pt = (buffer[20] >> 2) & 0x01;
action_key = get_unaligned((__be64 *)&buffer[8]); action_key = get_unaligned((__be64 *)&buffer[8]);
@@ -1696,6 +1782,8 @@ void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
aptpl = buffer[17] & 0x01; aptpl = buffer[17] & 0x01;
key = get_unaligned((__be64 *)&buffer[0]); key = get_unaligned((__be64 *)&buffer[0]);
action_key = get_unaligned((__be64 *)&buffer[8]); action_key = get_unaligned((__be64 *)&buffer[8]);
@@ -1839,6 +1927,8 @@ void scst_pr_reserve(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size)
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
key = get_unaligned((__be64 *)&buffer[0]); key = get_unaligned((__be64 *)&buffer[0]);
scope = cmd->cdb[2] >> 4; scope = cmd->cdb[2] >> 4;
type = cmd->cdb[2] & 0x0f; type = cmd->cdb[2] & 0x0f;
@@ -1926,6 +2016,8 @@ void scst_pr_release(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size)
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
key = get_unaligned((__be64 *)&buffer[0]); key = get_unaligned((__be64 *)&buffer[0]);
scope = cmd->cdb[2] >> 4; scope = cmd->cdb[2] >> 4;
type = cmd->cdb[2] & 0x0f; type = cmd->cdb[2] & 0x0f;
@@ -2001,6 +2093,8 @@ void scst_pr_clear(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size)
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
key = get_unaligned((__be64 *)&buffer[0]); key = get_unaligned((__be64 *)&buffer[0]);
if (buffer_size != 24) { if (buffer_size != 24) {
@@ -2211,6 +2305,8 @@ void scst_pr_preempt(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size)
{ {
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
scst_pr_do_preempt(cmd, buffer, buffer_size, false); scst_pr_do_preempt(cmd, buffer, buffer_size, false);
TRACE_EXIT(); TRACE_EXIT();
@@ -2248,6 +2344,8 @@ void scst_pr_preempt_and_abort(struct scst_cmd *cmd, uint8_t *buffer,
{ {
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
cmd->pr_abort_counter = kzalloc(sizeof(*cmd->pr_abort_counter), cmd->pr_abort_counter = kzalloc(sizeof(*cmd->pr_abort_counter),
GFP_KERNEL); GFP_KERNEL);
if (cmd->pr_abort_counter == NULL) { if (cmd->pr_abort_counter == NULL) {
@@ -2424,6 +2522,8 @@ void scst_pr_read_keys(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size)
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
if (buffer_size < 8) { if (buffer_size < 8) {
TRACE_PR("buffer_size too small: %d. expected >= 8 " TRACE_PR("buffer_size too small: %d. expected >= 8 "
"(buffer %p)", buffer_size, buffer); "(buffer %p)", buffer_size, buffer);
@@ -2475,6 +2575,8 @@ void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
if (buffer_size < 8) { if (buffer_size < 8) {
TRACE_PR("buffer_size too small: %d. expected >= 8 " TRACE_PR("buffer_size too small: %d. expected >= 8 "
"(buffer %p)", buffer_size, buffer); "(buffer %p)", buffer_size, buffer);
@@ -2538,6 +2640,8 @@ void scst_pr_report_caps(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size)
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(dev);
if (buffer_size < 8) { if (buffer_size < 8) {
TRACE_PR("buffer_size too small: %d. expected >= 8 " TRACE_PR("buffer_size too small: %d. expected >= 8 "
"(buffer %p)", buffer_size, buffer); "(buffer %p)", buffer_size, buffer);
@@ -2577,6 +2681,8 @@ void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer,
TRACE_ENTRY(); TRACE_ENTRY();
scst_assert_pr_mutex_held(cmd->dev);
if (buffer_size < 8) if (buffer_size < 8)
goto skip; goto skip;

View File

@@ -89,6 +89,9 @@ static inline void scst_pr_write_unlock(struct scst_device *dev)
mutex_unlock(&dev->dev_pr_mutex); mutex_unlock(&dev->dev_pr_mutex);
} }
int scst_pr_set_file_name(struct scst_device *dev, char **prev,
const char *fmt, ...) __printf(3, 4);
int scst_pr_init_dev(struct scst_device *dev); int scst_pr_init_dev(struct scst_device *dev);
void scst_pr_clear_dev(struct scst_device *dev); void scst_pr_clear_dev(struct scst_device *dev);

View File

@@ -335,6 +335,7 @@ void scst_free_tgt(struct scst_tgt *tgt);
int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev); int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev);
void scst_free_device(struct scst_device *dev); void scst_free_device(struct scst_device *dev);
bool scst_device_is_exported(struct scst_device *dev);
struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt,
const char *acg_name, bool tgt_acg); const char *acg_name, bool tgt_acg);

View File

@@ -2756,6 +2756,135 @@ static ssize_t scst_dev_sysfs_type_show(struct kobject *kobj,
static struct kobj_attribute dev_type_attr = static struct kobj_attribute dev_type_attr =
__ATTR(type, S_IRUGO, scst_dev_sysfs_type_show, NULL); __ATTR(type, S_IRUGO, scst_dev_sysfs_type_show, NULL);
static ssize_t scst_dev_sysfs_pr_file_name_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct scst_device *dev;
int res;
dev = container_of(kobj, struct scst_device, dev_kobj);
res = mutex_lock_interruptible(&dev->dev_pr_mutex);
if (res != 0)
goto out;
/* pr_file_name is NULL for SCSI pass-through devices */
WARN_ON_ONCE(!dev->pr_file_name);
res = scnprintf(buf, PAGE_SIZE, "%s\n%s", dev->pr_file_name ? : "",
dev->pr_file_name_is_set ? SCST_SYSFS_KEY_MARK "\n" :
"");
mutex_unlock(&dev->dev_pr_mutex);
out:
return res;
}
static int
scst_dev_sysfs_pr_file_name_process_store(struct scst_sysfs_work_item *work)
{
struct scst_device *dev = work->dev;
char *pr_file_name = work->buf, *prev = NULL;
int res;
res = mutex_lock_interruptible(&scst_mutex);
if (res != 0)
goto out;
res = -EBUSY;
if (scst_device_is_exported(dev)) {
PRINT_ERROR("%s: not changing pr_file_name because the device"
" has already been exported", dev->virt_name);
goto unlock_scst;
}
res = mutex_lock_interruptible(&dev->dev_pr_mutex);
if (res)
goto unlock_scst;
if (strcmp(dev->pr_file_name, pr_file_name) == 0)
goto unlock_dev_pr;
res = scst_pr_set_file_name(dev, &prev, "%s", pr_file_name);
if (res != 0)
goto unlock_dev_pr;
res = scst_pr_init_dev(dev);
if (res != 0) {
PRINT_ERROR("%s: loading PR from %s failed (%d) - restoring %s",
dev->virt_name, dev->pr_file_name, res,
prev ? : "");
scst_pr_set_file_name(dev, NULL, "%s", prev);
scst_pr_init_dev(dev);
goto unlock_dev_pr;
}
dev->pr_file_name_is_set = !work->default_val;
unlock_dev_pr:
mutex_unlock(&dev->dev_pr_mutex);
unlock_scst:
mutex_unlock(&scst_mutex);
out:
kobject_put(&dev->dev_kobj);
kfree(prev);
return res;
}
static ssize_t scst_dev_sysfs_pr_file_name_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
struct scst_sysfs_work_item *work;
struct scst_device *dev;
char *pr_file_name, *p;
int res = -ENOMEM;
bool def = false;
dev = container_of(kobj, struct scst_device, dev_kobj);
pr_file_name = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf);
if (!pr_file_name) {
PRINT_ERROR("Unable to kasprintf() PR file name");
goto out;
}
p = pr_file_name;
strsep(&p, "\n"); /* strip trailing whitespace */
if (pr_file_name[0] == '\0') {
kfree(pr_file_name);
pr_file_name = kasprintf(GFP_KERNEL, "%s/%s", SCST_PR_DIR,
dev->virt_name);
if (!pr_file_name) {
PRINT_ERROR("Unable to kasprintf() PR file name");
goto out;
}
def = true;
}
res = scst_alloc_sysfs_work(scst_dev_sysfs_pr_file_name_process_store,
false, &work);
if (res != 0)
goto out;
kobject_get(&dev->dev_kobj);
work->dev = dev;
work->default_val = def;
swap(work->buf, pr_file_name);
res = scst_sysfs_queue_wait_work(work);
if (res == 0)
res = count;
out:
kfree(pr_file_name);
return res;
}
static struct kobj_attribute dev_pr_file_name_attr =
__ATTR(pr_file_name, S_IWUSR|S_IRUGO,
scst_dev_sysfs_pr_file_name_show,
scst_dev_sysfs_pr_file_name_store);
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
static ssize_t scst_dev_sysfs_dump_prs(struct kobject *kobj, static ssize_t scst_dev_sysfs_dump_prs(struct kobject *kobj,
@@ -3205,6 +3334,15 @@ int scst_dev_sysfs_create(struct scst_device *dev)
dev->virt_name); dev->virt_name);
goto out_del; goto out_del;
} }
} else {
res = sysfs_create_file(&dev->dev_kobj,
&dev_pr_file_name_attr.attr);
if (res != 0) {
PRINT_ERROR("Can't create attr %s for dev %s",
dev_pr_file_name_attr.attr.name,
dev->virt_name);
goto out_del;
}
} }
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)