diff --git a/CMakeLists.txt b/CMakeLists.txt index 21a4fe7a..8c2461b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ add_library(tcmu libtcmu_log.c libtcmu_config.c libtcmu_time.c + libtcmu_timer.c ) set_target_properties(tcmu PROPERTIES @@ -75,6 +76,7 @@ add_library(tcmu_static libtcmu_log.c libtcmu_config.c libtcmu_time.c + libtcmu_timer.c ) target_include_directories(tcmu_static PUBLIC ${LIBNL_INCLUDE_DIR} diff --git a/ccan/ccan/list/list.h b/ccan/ccan/list/list.h index 9ac01c69..33b6e7de 100644 --- a/ccan/ccan/list/list.h +++ b/ccan/ccan/list/list.h @@ -306,6 +306,58 @@ static inline bool list_empty_nocheck(const struct list_head *h) return h->n.next == &h->n; } +static inline void __list_splice(struct list_head *list, struct list_head *head) +{ + list->n.prev->next = head->n.next; + head->n.next->prev = list->n.prev; + + head->n.next = list->n.next; + list->n.next->prev = &head->n; +} + +static inline void +list_splice(struct list_head *list, struct list_head *head) +{ + if (list_empty(list)) + return; + + __list_splice(list, head); +} + +/* Splice moves @list to the head of the list at @head. */ +static inline void +list_splice_init(struct list_head *list, struct list_head *head) +{ + if (list_empty(list)) + return; + + __list_splice(list, head); + list_head_init(list); +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void +list_replace(struct list_head *old, struct list_head *new) +{ + new->n.next = old->n.next; + new->n.next->prev = &new->n; + new->n.prev = old->n.prev; + new->n.prev->next = &new->n; +} + +static inline void +list_replace_init(struct list_head *old, struct list_head *new) +{ + list_replace(old, new); + list_head_init(old); +} + /** * list_del - delete an entry from an (unknown) linked list. * @n: the list_node to delete from the list. diff --git a/libtcmu.c b/libtcmu.c index 1f4d7293..c4e3a041 100644 --- a/libtcmu.c +++ b/libtcmu.c @@ -6,6 +6,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -535,6 +536,9 @@ static int add_device(struct tcmulib_context *ctx, char *dev_name, } darray_append(ctx->devices, dev); + pthread_mutex_lock(&pending_cmds_lock); + list_add_tail(&pending_cmds_head, &dev->entry); + pthread_mutex_unlock(&pending_cmds_lock); if (reopen && reset_supp) tcmu_cfgfs_dev_exec_action(dev, "block_dev", 0); @@ -588,6 +592,9 @@ static void remove_device(struct tcmulib_context *ctx, char *dev_name, } darray_remove(ctx->devices, i); + pthread_mutex_lock(&pending_cmds_lock); + list_del_init(&dev->entry); + pthread_mutex_unlock(&pending_cmds_lock); dev->handler->removed(dev); @@ -964,6 +971,9 @@ struct tcmulib_cmd *tcmulib_get_next_command(struct tcmu_device *dev) if (!cmd) return NULL; cmd->cmd_id = ent->hdr.cmd_id; + cmd->dev = dev; + cmd->timer = NULL; + cmd->timeout = 0; /* Convert iovec addrs in-place to not be offsets */ cmd->iov_cnt = ent->req.iov_cnt; @@ -1124,6 +1134,11 @@ void tcmulib_command_complete( struct tcmu_mailbox *mb = dev->map; struct tcmu_cmd_entry *ent = (void *) mb + mb->cmdr_off + mb->cmd_tail; + if (cmd->timeout) { + dev->timeout_cmds[cmd->timeout / CMD_TO_STEP - 1]--; + pthread_cond_signal(&pending_cmds_cond); + } + /* current command could be PAD in async case */ while (ent != (void *) mb + mb->cmdr_off + mb->cmd_head) { if (tcmu_hdr_get_op(ent->hdr.len_op) == TCMU_OP_CMD) @@ -1144,6 +1159,9 @@ void tcmulib_command_complete( } TCMU_UPDATE_RB_TAIL(mb, ent); + + if (cmd->timer) + free(cmd->timer); free(cmd); } diff --git a/libtcmu_common.h b/libtcmu_common.h index 35e854ea..eab31a45 100644 --- a/libtcmu_common.h +++ b/libtcmu_common.h @@ -111,6 +111,10 @@ struct tcmulib_cmd { /* callback to finish/continue command processing */ cmd_done_t done; + + struct tcmu_device *dev; + struct tcmu_timer *timer; + uint16_t timeout; }; /* Set/Get methods for the opaque tcmu_device */ diff --git a/libtcmu_log.c b/libtcmu_log.c index 8ad97ae4..d7f24be6 100644 --- a/libtcmu_log.c +++ b/libtcmu_log.c @@ -220,7 +220,7 @@ log_internal(int pri, struct tcmu_device *dev, const char *funcname, int linenr, const char *fmt, va_list args) { char buf[LOG_MSG_LEN]; - int n; + int n = 0; struct tcmulib_handler *handler; if (pri > tcmu_log_level) @@ -236,13 +236,14 @@ log_internal(int pri, struct tcmu_device *dev, const char *funcname, } /* Format the log msg */ + if (funcname) + n = sprintf(buf, "%s:%d: ", funcname, linenr); + if (dev) { handler = tcmu_dev_get_handler(dev); - n = sprintf(buf, "%s:%d %s/%s: ", funcname, linenr, - handler ? handler->subtype: "", - dev ? dev->tcm_dev_name: ""); - } else { - n = sprintf(buf, "%s:%d: ", funcname, linenr); + n += sprintf(buf + n, "%s/%s: ", + handler ? handler->subtype: "", + dev ? dev->tcm_dev_name: ""); } vsnprintf(buf + n, LOG_MSG_LEN - n, fmt, args); @@ -514,6 +515,34 @@ static bool log_dequeue_msg(struct log_buf *logbuf) return true; } +pthread_cond_t pending_cmds_cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t pending_cmds_lock = PTHREAD_MUTEX_INITIALIZER; +struct list_head pending_cmds_head = LIST_HEAD_INIT(pending_cmds_head); +static pthread_t pending_thread_id; +static void *log_thread_pending_start(void *arg) +{ + struct tcmu_device *dev, *tmp; + + while (1) { + pthread_mutex_lock(&pending_cmds_lock); + pthread_cond_wait(&pending_cmds_cond, &pending_cmds_lock); + list_for_each_safe(&pending_cmds_head, dev, tmp, entry) { + tcmu_dev_warn_simple(dev, "Pending cmds: 180(+)s:[%lu]," + " 150s:[%lu], 120s:[%lu], 90s:[%lu], 60s:[%lu]," + " 30s:[%lu]\n", + dev->timeout_cmds[CMD_TO_180SEC / CMD_TO_STEP - 1], + dev->timeout_cmds[CMD_TO_150SEC / CMD_TO_STEP - 1], + dev->timeout_cmds[CMD_TO_120SEC / CMD_TO_STEP - 1], + dev->timeout_cmds[CMD_TO_90SEC / CMD_TO_STEP - 1], + dev->timeout_cmds[CMD_TO_60SEC / CMD_TO_STEP - 1], + dev->timeout_cmds[CMD_TO_30SEC / CMD_TO_STEP - 1]); + } + pthread_mutex_unlock(&pending_cmds_lock); + } + + return NULL; +} + static void *log_thread_start(void *arg) { tcmu_logbuf = arg; @@ -694,6 +723,13 @@ int tcmu_setup_log(char *log_dir) return ret; } + ret = pthread_create(&pending_thread_id, NULL, log_thread_pending_start, + NULL); + if (ret) { + pthread_cancel(logbuf->thread_id); + return ret; + } + return 0; free_log_dir: diff --git a/libtcmu_log.h b/libtcmu_log.h index c2924e22..dfa811d7 100644 --- a/libtcmu_log.h +++ b/libtcmu_log.h @@ -26,6 +26,9 @@ struct tcmu_device; struct tcmu_config; +extern struct list_head pending_cmds_head; +extern pthread_cond_t pending_cmds_cond; +extern pthread_mutex_t pending_cmds_lock; void tcmu_set_log_level(int level); unsigned int tcmu_get_log_level(void); @@ -50,6 +53,7 @@ void tcmu_dbg_scsi_cmd_message(struct tcmu_device *dev, const char *funcname, in #define tcmu_dev_crit(dev, ...) do { tcmu_crit_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) #define tcmu_dev_err(dev, ...) do { tcmu_err_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) #define tcmu_dev_warn(dev, ...) do { tcmu_warn_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) +#define tcmu_dev_warn_simple(dev, ...) do { tcmu_warn_message(dev, NULL, 0, __VA_ARGS__);} while (0) #define tcmu_dev_info(dev, ...) do { tcmu_info_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) #define tcmu_dev_dbg(dev, ...) do { tcmu_dbg_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) #define tcmu_dev_dbg_scsi_cmd(dev, ...) do { tcmu_dbg_scsi_cmd_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) diff --git a/libtcmu_priv.h b/libtcmu_priv.h index 5049b100..a5d70773 100644 --- a/libtcmu_priv.h +++ b/libtcmu_priv.h @@ -20,6 +20,7 @@ #include #include "darray.h" +#include "ccan/list/list.h" #define KERN_IFACE_VER 2 @@ -35,6 +36,15 @@ struct tcmulib_context { GDBusConnection *connection; }; +#define CMD_TO_30SEC 30 +#define CMD_TO_60SEC 60 +#define CMD_TO_90SEC 90 +#define CMD_TO_120SEC 120 +#define CMD_TO_150SEC 150 +#define CMD_TO_180SEC 180 +#define CMD_TO_STEP 30 +#define CMD_TO_COUNT 6 + struct tcmu_device { int fd; @@ -63,6 +73,9 @@ struct tcmu_device { struct tcmulib_handler *handler; struct tcmulib_context *ctx; + uint64_t timeout_cmds[CMD_TO_COUNT]; + struct list_node entry; + void *hm_private; /* private ptr for handler module */ }; diff --git a/libtcmu_timer.c b/libtcmu_timer.c new file mode 100644 index 00000000..1090193f --- /dev/null +++ b/libtcmu_timer.c @@ -0,0 +1,367 @@ +/* + * tcmu timer wheel + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (c) 2018 Red Hat, Inc. + * + * This file is licensed to you under your choice of the GNU Lesser + * General Public License, version 2.1 or any later version (LGPLv2.1 or + * later), or the Apache License 2.0. + * + * Most of the code is from glusterfs project and which is from Linux + * kernel's internal timer wheel driver. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "libtcmu_timer.h" +#include "tcmu-runner.h" + +#define TVR_BITS 8 +#define TVN_BITS 6 +#define TVR_SIZE (1 << TVR_BITS) +#define TVN_SIZE (1 << TVN_BITS) +#define TVR_MASK (TVR_SIZE - 1) +#define TVN_MASK (TVN_SIZE - 1) + +#define BITS_PER_LONG 64 + +struct tvec { + struct list_head vec[TVN_SIZE]; +}; + +struct tvec_root { + struct list_head vec[TVR_SIZE]; +}; + +struct tvec_base { + pthread_t runner; /* run_timer() */ + + unsigned long timer_sec; /* time counter */ + + struct tvec_root tv1; + struct tvec tv2; + struct tvec tv3; + struct tvec tv4; + struct tvec tv5; +}; + +static struct tvec_base *timer_base; +static pthread_spinlock_t timer_base_lock; /* base lock */ + +static inline void __tcmu_add_timer (struct tcmu_timer *timer) +{ + int i; + unsigned long idx; + unsigned long expires; + struct list_head *vec; + + expires = timer->expires; + + idx = expires - timer_base->timer_sec; + + if (idx < TVR_SIZE) { + i = expires & TVR_MASK; + vec = timer_base->tv1.vec + i; + } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { + i = (expires >> TVR_BITS) & TVN_MASK; + vec = timer_base->tv2.vec + i; + } else if (idx < 1 << (TVR_BITS + 2*TVN_BITS)) { + i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; + vec = timer_base->tv3.vec + i; + } else if (idx < 1 << (TVR_BITS + 3*TVN_BITS)) { + i = (expires >> (TVR_BITS + 2*TVN_BITS)) & TVN_MASK; + vec = timer_base->tv4.vec + i; + } else if (idx < 0) { + vec = timer_base->tv1.vec + (timer_base->timer_sec & TVR_MASK); + } else { + i = (expires >> (TVR_BITS + 3*TVN_BITS)) & TVN_MASK; + vec = timer_base->tv5.vec + i; + } + + list_add_tail(vec, &timer->entry); +} + +static inline unsigned long tcmu_fls(unsigned long word) +{ + return BITS_PER_LONG - __builtin_clzl(word); +} + +static inline unsigned long apply_slack(struct tcmu_timer *timer) +{ + long delta; + unsigned long mask, expires, expires_limit; + int bit; + + expires = timer->expires; + + delta = expires - timer_base->timer_sec; + if (delta < 256) + return expires; + + expires_limit = expires + delta / 256; + mask = expires ^ expires_limit; + if (mask == 0) + return expires; + + bit = tcmu_fls(mask); + mask = (1UL << bit) - 1; + + expires_limit = expires_limit & ~(mask); + return expires_limit; +} + +static inline int cascade(struct tvec *tv, int index) +{ + struct tcmu_timer *timer, *tmp; + struct list_head tv_list; + + list_replace_init(tv->vec + index, &tv_list); + + list_for_each_safe(&tv_list, tmp, timer, entry) { + __tcmu_add_timer(timer); + } + + return index; +} + +#define INDEX(N) ((timer_base->timer_sec >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK) + +/** + * run expired timers + */ +static inline void run_timers(void) +{ + unsigned long index; + struct tcmu_timer *timer; + struct list_head work_list; + struct list_head *head = &work_list; + + pthread_spin_lock(&timer_base_lock); + + index = timer_base->timer_sec & TVR_MASK; + + if (!index && (!cascade(&timer_base->tv2, INDEX(0))) && + (!cascade(&timer_base->tv3, INDEX(1))) && + (!cascade(&timer_base->tv4, INDEX(2)))) + cascade(&timer_base->tv5, INDEX(3)); + + timer_base->timer_sec++; + list_replace_init(timer_base->tv1.vec + index, head); + while (!list_empty(head)) { + void (*fn)(struct tcmu_timer *, void *); + void *data; + + timer = list_first_entry(head, struct tcmu_timer, entry); + fn = timer->function; + data = timer->data; + + list_del_init(&timer->entry); + pthread_spin_unlock(&timer_base_lock); + fn(timer, data); + pthread_spin_lock(&timer_base_lock); + } + + pthread_spin_unlock(&timer_base_lock); +} + +void *runner(void *arg) +{ + struct timeval tv = {0,}; + + while(1) { + run_timers(); + + tv.tv_sec = 1; + tv.tv_usec = 0; + select(0, NULL, NULL, NULL, &tv); + } + + return NULL; +} + +static inline int timer_pending(struct tcmu_timer *timer) +{ + struct list_node *entry = &timer->entry; + + return entry->next != entry; +} + +static inline int __detach_if_pending(struct tcmu_timer *timer) +{ + if (!timer_pending(timer)) + return 0; + + list_del_init(&timer->entry); + return 1; +} + +static inline int __mod_timer(struct tcmu_timer *timer, int pending_only) +{ + int ret = 0; + + ret = __detach_if_pending(timer); + if (!ret && pending_only) + goto done; + + ret = 1; + __tcmu_add_timer(timer); + +done: + return ret; +} + +/* interface */ + +/** + * Add a timer in the timer wheel + */ +int tcmu_add_timer(struct tcmu_timer *timer) +{ + pthread_spin_lock(&timer_base_lock); + + timer->expires += timer_base->timer_sec; + timer->expires = apply_slack(timer); + __tcmu_add_timer(timer); + + pthread_spin_unlock(&timer_base_lock); + + return 0; +} + +/** + * Remove a timer from the timer wheel + */ +int tcmu_del_timer(struct tcmu_timer *timer) +{ + int ret = 0; + + pthread_spin_lock(&timer_base_lock); + + if (timer_pending(timer)) { + ret = 1; + list_del_init(&timer->entry); + } + + pthread_spin_unlock(&timer_base_lock); + + return ret; +} + +int tcmu_mod_timer_pending(struct tcmu_timer *timer, + unsigned long expires) +{ + int ret = 1; + + pthread_spin_lock(&timer_base_lock); + + timer->expires = expires + timer_base->timer_sec; + timer->expires = apply_slack(timer); + ret = __mod_timer(timer, 1); + + pthread_spin_unlock(&timer_base_lock); + + return ret; +} + +int tcmu_mod_timer(struct tcmu_timer *timer, unsigned long expires) +{ + int ret = 1; + + pthread_spin_lock (&timer_base_lock); + + /* fast path optimization */ + if (timer_pending(timer) && timer->expires == expires) + goto unblock; + + timer->expires = expires + timer_base->timer_sec; + timer->expires = apply_slack(timer); + + ret = __mod_timer(timer, 0); + +unblock: + pthread_spin_unlock(&timer_base_lock); + + return ret; +} + +void tcmu_cleanup_timer_base(void) +{ + int ret = 0; + + pthread_spin_lock(&timer_base_lock); + if (!timer_base) + goto unlock; + + tcmu_thread_cancel(timer_base->runner); + + /* destroy lock */ + if (pthread_spin_destroy(&timer_base_lock) != 0) + tcmu_err("could not cleanup mailbox lock %d\n", ret); + + /* deallocated timer timer_base */ + free(timer_base); +unlock: + pthread_spin_unlock(&timer_base_lock); +} + +/** + * Initialize various timer wheel lists and spawn a thread that + * invokes run_timers() + */ +int tcmu_init_timer_base(void) +{ + struct timeval tv = {0,}; + int ret = 0; + int i = 0; + + if (timer_base) { + tcmu_warn("The timer is already initialized!\n"); + return 0; + } + + timer_base = malloc(sizeof(*timer_base)); + if (!timer_base) { + tcmu_err("malloc timer_base failed!\n"); + return -ENOMEM; + } + + ret = pthread_spin_init(&timer_base_lock, 0); + if (ret != 0) + goto free_base; + + for (i = 0; i < TVN_SIZE; i++) { + list_head_init(timer_base->tv5.vec + i); + list_head_init(timer_base->tv4.vec + i); + list_head_init(timer_base->tv3.vec + i); + list_head_init(timer_base->tv2.vec + i); + } + + for (i = 0; i < TVR_SIZE; i++) { + list_head_init(timer_base->tv1.vec + i); + } + + ret = gettimeofday(&tv, 0); + if (ret < 0) + goto destroy_lock; + timer_base->timer_sec = tv.tv_sec; + + ret = pthread_create(&timer_base->runner, NULL, runner, timer_base); + if (ret != 0) + goto destroy_lock; + + return 0; + +destroy_lock: + pthread_spin_destroy(&timer_base_lock); +free_base: + free(timer_base); + return ret; +} diff --git a/libtcmu_timer.h b/libtcmu_timer.h new file mode 100644 index 00000000..3556f71f --- /dev/null +++ b/libtcmu_timer.h @@ -0,0 +1,33 @@ +/* + * tcmu timer wheel + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This file is licensed to you under your choice of the GNU Lesser + * General Public License, version 2.1 or any later version (LGPLv2.1 or + * later), or the Apache License 2.0. + */ + +#ifndef __LIBTCMU_TIMER_H +#define __LIBTCMU_TIMER_H + +#include +#include "ccan/list/list.h" + +struct tcmu_timer { + void *data; + unsigned long expires; + + void (*function)(struct tcmu_timer *, void *); + + struct list_node entry; +}; + +int tcmu_init_timer_base(void); +void tcmu_cleanup_timer_base(void); +int tcmu_add_timer(struct tcmu_timer *); +int tcmu_del_timer(struct tcmu_timer *); +int tcmu_mod_timer_pending(struct tcmu_timer *, unsigned long); +int tcmu_mod_timer(struct tcmu_timer *, unsigned long); + +#endif diff --git a/main.c b/main.c index eff05e9e..fc76a861 100644 --- a/main.c +++ b/main.c @@ -47,6 +47,7 @@ #include "version.h" #include "libtcmu_config.h" #include "libtcmu_log.h" +#include "libtcmu_timer.h" #define TCMU_LOCK_FILE "/run/tcmu.lock" @@ -1022,6 +1023,12 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + ret = tcmu_init_timer_base(); + if (ret) { + tcmu_err("failed to init tcmu timer base!\n"); + exit(1); + } + while (1) { int option_index = 0; int c, nr_files; @@ -1235,6 +1242,8 @@ int main(int argc, char **argv) if (new_path) free(handler_path); + tcmu_cleanup_timer_base(); + if (ret) exit(EXIT_FAILURE); diff --git a/tcmur_cmd_handler.c b/tcmur_cmd_handler.c index 8153d390..cd848254 100644 --- a/tcmur_cmd_handler.c +++ b/tcmur_cmd_handler.c @@ -21,6 +21,7 @@ #include "libtcmu_log.h" #include "libtcmu_priv.h" #include "libtcmu_common.h" +#include "libtcmu_timer.h" #include "tcmur_aio.h" #include "tcmur_device.h" #include "tcmu-runner.h" @@ -39,10 +40,11 @@ void tcmur_command_complete(struct tcmu_device *dev, struct tcmulib_cmd *cmd, struct tcmur_device *rdev = tcmu_dev_get_private(dev); pthread_cleanup_push(_cleanup_spin_lock, (void *)&rdev->lock); - pthread_spin_lock(&rdev->lock); + if (cmd->timer) + tcmu_del_timer(cmd->timer); + pthread_spin_lock(&rdev->lock); tcmulib_command_complete(dev, cmd, rc); - pthread_spin_unlock(&rdev->lock); pthread_cleanup_pop(0); } @@ -61,6 +63,29 @@ static void aio_command_finish(struct tcmu_device *dev, struct tcmulib_cmd *cmd, } } +void tcmur_cmd_timeout(struct tcmu_timer *timer, void *data) +{ + struct tcmulib_cmd *cmd = (struct tcmulib_cmd *)data; + struct tcmu_device *dev = cmd->dev; + struct tcmur_device *rdev = tcmu_dev_get_private(dev); + + if (cmd->timeout >= CMD_TO_180SEC) + return; + + pthread_spin_lock(&rdev->lock); + if (!cmd->timeout) { + cmd->timeout = CMD_TO_30SEC; + } else { + dev->timeout_cmds[cmd->timeout / CMD_TO_STEP - 1]--; + cmd->timeout += CMD_TO_STEP; + } + dev->timeout_cmds[cmd->timeout / CMD_TO_STEP - 1]++; + pthread_spin_unlock(&rdev->lock); + + tcmu_mod_timer(timer, CMD_TO_STEP); + pthread_cond_signal(&pending_cmds_cond); +} + static int alloc_iovec(struct tcmulib_cmd *cmd, size_t length) { struct iovec *iov; @@ -2218,6 +2243,8 @@ static int tcmur_cmd_handler(struct tcmu_device *dev, struct tcmulib_cmd *cmd) struct tcmur_handler *rhandler = tcmu_get_runner_handler(dev); struct tcmur_device *rdev = tcmu_dev_get_private(dev); uint8_t *cdb = cmd->cdb; + struct tcmu_timer *timer; + track_aio_request_start(rdev); @@ -2254,6 +2281,18 @@ static int tcmur_cmd_handler(struct tcmu_device *dev, struct tcmulib_cmd *cmd) break; } + timer = malloc(sizeof(*timer)); + if (!timer) { + tcmu_dev_err(dev, "malloc timer failed!\n"); + goto untrack; + } + + timer->data = cmd; + timer->expires = CMD_TO_30SEC; + timer->function = tcmur_cmd_timeout; + cmd->timer = timer; + tcmu_add_timer(timer); + switch(cdb[0]) { case READ_6: case READ_10: @@ -2297,8 +2336,11 @@ static int tcmur_cmd_handler(struct tcmu_device *dev, struct tcmulib_cmd *cmd) } untrack: - if (ret != TCMU_STS_ASYNC_HANDLED) + if (ret != TCMU_STS_ASYNC_HANDLED) { + if (timer) + tcmu_del_timer(timer); track_aio_request_finish(rdev, NULL); + } return ret; } diff --git a/tcmur_cmd_handler.h b/tcmur_cmd_handler.h index ad7d25ec..8a3d9039 100644 --- a/tcmur_cmd_handler.h +++ b/tcmur_cmd_handler.h @@ -31,5 +31,6 @@ typedef int (*tcmur_caw_fn_t)(struct tcmu_device *dev, struct tcmulib_cmd *cmd, size_t iov_cnt); int tcmur_handle_caw(struct tcmu_device *dev, struct tcmulib_cmd *cmd, tcmur_caw_fn_t caw_fn); +void tcmur_cmd_timeout(struct tcmu_timer *timer, void *data); #endif /* __TCMUR_CMD_HANDLER_H */