@@ -159,9 +159,11 @@ struct tcmu_dev {
struct timer_list cmd_timer;
unsigned int cmd_time_out;
+ unsigned long cmd_next_deadline;
struct timer_list qfull_timer;
int qfull_time_out;
+ unsigned long qfull_next_deadline;
struct list_head timedout_entry;
@@ -915,7 +917,9 @@ static int tcmu_setup_cmd_timer(struct tcmu_cmd *tcmu_cmd, unsigned int tmo,
return 0;
tcmu_cmd->deadline = round_jiffies_up(jiffies + msecs_to_jiffies(tmo));
- mod_timer(timer, tcmu_cmd->deadline);
+ if (!timer_pending(timer))
+ mod_timer(timer, tcmu_cmd->deadline);
+
return 0;
}
@@ -1197,6 +1201,7 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
static unsigned int tcmu_handle_completions(struct tcmu_dev *udev)
{
struct tcmu_mailbox *mb;
+ struct tcmu_cmd *cmd;
int handled = 0;
if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) {
@@ -1210,7 +1215,6 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev)
while (udev->cmdr_last_cleaned != READ_ONCE(mb->cmd_tail)) {
struct tcmu_cmd_entry *entry = (void *) mb + CMDR_OFF + udev->cmdr_last_cleaned;
- struct tcmu_cmd *cmd;
tcmu_flush_dcache_range(entry, sizeof(*entry));
@@ -1252,6 +1256,22 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev)
tcmu_global_max_blocks)
schedule_delayed_work(&tcmu_unmap_work, 0);
}
+ } else if (udev->cmd_time_out) {
+ unsigned long deadline;
+ bool is_running;
+ int i;
+
+ deadline = round_jiffies_up(jiffies + msecs_to_jiffies(UINT_MAX));
+ udev->cmd_next_deadline = deadline;
+ idr_for_each_entry(&udev->commands, cmd, i) {
+ is_running = list_empty(&cmd->cmdr_queue_entry);
+ if (is_running && udev->cmd_next_deadline > cmd->deadline)
+ udev->cmd_next_deadline = cmd->deadline;
+ }
+ if (udev->cmd_next_deadline != deadline)
+ mod_timer(&udev->cmd_timer, udev->cmd_next_deadline);
+ else
+ del_timer(&udev->cmd_timer);
}
return handled;
@@ -1261,18 +1281,23 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data)
{
struct tcmu_cmd *cmd = p;
struct tcmu_dev *udev = cmd->tcmu_dev;
+ bool is_running = list_empty(&cmd->cmdr_queue_entry);
+ struct se_cmd *se_cmd = cmd->se_cmd;
u8 scsi_status;
- struct se_cmd *se_cmd;
- bool is_running;
if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags))
return 0;
- if (!time_after(jiffies, cmd->deadline))
+ if (!time_after(jiffies, cmd->deadline)) {
+ if (is_running) {
+ if (udev->cmd_next_deadline > cmd->deadline)
+ udev->cmd_next_deadline = cmd->deadline;
+ } else {
+ if (udev->qfull_next_deadline > cmd->deadline)
+ udev->qfull_next_deadline = cmd->deadline;
+ }
return 0;
-
- is_running = list_empty(&cmd->cmdr_queue_entry);
- se_cmd = cmd->se_cmd;
+ }
if (is_running) {
/*
@@ -2661,11 +2686,22 @@ static void check_timedout_devices(void)
list_splice_init(&timed_out_udevs, &devs);
list_for_each_entry_safe(udev, tmp_dev, &devs, timedout_entry) {
+ unsigned long max_deadline;
+
list_del_init(&udev->timedout_entry);
spin_unlock_bh(&timed_out_udevs_lock);
mutex_lock(&udev->cmdr_lock);
+ max_deadline = round_jiffies_up(jiffies + msecs_to_jiffies(UINT_MAX));
+ udev->cmd_next_deadline = max_deadline;
+ udev->qfull_next_deadline = max_deadline;
idr_for_each(&udev->commands, tcmu_check_expired_cmd, NULL);
+
+ if (udev->cmd_next_deadline != max_deadline)
+ mod_timer(&udev->cmd_timer, udev->cmd_next_deadline);
+ if (udev->qfull_next_deadline != max_deadline)
+ mod_timer(&udev->qfull_timer, udev->qfull_next_deadline);
+
mutex_unlock(&udev->cmdr_lock);
spin_lock_bh(&timed_out_udevs_lock);