From: Sven Schnelle <sv...@linux.ibm.com>

commit b3bd02495cb339124f13135d51940cf48d83e5cb upstream.

The sysfs function might race with stp_work_fn. To prevent that,
add the required locking. Another issue is that the sysfs functions
are checking the stp_online flag, but this flag just holds the user
setting whether STP is enabled. Add a flag to clock_sync_flag whether
stp_info holds valid data and use that instead.

Cc: sta...@vger.kernel.org
Signed-off-by: Sven Schnelle <sv...@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egore...@linux.ibm.com>
Signed-off-by: Vasily Gorbik <g...@linux.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 arch/s390/kernel/time.c |  118 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 85 insertions(+), 33 deletions(-)

--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -345,8 +345,9 @@ static DEFINE_PER_CPU(atomic_t, clock_sy
 static DEFINE_MUTEX(clock_sync_mutex);
 static unsigned long clock_sync_flags;
 
-#define CLOCK_SYNC_HAS_STP     0
-#define CLOCK_SYNC_STP         1
+#define CLOCK_SYNC_HAS_STP             0
+#define CLOCK_SYNC_STP                 1
+#define CLOCK_SYNC_STPINFO_VALID       2
 
 /*
  * The get_clock function for the physical clock. It will get the current
@@ -583,6 +584,22 @@ void stp_queue_work(void)
        queue_work(time_sync_wq, &stp_work);
 }
 
+static int __store_stpinfo(void)
+{
+       int rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
+
+       if (rc)
+               clear_bit(CLOCK_SYNC_STPINFO_VALID, &clock_sync_flags);
+       else
+               set_bit(CLOCK_SYNC_STPINFO_VALID, &clock_sync_flags);
+       return rc;
+}
+
+static int stpinfo_valid(void)
+{
+       return stp_online && test_bit(CLOCK_SYNC_STPINFO_VALID, 
&clock_sync_flags);
+}
+
 static int stp_sync_clock(void *data)
 {
        struct clock_sync_data *sync = data;
@@ -604,8 +621,7 @@ static int stp_sync_clock(void *data)
                        if (rc == 0) {
                                sync->clock_delta = clock_delta;
                                clock_sync_global(clock_delta);
-                               rc = chsc_sstpi(stp_page, &stp_info,
-                                               sizeof(struct stp_sstpi));
+                               rc = __store_stpinfo();
                                if (rc == 0 && stp_info.tmd != 2)
                                        rc = -EAGAIN;
                        }
@@ -650,7 +666,7 @@ static void stp_work_fn(struct work_stru
        if (rc)
                goto out_unlock;
 
-       rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
+       rc = __store_stpinfo();
        if (rc || stp_info.c == 0)
                goto out_unlock;
 
@@ -687,10 +703,14 @@ static ssize_t ctn_id_show(struct device
                                struct device_attribute *attr,
                                char *buf)
 {
-       if (!stp_online)
-               return -ENODATA;
-       return sprintf(buf, "%016llx\n",
-                      *(unsigned long long *) stp_info.ctnid);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid())
+               ret = sprintf(buf, "%016llx\n",
+                             *(unsigned long long *) stp_info.ctnid);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(ctn_id);
@@ -699,9 +719,13 @@ static ssize_t ctn_type_show(struct devi
                                struct device_attribute *attr,
                                char *buf)
 {
-       if (!stp_online)
-               return -ENODATA;
-       return sprintf(buf, "%i\n", stp_info.ctn);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid())
+               ret = sprintf(buf, "%i\n", stp_info.ctn);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(ctn_type);
@@ -710,9 +734,13 @@ static ssize_t dst_offset_show(struct de
                                   struct device_attribute *attr,
                                   char *buf)
 {
-       if (!stp_online || !(stp_info.vbits & 0x2000))
-               return -ENODATA;
-       return sprintf(buf, "%i\n", (int)(s16) stp_info.dsto);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid() && (stp_info.vbits & 0x2000))
+               ret = sprintf(buf, "%i\n", (int)(s16) stp_info.dsto);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(dst_offset);
@@ -721,9 +749,13 @@ static ssize_t leap_seconds_show(struct
                                        struct device_attribute *attr,
                                        char *buf)
 {
-       if (!stp_online || !(stp_info.vbits & 0x8000))
-               return -ENODATA;
-       return sprintf(buf, "%i\n", (int)(s16) stp_info.leaps);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid() && (stp_info.vbits & 0x8000))
+               ret = sprintf(buf, "%i\n", (int)(s16) stp_info.leaps);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(leap_seconds);
@@ -732,9 +764,13 @@ static ssize_t stratum_show(struct devic
                                struct device_attribute *attr,
                                char *buf)
 {
-       if (!stp_online)
-               return -ENODATA;
-       return sprintf(buf, "%i\n", (int)(s16) stp_info.stratum);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid())
+               ret = sprintf(buf, "%i\n", (int)(s16) stp_info.stratum);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(stratum);
@@ -743,9 +779,13 @@ static ssize_t time_offset_show(struct d
                                struct device_attribute *attr,
                                char *buf)
 {
-       if (!stp_online || !(stp_info.vbits & 0x0800))
-               return -ENODATA;
-       return sprintf(buf, "%i\n", (int) stp_info.tto);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid() && (stp_info.vbits & 0x0800))
+               ret = sprintf(buf, "%i\n", (int) stp_info.tto);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(time_offset);
@@ -754,9 +794,13 @@ static ssize_t time_zone_offset_show(str
                                struct device_attribute *attr,
                                char *buf)
 {
-       if (!stp_online || !(stp_info.vbits & 0x4000))
-               return -ENODATA;
-       return sprintf(buf, "%i\n", (int)(s16) stp_info.tzo);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid() && (stp_info.vbits & 0x4000))
+               ret = sprintf(buf, "%i\n", (int)(s16) stp_info.tzo);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(time_zone_offset);
@@ -765,9 +809,13 @@ static ssize_t timing_mode_show(struct d
                                struct device_attribute *attr,
                                char *buf)
 {
-       if (!stp_online)
-               return -ENODATA;
-       return sprintf(buf, "%i\n", stp_info.tmd);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid())
+               ret = sprintf(buf, "%i\n", stp_info.tmd);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(timing_mode);
@@ -776,9 +824,13 @@ static ssize_t timing_state_show(struct
                                struct device_attribute *attr,
                                char *buf)
 {
-       if (!stp_online)
-               return -ENODATA;
-       return sprintf(buf, "%i\n", stp_info.tst);
+       ssize_t ret = -ENODATA;
+
+       mutex_lock(&stp_work_mutex);
+       if (stpinfo_valid())
+               ret = sprintf(buf, "%i\n", stp_info.tst);
+       mutex_unlock(&stp_work_mutex);
+       return ret;
 }
 
 static DEVICE_ATTR_RO(timing_state);


Reply via email to