]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/char/ipmi/ipmi_si_intf.c
ipmi: Turn off all activity on an idle ipmi interface
[mirror_ubuntu-artful-kernel.git] / drivers / char / ipmi / ipmi_si_intf.c
index b7efd3c1a882525431da787f1679e33729243f82..444ea548dfe3f910da0960dd82a6ffa86643ce48 100644 (file)
@@ -248,12 +248,18 @@ struct smi_info {
        /* The timer for this si. */
        struct timer_list   si_timer;
 
+       /* This flag is set, if the timer is running (timer_pending() isn't enough) */
+       bool                timer_running;
+
        /* The time (in jiffies) the last timeout occurred at. */
        unsigned long       last_timeout_jiffies;
 
        /* Used to gracefully stop the timer without race conditions. */
        atomic_t            stop_operation;
 
+       /* Are we waiting for the events, pretimeouts, received msgs? */
+       atomic_t            need_watch;
+
        /*
         * The driver will disable interrupts when it gets into a
         * situation where it cannot handle messages due to lack of
@@ -434,6 +440,13 @@ static void start_clear_flags(struct smi_info *smi_info)
        smi_info->si_state = SI_CLEARING_FLAGS;
 }
 
+static void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val)
+{
+       smi_info->last_timeout_jiffies = jiffies;
+       mod_timer(&smi_info->si_timer, new_val);
+       smi_info->timer_running = true;
+}
+
 /*
  * When we have a situtaion where we run out of memory and cannot
  * allocate messages, we just leave them in the BMC and run the system
@@ -446,8 +459,7 @@ static inline void disable_si_irq(struct smi_info *smi_info)
                start_disable_irq(smi_info);
                smi_info->interrupt_disabled = 1;
                if (!atomic_read(&smi_info->stop_operation))
-                       mod_timer(&smi_info->si_timer,
-                                 jiffies + SI_TIMEOUT_JIFFIES);
+                       smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
        }
 }
 
@@ -853,6 +865,19 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
        return si_sm_result;
 }
 
+static void check_start_timer_thread(struct smi_info *smi_info)
+{
+       if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) {
+               smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
+
+               if (smi_info->thread)
+                       wake_up_process(smi_info->thread);
+
+               start_next_msg(smi_info);
+               smi_event_handler(smi_info, 0);
+       }
+}
+
 static void sender(void                *send_info,
                   struct ipmi_smi_msg *msg,
                   int                 priority)
@@ -906,23 +931,7 @@ static void sender(void                *send_info,
        else
                list_add_tail(&msg->link, &smi_info->xmit_msgs);
 
-       if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) {
-               /*
-                * last_timeout_jiffies is updated here to avoid
-                * smi_timeout() handler passing very large time_diff
-                * value to smi_event_handler() that causes
-                * the send command to abort.
-                */
-               smi_info->last_timeout_jiffies = jiffies;
-
-               mod_timer(&smi_info->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
-
-               if (smi_info->thread)
-                       wake_up_process(smi_info->thread);
-
-               start_next_msg(smi_info);
-               smi_event_handler(smi_info, 0);
-       }
+       check_start_timer_thread(smi_info);
        spin_unlock_irqrestore(&smi_info->si_lock, flags);
 }
 
@@ -1004,6 +1013,17 @@ static int ipmi_thread(void *data)
 
                spin_lock_irqsave(&(smi_info->si_lock), flags);
                smi_result = smi_event_handler(smi_info, 0);
+
+               /*
+                * If the driver is doing something, there is a possible
+                * race with the timer.  If the timer handler see idle,
+                * and the thread here sees something else, the timer
+                * handler won't restart the timer even though it is
+                * required.  So start it here if necessary.
+                */
+               if (smi_result != SI_SM_IDLE && !smi_info->timer_running)
+                       smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
+
                spin_unlock_irqrestore(&(smi_info->si_lock), flags);
                busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
                                                  &busy_until);
@@ -1011,9 +1031,15 @@ static int ipmi_thread(void *data)
                        ; /* do nothing */
                else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait)
                        schedule();
-               else if (smi_result == SI_SM_IDLE)
-                       schedule_timeout_interruptible(100);
-               else
+               else if (smi_result == SI_SM_IDLE) {
+                       if (atomic_read(&smi_info->need_watch)) {
+                               schedule_timeout_interruptible(100);
+                       } else {
+                               /* Wait to be woken up when we are needed. */
+                               __set_current_state(TASK_INTERRUPTIBLE);
+                               schedule();
+                       }
+               } else
                        schedule_timeout_interruptible(1);
        }
        return 0;
@@ -1049,6 +1075,17 @@ static void request_events(void *send_info)
        atomic_set(&smi_info->req_events, 1);
 }
 
+static void set_need_watch(void *send_info, int enable)
+{
+       struct smi_info *smi_info = send_info;
+       unsigned long flags;
+
+       atomic_set(&smi_info->need_watch, enable);
+       spin_lock_irqsave(&smi_info->si_lock, flags);
+       check_start_timer_thread(smi_info);
+       spin_unlock_irqrestore(&smi_info->si_lock, flags);
+}
+
 static int initialized;
 
 static void smi_timeout(unsigned long data)
@@ -1073,10 +1110,6 @@ static void smi_timeout(unsigned long data)
                     * SI_USEC_PER_JIFFY);
        smi_result = smi_event_handler(smi_info, time_diff);
 
-       spin_unlock_irqrestore(&(smi_info->si_lock), flags);
-
-       smi_info->last_timeout_jiffies = jiffies_now;
-
        if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
                /* Running with interrupts, only do long timeouts. */
                timeout = jiffies + SI_TIMEOUT_JIFFIES;
@@ -1098,7 +1131,10 @@ static void smi_timeout(unsigned long data)
 
  do_mod_timer:
        if (smi_result != SI_SM_IDLE)
-               mod_timer(&(smi_info->si_timer), timeout);
+               smi_mod_timer(smi_info, timeout);
+       else
+               smi_info->timer_running = false;
+       spin_unlock_irqrestore(&(smi_info->si_lock), flags);
 }
 
 static irqreturn_t si_irq_handler(int irq, void *data)
@@ -1146,8 +1182,7 @@ static int smi_start_processing(void       *send_info,
 
        /* Set up the timer that drives the interface. */
        setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi);
-       new_smi->last_timeout_jiffies = jiffies;
-       mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
+       smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
 
        /*
         * Check if the user forcefully enabled the daemon.
@@ -1202,6 +1237,7 @@ static struct ipmi_smi_handlers handlers = {
        .get_smi_info           = get_smi_info,
        .sender                 = sender,
        .request_events         = request_events,
+       .set_need_watch         = set_need_watch,
        .set_maintenance_mode   = set_maintenance_mode,
        .set_run_to_completion  = set_run_to_completion,
        .poll                   = poll,
@@ -1229,7 +1265,7 @@ static bool          si_tryplatform = 1;
 #ifdef CONFIG_PCI
 static bool          si_trypci = 1;
 #endif
-static bool          si_trydefaults = 1;
+static bool          si_trydefaults = IS_ENABLED(CONFIG_IPMI_SI_PROBE_DEFAULTS);
 static char          *si_type[SI_MAX_PARMS];
 #define MAX_SI_TYPE_STR 30
 static char          si_type_str[MAX_SI_TYPE_STR];
@@ -3342,6 +3378,7 @@ static int try_smi_init(struct smi_info *new_smi)
 
        new_smi->interrupt_disabled = 1;
        atomic_set(&new_smi->stop_operation, 0);
+       atomic_set(&new_smi->need_watch, 0);
        new_smi->intf_num = smi_num;
        smi_num++;