aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Stanley <joel@jms.id.au>2014-04-01 17:04:43 +1100
committerEli Qiao <taget@linux.vnet.ibm.com>2014-04-01 14:30:36 +0800
commit6c45251bf7b23b187e58f5f8fc271606d46fb463 (patch)
treefdf75bede72c8163a8d24a926685e19b9b8dadf0
parent19da3e46931fa15713c0503720f567c2b8d8ff2b (diff)
downloadpowerkvm-6c45251bf7b23b187e58f5f8fc271606d46fb463.tar.gz
powerpc/powernv: OPAL message log interface rework
This reworks the opal message log following upstream review. A bug was fixed where wrapped logs were not read correctly, and locking was added to reduce the impact of races between reading counters and the buffer contents. Signed-off-by: Joel Stanley <joel@jms.id.au> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/platforms/powernv/opal-msglog.c58
1 files changed, 40 insertions, 18 deletions
diff --git a/arch/powerpc/platforms/powernv/opal-msglog.c b/arch/powerpc/platforms/powernv/opal-msglog.c
index 5cb562d033d4a..1bb25b9525046 100644
--- a/arch/powerpc/platforms/powernv/opal-msglog.c
+++ b/arch/powerpc/platforms/powernv/opal-msglog.c
@@ -14,6 +14,7 @@
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/types.h>
+#include <asm/barrier.h>
/* OPAL in-memory console. Defined in OPAL source at core/console.c */
struct memcons {
@@ -36,33 +37,54 @@ static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj,
{
struct memcons *mc = bin_attr->private;
const char *conbuf;
- bool wrapped;
- size_t num_read;
- int out_pos;
+ size_t ret, first_read = 0;
+ uint32_t out_pos, avail;
if (!mc)
return -ENODEV;
+ out_pos = be32_to_cpu(ACCESS_ONCE(mc->out_pos));
+
+ /* Now we've read out_pos, put a barrier in before reading the new
+ * data it points to in conbuf. */
+ smp_rmb();
+
conbuf = phys_to_virt(be64_to_cpu(mc->obuf_phys));
- wrapped = be32_to_cpu(mc->out_pos) & MEMCONS_OUT_POS_WRAP;
- out_pos = be32_to_cpu(mc->out_pos) & MEMCONS_OUT_POS_MASK;
-
- if (!wrapped) {
- num_read = memory_read_from_buffer(to, count, &pos, conbuf,
- out_pos);
- } else {
- num_read = memory_read_from_buffer(to, count, &pos,
- conbuf + out_pos,
- be32_to_cpu(mc->obuf_size) - out_pos);
-
- if (num_read < 0)
+
+ /* When the buffer has wrapped, read from the out_pos marker to the end
+ * of the buffer, and then read the remaining data as in the un-wrapped
+ * case. */
+ if (out_pos & MEMCONS_OUT_POS_WRAP) {
+
+ out_pos &= MEMCONS_OUT_POS_MASK;
+ avail = be32_to_cpu(mc->obuf_size) - out_pos;
+
+ ret = memory_read_from_buffer(to, count, &pos,
+ conbuf + out_pos, avail);
+
+ if (ret < 0)
goto out;
- num_read += memory_read_from_buffer(to + num_read,
- count - num_read, &pos, conbuf, out_pos);
+ first_read = ret;
+ to += first_read;
+ count -= first_read;
+ pos -= avail;
}
+
+ /* Sanity check. The firmware should not do this to us. */
+ if (out_pos > be32_to_cpu(mc->obuf_size)) {
+ pr_err("OPAL: memory console corruption. Aborting read.\n");
+ return -EINVAL;
+ }
+
+ ret = memory_read_from_buffer(to, count, &pos, conbuf, out_pos);
+
+ if (ret < 0)
+ goto out;
+
+ ret += first_read;
out:
- return num_read;
+ return ret;
}
static struct bin_attribute opal_msglog_attr = {