diff --git a/configfs.c b/configfs.c
index 222fae40..5476da17 100644
--- a/configfs.c
+++ b/configfs.c
@@ -60,18 +60,23 @@ int tcmu_get_attribute(struct tcmu_device *dev, const char *name)
return tcmu_get_cfgfs_int(path);
}
-int tcmu_set_control(struct tcmu_device *dev, const char *key, unsigned long val)
-{
- char path[PATH_MAX];
- char buf[CFGFS_BUF_SIZE];
-
- snprintf(path, sizeof(path), CFGFS_CORE"/%s/%s/control",
- dev->tcm_hba_name, dev->tcm_dev_name);
- snprintf(buf, sizeof(buf), "%s=%lu", key, val);
-
- return tcmu_set_cfgfs_str(path, buf, strlen(buf) + 1);
+#define tcmu_set_cfgfs_ctrl(type_name, type, type_frmt) \
+int tcmu_set_cfgfs_ctrl_##type_name(struct tcmu_device *dev, \
+ const char *key, type val) \
+{ \
+ char path[PATH_MAX]; \
+ char buf[CFGFS_BUF_SIZE]; \
+ \
+ snprintf(path, sizeof(path), CFGFS_CORE"/%s/%s/control", \
+ dev->tcm_hba_name, dev->tcm_dev_name); \
+ snprintf(buf, sizeof(buf), "%s="type_frmt, key, val); \
+ \
+ return tcmu_set_cfgfs_str(path, buf, strlen(buf) + 1); \
}
+tcmu_set_cfgfs_ctrl(ull, unsigned long long, "%llu");
+tcmu_set_cfgfs_ctrl(str, const char *, "%s");
+
static bool tcmu_cfgfs_mod_param_is_supported(const char *name)
{
char path[PATH_MAX];
@@ -211,7 +216,7 @@ int tcmu_set_dev_size(struct tcmu_device *dev)
dev_size = tcmu_get_dev_num_lbas(dev) * tcmu_get_dev_block_size(dev);
- return tcmu_set_control(dev, "dev_size", dev_size);
+ return tcmu_set_cfgfs_ctrl_ull(dev, "dev_size", dev_size);
}
long long tcmu_get_dev_size(struct tcmu_device *dev)
diff --git a/file_optical.c b/file_optical.c
index 505b11a6..bad751de 100644
--- a/file_optical.c
+++ b/file_optical.c
@@ -1559,6 +1559,7 @@ static struct tcmur_handler fbo_handler = {
.subtype = "fbo",
.handle_cmd = fbo_handle_cmd,
.nr_threads = 1,
+ .medium_change_supp = 1,
};
/* Entry point must be named "handler_init". */
diff --git a/libtcmu.c b/libtcmu.c
index 084a8f53..f6a80a96 100644
--- a/libtcmu.c
+++ b/libtcmu.c
@@ -141,6 +141,22 @@ lookup_dev_by_name(struct tcmulib_context *ctx, char *dev_name, int *index)
return NULL;
}
+struct tcmu_device *
+tcmulib_lookup_dev_by_tcmu_name(struct tcmulib_context *ctx, char *tcmu_name)
+{
+ struct tcmu_device **dev_ptr;
+ struct tcmu_device *dev;
+
+ darray_foreach(dev_ptr, ctx->devices) {
+ dev = *dev_ptr;
+
+ if (!strcmp(dev->tcm_dev_name, tcmu_name))
+ return dev;
+ }
+
+ return NULL;
+}
+
static int reconfig_device(struct tcmulib_context *ctx, char *dev_name,
struct genl_info *info)
{
@@ -1008,11 +1024,15 @@ struct tcmulib_cmd *tcmulib_get_next_command(struct tcmu_device *dev)
return NULL;
}
-static int tcmu_sts_to_scsi(int tcmu_sts, uint8_t *sense)
+static int tcmu_sts_to_scsi(struct tcmu_device *dev, int tcmu_sts,
+ uint8_t *sense, uint8_t *cdb)
{
- switch (tcmu_sts) {
- case TCMU_STS_OK:
+ if (tcmu_sts == TCMU_STS_OK)
return SAM_STAT_GOOD;
+
+ tcmu_dev_dbg(dev, "Completing 0x%x with status %d\n", cdb[0], tcmu_sts);
+
+ switch (tcmu_sts) {
case TCMU_STS_NO_RESOURCE:
return SAM_STAT_TASK_SET_FULL;
/*
@@ -1023,6 +1043,7 @@ static int tcmu_sts_to_scsi(int tcmu_sts, uint8_t *sense)
case TCMU_STS_BUSY:
return SAM_STAT_BUSY;
case TCMU_STS_PASSTHROUGH_ERR:
+ case TCMU_STS_UNIT_ATTENTION:
break;
/* Check Conditions below */
case TCMU_STS_RANGE:
@@ -1073,10 +1094,6 @@ static int tcmu_sts_to_scsi(int tcmu_sts, uint8_t *sense)
/* Invalid copy target device type */
tcmu_set_sense_data(sense, COPY_ABORTED, 0x0D03);
break;
- case TCMU_STS_CAPACITY_CHANGED:
- /* Device capacity has changed */
- tcmu_set_sense_data(sense, UNIT_ATTENTION, 0x2A09);
- break;
case TCMU_STS_TRANSITION:
/* ALUA state transition */
tcmu_set_sense_data(sense, NOT_READY, 0x040A);
@@ -1145,7 +1162,8 @@ void tcmulib_command_complete(
ent->hdr.cmd_id = cmd->cmd_id;
}
- ent->rsp.scsi_status = tcmu_sts_to_scsi(result, cmd->sense_buf);
+ ent->rsp.scsi_status = tcmu_sts_to_scsi(dev, result, cmd->sense_buf,
+ cmd->cdb);
if (ent->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) {
memcpy(ent->rsp.sense_buffer, cmd->sense_buf,
TCMU_SENSE_BUFFERSIZE);
diff --git a/libtcmu.h b/libtcmu.h
index f49de5ff..0598d01c 100644
--- a/libtcmu.h
+++ b/libtcmu.h
@@ -124,6 +124,13 @@ void tcmulib_processing_complete(struct tcmu_device *dev);
/* Clean up loose ends when exiting */
void tcmulib_close(struct tcmulib_context *ctx);
+/*
+ * Get a tcmu_device by its tcmu name. tcmu_name is the string found in:
+ * /sys/kernel/config/target/core/user_$N/$tcmu_name
+ */
+struct tcmu_device *tcmulib_lookup_dev_by_tcmu_name(struct tcmulib_context *ctx,
+ char *tcmu_name);
+
#ifdef __cplusplus
}
#endif
diff --git a/libtcmu_common.h b/libtcmu_common.h
index 1dfffe7e..c70e2cb1 100644
--- a/libtcmu_common.h
+++ b/libtcmu_common.h
@@ -33,6 +33,8 @@ enum {
TCMU_STS_NO_RESOURCE,
/* handler has setup sense. */
TCMU_STS_PASSTHROUGH_ERR,
+ /* UA code has setup the sense */
+ TCMU_STS_UNIT_ATTENTION,
TCMU_STS_BUSY,
TCMU_STS_WR_ERR,
TCMU_STS_RD_ERR,
@@ -46,7 +48,6 @@ enum {
TCMU_STS_HW_ERR,
TCMU_STS_RANGE,
TCMU_STS_FRMT_IN_PROGRESS,
- TCMU_STS_CAPACITY_CHANGED,
TCMU_STS_NOTSUPP_SAVE_PARAMS,
TCMU_STS_WR_ERR_INCOMPAT_FRMT,
TCMU_STS_TRANSITION,
@@ -151,13 +152,14 @@ int tcmu_set_cfgfs_str(const char *path, const char *val, int val_len);
int tcmu_get_cfgfs_int(const char *path);
int tcmu_set_cfgfs_ul(const char *path, unsigned long val);
int tcmu_get_attribute(struct tcmu_device *dev, const char *name);
+int tcmu_set_cfgfs_ctrl_str(struct tcmu_device *dev, const char *key,
+ const char *val);
bool tcmu_cfgfs_file_is_supported(struct tcmu_device *dev, const char *name);
int tcmu_exec_cfgfs_dev_action(struct tcmu_device *dev, const char *name,
unsigned long val);
int tcmu_set_dev_size(struct tcmu_device *dev);
long long tcmu_get_dev_size(struct tcmu_device *dev);
char *tcmu_get_wwn(struct tcmu_device *dev);
-int tcmu_set_control(struct tcmu_device *dev, const char *key, unsigned long val);
void tcmu_reset_netlink(void);
void tcmu_block_netlink(void);
void tcmu_unblock_netlink(void);
diff --git a/main.c b/main.c
index f2e65bf1..25d5a731 100644
--- a/main.c
+++ b/main.c
@@ -74,46 +74,6 @@ int tcmur_register_handler(struct tcmur_handler *handler)
return 0;
}
-static int tcmur_register_dbus_handler(struct tcmur_handler *handler)
-{
- assert(handler->_is_dbus_handler == true);
- return tcmur_register_handler(handler);
-}
-
-bool tcmur_unregister_handler(struct tcmur_handler *handler)
-{
- int i;
- for (i = 0; i < darray_size(g_runner_handlers); i++) {
- if (darray_item(g_runner_handlers, i) == handler) {
- darray_remove(g_runner_handlers, i);
- return true;
- }
- }
- return false;
-}
-
-static void free_dbus_handler(struct tcmur_handler *handler)
-{
- g_free((char*)handler->opaque);
- g_free((char*)handler->subtype);
- g_free((char*)handler->cfg_desc);
- g_free(handler);
-}
-
-static bool tcmur_unregister_dbus_handler(struct tcmur_handler *handler)
-{
- bool ret = false;
- assert(handler->_is_dbus_handler == true);
-
- ret = tcmur_unregister_handler(handler);
-
- if (ret == true) {
- free_dbus_handler(handler);
- }
-
- return ret;
-}
-
static int is_handler(const struct dirent *dirent)
{
if (strncmp(dirent->d_name, "handler_", 8))
@@ -229,8 +189,62 @@ on_check_config(TCMUService1 *interface,
return TRUE;
}
+static gboolean
+on_change_medium(TCMUService1 *interface,
+ GDBusMethodInvocation *invocation,
+ gchar *name,
+ guint64 size,
+ gchar *cfgstring,
+ gpointer user_data)
+{
+ struct tcmulib_context *ctx = user_data;
+ struct tcmur_handler *rhandler;
+ struct tcmu_device *dev = NULL;
+ char *reason = NULL;
+ int ret = 0;
+
+ dev = tcmulib_lookup_dev_by_tcmu_name(ctx, name);
+ if (!dev) {
+ ret = ENODEV;
+ reason = "Device not found.";
+ goto exit;
+ }
+
+ rhandler = tcmu_get_runner_handler(dev);
+ if (!rhandler->medium_change_supp) {
+ ret = EOPNOTSUPP;
+ reason = "Handler does not support medium changes.";
+ goto exit;
+ }
+
+ tcmu_block_device(dev);
+ tcmu_flush_device(dev);
+
+ tcmu_set_cfgfs_ctrl_str(dev, "dev_config", cfgstring);
+ tcmu_update_num_lbas(dev, size);
+ tcmu_set_dev_size(dev);
+
+ ret = tcmu_reopen_dev(dev, false, 0);
+ if (ret == 0) {
+ reason = "success";
+ } else {
+ reason = strerror(ret);
+ }
+
+ tcmu_dev_set_pending_ua(dev, TCMUR_UA_DEV_MEDIUM_CHANGED);
+ tcmu_unblock_device(dev);
+
+exit:
+ g_dbus_method_invocation_return_value(invocation,
+ g_variant_new("(is)", ret, reason));
+
+ return TRUE;
+}
+
static void
-dbus_export_handler(struct tcmur_handler *handler, GCallback check_config)
+dbus_export_handler(struct tcmulib_context *tcmulib_context,
+ struct tcmur_handler *handler, GCallback check_config,
+ GCallback change_medium)
{
GDBusObjectSkeleton *object;
char obj_name[128];
@@ -245,204 +259,31 @@ dbus_export_handler(struct tcmur_handler *handler, GCallback check_config)
"handle-check-config",
check_config,
handler); /* user_data */
+ g_signal_connect(interface,
+ "handle-change-medium",
+ change_medium,
+ tcmulib_context);
tcmuservice1_set_config_desc(interface, handler->cfg_desc);
g_dbus_object_manager_server_export(manager, G_DBUS_OBJECT_SKELETON(object));
g_object_unref(object);
}
-static bool
-dbus_unexport_handler(struct tcmur_handler *handler)
-{
- char obj_name[128];
-
- snprintf(obj_name, sizeof(obj_name), "/org/kernel/TCMUService1/%s",
- handler->subtype);
- return g_dbus_object_manager_server_unexport(manager, obj_name) == TRUE;
-}
-
-struct dbus_info {
- guint watcher_id;
- /* The RegisterHandler invocation on
- * org.kernel.TCMUService1.HandlerManager1 interface. */
- GDBusMethodInvocation *register_invocation;
- /* Connection to the handler's bus_name. */
- GDBusConnection *connection;
-};
-
-static int dbus_handler_open(struct tcmu_device *dev, bool reopen)
-{
- return -1;
-}
-
-static void dbus_handler_close(struct tcmu_device *dev)
-{
- /* nop */
-}
-
-static int dbus_handler_handle_cmd(struct tcmu_device *dev,
- struct tcmulib_cmd *cmd)
-{
- abort();
-}
-
-static gboolean
-on_dbus_check_config(TCMUService1 *interface,
- GDBusMethodInvocation *invocation,
- gchar *cfgstring,
- gpointer user_data)
-{
- char *bus_name, *obj_name;
- struct tcmur_handler *handler = user_data;
- GDBusConnection *connection;
- GError *error = NULL;
- GVariant *result;
-
- bus_name = g_strdup_printf("org.kernel.TCMUService1.HandlerManager1.%s",
- handler->subtype);
- obj_name = g_strdup_printf("/org/kernel/TCMUService1/HandlerManager1/%s",
- handler->subtype);
- connection = g_dbus_method_invocation_get_connection(invocation);
- result = g_dbus_connection_call_sync(connection,
- bus_name,
- obj_name,
- "org.kernel.TCMUService1",
- "CheckConfig",
- g_variant_new("(s)", cfgstring),
- NULL, G_DBUS_CALL_FLAGS_NONE, -1,
- NULL, &error);
- if (result)
- g_dbus_method_invocation_return_value(invocation, result);
- else
- g_dbus_method_invocation_return_value(invocation,
- g_variant_new("(bs)", FALSE, error->message));
- g_free(bus_name);
- g_free(obj_name);
- return TRUE;
-}
-
-static void
-on_handler_appeared(GDBusConnection *connection,
- const gchar *name,
- const gchar *name_owner,
- gpointer user_data)
-{
- struct tcmur_handler *handler = user_data;
- struct dbus_info *info = handler->opaque;
-
- if (info->register_invocation) {
- info->connection = connection;
- tcmur_register_dbus_handler(handler);
- dbus_export_handler(handler, G_CALLBACK(on_dbus_check_config));
- g_dbus_method_invocation_return_value(info->register_invocation,
- g_variant_new("(bs)", TRUE, "succeeded"));
- info->register_invocation = NULL;
- }
-}
-
-static void
-on_handler_vanished(GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
- struct tcmur_handler *handler = user_data;
- struct dbus_info *info = handler->opaque;
-
- if (info->register_invocation) {
- char *reason;
- reason = g_strdup_printf("Cannot find handler bus name: "
- "org.kernel.TCMUService1.HandlerManager1.%s",
- handler->subtype);
- g_dbus_method_invocation_return_value(info->register_invocation,
- g_variant_new("(bs)", FALSE, reason));
- g_free(reason);
- }
- dbus_unexport_handler(handler);
- g_bus_unwatch_name(info->watcher_id);
- tcmur_unregister_dbus_handler(handler);
-}
-
-static gboolean
-on_register_handler(TCMUService1HandlerManager1 *interface,
- GDBusMethodInvocation *invocation,
- gchar *subtype,
- gchar *cfg_desc,
- gpointer user_data)
-{
- struct tcmur_handler *handler;
- struct dbus_info *info;
- char *bus_name;
-
- bus_name = g_strdup_printf("org.kernel.TCMUService1.HandlerManager1.%s",
- subtype);
-
- handler = g_new0(struct tcmur_handler, 1);
- handler->subtype = g_strdup(subtype);
- handler->cfg_desc = g_strdup(cfg_desc);
- handler->open = dbus_handler_open;
- handler->close = dbus_handler_close;
- handler->handle_cmd = dbus_handler_handle_cmd;
-
- info = g_new0(struct dbus_info, 1);
- handler->opaque = info;
- handler->_is_dbus_handler = 1;
- info->register_invocation = invocation;
- info->watcher_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM,
- bus_name,
- G_BUS_NAME_WATCHER_FLAGS_NONE,
- on_handler_appeared,
- on_handler_vanished,
- handler,
- NULL);
- if (info->watcher_id == 0) {
- // probably an invalid name, roll back and report an error
- free_dbus_handler(handler);
-
- g_dbus_method_invocation_return_value(invocation,
- g_variant_new("(bs)", FALSE,
- "failed to watch for DBus handler name"));
- }
- g_free(bus_name);
- return TRUE;
-}
-
-void dbus_handler_manager1_init(GDBusConnection *connection)
-{
- GError *error = NULL;
- TCMUService1HandlerManager1 *interface;
- gboolean ret;
-
- interface = tcmuservice1_handler_manager1_skeleton_new();
- ret = g_dbus_interface_skeleton_export(
- G_DBUS_INTERFACE_SKELETON(interface),
- connection,
- "/org/kernel/TCMUService1/HandlerManager1",
- &error);
- g_signal_connect(interface,
- "handle-register-handler",
- G_CALLBACK (on_register_handler),
- NULL);
- if (!ret)
- tcmu_err("Handler manager export failed: %s\n",
- error ? error->message : "unknown error");
- if (error)
- g_error_free(error);
-}
-
static void dbus_bus_acquired(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
struct tcmur_handler **handler;
- tcmu_dbg("bus %s acquired\n", name);
+ tcmu_dbg("bus %s acquired %p\n", name, user_data);
manager = g_dbus_object_manager_server_new("/org/kernel/TCMUService1");
darray_foreach(handler, g_runner_handlers) {
- dbus_export_handler(*handler, G_CALLBACK(on_check_config));
+ dbus_export_handler(user_data, *handler,
+ G_CALLBACK(on_check_config),
+ G_CALLBACK(on_change_medium));
}
- dbus_handler_manager1_init(connection);
g_dbus_object_manager_server_set_connection(manager, connection);
}
@@ -701,7 +542,7 @@ static int dev_resize(struct tcmu_device *dev, struct tcmulib_cfg_info *cfg)
ret = tcmu_update_num_lbas(dev, cfg->data.dev_size);
if (!ret)
- tcmur_set_pending_ua(dev, TCMUR_UA_DEV_SIZE_CHANGED);
+ tcmu_dev_set_pending_ua(dev, TCMUR_UA_DEV_SIZE_CHANGED);
return ret;
}
@@ -1121,7 +962,7 @@ int main(int argc, char **argv)
dbus_bus_acquired,
dbus_name_acquired, // name acquired
dbus_name_lost, // name lost
- NULL, // user data
+ tcmulib_context, // user data
NULL // user date free func
);
diff --git a/tcmu-handler.xml b/tcmu-handler.xml
index 933d671c..4824eccf 100644
--- a/tcmu-handler.xml
+++ b/tcmu-handler.xml
@@ -16,6 +16,13 @@ that the configstring is valid before the device has been created.
+
+
+
+
+
+
+
diff --git a/tcmu-runner.h b/tcmu-runner.h
index 47cde0d7..d09c08d9 100644
--- a/tcmu-runner.h
+++ b/tcmu-runner.h
@@ -59,6 +59,11 @@ struct tcmur_handler {
int (*reconfig)(struct tcmu_device *dev, struct tcmulib_cfg_info *cfg);
+ /*
+ * True if handler supports medium changes via reopen calls.
+ */
+ bool medium_change_supp;
+
/* Per-device added/removed callbacks */
int (*open)(struct tcmu_device *dev, bool reopen);
void (*close)(struct tcmu_device *dev);
@@ -125,15 +130,6 @@ struct tcmur_handler {
* indicating success/failure.
*/
int (*get_lock_tag)(struct tcmu_device *dev, uint16_t *tag);
-
- /*
- * internal field, don't touch this
- *
- * indicates to tcmu-runner whether this is an internal handler loaded
- * via dlopen or an external handler registered via dbus. In the
- * latter case opaque will point to a struct dbus_info.
- */
- bool _is_dbus_handler;
};
/*
@@ -147,7 +143,6 @@ struct tcmur_handler {
* APIs for tcmur only
*/
int tcmur_register_handler(struct tcmur_handler *handler);
-bool tcmur_unregister_handler(struct tcmur_handler *handler);
/*
* Misc
diff --git a/tcmur_cmd_handler.c b/tcmur_cmd_handler.c
index a693f82e..93c0c536 100644
--- a/tcmur_cmd_handler.c
+++ b/tcmur_cmd_handler.c
@@ -2134,6 +2134,10 @@ int tcmur_cmd_passthrough_handler(struct tcmu_device *dev,
if (!rhandler->handle_cmd)
return TCMU_STS_NOT_HANDLED;
+ ret = tcmu_dev_handle_pending_ua(dev, cmd);
+ if (ret != TCMU_STS_NOT_HANDLED)
+ return ret;
+
/*
* Support handlers that implement their own threading/AIO
* and only use runner's main event loop.
@@ -2359,52 +2363,9 @@ int tcmur_dev_update_size(struct tcmu_device *dev, unsigned long new_size)
if (ret)
tcmu_update_num_lbas(dev, old_size); /* Rolling back */
else
- tcmur_set_pending_ua(dev, TCMUR_UA_DEV_SIZE_CHANGED);
- }
-
- return ret;
-}
-
-void tcmur_set_pending_ua(struct tcmu_device *dev, int ua)
-{
- struct tcmur_device *rdev = tcmu_get_daemon_dev_private(dev);
-
- pthread_mutex_lock(&rdev->state_lock);
- rdev->pending_uas |= (1 << ua);
- pthread_mutex_unlock(&rdev->state_lock);
-}
-
-/*
- * TODO - coordinate with the kernel.
- */
-static int handle_pending_ua(struct tcmur_device *rdev, struct tcmulib_cmd *cmd)
-{
- uint8_t *cdb = cmd->cdb;
- int ret = TCMU_STS_NOT_HANDLED, ua;
-
- switch (cdb[0]) {
- case INQUIRY:
- case REQUEST_SENSE:
- /* The kernel will handle REPORT_LUNS */
- return TCMU_STS_NOT_HANDLED;
- }
- pthread_mutex_lock(&rdev->state_lock);
-
- if (!rdev->pending_uas) {
- ret = TCMU_STS_NOT_HANDLED;
- goto unlock;
- }
-
- ua = ffs(rdev->pending_uas) - 1;
- switch (ua) {
- case TCMUR_UA_DEV_SIZE_CHANGED:
- ret = TCMU_STS_CAPACITY_CHANGED;
- break;
+ tcmu_dev_set_pending_ua(dev, TCMUR_UA_DEV_SIZE_CHANGED);
}
- rdev->pending_uas &= ~(1 << ua);
-unlock:
- pthread_mutex_unlock(&rdev->state_lock);
return ret;
}
@@ -2413,7 +2374,7 @@ int tcmur_generic_handle_cmd(struct tcmu_device *dev, struct tcmulib_cmd *cmd)
struct tcmur_device *rdev = tcmu_get_daemon_dev_private(dev);
int ret;
- ret = handle_pending_ua(rdev, cmd);
+ ret = tcmu_dev_handle_pending_ua(dev, cmd);
if (ret != TCMU_STS_NOT_HANDLED)
return ret;
diff --git a/tcmur_device.c b/tcmur_device.c
index efbbc755..9f4e9aa4 100644
--- a/tcmur_device.c
+++ b/tcmur_device.c
@@ -21,6 +21,7 @@
#include "tcmur_device.h"
#include "tcmur_cmd_handler.h"
#include "target.h"
+#include "scsi/scsi.h"
bool tcmu_dev_in_recovery(struct tcmu_device *dev)
{
@@ -387,3 +388,56 @@ int tcmu_acquire_dev_lock(struct tcmu_device *dev, bool is_sync,
return ret;
}
+
+void tcmu_dev_set_pending_ua(struct tcmu_device *dev, int ua)
+{
+ struct tcmur_device *rdev = tcmu_get_daemon_dev_private(dev);
+
+ pthread_mutex_lock(&rdev->state_lock);
+ rdev->pending_uas |= (1 << ua);
+ pthread_mutex_unlock(&rdev->state_lock);
+}
+
+/*
+ * TODO - coordinate with the kernel.
+ */
+int tcmu_dev_handle_pending_ua(struct tcmu_device *dev, struct tcmulib_cmd *cmd)
+{
+ struct tcmur_device *rdev = tcmu_get_daemon_dev_private(dev);
+ uint8_t *cdb = cmd->cdb;
+ int ret = TCMU_STS_NOT_HANDLED, ua;
+
+ switch (cdb[0]) {
+ case INQUIRY:
+ case REQUEST_SENSE:
+ /* The kernel will handle REPORT_LUNS */
+ return TCMU_STS_NOT_HANDLED;
+ }
+ pthread_mutex_lock(&rdev->state_lock);
+
+ if (!rdev->pending_uas) {
+ ret = TCMU_STS_NOT_HANDLED;
+ goto unlock;
+ }
+
+ ua = ffs(rdev->pending_uas) - 1;
+ switch (ua) {
+ case TCMUR_UA_DEV_SIZE_CHANGED:
+ /* Device capacity has changed */
+ tcmu_set_sense_data(cmd->sense_buf, UNIT_ATTENTION, 0x2A09);
+ break;
+ case TCMUR_UA_DEV_MEDIUM_CHANGED:
+ /* medium changed */
+ tcmu_set_sense_data(cmd->sense_buf, UNIT_ATTENTION, 0x2800);
+ break;
+ default:
+ tcmu_dev_err(dev, "Unhandled UA %d\n", ua);
+ goto unlock;
+ }
+ ret = TCMU_STS_UNIT_ATTENTION;
+ rdev->pending_uas &= ~(1 << ua);
+
+unlock:
+ pthread_mutex_unlock(&rdev->state_lock);
+ return ret;
+}
diff --git a/tcmur_device.h b/tcmur_device.h
index 128a8ba8..f72af914 100644
--- a/tcmur_device.h
+++ b/tcmur_device.h
@@ -22,6 +22,7 @@
#define TCMUR_DEV_FLAG_STOPPED (1 << 4)
#define TCMUR_UA_DEV_SIZE_CHANGED 0
+#define TCMUR_UA_DEV_MEDIUM_CHANGED 1
enum {
TMCUR_DEV_FAILOVER_ALL_ACTIVE,
@@ -84,4 +85,7 @@ int tcmu_acquire_dev_lock(struct tcmu_device *dev, bool is_sync, uint16_t tag);
void tcmu_release_dev_lock(struct tcmu_device *dev);
int tcmu_get_lock_tag(struct tcmu_device *dev, uint16_t *tag);
+void tcmu_dev_set_pending_ua(struct tcmu_device *dev, int ua);
+int tcmu_dev_handle_pending_ua(struct tcmu_device *dev, struct tcmulib_cmd *cmd)
+;
#endif