summaryrefslogtreecommitdiff
path: root/drivers/android/binder.c
diff options
context:
space:
mode:
authorCarlos Llamas <cmllamas@google.com>2022-04-29 23:56:41 +0000
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-05-09 15:43:24 +0200
commitbd32889e841c12533d09a1bd02bba932baa9ed8f (patch)
tree3eb429f9618c91bdfcc030a0c70f6a8b3e3f703b /drivers/android/binder.c
parent9474be34a72726c5a20467e36d9b08102ff9d304 (diff)
binder: add BINDER_GET_EXTENDED_ERROR ioctl
Provide a userspace mechanism to pull precise error information upon failed operations. Extending the current error codes returned by the interfaces allows userspace to better determine the course of action. This could be for instance, retrying a failed transaction at a later point and thus offloading the error handling from the driver. Acked-by: Christian Brauner (Microsoft) <brauner@kernel.org> Acked-by: Todd Kjos <tkjos@google.com> Signed-off-by: Carlos Llamas <cmllamas@google.com> Link: https://lore.kernel.org/r/20220429235644.697372-3-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/android/binder.c')
-rw-r--r--drivers/android/binder.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index f0690d46caa1..4c2caf38e056 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -147,6 +147,13 @@ module_param_call(stop_on_user_error, binder_set_stop_on_user_error,
binder_stop_on_user_error = 2; \
} while (0)
+#define binder_set_extended_error(ee, _id, _command, _param) \
+ do { \
+ (ee)->id = _id; \
+ (ee)->command = _command; \
+ (ee)->param = _param; \
+ } while (0)
+
#define to_flat_binder_object(hdr) \
container_of(hdr, struct flat_binder_object, hdr)
@@ -2708,6 +2715,24 @@ static struct binder_node *binder_get_node_refs_for_txn(
return target_node;
}
+static void binder_set_txn_from_error(struct binder_transaction *t, int id,
+ uint32_t command, int32_t param)
+{
+ struct binder_thread *from = binder_get_txn_from_and_acq_inner(t);
+
+ if (!from) {
+ /* annotation for sparse */
+ __release(&from->proc->inner_lock);
+ return;
+ }
+
+ /* don't override existing errors */
+ if (from->ee.command == BR_OK)
+ binder_set_extended_error(&from->ee, id, command, param);
+ binder_inner_proc_unlock(from->proc);
+ binder_thread_dec_tmpref(from);
+}
+
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
@@ -2753,6 +2778,10 @@ static void binder_transaction(struct binder_proc *proc,
e->offsets_size = tr->offsets_size;
strscpy(e->context_name, proc->context->name, BINDERFS_MAX_NAME);
+ binder_inner_proc_lock(proc);
+ binder_set_extended_error(&thread->ee, t_debug_id, BR_OK, 0);
+ binder_inner_proc_unlock(proc);
+
if (reply) {
binder_inner_proc_lock(proc);
in_reply_to = thread->transaction_stack;
@@ -3498,10 +3527,16 @@ err_invalid_target_handle:
BUG_ON(thread->return_error.cmd != BR_OK);
if (in_reply_to) {
+ binder_set_txn_from_error(in_reply_to, t_debug_id,
+ return_error, return_error_param);
thread->return_error.cmd = BR_TRANSACTION_COMPLETE;
binder_enqueue_thread_work(thread, &thread->return_error.work);
binder_send_failed_reply(in_reply_to, return_error);
} else {
+ binder_inner_proc_lock(proc);
+ binder_set_extended_error(&thread->ee, t_debug_id,
+ return_error, return_error_param);
+ binder_inner_proc_unlock(proc);
thread->return_error.cmd = return_error;
binder_enqueue_thread_work(thread, &thread->return_error.work);
}
@@ -4628,6 +4663,7 @@ static struct binder_thread *binder_get_thread_ilocked(
thread->return_error.cmd = BR_OK;
thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR;
thread->reply_error.cmd = BR_OK;
+ thread->ee.command = BR_OK;
INIT_LIST_HEAD(&new_thread->waiting_thread_node);
return thread;
}
@@ -5066,6 +5102,25 @@ static int binder_ioctl_get_freezer_info(
return 0;
}
+static int binder_ioctl_get_extended_error(struct binder_thread *thread,
+ void __user *ubuf)
+{
+ struct binder_extended_error *ee = &thread->ee;
+
+ binder_inner_proc_lock(thread->proc);
+ if (copy_to_user(ubuf, ee, sizeof(*ee))) {
+ binder_inner_proc_unlock(thread->proc);
+ return -EFAULT;
+ }
+
+ ee->id = 0;
+ ee->command = BR_OK;
+ ee->param = 0;
+ binder_inner_proc_unlock(thread->proc);
+
+ return 0;
+}
+
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
@@ -5274,6 +5329,11 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
binder_inner_proc_unlock(proc);
break;
}
+ case BINDER_GET_EXTENDED_ERROR:
+ ret = binder_ioctl_get_extended_error(thread, ubuf);
+ if (ret < 0)
+ goto err;
+ break;
default:
ret = -EINVAL;
goto err;