aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2020-08-27 03:55:53 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2020-08-27 03:55:52 -0700
commit09e08c4172e3bc01055d220b1c09c3fa7222fb6d (patch)
tree05ee7ef4347e8954fc23493bf27bb996e5f32aa1
parent2aa52622e2d9a169a5df7acf86b5035b50b7a166 (diff)
parent43733cedc7186c44f323f857b12b5accb2f68af6 (diff)
Merge "soc: qcom: ramdump: Abort user-space read if timed-out"LA.UM.8.13.r1-09800-SAIPAN.0
-rw-r--r--drivers/soc/qcom/ramdump.c39
1 files changed, 36 insertions, 3 deletions
diff --git a/drivers/soc/qcom/ramdump.c b/drivers/soc/qcom/ramdump.c
index 4746a9fea2a9..1555c4f44475 100644
--- a/drivers/soc/qcom/ramdump.c
+++ b/drivers/soc/qcom/ramdump.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
@@ -17,6 +17,7 @@
#include <linux/elf.h>
#include <linux/wait.h>
#include <linux/cdev.h>
+#include <linux/srcu.h>
#include <linux/atomic.h>
#include <soc/qcom/ramdump.h>
#include <linux/dma-mapping.h>
@@ -61,6 +62,8 @@ struct ramdump_device {
char *elfcore_buf;
unsigned long attrs;
bool complete_ramdump;
+ bool abort_ramdump;
+ struct srcu_struct rd_srcu;
};
static int ramdump_open(struct inode *inode, struct file *filep)
@@ -160,15 +163,26 @@ static ssize_t ramdump_read(struct file *filep, char __user *buf, size_t count,
size_t copy_size = 0, alignsize;
unsigned char *alignbuf = NULL, *finalbuf = NULL;
int ret = 0;
+ int srcu_idx;
loff_t orig_pos = *pos;
if ((filep->f_flags & O_NONBLOCK) && !entry->data_ready)
return -EAGAIN;
- ret = wait_event_interruptible(rd_dev->dump_wait_q, entry->data_ready);
+ ret = wait_event_interruptible(rd_dev->dump_wait_q,
+ (entry->data_ready || rd_dev->abort_ramdump));
if (ret)
return ret;
+ srcu_idx = srcu_read_lock(&rd_dev->rd_srcu);
+
+ if (rd_dev->abort_ramdump) {
+ pr_err("Ramdump(%s): Ramdump aborted\n", rd_dev->name);
+ rd_dev->ramdump_status = -1;
+ ret = -ETIME;
+ goto ramdump_done;
+ }
+
if (*pos < rd_dev->elfcore_size) {
copy_size = rd_dev->elfcore_size - *pos;
copy_size = min(copy_size, count);
@@ -180,8 +194,10 @@ static ssize_t ramdump_read(struct file *filep, char __user *buf, size_t count,
*pos += copy_size;
count -= copy_size;
buf += copy_size;
- if (count == 0)
+ if (count == 0) {
+ srcu_read_unlock(&rd_dev->rd_srcu, srcu_idx);
return copy_size;
+ }
}
addr = offset_translate(*pos - rd_dev->elfcore_size, rd_dev,
@@ -258,12 +274,15 @@ static ssize_t ramdump_read(struct file *filep, char __user *buf, size_t count,
pr_debug("Ramdump(%s): Read %zd bytes from address %lx.",
rd_dev->name, copy_size, addr);
+ srcu_read_unlock(&rd_dev->rd_srcu, srcu_idx);
+
return *pos - orig_pos;
ramdump_done:
if (!vaddr && origdevice_mem)
dma_unremap(rd_dev->dev->parent, origdevice_mem, copy_size);
+ srcu_read_unlock(&rd_dev->rd_srcu, srcu_idx);
kfree(finalbuf);
*pos = 0;
reset_ramdump_entry(entry);
@@ -368,6 +387,7 @@ void *create_ramdump_device(const char *dev_name, struct device *parent)
mutex_init(&rd_dev->consumer_lock);
atomic_set(&rd_dev->readers_left, 0);
+ init_srcu_struct(&rd_dev->rd_srcu);
cdev_init(&rd_dev->cdev, &ramdump_file_ops);
ret = cdev_add(&rd_dev->cdev, MKDEV(MAJOR(ramdump_dev), minor), 1);
@@ -380,6 +400,7 @@ void *create_ramdump_device(const char *dev_name, struct device *parent)
return (void *)rd_dev;
fail_cdev_add:
+ cleanup_srcu_struct(&rd_dev->rd_srcu);
mutex_destroy(&rd_dev->consumer_lock);
device_unregister(rd_dev->dev);
fail_return_minor:
@@ -400,6 +421,7 @@ void destroy_ramdump_device(void *dev)
cdev_del(&rd_dev->cdev);
device_unregister(rd_dev->dev);
+ cleanup_srcu_struct(&rd_dev->rd_srcu);
ida_simple_remove(&rd_minor_id, minor);
kfree(rd_dev);
}
@@ -480,6 +502,7 @@ static int _do_ramdump(void *handle, struct ramdump_segment *segments,
list_for_each_entry(entry, &rd_dev->consumer_list, list)
entry->data_ready = true;
rd_dev->ramdump_status = -1;
+ rd_dev->abort_ramdump = false;
reinit_completion(&rd_dev->ramdump_complete);
atomic_set(&rd_dev->readers_left, rd_dev->consumers);
@@ -496,6 +519,11 @@ static int _do_ramdump(void *handle, struct ramdump_segment *segments,
pr_err("Ramdump(%s): Timed out waiting for userspace.\n",
rd_dev->name);
ret = -EPIPE;
+ rd_dev->abort_ramdump = true;
+
+ /* Wait for pending readers to complete (if any) */
+ synchronize_srcu(&rd_dev->rd_srcu);
+
} else
ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE;
@@ -609,6 +637,7 @@ static int _do_minidump(void *handle, struct ramdump_segment *segments,
list_for_each_entry(entry, &rd_dev->consumer_list, list)
entry->data_ready = true;
rd_dev->ramdump_status = -1;
+ rd_dev->abort_ramdump = false;
reinit_completion(&rd_dev->ramdump_complete);
atomic_set(&rd_dev->readers_left, rd_dev->consumers);
@@ -625,6 +654,10 @@ static int _do_minidump(void *handle, struct ramdump_segment *segments,
pr_err("Ramdump(%s): Timed out waiting for userspace.\n",
rd_dev->name);
ret = -EPIPE;
+ rd_dev->abort_ramdump = true;
+
+ /* Wait for pending readers to complete (if any) */
+ synchronize_srcu(&rd_dev->rd_srcu);
} else {
ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE;
}