mirror of
https://github.com/SCST-project/scst.git
synced 2026-06-09 23:22:33 +00:00
Devices external blocking support
Prepared with help from Prasidh Srikanth <Prasidh.Srikanth@sandisk.com> git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6575 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
+32
@@ -508,6 +508,8 @@ documentation for your dev handlers for more info about it as well as
|
||||
SysfsRules file for more info about common to all dev handlers rules.
|
||||
SCST dev handlers can have the following common entries:
|
||||
|
||||
- block - allows to temporary block and unblock this device. See below.
|
||||
|
||||
- exported - subdirectory containing links to all LUNs where this
|
||||
device was exported.
|
||||
|
||||
@@ -531,6 +533,35 @@ SCST dev handlers can have the following common entries:
|
||||
|
||||
- type - SCSI type of this device
|
||||
|
||||
Attribute "block" allows to temporary block and unblock this device.
|
||||
"Blocking" means that no new commands for this device will go into the
|
||||
execution stage, but instead will be suspended just before it. The
|
||||
blocked state is not reached until queue of the corresponding device is
|
||||
completely drained. You can also call this state "frozen". It is useful
|
||||
in many cases, like consistent snapshots and graceful shutdown.
|
||||
|
||||
On write "block" entry allows the following 3 types of parameters:
|
||||
|
||||
- 1 - block device synchronously, i.e. don't return until this device
|
||||
becomes blocked, i.e. until queue of it is not completely drained. Can
|
||||
be called as many times as needed.
|
||||
|
||||
- 11 params - block device asynchronously, i.e. return immediately.
|
||||
Notification about completing is delivered using SCST_EVENT_EXT_BLOCKING_DONE
|
||||
event. "Params" delivered to it as is in "data" payload. Can be
|
||||
called as many times as needed. Alternatively, status of blocking could be
|
||||
polled by reading this attributes until the second number reaches 0
|
||||
(see below).
|
||||
|
||||
- 0 - unblock this device.
|
||||
|
||||
Reading from "block" entry returns two numbers separated by space:
|
||||
|
||||
1. How many times this device was blocked, i.e. how many times writing
|
||||
"0" to it is needed to unblock this device.
|
||||
|
||||
2. Boolean (0 or 1) if blocking, if any, is done (0) or still pending (1).
|
||||
|
||||
See below for more information about other entries of this subdirectory
|
||||
of the standard SCST dev handlers.
|
||||
|
||||
@@ -1257,6 +1288,7 @@ Each vdisk_fileio's device has the following attributes in
|
||||
For example:
|
||||
|
||||
/sys/kernel/scst_tgt/devices/disk1
|
||||
|-- block
|
||||
|-- blocksize
|
||||
|-- exported
|
||||
| |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
|
||||
|
||||
@@ -367,6 +367,8 @@ documentation for your dev handlers for more info about it as well as
|
||||
SysfsRules file for more info about common to all dev handlers rules.
|
||||
SCST dev handlers can have the following common entries:
|
||||
|
||||
- block - allows to temporary block and unblock this device. See below.
|
||||
|
||||
- exported - subdirectory containing links to all LUNs where this
|
||||
device was exported.
|
||||
|
||||
@@ -390,6 +392,35 @@ SCST dev handlers can have the following common entries:
|
||||
|
||||
- type - SCSI type of this device
|
||||
|
||||
Attribute "block" allows to temporary block and unblock this device.
|
||||
"Blocking" means that no new commands for this device will go into the
|
||||
execution stage, but instead will be suspended just before it. The
|
||||
blocked state is not reached until queue of the corresponding device is
|
||||
completely drained. You can also call this state "frozen". It is useful
|
||||
in many cases, like consistent snapshots and graceful shutdown.
|
||||
|
||||
On write "block" entry allows the following 3 types of parameters:
|
||||
|
||||
- 1 - block device synchronously, i.e. don't return until this device
|
||||
becomes blocked, i.e. until queue of it is not completely drained. Can
|
||||
be called as many times as needed.
|
||||
|
||||
- 11 params - block device asynchronously, i.e. return immediately.
|
||||
Notification about completing is delivered using SCST_EVENT_EXT_BLOCKING_DONE
|
||||
event. "Params" delivered to it as is in "data" payload. Can be
|
||||
called as many times as needed. Alternatively, status of blocking could be
|
||||
polled by reading this attributes until the second number reaches 0
|
||||
(see below).
|
||||
|
||||
- 0 - unblock this device.
|
||||
|
||||
Reading from "block" entry returns two numbers separated by space:
|
||||
|
||||
1. How many times this device was blocked, i.e. how many times writing
|
||||
"0" to it is needed to unblock this device.
|
||||
|
||||
2. Boolean (0 or 1) if blocking, if any, is done (0) or still pending (1).
|
||||
|
||||
See below for more information about other entries of this subdirectory
|
||||
of the standard SCST dev handlers.
|
||||
|
||||
@@ -1110,6 +1141,7 @@ Each vdisk_fileio's device has the following attributes in
|
||||
For example:
|
||||
|
||||
/sys/kernel/scst_tgt/devices/disk1
|
||||
|-- block
|
||||
|-- blocksize
|
||||
|-- exported
|
||||
| |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
|
||||
|
||||
@@ -2607,6 +2607,20 @@ struct scst_cl_ops {
|
||||
void (*reserve)(struct scst_device *dev, struct scst_session *sess);
|
||||
};
|
||||
|
||||
/*
|
||||
* Extended, e.g. via sysfs, blockers
|
||||
*/
|
||||
typedef void (*ext_blocker_done_fn_t) (struct scst_device *dev,
|
||||
uint8_t *data, int len);
|
||||
|
||||
struct scst_ext_blocker {
|
||||
struct list_head ext_blockers_list_entry;
|
||||
|
||||
ext_blocker_done_fn_t ext_blocker_done_fn;
|
||||
int ext_blocker_data_len;
|
||||
uint8_t ext_blocker_data[];
|
||||
};
|
||||
|
||||
/*
|
||||
* SCST device
|
||||
*/
|
||||
@@ -2635,6 +2649,15 @@ struct scst_device {
|
||||
*/
|
||||
unsigned int dev_unregistering:1;
|
||||
|
||||
/*
|
||||
* Set if ext blocking is pending. It if just shortcut for
|
||||
* !list_empty(&dev->ext_blockers_list) to save a cache miss.
|
||||
*/
|
||||
unsigned int ext_blocking_pending:1;
|
||||
|
||||
/* Used to serialize invocations of __scst_ext_blocking_done() */
|
||||
unsigned int ext_unblock_scheduled:1;
|
||||
|
||||
/* Set if this device does not support DIF IP checking */
|
||||
unsigned int dev_dif_ip_not_supported:1;
|
||||
|
||||
@@ -2858,6 +2881,15 @@ struct scst_device {
|
||||
/* List of blocked commands, protected by dev_lock. */
|
||||
struct list_head blocked_cmd_list;
|
||||
|
||||
/* Number of ext blocking requests, protected by dev_lock */
|
||||
int ext_blocks_cnt;
|
||||
|
||||
/* List of ext blockers, protected by dev_lock */
|
||||
struct list_head ext_blockers_list;
|
||||
|
||||
/* Work to notify ext blockers out of dev_lock context */
|
||||
struct work_struct ext_blockers_work;
|
||||
|
||||
/* MAXIMUM WRITE SAME LENGTH in bytes */
|
||||
uint64_t max_write_same_len;
|
||||
|
||||
|
||||
@@ -114,6 +114,13 @@ struct scst_event_negative_luns_inquiry_payload {
|
||||
uint8_t target_name[SCST_MAX_EXTERNAL_NAME];
|
||||
};
|
||||
|
||||
#define SCST_EVENT_EXT_BLOCKING_DONE 3
|
||||
struct scst_event_ext_blocking_done_payload {
|
||||
uint8_t device_name[SCST_MAX_EXTERNAL_NAME];
|
||||
uint32_t data_len;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
#define SCST_EVENT_TM_FN_RECEIVED 4
|
||||
struct scst_event_tm_fn_received_payload {
|
||||
uint32_t fn; /* TM fn */
|
||||
|
||||
@@ -362,6 +362,47 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
int scst_event_queue_ext_blocking_done(struct scst_device *dev, void *data, int len)
|
||||
{
|
||||
int res, event_entry_len;
|
||||
struct scst_event_entry *event_entry;
|
||||
struct scst_event *event;
|
||||
struct scst_event_ext_blocking_done_payload *payload;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
event_entry_len = sizeof(*event_entry) + sizeof(*payload) + len;
|
||||
event_entry = kzalloc(event_entry_len, GFP_ATOMIC);
|
||||
if (event_entry == NULL) {
|
||||
PRINT_CRIT_ERROR("Unable to allocate event. Ext blocking "
|
||||
"done event is lost (device %s, size %zd)!", dev->virt_name,
|
||||
sizeof(*event_entry) + sizeof(*payload) + len);
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_MEM("event_entry %p (len %d) allocated", event_entry,
|
||||
event_entry_len);
|
||||
|
||||
event = &event_entry->event;
|
||||
|
||||
event->payload_len = sizeof(*payload) + len;
|
||||
payload = (struct scst_event_ext_blocking_done_payload *)event->payload;
|
||||
|
||||
strlcpy(payload->device_name, dev->virt_name, sizeof(payload->device_name));
|
||||
if (len > 0)
|
||||
memcpy(payload->data, data, len);
|
||||
|
||||
scst_event_queue(SCST_EVENT_EXT_BLOCKING_DONE,
|
||||
SCST_EVENT_SCST_CORE_ISSUER, event_entry);
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
int scst_event_queue_tm_fn_received(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
|
||||
@@ -3932,6 +3932,8 @@ static void scst_init_order_data(struct scst_order_data *order_data)
|
||||
return;
|
||||
}
|
||||
|
||||
static void scst_ext_blocking_done_fn(struct work_struct *work);
|
||||
|
||||
static int scst_dif_none(struct scst_cmd *cmd);
|
||||
#ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS
|
||||
static int scst_dif_none_type1(struct scst_cmd *cmd);
|
||||
@@ -3963,6 +3965,8 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev)
|
||||
INIT_LIST_HEAD(&dev->blocked_cmd_list);
|
||||
INIT_LIST_HEAD(&dev->dev_tgt_dev_list);
|
||||
INIT_LIST_HEAD(&dev->dev_acg_dev_list);
|
||||
INIT_LIST_HEAD(&dev->ext_blockers_list);
|
||||
INIT_WORK(&dev->ext_blockers_work, scst_ext_blocking_done_fn);
|
||||
dev->dev_double_ua_possible = 1;
|
||||
dev->queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER;
|
||||
|
||||
@@ -3997,6 +4001,9 @@ void scst_free_device(struct scst_device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Ensure that ext_blockers_work is done */
|
||||
flush_scheduled_work();
|
||||
|
||||
scst_deinit_threads(&dev->dev_cmd_threads);
|
||||
|
||||
scst_pr_cleanup(dev);
|
||||
@@ -13111,6 +13118,241 @@ out_too_many:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_restore_global_mode_pages);
|
||||
|
||||
/* Must be called under dev_lock and BHs off. Might release it, then reacquire. */
|
||||
void __scst_ext_blocking_done(struct scst_device *dev)
|
||||
{
|
||||
bool stop;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_BLOCK("Notifying ext blockers for dev %s (ext_blocks_cnt %d)",
|
||||
dev->virt_name, dev->ext_blocks_cnt);
|
||||
|
||||
stop = list_empty(&dev->ext_blockers_list);
|
||||
while (!stop) {
|
||||
struct scst_ext_blocker *b;
|
||||
|
||||
b = list_first_entry(&dev->ext_blockers_list, typeof(*b),
|
||||
ext_blockers_list_entry);
|
||||
|
||||
TRACE_DBG("Notifying async ext blocker %p (cnt %d)", b,
|
||||
dev->ext_blocks_cnt);
|
||||
|
||||
list_del(&b->ext_blockers_list_entry);
|
||||
|
||||
stop = list_empty(&dev->ext_blockers_list);
|
||||
if (stop)
|
||||
dev->ext_blocking_pending = 0;
|
||||
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
b->ext_blocker_done_fn(dev, b->ext_blocker_data,
|
||||
b->ext_blocker_data_len);
|
||||
|
||||
kfree(b);
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static void scst_ext_blocking_done_fn(struct work_struct *work)
|
||||
{
|
||||
struct scst_device *dev = container_of(work, struct scst_device,
|
||||
ext_blockers_work);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
|
||||
WARN_ON(!dev->ext_unblock_scheduled);
|
||||
|
||||
/* We might have new ext blocker any time w/o dev_lock */
|
||||
while (!list_empty(&dev->ext_blockers_list))
|
||||
__scst_ext_blocking_done(dev);
|
||||
|
||||
WARN_ON(!dev->ext_unblock_scheduled);
|
||||
dev->ext_unblock_scheduled = 0;
|
||||
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Must be called under dev_lock and BHs off */
|
||||
void scst_ext_blocking_done(struct scst_device *dev)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
lockdep_assert_held(&dev->dev_lock);
|
||||
|
||||
if (dev->ext_unblock_scheduled)
|
||||
goto out;
|
||||
|
||||
TRACE_DBG("Scheduling ext_blockers_work for dev %s", dev->virt_name);
|
||||
|
||||
dev->ext_unblock_scheduled = 1;
|
||||
schedule_work(&dev->ext_blockers_work);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static void scst_sync_ext_blocking_done(struct scst_device *dev,
|
||||
uint8_t *data, int len)
|
||||
{
|
||||
wait_queue_head_t *w;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
w = (void *)*((unsigned long *)data);
|
||||
wake_up_all(w);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
int scst_ext_block_dev(struct scst_device *dev, bool sync,
|
||||
ext_blocker_done_fn_t done_fn, const uint8_t *priv, int priv_len)
|
||||
{
|
||||
int res;
|
||||
struct scst_ext_blocker *b;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (sync)
|
||||
priv_len = sizeof(void *);
|
||||
|
||||
b = kzalloc(sizeof(*b) + priv_len, GFP_KERNEL);
|
||||
if (b == NULL) {
|
||||
PRINT_ERROR("Unable to alloc struct scst_ext_blocker with data "
|
||||
"(size %zd)", sizeof(*b) + priv_len);
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_MGMT_DBG("New (%d) %s ext blocker %p for dev %s", dev->ext_blocks_cnt+1,
|
||||
sync ? "sync" : "async", b, dev->virt_name);
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
|
||||
if (dev->strictly_serialized_cmd_waiting) {
|
||||
/*
|
||||
* Avoid deadlock when this strictly serialized cmd
|
||||
* will not proceed stopped on our blocking, so our
|
||||
* blocking does not proceed as well.
|
||||
*/
|
||||
TRACE_DBG("Unstrictlyserialize dev %s", dev->virt_name);
|
||||
dev->strictly_serialized_cmd_waiting = 0;
|
||||
/* We will reuse blocking done by the strictly serialized cmd */
|
||||
} else
|
||||
scst_block_dev(dev);
|
||||
|
||||
dev->ext_blocks_cnt++;
|
||||
TRACE_DBG("ext_blocks_cnt %d", dev->ext_blocks_cnt);
|
||||
|
||||
if (sync && (dev->on_dev_cmd_count == 0)) {
|
||||
TRACE_DBG("No commands to wait for sync blocking (dev %s)",
|
||||
dev->virt_name);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
goto out_free_success;
|
||||
}
|
||||
|
||||
list_add_tail(&b->ext_blockers_list_entry, &dev->ext_blockers_list);
|
||||
dev->ext_blocking_pending = 1;
|
||||
|
||||
if (sync) {
|
||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(w);
|
||||
|
||||
b->ext_blocker_done_fn = scst_sync_ext_blocking_done;
|
||||
*((void **)&b->ext_blocker_data[0]) = &w;
|
||||
|
||||
wait_event_locked(w, (dev->on_dev_cmd_count == 0),
|
||||
lock_bh, dev->dev_lock);
|
||||
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
} else {
|
||||
b->ext_blocker_done_fn = done_fn;
|
||||
if (priv_len > 0) {
|
||||
b->ext_blocker_data_len = priv_len;
|
||||
memcpy(b->ext_blocker_data, priv, priv_len);
|
||||
}
|
||||
if (dev->on_dev_cmd_count == 0) {
|
||||
TRACE_DBG("No commands to wait for async blocking "
|
||||
"(dev %s)", dev->virt_name);
|
||||
if (!dev->ext_unblock_scheduled)
|
||||
__scst_ext_blocking_done(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
} else
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
out_success:
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_free_success:
|
||||
sBUG_ON(!sync);
|
||||
kfree(b);
|
||||
goto out_success;
|
||||
}
|
||||
|
||||
void scst_ext_unblock_dev(struct scst_device *dev)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
|
||||
if (dev->ext_blocks_cnt == 0) {
|
||||
TRACE_DBG("Nothing to unblock (dev %p)", dev);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
dev->ext_blocks_cnt--;
|
||||
TRACE_MGMT_DBG("Ext unblocking for dev %s (left: %d, pending %d)",
|
||||
dev->virt_name, dev->ext_blocks_cnt,
|
||||
dev->ext_blocking_pending);
|
||||
|
||||
if ((dev->ext_blocks_cnt == 0) && dev->ext_blocking_pending) {
|
||||
int rc;
|
||||
/* Wait pending ext blocking to finish */
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
TRACE_DBG("Ext unblock (dev %s): still pending...",
|
||||
dev->virt_name);
|
||||
rc = scst_ext_block_dev(dev, true, NULL, NULL, 0);
|
||||
if (rc != 0) {
|
||||
/* Oops, have to poll */
|
||||
PRINT_WARNING("scst_ext_block_dev(dev %s) failed, "
|
||||
"switch to polling", dev->virt_name);
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
while (dev->ext_blocking_pending) {
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
msleep(10);
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
}
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
} else {
|
||||
TRACE_DBG("Ext unblock: pending done, unblocking...");
|
||||
scst_ext_unblock_dev(dev);
|
||||
}
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
scst_unblock_dev(dev);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Abstract vfs_unlink() for different kernel versions (as possible) */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
|
||||
|
||||
@@ -715,6 +715,12 @@ void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
|
||||
void scst_block_dev(struct scst_device *dev);
|
||||
void scst_unblock_dev(struct scst_device *dev);
|
||||
|
||||
int scst_ext_block_dev(struct scst_device *dev, bool sync,
|
||||
ext_blocker_done_fn_t done_fn, const uint8_t *priv, int priv_len);
|
||||
void scst_ext_unblock_dev(struct scst_device *dev);
|
||||
void __scst_ext_blocking_done(struct scst_device *dev);
|
||||
void scst_ext_blocking_done(struct scst_device *dev);
|
||||
|
||||
bool __scst_check_blocked_dev(struct scst_cmd *cmd);
|
||||
|
||||
/*
|
||||
@@ -857,6 +863,7 @@ void scst_event_exit(void);
|
||||
int scst_event_queue_lun_not_found(const struct scst_cmd *cmd);
|
||||
int scst_event_queue_negative_luns_inquiry(const struct scst_tgt *tgt,
|
||||
const char *initiator_name);
|
||||
int scst_event_queue_ext_blocking_done(struct scst_device *dev, void *data, int len);
|
||||
int scst_event_queue_tm_fn_received(struct scst_mgmt_cmd *mcmd);
|
||||
|
||||
typedef void __printf(2, 3) (*scst_show_fn)(void *arg, const char *fmt, ...);
|
||||
|
||||
@@ -3471,8 +3471,137 @@ static struct kobj_attribute dev_threads_pool_type_attr =
|
||||
scst_dev_sysfs_threads_pool_type_show,
|
||||
scst_dev_sysfs_threads_pool_type_store);
|
||||
|
||||
static ssize_t scst_dev_block_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int pos = 0;
|
||||
struct scst_device *dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev = container_of(kobj, struct scst_device, dev_kobj);
|
||||
|
||||
pos = sprintf(buf, "%d %d\n", ACCESS_ONCE(dev->ext_blocks_cnt),
|
||||
dev->ext_blocking_pending);
|
||||
|
||||
TRACE_EXIT_RES(pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void scst_sysfs_ext_blocking_done(struct scst_device *dev,
|
||||
uint8_t *data, int len)
|
||||
{
|
||||
scst_event_queue_ext_blocking_done(dev, data, len);
|
||||
}
|
||||
|
||||
static ssize_t scst_dev_block_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int res, data_len = 0, pos = 0;
|
||||
struct scst_device *dev;
|
||||
const char *p = buf, *data_start = NULL;
|
||||
bool sync;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev = container_of(kobj, struct scst_device, dev_kobj);
|
||||
|
||||
switch (*p) {
|
||||
case '0':
|
||||
p++;
|
||||
pos++;
|
||||
while ((pos < count) && isspace(*p) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
}
|
||||
if ((pos != count) && (*p != '\0')) {
|
||||
PRINT_ERROR("Parse error on %c", *p);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_DBG("Sysfs unblocking (dev %s)", dev->virt_name);
|
||||
|
||||
scst_ext_unblock_dev(dev);
|
||||
res = 0;
|
||||
goto out;
|
||||
case '1':
|
||||
p++;
|
||||
pos++;
|
||||
while ((pos < count) && isspace(*p) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
}
|
||||
if ((pos == count) || (*p == '\0')) {
|
||||
data_len = sizeof(void *);
|
||||
sync = true;
|
||||
break;
|
||||
} else if (*p != '1') {
|
||||
PRINT_ERROR("Parse error on %c", *p);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sync = false;
|
||||
|
||||
p++;
|
||||
pos++;
|
||||
if ((pos == count) || (*p == '\0'))
|
||||
break;
|
||||
|
||||
while ((pos < count) && isspace(*p) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
}
|
||||
if ((pos == count) || (*p == '\0'))
|
||||
break;
|
||||
|
||||
data_start = p;
|
||||
while ((pos < count) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
data_len++;
|
||||
}
|
||||
/* Skip trailing spaces, if any */
|
||||
while (isspace(*(p-1))) {
|
||||
p--;
|
||||
data_len--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PRINT_ERROR("Illegal blocking value %c", *p);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_DBG("Sysfs blocking dev %s (sync %d, data_start %p, "
|
||||
"data_len %d)", dev->virt_name, sync, data_start, data_len);
|
||||
|
||||
if (sync)
|
||||
res = scst_ext_block_dev(dev, true, NULL, NULL, 0);
|
||||
else
|
||||
res = scst_ext_block_dev(dev, false, scst_sysfs_ext_blocking_done,
|
||||
data_start, data_len);
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
if (res == 0)
|
||||
res = count;
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct kobj_attribute dev_block_attr =
|
||||
__ATTR(block, S_IRUGO | S_IWUSR, scst_dev_block_show,
|
||||
scst_dev_block_store);
|
||||
|
||||
static struct attribute *scst_dev_attrs[] = {
|
||||
&dev_type_attr.attr,
|
||||
&dev_block_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
@@ -176,6 +176,14 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(dev->ext_blocking_pending)) {
|
||||
if (dev->on_dev_cmd_count == 0) {
|
||||
TRACE_MGMT_DBG("Releasing pending dev %s extended "
|
||||
"blocks", dev->virt_name);
|
||||
scst_ext_blocking_done(dev);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user