[S390] serialize stp/etr work
Martin Schwidefsky [Thu, 25 Dec 2008 12:38:38 +0000 (13:38 +0100)]
The work function dispatched with schedule_work() can be run twice
on different cpus because run_workqueue clears the WORK_STRUCT_PENDING
bit and then executes the function. Another cpu can call schedule_work()
again and run the work function a second time before the first call
is completed. This patch serialized the etr and stp work function with
a mutex.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

arch/s390/kernel/time.c

index 6e09bc2..4965285 100644 (file)
@@ -467,6 +467,7 @@ static struct timer_list etr_timer;
 
 static void etr_timeout(unsigned long dummy);
 static void etr_work_fn(struct work_struct *work);
+static DEFINE_MUTEX(etr_work_mutex);
 static DECLARE_WORK(etr_work, etr_work_fn);
 
 /*
@@ -976,6 +977,9 @@ static void etr_work_fn(struct work_struct *work)
        struct etr_aib aib;
        int sync_port;
 
+       /* prevent multiple execution. */
+       mutex_lock(&etr_work_mutex);
+
        /* Create working copy of etr_eacr. */
        eacr = etr_eacr;
 
@@ -991,7 +995,7 @@ static void etr_work_fn(struct work_struct *work)
                del_timer_sync(&etr_timer);
                etr_update_eacr(eacr);
                clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
-               return;
+               goto out_unlock;
        }
 
        /* Store aib to get the current ETR status word. */
@@ -1078,7 +1082,7 @@ static void etr_work_fn(struct work_struct *work)
            eacr.es || sync_port < 0) {
                etr_update_eacr(eacr);
                etr_set_tolec_timeout(now);
-               return;
+               goto out_unlock;
        }
 
        /*
@@ -1106,6 +1110,8 @@ static void etr_work_fn(struct work_struct *work)
                etr_set_sync_timeout();
        } else
                etr_set_tolec_timeout(now);
+out_unlock:
+       mutex_unlock(&etr_work_mutex);
 }
 
 /*
@@ -1394,6 +1400,7 @@ static struct stp_sstpi stp_info;
 static void *stp_page;
 
 static void stp_work_fn(struct work_struct *work);
+static DEFINE_MUTEX(stp_work_mutex);
 static DECLARE_WORK(stp_work, stp_work_fn);
 
 static int __init early_parse_stp(char *p)
@@ -1542,24 +1549,30 @@ static void stp_work_fn(struct work_struct *work)
        struct clock_sync_data stp_sync;
        int rc;
 
+       /* prevent multiple execution. */
+       mutex_lock(&stp_work_mutex);
+
        if (!stp_online) {
                chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
-               return;
+               goto out_unlock;
        }
 
        rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0);
        if (rc)
-               return;
+               goto out_unlock;
 
        rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
        if (rc || stp_info.c == 0)
-               return;
+               goto out_unlock;
 
        memset(&stp_sync, 0, sizeof(stp_sync));
        get_online_cpus();
        atomic_set(&stp_sync.cpus, num_online_cpus() - 1);
        stop_machine(stp_sync_clock, &stp_sync, &cpu_online_map);
        put_online_cpus();
+
+out_unlock:
+       mutex_unlock(&stp_work_mutex);
 }
 
 /*