| From b3bd02495cb339124f13135d51940cf48d83e5cb Mon Sep 17 00:00:00 2001 |
| From: Sven Schnelle <svens@linux.ibm.com> |
| Date: Tue, 15 Sep 2020 08:53:50 +0200 |
| Subject: s390/stp: add locking to sysfs functions |
| |
| From: Sven Schnelle <svens@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: stable@vger.kernel.org |
| Signed-off-by: Sven Schnelle <svens@linux.ibm.com> |
| Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com> |
| Signed-off-by: Vasily Gorbik <gor@linux.ibm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@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); |