soc: qcom: ramdump: Abort user-space read if timed-out

It's possible that the user-space process can be woken up
very late to collect the ramdumps by which time, the ramdump
collection timer would have expired. Following this, the PIL
driver unmaps the memory and continues. The user-space process
has no way to know about this and can make an attempt to read
the unmapped memory, thus resulting in a data abort.

Hence, notify the user-space process that the ramdump timer
has expired such that it can reset its state machine.

Also add the srcu related symbols to the allowed-list and update
the ABI snapshot accordingly.

Change-Id: Idb92d98406a9032c999c82e2089213883c5a65eb
Signed-off-by: Raghavendra Rao Ananta <rananta@codeaurora.org>
This commit is contained in:
Raghavendra Rao Ananta 2020-08-16 20:13:23 -07:00
parent 1e4aef37ea
commit aec59fab94
3 changed files with 462 additions and 365 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1098,6 +1098,7 @@
init_net
__init_rwsem
init_special_inode
init_srcu_struct
init_timer_key
init_uts_ns
init_wait_entry
@ -2275,6 +2276,8 @@
srcu_notifier_call_chain
srcu_notifier_chain_register
srcu_notifier_chain_unregister
__srcu_read_lock
__srcu_read_unlock
sscanf
__stack_chk_fail
__stack_chk_guard
@ -2317,6 +2320,7 @@
synchronize_irq
synchronize_net
synchronize_rcu
synchronize_srcu
syscon_node_to_regmap
syscon_regmap_lookup_by_phandle
sysfs_create_bin_file

View File

@ -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/of.h>
@ -59,6 +60,8 @@ struct ramdump_device {
size_t elfcore_size;
char *elfcore_buf;
bool complete_ramdump;
bool abort_ramdump;
struct srcu_struct rd_srcu;
};
static int ramdump_open(struct inode *inode, struct file *filep)
@ -158,15 +161,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);
@ -178,8 +192,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,
@ -252,9 +268,12 @@ static ssize_t ramdump_read(struct file *filep, char __user *buf, size_t count,
pr_debug("Ramdump(%s): Read %zd bytes from address %lx.\n",
rd_dev->name, copy_size, addr);
srcu_read_unlock(&rd_dev->rd_srcu, srcu_idx);
return *pos - orig_pos;
ramdump_done:
srcu_read_unlock(&rd_dev->rd_srcu, srcu_idx);
kfree(finalbuf);
*pos = 0;
reset_ramdump_entry(entry);
@ -361,6 +380,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);
@ -373,6 +393,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:
@ -393,6 +414,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);
}
@ -473,6 +495,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);
@ -489,6 +512,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;
@ -602,6 +630,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);
@ -618,6 +647,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;
}