From 0d292d11f2158424499ed57da657b7b920131e5c Mon Sep 17 00:00:00 2001 From: Sumukh Hallymysore Ravindra Date: Wed, 8 Sep 2021 13:43:05 +0530 Subject: [PATCH 01/71] msm: synx: Prevent accessing valid but released handles Release function removes the reference taken during create, but same release function can be invoked again to take away the valid reference held by other threads, which got these references before release was invoked the first time. This could lead to successive release call/s taking away reference held by other threads leading to UAF. Change adds another member variable to fail release function even though it is still valid, if the rel_count member has reached zero. rel_count variable is set to 1 during synx creation and decremented on release. All functions after release will fail, as per design and avoid taking away the reference of pending functions on same handle. Change-Id: Ie7ab279bc1e08e82c394b704e181716b726bcf4e Signed-off-by: Sumukh Hallymysore Ravindra --- drivers/media/platform/msm/synx/synx.c | 44 ++++++++++++------- .../media/platform/msm/synx/synx_private.h | 4 +- drivers/media/platform/msm/synx/synx_util.c | 4 ++ 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/msm/synx/synx.c b/drivers/media/platform/msm/synx/synx.c index 4ab19050a39f..8043cc1489ed 100644 --- a/drivers/media/platform/msm/synx/synx.c +++ b/drivers/media/platform/msm/synx/synx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "synx: " fmt @@ -681,9 +681,9 @@ EXPORT_SYMBOL(synx_merge); int synx_release(struct synx_session session_id, s32 h_synx) { int rc = 0; + u32 idx; struct synx_client *client; struct synx_handle_coredata *synx_data; - struct synx_coredata *synx_obj; pr_debug("[sess: %u] Enter release from pid %d\n", session_id.client_id, current->pid); @@ -692,21 +692,31 @@ int synx_release(struct synx_session session_id, s32 h_synx) if (!client) return -EINVAL; - synx_data = synx_util_acquire_handle(client, h_synx); - synx_obj = synx_util_obtain_object(synx_data); - /* no need to check for fence here */ - if (!synx_obj) { - pr_err("%s: [sess: %u] invalid handle access %d\n", - __func__, client->id, h_synx); - rc = -EINVAL; - goto fail; + synx_data = NULL; + idx = synx_util_handle_index(h_synx); + + mutex_lock(&client->synx_table_lock[idx]); + synx_data = &client->synx_table[idx]; + if (!synx_data->synx_obj) { + pr_err("[sess: %u] invalid object handle %d\n", + client->id, h_synx); + } else if (synx_data->handle != h_synx) { + pr_err("[sess: %u] stale object handle %d\n", + client->id, h_synx); + } else if (synx_data->rel_count == 0) { + pr_err("[sess: %u] released object handle %d\n", + client->id, h_synx); + } else if (!kref_read(&synx_data->internal_refcount)) { + pr_err("[sess: %u] destroyed object handle %d\n", + client->id, h_synx); + } else { + synx_data->rel_count--; + /* release the reference obtained at synx creation */ + kref_put(&synx_data->internal_refcount, + synx_util_destroy_internal_handle); } + mutex_unlock(&client->synx_table_lock[idx]); - /* release the reference obtained at synx creation */ - synx_util_release_handle(synx_data); - -fail: - synx_util_release_handle(synx_data); synx_put_client(client); pr_debug("[sess: %u] exit release with status %d\n", session_id.client_id, rc); @@ -953,8 +963,10 @@ int synx_addrefcount(struct synx_session session_id, s32 h_synx, s32 count) idx = synx_util_handle_index(h_synx); mutex_lock(&client->synx_table_lock[idx]); /* acquire additional references to handle */ - while (count--) + while (count--) { + synx_data->rel_count++; kref_get(&synx_data->internal_refcount); + } mutex_unlock(&client->synx_table_lock[idx]); fail: diff --git a/drivers/media/platform/msm/synx/synx_private.h b/drivers/media/platform/msm/synx/synx_private.h index e8f5d8e3ba41..9ebc162ee88c 100644 --- a/drivers/media/platform/msm/synx/synx_private.h +++ b/drivers/media/platform/msm/synx/synx_private.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #ifndef __SYNX_PRIVATE_H__ @@ -181,6 +181,7 @@ struct synx_device; * @import_refcount : References for external clients for import * @id : Synx object handle for the client * @key : Key for import authentication + * @rel_count : No of allowed release counts */ struct synx_handle_coredata { struct synx_client *client; @@ -189,6 +190,7 @@ struct synx_handle_coredata { struct kref import_refcount; u32 handle; u16 key; + u32 rel_count; }; /** diff --git a/drivers/media/platform/msm/synx/synx_util.c b/drivers/media/platform/msm/synx/synx_util.c index 3739f81a13d3..737351fadf60 100644 --- a/drivers/media/platform/msm/synx/synx_util.c +++ b/drivers/media/platform/msm/synx/synx_util.c @@ -231,6 +231,7 @@ int synx_util_init_handle(struct synx_client *client, synx_data->handle = h_synx; synx_data->synx_obj = synx_obj; kref_init(&synx_data->internal_refcount); + synx_data->rel_count = 1; mutex_unlock(&client->synx_table_lock[idx]); *new_synx = h_synx; @@ -570,6 +571,9 @@ struct synx_handle_coredata *synx_util_acquire_handle( } else if (synx_data->handle != h_synx) { pr_err("[sess: %u] stale object handle %d\n", client->id, h_synx); + } else if (synx_data->rel_count == 0) { + pr_err("[sess: %u] released object handle %d\n", + client->id, h_synx); } else if (!kref_read(&synx_data->internal_refcount)) { pr_err("[sess: %u] destroyed object handle %d\n", client->id, h_synx); From 4dd02661127787e0b2b2c04447451e244f498fb1 Mon Sep 17 00:00:00 2001 From: Ashish Chavan Date: Wed, 28 Jul 2021 09:30:59 +0530 Subject: [PATCH 02/71] power: supply: qcom: Disable suspend on collapse on USB removal In current implementation with "qcom,disable-suspend-on-collapse" flag set SUSPEND_ON_COLLAPSE_USBIN_BIT is not set initially but gets set when there is a typec/uusb removal. Fix this by adding an additional check here to not to enable it when this flag is set. Change-Id: I53407b7f385cecc31e4498a8f50696ad643e1a40 Signed-off-by: Ashish Chavan --- drivers/power/supply/qcom/qpnp-smb5.c | 5 ++--- drivers/power/supply/qcom/smb5-lib.c | 26 ++++++++++++++++---------- drivers/power/supply/qcom/smb5-lib.h | 1 + 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index 321956716355..8a25745698a3 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -218,7 +218,6 @@ struct smb_dt_props { int term_current_src; int term_current_thresh_hi_ma; int term_current_thresh_lo_ma; - int disable_suspend_on_collapse; }; struct smb5 { @@ -556,7 +555,7 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) of_property_read_u32(node, "qcom,connector-internal-pull-kohm", &chg->connector_pull_up); - chip->dt.disable_suspend_on_collapse = of_property_read_bool(node, + chg->disable_suspend_on_collapse = of_property_read_bool(node, "qcom,disable-suspend-on-collapse"); chg->smb_pull_up = -EINVAL; of_property_read_u32(node, "qcom,smb-internal-pull-kohm", @@ -2148,7 +2147,7 @@ static int smb5_init_hw(struct smb5 *chip) mask = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT; val = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_EN_BIT; - if (!chip->dt.disable_suspend_on_collapse) + if (!chg->disable_suspend_on_collapse) val |= SUSPEND_ON_COLLAPSE_USBIN_BIT; if (chip->dt.adc_based_aicl) val |= USBIN_AICL_ADC_EN_BIT; diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index 461fdf2d061a..ce4ba0aaffab 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.c @@ -1354,12 +1354,15 @@ static void smblib_uusb_removal(struct smb_charger *chg) smblib_err(chg, "Couldn't restore max pulses rc=%d\n", rc); - rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, - SUSPEND_ON_COLLAPSE_USBIN_BIT, - SUSPEND_ON_COLLAPSE_USBIN_BIT); - if (rc < 0) - smblib_err(chg, "Couldn't turn on SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n", + if (!chg->disable_suspend_on_collapse) { + rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, + SUSPEND_ON_COLLAPSE_USBIN_BIT, + SUSPEND_ON_COLLAPSE_USBIN_BIT); + if (rc < 0) + smblib_err(chg, + "Couldn't turn on SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n", rc); + } chg->qc2_unsupported_voltage = QC2_COMPLIANT; } @@ -6458,12 +6461,15 @@ static void typec_src_removal(struct smb_charger *chg) smblib_err(chg, "Couldn't restore max pulses rc=%d\n", rc); - rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, - SUSPEND_ON_COLLAPSE_USBIN_BIT, - SUSPEND_ON_COLLAPSE_USBIN_BIT); - if (rc < 0) - smblib_err(chg, "Couldn't turn on SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n", + if (!chg->disable_suspend_on_collapse) { + rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, + SUSPEND_ON_COLLAPSE_USBIN_BIT, + SUSPEND_ON_COLLAPSE_USBIN_BIT); + if (rc < 0) + smblib_err(chg, + "Couldn't turn on SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n", rc); + } chg->qc2_unsupported_voltage = QC2_COMPLIANT; } diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h index bb8f34401a92..f3c38894650a 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -594,6 +594,7 @@ struct smb_charger { bool apsd_ext_timeout; bool qc3p5_detected; int qc3p5_detected_mw; + bool disable_suspend_on_collapse; /* workaround flag */ int real_charger_type; From c2e51390fffe2e1a02b6f2129afe400be087827f Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Savaliya Date: Wed, 18 Aug 2021 12:42:49 +0530 Subject: [PATCH 03/71] i2c: i2c-msm-geni: Change the high and low time for SCL This change is to meet the spec for i2c at 400KHz. Note that there is a QUP FW change along with this and then only this will work. FW change takes care of the duty cycle, clock on/off. FW change CR : 3019821 CR fixed : 3017809. Change-Id: I7c03d78ce4c54454b0bb66dcd587f327e63d07e1 Signed-off-by: Mukesh Kumar Savaliya --- drivers/i2c/busses/i2c-msm-geni.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-msm-geni.c b/drivers/i2c/busses/i2c-msm-geni.c index d1c7af60c50f..33aa3d597edd 100644 --- a/drivers/i2c/busses/i2c-msm-geni.c +++ b/drivers/i2c/busses/i2c-msm-geni.c @@ -171,7 +171,7 @@ struct geni_i2c_clk_fld { static struct geni_i2c_clk_fld geni_i2c_clk_map[] = { {KHz(100), 7, 10, 11, 26}, - {KHz(400), 2, 5, 12, 24}, + {KHz(400), 2, 7, 10, 24}, {KHz(1000), 1, 3, 9, 18}, }; From a0800501564374bf43dbf7badd4da1e3a845ce4c Mon Sep 17 00:00:00 2001 From: Alistair Delva Date: Wed, 17 Nov 2021 09:58:10 -0800 Subject: [PATCH 04/71] ANDROID: setlocalversion: make KMI_GENERATION optional GKI required the KMI_GENERATION to be added to the kernel version string, but this only makes sense for GKI kernels, for non-GKI kernels we don't need it. Leave all the other stuff we added, though, as it seems useful. Bug: 205897686 Change-Id: I2e7b3cb7ed5529f1e5e7c9d79a1f7ce4a1b6ee1f Signed-off-by: Alistair Delva --- scripts/setlocalversion | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/setlocalversion b/scripts/setlocalversion index 63a87f45c5c2..fc331309707c 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -41,8 +41,6 @@ if test $# -gt 0; then kmi_generation=$1 [ $(expr $kmi_generation : '^[0-9]\+$') -eq 0 ] && usage shift - else - usage fi fi if test $# -gt 0 -o ! -d "$srctree"; then @@ -69,6 +67,8 @@ scm_version() if [ -n "$android_release" ] && [ -n "$kmi_generation" ]; then printf '%s' "-$android_release-$kmi_generation" + elif [ -n "$android_release" ]; then + printf '%s' "-$android_release" fi # If we are at a tagged commit (like "v2.6.30-rc6"), we ignore From 1fe8a2bb64cdadd701e80a1946cfce242aa95a25 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Tue, 12 Oct 2021 09:56:12 -0700 Subject: [PATCH 05/71] BACKPORT: binder: use euid from cred instead of using task Save the 'struct cred' associated with a binder process at initial open to avoid potential race conditions when converting to an euid. Set a transaction's sender_euid from the 'struct cred' saved at binder_open() instead of looking up the euid from the binder proc's 'struct task'. This ensures the euid is associated with the security context that of the task that opened binder. Cc: stable@vger.kernel.org # 4.4+ Fixes: 457b9a6f09f0 ("Staging: android: add binder driver") Signed-off-by: Todd Kjos Suggested-by: Stephen Smalley Suggested-by: Jann Horn Acked-by: Casey Schaufler Signed-off-by: Paul Moore Bug: 200688826 (cherry picked from commit 29bc22ac5e5bc63275e850f0c8fc549e3d0e306b) [ refactored to avoid changing KMI: struct binder_proc ] Change-Id: Icaf996a7f5543b7d6943dff3cbe89bfc3614044c --- drivers/android/binder.c | 12 +++++++++--- drivers/android/binder_internal.h | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 020aadc4f0a9..51bfb4d55ff2 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2819,7 +2819,7 @@ static void binder_transaction(struct binder_proc *proc, t->from = thread; else t->from = NULL; - t->sender_euid = task_euid(proc->tsk); + t->sender_euid = binder_get_cred(proc)->euid; t->to_proc = target_proc; t->to_thread = target_thread; t->code = tr->code; @@ -4430,6 +4430,8 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc) static void binder_free_proc(struct binder_proc *proc) { struct binder_device *device; + struct binder_proc_ext *eproc = + container_of(proc, struct binder_proc_ext, proc); BUG_ON(!list_empty(&proc->todo)); BUG_ON(!list_empty(&proc->delivered_death)); @@ -4440,8 +4442,9 @@ static void binder_free_proc(struct binder_proc *proc) } binder_alloc_deferred_release(&proc->alloc); put_task_struct(proc->tsk); + put_cred(eproc->cred); binder_stats_deleted(BINDER_STAT_PROC); - kfree(proc); + kfree(eproc); } static void binder_free_thread(struct binder_thread *thread) @@ -4953,6 +4956,7 @@ err_bad_arg: static int binder_open(struct inode *nodp, struct file *filp) { struct binder_proc *proc, *itr; + struct binder_proc_ext *eproc; struct binder_device *binder_dev; struct binderfs_info *info; struct dentry *binder_binderfs_dir_entry_proc = NULL; @@ -4961,13 +4965,15 @@ static int binder_open(struct inode *nodp, struct file *filp) binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__, current->group_leader->pid, current->pid); - proc = kzalloc(sizeof(*proc), GFP_KERNEL); + eproc = kzalloc(sizeof(*eproc), GFP_KERNEL); + proc = &eproc->proc; if (proc == NULL) return -ENOMEM; spin_lock_init(&proc->inner_lock); spin_lock_init(&proc->outer_lock); get_task_struct(current->group_leader); proc->tsk = current->group_leader; + eproc->cred = get_cred(filp->f_cred); INIT_LIST_HEAD(&proc->todo); if (binder_supported_policy(current->policy)) { proc->default_priority.sched_policy = current->policy; diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 9765fde875a5..594699029568 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -449,6 +449,29 @@ struct binder_proc { struct dentry *binderfs_entry; }; +/** + * struct binder_proc_ext - binder process bookkeeping + * @proc: element for binder_procs list + * @cred struct cred associated with the `struct file` + * in binder_open() + * (invariant after initialized) + * + * Extended binder_proc -- needed to add the "cred" field without + * changing the KMI for binder_proc. + */ +struct binder_proc_ext { + struct binder_proc proc; + const struct cred *cred; +}; + +static inline const struct cred *binder_get_cred(struct binder_proc *proc) +{ + struct binder_proc_ext *eproc; + + eproc = container_of(proc, struct binder_proc_ext, proc); + return eproc->cred; +} + /** * struct binder_thread - binder thread bookkeeping * @proc: binder process for this thread From ea1a2391d530b3106fb50f9e8f8e59200daa5149 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Wed, 10 Nov 2021 15:00:24 -0800 Subject: [PATCH 06/71] BACKPORT: binder: use cred instead of task for selinux checks commit 52f88693378a58094c538662ba652aff0253c4fe upstream. Since binder was integrated with selinux, it has passed 'struct task_struct' associated with the binder_proc to represent the source and target of transactions. The conversion of task to SID was then done in the hook implementations. It turns out that there are race conditions which can result in an incorrect security context being used. Fix by using the 'struct cred' saved during binder_open and pass it to the selinux subsystem. Cc: stable@vger.kernel.org # 5.14 (need backport for earlier stables) Fixes: 79af73079d75 ("Add security hooks to binder and implement the hooks for SELinux.") Suggested-by: Jann Horn Signed-off-by: Todd Kjos Acked-by: Casey Schaufler Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman Change-Id: I9053ce58e7449b92928ed8a17148a598b5c40f21 Bug: 200688826 [ resolved minor conflict ] Signed-off-by: Todd Kjos --- drivers/android/binder.c | 15 +++++++++------ include/linux/lsm_hooks.h | 28 ++++++++++++++-------------- include/linux/security.h | 28 ++++++++++++++-------------- security/security.c | 14 +++++++------- security/selinux/hooks.c | 36 +++++++++++++++--------------------- 5 files changed, 59 insertions(+), 62 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 51bfb4d55ff2..03388c75a0e6 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2169,7 +2169,8 @@ static int binder_translate_binder(struct flat_binder_object *fp, ret = -EINVAL; goto done; } - if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + if (security_binder_transfer_binder(binder_get_cred(proc), + binder_get_cred(target_proc))) { ret = -EPERM; goto done; } @@ -2215,7 +2216,8 @@ static int binder_translate_handle(struct flat_binder_object *fp, proc->pid, thread->pid, fp->handle); return -EINVAL; } - if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + if (security_binder_transfer_binder(binder_get_cred(proc), + binder_get_cred(target_proc))) { ret = -EPERM; goto done; } @@ -2303,7 +2305,8 @@ static int binder_translate_fd(u32 fd, binder_size_t fd_offset, ret = -EBADF; goto err_fget; } - ret = security_binder_transfer_file(proc->tsk, target_proc->tsk, file); + ret = security_binder_transfer_file(binder_get_cred(proc), + binder_get_cred(target_proc), file); if (ret < 0) { ret = -EPERM; goto err_security; @@ -2702,8 +2705,8 @@ static void binder_transaction(struct binder_proc *proc, goto err_dead_binder; } e->to_node = target_node->debug_id; - if (security_binder_transaction(proc->tsk, - target_proc->tsk) < 0) { + if (security_binder_transaction(binder_get_cred(proc), + binder_get_cred(target_proc)) < 0) { return_error = BR_FAILED_REPLY; return_error_param = -EPERM; return_error_line = __LINE__; @@ -4654,7 +4657,7 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp, ret = -EBUSY; goto out; } - ret = security_binder_set_context_mgr(proc->tsk); + ret = security_binder_set_context_mgr(binder_get_cred(proc)); if (ret < 0) goto out; if (uid_valid(context->binder_context_mgr_uid)) { diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 20d8cf194fb7..84bca99cf562 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1241,22 +1241,22 @@ * * @binder_set_context_mgr: * Check whether @mgr is allowed to be the binder context manager. - * @mgr contains the task_struct for the task being registered. + * @mgr contains the struct cred for the current binder process. * Return 0 if permission is granted. * @binder_transaction: * Check whether @from is allowed to invoke a binder transaction call * to @to. - * @from contains the task_struct for the sending task. - * @to contains the task_struct for the receiving task. + * @from contains the struct cred for the sending process. + * @to contains the struct cred for the receiving process. * @binder_transfer_binder: * Check whether @from is allowed to transfer a binder reference to @to. - * @from contains the task_struct for the sending task. - * @to contains the task_struct for the receiving task. + * @from contains the struct cred for the sending process. + * @to contains the struct cred for the receiving process. * @binder_transfer_file: * Check whether @from is allowed to transfer @file to @to. - * @from contains the task_struct for the sending task. + * @from contains the struct cred for the sending process. * @file contains the struct file being transferred. - * @to contains the task_struct for the receiving task. + * @to contains the struct cred for the receiving process. * * @ptrace_access_check: * Check permission before allowing the current process to trace the @@ -1456,13 +1456,13 @@ * @what: kernel feature being accessed */ union security_list_options { - int (*binder_set_context_mgr)(struct task_struct *mgr); - int (*binder_transaction)(struct task_struct *from, - struct task_struct *to); - int (*binder_transfer_binder)(struct task_struct *from, - struct task_struct *to); - int (*binder_transfer_file)(struct task_struct *from, - struct task_struct *to, + int (*binder_set_context_mgr)(const struct cred *mgr); + int (*binder_transaction)(const struct cred *from, + const struct cred *to); + int (*binder_transfer_binder)(const struct cred *from, + const struct cred *to); + int (*binder_transfer_file)(const struct cred *from, + const struct cred *to, struct file *file); int (*ptrace_access_check)(struct task_struct *child, diff --git a/include/linux/security.h b/include/linux/security.h index 269625983941..6465e86b5013 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -249,13 +249,13 @@ extern int security_init(void); extern int early_security_init(void); /* Security operations */ -int security_binder_set_context_mgr(struct task_struct *mgr); -int security_binder_transaction(struct task_struct *from, - struct task_struct *to); -int security_binder_transfer_binder(struct task_struct *from, - struct task_struct *to); -int security_binder_transfer_file(struct task_struct *from, - struct task_struct *to, struct file *file); +int security_binder_set_context_mgr(const struct cred *mgr); +int security_binder_transaction(const struct cred *from, + const struct cred *to); +int security_binder_transfer_binder(const struct cred *from, + const struct cred *to); +int security_binder_transfer_file(const struct cred *from, + const struct cred *to, struct file *file); int security_ptrace_access_check(struct task_struct *child, unsigned int mode); int security_ptrace_traceme(struct task_struct *parent); int security_capget(struct task_struct *target, @@ -481,25 +481,25 @@ static inline int early_security_init(void) return 0; } -static inline int security_binder_set_context_mgr(struct task_struct *mgr) +static inline int security_binder_set_context_mgr(const struct cred *mgr) { return 0; } -static inline int security_binder_transaction(struct task_struct *from, - struct task_struct *to) +static inline int security_binder_transaction(const struct cred *from, + const struct cred *to) { return 0; } -static inline int security_binder_transfer_binder(struct task_struct *from, - struct task_struct *to) +static inline int security_binder_transfer_binder(const struct cred *from, + const struct cred *to) { return 0; } -static inline int security_binder_transfer_file(struct task_struct *from, - struct task_struct *to, +static inline int security_binder_transfer_file(const struct cred *from, + const struct cred *to, struct file *file) { return 0; diff --git a/security/security.c b/security/security.c index cd2d18d2d279..9335b2e1bcd2 100644 --- a/security/security.c +++ b/security/security.c @@ -670,25 +670,25 @@ static void __init lsm_early_task(struct task_struct *task) /* Security operations */ -int security_binder_set_context_mgr(struct task_struct *mgr) +int security_binder_set_context_mgr(const struct cred *mgr) { return call_int_hook(binder_set_context_mgr, 0, mgr); } -int security_binder_transaction(struct task_struct *from, - struct task_struct *to) +int security_binder_transaction(const struct cred *from, + const struct cred *to) { return call_int_hook(binder_transaction, 0, from, to); } -int security_binder_transfer_binder(struct task_struct *from, - struct task_struct *to) +int security_binder_transfer_binder(const struct cred *from, + const struct cred *to) { return call_int_hook(binder_transfer_binder, 0, from, to); } -int security_binder_transfer_file(struct task_struct *from, - struct task_struct *to, struct file *file) +int security_binder_transfer_file(const struct cred *from, + const struct cred *to, struct file *file) { return call_int_hook(binder_transfer_file, 0, from, to, file); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 027c7b99434e..912f9d7fee6f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2055,22 +2055,19 @@ static inline u32 open_file_to_av(struct file *file) /* Hook functions begin here. */ -static int selinux_binder_set_context_mgr(struct task_struct *mgr) +static int selinux_binder_set_context_mgr(const struct cred *mgr) { - u32 mysid = current_sid(); - u32 mgrsid = task_sid(mgr); - return avc_has_perm(&selinux_state, - mysid, mgrsid, SECCLASS_BINDER, + current_sid(), cred_sid(mgr), SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); } -static int selinux_binder_transaction(struct task_struct *from, - struct task_struct *to) +static int selinux_binder_transaction(const struct cred *from, + const struct cred *to) { u32 mysid = current_sid(); - u32 fromsid = task_sid(from); - u32 tosid = task_sid(to); + u32 fromsid = cred_sid(from); + u32 tosid = cred_sid(to); int rc; if (mysid != fromsid) { @@ -2081,27 +2078,24 @@ static int selinux_binder_transaction(struct task_struct *from, return rc; } - return avc_has_perm(&selinux_state, - fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, - NULL); + return avc_has_perm(&selinux_state, fromsid, tosid, + SECCLASS_BINDER, BINDER__CALL, NULL); } -static int selinux_binder_transfer_binder(struct task_struct *from, - struct task_struct *to) +static int selinux_binder_transfer_binder(const struct cred *from, + const struct cred *to) { - u32 fromsid = task_sid(from); - u32 tosid = task_sid(to); - return avc_has_perm(&selinux_state, - fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, + cred_sid(from), cred_sid(to), + SECCLASS_BINDER, BINDER__TRANSFER, NULL); } -static int selinux_binder_transfer_file(struct task_struct *from, - struct task_struct *to, +static int selinux_binder_transfer_file(const struct cred *from, + const struct cred *to, struct file *file) { - u32 sid = task_sid(to); + u32 sid = cred_sid(to); struct file_security_struct *fsec = selinux_file(file); struct dentry *dentry = file->f_path.dentry; struct inode_security_struct *isec; From 1c1f571840bc39f66f598b615524ef79c62a53ac Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Wed, 10 Nov 2021 15:00:25 -0800 Subject: [PATCH 07/71] BACKPORT: binder: use cred instead of task for getsecid commit 4d5b5539742d2554591751b4248b0204d20dcc9d upstream. Use the 'struct cred' saved at binder_open() to lookup the security ID via security_cred_getsecid(). This ensures that the security context that opened binder is the one used to generate the secctx. Cc: stable@vger.kernel.org # 5.4+ Fixes: ec74136ded79 ("binder: create node flag to request sender's security context") Signed-off-by: Todd Kjos Suggested-by: Stephen Smalley Reported-by: kernel test robot Acked-by: Casey Schaufler Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman Bug: 200688826 [ fixed minor conflict ] Signed-off-by: Todd Kjos Change-Id: I4ef69da21d7ae651895c498b3aa224f180026fa9 --- drivers/android/binder.c | 2 +- include/linux/security.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 03388c75a0e6..2ae6591d0dd3 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2841,7 +2841,7 @@ static void binder_transaction(struct binder_proc *proc, u32 secid; size_t added_size; - security_task_getsecid(proc->tsk, &secid); + security_cred_getsecid(binder_get_cred(proc), &secid); ret = security_secid_to_secctx(secid, &secctx, &secctx_sz); if (ret) { return_error = BR_FAILED_REPLY; diff --git a/include/linux/security.h b/include/linux/security.h index 6465e86b5013..a52748661a38 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -985,6 +985,11 @@ static inline void security_transfer_creds(struct cred *new, { } +static inline void security_cred_getsecid(const struct cred *c, u32 *secid) +{ + *secid = 0; +} + static inline int security_kernel_act_as(struct cred *cred, u32 secid) { return 0; From 50c9c8cb332373f0a6b308baa6a2af2402044a12 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Fri, 12 Nov 2021 10:07:20 -0800 Subject: [PATCH 08/71] FROMGIT: binder: fix test regression due to sender_euid change This is a partial revert of commit 29bc22ac5e5b ("binder: use euid from cred instead of using task"). Setting sender_euid using proc->cred caused some Android system test regressions that need further investigation. It is a partial reversion because subsequent patches rely on proc->cred. Fixes: 29bc22ac5e5b ("binder: use euid from cred instead of using task") Cc: stable@vger.kernel.org # 4.4+ Acked-by: Christian Brauner Signed-off-by: Todd Kjos Change-Id: I9b1769a3510fed250bb21859ef8beebabe034c66 Link: https://lore.kernel.org/r/20211112180720.2858135-1-tkjos@google.com Signed-off-by: Greg Kroah-Hartman Bug: 200688826 (cherry picked from commit c21a80ca0684ec2910344d72556c816cb8940c01 git: //git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-linus) Signed-off-by: Todd Kjos --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 2ae6591d0dd3..e4b32802e608 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2822,7 +2822,7 @@ static void binder_transaction(struct binder_proc *proc, t->from = thread; else t->from = NULL; - t->sender_euid = binder_get_cred(proc)->euid; + t->sender_euid = task_euid(proc->tsk); t->to_proc = target_proc; t->to_thread = target_thread; t->code = tr->code; From 63ef44477302ee8e69bb8e247d8f7c626db21e59 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 25 Nov 2021 11:52:14 +0000 Subject: [PATCH 09/71] ANDROID: usb: gadget: f_accessory: Mitgate handling of non-existent USB request Prevents mishandling USB requests that are no longer present. Bug: 161010552 Fixes: 483cb5629ea78 ("ANDROID: usb: gadget: f_accessory: Add Android Accessory function") Signed-off-by: Lee Jones Change-Id: I8ff24d6d49214c3bd10a1b5d5e72814ec2a91c61 --- drivers/usb/gadget/function/f_accessory.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 718e01e3190f..b6adf7d803ef 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -601,8 +601,11 @@ fail: pr_err("acc_bind() could not allocate requests\n"); while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); - for (i = 0; i < RX_REQ_MAX; i++) + for (i = 0; i < RX_REQ_MAX; i++) { acc_request_free(dev->rx_req[i], dev->ep_out); + dev->rx_req[i] = NULL; + } + return -1; } @@ -634,6 +637,12 @@ static ssize_t acc_read(struct file *fp, char __user *buf, goto done; } + if (!dev->rx_req[0]) { + pr_warn("acc_read: USB request already handled/freed"); + r = -EINVAL; + goto done; + } + /* * Calculate the data length by considering termination character. * Then compansite the difference of rounding up to @@ -1098,8 +1107,10 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); - for (i = 0; i < RX_REQ_MAX; i++) + for (i = 0; i < RX_REQ_MAX; i++) { acc_request_free(dev->rx_req[i], dev->ep_out); + dev->rx_req[i] = NULL; + } acc_hid_unbind(dev); } From e658e9e4bcd95242f8d994cc39abcc4af46ef9a3 Mon Sep 17 00:00:00 2001 From: Marco Ballesio Date: Tue, 9 Mar 2021 18:18:56 -0800 Subject: [PATCH 10/71] BACKPORT: FROMGIT: binder: BINDER_FREEZE ioctl Frozen tasks can't process binder transactions, so a way is required to inform transmitting ends of communication failures due to the frozen state of their receiving counterparts. Additionally, races are possible between transitions to frozen state and binder transactions enqueued to a specific process. Implement BINDER_FREEZE ioctl for user space to inform the binder driver about the intention to freeze or unfreeze a process. When the ioctl is called, block the caller until any pending binder transactions toward the target process are flushed. Return an error to transactions to processes marked as frozen. Bug: 180989544 (cherry picked from commit 15949c3cdd97bccdcd45c0c0f6c31058520b6494 git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-testing) Co-developed-by: Todd Kjos Acked-by: Todd Kjos Signed-off-by: Marco Ballesio Signed-off-by: Todd Kjos Signed-off-by: Li Li Link: https://lore.kernel.org/r/20210316011630.1121213-2-dualli@chromium.org Signed-off-by: Greg Kroah-Hartman Change-Id: Ia1b5951cd99eeb98b59e06c3e27d59062dc725f6 --- drivers/android/binder.c | 139 ++++++++++++++++++++++++++-- drivers/android/binder_internal.h | 12 +++ include/uapi/linux/android/binder.h | 13 +++ 3 files changed, 154 insertions(+), 10 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index e4b32802e608..22e4e3107de4 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1628,6 +1628,12 @@ static void binder_free_transaction(struct binder_transaction *t) if (target_proc) { binder_inner_proc_lock(target_proc); + target_proc->outstanding_txns--; + if (target_proc->outstanding_txns < 0) + pr_warn("%s: Unexpected outstanding_txns %d\n", + __func__, target_proc->outstanding_txns); + if (!target_proc->outstanding_txns && target_proc->is_frozen) + wake_up_interruptible_all(&target_proc->freeze_wait); if (t->buffer) t->buffer->transaction = NULL; binder_inner_proc_unlock(target_proc); @@ -2462,10 +2468,11 @@ static int binder_fixup_parent(struct binder_transaction *t, * If the @thread parameter is not NULL, the transaction is always queued * to the waitlist of that specific thread. * - * Return: true if the transactions was successfully queued - * false if the target process or thread is dead + * Return: 0 if the transaction was successfully queued + * BR_DEAD_REPLY if the target process or thread is dead + * BR_FROZEN_REPLY if the target process or thread is frozen */ -static bool binder_proc_transaction(struct binder_transaction *t, +static int binder_proc_transaction(struct binder_transaction *t, struct binder_proc *proc, struct binder_thread *thread) { @@ -2490,10 +2497,11 @@ static bool binder_proc_transaction(struct binder_transaction *t, binder_inner_proc_lock(proc); - if (proc->is_dead || (thread && thread->is_dead)) { + if ((proc->is_frozen && !oneway) || proc->is_dead || + (thread && thread->is_dead)) { binder_inner_proc_unlock(proc); binder_node_unlock(node); - return false; + return proc->is_frozen ? BR_FROZEN_REPLY : BR_DEAD_REPLY; } if (!thread && !pending_async) @@ -2512,10 +2520,11 @@ static bool binder_proc_transaction(struct binder_transaction *t, if (!pending_async) binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); + proc->outstanding_txns++; binder_inner_proc_unlock(proc); binder_node_unlock(node); - return true; + return 0; } /** @@ -3154,13 +3163,16 @@ static void binder_transaction(struct binder_proc *proc, if (reply) { binder_enqueue_thread_work(thread, tcomplete); binder_inner_proc_lock(target_proc); - if (target_thread->is_dead) { + if (target_thread->is_dead || target_proc->is_frozen) { + return_error = target_thread->is_dead ? + BR_DEAD_REPLY : BR_FROZEN_REPLY; binder_inner_proc_unlock(target_proc); goto err_dead_proc_or_thread; } BUG_ON(t->buffer->async_transaction != 0); binder_pop_transaction_ilocked(target_thread, in_reply_to); binder_enqueue_thread_work_ilocked(target_thread, &t->work); + target_proc->outstanding_txns++; binder_inner_proc_unlock(target_proc); wake_up_interruptible_sync(&target_thread->wait); trace_android_vh_binder_restore_priority(in_reply_to, current); @@ -3181,7 +3193,9 @@ static void binder_transaction(struct binder_proc *proc, t->from_parent = thread->transaction_stack; thread->transaction_stack = t; binder_inner_proc_unlock(proc); - if (!binder_proc_transaction(t, target_proc, target_thread)) { + return_error = binder_proc_transaction(t, + target_proc, target_thread); + if (return_error) { binder_inner_proc_lock(proc); binder_pop_transaction_ilocked(thread, t); binder_inner_proc_unlock(proc); @@ -3191,7 +3205,8 @@ static void binder_transaction(struct binder_proc *proc, BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); binder_enqueue_thread_work(thread, tcomplete); - if (!binder_proc_transaction(t, target_proc, NULL)) + return_error = binder_proc_transaction(t, target_proc, NULL); + if (return_error) goto err_dead_proc_or_thread; } if (target_thread) @@ -3208,7 +3223,6 @@ static void binder_transaction(struct binder_proc *proc, return; err_dead_proc_or_thread: - return_error = BR_DEAD_REPLY; return_error_line = __LINE__; binder_dequeue_work(proc, tcomplete); err_translate_failed: @@ -4438,6 +4452,9 @@ static void binder_free_proc(struct binder_proc *proc) BUG_ON(!list_empty(&proc->todo)); BUG_ON(!list_empty(&proc->delivered_death)); + if (proc->outstanding_txns) + pr_warn("%s: Unexpected outstanding_txns %d\n", + __func__, proc->outstanding_txns); device = container_of(proc->context, struct binder_device, context); if (refcount_dec_and_test(&device->ref)) { kfree(proc->context->name); @@ -4501,6 +4518,7 @@ static int binder_thread_release(struct binder_proc *proc, (t->to_thread == thread) ? "in" : "out"); if (t->to_thread == thread) { + thread->proc->outstanding_txns--; t->to_proc = NULL; t->to_thread = NULL; if (t->buffer) { @@ -4751,6 +4769,45 @@ static int binder_ioctl_get_node_debug_info(struct binder_proc *proc, return 0; } +static int binder_ioctl_freeze(struct binder_freeze_info *info, + struct binder_proc *target_proc) +{ + int ret = 0; + + if (!info->enable) { + binder_inner_proc_lock(target_proc); + target_proc->is_frozen = false; + binder_inner_proc_unlock(target_proc); + return 0; + } + + /* + * Freezing the target. Prevent new transactions by + * setting frozen state. If timeout specified, wait + * for transactions to drain. + */ + binder_inner_proc_lock(target_proc); + target_proc->is_frozen = true; + binder_inner_proc_unlock(target_proc); + + if (info->timeout_ms > 0) + ret = wait_event_interruptible_timeout( + target_proc->freeze_wait, + (!target_proc->outstanding_txns), + msecs_to_jiffies(info->timeout_ms)); + + if (!ret && target_proc->outstanding_txns) + ret = -EAGAIN; + + if (ret < 0) { + binder_inner_proc_lock(target_proc); + target_proc->is_frozen = false; + binder_inner_proc_unlock(target_proc); + } + + return ret; +} + static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; @@ -4869,6 +4926,66 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; } + case BINDER_FREEZE: { + struct binder_freeze_info info; + struct binder_proc **target_procs = NULL, *target_proc; + int target_procs_count = 0, i = 0; + + ret = 0; + + if (copy_from_user(&info, ubuf, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + mutex_lock(&binder_procs_lock); + hlist_for_each_entry(target_proc, &binder_procs, proc_node) { + if (target_proc->pid == info.pid) + target_procs_count++; + } + + if (target_procs_count == 0) { + mutex_unlock(&binder_procs_lock); + ret = -EINVAL; + goto err; + } + + target_procs = kcalloc(target_procs_count, + sizeof(struct binder_proc *), + GFP_KERNEL); + + if (!target_procs) { + mutex_unlock(&binder_procs_lock); + ret = -ENOMEM; + goto err; + } + + hlist_for_each_entry(target_proc, &binder_procs, proc_node) { + if (target_proc->pid != info.pid) + continue; + + binder_inner_proc_lock(target_proc); + target_proc->tmp_ref++; + binder_inner_proc_unlock(target_proc); + + target_procs[i++] = target_proc; + } + mutex_unlock(&binder_procs_lock); + + for (i = 0; i < target_procs_count; i++) { + if (ret >= 0) + ret = binder_ioctl_freeze(&info, + target_procs[i]); + + binder_proc_dec_tmpref(target_procs[i]); + } + + kfree(target_procs); + + if (ret < 0) + goto err; + break; + } default: ret = -EINVAL; goto err; @@ -4978,6 +5095,7 @@ static int binder_open(struct inode *nodp, struct file *filp) proc->tsk = current->group_leader; eproc->cred = get_cred(filp->f_cred); INIT_LIST_HEAD(&proc->todo); + init_waitqueue_head(&proc->freeze_wait); if (binder_supported_policy(current->policy)) { proc->default_priority.sched_policy = current->policy; proc->default_priority.prio = current->normal_prio; @@ -5197,6 +5315,7 @@ static void binder_deferred_release(struct binder_proc *proc) proc->tmp_ref++; proc->is_dead = true; + proc->is_frozen = false; threads = 0; active_transactions = 0; while ((n = rb_first(&proc->threads))) { diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 594699029568..4f02eb0b6903 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -388,9 +388,18 @@ struct binder_priority { * (protected by binder_deferred_lock) * @deferred_work: bitmap of deferred work to perform * (protected by binder_deferred_lock) + * @outstanding_txns: number of transactions to be transmitted before + * processes in freeze_wait are woken up + * (protected by @inner_lock) * @is_dead: process is dead and awaiting free * when outstanding transactions are cleaned up * (protected by @inner_lock) + * @is_frozen: process is frozen and unable to service + * binder transactions + * (protected by @inner_lock) + * @freeze_wait: waitqueue of processes waiting for all outstanding + * transactions to be processed + * (protected by @inner_lock) * @todo: list of work for this process * (protected by @inner_lock) * @stats: per-process binder statistics @@ -431,7 +440,10 @@ struct binder_proc { struct task_struct *tsk; struct hlist_node deferred_work_node; int deferred_work; + int outstanding_txns; bool is_dead; + bool is_frozen; + wait_queue_head_t freeze_wait; struct list_head todo; struct binder_stats stats; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 25d12f7ab3f1..88c99a1abc5d 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -265,6 +265,12 @@ struct binder_node_info_for_ref { __u32 reserved3; }; +struct binder_freeze_info { + __u32 pid; + __u32 enable; + __u32 timeout_ms; +}; + #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) @@ -275,6 +281,7 @@ struct binder_node_info_for_ref { #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) #define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) #define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object) +#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info) /* * NOTE: Two special error codes you should check for when calling @@ -456,6 +463,12 @@ enum binder_driver_return_protocol { * The the last transaction (either a bcTRANSACTION or * a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters. */ + + BR_FROZEN_REPLY = _IO('r', 18), + /* + * The target of the last transaction (either a bcTRANSACTION or + * a bcATTEMPT_ACQUIRE) is frozen. No parameters. + */ }; enum binder_driver_command_protocol { From a02d0f685d47b1a7aa7eb3a140f28713d1577121 Mon Sep 17 00:00:00 2001 From: Marco Ballesio Date: Tue, 9 Mar 2021 16:35:45 -0800 Subject: [PATCH 11/71] FROMGIT: binder: use EINTR for interrupted wait for work when interrupted by a signal, binder_wait_for_work currently returns -ERESTARTSYS. This error code isn't propagated to user space, but a way to handle interruption due to signals must be provided to code using this API. Replace this instance of -ERESTARTSYS with -EINTR, which is propagated to user space. Bug: 180989544 (cherry picked from commit 48f10b7ed0c23e2df7b2c752ad1d3559dad007f9 git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-testing) Signed-off-by: Marco Ballesio Signed-off-by: Li Li Test: built, booted, interrupted a worker thread within Acked-by: Todd Kjos Link: https://lore.kernel.org/r/20210316011630.1121213-3-dualli@chromium.org Signed-off-by: Greg Kroah-Hartman Change-Id: Ie6c7993cab699bc2c1a25a2f9d94b200a1156e5d --- drivers/android/binder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 22e4e3107de4..e6e6a444e510 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3847,7 +3847,7 @@ static int binder_wait_for_work(struct binder_thread *thread, binder_inner_proc_lock(proc); list_del_init(&thread->waiting_thread_node); if (signal_pending(current)) { - ret = -ERESTARTSYS; + ret = -EINTR; break; } } @@ -4995,7 +4995,7 @@ err: if (thread) thread->looper_need_return = false; wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); - if (ret && ret != -ERESTARTSYS) + if (ret && ret != -EINTR) pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); err_unlocked: trace_binder_ioctl_done(ret); From b1232b020f2ff0dbdbff97f4dca7b3325aca9f93 Mon Sep 17 00:00:00 2001 From: Marco Ballesio Date: Tue, 9 Mar 2021 16:54:53 -0800 Subject: [PATCH 12/71] FROMGIT: binder: BINDER_GET_FROZEN_INFO ioctl User space needs to know if binder transactions occurred to frozen processes. Introduce a new BINDER_GET_FROZEN ioctl and keep track of transactions occurring to frozen proceses. Bug: 180989544 (cherry picked from commit c55019c24b22d6770bd8e2f12fbddf3f83d37547 git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-testing) Signed-off-by: Marco Ballesio Signed-off-by: Li Li Acked-by: Todd Kjos Link: https://lore.kernel.org/r/20210316011630.1121213-4-dualli@chromium.org Signed-off-by: Greg Kroah-Hartman Change-Id: Ie631f331ba4ca94a3bcdd43dec25fe9ba1306af2 --- drivers/android/binder.c | 55 +++++++++++++++++++++++++++++ drivers/android/binder_internal.h | 6 ++++ include/uapi/linux/android/binder.h | 7 ++++ 3 files changed, 68 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index e6e6a444e510..d8615c59796d 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2496,6 +2496,10 @@ static int binder_proc_transaction(struct binder_transaction *t, } binder_inner_proc_lock(proc); + if (proc->is_frozen) { + proc->sync_recv |= !oneway; + proc->async_recv |= oneway; + } if ((proc->is_frozen && !oneway) || proc->is_dead || (thread && thread->is_dead)) { @@ -4776,6 +4780,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, if (!info->enable) { binder_inner_proc_lock(target_proc); + target_proc->sync_recv = false; + target_proc->async_recv = false; target_proc->is_frozen = false; binder_inner_proc_unlock(target_proc); return 0; @@ -4787,6 +4793,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, * for transactions to drain. */ binder_inner_proc_lock(target_proc); + target_proc->sync_recv = false; + target_proc->async_recv = false; target_proc->is_frozen = true; binder_inner_proc_unlock(target_proc); @@ -4808,6 +4816,33 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, return ret; } +static int binder_ioctl_get_freezer_info( + struct binder_frozen_status_info *info) +{ + struct binder_proc *target_proc; + bool found = false; + + info->sync_recv = 0; + info->async_recv = 0; + + mutex_lock(&binder_procs_lock); + hlist_for_each_entry(target_proc, &binder_procs, proc_node) { + if (target_proc->pid == info->pid) { + found = true; + binder_inner_proc_lock(target_proc); + info->sync_recv |= target_proc->sync_recv; + info->async_recv |= target_proc->async_recv; + binder_inner_proc_unlock(target_proc); + } + } + mutex_unlock(&binder_procs_lock); + + if (!found) + return -EINVAL; + + return 0; +} + static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; @@ -4986,6 +5021,24 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) goto err; break; } + case BINDER_GET_FROZEN_INFO: { + struct binder_frozen_status_info info; + + if (copy_from_user(&info, ubuf, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + ret = binder_ioctl_get_freezer_info(&info); + if (ret < 0) + goto err; + + if (copy_to_user(ubuf, &info, sizeof(info))) { + ret = -EFAULT; + goto err; + } + break; + } default: ret = -EINVAL; goto err; @@ -5316,6 +5369,8 @@ static void binder_deferred_release(struct binder_proc *proc) proc->is_dead = true; proc->is_frozen = false; + proc->sync_recv = false; + proc->async_recv = false; threads = 0; active_transactions = 0; while ((n = rb_first(&proc->threads))) { diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 4f02eb0b6903..0b0c8e53e13c 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -397,6 +397,10 @@ struct binder_priority { * @is_frozen: process is frozen and unable to service * binder transactions * (protected by @inner_lock) + * @sync_recv: process received sync transactions since last frozen + * (protected by @inner_lock) + * @async_recv: process received async transactions since last frozen + * (protected by @inner_lock) * @freeze_wait: waitqueue of processes waiting for all outstanding * transactions to be processed * (protected by @inner_lock) @@ -443,6 +447,8 @@ struct binder_proc { int outstanding_txns; bool is_dead; bool is_frozen; + bool sync_recv; + bool async_recv; wait_queue_head_t freeze_wait; struct list_head todo; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 88c99a1abc5d..926b05cf8e2c 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -271,6 +271,12 @@ struct binder_freeze_info { __u32 timeout_ms; }; +struct binder_frozen_status_info { + __u32 pid; + __u32 sync_recv; + __u32 async_recv; +}; + #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) @@ -282,6 +288,7 @@ struct binder_freeze_info { #define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) #define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object) #define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info) +#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) /* * NOTE: Two special error codes you should check for when calling From ce664fbdcc0c86319acf3b198dfd7683b7ed9db4 Mon Sep 17 00:00:00 2001 From: Li Li Date: Tue, 7 Sep 2021 16:12:53 -0700 Subject: [PATCH 13/71] BACKPORT: FROMGIT: binder: fix freeze race Currently cgroup freezer is used to freeze the application threads, and BINDER_FREEZE is used to freeze the corresponding binder interface. There's already a mechanism in ioctl(BINDER_FREEZE) to wait for any existing transactions to drain out before actually freezing the binder interface. But freezing an app requires 2 steps, freezing the binder interface with ioctl(BINDER_FREEZE) and then freezing the application main threads with cgroupfs. This is not an atomic operation. The following race issue might happen. 1) Binder interface is frozen by ioctl(BINDER_FREEZE); 2) Main thread A initiates a new sync binder transaction to process B; 3) Main thread A is frozen by "echo 1 > cgroup.freeze"; 4) The response from process B reaches the frozen thread, which will unexpectedly fail. This patch provides a mechanism to check if there's any new pending transaction happening between ioctl(BINDER_FREEZE) and freezing the main thread. If there's any, the main thread freezing operation can be rolled back to finish the pending transaction. Furthermore, the response might reach the binder driver before the rollback actually happens. That will still cause failed transaction. As the other process doesn't wait for another response of the response, the response transaction failure can be fixed by treating the response transaction like an oneway/async one, allowing it to reach the frozen thread. And it will be consumed when the thread gets unfrozen later. NOTE: This patch reuses the existing definition of struct binder_frozen_status_info but expands the bit assignments of __u32 member sync_recv. To ensure backward compatibility, bit 0 of sync_recv still indicates there's an outstanding sync binder transaction. This patch adds new information to bit 1 of sync_recv, indicating the binder transaction happens exactly when there's a race. If an existing userspace app runs on a new kernel, a sync binder call will set bit 0 of sync_recv so ioctl(BINDER_GET_FROZEN_INFO) still return the expected value (true). The app just doesn't check bit 1 intentionally so it doesn't have the ability to tell if there's a race. This behavior is aligned with what happens on an old kernel which doesn't set bit 1 at all. A new userspace app can 1) check bit 0 to know if there's a sync binder transaction happened when being frozen - same as before; and 2) check bit 1 to know if that sync binder transaction happened exactly when there's a race - a new information for rollback decision. Fixes: 432ff1e91694 ("binder: BINDER_FREEZE ioctl") Acked-by: Todd Kjos Cc: stable Signed-off-by: Li Li Test: stress test with apps being frozen and initiating binder calls at the same time, confirmed the pending transactions succeeded. Link: https://lore.kernel.org/r/20210910164210.2282716-2-dualli@chromium.org Signed-off-by: Greg Kroah-Hartman Bug: 198493121 (cherry picked from commit b564171ade70570b7f335fa8ed17adb28409e3ac git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-linus) Change-Id: I488ba75056f18bb3094ba5007027b76b5caebec9 --- drivers/android/binder.c | 35 ++++++++++++++++++++++++----- drivers/android/binder_internal.h | 2 ++ include/uapi/linux/android/binder.h | 7 ++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index d8615c59796d..7d86c149e4e4 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3167,9 +3167,8 @@ static void binder_transaction(struct binder_proc *proc, if (reply) { binder_enqueue_thread_work(thread, tcomplete); binder_inner_proc_lock(target_proc); - if (target_thread->is_dead || target_proc->is_frozen) { - return_error = target_thread->is_dead ? - BR_DEAD_REPLY : BR_FROZEN_REPLY; + if (target_thread->is_dead) { + return_error = BR_DEAD_REPLY; binder_inner_proc_unlock(target_proc); goto err_dead_proc_or_thread; } @@ -4773,6 +4772,22 @@ static int binder_ioctl_get_node_debug_info(struct binder_proc *proc, return 0; } +static bool binder_txns_pending_ilocked(struct binder_proc *proc) +{ + struct rb_node *n; + struct binder_thread *thread; + + if (proc->outstanding_txns > 0) + return true; + + for (n = rb_first(&proc->threads); n; n = rb_next(n)) { + thread = rb_entry(n, struct binder_thread, rb_node); + if (thread->transaction_stack) + return true; + } + return false; +} + static int binder_ioctl_freeze(struct binder_freeze_info *info, struct binder_proc *target_proc) { @@ -4804,8 +4819,13 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, (!target_proc->outstanding_txns), msecs_to_jiffies(info->timeout_ms)); - if (!ret && target_proc->outstanding_txns) - ret = -EAGAIN; + /* Check pending transactions that wait for reply */ + if (ret >= 0) { + binder_inner_proc_lock(target_proc); + if (binder_txns_pending_ilocked(target_proc)) + ret = -EAGAIN; + binder_inner_proc_unlock(target_proc); + } if (ret < 0) { binder_inner_proc_lock(target_proc); @@ -4821,6 +4841,7 @@ static int binder_ioctl_get_freezer_info( { struct binder_proc *target_proc; bool found = false; + __u32 txns_pending; info->sync_recv = 0; info->async_recv = 0; @@ -4830,7 +4851,9 @@ static int binder_ioctl_get_freezer_info( if (target_proc->pid == info->pid) { found = true; binder_inner_proc_lock(target_proc); - info->sync_recv |= target_proc->sync_recv; + txns_pending = binder_txns_pending_ilocked(target_proc); + info->sync_recv |= target_proc->sync_recv | + (txns_pending << 1); info->async_recv |= target_proc->async_recv; binder_inner_proc_unlock(target_proc); } diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 0b0c8e53e13c..7c5993bcfc10 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -398,6 +398,8 @@ struct binder_priority { * binder transactions * (protected by @inner_lock) * @sync_recv: process received sync transactions since last frozen + * bit 0: received sync transaction after being frozen + * bit 1: new pending sync transaction during freezing * (protected by @inner_lock) * @async_recv: process received async transactions since last frozen * (protected by @inner_lock) diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 926b05cf8e2c..14340b0267b7 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -273,7 +273,14 @@ struct binder_freeze_info { struct binder_frozen_status_info { __u32 pid; + + /* process received sync transactions since last frozen + * bit 0: received sync transaction after being frozen + * bit 1: new pending sync transaction during freezing + */ __u32 sync_recv; + + /* process received async transactions since last frozen */ __u32 async_recv; }; From e6fd4c5c6a353d07409b58711076535153495c09 Mon Sep 17 00:00:00 2001 From: Wang Xiaojun Date: Thu, 17 Sep 2020 19:11:58 +0800 Subject: [PATCH 14/71] BACKPORT: f2fs: fix wrong total_sections check and fsmeta check Meta area is not included in section_count computation. So the minimum number of total_sections is 1 meanwhile it cannot be greater than segment_count_main. The minimum number of meta segments is 8 (SB + 2 (CP + SIT + NAT) + SSA). Bug: 208371174 Signed-off-by: Wang Xiaojun Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim (cherry picked from commit 56faf97ebce8) Change-Id: I36a97300e13bd760c29958bc36d85b441dd40eaa --- fs/f2fs/segment.h | 1 + fs/f2fs/super.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 218487dd9f02..084d37b21c29 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -16,6 +16,7 @@ #define DEF_MAX_RECLAIM_PREFREE_SEGMENTS 4096 /* 8GB in maximum */ #define F2FS_MIN_SEGMENTS 9 /* SB + 2 (CP + SIT + NAT) + SSA + MAIN */ +#define F2FS_MIN_META_SEGMENTS 8 /* SB + 2 (CP + SIT + NAT) + SSA */ /* L: Logical segment # in volume, R: Relative segment # in main area */ #define GET_L2R_SEGNO(free_i, segno) ((segno) - (free_i)->start_segno) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 2add5545a4bb..d4970371f376 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2708,7 +2708,7 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi, static int sanity_check_raw_super(struct f2fs_sb_info *sbi, struct buffer_head *bh) { - block_t segment_count, segs_per_sec, secs_per_zone; + block_t segment_count, segs_per_sec, secs_per_zone, segment_count_main; block_t total_sections, blocks_per_seg; struct f2fs_super_block *raw_super = (struct f2fs_super_block *) (bh->b_data + F2FS_SUPER_OFFSET); @@ -2778,6 +2778,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, } segment_count = le32_to_cpu(raw_super->segment_count); + segment_count_main = le32_to_cpu(raw_super->segment_count_main); segs_per_sec = le32_to_cpu(raw_super->segs_per_sec); secs_per_zone = le32_to_cpu(raw_super->secs_per_zone); total_sections = le32_to_cpu(raw_super->section_count); @@ -2791,8 +2792,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, return -EFSCORRUPTED; } - if (total_sections > segment_count || - total_sections < F2FS_MIN_SEGMENTS || + if (total_sections > segment_count_main || total_sections < 1 || segs_per_sec > segment_count || !segs_per_sec) { f2fs_info(sbi, "Invalid segment/section count (%u, %u x %u)", segment_count, total_sections, segs_per_sec); @@ -2899,7 +2899,7 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi) ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); - if (unlikely(fsmeta < F2FS_MIN_SEGMENTS || + if (unlikely(fsmeta < F2FS_MIN_META_SEGMENTS || ovp_segments == 0 || reserved_segments == 0)) { f2fs_err(sbi, "Wrong layout: check mkfs.f2fs version"); return 1; From 4f4602b711a1b6f26dc9e60f0c92176c505df929 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 21 May 2021 01:32:53 -0700 Subject: [PATCH 15/71] BACKPORT: f2fs: support RO feature Given RO feature in superblock, we don't need to check provisioning/reserve spaces and SSA area. Bug: 208371174 Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim (cherry picked from commit 60bad8157831) Change-Id: I2ec5fda15948bb17bd34b3b1c3efc356f527b515 --- fs/f2fs/f2fs.h | 3 +++ fs/f2fs/segment.c | 4 ++++ fs/f2fs/super.c | 37 +++++++++++++++++++++++++++++++------ fs/f2fs/sysfs.c | 7 +++++++ 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index a6fa880a84cc..ed3e084c28e4 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -168,6 +168,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_SB_CHKSUM 0x0800 #define F2FS_FEATURE_CASEFOLD 0x1000 #define F2FS_FEATURE_COMPRESSION 0x2000 +#define F2FS_FEATURE_RO 0x4000 #define __F2FS_HAS_FEATURE(raw_super, mask) \ ((raw_super->feature & cpu_to_le32(mask)) != 0) @@ -991,6 +992,7 @@ static inline void set_new_dnode(struct dnode_of_data *dn, struct inode *inode, */ #define NR_CURSEG_DATA_TYPE (3) #define NR_CURSEG_NODE_TYPE (3) +#define NR_CURSEG_RO_TYPE (2) #define NR_CURSEG_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE) enum { @@ -3968,6 +3970,7 @@ F2FS_FEATURE_FUNCS(verity, VERITY); F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM); F2FS_FEATURE_FUNCS(casefold, CASEFOLD); F2FS_FEATURE_FUNCS(compression, COMPRESSION); +F2FS_FEATURE_FUNCS(readonly, RO); #ifdef CONFIG_BLK_DEV_ZONED static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f41b475aa4aa..ea92a85d179e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -4372,6 +4372,10 @@ static int sanity_check_curseg(struct f2fs_sb_info *sbi) struct seg_entry *se = get_seg_entry(sbi, curseg->segno); unsigned int blkofs = curseg->next_blkoff; + if (f2fs_sb_has_readonly(sbi) && + i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE) + continue; + if (f2fs_test_bit(blkofs, se->cur_valid_map)) goto out; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d4970371f376..c43b5eea0d40 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -469,7 +469,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) int ret; if (!options) - return 0; + goto default_check; while ((p = strsep(&options, ",")) != NULL) { int token; @@ -932,6 +932,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) return -EINVAL; } } +default_check: #ifdef CONFIG_QUOTA if (f2fs_check_quota_options(sbi)) return -EINVAL; @@ -993,6 +994,11 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) */ if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_TYPE) F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF; + + if (f2fs_sb_has_readonly(sbi) && !f2fs_readonly(sbi->sb)) { + f2fs_err(sbi, "Allow to mount readonly mode only"); + return -EROFS; + } return 0; } @@ -1626,7 +1632,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) static void default_options(struct f2fs_sb_info *sbi) { /* init some FS parameters */ - F2FS_OPTION(sbi).active_logs = NR_CURSEG_TYPE; + if (f2fs_sb_has_readonly(sbi)) + F2FS_OPTION(sbi).active_logs = NR_CURSEG_RO_TYPE; + else + F2FS_OPTION(sbi).active_logs = NR_CURSEG_TYPE; + F2FS_OPTION(sbi).inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF; F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT; @@ -1805,6 +1815,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) if (f2fs_readonly(sb) && (*flags & SB_RDONLY)) goto skip; + if (f2fs_sb_has_readonly(sbi) && !(*flags & SB_RDONLY)) { + err = -EROFS; + goto restore_opts; + } + #ifdef CONFIG_QUOTA if (!f2fs_readonly(sb) && (*flags & SB_RDONLY)) { err = dquot_suspend(sb, -1); @@ -2899,14 +2914,15 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi) ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); - if (unlikely(fsmeta < F2FS_MIN_META_SEGMENTS || + if (!f2fs_sb_has_readonly(sbi) && + unlikely(fsmeta < F2FS_MIN_META_SEGMENTS || ovp_segments == 0 || reserved_segments == 0)) { f2fs_err(sbi, "Wrong layout: check mkfs.f2fs version"); return 1; } - user_block_count = le64_to_cpu(ckpt->user_block_count); - segment_count_main = le32_to_cpu(raw_super->segment_count_main); + segment_count_main = le32_to_cpu(raw_super->segment_count_main) + + (f2fs_sb_has_readonly(sbi) ? 1 : 0); log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); if (!user_block_count || user_block_count >= segment_count_main << log_blocks_per_seg) { @@ -2937,6 +2953,10 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi) if (le32_to_cpu(ckpt->cur_node_segno[i]) >= main_segs || le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg) return 1; + + if (f2fs_sb_has_readonly(sbi)) + goto check_data; + for (j = i + 1; j < NR_CURSEG_NODE_TYPE; j++) { if (le32_to_cpu(ckpt->cur_node_segno[i]) == le32_to_cpu(ckpt->cur_node_segno[j])) { @@ -2947,10 +2967,15 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi) } } } +check_data: for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) { if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs || le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg) return 1; + + if (f2fs_sb_has_readonly(sbi)) + goto skip_cross; + for (j = i + 1; j < NR_CURSEG_DATA_TYPE; j++) { if (le32_to_cpu(ckpt->cur_data_segno[i]) == le32_to_cpu(ckpt->cur_data_segno[j])) { @@ -2972,7 +2997,7 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi) } } } - +skip_cross: sit_bitmap_size = le32_to_cpu(ckpt->sit_ver_bitmap_bytesize); nat_bitmap_size = le32_to_cpu(ckpt->nat_ver_bitmap_bytesize); diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 242ed0abfe33..c420cbbd7d6d 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -145,6 +145,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_casefold(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "casefold"); + if (f2fs_sb_has_readonly(sbi)) + len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "readonly"); if (f2fs_sb_has_compression(sbi)) len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "compression"); @@ -457,6 +460,7 @@ enum feat_id { FEAT_SB_CHECKSUM, FEAT_CASEFOLD, FEAT_COMPRESSION, + FEAT_RO, FEAT_TEST_DUMMY_ENCRYPTION_V2, FEAT_ENCRYPTED_CASEFOLD, }; @@ -479,6 +483,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_SB_CHECKSUM: case FEAT_CASEFOLD: case FEAT_COMPRESSION: + case FEAT_RO: case FEAT_TEST_DUMMY_ENCRYPTION_V2: case FEAT_ENCRYPTED_CASEFOLD: return sprintf(buf, "supported\n"); @@ -604,6 +609,7 @@ F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM); #ifdef CONFIG_UNICODE F2FS_FEATURE_RO_ATTR(casefold, FEAT_CASEFOLD); #endif +F2FS_FEATURE_RO_ATTR(readonly, FEAT_RO); #ifdef CONFIG_F2FS_FS_COMPRESSION F2FS_FEATURE_RO_ATTR(compression, FEAT_COMPRESSION); #endif @@ -697,6 +703,7 @@ static struct attribute *f2fs_feat_attrs[] = { #ifdef CONFIG_UNICODE ATTR_LIST(casefold), #endif + ATTR_LIST(readonly), #ifdef CONFIG_F2FS_FS_COMPRESSION ATTR_LIST(compression), #endif From b0f930a58edb90955ca1fdd9797f9067c7fe318f Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 2 Feb 2021 16:03:11 +0800 Subject: [PATCH 16/71] BACKPORT: f2fs: relocate inline conversion from mmap() to mkwrite() If there is page fault only for read case on inline inode, we don't need to convert inline inode, instead, let's do conversion for write case. Bug: 208371174 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim (cherry picked from commit 58c3b33eb9f1) Change-Id: I68168bc08a77d8d8f039189d44d23f83a7eda1f5 --- fs/f2fs/file.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d796992f40a5..57a9d894beb6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -68,6 +68,10 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf) goto err; } + err = f2fs_convert_inline_inode(inode); + if (err) + goto err; + #ifdef CONFIG_F2FS_FS_COMPRESSION if (f2fs_compressed_file(inode)) { int ret = f2fs_is_compressed_cluster(inode, page->index); @@ -515,7 +519,6 @@ static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence) static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file_inode(file); - int err; if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) return -EIO; @@ -523,11 +526,6 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) if (!f2fs_is_compress_backend_ready(inode)) return -EOPNOTSUPP; - /* we don't need to use inline_data strictly */ - err = f2fs_convert_inline_inode(inode); - if (err) - return err; - file_accessed(file); vma->vm_ops = &f2fs_file_vm_ops; set_inode_flag(inode, FI_MMAP_FILE); From 570829fbc17f57abd41571a7fb1ee3f1ad6218c3 Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Wed, 24 Nov 2021 14:19:07 +0530 Subject: [PATCH 17/71] msm: mhi_dev: Don't process any other interrupts during M0 processing M0 (mhi resume) processing and doorbell interrupt processing are done in different contexts. And there can be a chance that DB processing can proceed even before the resume is completely done. Say while processing M0, MHI has issued M0-ack but not updated channel state to running state for *all* channels yet. The host can start ringing DB the moment it sees M0-ack and it's the channel is in running state. To avoid this race condition, ensure MHI doesn't process any CHDB interrupt until it's completely done with the resume. Change-Id: Ie8f7fec034be6b17c71e3b7437ab8e6effb63365 Signed-off-by: Veerabhadrarao Badiganti --- drivers/platform/msm/mhi_dev/mhi_sm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/msm/mhi_dev/mhi_sm.c b/drivers/platform/msm/mhi_dev/mhi_sm.c index 6c826dbcea38..0d015e7a9127 100644 --- a/drivers/platform/msm/mhi_dev/mhi_sm.c +++ b/drivers/platform/msm/mhi_dev/mhi_sm.c @@ -1338,6 +1338,17 @@ int mhi_dev_notify_sm_event(enum mhi_dev_event event) INIT_WORK(&state_change_event->work, mhi_sm_dev_event_manager); atomic_inc(&mhi_sm_ctx->pending_device_events); queue_work(mhi_sm_ctx->mhi_sm_wq, &state_change_event->work); + + /* + * Wait until M0 processing is completely done. + * This ensures CHDB won't get processed while resume is in + * progress thus avoids race between M0 and CHDB processing. + */ + if (event == MHI_DEV_EVENT_M0_STATE) { + MHI_SM_DBG("Got M0, wait until resume is done\n"); + flush_workqueue(mhi_sm_ctx->mhi_sm_wq); + } + res = 0; exit: From 52f01ef02319336b43f59e99b2c0be2767932d10 Mon Sep 17 00:00:00 2001 From: Sai Chaitanya Kaveti Date: Tue, 9 Nov 2021 13:31:54 +0530 Subject: [PATCH 18/71] msm: mhi_dev: Checking the out channel status directly If the net dev init process is scheduled out in between registering a callback for in-channel and out-channel and start command for these channels gets processed at this time (after registering the callback for out channel but before registeringthe callback for in-channel) then with existing logic, we never register with the network interface. Checking the out channel status directly rather than using local bool variable out_channel_started before calling mhi_dev_net_open_chan_create_netif API. If out channel status is connected the mhi_dev_net_open_chan_create_netif API is called. Change-Id: I69af6c49ee565ffd04c4d0ac22b686ff986b7bc7 Signed-off-by: Sai Chaitanya Kaveti --- drivers/platform/msm/mhi_dev/mhi_dev_net.c | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi_dev_net.c b/drivers/platform/msm/mhi_dev/mhi_dev_net.c index fedda63c202c..08bf7f995f8f 100644 --- a/drivers/platform/msm/mhi_dev/mhi_dev_net.c +++ b/drivers/platform/msm/mhi_dev/mhi_dev_net.c @@ -695,7 +695,7 @@ static void mhi_dev_net_state_cb(struct mhi_dev_client_cb_data *cb_data) int mhi_dev_net_interface_init(void) { int ret_val = 0, index = 0; - bool out_channel_started = false; + uint32_t info_out_ch = 0; struct mhi_dev_net_client *mhi_net_client = NULL; if (mhi_net_ctxt.client_handle) { @@ -750,10 +750,7 @@ int mhi_dev_net_interface_init(void) } ret_val = mhi_register_state_cb(mhi_dev_net_state_cb, mhi_net_client, MHI_CLIENT_IP_SW_4_OUT); - /* -EEXIST indicates success and channel is already open */ - if (ret_val == -EEXIST) - out_channel_started = true; - else if (ret_val < 0) + if (ret_val < 0 && ret_val != -EEXIST) goto register_state_cb_fail; ret_val = mhi_register_state_cb(mhi_dev_net_state_cb, @@ -770,13 +767,16 @@ int mhi_dev_net_interface_init(void) * with mhi_dev_net_open_chan_create_netif(). */ ret_val = 0; - if (out_channel_started) { - ret_val = mhi_dev_net_open_chan_create_netif - (mhi_net_client); - if (ret_val < 0) { - mhi_dev_net_log(MHI_ERROR, - "Failed to open channels\n"); - goto channel_open_fail; + if (!mhi_ctrl_state_info(mhi_net_client->out_chan, + &info_out_ch)) { + if (info_out_ch == MHI_STATE_CONNECTED) { + ret_val = mhi_dev_net_open_chan_create_netif + (mhi_net_client); + if (ret_val < 0) { + mhi_dev_net_log(MHI_ERROR, + "Failed to open channels\n"); + goto channel_open_fail; + } } } } else if (ret_val < 0) { From 63b8a425d9cbd0ae21b3fbab647f2c28c7f42d75 Mon Sep 17 00:00:00 2001 From: Pratham Pratap Date: Fri, 3 Dec 2021 11:40:14 +0530 Subject: [PATCH 19/71] USB: dwc3: remove debugfs root dentry storage There is no need to keep around the debugfs "root" directory for the dwc3 device. Instead, look it up anytime we need to find it. This will help when callers get out-of-order and we had the potential to have a "stale" pointer around for the root dentry, as has happened in the past. Change-Id: I7fd1973704cd2a9e193fd77e01f7e09236f41117 Tested-by: Jack Pham Reviewed-by: Peter Chen Acked-by: Felipe Balbi Link: https://lore.kernel.org/r/20210609093924.3293230-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman Git-commit: 8562d5bfc0fcdfd3aef32991e17dca585ae5ae7d Git-Repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [quic_ppratap@quicinc.com: resolved trivial merge conflicts] Signed-off-by: Pratham Pratap --- drivers/usb/dwc3/core.h | 2 -- drivers/usb/dwc3/debugfs.c | 8 ++++---- drivers/usb/dwc3/gadget.c | 4 +++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a3b7ab11a1b8..3bd15568f1ab 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1081,7 +1081,6 @@ struct dwc3_scratchpad_array { * @link_state: link state * @speed: device speed (super, high, full, low) * @hwparams: copy of hwparams registers - * @root: debugfs root folder pointer * @regset: debugfs pointer to regdump file * @dbg_lsp_select: current debug lsp mux register selection * @test_mode: true when we're entering a USB test mode @@ -1307,7 +1306,6 @@ struct dwc3 { u8 num_eps; struct dwc3_hwparams hwparams; - struct dentry *root; struct debugfs_regset32 *regset; u32 dbg_lsp_select; diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 038eb17167ce..fe0d800227ac 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -1023,8 +1023,10 @@ static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep, void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep) { struct dentry *dir; + struct dentry *root; - dir = debugfs_create_dir(dep->name, dep->dwc->root); + root = debugfs_lookup(dev_name(dep->dwc->dev), usb_debug_root); + dir = debugfs_create_dir(dep->name, root); if (!dir) { pr_err("%s: failed to create dir %s\n", __func__, dep->name); return; @@ -1278,8 +1280,6 @@ void dwc3_debugfs_init(struct dwc3 *dwc) return; } - dwc->root = root; - debugfs_create_file("regdump", 0444, root, dwc, &dwc3_regdump_fops); debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc, @@ -1310,6 +1310,6 @@ void dwc3_debugfs_init(struct dwc3 *dwc) void dwc3_debugfs_exit(struct dwc3 *dwc) { - debugfs_remove_recursive(dwc->root); + debugfs_remove(debugfs_lookup(dev_name(dwc->dev), usb_debug_root)); kfree(dwc->regset); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a73035e7480f..bcded2a041c5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3144,7 +3144,9 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) list_del(&dep->endpoint.ep_list); } - debugfs_remove_recursive(debugfs_lookup(dep->name, dwc->root)); + debugfs_remove_recursive(debugfs_lookup(dep->name, + debugfs_lookup(dev_name(dep->dwc->dev), + usb_debug_root))); kfree(dep); } } From 1cdfc65d536a0903ff3d0004b22f4c4f9c396b92 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Fri, 3 Dec 2021 15:00:45 +0530 Subject: [PATCH 20/71] clk: qcom: gpucc-blair: Scale both cx and gx rails for gfx3d clock There is a requirement to scale both CX and GX rails during the gpu core clock scaling, thus add the corresponding vdd data. Change-Id: Ib27f4697a84ece1d0d1ce2b5297c03bf8ed955c6 Signed-off-by: Taniya Das --- drivers/clk/qcom/gpucc-blair.c | 20 +++++++++++++++++--- drivers/clk/qcom/vdd-level-holi.h | 13 +++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/clk/qcom/gpucc-blair.c b/drivers/clk/qcom/gpucc-blair.c index c92a8c7bc6f2..51ce96715ca7 100644 --- a/drivers/clk/qcom/gpucc-blair.c +++ b/drivers/clk/qcom/gpucc-blair.c @@ -23,12 +23,10 @@ static DEFINE_VDD_REGULATORS(vdd_cx, VDD_HIGH_L1 + 1, 1, vdd_corner); static DEFINE_VDD_REGULATORS(vdd_mx, VDD_HIGH_L1 + 1, 1, vdd_corner); -static DEFINE_VDD_REGULATORS(vdd_gx, VDD_HIGH_L1 + 1, 1, vdd_corner); +static DEFINE_VDD_REGULATORS(vdd_gx, VDD_NUM, 2, vdd_gx_corner); static struct clk_vdd_class *gpu_cc_blair_regulators[] = { - &vdd_cx, &vdd_mx, - &vdd_gx, }; enum { @@ -468,6 +466,22 @@ static int gpu_cc_blair_probe(struct platform_device *pdev) struct regmap *regmap; int ret; + vdd_gx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_gx"); + if (IS_ERR(vdd_gx.regulator[0])) { + if (!(PTR_ERR(vdd_gx.regulator[0]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, "Unable to get vdd_gx regulator\n"); + return PTR_ERR(vdd_gx.regulator[0]); + } + + vdd_gx.regulator[1] = devm_regulator_get(&pdev->dev, "vdd_cx"); + if (IS_ERR(vdd_gx.regulator[1])) { + if (!(PTR_ERR(vdd_gx.regulator[1]) == -EPROBE_DEFER)) + dev_err(&pdev->dev, "Unable to get vdd_cx regulator\n"); + return PTR_ERR(vdd_gx.regulator[1]); + } + + vdd_cx.regulator[0] = vdd_gx.regulator[1]; + regmap = qcom_cc_map(pdev, &gpu_cc_blair_desc); if (IS_ERR(regmap)) return PTR_ERR(regmap); diff --git a/drivers/clk/qcom/vdd-level-holi.h b/drivers/clk/qcom/vdd-level-holi.h index d37d307e45a1..356c6eaee427 100644 --- a/drivers/clk/qcom/vdd-level-holi.h +++ b/drivers/clk/qcom/vdd-level-holi.h @@ -32,4 +32,17 @@ static int vdd_corner[] = { [VDD_HIGH_L1] = RPM_SMD_REGULATOR_LEVEL_TURBO_NO_CPR, }; + +static int vdd_gx_corner[] = { + 0, 0, + RPM_SMD_REGULATOR_LEVEL_MIN_SVS, RPM_SMD_REGULATOR_LEVEL_MIN_SVS, + RPM_SMD_REGULATOR_LEVEL_LOW_SVS, RPM_SMD_REGULATOR_LEVEL_LOW_SVS, + RPM_SMD_REGULATOR_LEVEL_SVS, RPM_SMD_REGULATOR_LEVEL_SVS, + RPM_SMD_REGULATOR_LEVEL_SVS_PLUS, RPM_SMD_REGULATOR_LEVEL_SVS_PLUS, + RPM_SMD_REGULATOR_LEVEL_NOM, RPM_SMD_REGULATOR_LEVEL_NOM, + RPM_SMD_REGULATOR_LEVEL_NOM_PLUS, RPM_SMD_REGULATOR_LEVEL_NOM_PLUS, + RPM_SMD_REGULATOR_LEVEL_TURBO, RPM_SMD_REGULATOR_LEVEL_TURBO, + RPM_SMD_REGULATOR_LEVEL_TURBO_NO_CPR, RPM_SMD_REGULATOR_LEVEL_TURBO_NO_CPR, +}; + #endif From 3d236ade79621dd3ca08fa1f318fb60ac769356d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 1 Dec 2021 19:35:01 +0100 Subject: [PATCH 21/71] UPSTREAM: HID: add hid_is_usb() function to make it simpler for USB detection A number of HID drivers already call hid_is_using_ll_driver() but only for the detection of if this is a USB device or not. Make this more obvious by creating hid_is_usb() and calling the function that way. Also converts the existing hid_is_using_ll_driver() functions to use the new call. Cc: Jiri Kosina Cc: Benjamin Tissoires Cc: linux-input@vger.kernel.org Cc: stable@vger.kernel.org Tested-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20211201183503.2373082-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f83baa0cb6cfc92ebaf7f9d3a99d7e34f2e77a8a) Bug: 188677105 Signed-off-by: Greg Kroah-Hartman Change-Id: Ibfa4252d4b6501a09a997bb9efbab46fae3fd1dc --- drivers/hid/hid-asus.c | 2 +- drivers/hid/hid-logitech-dj.c | 2 +- drivers/hid/hid-u2fzero.c | 2 +- drivers/hid/hid-uclogic-params.c | 3 +-- drivers/hid/wacom_sys.c | 2 +- include/linux/hid.h | 5 +++++ 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index e6e4c841fb06..7f84ed0afdfe 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -849,7 +849,7 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) if (drvdata->quirks & QUIRK_IS_MULTITOUCH) drvdata->tp = &asus_i2c_tp; - if (drvdata->quirks & QUIRK_T100_KEYBOARD) { + if ((drvdata->quirks & QUIRK_T100_KEYBOARD) && hid_is_usb(hdev)) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); if (intf->altsetting->desc.bInterfaceNumber == T100_TPAD_INTF) { diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index b499ac37dc7b..4267e2f2e70f 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1686,7 +1686,7 @@ static int logi_dj_probe(struct hid_device *hdev, case recvr_type_27mhz: no_dj_interfaces = 2; break; case recvr_type_bluetooth: no_dj_interfaces = 2; break; } - if (hid_is_using_ll_driver(hdev, &usb_hid_driver)) { + if (hid_is_usb(hdev)) { intf = to_usb_interface(hdev->dev.parent); if (intf && intf->altsetting->desc.bInterfaceNumber >= no_dj_interfaces) { diff --git a/drivers/hid/hid-u2fzero.c b/drivers/hid/hid-u2fzero.c index 95e0807878c7..6f107e35d8a4 100644 --- a/drivers/hid/hid-u2fzero.c +++ b/drivers/hid/hid-u2fzero.c @@ -286,7 +286,7 @@ static int u2fzero_probe(struct hid_device *hdev, unsigned int minor; int ret; - if (!hid_is_using_ll_driver(hdev, &usb_hid_driver)) + if (!hid_is_usb(hdev)) return -EINVAL; dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL); diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index e80c812f44a7..ed4ede52b017 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -841,8 +841,7 @@ int uclogic_params_init(struct uclogic_params *params, struct uclogic_params p = {0, }; /* Check arguments */ - if (params == NULL || hdev == NULL || - !hid_is_using_ll_driver(hdev, &usb_hid_driver)) { + if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { rc = -EINVAL; goto cleanup; } diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 73dafa60080f..e94ec84aec6a 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -2217,7 +2217,7 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix) if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) { char *product_name = wacom->hdev->name; - if (hid_is_using_ll_driver(wacom->hdev, &usb_hid_driver)) { + if (hid_is_usb(wacom->hdev)) { struct usb_interface *intf = to_usb_interface(wacom->hdev->dev.parent); struct usb_device *dev = interface_to_usbdev(intf); product_name = dev->product; diff --git a/include/linux/hid.h b/include/linux/hid.h index 2c2b26ac11f6..bf8ec809b11d 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -837,6 +837,11 @@ static inline bool hid_is_using_ll_driver(struct hid_device *hdev, return hdev->ll_driver == driver; } +static inline bool hid_is_usb(struct hid_device *hdev) +{ + return hid_is_using_ll_driver(hdev, &usb_hid_driver); +} + #define PM_HINT_FULLON 1<<5 #define PM_HINT_NORMAL 1<<1 From edf570221639918cf5e9453fbac508935e82f0c7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 3 Dec 2021 09:12:31 +0100 Subject: [PATCH 22/71] UPSTREAM: HID: add USB_HID dependancy to hid-prodikeys The prodikeys HID driver only controls USB devices, yet did not have a dependancy on USB_HID. This causes build errors on some configurations like nios2 when building due to new changes to the prodikeys driver. Reported-by: kernel test robot Cc: stable@vger.kernel.org Cc: Jiri Kosina Cc: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20211203081231.2856936-1-gregkh@linuxfoundation.org (cherry picked from commit 30cb3c2ad24b66fb7639a6d1f4390c74d6e68f94) Bug: 188677105 Signed-off-by: Greg Kroah-Hartman Change-Id: Ifbff6c7522a684fab6974565570a92e61651ae0a --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 542158d9feb8..208128e2d4a3 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -244,7 +244,7 @@ config HID_MACALLY config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" - depends on HID && SND + depends on USB_HID && SND select SND_RAWMIDI ---help--- Support for Prodikeys PC-MIDI Keyboard device support. From 058d09a5c5f11339601d295cbcff9f83514b78db Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 3 Dec 2021 08:59:27 +0100 Subject: [PATCH 23/71] UPSTREAM: HID: add USB_HID dependancy to hid-chicony The chicony HID driver only controls USB devices, yet did not have a dependancy on USB_HID. This causes build errors on some configurations like sparc when building due to new changes to the chicony driver. Reported-by: Stephen Rothwell Cc: stable@vger.kernel.org Cc: Jiri Kosina Cc: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20211203075927.2829218-1-gregkh@linuxfoundation.org (cherry picked from commit d080811f27936f712f619f847389f403ac873b8f) Bug: 188677105 Signed-off-by: Greg Kroah-Hartman Change-Id: Ic65d7879192eb876683d13eb18628dd0f4594e98 --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 208128e2d4a3..3868347006e5 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -206,7 +206,7 @@ config HID_CHERRY config HID_CHICONY tristate "Chicony devices" - depends on HID + depends on USB_HID default !EXPERT ---help--- Support for Chicony Tactical pad and special keys on Chicony keyboards. From 56eb2fdbc1a5cba7f8a2dad8f4fe1dfd41b17f45 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Dec 2021 12:48:19 +0100 Subject: [PATCH 24/71] UPSTREAM: HID: add USB_HID dependancy on some USB HID drivers commit f237d9028f844a86955fc9da59d7ac4a5c55d7d5 upstream. Some HID drivers are only for USB drivers, yet did not depend on CONFIG_USB_HID. This was hidden by the fact that the USB functions were stubbed out in the past, but now that drivers are checking for USB devices properly, build errors can occur with some random configurations. Reported-by: kernel test robot Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20211202114819.2511954-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f237d9028f844a86955fc9da59d7ac4a5c55d7d5) Bug: 188677105 Signed-off-by: Greg Kroah-Hartman Change-Id: Ia755dc2803f1111c33d1c4b06b02913eebdf34c0 --- drivers/hid/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 3868347006e5..45f6b84f53ef 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -213,7 +213,7 @@ config HID_CHICONY config HID_CORSAIR tristate "Corsair devices" - depends on HID && USB && LEDS_CLASS + depends on USB_HID && LEDS_CLASS ---help--- Support for Corsair devices that are not fully compliant with the HID standard. @@ -524,7 +524,7 @@ config HID_LENOVO config HID_LOGITECH tristate "Logitech devices" - depends on HID + depends on USB_HID default !EXPERT ---help--- Support for Logitech devices that are not fully compliant with HID standard. @@ -900,7 +900,7 @@ config HID_SAITEK config HID_SAMSUNG tristate "Samsung InfraRed remote control or keyboards" - depends on HID + depends on USB_HID ---help--- Support for Samsung InfraRed remote control or keyboards. From 3b49ff9fcb2eec145ba099595e90b753634a52cd Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 2 Dec 2021 10:53:32 +0100 Subject: [PATCH 25/71] UPSTREAM: HID: bigbenff: prevent null pointer dereference When emulating the device through uhid, there is a chance we don't have output reports and so report_field is null. Cc: stable@vger.kernel.org Signed-off-by: Benjamin Tissoires Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20211202095334.14399-3-benjamin.tissoires@redhat.com (cherry picked from commit 918aa1ef104d286d16b9e7ef139a463ac7a296f0) Bug: 188677105 Signed-off-by: Greg Kroah-Hartman Change-Id: Ia6fb77a7bd9426ce055e090fb2c1f3a21a2011cc --- drivers/hid/hid-bigbenff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c index db6da21ade06..74ad8bf98bfd 100644 --- a/drivers/hid/hid-bigbenff.c +++ b/drivers/hid/hid-bigbenff.c @@ -191,7 +191,7 @@ static void bigben_worker(struct work_struct *work) struct bigben_device, worker); struct hid_field *report_field = bigben->report->field[0]; - if (bigben->removed) + if (bigben->removed || !report_field) return; if (bigben->work_led) { From 89c9193d7332d6a18364cee23600c81a61b0ea1c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 1 Dec 2021 19:35:02 +0100 Subject: [PATCH 26/71] UPSTREAM: HID: wacom: fix problems when device is not a valid USB device The wacom driver accepts devices of more than just USB types, but some code paths can cause problems if the device being controlled is not a USB device due to a lack of checking. Add the needed checks to ensure that the USB device accesses are only happening on a "real" USB device, and not one on some other bus. Cc: Jiri Kosina Cc: Benjamin Tissoires Cc: linux-input@vger.kernel.org Cc: stable@vger.kernel.org Tested-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20211201183503.2373082-2-gregkh@linuxfoundation.org (cherry picked from commit 720ac467204a70308bd687927ed475afb904e11b) Bug: 188677105 Signed-off-by: Greg Kroah-Hartman Change-Id: I605a7a3598b54693ce2104d4afdbdf879bb7fb2e --- drivers/hid/wacom_sys.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index e94ec84aec6a..329bb1a46f90 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -726,7 +726,7 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev, * Skip the query for this type and modify defaults based on * interface number. */ - if (features->type == WIRELESS) { + if (features->type == WIRELESS && intf) { if (intf->cur_altsetting->desc.bInterfaceNumber == 0) features->device_type = WACOM_DEVICETYPE_WL_MONITOR; else @@ -2448,6 +2448,9 @@ static void wacom_wireless_work(struct work_struct *work) wacom_destroy_battery(wacom); + if (!usbdev) + return; + /* Stylus interface */ hdev1 = usb_get_intfdata(usbdev->config->interface[1]); wacom1 = hid_get_drvdata(hdev1); @@ -2727,8 +2730,6 @@ static void wacom_mode_change_work(struct work_struct *work) static int wacom_probe(struct hid_device *hdev, const struct hid_device_id *id) { - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct usb_device *dev = interface_to_usbdev(intf); struct wacom *wacom; struct wacom_wac *wacom_wac; struct wacom_features *features; @@ -2763,8 +2764,14 @@ static int wacom_probe(struct hid_device *hdev, wacom_wac->hid_data.inputmode = -1; wacom_wac->mode_report = -1; - wacom->usbdev = dev; - wacom->intf = intf; + if (hid_is_usb(hdev)) { + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *dev = interface_to_usbdev(intf); + + wacom->usbdev = dev; + wacom->intf = intf; + } + mutex_init(&wacom->lock); INIT_DELAYED_WORK(&wacom->init_work, wacom_init_work); INIT_WORK(&wacom->wireless_work, wacom_wireless_work); From 26ddf0ab2d3a4aa598d616bee56fb43b534e73eb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 1 Dec 2021 19:35:03 +0100 Subject: [PATCH 27/71] UPSTREAM: HID: check for valid USB device for many HID drivers Many HID drivers assume that the HID device assigned to them is a USB device as that was the only way HID devices used to be able to be created in Linux. However, with the additional ways that HID devices can be created for many different bus types, that is no longer true, so properly check that we have a USB device associated with the HID device before allowing a driver that makes this assumption to claim it. Cc: Jiri Kosina Cc: Benjamin Tissoires Cc: Michael Zaidman Cc: Stefan Achatz Cc: Maxime Coquelin Cc: Alexandre Torgue Cc: linux-input@vger.kernel.org Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Tested-by: Benjamin Tissoires [bentiss: amended for thrustmater.c hunk to apply] Signed-off-by: Benjamin Tissoires Link: https://lore.kernel.org/r/20211201183503.2373082-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 93020953d0fa7035fd036ad87a47ae2b7aa4ae33) Bug: 188677105 Signed-off-by: Greg Kroah-Hartman Change-Id: I7908d6af9e70865a6db17fac75624064165449ad --- drivers/hid/hid-chicony.c | 8 ++++++-- drivers/hid/hid-corsair.c | 7 ++++++- drivers/hid/hid-elan.c | 2 +- drivers/hid/hid-elo.c | 3 +++ drivers/hid/hid-holtek-kbd.c | 9 +++++++-- drivers/hid/hid-holtek-mouse.c | 9 +++++++++ drivers/hid/hid-lg.c | 10 ++++++++-- drivers/hid/hid-prodikeys.c | 10 ++++++++-- drivers/hid/hid-roccat-arvo.c | 3 +++ drivers/hid/hid-roccat-isku.c | 3 +++ drivers/hid/hid-roccat-kone.c | 3 +++ drivers/hid/hid-roccat-koneplus.c | 3 +++ drivers/hid/hid-roccat-konepure.c | 3 +++ drivers/hid/hid-roccat-kovaplus.c | 3 +++ drivers/hid/hid-roccat-lua.c | 3 +++ drivers/hid/hid-roccat-pyra.c | 3 +++ drivers/hid/hid-roccat-ryos.c | 3 +++ drivers/hid/hid-roccat-savu.c | 3 +++ drivers/hid/hid-samsung.c | 3 +++ drivers/hid/hid-uclogic-core.c | 3 +++ 20 files changed, 84 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c index 3f0ed6a95223..e19e2b597339 100644 --- a/drivers/hid/hid-chicony.c +++ b/drivers/hid/hid-chicony.c @@ -58,8 +58,12 @@ static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi, static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - + struct usb_interface *intf; + + if (!hid_is_usb(hdev)) + return rdesc; + + intf = to_usb_interface(hdev->dev.parent); if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { /* Change usage maximum and logical maximum from 0x7fff to * 0x2fff, so they don't exceed HID_MAX_USAGES */ diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c index 902a60e249ed..8c895c820b67 100644 --- a/drivers/hid/hid-corsair.c +++ b/drivers/hid/hid-corsair.c @@ -553,7 +553,12 @@ static int corsair_probe(struct hid_device *dev, const struct hid_device_id *id) int ret; unsigned long quirks = id->driver_data; struct corsair_drvdata *drvdata; - struct usb_interface *usbif = to_usb_interface(dev->dev.parent); + struct usb_interface *usbif; + + if (!hid_is_usb(dev)) + return -EINVAL; + + usbif = to_usb_interface(dev->dev.parent); drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata), GFP_KERNEL); diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index dae193749d44..0e8f424025fe 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -50,7 +50,7 @@ struct elan_drvdata { static int is_not_elan_touchpad(struct hid_device *hdev) { - if (hdev->bus == BUS_USB) { + if (hid_is_usb(hdev)) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); return (intf->altsetting->desc.bInterfaceNumber != diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c index 0d22713a3874..2876cb6a7dca 100644 --- a/drivers/hid/hid-elo.c +++ b/drivers/hid/hid-elo.c @@ -229,6 +229,9 @@ static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) struct elo_priv *priv; int ret; + if (!hid_is_usb(hdev)) + return -EINVAL; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/drivers/hid/hid-holtek-kbd.c b/drivers/hid/hid-holtek-kbd.c index 0a38e8e9bc78..403506b9697e 100644 --- a/drivers/hid/hid-holtek-kbd.c +++ b/drivers/hid/hid-holtek-kbd.c @@ -140,12 +140,17 @@ static int holtek_kbd_input_event(struct input_dev *dev, unsigned int type, static int holtek_kbd_probe(struct hid_device *hdev, const struct hid_device_id *id) { - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - int ret = hid_parse(hdev); + struct usb_interface *intf; + int ret; + if (!hid_is_usb(hdev)) + return -EINVAL; + + ret = hid_parse(hdev); if (!ret) ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + intf = to_usb_interface(hdev->dev.parent); if (!ret && intf->cur_altsetting->desc.bInterfaceNumber == 1) { struct hid_input *hidinput; list_for_each_entry(hidinput, &hdev->inputs, list) { diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c index 195b735b001d..b7172c48ef9f 100644 --- a/drivers/hid/hid-holtek-mouse.c +++ b/drivers/hid/hid-holtek-mouse.c @@ -62,6 +62,14 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } +static int holtek_mouse_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + if (!hid_is_usb(hdev)) + return -EINVAL; + return 0; +} + static const struct hid_device_id holtek_mouse_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, @@ -83,6 +91,7 @@ static struct hid_driver holtek_mouse_driver = { .name = "holtek_mouse", .id_table = holtek_mouse_devices, .report_fixup = holtek_mouse_report_fixup, + .probe = holtek_mouse_probe, }; module_hid_driver(holtek_mouse_driver); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 0dc7cdfc56f7..2c7e7c089bf9 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -769,12 +769,18 @@ static int lg_raw_event(struct hid_device *hdev, struct hid_report *report, static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) { - struct usb_interface *iface = to_usb_interface(hdev->dev.parent); - __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; + struct usb_interface *iface; + __u8 iface_num; unsigned int connect_mask = HID_CONNECT_DEFAULT; struct lg_drv_data *drv_data; int ret; + if (!hid_is_usb(hdev)) + return -EINVAL; + + iface = to_usb_interface(hdev->dev.parent); + iface_num = iface->cur_altsetting->desc.bInterfaceNumber; + /* G29 only work with the 1st interface */ if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) && (iface_num != 0)) { diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index 2666af02d5c1..e4e9471d0f1e 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -798,12 +798,18 @@ static int pk_raw_event(struct hid_device *hdev, struct hid_report *report, static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + struct usb_interface *intf; + unsigned short ifnum; unsigned long quirks = id->driver_data; struct pk_device *pk; struct pcmidi_snd *pm = NULL; + if (!hid_is_usb(hdev)) + return -EINVAL; + + intf = to_usb_interface(hdev->dev.parent); + ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + pk = kzalloc(sizeof(*pk), GFP_KERNEL); if (pk == NULL) { hid_err(hdev, "can't alloc descriptor\n"); diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index ffcd444ae2ba..4b18e1a4fc7a 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -344,6 +344,9 @@ static int arvo_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index ce5f22519956..e95d59cd8d07 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -324,6 +324,9 @@ static int isku_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 509b9bb1362c..6cf59b5e5484 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -749,6 +749,9 @@ static int kone_probe(struct hid_device *hdev, const struct hid_device_id *id) { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 0316edf8c5bb..1896c69ea512 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -431,6 +431,9 @@ static int koneplus_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c index 5248b3c7cf78..cf8eeb33a125 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -133,6 +133,9 @@ static int konepure_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 960012881570..6fb9b9563769 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -501,6 +501,9 @@ static int kovaplus_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index 4a88a76d5c62..d5ddf0d68346 100644 --- a/drivers/hid/hid-roccat-lua.c +++ b/drivers/hid/hid-roccat-lua.c @@ -160,6 +160,9 @@ static int lua_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 989927defe8d..4fcc8e7d276f 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -449,6 +449,9 @@ static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c index 3956a6c9c521..5bf1971a2b14 100644 --- a/drivers/hid/hid-roccat-ryos.c +++ b/drivers/hid/hid-roccat-ryos.c @@ -141,6 +141,9 @@ static int ryos_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 818701f7a028..a784bb4ee651 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -113,6 +113,9 @@ static int savu_probe(struct hid_device *hdev, { int retval; + if (!hid_is_usb(hdev)) + return -EINVAL; + retval = hid_parse(hdev); if (retval) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index 2e1c31156eca..cf5992e97094 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -152,6 +152,9 @@ static int samsung_probe(struct hid_device *hdev, int ret; unsigned int cmask = HID_CONNECT_DEFAULT; + if (!hid_is_usb(hdev)) + return -EINVAL; + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 8e9c9e646cb7..4edb24195704 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -164,6 +164,9 @@ static int uclogic_probe(struct hid_device *hdev, struct uclogic_drvdata *drvdata = NULL; bool params_initialized = false; + if (!hid_is_usb(hdev)) + return -EINVAL; + /* * libinput requires the pad interface to be on a different node * than the pen, so use QUIRK_MULTI_INPUT for all tablets. From 4b95e275cf25c4b5b8f14540c338a8d3c0c3f9c5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Dec 2021 18:59:27 +0100 Subject: [PATCH 28/71] UPSTREAM: USB: gadget: detect too-big endpoint 0 requests Sometimes USB hosts can ask for buffers that are too large from endpoint 0, which should not be allowed. If this happens for OUT requests, stall the endpoint, but for IN requests, trim the request size to the endpoint buffer size. Co-developed-by: Szymon Heidrich Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 153a2d7e3350cc89d406ba2d35be8793a64c2038) Bug: 210292367 Signed-off-by: Greg Kroah-Hartman Change-Id: I9bbd6154177d7a1fb6c2e3a3dffa96634d85bb7f Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 12 ++++++++++++ drivers/usb/gadget/legacy/dbgp.c | 13 +++++++++++++ drivers/usb/gadget/legacy/inode.c | 16 +++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 24dad1d78d1e..60201b6cdd9e 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1644,6 +1644,18 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) struct usb_function *f = NULL; u8 endp; + if (w_length > USB_COMP_EP0_BUFSIZ) { + if (ctrl->bRequestType == USB_DIR_OUT) { + goto done; + } else { + /* Cast away the const, we are going to overwrite on purpose. */ + __le16 *temp = (__le16 *)&ctrl->wLength; + + *temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ); + w_length = USB_COMP_EP0_BUFSIZ; + } + } + /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion * when we delegate to it. diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index e1d566c9918a..e567afcb2794 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -345,6 +345,19 @@ static int dbgp_setup(struct usb_gadget *gadget, void *data = NULL; u16 len = 0; + if (length > DBGP_REQ_LEN) { + if (ctrl->bRequestType == USB_DIR_OUT) { + return err; + } else { + /* Cast away the const, we are going to overwrite on purpose. */ + __le16 *temp = (__le16 *)&ctrl->wLength; + + *temp = cpu_to_le16(DBGP_REQ_LEN); + length = DBGP_REQ_LEN; + } + } + + if (request == USB_REQ_GET_DESCRIPTOR) { switch (value>>8) { case USB_DT_DEVICE: diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index cabcbb47f0ac..f0aff79f544c 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -110,6 +110,8 @@ enum ep0_state { /* enough for the whole queue: most events invalidate others */ #define N_EVENT 5 +#define RBUF_SIZE 256 + struct dev_data { spinlock_t lock; refcount_t count; @@ -144,7 +146,7 @@ struct dev_data { struct dentry *dentry; /* except this scratch i/o buffer for ep0 */ - u8 rbuf [256]; + u8 rbuf[RBUF_SIZE]; }; static inline void get_dev (struct dev_data *data) @@ -1333,6 +1335,18 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + if (w_length > RBUF_SIZE) { + if (ctrl->bRequestType == USB_DIR_OUT) { + return value; + } else { + /* Cast away the const, we are going to overwrite on purpose. */ + __le16 *temp = (__le16 *)&ctrl->wLength; + + *temp = cpu_to_le16(RBUF_SIZE); + w_length = RBUF_SIZE; + } + } + spin_lock (&dev->lock); dev->setup_abort = 0; if (dev->state == STATE_DEV_UNCONNECTED) { From 425aade96067169a2565bcb52fd8620467dd485f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Dec 2021 19:02:15 +0100 Subject: [PATCH 29/71] UPSTREAM: USB: gadget: zero allocate endpoint 0 buffers Under some conditions, USB gadget devices can show allocated buffer contents to a host. Fix this up by zero-allocating them so that any extra data will all just be zeros. Reported-by: Szymon Heidrich Tested-by: Szymon Heidrich Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 86ebbc11bb3f60908a51f3e41a17e3f477c2eaa3) Bug: 210292367 Signed-off-by: Greg Kroah-Hartman Change-Id: I72b4376cd4296a8b8af0ade2d702cd420146f3aa Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 2 +- drivers/usb/gadget/legacy/dbgp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 60201b6cdd9e..cc31a47949cd 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2169,7 +2169,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite, if (!cdev->req) return -ENOMEM; - cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL); + cdev->req->buf = kzalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL); if (!cdev->req->buf) goto fail; diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index e567afcb2794..355bc7dab9d5 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -137,7 +137,7 @@ static int dbgp_enable_ep_req(struct usb_ep *ep) goto fail_1; } - req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL); + req->buf = kzalloc(DBGP_REQ_LEN, GFP_KERNEL); if (!req->buf) { err = -ENOMEM; stp = 2; From a20ea4a1950e488d8c5580c672b8c6e2f2814dbf Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 10 Dec 2021 15:50:50 -0800 Subject: [PATCH 30/71] UPSTREAM: wait: add wake_up_pollfree() commit 42288cb44c4b5fff7653bc392b583a2b8bd6a8c0 upstream. Several ->poll() implementations are special in that they use a waitqueue whose lifetime is the current task, rather than the struct file as is normally the case. This is okay for blocking polls, since a blocking poll occurs within one task; however, non-blocking polls require another solution. This solution is for the queue to be cleared before it is freed, using 'wake_up_poll(wq, EPOLLHUP | POLLFREE);'. However, that has a bug: wake_up_poll() calls __wake_up() with nr_exclusive=1. Therefore, if there are multiple "exclusive" waiters, and the wakeup function for the first one returns a positive value, only that one will be called. That's *not* what's needed for POLLFREE; POLLFREE is special in that it really needs to wake up everyone. Considering the three non-blocking poll systems: - io_uring poll doesn't handle POLLFREE at all, so it is broken anyway. - aio poll is unaffected, since it doesn't support exclusive waits. However, that's fragile, as someone could add this feature later. - epoll doesn't appear to be broken by this, since its wakeup function returns 0 when it sees POLLFREE. But this is fragile. Although there is a workaround (see epoll), it's better to define a function which always sends POLLFREE to all waiters. Add such a function. Also make it verify that the queue really becomes empty after all waiters have been woken up. Reported-by: Linus Torvalds Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20211209010455.42744-2-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman Bug: 185125206 Signed-off-by: Greg Kroah-Hartman Change-Id: I1ec70169a39be42640172af512f4b47827ef5569 --- include/linux/wait.h | 26 ++++++++++++++++++++++++++ kernel/sched/wait.c | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/include/linux/wait.h b/include/linux/wait.h index 032ae61c22a2..5903b1d17c92 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -204,6 +204,7 @@ void __wake_up_locked_key_bookmark(struct wait_queue_head *wq_head, void __wake_up_sync_key(struct wait_queue_head *wq_head, unsigned int mode, int nr, void *key); void __wake_up_locked(struct wait_queue_head *wq_head, unsigned int mode, int nr); void __wake_up_sync(struct wait_queue_head *wq_head, unsigned int mode, int nr); +void __wake_up_pollfree(struct wait_queue_head *wq_head); #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL) #define wake_up_nr(x, nr) __wake_up(x, TASK_NORMAL, nr, NULL) @@ -230,6 +231,31 @@ void __wake_up_sync(struct wait_queue_head *wq_head, unsigned int mode, int nr); #define wake_up_interruptible_sync_poll(x, m) \ __wake_up_sync_key((x), TASK_INTERRUPTIBLE, 1, poll_to_key(m)) +/** + * wake_up_pollfree - signal that a polled waitqueue is going away + * @wq_head: the wait queue head + * + * In the very rare cases where a ->poll() implementation uses a waitqueue whose + * lifetime is tied to a task rather than to the 'struct file' being polled, + * this function must be called before the waitqueue is freed so that + * non-blocking polls (e.g. epoll) are notified that the queue is going away. + * + * The caller must also RCU-delay the freeing of the wait_queue_head, e.g. via + * an explicit synchronize_rcu() or call_rcu(), or via SLAB_TYPESAFE_BY_RCU. + */ +static inline void wake_up_pollfree(struct wait_queue_head *wq_head) +{ + /* + * For performance reasons, we don't always take the queue lock here. + * Therefore, we might race with someone removing the last entry from + * the queue, and proceed while they still hold the queue lock. + * However, rcu_read_lock() is required to be held in such cases, so we + * can safely proceed with an RCU-delayed free. + */ + if (waitqueue_active(wq_head)) + __wake_up_pollfree(wq_head); +} + #define ___wait_cond_timeout(condition) \ ({ \ bool __cond = (condition); \ diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index 84bd05117dc2..7d668b31dbc6 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -206,6 +206,13 @@ void __wake_up_sync(struct wait_queue_head *wq_head, unsigned int mode, int nr_e } EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */ +void __wake_up_pollfree(struct wait_queue_head *wq_head) +{ + __wake_up(wq_head, TASK_NORMAL, 0, poll_to_key(EPOLLHUP | POLLFREE)); + /* POLLFREE must have cleared the queue. */ + WARN_ON_ONCE(waitqueue_active(wq_head)); +} + /* * Note: we use "set_current_state()" _after_ the wait-queue add, * because we need a memory barrier there on SMP, so that any From da5ecad7100b90a54129404ae90b35bc205907f8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 10 Dec 2021 15:50:51 -0800 Subject: [PATCH 31/71] UPSTREAM: binder: use wake_up_pollfree() commit a880b28a71e39013e357fd3adccd1d8a31bc69a8 upstream. wake_up_poll() uses nr_exclusive=1, so it's not guaranteed to wake up all exclusive waiters. Yet, POLLFREE *must* wake up all waiters. epoll and aio poll are fortunately not affected by this, but it's very fragile. Thus, the new function wake_up_pollfree() has been introduced. Convert binder to use wake_up_pollfree(). Reported-by: Linus Torvalds Fixes: f5cb779ba163 ("ANDROID: binder: remove waitqueue when thread exits.") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20211209010455.42744-3-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman Bug: 185125206 Signed-off-by: Greg Kroah-Hartman Change-Id: I0f08d9f9a40348d7d2381607fabe3648416ab2a2 --- drivers/android/binder.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 7d86c149e4e4..e2c61938fb18 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -4544,23 +4544,20 @@ static int binder_thread_release(struct binder_proc *proc, __release(&t->lock); /* - * If this thread used poll, make sure we remove the waitqueue - * from any epoll data structures holding it with POLLFREE. - * waitqueue_active() is safe to use here because we're holding - * the inner lock. + * If this thread used poll, make sure we remove the waitqueue from any + * poll data structures holding it. */ - if ((thread->looper & BINDER_LOOPER_STATE_POLL) && - waitqueue_active(&thread->wait)) { - wake_up_poll(&thread->wait, EPOLLHUP | POLLFREE); - } + if (thread->looper & BINDER_LOOPER_STATE_POLL) + wake_up_pollfree(&thread->wait); binder_inner_proc_unlock(thread->proc); /* - * This is needed to avoid races between wake_up_poll() above and - * and ep_remove_waitqueue() called for other reasons (eg the epoll file - * descriptor being closed); ep_remove_waitqueue() holds an RCU read - * lock, so we can be sure it's done after calling synchronize_rcu(). + * This is needed to avoid races between wake_up_pollfree() above and + * someone else removing the last entry from the queue for other reasons + * (e.g. ep_remove_wait_queue() being called due to an epoll file + * descriptor being closed). Such other users hold an RCU read lock, so + * we can be sure they're done after we call synchronize_rcu(). */ if (thread->looper & BINDER_LOOPER_STATE_POLL) synchronize_rcu(); From 480f70a4e18ea25ad766043be05cc496ee7245c9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 10 Dec 2021 15:50:52 -0800 Subject: [PATCH 32/71] UPSTREAM: signalfd: use wake_up_pollfree() commit 9537bae0da1f8d1e2361ab6d0479e8af7824e160 upstream. wake_up_poll() uses nr_exclusive=1, so it's not guaranteed to wake up all exclusive waiters. Yet, POLLFREE *must* wake up all waiters. epoll and aio poll are fortunately not affected by this, but it's very fragile. Thus, the new function wake_up_pollfree() has been introduced. Convert signalfd to use wake_up_pollfree(). Reported-by: Linus Torvalds Fixes: d80e731ecab4 ("epoll: introduce POLLFREE to flush ->signalfd_wqh before kfree()") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20211209010455.42744-4-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman Bug: 185125206 Signed-off-by: Greg Kroah-Hartman Change-Id: I2534cce4e3d5be1b439b01eb8717903e08bb8e65 --- fs/signalfd.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/fs/signalfd.c b/fs/signalfd.c index 5b78719be445..3e94d181930f 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -35,17 +35,7 @@ void signalfd_cleanup(struct sighand_struct *sighand) { - wait_queue_head_t *wqh = &sighand->signalfd_wqh; - /* - * The lockless check can race with remove_wait_queue() in progress, - * but in this case its caller should run under rcu_read_lock() and - * sighand_cachep is SLAB_TYPESAFE_BY_RCU, we can safely return. - */ - if (likely(!waitqueue_active(wqh))) - return; - - /* wait_queue_entry_t->func(POLLFREE) should do remove_wait_queue() */ - wake_up_poll(wqh, EPOLLHUP | POLLFREE); + wake_up_pollfree(&sighand->signalfd_wqh); } struct signalfd_ctx { From 71cd014915eb3611e3897103fbcdfb58b0d7d552 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 10 Dec 2021 15:50:53 -0800 Subject: [PATCH 33/71] UPSTREAM: aio: keep poll requests on waitqueue until completed commit 363bee27e25804d8981dd1c025b4ad49dc39c530 upstream. Currently, aio_poll_wake() will always remove the poll request from the waitqueue. Then, if aio_poll_complete_work() sees that none of the polled events are ready and the request isn't cancelled, it re-adds the request to the waitqueue. (This can easily happen when polling a file that doesn't pass an event mask when waking up its waitqueue.) This is fundamentally broken for two reasons: 1. If a wakeup occurs between vfs_poll() and the request being re-added to the waitqueue, it will be missed because the request wasn't on the waitqueue at the time. Therefore, IOCB_CMD_POLL might never complete even if the polled file is ready. 2. When the request isn't on the waitqueue, there is no way to be notified that the waitqueue is being freed (which happens when its lifetime is shorter than the struct file's). This is supposed to happen via the waitqueue entries being woken up with POLLFREE. Therefore, leave the requests on the waitqueue until they are actually completed (or cancelled). To keep track of when aio_poll_complete_work needs to be scheduled, use new fields in struct poll_iocb. Remove the 'done' field which is now redundant. Note that this is consistent with how sys_poll() and eventpoll work; their wakeup functions do *not* remove the waitqueue entries. Fixes: 2c14fa838cbe ("aio: implement IOCB_CMD_POLL") Cc: # v4.18+ Link: https://lore.kernel.org/r/20211209010455.42744-5-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman Bug: 185125206 Signed-off-by: Greg Kroah-Hartman Change-Id: Ib42bf5a65b2e21f15ae3f223f8949719c8ffeea5 --- fs/aio.c | 83 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index 47bb7b5685ba..2820e779fe1f 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -183,8 +183,9 @@ struct poll_iocb { struct file *file; struct wait_queue_head *head; __poll_t events; - bool done; bool cancelled; + bool work_scheduled; + bool work_need_resched; struct wait_queue_entry wait; struct work_struct work; }; @@ -1645,14 +1646,26 @@ static void aio_poll_complete_work(struct work_struct *work) * avoid further branches in the fast path. */ spin_lock_irq(&ctx->ctx_lock); + spin_lock(&req->head->lock); if (!mask && !READ_ONCE(req->cancelled)) { - add_wait_queue(req->head, &req->wait); + /* + * The request isn't actually ready to be completed yet. + * Reschedule completion if another wakeup came in. + */ + if (req->work_need_resched) { + schedule_work(&req->work); + req->work_need_resched = false; + } else { + req->work_scheduled = false; + } + spin_unlock(&req->head->lock); spin_unlock_irq(&ctx->ctx_lock); return; } + list_del_init(&req->wait.entry); + spin_unlock(&req->head->lock); list_del_init(&iocb->ki_list); iocb->ki_res.res = mangle_poll(mask); - req->done = true; spin_unlock_irq(&ctx->ctx_lock); iocb_put(iocb); @@ -1666,9 +1679,9 @@ static int aio_poll_cancel(struct kiocb *iocb) spin_lock(&req->head->lock); WRITE_ONCE(req->cancelled, true); - if (!list_empty(&req->wait.entry)) { - list_del_init(&req->wait.entry); + if (!req->work_scheduled) { schedule_work(&aiocb->poll.work); + req->work_scheduled = true; } spin_unlock(&req->head->lock); @@ -1687,20 +1700,26 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, if (mask && !(mask & req->events)) return 0; - list_del_init(&req->wait.entry); - - if (mask && spin_trylock_irqsave(&iocb->ki_ctx->ctx_lock, flags)) { + /* + * Complete the request inline if possible. This requires that three + * conditions be met: + * 1. An event mask must have been passed. If a plain wakeup was done + * instead, then mask == 0 and we have to call vfs_poll() to get + * the events, so inline completion isn't possible. + * 2. The completion work must not have already been scheduled. + * 3. ctx_lock must not be busy. We have to use trylock because we + * already hold the waitqueue lock, so this inverts the normal + * locking order. Use irqsave/irqrestore because not all + * filesystems (e.g. fuse) call this function with IRQs disabled, + * yet IRQs have to be disabled before ctx_lock is obtained. + */ + if (mask && !req->work_scheduled && + spin_trylock_irqsave(&iocb->ki_ctx->ctx_lock, flags)) { struct kioctx *ctx = iocb->ki_ctx; - /* - * Try to complete the iocb inline if we can. Use - * irqsave/irqrestore because not all filesystems (e.g. fuse) - * call this function with IRQs disabled and because IRQs - * have to be disabled before ctx_lock is obtained. - */ + list_del_init(&req->wait.entry); list_del(&iocb->ki_list); iocb->ki_res.res = mangle_poll(mask); - req->done = true; if (iocb->ki_eventfd && eventfd_signal_count()) { iocb = NULL; INIT_WORK(&req->work, aio_poll_put_work); @@ -1710,7 +1729,20 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, if (iocb) iocb_put(iocb); } else { - schedule_work(&req->work); + /* + * Schedule the completion work if needed. If it was already + * scheduled, record that another wakeup came in. + * + * Don't remove the request from the waitqueue here, as it might + * not actually be complete yet (we won't know until vfs_poll() + * is called), and we must not miss any wakeups. + */ + if (req->work_scheduled) { + req->work_need_resched = true; + } else { + schedule_work(&req->work); + req->work_scheduled = true; + } } return 1; } @@ -1757,8 +1789,9 @@ static int aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) req->events = demangle_poll(iocb->aio_buf) | EPOLLERR | EPOLLHUP; req->head = NULL; - req->done = false; req->cancelled = false; + req->work_scheduled = false; + req->work_need_resched = false; apt.pt._qproc = aio_poll_queue_proc; apt.pt._key = req->events; @@ -1773,17 +1806,27 @@ static int aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) spin_lock_irq(&ctx->ctx_lock); if (likely(req->head)) { spin_lock(&req->head->lock); - if (unlikely(list_empty(&req->wait.entry))) { - if (apt.error) + if (list_empty(&req->wait.entry) || req->work_scheduled) { + /* + * aio_poll_wake() already either scheduled the async + * completion work, or completed the request inline. + */ + if (apt.error) /* unsupported case: multiple queues */ cancel = true; apt.error = 0; mask = 0; } if (mask || apt.error) { + /* Steal to complete synchronously. */ list_del_init(&req->wait.entry); } else if (cancel) { + /* Cancel if possible (may be too late though). */ WRITE_ONCE(req->cancelled, true); - } else if (!req->done) { /* actually waiting for an event */ + } else if (!list_empty(&req->wait.entry)) { + /* + * Actually waiting for an event, so add the request to + * active_reqs so that it can be cancelled if needed. + */ list_add_tail(&aiocb->ki_list, &ctx->active_reqs); aiocb->ki_cancel = aio_poll_cancel; } From 0e2d9234d4d63d80df752a1c164d898256ab11a8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 10 Dec 2021 15:50:54 -0800 Subject: [PATCH 34/71] UPSTREAM: aio: fix use-after-free due to missing POLLFREE handling commit 50252e4b5e989ce64555c7aef7516bdefc2fea72 upstream. signalfd_poll() and binder_poll() are special in that they use a waitqueue whose lifetime is the current task, rather than the struct file as is normally the case. This is okay for blocking polls, since a blocking poll occurs within one task; however, non-blocking polls require another solution. This solution is for the queue to be cleared before it is freed, by sending a POLLFREE notification to all waiters. Unfortunately, only eventpoll handles POLLFREE. A second type of non-blocking poll, aio poll, was added in kernel v4.18, and it doesn't handle POLLFREE. This allows a use-after-free to occur if a signalfd or binder fd is polled with aio poll, and the waitqueue gets freed. Fix this by making aio poll handle POLLFREE. A patch by Ramji Jiyani (https://lore.kernel.org/r/20211027011834.2497484-1-ramjiyani@google.com) tried to do this by making aio_poll_wake() always complete the request inline if POLLFREE is seen. However, that solution had two bugs. First, it introduced a deadlock, as it unconditionally locked the aio context while holding the waitqueue lock, which inverts the normal locking order. Second, it didn't consider that POLLFREE notifications are missed while the request has been temporarily de-queued. The second problem was solved by my previous patch. This patch then properly fixes the use-after-free by handling POLLFREE in a deadlock-free way. It does this by taking advantage of the fact that freeing of the waitqueue is RCU-delayed, similar to what eventpoll does. Fixes: 2c14fa838cbe ("aio: implement IOCB_CMD_POLL") Cc: # v4.18+ Link: https://lore.kernel.org/r/20211209010455.42744-6-ebiggers@kernel.org Signed-off-by: Eric Biggers Signed-off-by: Greg Kroah-Hartman Bug: 185125206 Signed-off-by: Greg Kroah-Hartman Change-Id: I87b4b20981f4283b15d51c48c8e422fcf0612ba8 --- fs/aio.c | 137 ++++++++++++++++++++++++-------- include/uapi/asm-generic/poll.h | 2 +- 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index 2820e779fe1f..fb92c32a6f1e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1627,6 +1627,51 @@ static void aio_poll_put_work(struct work_struct *work) iocb_put(iocb); } +/* + * Safely lock the waitqueue which the request is on, synchronizing with the + * case where the ->poll() provider decides to free its waitqueue early. + * + * Returns true on success, meaning that req->head->lock was locked, req->wait + * is on req->head, and an RCU read lock was taken. Returns false if the + * request was already removed from its waitqueue (which might no longer exist). + */ +static bool poll_iocb_lock_wq(struct poll_iocb *req) +{ + wait_queue_head_t *head; + + /* + * While we hold the waitqueue lock and the waitqueue is nonempty, + * wake_up_pollfree() will wait for us. However, taking the waitqueue + * lock in the first place can race with the waitqueue being freed. + * + * We solve this as eventpoll does: by taking advantage of the fact that + * all users of wake_up_pollfree() will RCU-delay the actual free. If + * we enter rcu_read_lock() and see that the pointer to the queue is + * non-NULL, we can then lock it without the memory being freed out from + * under us, then check whether the request is still on the queue. + * + * Keep holding rcu_read_lock() as long as we hold the queue lock, in + * case the caller deletes the entry from the queue, leaving it empty. + * In that case, only RCU prevents the queue memory from being freed. + */ + rcu_read_lock(); + head = smp_load_acquire(&req->head); + if (head) { + spin_lock(&head->lock); + if (!list_empty(&req->wait.entry)) + return true; + spin_unlock(&head->lock); + } + rcu_read_unlock(); + return false; +} + +static void poll_iocb_unlock_wq(struct poll_iocb *req) +{ + spin_unlock(&req->head->lock); + rcu_read_unlock(); +} + static void aio_poll_complete_work(struct work_struct *work) { struct poll_iocb *req = container_of(work, struct poll_iocb, work); @@ -1646,24 +1691,25 @@ static void aio_poll_complete_work(struct work_struct *work) * avoid further branches in the fast path. */ spin_lock_irq(&ctx->ctx_lock); - spin_lock(&req->head->lock); - if (!mask && !READ_ONCE(req->cancelled)) { - /* - * The request isn't actually ready to be completed yet. - * Reschedule completion if another wakeup came in. - */ - if (req->work_need_resched) { - schedule_work(&req->work); - req->work_need_resched = false; - } else { - req->work_scheduled = false; + if (poll_iocb_lock_wq(req)) { + if (!mask && !READ_ONCE(req->cancelled)) { + /* + * The request isn't actually ready to be completed yet. + * Reschedule completion if another wakeup came in. + */ + if (req->work_need_resched) { + schedule_work(&req->work); + req->work_need_resched = false; + } else { + req->work_scheduled = false; + } + poll_iocb_unlock_wq(req); + spin_unlock_irq(&ctx->ctx_lock); + return; } - spin_unlock(&req->head->lock); - spin_unlock_irq(&ctx->ctx_lock); - return; - } - list_del_init(&req->wait.entry); - spin_unlock(&req->head->lock); + list_del_init(&req->wait.entry); + poll_iocb_unlock_wq(req); + } /* else, POLLFREE has freed the waitqueue, so we must complete */ list_del_init(&iocb->ki_list); iocb->ki_res.res = mangle_poll(mask); spin_unlock_irq(&ctx->ctx_lock); @@ -1677,13 +1723,14 @@ static int aio_poll_cancel(struct kiocb *iocb) struct aio_kiocb *aiocb = container_of(iocb, struct aio_kiocb, rw); struct poll_iocb *req = &aiocb->poll; - spin_lock(&req->head->lock); - WRITE_ONCE(req->cancelled, true); - if (!req->work_scheduled) { - schedule_work(&aiocb->poll.work); - req->work_scheduled = true; - } - spin_unlock(&req->head->lock); + if (poll_iocb_lock_wq(req)) { + WRITE_ONCE(req->cancelled, true); + if (!req->work_scheduled) { + schedule_work(&aiocb->poll.work); + req->work_scheduled = true; + } + poll_iocb_unlock_wq(req); + } /* else, the request was force-cancelled by POLLFREE already */ return 0; } @@ -1735,7 +1782,8 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, * * Don't remove the request from the waitqueue here, as it might * not actually be complete yet (we won't know until vfs_poll() - * is called), and we must not miss any wakeups. + * is called), and we must not miss any wakeups. POLLFREE is an + * exception to this; see below. */ if (req->work_scheduled) { req->work_need_resched = true; @@ -1743,6 +1791,28 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, schedule_work(&req->work); req->work_scheduled = true; } + + /* + * If the waitqueue is being freed early but we can't complete + * the request inline, we have to tear down the request as best + * we can. That means immediately removing the request from its + * waitqueue and preventing all further accesses to the + * waitqueue via the request. We also need to schedule the + * completion work (done above). Also mark the request as + * cancelled, to potentially skip an unneeded call to ->poll(). + */ + if (mask & POLLFREE) { + WRITE_ONCE(req->cancelled, true); + list_del_init(&req->wait.entry); + + /* + * Careful: this *must* be the last step, since as soon + * as req->head is NULL'ed out, the request can be + * completed and freed, since aio_poll_complete_work() + * will no longer need to take the waitqueue lock. + */ + smp_store_release(&req->head, NULL); + } } return 1; } @@ -1750,6 +1820,7 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, struct aio_poll_table { struct poll_table_struct pt; struct aio_kiocb *iocb; + bool queued; int error; }; @@ -1760,11 +1831,12 @@ aio_poll_queue_proc(struct file *file, struct wait_queue_head *head, struct aio_poll_table *pt = container_of(p, struct aio_poll_table, pt); /* multiple wait queues per file are not supported */ - if (unlikely(pt->iocb->poll.head)) { + if (unlikely(pt->queued)) { pt->error = -EINVAL; return; } + pt->queued = true; pt->error = 0; pt->iocb->poll.head = head; add_wait_queue(head, &pt->iocb->poll.wait); @@ -1796,6 +1868,7 @@ static int aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) apt.pt._qproc = aio_poll_queue_proc; apt.pt._key = req->events; apt.iocb = aiocb; + apt.queued = false; apt.error = -EINVAL; /* same as no support for IOCB_CMD_POLL */ /* initialized the list so that we can do list_empty checks */ @@ -1804,9 +1877,10 @@ static int aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) mask = vfs_poll(req->file, &apt.pt) & req->events; spin_lock_irq(&ctx->ctx_lock); - if (likely(req->head)) { - spin_lock(&req->head->lock); - if (list_empty(&req->wait.entry) || req->work_scheduled) { + if (likely(apt.queued)) { + bool on_queue = poll_iocb_lock_wq(req); + + if (!on_queue || req->work_scheduled) { /* * aio_poll_wake() already either scheduled the async * completion work, or completed the request inline. @@ -1822,7 +1896,7 @@ static int aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) } else if (cancel) { /* Cancel if possible (may be too late though). */ WRITE_ONCE(req->cancelled, true); - } else if (!list_empty(&req->wait.entry)) { + } else if (on_queue) { /* * Actually waiting for an event, so add the request to * active_reqs so that it can be cancelled if needed. @@ -1830,7 +1904,8 @@ static int aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) list_add_tail(&aiocb->ki_list, &ctx->active_reqs); aiocb->ki_cancel = aio_poll_cancel; } - spin_unlock(&req->head->lock); + if (on_queue) + poll_iocb_unlock_wq(req); } if (mask) { /* no async, we'd stolen it */ aiocb->ki_res.res = mangle_poll(mask); diff --git a/include/uapi/asm-generic/poll.h b/include/uapi/asm-generic/poll.h index 41b509f410bf..f9c520ce4bf4 100644 --- a/include/uapi/asm-generic/poll.h +++ b/include/uapi/asm-generic/poll.h @@ -29,7 +29,7 @@ #define POLLRDHUP 0x2000 #endif -#define POLLFREE (__force __poll_t)0x4000 /* currently only for epoll */ +#define POLLFREE (__force __poll_t)0x4000 #define POLL_BUSY_LOOP (__force __poll_t)0x8000 From 08122aebc024341d5cfca978d88e2982a1f87a82 Mon Sep 17 00:00:00 2001 From: Rohith Kollalsi Date: Mon, 22 Nov 2021 16:27:03 +0530 Subject: [PATCH 35/71] usb: f_cdev: Handle the serial state properly Currently when dtr high is send by the host, dsr high should be sent by the device by performing a term io to acm conversion. But if dtr high is received twice, serial state which is converted to acm signal is again converted, leading to serial state becoming null. Fix this by clearing ACM bits from previous conversion before performing uart to acm conversion. Change-Id: Iaad3f200599f6bd4224db0c3987420cc022b34ef Signed-off-by: Rohith Kollalsi --- drivers/usb/gadget/function/f_cdev.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/function/f_cdev.c b/drivers/usb/gadget/function/f_cdev.c index 29bc22bb2ef5..9de5f19f3bbf 100644 --- a/drivers/usb/gadget/function/f_cdev.c +++ b/drivers/usb/gadget/function/f_cdev.c @@ -358,21 +358,28 @@ static inline struct f_cdev *cser_to_port(struct cserial *cser) return container_of(cser, struct f_cdev, port_usb); } -static unsigned int convert_uart_sigs_to_acm(unsigned int uart_sig) +static unsigned int convert_uart_sigs_to_acm(struct cserial *cser, unsigned int uart_sig) { - unsigned int acm_sig = 0; + u16 state; + + state = cser->serial_state; + + /* Make sure that ACM bits from previous conversion are cleared */ + state &= ~(ACM_CTRL_RI | ACM_CTRL_DCD | ACM_CTRL_DSR | ACM_CTRL_BRK); /* should this needs to be in calling functions ??? */ - uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR); + uart_sig &= (TIOCM_RI | TIOCM_CD | TIOCM_DSR | TIOCM_CTS); if (uart_sig & TIOCM_RI) - acm_sig |= ACM_CTRL_RI; + state |= ACM_CTRL_RI; if (uart_sig & TIOCM_CD) - acm_sig |= ACM_CTRL_DCD; + state |= ACM_CTRL_DCD; if (uart_sig & TIOCM_DSR) - acm_sig |= ACM_CTRL_DSR; + state |= ACM_CTRL_DSR; + if (uart_sig & TIOCM_CTS) + state |= ACM_CTRL_BRK; - return acm_sig; + return state; } static unsigned int convert_acm_sigs_to_uart(unsigned int acm_sig) @@ -1660,7 +1667,7 @@ static void usb_cser_notify_modem(void *fport, int ctrl_bits) unsigned int cbits_to_laptop; result = f_cdev_tiocmget(port); - cbits_to_laptop = convert_uart_sigs_to_acm(result); + cbits_to_laptop = convert_uart_sigs_to_acm(cser, result); if (cser->send_modem_ctrl_bits) cser->send_modem_ctrl_bits(cser, cbits_to_laptop); } From 859afae3297db7346d9331bd7cb12e767e61bee6 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Mon, 13 Jan 2020 23:31:39 +0100 Subject: [PATCH 36/71] net: macsec: move some definitions in a dedicated header This patch moves some structure, type and identifier definitions into a MACsec specific header. This patch does not modify how the MACsec code is running and only move things around. This is a preparation for the future MACsec hardware offloading support, which will re-use those definitions outside macsec.c. Change-Id: If052b8bf048951d915ea9def85512adb41b20520 Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller Git-commit: c0e4eadfb8daf2e9557c7450f9b237c08b404419 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [quic_krudrapa@quicinc.com: Fixed merge conflicts] Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 164 +-------------------------------------- include/net/macsec.h | 177 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 163 deletions(-) create mode 100644 include/net/macsec.h diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index f233a6933a97..2a3b2fa0a7df 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -16,12 +16,11 @@ #include #include #include +#include #include #include -typedef u64 __bitwise sci_t; - #define MACSEC_SCI_LEN 8 /* SecTAG length = macsec_eth_header without the optional SCI */ @@ -59,8 +58,6 @@ struct macsec_eth_header { #define GCM_AES_IV_LEN 12 #define DEFAULT_ICV_LEN 16 -#define MACSEC_NUM_AN 4 /* 2 bits for the association number */ - #define for_each_rxsc(secy, sc) \ for (sc = rcu_dereference_bh(secy->rx_sc); \ sc; \ @@ -78,49 +75,6 @@ struct gcm_iv { __be32 pn; }; -/** - * struct macsec_key - SA key - * @id: user-provided key identifier - * @tfm: crypto struct, key storage - */ -struct macsec_key { - u8 id[MACSEC_KEYID_LEN]; - struct crypto_aead *tfm; -}; - -struct macsec_rx_sc_stats { - __u64 InOctetsValidated; - __u64 InOctetsDecrypted; - __u64 InPktsUnchecked; - __u64 InPktsDelayed; - __u64 InPktsOK; - __u64 InPktsInvalid; - __u64 InPktsLate; - __u64 InPktsNotValid; - __u64 InPktsNotUsingSA; - __u64 InPktsUnusedSA; -}; - -struct macsec_rx_sa_stats { - __u32 InPktsOK; - __u32 InPktsInvalid; - __u32 InPktsNotValid; - __u32 InPktsNotUsingSA; - __u32 InPktsUnusedSA; -}; - -struct macsec_tx_sa_stats { - __u32 OutPktsProtected; - __u32 OutPktsEncrypted; -}; - -struct macsec_tx_sc_stats { - __u64 OutPktsProtected; - __u64 OutPktsEncrypted; - __u64 OutOctetsProtected; - __u64 OutOctetsEncrypted; -}; - struct macsec_dev_stats { __u64 OutPktsUntagged; __u64 InPktsUntagged; @@ -132,124 +86,8 @@ struct macsec_dev_stats { __u64 InPktsOverrun; }; -/** - * struct macsec_rx_sa - receive secure association - * @active: - * @next_pn: packet number expected for the next packet - * @lock: protects next_pn manipulations - * @key: key structure - * @stats: per-SA stats - */ -struct macsec_rx_sa { - struct macsec_key key; - spinlock_t lock; - u32 next_pn; - refcount_t refcnt; - bool active; - struct macsec_rx_sa_stats __percpu *stats; - struct macsec_rx_sc *sc; - struct rcu_head rcu; -}; - -struct pcpu_rx_sc_stats { - struct macsec_rx_sc_stats stats; - struct u64_stats_sync syncp; -}; - -/** - * struct macsec_rx_sc - receive secure channel - * @sci: secure channel identifier for this SC - * @active: channel is active - * @sa: array of secure associations - * @stats: per-SC stats - */ -struct macsec_rx_sc { - struct macsec_rx_sc __rcu *next; - sci_t sci; - bool active; - struct macsec_rx_sa __rcu *sa[MACSEC_NUM_AN]; - struct pcpu_rx_sc_stats __percpu *stats; - refcount_t refcnt; - struct rcu_head rcu_head; -}; - -/** - * struct macsec_tx_sa - transmit secure association - * @active: - * @next_pn: packet number to use for the next packet - * @lock: protects next_pn manipulations - * @key: key structure - * @stats: per-SA stats - */ -struct macsec_tx_sa { - struct macsec_key key; - spinlock_t lock; - u32 next_pn; - refcount_t refcnt; - bool active; - struct macsec_tx_sa_stats __percpu *stats; - struct rcu_head rcu; -}; - -struct pcpu_tx_sc_stats { - struct macsec_tx_sc_stats stats; - struct u64_stats_sync syncp; -}; - -/** - * struct macsec_tx_sc - transmit secure channel - * @active: - * @encoding_sa: association number of the SA currently in use - * @encrypt: encrypt packets on transmit, or authenticate only - * @send_sci: always include the SCI in the SecTAG - * @end_station: - * @scb: single copy broadcast flag - * @sa: array of secure associations - * @stats: stats for this TXSC - */ -struct macsec_tx_sc { - bool active; - u8 encoding_sa; - bool encrypt; - bool send_sci; - bool end_station; - bool scb; - struct macsec_tx_sa __rcu *sa[MACSEC_NUM_AN]; - struct pcpu_tx_sc_stats __percpu *stats; -}; - #define MACSEC_VALIDATE_DEFAULT MACSEC_VALIDATE_STRICT -/** - * struct macsec_secy - MACsec Security Entity - * @netdev: netdevice for this SecY - * @n_rx_sc: number of receive secure channels configured on this SecY - * @sci: secure channel identifier used for tx - * @key_len: length of keys used by the cipher suite - * @icv_len: length of ICV used by the cipher suite - * @validate_frames: validation mode - * @operational: MAC_Operational flag - * @protect_frames: enable protection for this SecY - * @replay_protect: enable packet number checks on receive - * @replay_window: size of the replay window - * @tx_sc: transmit secure channel - * @rx_sc: linked list of receive secure channels - */ -struct macsec_secy { - struct net_device *netdev; - unsigned int n_rx_sc; - sci_t sci; - u16 key_len; - u16 icv_len; - enum macsec_validation_type validate_frames; - bool operational; - bool protect_frames; - bool replay_protect; - u32 replay_window; - struct macsec_tx_sc tx_sc; - struct macsec_rx_sc __rcu *rx_sc; -}; - struct pcpu_secy_stats { struct macsec_dev_stats stats; struct u64_stats_sync syncp; diff --git a/include/net/macsec.h b/include/net/macsec.h new file mode 100644 index 000000000000..e7b41c1043f6 --- /dev/null +++ b/include/net/macsec.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * MACsec netdev header, used for h/w accelerated implementations. + * + * Copyright (c) 2015 Sabrina Dubroca + */ +#ifndef _NET_MACSEC_H_ +#define _NET_MACSEC_H_ + +#include +#include +#include + +typedef u64 __bitwise sci_t; + +#define MACSEC_NUM_AN 4 /* 2 bits for the association number */ + +/** + * struct macsec_key - SA key + * @id: user-provided key identifier + * @tfm: crypto struct, key storage + */ +struct macsec_key { + u8 id[MACSEC_KEYID_LEN]; + struct crypto_aead *tfm; +}; + +struct macsec_rx_sc_stats { + __u64 InOctetsValidated; + __u64 InOctetsDecrypted; + __u64 InPktsUnchecked; + __u64 InPktsDelayed; + __u64 InPktsOK; + __u64 InPktsInvalid; + __u64 InPktsLate; + __u64 InPktsNotValid; + __u64 InPktsNotUsingSA; + __u64 InPktsUnusedSA; +}; + +struct macsec_rx_sa_stats { + __u32 InPktsOK; + __u32 InPktsInvalid; + __u32 InPktsNotValid; + __u32 InPktsNotUsingSA; + __u32 InPktsUnusedSA; +}; + +struct macsec_tx_sa_stats { + __u32 OutPktsProtected; + __u32 OutPktsEncrypted; +}; + +struct macsec_tx_sc_stats { + __u64 OutPktsProtected; + __u64 OutPktsEncrypted; + __u64 OutOctetsProtected; + __u64 OutOctetsEncrypted; +}; + +/** + * struct macsec_rx_sa - receive secure association + * @active: + * @next_pn: packet number expected for the next packet + * @lock: protects next_pn manipulations + * @key: key structure + * @stats: per-SA stats + */ +struct macsec_rx_sa { + struct macsec_key key; + spinlock_t lock; + u32 next_pn; + refcount_t refcnt; + bool active; + struct macsec_rx_sa_stats __percpu *stats; + struct macsec_rx_sc *sc; + struct rcu_head rcu; +}; + +struct pcpu_rx_sc_stats { + struct macsec_rx_sc_stats stats; + struct u64_stats_sync syncp; +}; + +struct pcpu_tx_sc_stats { + struct macsec_tx_sc_stats stats; + struct u64_stats_sync syncp; +}; + +/** + * struct macsec_rx_sc - receive secure channel + * @sci: secure channel identifier for this SC + * @active: channel is active + * @sa: array of secure associations + * @stats: per-SC stats + */ +struct macsec_rx_sc { + struct macsec_rx_sc __rcu *next; + sci_t sci; + bool active; + struct macsec_rx_sa __rcu *sa[MACSEC_NUM_AN]; + struct pcpu_rx_sc_stats __percpu *stats; + refcount_t refcnt; + struct rcu_head rcu_head; +}; + +/** + * struct macsec_tx_sa - transmit secure association + * @active: + * @next_pn: packet number to use for the next packet + * @lock: protects next_pn manipulations + * @key: key structure + * @stats: per-SA stats + */ +struct macsec_tx_sa { + struct macsec_key key; + spinlock_t lock; + u32 next_pn; + refcount_t refcnt; + bool active; + struct macsec_tx_sa_stats __percpu *stats; + struct rcu_head rcu; +}; + +/** + * struct macsec_tx_sc - transmit secure channel + * @active: + * @encoding_sa: association number of the SA currently in use + * @encrypt: encrypt packets on transmit, or authenticate only + * @send_sci: always include the SCI in the SecTAG + * @end_station: + * @scb: single copy broadcast flag + * @sa: array of secure associations + * @stats: stats for this TXSC + */ +struct macsec_tx_sc { + bool active; + u8 encoding_sa; + bool encrypt; + bool send_sci; + bool end_station; + bool scb; + struct macsec_tx_sa __rcu *sa[MACSEC_NUM_AN]; + struct pcpu_tx_sc_stats __percpu *stats; +}; + +/** + * struct macsec_secy - MACsec Security Entity + * @netdev: netdevice for this SecY + * @n_rx_sc: number of receive secure channels configured on this SecY + * @sci: secure channel identifier used for tx + * @key_len: length of keys used by the cipher suite + * @icv_len: length of ICV used by the cipher suite + * @validate_frames: validation mode + * @operational: MAC_Operational flag + * @protect_frames: enable protection for this SecY + * @replay_protect: enable packet number checks on receive + * @replay_window: size of the replay window + * @tx_sc: transmit secure channel + * @rx_sc: linked list of receive secure channels + */ +struct macsec_secy { + struct net_device *netdev; + unsigned int n_rx_sc; + sci_t sci; + u16 key_len; + u16 icv_len; + enum macsec_validation_type validate_frames; + bool operational; + bool protect_frames; + bool replay_protect; + u32 replay_window; + struct macsec_tx_sc tx_sc; + struct macsec_rx_sc __rcu *rx_sc; +}; + +#endif /* _NET_MACSEC_H_ */ From e593865416a0cc375df29104fdf5e734fe4e3bf0 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Mon, 13 Jan 2020 23:31:40 +0100 Subject: [PATCH 37/71] net: macsec: introduce the macsec_context structure This patch introduces the macsec_context structure. It will be used in the kernel to exchange information between the common MACsec implementation (macsec.c) and the MACsec hardware offloading implementations. This structure contains pointers to MACsec specific structures which contain the actual MACsec configuration, and to the underlying device (phydev for now). Change-Id: Ib7d4e6f1667c2f4679e4d51d68671075462237db Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller Git-commit: 76564261a7db80c5f5c624e0122a28787f266bdf Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- include/linux/phy.h | 2 ++ include/net/macsec.h | 21 +++++++++++++++++++++ include/uapi/linux/if_link.h | 7 +++++++ tools/include/uapi/linux/if_link.h | 7 +++++++ 4 files changed, 37 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index 2b3726ff9801..f48307d822f9 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -326,6 +326,8 @@ struct phy_c45_device_ids { u32 device_ids[8]; }; +struct macsec_context; + /* phy_device: An instance of a PHY * * drv: Pointer to the driver for this PHY instance diff --git a/include/net/macsec.h b/include/net/macsec.h index e7b41c1043f6..0b98803f92ec 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -174,4 +174,25 @@ struct macsec_secy { struct macsec_rx_sc __rcu *rx_sc; }; +/** + * struct macsec_context - MACsec context for hardware offloading + */ +struct macsec_context { + struct phy_device *phydev; + enum macsec_offload offload; + + struct macsec_secy *secy; + struct macsec_rx_sc *rx_sc; + struct { + unsigned char assoc_num; + u8 key[MACSEC_KEYID_LEN]; + union { + struct macsec_rx_sa *rx_sa; + struct macsec_tx_sa *tx_sa; + }; + } sa; + + u8 prepare:1; +}; + #endif /* _NET_MACSEC_H_ */ diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 4a8c02cafa9a..80c9bd8de89f 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -483,6 +483,13 @@ enum macsec_validation_type { MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, }; +enum macsec_offload { + MACSEC_OFFLOAD_OFF = 0, + MACSEC_OFFLOAD_PHY = 1, + __MACSEC_OFFLOAD_END, + MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1, +}; + /* IPVLAN section */ enum { IFLA_IPVLAN_UNSPEC, diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 4a8c02cafa9a..80c9bd8de89f 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -483,6 +483,13 @@ enum macsec_validation_type { MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1, }; +enum macsec_offload { + MACSEC_OFFLOAD_OFF = 0, + MACSEC_OFFLOAD_PHY = 1, + __MACSEC_OFFLOAD_END, + MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1, +}; + /* IPVLAN section */ enum { IFLA_IPVLAN_UNSPEC, From 090a240d7f1036b88863e3e9b32016660521f4d0 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Mon, 13 Jan 2020 23:31:41 +0100 Subject: [PATCH 38/71] net: macsec: introduce MACsec ops This patch introduces MACsec ops for drivers to support offloading MACsec operations. Change-Id: I860013e7872f19f0611d07dfd9d69284076c45fd Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller Git-commit: 0830e20b62ad156f7df5ff5b9c4cea280ebe8fef Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- include/net/macsec.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/net/macsec.h b/include/net/macsec.h index 0b98803f92ec..16e7e5061178 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -195,4 +195,28 @@ struct macsec_context { u8 prepare:1; }; +/** + * struct macsec_ops - MACsec offloading operations + */ +struct macsec_ops { + /* Device wide */ + int (*mdo_dev_open)(struct macsec_context *ctx); + int (*mdo_dev_stop)(struct macsec_context *ctx); + /* SecY */ + int (*mdo_add_secy)(struct macsec_context *ctx); + int (*mdo_upd_secy)(struct macsec_context *ctx); + int (*mdo_del_secy)(struct macsec_context *ctx); + /* Security channels */ + int (*mdo_add_rxsc)(struct macsec_context *ctx); + int (*mdo_upd_rxsc)(struct macsec_context *ctx); + int (*mdo_del_rxsc)(struct macsec_context *ctx); + /* Security associations */ + int (*mdo_add_rxsa)(struct macsec_context *ctx); + int (*mdo_upd_rxsa)(struct macsec_context *ctx); + int (*mdo_del_rxsa)(struct macsec_context *ctx); + int (*mdo_add_txsa)(struct macsec_context *ctx); + int (*mdo_upd_txsa)(struct macsec_context *ctx); + int (*mdo_del_txsa)(struct macsec_context *ctx); +}; + #endif /* _NET_MACSEC_H_ */ From 1b6505aa4991e67350fb139ec4a3ae06544057f2 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Mon, 13 Jan 2020 23:31:42 +0100 Subject: [PATCH 39/71] net: phy: add MACsec ops in phy_device This patch adds a reference to MACsec ops in the phy_device, to allow PHYs to support offloading MACsec operations. The phydev lock will be held while calling those helpers. Change-Id: I7076430d419c1ff260380e42f48d6077d673ba3f Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller Git-commit: 2e18135845b359f26c37df38ba56565496517c10 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [quic_krudrapa@quicinc.com: Fixed Merge conflicts ] Signed-off-by: Karthik Rudrapatna --- include/linux/phy.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index f48307d822f9..97c3ce74a081 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -327,6 +327,7 @@ struct phy_c45_device_ids { }; struct macsec_context; +struct macsec_ops; /* phy_device: An instance of a PHY * @@ -349,6 +350,7 @@ struct macsec_context; * attached_dev: The attached enet driver's device instance ptr * adjust_link: Callback for the enet controller to respond to * changes in the link state. + * macsec_ops: MACsec offloading ops. * * speed, duplex, pause, supported, advertising, lp_advertising, * and autoneg are used like in mii_if_info @@ -450,6 +452,10 @@ struct phy_device { ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); ANDROID_KABI_RESERVE(4); +#if IS_ENABLED(CONFIG_MACSEC) + /* MACsec management functions */ + const struct macsec_ops *macsec_ops; +#endif }; #define to_phy_device(d) container_of(to_mdio_device(d), \ struct phy_device, mdio) From 865de9354ee13103fc2c4891a52e7ecab6d1242a Mon Sep 17 00:00:00 2001 From: Karthik Rudrapatna Date: Mon, 13 Jan 2020 23:31:43 +0100 Subject: [PATCH 40/71] net: macsec: hardware offloading infrastructure This patch introduces the MACsec hardware offloading infrastructure. The main idea here is to re-use the logic and data structures of the software MACsec implementation. This allows not to duplicate definitions and structure storing the same kind of information. It also allows to use a unified genlink interface for both MACsec implementations (so that the same userspace tool, `ip macsec`, is used with the same arguments). The MACsec offloading support cannot be disabled if an interface supports it at the moment. The MACsec configuration is passed to device drivers supporting it through macsec_ops which are called from the MACsec genl helpers. Those functions call the macsec ops of PHY and Ethernet drivers in two steps: a preparation one, and a commit one. The first step is allowed to fail and should be used to check if a provided configuration is compatible with the features provided by a MACsec engine, while the second step is not allowed to fail and should only be used to enable a given MACsec configuration. Two extra calls are made: when a virtual MACsec interface is created and when it is deleted, so that the hardware driver can stay in sync. The Rx and TX handlers are modified to take in account the special case were the MACsec transformation happens in the hardware, whether in a PHY or in a MAC, as the packets seen by the networking stack on both the physical and MACsec virtual interface are exactly the same. This leads to some limitations: the hardware and software implementations can't be used on the same physical interface, as the policies would be impossible to fulfill (such as strict validation of the frames). Also only a single virtual MACsec interface can be offloaded to a physical port supporting hardware offloading as it would be impossible to guess onto which interface a given packet should go (for ingress traffic). Another limitation as of now is that the counters and statistics are not reported back from the hardware to the software MACsec implementation. This isn't an issue when using offloaded MACsec transformations, but it should be added in the future so that the MACsec state can be reported to the user (which would also improve the debug). Change-Id: Ie705609ae0d11797e6f7adb203739958287a419c Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller Git-commit: 3cf3227a21d1fb020fe26128e60321bd2151e922 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [quic_krudrapa@quicinc.com: Fixed Merge conflicts ] Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 453 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 441 insertions(+), 12 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 2a3b2fa0a7df..f07fca28e060 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include @@ -99,6 +101,7 @@ struct pcpu_secy_stats { * @real_dev: pointer to underlying netdevice * @stats: MACsec device stats * @secys: linked list of SecY's on the underlying device + * @offload: status of offloading on the MACsec device */ struct macsec_dev { struct macsec_secy secy; @@ -106,6 +109,7 @@ struct macsec_dev { struct pcpu_secy_stats __percpu *stats; struct list_head secys; struct gro_cells gro_cells; + enum macsec_offload offload; }; /** @@ -319,6 +323,56 @@ static void macsec_set_shortlen(struct macsec_eth_header *h, size_t data_len) h->short_length = data_len; } +/* Checks if a MACsec interface is being offloaded to an hardware engine */ +static bool macsec_is_offloaded(struct macsec_dev *macsec) +{ + if (macsec->offload == MACSEC_OFFLOAD_PHY) + return true; + + return false; +} + +/* Checks if underlying layers implement MACsec offloading functions. */ +static bool macsec_check_offload(enum macsec_offload offload, + struct macsec_dev *macsec) +{ + if (!macsec || !macsec->real_dev) + return false; + + if (offload == MACSEC_OFFLOAD_PHY) + return macsec->real_dev->phydev && + macsec->real_dev->phydev->macsec_ops; + + return false; +} + +static const struct macsec_ops *__macsec_get_ops(enum macsec_offload offload, + struct macsec_dev *macsec, + struct macsec_context *ctx) +{ + if (ctx) { + memset(ctx, 0, sizeof(*ctx)); + ctx->offload = offload; + + if (offload == MACSEC_OFFLOAD_PHY) + ctx->phydev = macsec->real_dev->phydev; + } + + return macsec->real_dev->phydev->macsec_ops; +} + +/* Returns a pointer to the MACsec ops struct if any and updates the MACsec + * context device reference if provided. + */ +static const struct macsec_ops *macsec_get_ops(struct macsec_dev *macsec, + struct macsec_context *ctx) +{ + if (!macsec_check_offload(macsec->offload, macsec)) + return NULL; + + return __macsec_get_ops(macsec->offload, macsec, ctx); +} + /* validate MACsec packet according to IEEE 802.1AE-2006 9.12 */ static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len) { @@ -868,8 +922,10 @@ static struct macsec_rx_sc *find_rx_sc_rtnl(struct macsec_secy *secy, sci_t sci) return NULL; } -static void handle_not_macsec(struct sk_buff *skb) +static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) { + /* Deliver to the uncontrolled port by default */ + enum rx_handler_result ret = RX_HANDLER_PASS; struct macsec_rxh_data *rxd; struct macsec_dev *macsec; @@ -884,7 +940,8 @@ static void handle_not_macsec(struct sk_buff *skb) struct sk_buff *nskb; struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats); - if (macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) { + if (!macsec_is_offloaded(macsec) && + macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) { u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoTag++; u64_stats_update_end(&secy_stats->syncp); @@ -903,9 +960,17 @@ static void handle_not_macsec(struct sk_buff *skb) secy_stats->stats.InPktsUntagged++; u64_stats_update_end(&secy_stats->syncp); } + + if (netif_running(macsec->secy.netdev) && + macsec_is_offloaded(macsec)) { + ret = RX_HANDLER_EXACT; + goto out; + } } +out: rcu_read_unlock(); + return ret; } static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) @@ -931,12 +996,8 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) goto drop_direct; hdr = macsec_ethhdr(skb); - if (hdr->eth.h_proto != htons(ETH_P_MACSEC)) { - handle_not_macsec(skb); - - /* and deliver to the uncontrolled port */ - return RX_HANDLER_PASS; - } + if (hdr->eth.h_proto != htons(ETH_P_MACSEC)) + return handle_not_macsec(skb); skb = skb_unshare(skb, GFP_ATOMIC); *pskb = skb; @@ -1444,6 +1505,40 @@ static const struct nla_policy macsec_genl_sa_policy[NUM_MACSEC_SA_ATTR] = { .len = MACSEC_MAX_KEY_LEN, }, }; +/* Offloads an operation to a device driver */ +static int macsec_offload(int (* const func)(struct macsec_context *), + struct macsec_context *ctx) +{ + int ret; + + if (unlikely(!func)) + return 0; + + if (ctx->offload == MACSEC_OFFLOAD_PHY) + mutex_lock(&ctx->phydev->lock); + + /* Phase I: prepare. The drive should fail here if there are going to be + * issues in the commit phase. + */ + ctx->prepare = true; + ret = (*func)(ctx); + if (ret) + goto phy_unlock; + + /* Phase II: commit. This step cannot fail. */ + ctx->prepare = false; + ret = (*func)(ctx); + /* This should never happen: commit is not allowed to fail */ + if (unlikely(ret)) + WARN(1, "MACsec offloading commit failed (%d)\n", ret); + +phy_unlock: + if (ctx->offload == MACSEC_OFFLOAD_PHY) + mutex_unlock(&ctx->phydev->lock); + + return ret; +} + static int parse_sa_config(struct nlattr **attrs, struct nlattr **tb_sa) { if (!attrs[MACSEC_ATTR_SA_CONFIG]) @@ -1559,13 +1654,40 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_ACTIVE]) rx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]); - nla_memcpy(rx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); rx_sa->sc = rx_sc; + + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + err = -EOPNOTSUPP; + goto cleanup; + } + + ctx.sa.assoc_num = assoc_num; + ctx.sa.rx_sa = rx_sa; + memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), + MACSEC_KEYID_LEN); + + err = macsec_offload(ops->mdo_add_rxsa, &ctx); + if (err) + goto cleanup; + } + + nla_memcpy(rx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); rcu_assign_pointer(rx_sc->sa[assoc_num], rx_sa); rtnl_unlock(); return 0; + +cleanup: + kfree(rx_sa); + rtnl_unlock(); + return err; } static bool validate_add_rxsc(struct nlattr **attrs) @@ -1588,6 +1710,8 @@ static int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info) struct nlattr **attrs = info->attrs; struct macsec_rx_sc *rx_sc; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; + bool was_active; + int ret; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1613,12 +1737,35 @@ static int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(rx_sc); } + was_active = rx_sc->active; if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]) rx_sc->active = !!nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]); + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.rx_sc = rx_sc; + + ret = macsec_offload(ops->mdo_add_rxsc, &ctx); + if (ret) + goto cleanup; + } + rtnl_unlock(); return 0; + +cleanup: + rx_sc->active = was_active; + rtnl_unlock(); + return ret; } static bool validate_add_txsa(struct nlattr **attrs) @@ -1655,6 +1802,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) struct macsec_tx_sa *tx_sa; unsigned char assoc_num; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + bool was_operational; int err; if (!attrs[MACSEC_ATTR_IFINDEX]) @@ -1705,8 +1853,6 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) return err; } - nla_memcpy(tx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); - spin_lock_bh(&tx_sa->lock); tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&tx_sa->lock); @@ -1714,14 +1860,43 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_ACTIVE]) tx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]); + was_operational = secy->operational; if (assoc_num == tx_sc->encoding_sa && tx_sa->active) secy->operational = true; + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + err = -EOPNOTSUPP; + goto cleanup; + } + + ctx.sa.assoc_num = assoc_num; + ctx.sa.tx_sa = tx_sa; + memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), + MACSEC_KEYID_LEN); + + err = macsec_offload(ops->mdo_add_txsa, &ctx); + if (err) + goto cleanup; + } + + nla_memcpy(tx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); rcu_assign_pointer(tx_sc->sa[assoc_num], tx_sa); rtnl_unlock(); return 0; + +cleanup: + secy->operational = was_operational; + kfree(tx_sa); + rtnl_unlock(); + return err; } static int macsec_del_rxsa(struct sk_buff *skb, struct genl_info *info) @@ -1734,6 +1909,7 @@ static int macsec_del_rxsa(struct sk_buff *skb, struct genl_info *info) u8 assoc_num; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + int ret; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1757,12 +1933,35 @@ static int macsec_del_rxsa(struct sk_buff *skb, struct genl_info *info) return -EBUSY; } + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.sa.assoc_num = assoc_num; + ctx.sa.rx_sa = rx_sa; + + ret = macsec_offload(ops->mdo_del_rxsa, &ctx); + if (ret) + goto cleanup; + } + RCU_INIT_POINTER(rx_sc->sa[assoc_num], NULL); clear_rx_sa(rx_sa); rtnl_unlock(); return 0; + +cleanup: + rtnl_unlock(); + return ret; } static int macsec_del_rxsc(struct sk_buff *skb, struct genl_info *info) @@ -1773,6 +1972,7 @@ static int macsec_del_rxsc(struct sk_buff *skb, struct genl_info *info) struct macsec_rx_sc *rx_sc; sci_t sci; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; + int ret; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1799,10 +1999,31 @@ static int macsec_del_rxsc(struct sk_buff *skb, struct genl_info *info) return -ENODEV; } + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.rx_sc = rx_sc; + ret = macsec_offload(ops->mdo_del_rxsc, &ctx); + if (ret) + goto cleanup; + } + free_rx_sc(rx_sc); rtnl_unlock(); return 0; + +cleanup: + rtnl_unlock(); + return ret; } static int macsec_del_txsa(struct sk_buff *skb, struct genl_info *info) @@ -1814,6 +2035,7 @@ static int macsec_del_txsa(struct sk_buff *skb, struct genl_info *info) struct macsec_tx_sa *tx_sa; u8 assoc_num; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + int ret; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1834,12 +2056,35 @@ static int macsec_del_txsa(struct sk_buff *skb, struct genl_info *info) return -EBUSY; } + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.sa.assoc_num = assoc_num; + ctx.sa.tx_sa = tx_sa; + + ret = macsec_offload(ops->mdo_del_txsa, &ctx); + if (ret) + goto cleanup; + } + RCU_INIT_POINTER(tx_sc->sa[assoc_num], NULL); clear_tx_sa(tx_sa); rtnl_unlock(); return 0; + +cleanup: + rtnl_unlock(); + return ret; } static bool validate_upd_sa(struct nlattr **attrs) @@ -1872,6 +2117,9 @@ static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info) struct macsec_tx_sa *tx_sa; u8 assoc_num; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + bool was_operational, was_active; + u32 prev_pn = 0; + int ret = 0; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1892,19 +2140,52 @@ static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&tx_sa->lock); + prev_pn = tx_sa->next_pn; tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&tx_sa->lock); } + was_active = tx_sa->active; if (tb_sa[MACSEC_SA_ATTR_ACTIVE]) tx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]); + was_operational = secy->operational; if (assoc_num == tx_sc->encoding_sa) secy->operational = tx_sa->active; + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.sa.assoc_num = assoc_num; + ctx.sa.tx_sa = tx_sa; + + ret = macsec_offload(ops->mdo_upd_txsa, &ctx); + if (ret) + goto cleanup; + } + rtnl_unlock(); return 0; + +cleanup: + if (tb_sa[MACSEC_SA_ATTR_PN]) { + spin_lock_bh(&tx_sa->lock); + tx_sa->next_pn = prev_pn; + spin_unlock_bh(&tx_sa->lock); + } + tx_sa->active = was_active; + secy->operational = was_operational; + rtnl_unlock(); + return ret; } static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) @@ -1917,6 +2198,9 @@ static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) u8 assoc_num; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + bool was_active; + u32 prev_pn = 0; + int ret = 0; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1940,15 +2224,46 @@ static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&rx_sa->lock); + prev_pn = rx_sa->next_pn; rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&rx_sa->lock); } + was_active = rx_sa->active; if (tb_sa[MACSEC_SA_ATTR_ACTIVE]) rx_sa->active = nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]); + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.sa.assoc_num = assoc_num; + ctx.sa.rx_sa = rx_sa; + + ret = macsec_offload(ops->mdo_upd_rxsa, &ctx); + if (ret) + goto cleanup; + } + rtnl_unlock(); return 0; + +cleanup: + if (tb_sa[MACSEC_SA_ATTR_PN]) { + spin_lock_bh(&rx_sa->lock); + rx_sa->next_pn = prev_pn; + spin_unlock_bh(&rx_sa->lock); + } + rx_sa->active = was_active; + rtnl_unlock(); + return ret; } static int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info) @@ -1958,6 +2273,9 @@ static int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info) struct macsec_secy *secy; struct macsec_rx_sc *rx_sc; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; + unsigned int prev_n_rx_sc; + bool was_active; + int ret; if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -1975,6 +2293,8 @@ static int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(rx_sc); } + was_active = rx_sc->active; + prev_n_rx_sc = secy->n_rx_sc; if (tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]) { bool new = !!nla_get_u8(tb_rxsc[MACSEC_RXSC_ATTR_ACTIVE]); @@ -1984,9 +2304,33 @@ static int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info) rx_sc->active = new; } + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(netdev_priv(dev))) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.rx_sc = rx_sc; + + ret = macsec_offload(ops->mdo_upd_rxsc, &ctx); + if (ret) + goto cleanup; + } + rtnl_unlock(); return 0; + +cleanup: + secy->n_rx_sc = prev_n_rx_sc; + rx_sc->active = was_active; + rtnl_unlock(); + return ret; } static int copy_tx_sa_stats(struct sk_buff *skb, @@ -2554,6 +2898,11 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, struct pcpu_secy_stats *secy_stats; int ret, len; + if (macsec_is_offloaded(netdev_priv(dev))) { + skb->dev = macsec->real_dev; + return dev_queue_xmit(skb); + } + /* 10.5 */ if (!secy->protect_frames) { secy_stats = this_cpu_ptr(macsec->stats); @@ -2667,6 +3016,22 @@ static int macsec_dev_open(struct net_device *dev) goto clear_allmulti; } + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + err = -EOPNOTSUPP; + goto clear_allmulti; + } + + err = macsec_offload(ops->mdo_dev_open, &ctx); + if (err) + goto clear_allmulti; + } + if (netif_carrier_ok(real_dev)) netif_carrier_on(dev); @@ -2687,6 +3052,16 @@ static int macsec_dev_stop(struct net_device *dev) netif_carrier_off(dev); + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(macsec, &ctx); + if (ops) + macsec_offload(ops->mdo_dev_stop, &ctx); + } + dev_mc_unsync(real_dev, dev); dev_uc_unsync(real_dev, dev); @@ -2925,6 +3300,11 @@ static int macsec_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { + struct macsec_dev *macsec = macsec_priv(dev); + struct macsec_tx_sa tx_sc; + struct macsec_secy secy; + int ret; + if (!data) return 0; @@ -2934,7 +3314,41 @@ static int macsec_changelink(struct net_device *dev, struct nlattr *tb[], data[IFLA_MACSEC_PORT]) return -EINVAL; - return macsec_changelink_common(dev, data); + /* Keep a copy of unmodified secy and tx_sc, in case the offload + * propagation fails, to revert macsec_changelink_common. + */ + memcpy(&secy, &macsec->secy, sizeof(secy)); + memcpy(&tx_sc, &macsec->secy.tx_sc, sizeof(tx_sc)); + + ret = macsec_changelink_common(dev, data); + if (ret) + return ret; + + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + int ret; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto cleanup; + } + + ctx.secy = &macsec->secy; + ret = macsec_offload(ops->mdo_upd_secy, &ctx); + if (ret) + goto cleanup; + } + + return 0; + +cleanup: + memcpy(&macsec->secy.tx_sc, &tx_sc, sizeof(tx_sc)); + memcpy(&macsec->secy, &secy, sizeof(secy)); + + return ret; } static void macsec_del_dev(struct macsec_dev *macsec) @@ -2977,6 +3391,18 @@ static void macsec_dellink(struct net_device *dev, struct list_head *head) struct net_device *real_dev = macsec->real_dev; struct macsec_rxh_data *rxd = macsec_data_rtnl(real_dev); + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(netdev_priv(dev), &ctx); + if (ops) { + ctx.secy = &macsec->secy; + macsec_offload(ops->mdo_del_secy, &ctx); + } + } + macsec_common_dellink(dev, head); if (list_empty(&rxd->secys)) { @@ -3085,6 +3511,9 @@ static int macsec_newlink(struct net *net, struct net_device *dev, macsec->real_dev = real_dev; + /* MACsec offloading is off by default */ + macsec->offload = MACSEC_OFFLOAD_OFF; + if (data && data[IFLA_MACSEC_ICV_LEN]) icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]); mtu = real_dev->mtu - icv_len - macsec_extra_len(true); From a3430cfbacab86f7d75dc3b1c219eed8174c148f Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Mon, 13 Jan 2020 23:31:44 +0100 Subject: [PATCH 41/71] net: macsec: add nla support for changing the offloading selection MACsec offloading to underlying hardware devices is disabled by default (the software implementation is used). This patch adds support for changing this setting through the MACsec netlink interface. Many checks are done when enabling offloading on a given MACsec interface as there are limitations (it must be supported by the hardware, only a single interface can be offloaded on a given physical device at a time, rules can't be moved for now). Change-Id: Ia0b0898e7972f24e79eefcd65ff480ac819a4bb9 Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller Git-commit: dcb780fb279514f268826f2e9f4df3bc75610703 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 145 ++++++++++++++++++++++++++++++++- include/uapi/linux/if_macsec.h | 11 +++ 2 files changed, 153 insertions(+), 3 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index f07fca28e060..85effa615354 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1488,6 +1488,7 @@ static const struct nla_policy macsec_genl_policy[NUM_MACSEC_ATTR] = { [MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 }, [MACSEC_ATTR_RXSC_CONFIG] = { .type = NLA_NESTED }, [MACSEC_ATTR_SA_CONFIG] = { .type = NLA_NESTED }, + [MACSEC_ATTR_OFFLOAD] = { .type = NLA_NESTED }, }; static const struct nla_policy macsec_genl_rxsc_policy[NUM_MACSEC_RXSC_ATTR] = { @@ -1505,6 +1506,10 @@ static const struct nla_policy macsec_genl_sa_policy[NUM_MACSEC_SA_ATTR] = { .len = MACSEC_MAX_KEY_LEN, }, }; +static const struct nla_policy macsec_genl_offload_policy[NUM_MACSEC_OFFLOAD_ATTR] = { + [MACSEC_OFFLOAD_ATTR_TYPE] = { .type = NLA_U8 }, +}; + /* Offloads an operation to a device driver */ static int macsec_offload(int (* const func)(struct macsec_context *), struct macsec_context *ctx) @@ -2333,6 +2338,126 @@ cleanup: return ret; } +static bool macsec_is_configured(struct macsec_dev *macsec) +{ + struct macsec_secy *secy = &macsec->secy; + struct macsec_tx_sc *tx_sc = &secy->tx_sc; + int i; + + if (secy->n_rx_sc > 0) + return true; + + for (i = 0; i < MACSEC_NUM_AN; i++) + if (tx_sc->sa[i]) + return true; + + return false; +} + +static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb_offload[MACSEC_OFFLOAD_ATTR_MAX + 1]; + enum macsec_offload offload, prev_offload; + int (*func)(struct macsec_context *ctx); + struct nlattr **attrs = info->attrs; + struct net_device *dev, *loop_dev; + const struct macsec_ops *ops; + struct macsec_context ctx; + struct macsec_dev *macsec; + struct net *loop_net; + int ret; + + if (!attrs[MACSEC_ATTR_IFINDEX]) + return -EINVAL; + + if (!attrs[MACSEC_ATTR_OFFLOAD]) + return -EINVAL; + + if (nla_parse_nested_deprecated(tb_offload, MACSEC_OFFLOAD_ATTR_MAX, + attrs[MACSEC_ATTR_OFFLOAD], + macsec_genl_offload_policy, NULL)) + return -EINVAL; + + dev = get_dev_from_nl(genl_info_net(info), attrs); + if (IS_ERR(dev)) + return PTR_ERR(dev); + macsec = macsec_priv(dev); + + offload = nla_get_u8(tb_offload[MACSEC_OFFLOAD_ATTR_TYPE]); + if (macsec->offload == offload) + return 0; + + /* Check if the offloading mode is supported by the underlying layers */ + if (offload != MACSEC_OFFLOAD_OFF && + !macsec_check_offload(offload, macsec)) + return -EOPNOTSUPP; + + if (offload == MACSEC_OFFLOAD_OFF) + goto skip_limitation; + + /* Check the physical interface isn't offloading another interface + * first. + */ + for_each_net(loop_net) { + for_each_netdev(loop_net, loop_dev) { + struct macsec_dev *priv; + + if (!netif_is_macsec(loop_dev)) + continue; + + priv = macsec_priv(loop_dev); + + if (priv->real_dev == macsec->real_dev && + priv->offload != MACSEC_OFFLOAD_OFF) + return -EBUSY; + } + } + +skip_limitation: + /* Check if the net device is busy. */ + if (netif_running(dev)) + return -EBUSY; + + rtnl_lock(); + + prev_offload = macsec->offload; + macsec->offload = offload; + + /* Check if the device already has rules configured: we do not support + * rules migration. + */ + if (macsec_is_configured(macsec)) { + ret = -EBUSY; + goto rollback; + } + + ops = __macsec_get_ops(offload == MACSEC_OFFLOAD_OFF ? prev_offload : offload, + macsec, &ctx); + if (!ops) { + ret = -EOPNOTSUPP; + goto rollback; + } + + if (prev_offload == MACSEC_OFFLOAD_OFF) + func = ops->mdo_add_secy; + else + func = ops->mdo_del_secy; + + ctx.secy = &macsec->secy; + ret = macsec_offload(func, &ctx); + if (ret) + goto rollback; + + rtnl_unlock(); + return 0; + +rollback: + macsec->offload = prev_offload; + + rtnl_unlock(); + return ret; +} + static int copy_tx_sa_stats(struct sk_buff *skb, struct macsec_tx_sa_stats __percpu *pstats) { @@ -2594,12 +2719,13 @@ static noinline_for_stack int dump_secy(struct macsec_secy *secy, struct net_device *dev, struct sk_buff *skb, struct netlink_callback *cb) { - struct macsec_rx_sc *rx_sc; + struct macsec_dev *macsec = netdev_priv(dev); struct macsec_tx_sc *tx_sc = &secy->tx_sc; struct nlattr *txsa_list, *rxsc_list; - int i, j; - void *hdr; + struct macsec_rx_sc *rx_sc; struct nlattr *attr; + void *hdr; + int i, j; hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &macsec_fam, NLM_F_MULTI, MACSEC_CMD_GET_TXSC); @@ -2611,6 +2737,13 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, if (nla_put_u32(skb, MACSEC_ATTR_IFINDEX, dev->ifindex)) goto nla_put_failure; + attr = nla_nest_start_noflag(skb, MACSEC_ATTR_OFFLOAD); + if (!attr) + goto nla_put_failure; + if (nla_put_u8(skb, MACSEC_OFFLOAD_ATTR_TYPE, macsec->offload)) + goto nla_put_failure; + nla_nest_end(skb, attr); + if (nla_put_secy(secy, skb)) goto nla_put_failure; @@ -2876,6 +3009,12 @@ static const struct genl_ops macsec_genl_ops[] = { .doit = macsec_upd_rxsa, .flags = GENL_ADMIN_PERM, }, + { + .cmd = MACSEC_CMD_UPD_OFFLOAD, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = macsec_upd_offload, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_family macsec_fam __ro_after_init = { diff --git a/include/uapi/linux/if_macsec.h b/include/uapi/linux/if_macsec.h index 98e4d5d7c45c..1d63c43c38cc 100644 --- a/include/uapi/linux/if_macsec.h +++ b/include/uapi/linux/if_macsec.h @@ -45,6 +45,7 @@ enum macsec_attrs { MACSEC_ATTR_RXSC_LIST, /* dump, nested, macsec_rxsc_attrs for each RXSC */ MACSEC_ATTR_TXSC_STATS, /* dump, nested, macsec_txsc_stats_attr */ MACSEC_ATTR_SECY_STATS, /* dump, nested, macsec_secy_stats_attr */ + MACSEC_ATTR_OFFLOAD, /* config, nested, macsec_offload_attrs */ __MACSEC_ATTR_END, NUM_MACSEC_ATTR = __MACSEC_ATTR_END, MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1, @@ -97,6 +98,15 @@ enum macsec_sa_attrs { MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, }; +enum macsec_offload_attrs { + MACSEC_OFFLOAD_ATTR_UNSPEC, + MACSEC_OFFLOAD_ATTR_TYPE, /* config/dump, u8 0..2 */ + MACSEC_OFFLOAD_ATTR_PAD, + __MACSEC_OFFLOAD_ATTR_END, + NUM_MACSEC_OFFLOAD_ATTR = __MACSEC_OFFLOAD_ATTR_END, + MACSEC_OFFLOAD_ATTR_MAX = __MACSEC_OFFLOAD_ATTR_END - 1, +}; + enum macsec_nl_commands { MACSEC_CMD_GET_TXSC, MACSEC_CMD_ADD_RXSC, @@ -108,6 +118,7 @@ enum macsec_nl_commands { MACSEC_CMD_ADD_RXSA, MACSEC_CMD_DEL_RXSA, MACSEC_CMD_UPD_RXSA, + MACSEC_CMD_UPD_OFFLOAD, }; /* u64 per-RXSC stats */ From 473bba83f24aa1c56994be0ecda255d35cd60838 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Mon, 13 Jan 2020 23:31:47 +0100 Subject: [PATCH 42/71] net: macsec: PN wrap callback Allow to call macsec_pn_wrapped from hardware drivers to notify when a PN rolls over. Some drivers might used an interrupt to implement this. Change-Id: Icf37413f1c35a78f3edaecbf514a7cd85d3bdbe9 Signed-off-by: Antoine Tenart Signed-off-by: David S. Miller Git-commit: 5c937de78b39e47ce9924fc4b863c5b727edc328 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 25 +++++++++++++++++++------ include/net/macsec.h | 2 ++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 85effa615354..9c68ab955772 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -425,6 +425,23 @@ static struct macsec_eth_header *macsec_ethhdr(struct sk_buff *skb) return (struct macsec_eth_header *)skb_mac_header(skb); } +static void __macsec_pn_wrapped(struct macsec_secy *secy, + struct macsec_tx_sa *tx_sa) +{ + pr_debug("PN wrapped, transitioning to !oper\n"); + tx_sa->active = false; + if (secy->protect_frames) + secy->operational = false; +} + +void macsec_pn_wrapped(struct macsec_secy *secy, struct macsec_tx_sa *tx_sa) +{ + spin_lock_bh(&tx_sa->lock); + __macsec_pn_wrapped(secy, tx_sa); + spin_unlock_bh(&tx_sa->lock); +} +EXPORT_SYMBOL_GPL(macsec_pn_wrapped); + static u32 tx_sa_update_pn(struct macsec_tx_sa *tx_sa, struct macsec_secy *secy) { u32 pn; @@ -433,12 +450,8 @@ static u32 tx_sa_update_pn(struct macsec_tx_sa *tx_sa, struct macsec_secy *secy) pn = tx_sa->next_pn; tx_sa->next_pn++; - if (tx_sa->next_pn == 0) { - pr_debug("PN wrapped, transitioning to !oper\n"); - tx_sa->active = false; - if (secy->protect_frames) - secy->operational = false; - } + if (tx_sa->next_pn == 0) + __macsec_pn_wrapped(secy, tx_sa); spin_unlock_bh(&tx_sa->lock); return pn; diff --git a/include/net/macsec.h b/include/net/macsec.h index 16e7e5061178..92e43db8b566 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -219,4 +219,6 @@ struct macsec_ops { int (*mdo_del_txsa)(struct macsec_context *ctx); }; +void macsec_pn_wrapped(struct macsec_secy *secy, struct macsec_tx_sa *tx_sa); + #endif /* _NET_MACSEC_H_ */ From 64e3a0682cdb8f71dfe5e368148a7b29d4bc4983 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Thu, 9 Apr 2020 14:08:08 +0000 Subject: [PATCH 43/71] net: macsec: fix using wrong structure in macsec_changelink() In the macsec_changelink(), "struct macsec_tx_sa tx_sc" is used to store "macsec_secy.tx_sc". But, the struct type of tx_sc is macsec_tx_sc, not macsec_tx_sa. So, the macsec_tx_sc should be used instead. Test commands: ip link add dummy0 type dummy ip link add macsec0 link dummy0 type macsec ip link set macsec0 type macsec encrypt off Splat looks like: [61119.963483][ T9335] ================================================================== [61119.964709][ T9335] BUG: KASAN: slab-out-of-bounds in macsec_changelink.part.34+0xb6/0x200 [macsec] [61119.965787][ T9335] Read of size 160 at addr ffff888020d69c68 by task ip/9335 [61119.966699][ T9335] [61119.966979][ T9335] CPU: 0 PID: 9335 Comm: ip Not tainted 5.6.0+ #503 [61119.967791][ T9335] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [61119.968914][ T9335] Call Trace: [61119.969324][ T9335] dump_stack+0x96/0xdb [61119.969809][ T9335] ? macsec_changelink.part.34+0xb6/0x200 [macsec] [61119.970554][ T9335] print_address_description.constprop.5+0x1be/0x360 [61119.971294][ T9335] ? macsec_changelink.part.34+0xb6/0x200 [macsec] [61119.971973][ T9335] ? macsec_changelink.part.34+0xb6/0x200 [macsec] [61119.972703][ T9335] __kasan_report+0x12a/0x170 [61119.973323][ T9335] ? macsec_changelink.part.34+0xb6/0x200 [macsec] [61119.973942][ T9335] kasan_report+0xe/0x20 [61119.974397][ T9335] check_memory_region+0x149/0x1a0 [61119.974866][ T9335] memcpy+0x1f/0x50 [61119.975209][ T9335] macsec_changelink.part.34+0xb6/0x200 [macsec] [61119.975825][ T9335] ? macsec_get_stats64+0x3e0/0x3e0 [macsec] [61119.976451][ T9335] ? kernel_text_address+0x111/0x120 [61119.976990][ T9335] ? pskb_expand_head+0x25f/0xe10 [61119.977503][ T9335] ? stack_trace_save+0x82/0xb0 [61119.977986][ T9335] ? memset+0x1f/0x40 [61119.978397][ T9335] ? __nla_validate_parse+0x98/0x1ab0 [61119.978936][ T9335] ? macsec_alloc_tfm+0x90/0x90 [macsec] [61119.979511][ T9335] ? __kasan_slab_free+0x111/0x150 [61119.980021][ T9335] ? kfree+0xce/0x2f0 [61119.980700][ T9335] ? netlink_trim+0x196/0x1f0 [61119.981420][ T9335] ? nla_memcpy+0x90/0x90 [61119.982036][ T9335] ? register_lock_class+0x19e0/0x19e0 [61119.982776][ T9335] ? memcpy+0x34/0x50 [61119.983327][ T9335] __rtnl_newlink+0x922/0x1270 [ ... ] Fixes: 3cf3227a21d1 ("net: macsec: hardware offloading infrastructure") Change-Id: I56ffe532f51b66f3201b206ef1b0d5bbea54b72e Signed-off-by: Taehee Yoo Signed-off-by: David S. Miller Git-commit: 022e9d6090599c0593c78e87dc9ba98a290e6bc4 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 9c68ab955772..c0b9056cf151 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3453,7 +3453,7 @@ static int macsec_changelink(struct net_device *dev, struct nlattr *tb[], struct netlink_ext_ack *extack) { struct macsec_dev *macsec = macsec_priv(dev); - struct macsec_tx_sa tx_sc; + struct macsec_tx_sc tx_sc; struct macsec_secy secy; int ret; From db4d38d32f8bb22a7507519b302b19ebc88d3dbd Mon Sep 17 00:00:00 2001 From: Era Mayflower Date: Mon, 9 Mar 2020 19:47:01 +0000 Subject: [PATCH 44/71] macsec: Support XPN frame handling - IEEE 802.1AEbw Support extended packet number cipher suites (802.1AEbw) frames handling. This does not include the needed netlink patches. * Added xpn boolean field to `struct macsec_secy`. * Added ssci field to `struct_macsec_tx_sa` (802.1AE figure 10-5). * Added ssci field to `struct_macsec_rx_sa` (802.1AE figure 10-5). * Added salt field to `struct macsec_key` (802.1AE 10.7 NOTE 1). * Created pn_t type for easy access to lower and upper halves. * Created salt_t type for easy access to the "ssci" and "pn" parts. * Created `macsec_fill_iv_xpn` function to create IV in XPN mode. * Support in PN recovery and preliminary replay check in XPN mode. In addition, according to IEEE 802.1AEbw figure 10-5, the PN of incoming frame can be 0 when XPN cipher suite is used, so fixed the function `macsec_validate_skb` to fail on PN=0 only if XPN is off. Change-Id: I2558cd6ecfbfac3a183095278bcaafc6e8b19f23 Signed-off-by: Era Mayflower Signed-off-by: David S. Miller Git-commit: a21ecf0e033807b976967286e6c392f48ee2049f Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 130 +++++++++++++++++++++++++++++++------------ include/net/macsec.h | 47 ++++++++++++++-- 2 files changed, 137 insertions(+), 40 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index c0b9056cf151..5e32b30772e4 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -69,6 +70,16 @@ struct macsec_eth_header { sc; \ sc = rtnl_dereference(sc->next)) +#define pn_same_half(pn1, pn2) (!(((pn1) >> 31) ^ ((pn2) >> 31))) + +struct gcm_iv_xpn { + union { + u8 short_secure_channel_id[4]; + ssci_t ssci; + }; + __be64 pn; +} __packed; + struct gcm_iv { union { u8 secure_channel_id[8]; @@ -373,8 +384,8 @@ static const struct macsec_ops *macsec_get_ops(struct macsec_dev *macsec, return __macsec_get_ops(macsec->offload, macsec, ctx); } -/* validate MACsec packet according to IEEE 802.1AE-2006 9.12 */ -static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len) +/* validate MACsec packet according to IEEE 802.1AE-2018 9.12 */ +static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len, bool xpn) { struct macsec_eth_header *h = (struct macsec_eth_header *)skb->data; int len = skb->len - 2 * ETH_ALEN; @@ -399,8 +410,8 @@ static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len) if (h->unused) return false; - /* rx.pn != 0 (figure 10-5) */ - if (!h->packet_number) + /* rx.pn != 0 if not XPN (figure 10-5 with 802.11AEbw-2013 amendment) */ + if (!h->packet_number && !xpn) return false; /* length check, f) g) h) i) */ @@ -412,6 +423,15 @@ static bool macsec_validate_skb(struct sk_buff *skb, u16 icv_len) #define MACSEC_NEEDED_HEADROOM (macsec_extra_len(true)) #define MACSEC_NEEDED_TAILROOM MACSEC_STD_ICV_LEN +static void macsec_fill_iv_xpn(unsigned char *iv, ssci_t ssci, u64 pn, + salt_t salt) +{ + struct gcm_iv_xpn *gcm_iv = (struct gcm_iv_xpn *)iv; + + gcm_iv->ssci = ssci ^ salt.ssci; + gcm_iv->pn = cpu_to_be64(pn) ^ salt.pn; +} + static void macsec_fill_iv(unsigned char *iv, sci_t sci, u32 pn) { struct gcm_iv *gcm_iv = (struct gcm_iv *)iv; @@ -442,14 +462,19 @@ void macsec_pn_wrapped(struct macsec_secy *secy, struct macsec_tx_sa *tx_sa) } EXPORT_SYMBOL_GPL(macsec_pn_wrapped); -static u32 tx_sa_update_pn(struct macsec_tx_sa *tx_sa, struct macsec_secy *secy) +static pn_t tx_sa_update_pn(struct macsec_tx_sa *tx_sa, + struct macsec_secy *secy) { - u32 pn; + pn_t pn; spin_lock_bh(&tx_sa->lock); - pn = tx_sa->next_pn; - tx_sa->next_pn++; + pn = tx_sa->next_pn_halves; + if (secy->xpn) + tx_sa->next_pn++; + else + tx_sa->next_pn_halves.lower++; + if (tx_sa->next_pn == 0) __macsec_pn_wrapped(secy, tx_sa); spin_unlock_bh(&tx_sa->lock); @@ -564,7 +589,7 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, struct macsec_tx_sa *tx_sa; struct macsec_dev *macsec = macsec_priv(dev); bool sci_present; - u32 pn; + pn_t pn; secy = &macsec->secy; tx_sc = &secy->tx_sc; @@ -606,12 +631,12 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, memmove(hh, eth, 2 * ETH_ALEN); pn = tx_sa_update_pn(tx_sa, secy); - if (pn == 0) { + if (pn.full64 == 0) { macsec_txsa_put(tx_sa); kfree_skb(skb); return ERR_PTR(-ENOLINK); } - macsec_fill_sectag(hh, secy, pn, sci_present); + macsec_fill_sectag(hh, secy, pn.lower, sci_present); macsec_set_shortlen(hh, unprotected_len - 2 * ETH_ALEN); skb_put(skb, secy->icv_len); @@ -642,7 +667,10 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb, return ERR_PTR(-ENOMEM); } - macsec_fill_iv(iv, secy->sci, pn); + if (secy->xpn) + macsec_fill_iv_xpn(iv, tx_sa->ssci, pn.full64, tx_sa->key.salt); + else + macsec_fill_iv(iv, secy->sci, pn.lower); sg_init_table(sg, ret); ret = skb_to_sgvec(skb, sg, 0, skb->len); @@ -694,13 +722,14 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u32 lowest_pn = 0; spin_lock(&rx_sa->lock); - if (rx_sa->next_pn >= secy->replay_window) - lowest_pn = rx_sa->next_pn - secy->replay_window; + if (rx_sa->next_pn_halves.lower >= secy->replay_window) + lowest_pn = rx_sa->next_pn_halves.lower - secy->replay_window; /* Now perform replay protection check again * (see IEEE 802.1AE-2006 figure 10-5) */ - if (secy->replay_protect && pn < lowest_pn) { + if (secy->replay_protect && pn < lowest_pn && + (!secy->xpn || pn_same_half(pn, lowest_pn))) { spin_unlock(&rx_sa->lock); u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; @@ -749,8 +778,15 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u } u64_stats_update_end(&rxsc_stats->syncp); - if (pn >= rx_sa->next_pn) - rx_sa->next_pn = pn + 1; + // Instead of "pn >=" - to support pn overflow in xpn + if (pn + 1 > rx_sa->next_pn_halves.lower) { + rx_sa->next_pn_halves.lower = pn + 1; + } else if (secy->xpn && + !pn_same_half(pn, rx_sa->next_pn_halves.lower)) { + rx_sa->next_pn_halves.upper++; + rx_sa->next_pn_halves.lower = pn + 1; + } + spin_unlock(&rx_sa->lock); } @@ -837,6 +873,7 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, unsigned char *iv; struct aead_request *req; struct macsec_eth_header *hdr; + u32 hdr_pn; u16 icv_len = secy->icv_len; macsec_skb_cb(skb)->valid = false; @@ -856,7 +893,21 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb, } hdr = (struct macsec_eth_header *)skb->data; - macsec_fill_iv(iv, sci, ntohl(hdr->packet_number)); + hdr_pn = ntohl(hdr->packet_number); + + if (secy->xpn) { + pn_t recovered_pn = rx_sa->next_pn_halves; + + recovered_pn.lower = hdr_pn; + if (hdr_pn < rx_sa->next_pn_halves.lower && + !pn_same_half(hdr_pn, rx_sa->next_pn_halves.lower)) + recovered_pn.upper++; + + macsec_fill_iv_xpn(iv, rx_sa->ssci, recovered_pn.full64, + rx_sa->key.salt); + } else { + macsec_fill_iv(iv, sci, hdr_pn); + } sg_init_table(sg, ret); ret = skb_to_sgvec(skb, sg, 0, skb->len); @@ -998,7 +1049,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) struct macsec_dev *macsec; unsigned int len; sci_t sci; - u32 pn; + u32 hdr_pn; bool cbit; struct pcpu_rx_sc_stats *rxsc_stats; struct pcpu_secy_stats *secy_stats; @@ -1069,7 +1120,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) secy_stats = this_cpu_ptr(macsec->stats); rxsc_stats = this_cpu_ptr(rx_sc->stats); - if (!macsec_validate_skb(skb, secy->icv_len)) { + if (!macsec_validate_skb(skb, secy->icv_len, secy->xpn)) { u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsBadTag++; u64_stats_update_end(&secy_stats->syncp); @@ -1101,13 +1152,16 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) } /* First, PN check to avoid decrypting obviously wrong packets */ - pn = ntohl(hdr->packet_number); + hdr_pn = ntohl(hdr->packet_number); if (secy->replay_protect) { bool late; spin_lock(&rx_sa->lock); - late = rx_sa->next_pn >= secy->replay_window && - pn < (rx_sa->next_pn - secy->replay_window); + late = rx_sa->next_pn_halves.lower >= secy->replay_window && + hdr_pn < (rx_sa->next_pn_halves.lower - secy->replay_window); + + if (secy->xpn) + late = late && pn_same_half(rx_sa->next_pn_halves.lower, hdr_pn); spin_unlock(&rx_sa->lock); if (late) { @@ -1136,7 +1190,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) return RX_HANDLER_CONSUMED; } - if (!macsec_post_decrypt(skb, secy, pn)) + if (!macsec_post_decrypt(skb, secy, hdr_pn)) goto drop; deliver: @@ -1665,7 +1719,7 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&rx_sa->lock); - rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + rx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&rx_sa->lock); } @@ -1872,7 +1926,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) } spin_lock_bh(&tx_sa->lock); - tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + tx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&tx_sa->lock); if (tb_sa[MACSEC_SA_ATTR_ACTIVE]) @@ -2136,9 +2190,11 @@ static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info) u8 assoc_num; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; bool was_operational, was_active; - u32 prev_pn = 0; + pn_t prev_pn; int ret = 0; + prev_pn.full64 = 0; + if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -2158,8 +2214,8 @@ static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&tx_sa->lock); - prev_pn = tx_sa->next_pn; - tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + prev_pn = tx_sa->next_pn_halves; + tx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&tx_sa->lock); } @@ -2197,7 +2253,7 @@ static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info) cleanup: if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&tx_sa->lock); - tx_sa->next_pn = prev_pn; + tx_sa->next_pn_halves = prev_pn; spin_unlock_bh(&tx_sa->lock); } tx_sa->active = was_active; @@ -2217,9 +2273,11 @@ static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; bool was_active; - u32 prev_pn = 0; + pn_t prev_pn; int ret = 0; + prev_pn.full64 = 0; + if (!attrs[MACSEC_ATTR_IFINDEX]) return -EINVAL; @@ -2242,8 +2300,8 @@ static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&rx_sa->lock); - prev_pn = rx_sa->next_pn; - rx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + prev_pn = rx_sa->next_pn_halves; + rx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&rx_sa->lock); } @@ -2276,7 +2334,7 @@ static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) cleanup: if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&rx_sa->lock); - rx_sa->next_pn = prev_pn; + rx_sa->next_pn_halves = prev_pn; spin_unlock_bh(&rx_sa->lock); } rx_sa->active = was_active; @@ -2795,7 +2853,7 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, } if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) || - nla_put_u32(skb, MACSEC_SA_ATTR_PN, tx_sa->next_pn) || + nla_put_u32(skb, MACSEC_SA_ATTR_PN, tx_sa->next_pn_halves.lower) || nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, tx_sa->key.id) || nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, tx_sa->active)) { nla_nest_cancel(skb, txsa_nest); @@ -2899,7 +2957,7 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, nla_nest_end(skb, attr); if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) || - nla_put_u32(skb, MACSEC_SA_ATTR_PN, rx_sa->next_pn) || + nla_put_u32(skb, MACSEC_SA_ATTR_PN, rx_sa->next_pn_halves.lower) || nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, rx_sa->key.id) || nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, rx_sa->active)) { nla_nest_cancel(skb, rxsa_nest); diff --git a/include/net/macsec.h b/include/net/macsec.h index 92e43db8b566..43cd54e17877 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -11,18 +11,45 @@ #include #include -typedef u64 __bitwise sci_t; - +#define MACSEC_SALT_LEN 12 #define MACSEC_NUM_AN 4 /* 2 bits for the association number */ +typedef u64 __bitwise sci_t; +typedef u32 __bitwise ssci_t; + +typedef union salt { + struct { + u32 ssci; + u64 pn; + } __packed; + u8 bytes[MACSEC_SALT_LEN]; +} __packed salt_t; + +typedef union pn { + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + u32 lower; + u32 upper; +#elif defined(__BIG_ENDIAN_BITFIELD) + u32 upper; + u32 lower; +#else +#error "Please fix " +#endif + }; + u64 full64; +} pn_t; + /** * struct macsec_key - SA key * @id: user-provided key identifier * @tfm: crypto struct, key storage + * @salt: salt used to generate IV in XPN cipher suites */ struct macsec_key { u8 id[MACSEC_KEYID_LEN]; struct crypto_aead *tfm; + salt_t salt; }; struct macsec_rx_sc_stats { @@ -64,12 +91,17 @@ struct macsec_tx_sc_stats { * @next_pn: packet number expected for the next packet * @lock: protects next_pn manipulations * @key: key structure + * @ssci: short secure channel identifier * @stats: per-SA stats */ struct macsec_rx_sa { struct macsec_key key; + ssci_t ssci; spinlock_t lock; - u32 next_pn; + union { + pn_t next_pn_halves; + u64 next_pn; + }; refcount_t refcnt; bool active; struct macsec_rx_sa_stats __percpu *stats; @@ -110,12 +142,17 @@ struct macsec_rx_sc { * @next_pn: packet number to use for the next packet * @lock: protects next_pn manipulations * @key: key structure + * @ssci: short secure channel identifier * @stats: per-SA stats */ struct macsec_tx_sa { struct macsec_key key; + ssci_t ssci; spinlock_t lock; - u32 next_pn; + union { + pn_t next_pn_halves; + u64 next_pn; + }; refcount_t refcnt; bool active; struct macsec_tx_sa_stats __percpu *stats; @@ -152,6 +189,7 @@ struct macsec_tx_sc { * @key_len: length of keys used by the cipher suite * @icv_len: length of ICV used by the cipher suite * @validate_frames: validation mode + * @xpn: enable XPN for this SecY * @operational: MAC_Operational flag * @protect_frames: enable protection for this SecY * @replay_protect: enable packet number checks on receive @@ -166,6 +204,7 @@ struct macsec_secy { u16 key_len; u16 icv_len; enum macsec_validation_type validate_frames; + bool xpn; bool operational; bool protect_frames; bool replay_protect; From d02cb3a2fb3b0b61fd52bab794eb0fb2d53e87c3 Mon Sep 17 00:00:00 2001 From: Era Mayflower Date: Mon, 9 Mar 2020 19:47:02 +0000 Subject: [PATCH 45/71] macsec: Netlink support of XPN cipher suites (IEEE 802.1AEbw) Netlink support of extended packet number cipher suites, allows adding and updating XPN macsec interfaces. Added support in: * Creating interfaces with GCM-AES-XPN-128 and GCM-AES-XPN-256 suites. * Setting and getting 64bit packet numbers with of SAs. * Setting (only on SA creation) and getting ssci of SAs. * Setting salt when installing a SAK. Added 2 cipher suite identifiers according to 802.1AE-2018 table 14-1: * MACSEC_CIPHER_ID_GCM_AES_XPN_128 * MACSEC_CIPHER_ID_GCM_AES_XPN_256 In addition, added 2 new netlink attribute types: * MACSEC_SA_ATTR_SSCI * MACSEC_SA_ATTR_SALT Depends on: macsec: Support XPN frame handling - IEEE 802.1AEbw. Change-Id: Ia7efe40e1c9f7434a025faec075020717220327d Signed-off-by: Era Mayflower Signed-off-by: David S. Miller Git-commit: 48ef50fa866aae087f63c7de8a47e76537f88691 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 161 ++++++++++++++++++++++++++++++--- include/net/macsec.h | 3 + include/uapi/linux/if_macsec.h | 8 +- 3 files changed, 157 insertions(+), 15 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 5e32b30772e4..a42c4db27dfe 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -241,11 +241,13 @@ static struct macsec_cb *macsec_skb_cb(struct sk_buff *skb) #define MACSEC_PORT_ES (htons(0x0001)) #define MACSEC_PORT_SCB (0x0000) #define MACSEC_UNDEF_SCI ((__force sci_t)0xffffffffffffffffULL) +#define MACSEC_UNDEF_SSCI ((__force ssci_t)0xffffffff) #define MACSEC_GCM_AES_128_SAK_LEN 16 #define MACSEC_GCM_AES_256_SAK_LEN 32 #define DEFAULT_SAK_LEN MACSEC_GCM_AES_128_SAK_LEN +#define DEFAULT_XPN false #define DEFAULT_SEND_SCI true #define DEFAULT_ENCRYPT false #define DEFAULT_ENCODING_SA 0 @@ -1310,6 +1312,7 @@ static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len, return PTR_ERR(rx_sa->key.tfm); } + rx_sa->ssci = MACSEC_UNDEF_SSCI; rx_sa->active = false; rx_sa->next_pn = 1; refcount_set(&rx_sa->refcnt, 1); @@ -1408,6 +1411,7 @@ static int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len, return PTR_ERR(tx_sa->key.tfm); } + tx_sa->ssci = MACSEC_UNDEF_SSCI; tx_sa->active = false; refcount_set(&tx_sa->refcnt, 1); spin_lock_init(&tx_sa->lock); @@ -1451,6 +1455,16 @@ static int nla_put_sci(struct sk_buff *skb, int attrtype, sci_t value, return nla_put_u64_64bit(skb, attrtype, (__force u64)value, padattr); } +static ssci_t nla_get_ssci(const struct nlattr *nla) +{ + return (__force ssci_t)nla_get_u32(nla); +} + +static int nla_put_ssci(struct sk_buff *skb, int attrtype, ssci_t value) +{ + return nla_put_u32(skb, attrtype, (__force u64)value); +} + static struct macsec_tx_sa *get_txsa_from_nl(struct net *net, struct nlattr **attrs, struct nlattr **tb_sa, @@ -1566,11 +1580,14 @@ static const struct nla_policy macsec_genl_rxsc_policy[NUM_MACSEC_RXSC_ATTR] = { static const struct nla_policy macsec_genl_sa_policy[NUM_MACSEC_SA_ATTR] = { [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 }, [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 }, - [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 }, + [MACSEC_SA_ATTR_PN] = { .type = NLA_MIN_LEN, .len = 4 }, [MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY, .len = MACSEC_KEYID_LEN, }, [MACSEC_SA_ATTR_KEY] = { .type = NLA_BINARY, .len = MACSEC_MAX_KEY_LEN, }, + [MACSEC_SA_ATTR_SSCI] = { .type = NLA_U32 }, + [MACSEC_SA_ATTR_SALT] = { .type = NLA_BINARY, + .len = MACSEC_SALT_LEN, }, }; static const struct nla_policy macsec_genl_offload_policy[NUM_MACSEC_OFFLOAD_ATTR] = { @@ -1643,7 +1660,8 @@ static bool validate_add_rxsa(struct nlattr **attrs) if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN) return false; - if (attrs[MACSEC_SA_ATTR_PN] && nla_get_u32(attrs[MACSEC_SA_ATTR_PN]) == 0) + if (attrs[MACSEC_SA_ATTR_PN] && + *(u64 *)nla_data(attrs[MACSEC_SA_ATTR_PN]) == 0) return false; if (attrs[MACSEC_SA_ATTR_ACTIVE]) { @@ -1665,6 +1683,7 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) struct macsec_rx_sc *rx_sc; struct macsec_rx_sa *rx_sa; unsigned char assoc_num; + int pn_len; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; int err; @@ -1697,6 +1716,29 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN; + if (nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) { + pr_notice("macsec: nl: add_rxsa: bad pn length: %d != %d\n", + nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len); + rtnl_unlock(); + return -EINVAL; + } + + if (secy->xpn) { + if (!tb_sa[MACSEC_SA_ATTR_SSCI] || !tb_sa[MACSEC_SA_ATTR_SALT]) { + rtnl_unlock(); + return -EINVAL; + } + + if (nla_len(tb_sa[MACSEC_SA_ATTR_SALT]) != MACSEC_SALT_LEN) { + pr_notice("macsec: nl: add_rxsa: bad salt length: %d != %d\n", + nla_len(tb_sa[MACSEC_SA_ATTR_SALT]), + MACSEC_SA_ATTR_SALT); + rtnl_unlock(); + return -EINVAL; + } + } + rx_sa = rtnl_dereference(rx_sc->sa[assoc_num]); if (rx_sa) { rtnl_unlock(); @@ -1719,7 +1761,7 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_PN]) { spin_lock_bh(&rx_sa->lock); - rx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + rx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&rx_sa->lock); } @@ -1749,6 +1791,12 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) goto cleanup; } + if (secy->xpn) { + rx_sa->ssci = nla_get_ssci(tb_sa[MACSEC_SA_ATTR_SSCI]); + nla_memcpy(rx_sa->key.salt.bytes, tb_sa[MACSEC_SA_ATTR_SALT], + MACSEC_SALT_LEN); + } + nla_memcpy(rx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); rcu_assign_pointer(rx_sc->sa[assoc_num], rx_sa); @@ -1873,6 +1921,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) struct macsec_tx_sc *tx_sc; struct macsec_tx_sa *tx_sa; unsigned char assoc_num; + int pn_len; struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; bool was_operational; int err; @@ -1905,6 +1954,29 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN; + if (nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) { + pr_notice("macsec: nl: add_txsa: bad pn length: %d != %d\n", + nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len); + rtnl_unlock(); + return -EINVAL; + } + + if (secy->xpn) { + if (!tb_sa[MACSEC_SA_ATTR_SSCI] || !tb_sa[MACSEC_SA_ATTR_SALT]) { + rtnl_unlock(); + return -EINVAL; + } + + if (nla_len(tb_sa[MACSEC_SA_ATTR_SALT]) != MACSEC_SALT_LEN) { + pr_notice("macsec: nl: add_txsa: bad salt length: %d != %d\n", + nla_len(tb_sa[MACSEC_SA_ATTR_SALT]), + MACSEC_SA_ATTR_SALT); + rtnl_unlock(); + return -EINVAL; + } + } + tx_sa = rtnl_dereference(tx_sc->sa[assoc_num]); if (tx_sa) { rtnl_unlock(); @@ -1926,7 +1998,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) } spin_lock_bh(&tx_sa->lock); - tx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + tx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&tx_sa->lock); if (tb_sa[MACSEC_SA_ATTR_ACTIVE]) @@ -1957,6 +2029,12 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) goto cleanup; } + if (secy->xpn) { + tx_sa->ssci = nla_get_ssci(tb_sa[MACSEC_SA_ATTR_SSCI]); + nla_memcpy(tx_sa->key.salt.bytes, tb_sa[MACSEC_SA_ATTR_SALT], + MACSEC_SALT_LEN); + } + nla_memcpy(tx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); rcu_assign_pointer(tx_sc->sa[assoc_num], tx_sa); @@ -2163,7 +2241,9 @@ static bool validate_upd_sa(struct nlattr **attrs) { if (!attrs[MACSEC_SA_ATTR_AN] || attrs[MACSEC_SA_ATTR_KEY] || - attrs[MACSEC_SA_ATTR_KEYID]) + attrs[MACSEC_SA_ATTR_KEYID] || + attrs[MACSEC_SA_ATTR_SSCI] || + attrs[MACSEC_SA_ATTR_SALT]) return false; if (nla_get_u8(attrs[MACSEC_SA_ATTR_AN]) >= MACSEC_NUM_AN) @@ -2213,9 +2293,19 @@ static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info) } if (tb_sa[MACSEC_SA_ATTR_PN]) { + int pn_len; + + pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN; + if (nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) { + pr_notice("macsec: nl: upd_txsa: bad pn length: %d != %d\n", + nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len); + rtnl_unlock(); + return -EINVAL; + } + spin_lock_bh(&tx_sa->lock); prev_pn = tx_sa->next_pn_halves; - tx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + tx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&tx_sa->lock); } @@ -2299,9 +2389,19 @@ static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) } if (tb_sa[MACSEC_SA_ATTR_PN]) { + int pn_len; + + pn_len = secy->xpn ? MACSEC_XPN_PN_LEN : MACSEC_DEFAULT_PN_LEN; + if (nla_len(tb_sa[MACSEC_SA_ATTR_PN]) != pn_len) { + pr_notice("macsec: nl: upd_rxsa: bad pn length: %d != %d\n", + nla_len(tb_sa[MACSEC_SA_ATTR_PN]), pn_len); + rtnl_unlock(); + return -EINVAL; + } + spin_lock_bh(&rx_sa->lock); prev_pn = rx_sa->next_pn_halves; - rx_sa->next_pn_halves.lower = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + rx_sa->next_pn = nla_get_u64(tb_sa[MACSEC_SA_ATTR_PN]); spin_unlock_bh(&rx_sa->lock); } @@ -2748,10 +2848,10 @@ static int nla_put_secy(struct macsec_secy *secy, struct sk_buff *skb) switch (secy->key_len) { case MACSEC_GCM_AES_128_SAK_LEN: - csid = MACSEC_DEFAULT_CIPHER_ID; + csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_128 : MACSEC_DEFAULT_CIPHER_ID; break; case MACSEC_GCM_AES_256_SAK_LEN: - csid = MACSEC_CIPHER_ID_GCM_AES_256; + csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_256 : MACSEC_CIPHER_ID_GCM_AES_256; break; default: goto cancel; @@ -2842,6 +2942,8 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, for (i = 0, j = 1; i < MACSEC_NUM_AN; i++) { struct macsec_tx_sa *tx_sa = rtnl_dereference(tx_sc->sa[i]); struct nlattr *txsa_nest; + u64 pn; + int pn_len; if (!tx_sa) continue; @@ -2852,9 +2954,18 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, goto nla_put_failure; } + if (secy->xpn) { + pn = tx_sa->next_pn; + pn_len = MACSEC_XPN_PN_LEN; + } else { + pn = tx_sa->next_pn_halves.lower; + pn_len = MACSEC_DEFAULT_PN_LEN; + } + if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) || - nla_put_u32(skb, MACSEC_SA_ATTR_PN, tx_sa->next_pn_halves.lower) || + nla_put(skb, MACSEC_SA_ATTR_PN, pn_len, &pn) || nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, tx_sa->key.id) || + (secy->xpn && nla_put_ssci(skb, MACSEC_SA_ATTR_SSCI, tx_sa->ssci)) || nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, tx_sa->active)) { nla_nest_cancel(skb, txsa_nest); nla_nest_cancel(skb, txsa_list); @@ -2927,6 +3038,8 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, for (i = 0, k = 1; i < MACSEC_NUM_AN; i++) { struct macsec_rx_sa *rx_sa = rtnl_dereference(rx_sc->sa[i]); struct nlattr *rxsa_nest; + u64 pn; + int pn_len; if (!rx_sa) continue; @@ -2956,9 +3069,18 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, } nla_nest_end(skb, attr); + if (secy->xpn) { + pn = rx_sa->next_pn; + pn_len = MACSEC_XPN_PN_LEN; + } else { + pn = rx_sa->next_pn_halves.lower; + pn_len = MACSEC_DEFAULT_PN_LEN; + } + if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) || - nla_put_u32(skb, MACSEC_SA_ATTR_PN, rx_sa->next_pn_halves.lower) || + nla_put(skb, MACSEC_SA_ATTR_PN, pn_len, &pn) || nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, rx_sa->key.id) || + (secy->xpn && nla_put_ssci(skb, MACSEC_SA_ATTR_SSCI, rx_sa->ssci)) || nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, rx_sa->active)) { nla_nest_cancel(skb, rxsa_nest); nla_nest_cancel(skb, rxsc_nest); @@ -3494,9 +3616,19 @@ static int macsec_changelink_common(struct net_device *dev, case MACSEC_CIPHER_ID_GCM_AES_128: case MACSEC_DEFAULT_CIPHER_ID: secy->key_len = MACSEC_GCM_AES_128_SAK_LEN; + secy->xpn = false; break; case MACSEC_CIPHER_ID_GCM_AES_256: secy->key_len = MACSEC_GCM_AES_256_SAK_LEN; + secy->xpn = false; + break; + case MACSEC_CIPHER_ID_GCM_AES_XPN_128: + secy->key_len = MACSEC_GCM_AES_128_SAK_LEN; + secy->xpn = true; + break; + case MACSEC_CIPHER_ID_GCM_AES_XPN_256: + secy->key_len = MACSEC_GCM_AES_256_SAK_LEN; + secy->xpn = true; break; default: return -EINVAL; @@ -3686,6 +3818,7 @@ static int macsec_add_dev(struct net_device *dev, sci_t sci, u8 icv_len) secy->validate_frames = MACSEC_VALIDATE_DEFAULT; secy->protect_frames = true; secy->replay_protect = false; + secy->xpn = DEFAULT_XPN; secy->sci = sci; secy->tx_sc.active = true; @@ -3821,6 +3954,8 @@ static int macsec_validate_attr(struct nlattr *tb[], struct nlattr *data[], switch (csid) { case MACSEC_CIPHER_ID_GCM_AES_128: case MACSEC_CIPHER_ID_GCM_AES_256: + case MACSEC_CIPHER_ID_GCM_AES_XPN_128: + case MACSEC_CIPHER_ID_GCM_AES_XPN_256: case MACSEC_DEFAULT_CIPHER_ID: if (icv_len < MACSEC_MIN_ICV_LEN || icv_len > MACSEC_STD_ICV_LEN) @@ -3894,10 +4029,10 @@ static int macsec_fill_info(struct sk_buff *skb, switch (secy->key_len) { case MACSEC_GCM_AES_128_SAK_LEN: - csid = MACSEC_DEFAULT_CIPHER_ID; + csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_128 : MACSEC_DEFAULT_CIPHER_ID; break; case MACSEC_GCM_AES_256_SAK_LEN: - csid = MACSEC_CIPHER_ID_GCM_AES_256; + csid = secy->xpn ? MACSEC_CIPHER_ID_GCM_AES_XPN_256 : MACSEC_CIPHER_ID_GCM_AES_256; break; default: goto nla_put_failure; diff --git a/include/net/macsec.h b/include/net/macsec.h index 43cd54e17877..2e4780dbf5c6 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -11,6 +11,9 @@ #include #include +#define MACSEC_DEFAULT_PN_LEN 4 +#define MACSEC_XPN_PN_LEN 8 + #define MACSEC_SALT_LEN 12 #define MACSEC_NUM_AN 4 /* 2 bits for the association number */ diff --git a/include/uapi/linux/if_macsec.h b/include/uapi/linux/if_macsec.h index 1d63c43c38cc..3af2aa069a36 100644 --- a/include/uapi/linux/if_macsec.h +++ b/include/uapi/linux/if_macsec.h @@ -22,9 +22,11 @@ #define MACSEC_KEYID_LEN 16 -/* cipher IDs as per IEEE802.1AEbn-2011 */ +/* cipher IDs as per IEEE802.1AE-2018 (Table 14-1) */ #define MACSEC_CIPHER_ID_GCM_AES_128 0x0080C20001000001ULL #define MACSEC_CIPHER_ID_GCM_AES_256 0x0080C20001000002ULL +#define MACSEC_CIPHER_ID_GCM_AES_XPN_128 0x0080C20001000003ULL +#define MACSEC_CIPHER_ID_GCM_AES_XPN_256 0x0080C20001000004ULL /* deprecated cipher ID for GCM-AES-128 */ #define MACSEC_DEFAULT_CIPHER_ID 0x0080020001000001ULL @@ -88,11 +90,13 @@ enum macsec_sa_attrs { MACSEC_SA_ATTR_UNSPEC, MACSEC_SA_ATTR_AN, /* config/dump, u8 0..3 */ MACSEC_SA_ATTR_ACTIVE, /* config/dump, u8 0..1 */ - MACSEC_SA_ATTR_PN, /* config/dump, u32 */ + MACSEC_SA_ATTR_PN, /* config/dump, u32/u64 (u64 if XPN) */ MACSEC_SA_ATTR_KEY, /* config, data */ MACSEC_SA_ATTR_KEYID, /* config/dump, 128-bit */ MACSEC_SA_ATTR_STATS, /* dump, nested, macsec_sa_stats_attr */ MACSEC_SA_ATTR_PAD, + MACSEC_SA_ATTR_SSCI, /* config/dump, u32 - XPN only */ + MACSEC_SA_ATTR_SALT, /* config, 96-bit - XPN only */ __MACSEC_SA_ATTR_END, NUM_MACSEC_SA_ATTR = __MACSEC_SA_ATTR_END, MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1, From ac0f651428854401912c1e59a537f87b99278b3d Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Wed, 25 Mar 2020 15:52:34 +0300 Subject: [PATCH 46/71] net: macsec: init secy pointer in macsec_context This patch adds secy pointer initialization in the macsec_context. It will be used by MAC drivers in offloading operations. Change-Id: I59c0d751f2b026096dcbe57355b47d7f940b0a94 Signed-off-by: Dmitry Bogdanov Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: 182879f89b858fede98136ea3ad45fe9c7178387 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index a42c4db27dfe..88f7fa239288 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1783,6 +1783,7 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) ctx.sa.assoc_num = assoc_num; ctx.sa.rx_sa = rx_sa; + ctx.secy = secy; memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), MACSEC_KEYID_LEN); @@ -1830,6 +1831,7 @@ static int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info) struct nlattr **attrs = info->attrs; struct macsec_rx_sc *rx_sc; struct nlattr *tb_rxsc[MACSEC_RXSC_ATTR_MAX + 1]; + struct macsec_secy *secy; bool was_active; int ret; @@ -1849,6 +1851,7 @@ static int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info) return PTR_ERR(dev); } + secy = &macsec_priv(dev)->secy; sci = nla_get_sci(tb_rxsc[MACSEC_RXSC_ATTR_SCI]); rx_sc = create_rx_sc(dev, sci); @@ -1872,6 +1875,7 @@ static int macsec_add_rxsc(struct sk_buff *skb, struct genl_info *info) } ctx.rx_sc = rx_sc; + ctx.secy = secy; ret = macsec_offload(ops->mdo_add_rxsc, &ctx); if (ret) @@ -2021,6 +2025,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) ctx.sa.assoc_num = assoc_num; ctx.sa.tx_sa = tx_sa; + ctx.secy = secy; memcpy(ctx.sa.key, nla_data(tb_sa[MACSEC_SA_ATTR_KEY]), MACSEC_KEYID_LEN); @@ -2096,6 +2101,7 @@ static int macsec_del_rxsa(struct sk_buff *skb, struct genl_info *info) ctx.sa.assoc_num = assoc_num; ctx.sa.rx_sa = rx_sa; + ctx.secy = secy; ret = macsec_offload(ops->mdo_del_rxsa, &ctx); if (ret) @@ -2161,6 +2167,7 @@ static int macsec_del_rxsc(struct sk_buff *skb, struct genl_info *info) } ctx.rx_sc = rx_sc; + ctx.secy = secy; ret = macsec_offload(ops->mdo_del_rxsc, &ctx); if (ret) goto cleanup; @@ -2219,6 +2226,7 @@ static int macsec_del_txsa(struct sk_buff *skb, struct genl_info *info) ctx.sa.assoc_num = assoc_num; ctx.sa.tx_sa = tx_sa; + ctx.secy = secy; ret = macsec_offload(ops->mdo_del_txsa, &ctx); if (ret) @@ -2330,6 +2338,7 @@ static int macsec_upd_txsa(struct sk_buff *skb, struct genl_info *info) ctx.sa.assoc_num = assoc_num; ctx.sa.tx_sa = tx_sa; + ctx.secy = secy; ret = macsec_offload(ops->mdo_upd_txsa, &ctx); if (ret) @@ -2422,6 +2431,7 @@ static int macsec_upd_rxsa(struct sk_buff *skb, struct genl_info *info) ctx.sa.assoc_num = assoc_num; ctx.sa.rx_sa = rx_sa; + ctx.secy = secy; ret = macsec_offload(ops->mdo_upd_rxsa, &ctx); if (ret) @@ -2492,6 +2502,7 @@ static int macsec_upd_rxsc(struct sk_buff *skb, struct genl_info *info) } ctx.rx_sc = rx_sc; + ctx.secy = secy; ret = macsec_offload(ops->mdo_upd_rxsc, &ctx); if (ret) @@ -3359,6 +3370,7 @@ static int macsec_dev_open(struct net_device *dev) goto clear_allmulti; } + ctx.secy = &macsec->secy; err = macsec_offload(ops->mdo_dev_open, &ctx); if (err) goto clear_allmulti; @@ -3390,8 +3402,10 @@ static int macsec_dev_stop(struct net_device *dev) struct macsec_context ctx; ops = macsec_get_ops(macsec, &ctx); - if (ops) + if (ops) { + ctx.secy = &macsec->secy; macsec_offload(ops->mdo_dev_stop, &ctx); + } } dev_mc_unsync(real_dev, dev); From 93e3092501671a437e1b750dcaed23b9c9941a21 Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Wed, 25 Mar 2020 15:52:35 +0300 Subject: [PATCH 47/71] net: macsec: allow multiple macsec devices with offload Offload engine can setup several SecY. Each macsec interface shall have its own mac address. It will filter a traffic by dest mac address. Change-Id: Ic0e971c8061eb7ce74e1ddab4b76374571da5a8c Signed-off-by: Dmitry Bogdanov Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: A249f8050624f92f844605274de3367e2c8ac706 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 88f7fa239288..27cc355cd78a 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -2542,11 +2542,10 @@ static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info) enum macsec_offload offload, prev_offload; int (*func)(struct macsec_context *ctx); struct nlattr **attrs = info->attrs; - struct net_device *dev, *loop_dev; + struct net_device *dev; const struct macsec_ops *ops; struct macsec_context ctx; struct macsec_dev *macsec; - struct net *loop_net; int ret; if (!attrs[MACSEC_ATTR_IFINDEX]) @@ -2574,28 +2573,6 @@ static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info) !macsec_check_offload(offload, macsec)) return -EOPNOTSUPP; - if (offload == MACSEC_OFFLOAD_OFF) - goto skip_limitation; - - /* Check the physical interface isn't offloading another interface - * first. - */ - for_each_net(loop_net) { - for_each_netdev(loop_net, loop_dev) { - struct macsec_dev *priv; - - if (!netif_is_macsec(loop_dev)) - continue; - - priv = macsec_priv(loop_dev); - - if (priv->real_dev == macsec->real_dev && - priv->offload != MACSEC_OFFLOAD_OFF) - return -EBUSY; - } - } - -skip_limitation: /* Check if the net device is busy. */ if (netif_running(dev)) return -EBUSY; From 811cdefef13ef5a76640d2c4410d6a3dc15a287d Mon Sep 17 00:00:00 2001 From: Mark Starovoytov Date: Wed, 25 Mar 2020 15:52:36 +0300 Subject: [PATCH 48/71] net: macsec: support multicast/broadcast when offloading The idea is simple. If the frame is an exact match for the controlled port (based on DA comparison), then we simply divert this skb to matching port. Multicast/broadcast messages are delivered to all ports. Change-Id: Iaa716e14c1a178a99b51b1104e7c68eb2f8e592e Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: f428011b90ec0de7429886f753b7c3293392761c Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 27cc355cd78a..6e02dbf472e1 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -992,22 +992,53 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) { /* Deliver to the uncontrolled port by default */ enum rx_handler_result ret = RX_HANDLER_PASS; + struct ethhdr *hdr = eth_hdr(skb); struct macsec_rxh_data *rxd; struct macsec_dev *macsec; rcu_read_lock(); rxd = macsec_data_rcu(skb->dev); - /* 10.6 If the management control validateFrames is not - * Strict, frames without a SecTAG are received, counted, and - * delivered to the Controlled Port - */ list_for_each_entry_rcu(macsec, &rxd->secys, secys) { struct sk_buff *nskb; struct pcpu_secy_stats *secy_stats = this_cpu_ptr(macsec->stats); + struct net_device *ndev = macsec->secy.netdev; - if (!macsec_is_offloaded(macsec) && - macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) { + /* If h/w offloading is enabled, HW decodes frames and strips + * the SecTAG, so we have to deduce which port to deliver to. + */ + if (macsec_is_offloaded(macsec) && netif_running(ndev)) { + if (ether_addr_equal_64bits(hdr->h_dest, + ndev->dev_addr)) { + /* exact match, divert skb to this port */ + skb->dev = ndev; + skb->pkt_type = PACKET_HOST; + ret = RX_HANDLER_ANOTHER; + goto out; + } else if (is_multicast_ether_addr_64bits( + hdr->h_dest)) { + /* multicast frame, deliver on this port too */ + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + break; + + nskb->dev = ndev; + if (ether_addr_equal_64bits(hdr->h_dest, + ndev->broadcast)) + nskb->pkt_type = PACKET_BROADCAST; + else + nskb->pkt_type = PACKET_MULTICAST; + + netif_rx(nskb); + } + continue; + } + + /* 10.6 If the management control validateFrames is not + * Strict, frames without a SecTAG are received, counted, and + * delivered to the Controlled Port + */ + if (macsec->secy.validate_frames == MACSEC_VALIDATE_STRICT) { u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoTag++; u64_stats_update_end(&secy_stats->syncp); @@ -1019,19 +1050,13 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) if (!nskb) break; - nskb->dev = macsec->secy.netdev; + nskb->dev = ndev; if (netif_rx(nskb) == NET_RX_SUCCESS) { u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsUntagged++; u64_stats_update_end(&secy_stats->syncp); } - - if (netif_running(macsec->secy.netdev) && - macsec_is_offloaded(macsec)) { - ret = RX_HANDLER_EXACT; - goto out; - } } out: From f8451bfe630f828471d5c32fc0b87e9e75a36546 Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Wed, 25 Mar 2020 15:52:37 +0300 Subject: [PATCH 49/71] net: macsec: add support for getting offloaded stats When HW offloading is enabled, offloaded stats should be used, because s/w stats are wrong and out of sync with the HW in this case. Change-Id: I83ed6f427ae03d6624602e8763a8799f03cd5246 Signed-off-by: Dmitry Bogdanov Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: b62c3624500a7e1cc081e75973299c1f7901a438 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 325 ++++++++++++++++++++++++++++--------------- include/net/macsec.h | 24 ++++ 2 files changed, 239 insertions(+), 110 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 6e02dbf472e1..75801f526b4c 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -88,17 +88,6 @@ struct gcm_iv { __be32 pn; }; -struct macsec_dev_stats { - __u64 OutPktsUntagged; - __u64 InPktsUntagged; - __u64 OutPktsTooLong; - __u64 InPktsNoTag; - __u64 InPktsBadTag; - __u64 InPktsUnknownSCI; - __u64 InPktsNoSCI; - __u64 InPktsOverrun; -}; - #define MACSEC_VALIDATE_DEFAULT MACSEC_VALIDATE_STRICT struct pcpu_secy_stats { @@ -2642,207 +2631,309 @@ rollback: return ret; } -static int copy_tx_sa_stats(struct sk_buff *skb, - struct macsec_tx_sa_stats __percpu *pstats) +static void get_tx_sa_stats(struct net_device *dev, int an, + struct macsec_tx_sa *tx_sa, + struct macsec_tx_sa_stats *sum) { - struct macsec_tx_sa_stats sum = {0, }; + struct macsec_dev *macsec = macsec_priv(dev); int cpu; - for_each_possible_cpu(cpu) { - const struct macsec_tx_sa_stats *stats = per_cpu_ptr(pstats, cpu); + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; - sum.OutPktsProtected += stats->OutPktsProtected; - sum.OutPktsEncrypted += stats->OutPktsEncrypted; + ops = macsec_get_ops(macsec, &ctx); + if (ops) { + ctx.sa.assoc_num = an; + ctx.sa.tx_sa = tx_sa; + ctx.stats.tx_sa_stats = sum; + ctx.secy = &macsec_priv(dev)->secy; + macsec_offload(ops->mdo_get_tx_sa_stats, &ctx); + } + return; } - if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, sum.OutPktsProtected) || - nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, sum.OutPktsEncrypted)) + for_each_possible_cpu(cpu) { + const struct macsec_tx_sa_stats *stats = + per_cpu_ptr(tx_sa->stats, cpu); + + sum->OutPktsProtected += stats->OutPktsProtected; + sum->OutPktsEncrypted += stats->OutPktsEncrypted; + } +} + +static int copy_tx_sa_stats(struct sk_buff *skb, struct macsec_tx_sa_stats *sum) +{ + if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_PROTECTED, + sum->OutPktsProtected) || + nla_put_u32(skb, MACSEC_SA_STATS_ATTR_OUT_PKTS_ENCRYPTED, + sum->OutPktsEncrypted)) return -EMSGSIZE; return 0; } -static noinline_for_stack int -copy_rx_sa_stats(struct sk_buff *skb, - struct macsec_rx_sa_stats __percpu *pstats) +static void get_rx_sa_stats(struct net_device *dev, + struct macsec_rx_sc *rx_sc, int an, + struct macsec_rx_sa *rx_sa, + struct macsec_rx_sa_stats *sum) { - struct macsec_rx_sa_stats sum = {0, }; + struct macsec_dev *macsec = macsec_priv(dev); int cpu; - for_each_possible_cpu(cpu) { - const struct macsec_rx_sa_stats *stats = per_cpu_ptr(pstats, cpu); + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; - sum.InPktsOK += stats->InPktsOK; - sum.InPktsInvalid += stats->InPktsInvalid; - sum.InPktsNotValid += stats->InPktsNotValid; - sum.InPktsNotUsingSA += stats->InPktsNotUsingSA; - sum.InPktsUnusedSA += stats->InPktsUnusedSA; + ops = macsec_get_ops(macsec, &ctx); + if (ops) { + ctx.sa.assoc_num = an; + ctx.sa.rx_sa = rx_sa; + ctx.stats.rx_sa_stats = sum; + ctx.secy = &macsec_priv(dev)->secy; + ctx.rx_sc = rx_sc; + macsec_offload(ops->mdo_get_rx_sa_stats, &ctx); + } + return; } - if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_OK, sum.InPktsOK) || - nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, sum.InPktsInvalid) || - nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, sum.InPktsNotValid) || - nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, sum.InPktsNotUsingSA) || - nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, sum.InPktsUnusedSA)) + for_each_possible_cpu(cpu) { + const struct macsec_rx_sa_stats *stats = + per_cpu_ptr(rx_sa->stats, cpu); + + sum->InPktsOK += stats->InPktsOK; + sum->InPktsInvalid += stats->InPktsInvalid; + sum->InPktsNotValid += stats->InPktsNotValid; + sum->InPktsNotUsingSA += stats->InPktsNotUsingSA; + sum->InPktsUnusedSA += stats->InPktsUnusedSA; + } +} + +static int copy_rx_sa_stats(struct sk_buff *skb, + struct macsec_rx_sa_stats *sum) +{ + if (nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_OK, sum->InPktsOK) || + nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_INVALID, + sum->InPktsInvalid) || + nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_VALID, + sum->InPktsNotValid) || + nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_NOT_USING_SA, + sum->InPktsNotUsingSA) || + nla_put_u32(skb, MACSEC_SA_STATS_ATTR_IN_PKTS_UNUSED_SA, + sum->InPktsUnusedSA)) return -EMSGSIZE; return 0; } -static noinline_for_stack int -copy_rx_sc_stats(struct sk_buff *skb, struct pcpu_rx_sc_stats __percpu *pstats) +static void get_rx_sc_stats(struct net_device *dev, + struct macsec_rx_sc *rx_sc, + struct macsec_rx_sc_stats *sum) { - struct macsec_rx_sc_stats sum = {0, }; + struct macsec_dev *macsec = macsec_priv(dev); int cpu; + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(macsec, &ctx); + if (ops) { + ctx.stats.rx_sc_stats = sum; + ctx.secy = &macsec_priv(dev)->secy; + ctx.rx_sc = rx_sc; + macsec_offload(ops->mdo_get_rx_sc_stats, &ctx); + } + return; + } + for_each_possible_cpu(cpu) { const struct pcpu_rx_sc_stats *stats; struct macsec_rx_sc_stats tmp; unsigned int start; - stats = per_cpu_ptr(pstats, cpu); + stats = per_cpu_ptr(rx_sc->stats, cpu); do { start = u64_stats_fetch_begin_irq(&stats->syncp); memcpy(&tmp, &stats->stats, sizeof(tmp)); } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); - sum.InOctetsValidated += tmp.InOctetsValidated; - sum.InOctetsDecrypted += tmp.InOctetsDecrypted; - sum.InPktsUnchecked += tmp.InPktsUnchecked; - sum.InPktsDelayed += tmp.InPktsDelayed; - sum.InPktsOK += tmp.InPktsOK; - sum.InPktsInvalid += tmp.InPktsInvalid; - sum.InPktsLate += tmp.InPktsLate; - sum.InPktsNotValid += tmp.InPktsNotValid; - sum.InPktsNotUsingSA += tmp.InPktsNotUsingSA; - sum.InPktsUnusedSA += tmp.InPktsUnusedSA; + sum->InOctetsValidated += tmp.InOctetsValidated; + sum->InOctetsDecrypted += tmp.InOctetsDecrypted; + sum->InPktsUnchecked += tmp.InPktsUnchecked; + sum->InPktsDelayed += tmp.InPktsDelayed; + sum->InPktsOK += tmp.InPktsOK; + sum->InPktsInvalid += tmp.InPktsInvalid; + sum->InPktsLate += tmp.InPktsLate; + sum->InPktsNotValid += tmp.InPktsNotValid; + sum->InPktsNotUsingSA += tmp.InPktsNotUsingSA; + sum->InPktsUnusedSA += tmp.InPktsUnusedSA; } +} +static int copy_rx_sc_stats(struct sk_buff *skb, struct macsec_rx_sc_stats *sum) +{ if (nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED, - sum.InOctetsValidated, + sum->InOctetsValidated, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED, - sum.InOctetsDecrypted, + sum->InOctetsDecrypted, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED, - sum.InPktsUnchecked, + sum->InPktsUnchecked, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED, - sum.InPktsDelayed, + sum->InPktsDelayed, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK, - sum.InPktsOK, + sum->InPktsOK, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID, - sum.InPktsInvalid, + sum->InPktsInvalid, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE, - sum.InPktsLate, + sum->InPktsLate, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID, - sum.InPktsNotValid, + sum->InPktsNotValid, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA, - sum.InPktsNotUsingSA, + sum->InPktsNotUsingSA, MACSEC_RXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA, - sum.InPktsUnusedSA, + sum->InPktsUnusedSA, MACSEC_RXSC_STATS_ATTR_PAD)) return -EMSGSIZE; return 0; } -static noinline_for_stack int -copy_tx_sc_stats(struct sk_buff *skb, struct pcpu_tx_sc_stats __percpu *pstats) +static void get_tx_sc_stats(struct net_device *dev, + struct macsec_tx_sc_stats *sum) { - struct macsec_tx_sc_stats sum = {0, }; + struct macsec_dev *macsec = macsec_priv(dev); int cpu; + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(macsec, &ctx); + if (ops) { + ctx.stats.tx_sc_stats = sum; + ctx.secy = &macsec_priv(dev)->secy; + macsec_offload(ops->mdo_get_tx_sc_stats, &ctx); + } + return; + } + for_each_possible_cpu(cpu) { const struct pcpu_tx_sc_stats *stats; struct macsec_tx_sc_stats tmp; unsigned int start; - stats = per_cpu_ptr(pstats, cpu); + stats = per_cpu_ptr(macsec_priv(dev)->secy.tx_sc.stats, cpu); do { start = u64_stats_fetch_begin_irq(&stats->syncp); memcpy(&tmp, &stats->stats, sizeof(tmp)); } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); - sum.OutPktsProtected += tmp.OutPktsProtected; - sum.OutPktsEncrypted += tmp.OutPktsEncrypted; - sum.OutOctetsProtected += tmp.OutOctetsProtected; - sum.OutOctetsEncrypted += tmp.OutOctetsEncrypted; + sum->OutPktsProtected += tmp.OutPktsProtected; + sum->OutPktsEncrypted += tmp.OutPktsEncrypted; + sum->OutOctetsProtected += tmp.OutOctetsProtected; + sum->OutOctetsEncrypted += tmp.OutOctetsEncrypted; } +} +static int copy_tx_sc_stats(struct sk_buff *skb, struct macsec_tx_sc_stats *sum) +{ if (nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_PROTECTED, - sum.OutPktsProtected, + sum->OutPktsProtected, MACSEC_TXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_PKTS_ENCRYPTED, - sum.OutPktsEncrypted, + sum->OutPktsEncrypted, MACSEC_TXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_PROTECTED, - sum.OutOctetsProtected, + sum->OutOctetsProtected, MACSEC_TXSC_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_TXSC_STATS_ATTR_OUT_OCTETS_ENCRYPTED, - sum.OutOctetsEncrypted, + sum->OutOctetsEncrypted, MACSEC_TXSC_STATS_ATTR_PAD)) return -EMSGSIZE; return 0; } -static noinline_for_stack int -copy_secy_stats(struct sk_buff *skb, struct pcpu_secy_stats __percpu *pstats) +static void get_secy_stats(struct net_device *dev, struct macsec_dev_stats *sum) { - struct macsec_dev_stats sum = {0, }; + struct macsec_dev *macsec = macsec_priv(dev); int cpu; + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(macsec, &ctx); + if (ops) { + ctx.stats.dev_stats = sum; + ctx.secy = &macsec_priv(dev)->secy; + macsec_offload(ops->mdo_get_dev_stats, &ctx); + } + return; + } + for_each_possible_cpu(cpu) { const struct pcpu_secy_stats *stats; struct macsec_dev_stats tmp; unsigned int start; - stats = per_cpu_ptr(pstats, cpu); + stats = per_cpu_ptr(macsec_priv(dev)->stats, cpu); do { start = u64_stats_fetch_begin_irq(&stats->syncp); memcpy(&tmp, &stats->stats, sizeof(tmp)); } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); - sum.OutPktsUntagged += tmp.OutPktsUntagged; - sum.InPktsUntagged += tmp.InPktsUntagged; - sum.OutPktsTooLong += tmp.OutPktsTooLong; - sum.InPktsNoTag += tmp.InPktsNoTag; - sum.InPktsBadTag += tmp.InPktsBadTag; - sum.InPktsUnknownSCI += tmp.InPktsUnknownSCI; - sum.InPktsNoSCI += tmp.InPktsNoSCI; - sum.InPktsOverrun += tmp.InPktsOverrun; + sum->OutPktsUntagged += tmp.OutPktsUntagged; + sum->InPktsUntagged += tmp.InPktsUntagged; + sum->OutPktsTooLong += tmp.OutPktsTooLong; + sum->InPktsNoTag += tmp.InPktsNoTag; + sum->InPktsBadTag += tmp.InPktsBadTag; + sum->InPktsUnknownSCI += tmp.InPktsUnknownSCI; + sum->InPktsNoSCI += tmp.InPktsNoSCI; + sum->InPktsOverrun += tmp.InPktsOverrun; } +} +static int copy_secy_stats(struct sk_buff *skb, struct macsec_dev_stats *sum) +{ if (nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_UNTAGGED, - sum.OutPktsUntagged, + sum->OutPktsUntagged, MACSEC_SECY_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNTAGGED, - sum.InPktsUntagged, + sum->InPktsUntagged, MACSEC_SECY_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_OUT_PKTS_TOO_LONG, - sum.OutPktsTooLong, + sum->OutPktsTooLong, MACSEC_SECY_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_TAG, - sum.InPktsNoTag, + sum->InPktsNoTag, MACSEC_SECY_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_BAD_TAG, - sum.InPktsBadTag, + sum->InPktsBadTag, MACSEC_SECY_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_UNKNOWN_SCI, - sum.InPktsUnknownSCI, + sum->InPktsUnknownSCI, MACSEC_SECY_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_NO_SCI, - sum.InPktsNoSCI, + sum->InPktsNoSCI, MACSEC_SECY_STATS_ATTR_PAD) || nla_put_u64_64bit(skb, MACSEC_SECY_STATS_ATTR_IN_PKTS_OVERRUN, - sum.InPktsOverrun, + sum->InPktsOverrun, MACSEC_SECY_STATS_ATTR_PAD)) return -EMSGSIZE; @@ -2903,7 +2994,12 @@ static noinline_for_stack int dump_secy(struct macsec_secy *secy, struct net_device *dev, struct sk_buff *skb, struct netlink_callback *cb) { + struct macsec_tx_sc_stats tx_sc_stats = {0, }; + struct macsec_tx_sa_stats tx_sa_stats = {0, }; + struct macsec_rx_sc_stats rx_sc_stats = {0, }; + struct macsec_rx_sa_stats rx_sa_stats = {0, }; struct macsec_dev *macsec = netdev_priv(dev); + struct macsec_dev_stats dev_stats = {0, }; struct macsec_tx_sc *tx_sc = &secy->tx_sc; struct nlattr *txsa_list, *rxsc_list; struct macsec_rx_sc *rx_sc; @@ -2934,7 +3030,9 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, attr = nla_nest_start_noflag(skb, MACSEC_ATTR_TXSC_STATS); if (!attr) goto nla_put_failure; - if (copy_tx_sc_stats(skb, tx_sc->stats)) { + + get_tx_sc_stats(dev, &tx_sc_stats); + if (copy_tx_sc_stats(skb, &tx_sc_stats)) { nla_nest_cancel(skb, attr); goto nla_put_failure; } @@ -2943,7 +3041,8 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, attr = nla_nest_start_noflag(skb, MACSEC_ATTR_SECY_STATS); if (!attr) goto nla_put_failure; - if (copy_secy_stats(skb, macsec_priv(dev)->stats)) { + get_secy_stats(dev, &dev_stats); + if (copy_secy_stats(skb, &dev_stats)) { nla_nest_cancel(skb, attr); goto nla_put_failure; } @@ -2967,6 +3066,22 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, goto nla_put_failure; } + attr = nla_nest_start_noflag(skb, MACSEC_SA_ATTR_STATS); + if (!attr) { + nla_nest_cancel(skb, txsa_nest); + nla_nest_cancel(skb, txsa_list); + goto nla_put_failure; + } + memset(&tx_sa_stats, 0, sizeof(tx_sa_stats)); + get_tx_sa_stats(dev, i, tx_sa, &tx_sa_stats); + if (copy_tx_sa_stats(skb, &tx_sa_stats)) { + nla_nest_cancel(skb, attr); + nla_nest_cancel(skb, txsa_nest); + nla_nest_cancel(skb, txsa_list); + goto nla_put_failure; + } + nla_nest_end(skb, attr); + if (secy->xpn) { pn = tx_sa->next_pn; pn_len = MACSEC_XPN_PN_LEN; @@ -2985,20 +3100,6 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, goto nla_put_failure; } - attr = nla_nest_start_noflag(skb, MACSEC_SA_ATTR_STATS); - if (!attr) { - nla_nest_cancel(skb, txsa_nest); - nla_nest_cancel(skb, txsa_list); - goto nla_put_failure; - } - if (copy_tx_sa_stats(skb, tx_sa->stats)) { - nla_nest_cancel(skb, attr); - nla_nest_cancel(skb, txsa_nest); - nla_nest_cancel(skb, txsa_list); - goto nla_put_failure; - } - nla_nest_end(skb, attr); - nla_nest_end(skb, txsa_nest); } nla_nest_end(skb, txsa_list); @@ -3032,7 +3133,9 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, nla_nest_cancel(skb, rxsc_list); goto nla_put_failure; } - if (copy_rx_sc_stats(skb, rx_sc->stats)) { + memset(&rx_sc_stats, 0, sizeof(rx_sc_stats)); + get_rx_sc_stats(dev, rx_sc, &rx_sc_stats); + if (copy_rx_sc_stats(skb, &rx_sc_stats)) { nla_nest_cancel(skb, attr); nla_nest_cancel(skb, rxsc_nest); nla_nest_cancel(skb, rxsc_list); @@ -3073,7 +3176,9 @@ dump_secy(struct macsec_secy *secy, struct net_device *dev, nla_nest_cancel(skb, rxsc_list); goto nla_put_failure; } - if (copy_rx_sa_stats(skb, rx_sa->stats)) { + memset(&rx_sa_stats, 0, sizeof(rx_sa_stats)); + get_rx_sa_stats(dev, rx_sc, i, rx_sa, &rx_sa_stats); + if (copy_rx_sa_stats(skb, &rx_sa_stats)) { nla_nest_cancel(skb, attr); nla_nest_cancel(skb, rxsa_list); nla_nest_cancel(skb, rxsc_nest); diff --git a/include/net/macsec.h b/include/net/macsec.h index 2e4780dbf5c6..45e274e39f08 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -88,6 +88,17 @@ struct macsec_tx_sc_stats { __u64 OutOctetsEncrypted; }; +struct macsec_dev_stats { + __u64 OutPktsUntagged; + __u64 InPktsUntagged; + __u64 OutPktsTooLong; + __u64 InPktsNoTag; + __u64 InPktsBadTag; + __u64 InPktsUnknownSCI; + __u64 InPktsNoSCI; + __u64 InPktsOverrun; +}; + /** * struct macsec_rx_sa - receive secure association * @active: @@ -233,6 +244,13 @@ struct macsec_context { struct macsec_tx_sa *tx_sa; }; } sa; + union { + struct macsec_tx_sc_stats *tx_sc_stats; + struct macsec_tx_sa_stats *tx_sa_stats; + struct macsec_rx_sc_stats *rx_sc_stats; + struct macsec_rx_sa_stats *rx_sa_stats; + struct macsec_dev_stats *dev_stats; + } stats; u8 prepare:1; }; @@ -259,6 +277,12 @@ struct macsec_ops { int (*mdo_add_txsa)(struct macsec_context *ctx); int (*mdo_upd_txsa)(struct macsec_context *ctx); int (*mdo_del_txsa)(struct macsec_context *ctx); + /* Statistics */ + int (*mdo_get_dev_stats)(struct macsec_context *ctx); + int (*mdo_get_tx_sc_stats)(struct macsec_context *ctx); + int (*mdo_get_tx_sa_stats)(struct macsec_context *ctx); + int (*mdo_get_rx_sc_stats)(struct macsec_context *ctx); + int (*mdo_get_rx_sa_stats)(struct macsec_context *ctx); }; void macsec_pn_wrapped(struct macsec_secy *secy, struct macsec_tx_sa *tx_sa); From 8bf3347530f5ae9c968ae523c11e1fb419569e69 Mon Sep 17 00:00:00 2001 From: Subramanian Ananthanarayanan Date: Tue, 7 Dec 2021 19:39:07 +0530 Subject: [PATCH 50/71] pci: msm: Return -EDEFER when i2c is not probed Change is to reutrn -EDEFER error when i2c is not probed and delay PCIE probe. I2C probe is gettting called post pcie enumeration and this results in i2c de-emphasis settings not being applied. Pine doesn't enumerate as these settings are not applied. Change-Id: I1ac966b26c65f44afb32987927558209bf8787a7 Signed-off-by: Subramanian Ananthanarayanan --- drivers/pci/controller/pci-msm.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index ab03be06ef8b..bc7e19f0cc79 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -5941,6 +5941,12 @@ static int msm_pcie_i2c_ctrl_init(struct msm_pcie_dev_t *pcie_dev) PCIE_DBG(pcie_dev, "PCIe: RC%d: No i2c phandle found\n", pcie_dev->rc_idx); return 0; + } else { + if (!i2c_ctrl->client) { + PCIE_DBG(pcie_dev, "PCIe: RC%d: No i2c probe yet\n", + pcie_dev->rc_idx); + return -EPROBE_DEFER; + } } i2c_client_node = of_get_child_by_name(of_node, "pcie_i2c_ctrl"); @@ -6230,6 +6236,10 @@ static int msm_pcie_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, pcie_dev); + ret = msm_pcie_i2c_ctrl_init(pcie_dev); + if (ret) + goto decrease_rc_num; + ret = msm_pcie_get_resources(pcie_dev, pcie_dev->pdev); if (ret) goto decrease_rc_num; @@ -6244,6 +6254,7 @@ static int msm_pcie_probe(struct platform_device *pdev) else pcie_dev->use_pinctrl = true; + if (pcie_dev->use_pinctrl) { pcie_dev->pins_default = pinctrl_lookup_state(pcie_dev->pinctrl, "default"); @@ -6296,8 +6307,6 @@ static int msm_pcie_probe(struct platform_device *pdev) pcie_dev->rc_idx, ret); } - msm_pcie_i2c_ctrl_init(pcie_dev); - msm_pcie_sysfs_init(pcie_dev); pcie_dev->drv_ready = true; @@ -7070,12 +7079,12 @@ static int pcie_i2c_ctrl_probe(struct i2c_client *client, if (client_id == I2C_CLIENT_ID_NTN3) { i2c_ctrl = &msm_pcie_dev[rc_index].i2c_ctrl; - i2c_ctrl->client = client; i2c_ctrl->client_i2c_read = ntn3_i2c_read; i2c_ctrl->client_i2c_write = ntn3_i2c_write; i2c_ctrl->client_i2c_reset = ntn3_ep_reset_ctrl; i2c_ctrl->client_i2c_dump_regs = ntn3_dump_regs; i2c_ctrl->client_i2c_de_emphasis_wa = ntn3_de_emphasis_wa; + i2c_ctrl->client = client; } else { dev_err(&client->dev, "invalid client id %d\n", client_id); } From 90811ee5b8f4b6b1b66178287865447788825b9e Mon Sep 17 00:00:00 2001 From: Jilai Wang Date: Sun, 3 Oct 2021 21:10:09 -0400 Subject: [PATCH 51/71] msm: npu: fix network and misc commands race condition problem misc command doesn't do npu_init before sending to firmware, so it's possible that npu will be turned off while npu firmware is handling misc commands which causes times out in host driver. To fix this issue, npu_init/npu_deinit need to be called before and after misc commands are handled. In addition, only one misc command should be handled at any moment. The misc command should be failed right away while the other one is being handled. Change-Id: I523255da15fa4e3b8b21d74b28f90d3458ebeb3c Signed-off-by: Jilai Wang --- drivers/media/platform/msm/npu/npu_mgr.c | 68 +++++++++++++++++------- drivers/media/platform/msm/npu/npu_mgr.h | 4 +- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/msm/npu/npu_mgr.c b/drivers/media/platform/msm/npu/npu_mgr.c index f85a1ec02017..a6509666fd49 100644 --- a/drivers/media/platform/msm/npu/npu_mgr.c +++ b/drivers/media/platform/msm/npu/npu_mgr.c @@ -292,9 +292,8 @@ int npu_host_init(struct npu_device *npu_dev) struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; memset(host_ctx, 0, sizeof(*host_ctx)); - init_completion(&host_ctx->loopback_done); + init_completion(&host_ctx->misc_done); init_completion(&host_ctx->fw_deinit_done); - init_completion(&host_ctx->property_done); mutex_init(&host_ctx->lock); atomic_set(&host_ctx->ipc_trans_id, 1); host_ctx->npu_dev = npu_dev; @@ -308,6 +307,8 @@ int npu_host_init(struct npu_device *npu_dev) if (!host_ctx->prop_buf) return -ENOMEM; + host_ctx->misc_pending = false; + return 0; } @@ -368,6 +369,7 @@ static int host_error_hdlr(struct npu_device *npu_dev, bool force) network = &host_ctx->networks[i]; if (network->is_valid && network->cmd_pending && network->fw_error) { + network->cmd_pending = false; if (network->cmd_async) { pr_debug("async cmd, queue ssr event\n"); kevt.evt.type = MSM_NPU_EVENT_TYPE_SSR; @@ -382,7 +384,8 @@ static int host_error_hdlr(struct npu_device *npu_dev, bool force) } } } - complete_all(&host_ctx->loopback_done); + host_ctx->misc_pending = false; + complete_all(&host_ctx->misc_done); mutex_unlock(&host_ctx->lock); return 1; @@ -927,7 +930,9 @@ static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) pr_debug("NPU_IPC_MSG_LOOPBACK_DONE loopbackParams: 0x%x\n", lb_rsp_pkt->loopbackParams); - complete_all(&host_ctx->loopback_done); + host_ctx->misc_pending = false; + + complete_all(&host_ctx->misc_done); break; } case NPU_IPC_MSG_SET_PROPERTY_DONE: @@ -942,8 +947,9 @@ static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) param[0]); host_ctx->cmd_ret_status = prop_rsp_pkt->header.status; + host_ctx->misc_pending = false; - complete_all(&host_ctx->property_done); + complete_all(&host_ctx->misc_done); break; } case NPU_IPC_MSG_GET_PROPERTY_DONE: @@ -968,8 +974,9 @@ static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) sizeof(struct ipc_msg_header_pkt); memcpy(host_ctx->prop_buf, prop_data, prop_size); } + host_ctx->misc_pending = false; - complete_all(&host_ctx->property_done); + complete_all(&host_ctx->misc_done); break; } case NPU_IPC_MSG_GENERAL_NOTIFY: @@ -1143,7 +1150,7 @@ static int npu_send_network_cmd(struct npu_device *npu_dev, network->cmd_async = async; network->cmd_ret_status = 0; network->cmd_pending = true; - network->trans_id = atomic_read(&host_ctx->ipc_trans_id); + network->trans_id = ((struct ipc_cmd_header_pkt *)cmd_ptr)->trans_id; ret = npu_host_ipc_send_cmd(npu_dev, IPC_QUEUE_APPS_EXEC, cmd_ptr); if (ret) @@ -1163,11 +1170,18 @@ static int npu_send_misc_cmd(struct npu_device *npu_dev, uint32_t q_idx, if (host_ctx->fw_error || (host_ctx->fw_state == FW_DISABLED)) { pr_err("fw is in error state or disabled, can't send misc cmd\n"); ret = -EIO; + } else if (host_ctx->misc_pending) { + pr_err("Another misc cmd is pending\n"); + ret = -EBUSY; } else { pr_debug("Send cmd %d\n", ((struct ipc_cmd_header_pkt *)cmd_ptr)->cmd_type); host_ctx->cmd_ret_status = 0; + reinit_completion(&host_ctx->misc_done); + host_ctx->misc_pending = true; ret = npu_host_ipc_send_cmd(npu_dev, q_idx, cmd_ptr); + if (ret) + host_ctx->misc_pending = false; } mutex_unlock(&host_ctx->lock); @@ -1306,6 +1320,12 @@ int32_t npu_host_set_fw_property(struct npu_device *npu_dev, goto set_prop_exit; } + ret = fw_init(npu_dev); + if (ret) { + pr_err("fw_init fail\n"); + goto set_prop_exit; + } + prop_packet->header.cmd_type = NPU_IPC_CMD_SET_PROPERTY; prop_packet->header.size = pkt_size; prop_packet->header.trans_id = @@ -1318,34 +1338,36 @@ int32_t npu_host_set_fw_property(struct npu_device *npu_dev, for (i = 0; i < num_of_params; i++) prop_packet->prop_param[i] = property->prop_param[i]; - reinit_completion(&host_ctx->property_done); ret = npu_send_misc_cmd(npu_dev, IPC_QUEUE_APPS_EXEC, prop_packet); + pr_debug("NPU_IPC_CMD_SET_PROPERTY sent status: %d\n", ret); if (ret) { pr_err("NPU_IPC_CMD_SET_PROPERTY failed\n"); - goto set_prop_exit; + goto deinit_fw; } ret = wait_for_completion_interruptible_timeout( - &host_ctx->property_done, + &host_ctx->misc_done, (host_ctx->fw_dbg_mode & FW_DBG_MODE_INC_TIMEOUT) ? NW_DEBUG_TIMEOUT : NW_CMD_TIMEOUT); if (!ret) { pr_err_ratelimited("npu: NPU_IPC_CMD_SET_PROPERTY time out\n"); ret = -ETIMEDOUT; - goto set_prop_exit; + goto deinit_fw; } else if (ret < 0) { pr_err("Wait for set_property done interrupted by signal\n"); - goto set_prop_exit; + goto deinit_fw; } ret = host_ctx->cmd_ret_status; if (ret) pr_err("set fw property failed %d\n", ret); +deinit_fw: + fw_deinit(npu_dev, false, true); set_prop_exit: kfree(prop_packet); return ret; @@ -1368,6 +1390,12 @@ int32_t npu_host_get_fw_property(struct npu_device *npu_dev, if (!prop_packet) return -ENOMEM; + ret = fw_init(npu_dev); + if (ret) { + pr_err("fw_init fail\n"); + goto get_prop_exit; + } + prop_packet->header.cmd_type = NPU_IPC_CMD_GET_PROPERTY; prop_packet->header.size = pkt_size; prop_packet->header.trans_id = @@ -1380,28 +1408,27 @@ int32_t npu_host_get_fw_property(struct npu_device *npu_dev, for (i = 0; i < num_of_params; i++) prop_packet->prop_param[i] = property->prop_param[i]; - reinit_completion(&host_ctx->property_done); ret = npu_send_misc_cmd(npu_dev, IPC_QUEUE_APPS_EXEC, prop_packet); pr_debug("NPU_IPC_CMD_GET_PROPERTY sent status: %d\n", ret); if (ret) { pr_err("NPU_IPC_CMD_GET_PROPERTY failed\n"); - goto get_prop_exit; + goto deinit_fw; } ret = wait_for_completion_interruptible_timeout( - &host_ctx->property_done, + &host_ctx->misc_done, (host_ctx->fw_dbg_mode & FW_DBG_MODE_INC_TIMEOUT) ? NW_DEBUG_TIMEOUT : NW_CMD_TIMEOUT); if (!ret) { pr_err_ratelimited("npu: NPU_IPC_CMD_GET_PROPERTY time out\n"); ret = -ETIMEDOUT; - goto get_prop_exit; + goto deinit_fw; } else if (ret < 0) { pr_err("Wait for get_property done interrupted by signal\n"); - goto get_prop_exit; + goto deinit_fw; } ret = host_ctx->cmd_ret_status; @@ -1419,6 +1446,8 @@ int32_t npu_host_get_fw_property(struct npu_device *npu_dev, pr_err("get fw property failed %d\n", ret); } +deinit_fw: + fw_deinit(npu_dev, false, true); get_prop_exit: kfree(prop_packet); return ret; @@ -1609,7 +1638,7 @@ int32_t npu_host_load_network_v2(struct npu_client *client, mutex_lock(&host_ctx->lock); if (!ret) { - pr_err_ratelimited("npu: NPU_IPC_CMD_LOAD time out\n"); + pr_err_ratelimited("npu: NPU_IPC_CMD_LOAD_V2 time out\n"); ret = -ETIMEDOUT; goto error_free_network; } @@ -2052,7 +2081,6 @@ int32_t npu_host_loopback_test(struct npu_device *npu_dev) loopback_packet.header.flags = 0; loopback_packet.loopbackParams = 15; - reinit_completion(&host_ctx->loopback_done); ret = npu_send_misc_cmd(npu_dev, IPC_QUEUE_APPS_EXEC, &loopback_packet); if (ret) { @@ -2061,7 +2089,7 @@ int32_t npu_host_loopback_test(struct npu_device *npu_dev) } ret = wait_for_completion_interruptible_timeout( - &host_ctx->loopback_done, + &host_ctx->misc_done, (host_ctx->fw_dbg_mode & FW_DBG_MODE_INC_TIMEOUT) ? NW_DEBUG_TIMEOUT : NW_CMD_TIMEOUT); diff --git a/drivers/media/platform/msm/npu/npu_mgr.h b/drivers/media/platform/msm/npu/npu_mgr.h index cfc4e616e5ac..f0ab23267787 100644 --- a/drivers/media/platform/msm/npu/npu_mgr.h +++ b/drivers/media/platform/msm/npu/npu_mgr.h @@ -77,9 +77,9 @@ struct npu_host_ctx { struct delayed_work fw_deinit_work; atomic_t fw_deinit_work_cnt; struct workqueue_struct *wq; - struct completion loopback_done; + struct completion misc_done; struct completion fw_deinit_done; - struct completion property_done; + bool misc_pending; void *prop_buf; int32_t network_num; struct npu_network networks[MAX_LOADED_NETWORK]; From 983a7e7c981bd410ad1e8c09a094a8c2c6a7b616 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 14 Dec 2021 19:46:21 +0100 Subject: [PATCH 52/71] FROMGIT: USB: gadget: bRequestType is a bitfield, not a enum Szymon rightly pointed out that the previous check for the endpoint direction in bRequestType was not looking at only the bit involved, but rather the whole value. Normally this is ok, but for some request types, bits other than bit 8 could be set and the check for the endpoint length could not stall correctly. Fix that up by only checking the single bit. Fixes: 153a2d7e3350 ("USB: gadget: detect too-big endpoint 0 requests") Cc: Felipe Balbi Reported-by: Szymon Heidrich Link: https://lore.kernel.org/r/20211214184621.385828-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f08adf5add9a071160c68bb2a61d697f39ab0758 https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-linus) Bug: 210292376 Signed-off-by: Greg Kroah-Hartman Change-Id: I7e708b2b94433009c87f697346e0515d93454f48 --- drivers/usb/gadget/composite.c | 6 +++--- drivers/usb/gadget/legacy/dbgp.c | 6 +++--- drivers/usb/gadget/legacy/inode.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index cc31a47949cd..eec5ec939d72 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1645,14 +1645,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u8 endp; if (w_length > USB_COMP_EP0_BUFSIZ) { - if (ctrl->bRequestType == USB_DIR_OUT) { - goto done; - } else { + if (ctrl->bRequestType & USB_DIR_IN) { /* Cast away the const, we are going to overwrite on purpose. */ __le16 *temp = (__le16 *)&ctrl->wLength; *temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ); w_length = USB_COMP_EP0_BUFSIZ; + } else { + goto done; } } diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 355bc7dab9d5..6bcbad382580 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -346,14 +346,14 @@ static int dbgp_setup(struct usb_gadget *gadget, u16 len = 0; if (length > DBGP_REQ_LEN) { - if (ctrl->bRequestType == USB_DIR_OUT) { - return err; - } else { + if (ctrl->bRequestType & USB_DIR_IN) { /* Cast away the const, we are going to overwrite on purpose. */ __le16 *temp = (__le16 *)&ctrl->wLength; *temp = cpu_to_le16(DBGP_REQ_LEN); length = DBGP_REQ_LEN; + } else { + return err; } } diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index f0aff79f544c..5f1e15172403 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1336,14 +1336,14 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) u16 w_length = le16_to_cpu(ctrl->wLength); if (w_length > RBUF_SIZE) { - if (ctrl->bRequestType == USB_DIR_OUT) { - return value; - } else { + if (ctrl->bRequestType & USB_DIR_IN) { /* Cast away the const, we are going to overwrite on purpose. */ __le16 *temp = (__le16 *)&ctrl->wLength; *temp = cpu_to_le16(RBUF_SIZE); w_length = RBUF_SIZE; + } else { + return value; } } From 3e1f68e991bbd6edbdaac9fc9a7a2b74ccffea28 Mon Sep 17 00:00:00 2001 From: Pradeep P V K Date: Wed, 15 Dec 2021 21:03:30 +0530 Subject: [PATCH 53/71] mtd: msm_qpic_nand: Enable clocks while accessing BAM registers A NOC was seen while dumping BAM registers information. To avoid this, always enable qpic clocks before accessing BAM registers. Change-Id: I7a8799d742dc1fcc066512aba1537d7d48797a73 Signed-off-by: Pradeep P V K --- drivers/mtd/devices/msm_qpic_nand.c | 70 ++++++++++++++++------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c index 54842b377292..78517b27c309 100644 --- a/drivers/mtd/devices/msm_qpic_nand.c +++ b/drivers/mtd/devices/msm_qpic_nand.c @@ -20,36 +20,6 @@ #define ONE_CODEWORD_SIZE 516 static struct device *dev_node; -static int msm_nand_bam_panic_notifier(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct msm_nand_info *info = dev_get_drvdata(dev_node); - - pr_info("Dumping APSS bam pipes register dumps\n"); - sps_get_bam_debug_info(info->sps.bam_handle, 93, - (SPS_BAM_PIPE(0) | - SPS_BAM_PIPE(1) | - SPS_BAM_PIPE(2) | - SPS_BAM_PIPE(3)), - 0, 2); - return NOTIFY_DONE; -} - -static struct notifier_block msm_nand_bam_panic_blk = { - .notifier_call = msm_nand_bam_panic_notifier, -}; - -void msm_nand_bam_register_panic_handler(void) -{ - atomic_notifier_chain_register(&panic_notifier_list, - &msm_nand_bam_panic_blk); -} - -void msm_nand_bam_unregister_panic_handler(void) -{ - atomic_notifier_chain_unregister(&panic_notifier_list, - &msm_nand_bam_panic_blk); -} /* * Get the DMA memory for requested amount of size. It returns the pointer @@ -4370,6 +4340,46 @@ out: return -EINVAL; } +static int msm_nand_bam_panic_notifier(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct msm_nand_info *info = dev_get_drvdata(dev_node); + struct msm_nand_chip *chip = &info->nand_chip; + int err; + + err = msm_nand_get_device(chip->dev); + if (err) + goto out; + pr_info("Dumping APSS bam pipes register dumps\n"); + sps_get_bam_debug_info(info->sps.bam_handle, 93, + (SPS_BAM_PIPE(0) | + SPS_BAM_PIPE(1) | + SPS_BAM_PIPE(2) | + SPS_BAM_PIPE(3)), + 0, 2); + err = msm_nand_put_device(chip->dev); +out: + if (err) + pr_err("Failed to get/put the device.\n"); + return NOTIFY_DONE; +} + +static struct notifier_block msm_nand_bam_panic_blk = { + .notifier_call = msm_nand_bam_panic_notifier, +}; + +void msm_nand_bam_register_panic_handler(void) +{ + atomic_notifier_chain_register(&panic_notifier_list, + &msm_nand_bam_panic_blk); +} + +void msm_nand_bam_unregister_panic_handler(void) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &msm_nand_bam_panic_blk); +} + #define BOOT_DEV_MASK 0x1E #define BOOT_DEV_NAND 0x4 From dc75b47b7c3da22f64e2356cf7ebcb2047bc28ad Mon Sep 17 00:00:00 2001 From: Naman Padhiar Date: Thu, 16 Dec 2021 23:08:17 +0530 Subject: [PATCH 54/71] icnss: Add check for valid SMEM handler Validate SMEM state handler before using it. Change-Id: I762cef00daa267ff7e411a5c740556d7f235d383 Signed-off-by: Naman Padhiar --- drivers/soc/qcom/icnss2/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/icnss2/main.c b/drivers/soc/qcom/icnss2/main.c index 4180981c3a75..dc873a24e54f 100644 --- a/drivers/soc/qcom/icnss2/main.c +++ b/drivers/soc/qcom/icnss2/main.c @@ -1307,7 +1307,8 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, if (priv->force_err_fatal) ICNSS_ASSERT(0); - if (priv->device_id == WCN6750_DEVICE_ID) { + if (priv->device_id == WCN6750_DEVICE_ID && + !IS_ERR(priv->smp2p_info.smem_state)) { priv->smp2p_info.seq = 0; if (qcom_smem_state_update_bits( priv->smp2p_info.smem_state, @@ -3912,7 +3913,7 @@ static inline void icnss_get_smp2p_info(struct icnss_priv *priv) "wlan-smp2p-out", &priv->smp2p_info.smem_bit); if (IS_ERR(priv->smp2p_info.smem_state)) { - icnss_pr_smp2p("Failed to get smem state %d", + icnss_pr_err("Failed to get smem state %d", PTR_ERR(priv->smp2p_info.smem_state)); } From 0c9617b5de452f0bf0eb87775f1d828c8d863893 Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Mon, 13 Dec 2021 17:42:21 -0800 Subject: [PATCH 55/71] msm: ipa: Includes QMI structure changes for IPA Peripheral stats QMI structure definition for sending per_stats_smem info. Change-Id: I072075547b5921072e14711d1076c04fe25434b6 Signed-off-by: Michael Adisumarta --- include/uapi/linux/ipa_qmi_service_v01.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h index 5e6170028c4d..63dca0e5b116 100644 --- a/include/uapi/linux/ipa_qmi_service_v01.h +++ b/include/uapi/linux/ipa_qmi_service_v01.h @@ -385,6 +385,22 @@ struct ipa_init_modem_driver_req_msg_v01 { */ __u8 hw_fiter_stats_info_valid; struct ipa_filter_stats_info_type_v01 hw_filter_stats_info; + + /* optional + * Filter table smem info + * Must be set to true if smem_info + * is being passed(Currently not using it) + */ + __u8 smem_info_valid; + struct ipa_modem_mem_info_type_v01 smem_info; + + /* optional + * IPA Peripheral stats info + * Must be set to true if per_stats_info + * is being passed + */ + __u8 per_stats_smem_info_valid; + struct ipa_modem_mem_info_type_v01 per_stats_smem_info; }; /* Message */ /* Response Message; Requests the modem IPA driver about initialization */ @@ -2825,7 +2841,7 @@ struct ipa_move_nat_table_complt_ind_msg_v01 { #define QMI_IPA_MOVE_NAT_COMPLETE_IND_V01 0x0046 /* add for max length*/ -#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 186 +#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 210 #define QMI_IPA_INIT_MODEM_DRIVER_RESP_MAX_MSG_LEN_V01 25 #define QMI_IPA_INDICATION_REGISTER_REQ_MAX_MSG_LEN_V01 16 #define QMI_IPA_INDICATION_REGISTER_RESP_MAX_MSG_LEN_V01 7 From f4c81064a7ba883d6e16b2090a1e243b0179894a Mon Sep 17 00:00:00 2001 From: Jilai Wang Date: Mon, 18 Oct 2021 15:27:44 -0400 Subject: [PATCH 56/71] msm: npu: fix driver warnings This change is to fix warnings generated when running sparse check. Change-Id: I67bc66d1fc6d3fe39cd3d566e00b67e14e61bb7d Signed-off-by: Jilai Wang --- drivers/media/platform/msm/npu/npu_common.h | 3 +- drivers/media/platform/msm/npu/npu_debugfs.c | 195 +----------------- drivers/media/platform/msm/npu/npu_dev.c | 26 +-- drivers/media/platform/msm/npu/npu_host_ipc.c | 2 +- .../media/platform/msm/npu/npu_hw_access.c | 10 +- drivers/media/platform/msm/npu/npu_mgr.c | 4 +- 6 files changed, 24 insertions(+), 216 deletions(-) diff --git a/drivers/media/platform/msm/npu/npu_common.h b/drivers/media/platform/msm/npu/npu_common.h index c423933f7d1f..2d82b4236972 100644 --- a/drivers/media/platform/msm/npu/npu_common.h +++ b/drivers/media/platform/msm/npu/npu_common.h @@ -246,7 +246,8 @@ struct npu_device { struct npu_kevent { struct list_head list; struct msm_npu_event evt; - uint64_t reserved[4]; + void *stats_buf; + void __user *stats_buf_u; }; struct npu_client { diff --git a/drivers/media/platform/msm/npu/npu_debugfs.c b/drivers/media/platform/msm/npu/npu_debugfs.c index 13732b85fca0..18c8df0a4bb0 100644 --- a/drivers/media/platform/msm/npu/npu_debugfs.c +++ b/drivers/media/platform/msm/npu/npu_debugfs.c @@ -27,14 +27,6 @@ */ static int npu_debug_open(struct inode *inode, struct file *file); static int npu_debug_release(struct inode *inode, struct file *file); -static int npu_debug_reg_open(struct inode *inode, struct file *file); -static int npu_debug_reg_release(struct inode *inode, struct file *file); -static ssize_t npu_debug_reg_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos); -static ssize_t npu_debug_off_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos); -static ssize_t npu_debug_off_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos); static ssize_t npu_debug_log_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos); static ssize_t npu_debug_ctrl_write(struct file *file, @@ -44,20 +36,7 @@ static ssize_t npu_debug_ctrl_write(struct file *file, * Variables * ------------------------------------------------------------------------- */ -struct npu_device *g_npu_dev; - -static const struct file_operations npu_reg_fops = { - .open = npu_debug_reg_open, - .release = npu_debug_reg_release, - .read = npu_debug_reg_read, -}; - -static const struct file_operations npu_off_fops = { - .open = npu_debug_open, - .release = npu_debug_release, - .read = npu_debug_off_read, - .write = npu_debug_off_write, -}; +static struct npu_device *g_npu_dev; static const struct file_operations npu_log_fops = { .open = npu_debug_open, @@ -90,166 +69,6 @@ static int npu_debug_release(struct inode *inode, struct file *file) return 0; } -static int npu_debug_reg_open(struct inode *inode, struct file *file) -{ - struct npu_debugfs_reg_ctx *reg_ctx; - - reg_ctx = kzalloc(sizeof(*reg_ctx), GFP_KERNEL); - if (!reg_ctx) - return -ENOMEM; - - /* non-seekable */ - file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); - reg_ctx->npu_dev = inode->i_private; - file->private_data = reg_ctx; - return 0; -} - -static int npu_debug_reg_release(struct inode *inode, struct file *file) -{ - struct npu_debugfs_reg_ctx *reg_ctx = file->private_data; - - kfree(reg_ctx->buf); - kfree(reg_ctx); - file->private_data = NULL; - return 0; -} - -/* ------------------------------------------------------------------------- - * Function Implementations - Reg Read/Write - * ------------------------------------------------------------------------- - */ -static ssize_t npu_debug_reg_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - struct npu_debugfs_reg_ctx *reg_ctx = file->private_data; - struct npu_device *npu_dev = reg_ctx->npu_dev; - struct npu_debugfs_ctx *debugfs; - size_t len; - - debugfs = &npu_dev->debugfs_ctx; - - if (debugfs->reg_cnt == 0) - return 0; - - if (!reg_ctx->buf) { - char dump_buf[64]; - char *ptr; - int cnt, tot, off; - - reg_ctx->buf_len = sizeof(dump_buf) * - DIV_ROUND_UP(debugfs->reg_cnt, ROW_BYTES); - reg_ctx->buf = kzalloc(reg_ctx->buf_len, GFP_KERNEL); - - if (!reg_ctx->buf) - return -ENOMEM; - - ptr = npu_dev->core_io.base + debugfs->reg_off; - tot = 0; - off = (int)debugfs->reg_off; - - if (npu_enable_core_power(npu_dev)) - return -EPERM; - - for (cnt = debugfs->reg_cnt * 4; cnt > 0; cnt -= ROW_BYTES) { - hex_dump_to_buffer(ptr, min(cnt, ROW_BYTES), - ROW_BYTES, GROUP_BYTES, dump_buf, - sizeof(dump_buf), false); - len = scnprintf(reg_ctx->buf + tot, - reg_ctx->buf_len - tot, "0x%08x: %s\n", - ((int) (unsigned long) ptr) - - ((int) (unsigned long) npu_dev->core_io.base), - dump_buf); - - ptr += ROW_BYTES; - tot += len; - if (tot >= reg_ctx->buf_len) - break; - } - npu_disable_core_power(npu_dev); - - reg_ctx->buf_len = tot; - } - - if (*ppos >= reg_ctx->buf_len) - return 0; /* done reading */ - - len = min(count, reg_ctx->buf_len - (size_t) *ppos); - pr_debug("read %zi %zi\n", count, reg_ctx->buf_len - (size_t) *ppos); - if (copy_to_user(user_buf, reg_ctx->buf + *ppos, len)) { - pr_err("failed to copy to user\n"); - return -EFAULT; - } - - *ppos += len; /* increase offset */ - return len; -} - -/* ------------------------------------------------------------------------- - * Function Implementations - Offset Read/Write - * ------------------------------------------------------------------------- - */ -static ssize_t npu_debug_off_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - size_t off = 0; - uint32_t cnt, reg_cnt; - char buf[24]; - struct npu_device *npu_dev = file->private_data; - struct npu_debugfs_ctx *debugfs; - - pr_debug("npu_dev %pK %pK\n", npu_dev, g_npu_dev); - npu_dev = g_npu_dev; - debugfs = &npu_dev->debugfs_ctx; - - if (count >= sizeof(buf)) - return -EINVAL; - - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; - - buf[count] = 0; /* end of string */ - - cnt = sscanf(buf, "%zx %x", &off, ®_cnt); - if (cnt == 1) - reg_cnt = DEFAULT_REG_DUMP_NUM; - pr_debug("reg off = %zx, %d cnt=%d\n", off, reg_cnt, cnt); - if (cnt >= 1) { - debugfs->reg_off = off; - debugfs->reg_cnt = reg_cnt; - } - - return count; -} - -static ssize_t npu_debug_off_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - size_t len; - char buf[64]; - struct npu_device *npu_dev = file->private_data; - struct npu_debugfs_ctx *debugfs; - - pr_debug("npu_dev %pK %pK\n", npu_dev, g_npu_dev); - npu_dev = g_npu_dev; - debugfs = &npu_dev->debugfs_ctx; - - if (*ppos) - return 0; /* the end */ - - len = scnprintf(buf, sizeof(buf), "offset=0x%08x cnt=%d\n", - debugfs->reg_off, debugfs->reg_cnt); - len = min(len, count); - - if (copy_to_user(user_buf, buf, len)) { - pr_err("failed to copy to user\n"); - return -EFAULT; - } - - *ppos += len; /* increase offset */ - return len; -} - /* ------------------------------------------------------------------------- * Function Implementations - DebugFS Log * ------------------------------------------------------------------------- @@ -374,18 +193,6 @@ int npu_debugfs_init(struct npu_device *npu_dev) return -ENODEV; } - if (!debugfs_create_file("reg", 0644, debugfs->root, - npu_dev, &npu_reg_fops)) { - pr_err("debugfs_create_file reg fail\n"); - goto err; - } - - if (!debugfs_create_file("off", 0644, debugfs->root, - npu_dev, &npu_off_fops)) { - pr_err("debugfs_create_file off fail\n"); - goto err; - } - if (!debugfs_create_file("log", 0644, debugfs->root, npu_dev, &npu_log_fops)) { pr_err("debugfs_create_file log fail\n"); diff --git a/drivers/media/platform/msm/npu/npu_dev.c b/drivers/media/platform/msm/npu/npu_dev.c index 9a0ef0b0d453..319f5fee5516 100644 --- a/drivers/media/platform/msm/npu/npu_dev.c +++ b/drivers/media/platform/msm/npu/npu_dev.c @@ -111,7 +111,7 @@ static int npu_get_property(struct npu_client *client, unsigned long arg); static long npu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -static unsigned int npu_poll(struct file *filp, struct poll_table_struct *p); +static __poll_t npu_poll(struct file *filp, struct poll_table_struct *p); static int npu_parse_dt_clock(struct npu_device *npu_dev); static int npu_parse_dt_regulator(struct npu_device *npu_dev); static int npu_of_parse_pwrlevels(struct npu_device *npu_dev, @@ -541,7 +541,7 @@ static int npu_set_cdsprm_corner_limit(enum cdsprm_npu_corner corner) return npu_set_power_level(g_npu_dev, false); } -const struct cdsprm_npu_limit_cbs cdsprm_npu_limit_cbs = { +static const struct cdsprm_npu_limit_cbs cdsprm_npu_limit_cbs = { .set_corner_limit = npu_set_cdsprm_corner_limit, }; @@ -573,7 +573,7 @@ static uint32_t npu_notify_cdsprm_cxlimit_corner( return pwr_lvl_to_set; } -int npu_cdsprm_cxlimit_init(struct npu_device *npu_dev) +static int npu_cdsprm_cxlimit_init(struct npu_device *npu_dev) { bool enabled; int ret = 0; @@ -597,7 +597,7 @@ int npu_cdsprm_cxlimit_init(struct npu_device *npu_dev) return ret; } -int npu_cdsprm_cxlimit_deinit(struct npu_device *npu_dev) +static int npu_cdsprm_cxlimit_deinit(struct npu_device *npu_dev) { int ret = 0; @@ -1565,8 +1565,8 @@ static int npu_process_kevent(struct npu_kevent *kevt) switch (kevt->evt.type) { case MSM_NPU_EVENT_TYPE_EXEC_V2_DONE: - ret = copy_to_user((void __user *)kevt->reserved[1], - (void *)kevt->reserved[0], + ret = copy_to_user(kevt->stats_buf_u, + kevt->stats_buf, kevt->evt.u.exec_v2_done.stats_buf_size); if (ret) { pr_err("fail to copy to user\n"); @@ -1777,21 +1777,21 @@ static long npu_ioctl(struct file *file, unsigned int cmd, return ret; } -static unsigned int npu_poll(struct file *filp, struct poll_table_struct *p) +static __poll_t npu_poll(struct file *filp, struct poll_table_struct *p) { struct npu_client *client = filp->private_data; - int rc = 0; + __poll_t mask = 0; poll_wait(filp, &client->wait, p); mutex_lock(&client->list_lock); if (!list_empty(&client->evt_list)) { pr_debug("poll cmd done\n"); - rc = POLLIN | POLLRDNORM; + mask = EPOLLIN | EPOLLRDNORM; } mutex_unlock(&client->list_lock); - return rc; + return mask; } /* ------------------------------------------------------------------------- @@ -2135,9 +2135,9 @@ static int npu_hw_info_init(struct npu_device *npu_dev) static int npu_probe(struct platform_device *pdev) { int rc = 0; - struct resource *res = 0; - struct npu_device *npu_dev = 0; - struct thermal_cooling_device *tcdev = 0; + struct resource *res = NULL; + struct npu_device *npu_dev = NULL; + struct thermal_cooling_device *tcdev = NULL; npu_dev = devm_kzalloc(&pdev->dev, sizeof(struct npu_device), GFP_KERNEL); diff --git a/drivers/media/platform/msm/npu/npu_host_ipc.c b/drivers/media/platform/msm/npu/npu_host_ipc.c index cddabe507ddd..940a35d9ada8 100644 --- a/drivers/media/platform/msm/npu/npu_host_ipc.c +++ b/drivers/media/platform/msm/npu/npu_host_ipc.c @@ -68,7 +68,7 @@ static int npu_host_ipc_init_hfi(struct npu_device *npu_dev) struct hfi_queue_tbl_header *q_tbl_hdr = NULL; struct hfi_queue_header *q_hdr_arr = NULL; struct hfi_queue_header *q_hdr = NULL; - void *q_tbl_addr = 0; + void *q_tbl_addr = NULL; uint32_t reg_val = 0; uint32_t q_idx = 0; uint32_t q_tbl_size = sizeof(struct hfi_queue_tbl_header) + diff --git a/drivers/media/platform/msm/npu/npu_hw_access.c b/drivers/media/platform/msm/npu/npu_hw_access.c index a36fd1279320..3b7a420a1d11 100644 --- a/drivers/media/platform/msm/npu/npu_hw_access.c +++ b/drivers/media/platform/msm/npu/npu_hw_access.c @@ -103,7 +103,7 @@ void npu_mem_write(struct npu_device *npu_dev, void *dst, void *src, { size_t dst_off = (size_t)dst; uint32_t *src_ptr32 = (uint32_t *)src; - uint8_t *src_ptr8 = 0; + uint8_t *src_ptr8 = NULL; uint32_t i = 0; uint32_t num = 0; @@ -138,7 +138,7 @@ int32_t npu_mem_read(struct npu_device *npu_dev, void *src, void *dst, { size_t src_off = (size_t)src; uint32_t *out32 = (uint32_t *)dst; - uint8_t *out8 = 0; + uint8_t *out8 = NULL; uint32_t i = 0; uint32_t num = 0; @@ -367,7 +367,7 @@ void npu_mem_invalidate(struct npu_client *client, int buf_hdl) bool npu_mem_verify_addr(struct npu_client *client, uint64_t addr) { - struct npu_ion_buf *ion_buf = 0; + struct npu_ion_buf *ion_buf = NULL; struct list_head *pos = NULL; bool valid = false; @@ -387,7 +387,7 @@ bool npu_mem_verify_addr(struct npu_client *client, uint64_t addr) void npu_mem_unmap(struct npu_client *client, int buf_hdl, uint64_t addr) { struct npu_device *npu_dev = client->npu_dev; - struct npu_ion_buf *ion_buf = 0; + struct npu_ion_buf *ion_buf = NULL; /* clear entry and retrieve the corresponding buffer */ ion_buf = npu_get_npu_ion_buffer(client, buf_hdl); @@ -466,7 +466,7 @@ void npu_process_log_message(struct npu_device *npu_dev, uint32_t *message, debugfs->log_buf_size) { /* Wrap around case */ uint8_t *src_addr = (uint8_t *)message; - uint8_t *dst_addr = 0; + uint8_t *dst_addr = NULL; uint32_t remaining_to_end = debugfs->log_buf_size - debugfs->log_write_index + 1; dst_addr = debugfs->log_buf + debugfs->log_write_index; diff --git a/drivers/media/platform/msm/npu/npu_mgr.c b/drivers/media/platform/msm/npu/npu_mgr.c index a6509666fd49..639cba7234f3 100644 --- a/drivers/media/platform/msm/npu/npu_mgr.c +++ b/drivers/media/platform/msm/npu/npu_mgr.c @@ -829,8 +829,8 @@ static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) kevt.evt.u.exec_v2_done.exec_result = exe_rsp_pkt->header.status; kevt.evt.u.exec_v2_done.stats_buf_size = stats_size; - kevt.reserved[0] = (uint64_t)network->stats_buf; - kevt.reserved[1] = (uint64_t)network->stats_buf_u; + kevt.stats_buf = network->stats_buf; + kevt.stats_buf_u = network->stats_buf_u; if (npu_queue_event(network->client, &kevt)) pr_err("queue npu event failed\n"); } else { From a3aabaa61df4868cc23e5995f54834c47e700eb8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 22 Jul 2021 10:30:58 -0700 Subject: [PATCH 57/71] f2fs: don't sleep while grabing nat_tree_lock This tries to fix priority inversion in the below condition resulting in long checkpoint delay. f2fs_get_node_info() - nat_tree_lock -> sleep to grab journal_rwsem by contention checkpoint - waiting for nat_tree_lock In order to let checkpoint go, let's release nat_tree_lock, if there's a journal_rwsem contention. Change-Id: I72ca6cf52908a08976c200be32a09fb3105e726e Signed-off-by: Daeho Jeong Signed-off-by: Jaegeuk Kim Git-commit: 2eeb0dce728a7eac3e4dfe355d98af40d61f7a26 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git Signed-off-by: Sayali Lokhande --- fs/f2fs/node.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 23d82d022e26..8a0658b07fa6 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -538,7 +538,7 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid, int i; ni->nid = nid; - +retry: /* Check nat cache */ down_read(&nm_i->nat_tree_lock); e = __lookup_nat_cache(nm_i, nid); @@ -550,10 +550,19 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid, return 0; } - memset(&ne, 0, sizeof(struct f2fs_nat_entry)); + /* + * Check current segment summary by trying to grab journal_rwsem first. + * This sem is on the critical path on the checkpoint requiring the above + * nat_tree_lock. Therefore, we should retry, if we failed to grab here + * while not bothering checkpoint. + */ + if (!rwsem_is_locked(&sbi->cp_global_sem)) { + down_read(&curseg->journal_rwsem); + } else if (!down_read_trylock(&curseg->journal_rwsem)) { + up_read(&nm_i->nat_tree_lock); + goto retry; + } - /* Check current segment summary */ - down_read(&curseg->journal_rwsem); i = f2fs_lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0); if (i >= 0) { ne = nat_in_journal(journal, i); From f5f85bb96c4c19b0da3bc8f5b695e150100bdcd0 Mon Sep 17 00:00:00 2001 From: Pratham Pratap Date: Tue, 14 Dec 2021 16:10:13 +0530 Subject: [PATCH 58/71] usb: dwc: Release wakeup source if vbus_active is cleared Commit 980a25b5c2b7 ("usb: dwc3: Dont release wakeup source during usb restart") introduced a check in dwc3_msm_suspend for in_restart before releasing the wakeup source to ensure that driver doesn't release the wakeup source if suspend is kicked while restart session was executing. Now consider a case where, due to some reason, erratic event is seen on the controller and restart session is kicked. While restart work was running, physical cable disconnect happened which will call suspend routine eventually but due to in_restart flag being set driver will not release the wakeup source and will end up consuming power even though cable got disconnected. Fix this by adding one more level of judgement of vbus_active to ensure that wakeup source is released when cable gets disconnected during restart usb work. Change-Id: Ie54618640d08c4f2e3f0dc1d38260e0aa2d309c2 Signed-off-by: Pratham Pratap --- drivers/usb/dwc3/dwc3-msm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 959700da4498..48e27d4c6f29 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -2324,6 +2324,10 @@ static void dwc3_restart_usb_work(struct work_struct *w) dwc->err_evt_seen = false; flush_delayed_work(&mdwc->sm_work); + + /* see comments in dwc3_msm_suspend */ + if (!mdwc->vbus_active) + pm_relax(mdwc->dev); } /* @@ -3375,6 +3379,12 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool force_power_collapse, dwc3_msm_update_bus_bw(mdwc, BUS_VOTE_NONE); + /* + * If in_restart is marked as true from restart work do not release the wakeup + * active source as it can lead the device to enter system suspend (if usb is + * the last holding the wakeup active source). If actual cable disconnect happens + * while in_restart is true wakeup active source will be released from restart work. + */ if (!mdwc->in_restart) { /* * release wakeup source with timeout to defer system suspend to From eb096485ea1e8a648a38570fb7881c01834127c9 Mon Sep 17 00:00:00 2001 From: Liangwei Dong Date: Fri, 3 Dec 2021 12:05:25 +0800 Subject: [PATCH 59/71] defconfig: sdxlemur: Enable SHA512 crypto in kernel Enable SHA512 crypto in kernel. Fix typo of CTR config flags. Change-Id: I168d331ddc526c58f652a228ee11c0e38b1170b0 Signed-off-by: Liangwei Dong --- arch/arm/configs/vendor/sdxlemur.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/vendor/sdxlemur.config b/arch/arm/configs/vendor/sdxlemur.config index 249917a22b04..679c473c6feb 100644 --- a/arch/arm/configs/vendor/sdxlemur.config +++ b/arch/arm/configs/vendor/sdxlemur.config @@ -208,10 +208,11 @@ CONFIG_USB_LINK_LAYER_TEST=y CONFIG_USB_GADGET_VBUS_DRAW=900 CONFIG_SPMI=y CONFIG_HW_RANDOM_MSM_LEGACY=y +CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y CONFIG_CRYPTO_DEV_QCRYPTO=y CONFIG_CRYPTO_DEV_QCEDEV=y -CONIFG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTR=y CONFIG_MFD_I2C_PMIC=y CONFIG_MFD_SPMI_PMIC=y CONFIG_SPMI_MSM_PMIC_ARB=y From 77f9dec046450f17a67604fdfdb2949e3013c068 Mon Sep 17 00:00:00 2001 From: Jilai Wang Date: Thu, 9 Dec 2021 13:58:03 -0500 Subject: [PATCH 60/71] msm: npu: remove asynchronous network execution support Remove asynchronous network execution related code since it's not used. Change-Id: I9e9b54fddbbe9a0a1c0721983ae65e464fd49c0f Signed-off-by: Jilai Wang --- drivers/media/platform/msm/npu/npu_common.h | 10 --- drivers/media/platform/msm/npu/npu_dev.c | 86 ------------------- drivers/media/platform/msm/npu/npu_mgr.c | 92 +++------------------ 3 files changed, 12 insertions(+), 176 deletions(-) diff --git a/drivers/media/platform/msm/npu/npu_common.h b/drivers/media/platform/msm/npu/npu_common.h index 2d82b4236972..18d896c98e96 100644 --- a/drivers/media/platform/msm/npu/npu_common.h +++ b/drivers/media/platform/msm/npu/npu_common.h @@ -243,19 +243,9 @@ struct npu_device { uint32_t hw_version; }; -struct npu_kevent { - struct list_head list; - struct msm_npu_event evt; - void *stats_buf; - void __user *stats_buf_u; -}; - struct npu_client { struct npu_device *npu_dev; - wait_queue_head_t wait; - struct mutex list_lock; - struct list_head evt_list; struct list_head mapped_buffer_list; }; diff --git a/drivers/media/platform/msm/npu/npu_dev.c b/drivers/media/platform/msm/npu/npu_dev.c index 319f5fee5516..e5b2d93d9db0 100644 --- a/drivers/media/platform/msm/npu/npu_dev.c +++ b/drivers/media/platform/msm/npu/npu_dev.c @@ -102,8 +102,6 @@ static int npu_exec_network(struct npu_client *client, unsigned long arg); static int npu_exec_network_v2(struct npu_client *client, unsigned long arg); -static int npu_receive_event(struct npu_client *client, - unsigned long arg); static int npu_set_fw_state(struct npu_client *client, uint32_t enable); static int npu_set_property(struct npu_client *client, unsigned long arg); @@ -111,7 +109,6 @@ static int npu_get_property(struct npu_client *client, unsigned long arg); static long npu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -static __poll_t npu_poll(struct file *filp, struct poll_table_struct *p); static int npu_parse_dt_clock(struct npu_device *npu_dev); static int npu_parse_dt_regulator(struct npu_device *npu_dev); static int npu_of_parse_pwrlevels(struct npu_device *npu_dev, @@ -223,7 +220,6 @@ static const struct file_operations npu_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = npu_ioctl, #endif - .poll = npu_poll, }; static const struct thermal_cooling_device_ops npu_cooling_ops = { @@ -1215,9 +1211,7 @@ static int npu_open(struct inode *inode, struct file *file) return -ENOMEM; client->npu_dev = npu_dev; - init_waitqueue_head(&client->wait); mutex_init(&client->list_lock); - INIT_LIST_HEAD(&client->evt_list); INIT_LIST_HEAD(&(client->mapped_buffer_list)); file->private_data = client; @@ -1227,17 +1221,8 @@ static int npu_open(struct inode *inode, struct file *file) static int npu_close(struct inode *inode, struct file *file) { struct npu_client *client = file->private_data; - struct npu_kevent *kevent; npu_host_cleanup_networks(client); - - while (!list_empty(&client->evt_list)) { - kevent = list_first_entry(&client->evt_list, - struct npu_kevent, list); - list_del(&kevent->list); - kfree(kevent); - } - mutex_destroy(&client->list_lock); kfree(client); return 0; @@ -1559,57 +1544,6 @@ static int npu_exec_network_v2(struct npu_client *client, return ret; } -static int npu_process_kevent(struct npu_kevent *kevt) -{ - int ret = 0; - - switch (kevt->evt.type) { - case MSM_NPU_EVENT_TYPE_EXEC_V2_DONE: - ret = copy_to_user(kevt->stats_buf_u, - kevt->stats_buf, - kevt->evt.u.exec_v2_done.stats_buf_size); - if (ret) { - pr_err("fail to copy to user\n"); - kevt->evt.u.exec_v2_done.stats_buf_size = 0; - ret = -EFAULT; - } - break; - default: - break; - } - - return ret; -} - -static int npu_receive_event(struct npu_client *client, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - struct npu_kevent *kevt; - int ret = 0; - - mutex_lock(&client->list_lock); - if (list_empty(&client->evt_list)) { - pr_err("event list is empty\n"); - ret = -EINVAL; - } else { - kevt = list_first_entry(&client->evt_list, - struct npu_kevent, list); - list_del(&kevt->list); - npu_process_kevent(kevt); - ret = copy_to_user(argp, &kevt->evt, - sizeof(struct msm_npu_event)); - if (ret) { - pr_err("fail to copy to user\n"); - ret = -EFAULT; - } - kfree(kevt); - } - mutex_unlock(&client->list_lock); - - return ret; -} - static int npu_set_fw_state(struct npu_client *client, uint32_t enable) { struct npu_device *npu_dev = client->npu_dev; @@ -1761,9 +1695,6 @@ static long npu_ioctl(struct file *file, unsigned int cmd, case MSM_NPU_EXEC_NETWORK_V2: ret = npu_exec_network_v2(client, arg); break; - case MSM_NPU_RECEIVE_EVENT: - ret = npu_receive_event(client, arg); - break; case MSM_NPU_SET_PROP: ret = npu_set_property(client, arg); break; @@ -1777,23 +1708,6 @@ static long npu_ioctl(struct file *file, unsigned int cmd, return ret; } -static __poll_t npu_poll(struct file *filp, struct poll_table_struct *p) -{ - struct npu_client *client = filp->private_data; - __poll_t mask = 0; - - poll_wait(filp, &client->wait, p); - - mutex_lock(&client->list_lock); - if (!list_empty(&client->evt_list)) { - pr_debug("poll cmd done\n"); - mask = EPOLLIN | EPOLLRDNORM; - } - mutex_unlock(&client->list_lock); - - return mask; -} - /* ------------------------------------------------------------------------- * Device Tree Parsing * ------------------------------------------------------------------------- diff --git a/drivers/media/platform/msm/npu/npu_mgr.c b/drivers/media/platform/msm/npu/npu_mgr.c index 639cba7234f3..ca3a37288e57 100644 --- a/drivers/media/platform/msm/npu/npu_mgr.c +++ b/drivers/media/platform/msm/npu/npu_mgr.c @@ -53,10 +53,9 @@ static void host_session_msg_hdlr(struct npu_device *npu_dev); static void host_session_log_hdlr(struct npu_device *npu_dev); static int host_error_hdlr(struct npu_device *npu_dev, bool force); static int npu_send_network_cmd(struct npu_device *npu_dev, - struct npu_network *network, void *cmd_ptr, bool async); + struct npu_network *network, void *cmd_ptr); static int npu_send_misc_cmd(struct npu_device *npu_dev, uint32_t q_idx, void *cmd_ptr); -static int npu_queue_event(struct npu_client *client, struct npu_kevent *evt); static int npu_notify_dsp(struct npu_device *npu_dev, bool pwr_up); static int npu_notify_aop(struct npu_device *npu_dev, bool on); static int update_dcvs_activity(struct npu_device *npu_dev, uint32_t activity); @@ -349,7 +348,6 @@ static int host_error_hdlr(struct npu_device *npu_dev, bool force) { struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; struct npu_network *network = NULL; - struct npu_kevent kevt; int i; if ((host_ctx->wdg_irq_sts == 0) && (host_ctx->err_irq_sts == 0) @@ -370,18 +368,9 @@ static int host_error_hdlr(struct npu_device *npu_dev, bool force) if (network->is_valid && network->cmd_pending && network->fw_error) { network->cmd_pending = false; - if (network->cmd_async) { - pr_debug("async cmd, queue ssr event\n"); - kevt.evt.type = MSM_NPU_EVENT_TYPE_SSR; - kevt.evt.u.ssr.network_hdl = - network->network_hdl; - if (npu_queue_event(network->client, &kevt)) - pr_err("queue npu event failed\n"); - } else { - pr_debug("complete network %llx\n", - network->id); - complete(&network->cmd_done); - } + pr_debug("complete network %llx\n", + network->id); + complete(&network->cmd_done); } } host_ctx->misc_pending = false; @@ -702,31 +691,13 @@ static void free_network(struct npu_host_ctx *ctx, struct npu_client *client, * Function Definitions - IPC * ------------------------------------------------------------------------- */ -static int npu_queue_event(struct npu_client *client, struct npu_kevent *evt) -{ - struct npu_kevent *kevt = kmalloc(sizeof(*kevt), GFP_KERNEL); - - if (!kevt) - return -ENOMEM; - - *kevt = *evt; - INIT_LIST_HEAD(&kevt->list); - mutex_lock(&client->list_lock); - list_add_tail(&kevt->list, &client->evt_list); - mutex_unlock(&client->list_lock); - wake_up_interruptible(&client->wait); - - return 0; -} static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) { uint32_t msg_id; struct npu_network *network = NULL; - struct npu_kevent kevt; struct npu_device *npu_dev = host_ctx->npu_dev; - memset(&kevt, 0, sizeof(kevt)); msg_id = msg[1]; switch (msg_id) { case NPU_IPC_MSG_EXECUTE_DONE: @@ -765,18 +736,7 @@ static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) network->cmd_pending = false; network->cmd_ret_status = exe_rsp_pkt->header.status; - if (!network->cmd_async) { - complete(&network->cmd_done); - } else { - pr_debug("async cmd, queue event\n"); - kevt.evt.type = MSM_NPU_EVENT_TYPE_EXEC_DONE; - kevt.evt.u.exec_done.network_hdl = - exe_rsp_pkt->network_hdl; - kevt.evt.u.exec_done.exec_result = - exe_rsp_pkt->header.status; - if (npu_queue_event(network->client, &kevt)) - pr_err("queue npu event failed\n"); - } + complete(&network->cmd_done); network_put(network); break; @@ -820,22 +780,7 @@ static void app_msg_proc(struct npu_host_ctx *host_ctx, uint32_t *msg) network->stats_buf_size = stats_size; network->cmd_pending = false; network->cmd_ret_status = exe_rsp_pkt->header.status; - - if (network->cmd_async) { - pr_debug("async cmd, queue event\n"); - kevt.evt.type = MSM_NPU_EVENT_TYPE_EXEC_V2_DONE; - kevt.evt.u.exec_v2_done.network_hdl = - exe_rsp_pkt->network_hdl; - kevt.evt.u.exec_v2_done.exec_result = - exe_rsp_pkt->header.status; - kevt.evt.u.exec_v2_done.stats_buf_size = stats_size; - kevt.stats_buf = network->stats_buf; - kevt.stats_buf_u = network->stats_buf_u; - if (npu_queue_event(network->client, &kevt)) - pr_err("queue npu event failed\n"); - } else { - complete(&network->cmd_done); - } + complete(&network->cmd_done); network_put(network); break; } @@ -1131,7 +1076,7 @@ int32_t npu_host_unmap_buf(struct npu_client *client, } static int npu_send_network_cmd(struct npu_device *npu_dev, - struct npu_network *network, void *cmd_ptr, bool async) + struct npu_network *network, void *cmd_ptr) { struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; int ret = 0; @@ -1147,7 +1092,6 @@ static int npu_send_network_cmd(struct npu_device *npu_dev, pr_debug("Send cmd %d network id %lld\n", ((struct ipc_cmd_header_pkt *)cmd_ptr)->cmd_type, network->id); - network->cmd_async = async; network->cmd_ret_status = 0; network->cmd_pending = true; network->trans_id = ((struct ipc_cmd_header_pkt *)cmd_ptr)->trans_id; @@ -1504,7 +1448,7 @@ int32_t npu_host_load_network(struct npu_client *client, set_perf_mode(npu_dev); /* NPU_IPC_CMD_LOAD will go onto IPC_QUEUE_APPS_EXEC */ reinit_completion(&network->cmd_done); - ret = npu_send_network_cmd(npu_dev, network, &load_packet, false); + ret = npu_send_network_cmd(npu_dev, network, &load_packet); if (ret) { pr_err("NPU_IPC_CMD_LOAD sent failed: %d\n", ret); goto error_free_network; @@ -1622,7 +1566,7 @@ int32_t npu_host_load_network_v2(struct npu_client *client, set_perf_mode(npu_dev); /* NPU_IPC_CMD_LOAD_V2 will go onto IPC_QUEUE_APPS_EXEC */ reinit_completion(&network->cmd_done); - ret = npu_send_network_cmd(npu_dev, network, load_packet, false); + ret = npu_send_network_cmd(npu_dev, network, load_packet); if (ret) { pr_debug("NPU_IPC_CMD_LOAD_V2 sent failed: %d\n", ret); goto error_free_network; @@ -1714,7 +1658,7 @@ int32_t npu_host_unload_network(struct npu_client *client, retry: /* NPU_IPC_CMD_UNLOAD will go onto IPC_QUEUE_APPS_EXEC */ reinit_completion(&network->cmd_done); - ret = npu_send_network_cmd(npu_dev, network, &unload_packet, false); + ret = npu_send_network_cmd(npu_dev, network, &unload_packet); if (ret) { pr_err("NPU_IPC_CMD_UNLOAD sent failed: %d\n", ret); @@ -1791,7 +1735,6 @@ int32_t npu_host_exec_network(struct npu_client *client, int32_t ret; struct npu_network *network; struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; - bool async_ioctl = !!exec_ioctl->async; mutex_lock(&host_ctx->lock); network = get_network_by_hdl(host_ctx, client, @@ -1855,18 +1798,13 @@ int32_t npu_host_exec_network(struct npu_client *client, /* Send it on the high priority queue */ reinit_completion(&network->cmd_done); - ret = npu_send_network_cmd(npu_dev, network, &exec_packet, async_ioctl); + ret = npu_send_network_cmd(npu_dev, network, &exec_packet); if (ret) { pr_err("NPU_IPC_CMD_EXECUTE sent failed: %d\n", ret); goto exec_done; } - if (async_ioctl) { - pr_debug("Async ioctl, return now\n"); - goto exec_done; - } - mutex_unlock(&host_ctx->lock); ret = wait_for_completion_timeout( @@ -1921,7 +1859,6 @@ int32_t npu_host_exec_network_v2(struct npu_client *client, struct npu_network *network; struct npu_host_ctx *host_ctx = &npu_dev->host_ctx; uint32_t num_patch_params, pkt_size; - bool async_ioctl = !!exec_ioctl->async; int i; mutex_lock(&host_ctx->lock); @@ -1994,18 +1931,13 @@ int32_t npu_host_exec_network_v2(struct npu_client *client, /* Send it on the high priority queue */ reinit_completion(&network->cmd_done); - ret = npu_send_network_cmd(npu_dev, network, exec_packet, async_ioctl); + ret = npu_send_network_cmd(npu_dev, network, exec_packet); if (ret) { pr_err("NPU_IPC_CMD_EXECUTE_V2 sent failed: %d\n", ret); goto free_exec_packet; } - if (async_ioctl) { - pr_debug("Async ioctl, return now\n"); - goto free_exec_packet; - } - mutex_unlock(&host_ctx->lock); ret = wait_for_completion_timeout( From 7657f700dcc13c6def837858c7ed002cab0922bb Mon Sep 17 00:00:00 2001 From: Karthik Rudrapatna Date: Wed, 10 Nov 2021 12:15:01 -0800 Subject: [PATCH 61/71] net: macsec: Clones skb when netdev is in Primiscous mode When bridge0 is in Primiscous mode, passed the packets to macsec Change-Id: Ib325e0bb19cee792a8dd3759a4e752581e48827b Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 75801f526b4c..ca15e11b1bae 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -997,15 +997,26 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) * the SecTAG, so we have to deduce which port to deliver to. */ if (macsec_is_offloaded(macsec) && netif_running(ndev)) { - if (ether_addr_equal_64bits(hdr->h_dest, + if (hdr->h_proto == htons(ETH_P_PAE)) + continue; + + if (ndev->flags & IFF_PROMISC) { + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + break; + + count_rx(ndev, nskb->len); + nskb->dev = ndev; + netif_rx(nskb); + } else if (ether_addr_equal_64bits(hdr->h_dest, ndev->dev_addr)) { /* exact match, divert skb to this port */ skb->dev = ndev; skb->pkt_type = PACKET_HOST; + count_rx(ndev, skb->len); ret = RX_HANDLER_ANOTHER; goto out; - } else if (is_multicast_ether_addr_64bits( - hdr->h_dest)) { + } else if (is_multicast_ether_addr_64bits(hdr->h_dest)) { /* multicast frame, deliver on this port too */ nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) @@ -1018,6 +1029,7 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) else nskb->pkt_type = PACKET_MULTICAST; + count_rx(ndev, nskb->len); netif_rx(nskb); } continue; @@ -3353,6 +3365,14 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, return dev_queue_xmit(skb); } + if (macsec_is_offloaded(netdev_priv(dev))) { + skb->dev = macsec->real_dev; + ret = dev_queue_xmit(skb); + len = skb->len; + count_tx(dev, ret, len); + return ret; + } + /* 10.5 */ if (!secy->protect_frames) { secy_stats = this_cpu_ptr(macsec->stats); From c22e096303fa2040a802bae22a569dc4a3148517 Mon Sep 17 00:00:00 2001 From: Ashok Gummadidala Date: Wed, 8 Dec 2021 19:43:21 +0530 Subject: [PATCH 62/71] haven: hh_msgq: Use GFP_ATOMIC allocations in hh_msgq_alloc_entry() Commit 81d6b86bd96e ("haven: hh_msgq: Disallow multiple registrations with same label") makes the entire gh_msgq_alloc_entry() function a critical section by calling it with the gh_msgq_cap_list_lock spinlock held. It also changed the allocation flags of one of the three memory allocation operations from GFP_KERNEL to GFP_ATOMIC for this reason, leaving the other two intact. This sometimes leads to the following: [ 0.052850][ T1] BUG: sleeping function called from invalid context at mm/slab.h:557 [ 0.052852][ T1] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1, name: swapper/0 [ 0.052854][ T1] Preemption disabled at: [ 0.052861][ T1] [] gh_msgq_populate_cap_info+0x3c/0x26c [ 0.052864][ T1] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.10.43-debug-04611-g00783572e88c-dirty #1 [ 0.052866][ T1] Hardware name: Qualcomm Technologies, Inc. Waipio SVM MTP (DT) [ 0.052868][ T1] Call trace: [ 0.052873][ T1] dump_backtrace+0x0/0x20c [ 0.052875][ T1] show_stack+0x18/0x24 [ 0.052880][ T1] dump_stack_lvl+0xbc/0x134 [ 0.052883][ T1] ___might_sleep+0x16c/0x1c0 [ 0.052884][ T1] __might_sleep+0x50/0x88 [ 0.052887][ T1] __kmalloc_track_caller+0x6c/0x388 [ 0.052891][ T1] kvasprintf+0xa8/0x134 [ 0.052892][ T1] kasprintf+0x54/0x7c [ 0.052894][ T1] hh_msgq_alloc_entry+0x98/0x128 [ 0.052896][ T1] hh_msgq_populate_cap_info+0x74/0x26c [ 0.052897][ T1] hh_msgq_probe_direction+0x78/0xf0 [ 0.052899][ T1] hh_msgq_probe+0x4c/0xdc [ 0.052901][ T1] hh_rm_drv_probe+0x34/0x310 [ 0.052904][ T1] platform_drv_probe+0x3c/0x9c [ 0.052906][ T1] really_probe+0x11c/0x4c8 [ 0.052909][ T1] driver_probe_device+0x84/0xec [ 0.052911][ T1] device_driver_attach+0x4c/0x70 [ 0.052913][ T1] __driver_attach.llvm.3781602661191075997+0x40/0x154 [ 0.052915][ T1] bus_for_each_dev+0x7c/0xc8 [ 0.052917][ T1] bus_add_driver+0x110/0x20c [ 0.052919][ T1] driver_register+0x64/0x104 [ 0.052921][ T1] __platform_driver_register+0x44/0x50 [ 0.052926][ T1] __initstub__kmod_gh_rm_drv__334_1315_gh_rm_driver_init6+0x1c/0x28 [ 0.052928][ T1] do_one_initcall+0x50/0x29c [ 0.052931][ T1] do_initcall_level+0xa4/0x16c [ 0.052933][ T1] do_initcalls+0x60/0xa0 [ 0.052934][ T1] do_basic_setup+0x20/0x2c [ 0.052936][ T1] kernel_init_freeable+0x8c/0xdc [ 0.052939][ T1] kernel_init+0x14/0x110 [ 0.052941][ T1] ret_from_fork+0x10/0x30 Even though the error signature says "BUG:" it isn't really a kernel panic. It only dumps the stack and then carries on, courtesy CONFIG_DEBUG_ATOMIC_SLEEP. Change the remaining two memory allocation operations to use GFP_ATOMIC as well and thereby fix the appearance of this error message. Change-Id: If7f91e67541b1e925313eeb96f58cc4e2280dba2 Signed-off-by: Ashok Gummadidala --- drivers/virt/haven/hh_msgq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/virt/haven/hh_msgq.c b/drivers/virt/haven/hh_msgq.c index 0f19aa8468af..fc945f681d16 100644 --- a/drivers/virt/haven/hh_msgq.c +++ b/drivers/virt/haven/hh_msgq.c @@ -561,14 +561,14 @@ static int __init hh_msgq_init(void) spin_lock_init(&cap_table_entry->rx_lock); spin_lock_init(&cap_table_entry->cap_entry_lock); - cap_table_entry->tx_irq_name = kasprintf(GFP_KERNEL, + cap_table_entry->tx_irq_name = kasprintf(GFP_ATOMIC, "hh_msgq_tx_%d", i); if (!cap_table_entry->tx_irq_name) { ret = -ENOMEM; goto err; } - cap_table_entry->rx_irq_name = kasprintf(GFP_KERNEL, + cap_table_entry->rx_irq_name = kasprintf(GFP_ATOMIC, "hh_msgq_rx_%d", i); if (!cap_table_entry->rx_irq_name) { ret = -ENOMEM; From 8c194abb0ec8b48a84bb3a404169cc6fc4841c01 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 27 Sep 2021 15:33:04 +0800 Subject: [PATCH 63/71] input: qcom-hv-haptics: delay hBoost turning off Extend hBoost PBS control to all play modes, so hBoost disabling will be always handled in the driver by triggering the PBS. Also, delay hBoost turning off 2 seconds after the stop command to prevent hBoost being enabled/disabled too frequently in repeated short vibration case, this help to avoid hBoost lockup when it's enabled and disabled very quickly. Change-Id: I76263e51ad00a01d95ff7fd7b08c1655031516e6 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 61 ++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 4c8a8ca6a147..337b62f3998c 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -511,6 +512,7 @@ struct haptics_chip { struct device_node *pbs_node; struct class hap_class; struct regulator *hpwr_vreg; + struct hrtimer hbst_off_timer; int fifo_empty_irq; u32 hpwr_voltage_mv; u32 effects_count; @@ -529,6 +531,7 @@ struct haptics_chip { bool clamp_at_5v; bool hpwr_vreg_enabled; bool is_hv_haptics; + bool hboost_enabled; }; struct haptics_reg_info { @@ -1292,6 +1295,9 @@ static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) return 0; } + if (chip->hboost_enabled == en) + return 0; + val = en ? HAP_VREG_ON_VAL : HAP_VREG_OFF_VAL; rc = nvmem_device_write(chip->hap_cfg_nvmem, PBS_ARG_REG, 1, &val); @@ -1304,11 +1310,14 @@ static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) val = PBS_TRIG_SET_VAL; rc = nvmem_device_write(chip->hap_cfg_nvmem, PBS_TRIG_SET_REG, 1, &val); - if (rc < 0) + if (rc < 0) { dev_err(chip->dev, "Write SDAM %#x failed, rc=%d\n", PBS_TRIG_SET_REG, rc); + return rc; + } - return rc; + chip->hboost_enabled = en; + return 0; } static bool is_swr_play_enabled(struct haptics_chip *chip) @@ -1357,6 +1366,14 @@ static int haptics_wait_hboost_ready(struct haptics_chip *chip) if (is_haptics_external_powered(chip)) return 0; + + if ((hrtimer_get_remaining(&chip->hbst_off_timer) > 0) || + hrtimer_active(&chip->hbst_off_timer)) { + hrtimer_cancel(&chip->hbst_off_timer); + dev_dbg(chip->dev, "hboost is still on, ignore\n"); + return 0; + } + /* * Wait ~20ms until hBoost is ready, otherwise * bail out and return -EBUSY @@ -1493,6 +1510,7 @@ static int haptics_open_loop_drive_config(struct haptics_chip *chip, bool en) return rc; } +#define BOOST_VREG_OFF_DELAY_SECONDS 2 static int haptics_enable_play(struct haptics_chip *chip, bool en) { struct haptics_play_info *play = &chip->play; @@ -1526,11 +1544,17 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) return rc; } - if (play->pattern_src == FIFO) { - rc = haptics_boost_vreg_enable(chip, en); - if (rc < 0) - dev_err(chip->dev, "Notify vreg %s failed, rc=%d\n", - en ? "enabling" : "disabling", rc); + if (en) { + rc = haptics_boost_vreg_enable(chip, true); + if (rc < 0) { + dev_err(chip->dev, "Keep boost vreg on failed, rc=%d\n", + rc); + return rc; + } + } else { + hrtimer_start(&chip->hbst_off_timer, + ktime_set(BOOST_VREG_OFF_DELAY_SECONDS, 0), + HRTIMER_MODE_REL); } return rc; @@ -4582,6 +4606,21 @@ static bool is_swr_supported(struct haptics_chip *chip) return true; } +static enum hrtimer_restart haptics_disable_hbst_timer(struct hrtimer *timer) +{ + struct haptics_chip *chip = container_of(timer, + struct haptics_chip, hbst_off_timer); + int rc; + + rc = haptics_boost_vreg_enable(chip, false); + if (rc < 0) + dev_err(chip->dev, "disable boost vreg failed, rc=%d\n", rc); + else + dev_dbg(chip->dev, "boost vreg is disabled\n"); + + return HRTIMER_NORESTART; +} + static int haptics_probe(struct platform_device *pdev) { struct haptics_chip *chip; @@ -4646,6 +4685,8 @@ static int haptics_probe(struct platform_device *pdev) mutex_init(&chip->play.lock); disable_irq_nosync(chip->fifo_empty_irq); chip->fifo_empty_irq_en = false; + hrtimer_init(&chip->hbst_off_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + chip->hbst_off_timer.function = haptics_disable_hbst_timer; atomic_set(&chip->play.fifo_status.is_busy, 0); atomic_set(&chip->play.fifo_status.written_done, 0); @@ -4755,6 +4796,12 @@ static int haptics_suspend(struct device *dev) } mutex_unlock(&play->lock); + /* + * Cancel the hBoost turning off timer and disable + * hBoost if it's still enabled + */ + hrtimer_cancel(&chip->hbst_off_timer); + haptics_boost_vreg_enable(chip, false); rc = haptics_enable_hpwr_vreg(chip, false); if (rc < 0) return rc; From a00bf2b8d6eea47b4869ee3f049e2f5511dd5c85 Mon Sep 17 00:00:00 2001 From: Mark Starovoytov Date: Wed, 25 Mar 2020 16:01:34 +0300 Subject: [PATCH 64/71] net: macsec: add support for specifying offload upon link creation This patch adds new netlink attribute to allow a user to (optionally) specify the desired offload mode immediately upon MACSec link creation. Separate iproute patch will be required to support this from user space. Change-Id: I3aa8c9c2eb65763707487267a23ea2e645d1afde Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: 791bb3fcafcedd11f9066da9fee9342ecb6904d0 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Karthik Rudrapatna --- drivers/net/macsec.c | 31 ++++++++++++++++++++++++++++-- include/uapi/linux/if_link.h | 1 + tools/include/uapi/linux/if_link.h | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index ca15e11b1bae..18fe5226e662 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1470,6 +1470,11 @@ static struct net_device *get_dev_from_nl(struct net *net, return dev; } +static enum macsec_offload nla_get_offload(const struct nlattr *nla) +{ + return (__force enum macsec_offload)nla_get_u8(nla); +} + static sci_t nla_get_sci(const struct nlattr *nla) { return (__force sci_t)nla_get_u64(nla); @@ -3995,8 +4000,16 @@ static int macsec_newlink(struct net *net, struct net_device *dev, macsec->real_dev = real_dev; - /* MACsec offloading is off by default */ - macsec->offload = MACSEC_OFFLOAD_OFF; + if (data && data[IFLA_MACSEC_OFFLOAD]) + macsec->offload = nla_get_offload(data[IFLA_MACSEC_OFFLOAD]); + else + /* MACsec offloading is off by default */ + macsec->offload = MACSEC_OFFLOAD_OFF; + + /* Check if the offloading mode is supported by the underlying layers */ + if (macsec->offload != MACSEC_OFFLOAD_OFF && + !macsec_check_offload(macsec->offload, macsec)) + return -EOPNOTSUPP; if (data && data[IFLA_MACSEC_ICV_LEN]) icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]); @@ -4043,6 +4056,20 @@ static int macsec_newlink(struct net *net, struct net_device *dev, goto del_dev; } + /* If h/w offloading is available, propagate to the device */ + if (macsec_is_offloaded(macsec)) { + const struct macsec_ops *ops; + struct macsec_context ctx; + + ops = macsec_get_ops(macsec, &ctx); + if (ops) { + ctx.secy = &macsec->secy; + err = macsec_offload(ops->mdo_add_secy, &ctx); + if (err) + goto del_dev; + } + } + err = register_macsec_dev(real_dev, dev); if (err < 0) goto del_dev; diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 80c9bd8de89f..66e34f0a07fe 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -460,6 +460,7 @@ enum { IFLA_MACSEC_REPLAY_PROTECT, IFLA_MACSEC_VALIDATION, IFLA_MACSEC_PAD, + IFLA_MACSEC_OFFLOAD, __IFLA_MACSEC_MAX, }; diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h index 80c9bd8de89f..66e34f0a07fe 100644 --- a/tools/include/uapi/linux/if_link.h +++ b/tools/include/uapi/linux/if_link.h @@ -460,6 +460,7 @@ enum { IFLA_MACSEC_REPLAY_PROTECT, IFLA_MACSEC_VALIDATION, IFLA_MACSEC_PAD, + IFLA_MACSEC_OFFLOAD, __IFLA_MACSEC_MAX, }; From e9a8ea44931c3a5c89809d00046cb1a0d4a24986 Mon Sep 17 00:00:00 2001 From: Sarthak Garg Date: Thu, 30 Dec 2021 17:40:46 +0530 Subject: [PATCH 65/71] mmc: sdhci: Dump registers in case of DATA CRC/timeout errors Dump registers in case of Data CRC/timeout errors and print other useful information. This contains below patches as well: 4c196de7e0fd mmc: sdhci: rate limit sdhci_dumpregs() prints 16dabee056d4 mmc: sdhci: Add timestamp debug info for data timeout error e00d878df07e mmc: sdhci: Avoid dumping registers when SD card removed. Change-Id: I5efe8ff4bacc5da3a05829be7cac4ce40f9fc584 Signed-off-by: Sarthak Garg --- drivers/mmc/host/sdhci.c | 54 ++++++++++++++++++++++++++++++++++++++-- drivers/mmc/host/sdhci.h | 6 ++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ad0b4b331811..27049955e774 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -43,6 +43,11 @@ #define MAX_TUNING_LOOP 40 +#if defined(CONFIG_SDC_QTI) +#define SDHCI_DBG_DUMP_RS_INTERVAL (10 * HZ) +#define SDHCI_DBG_DUMP_RS_BURST 2 +#endif + static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; @@ -1486,6 +1491,10 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) timeout += 10 * HZ; sdhci_mod_timer(host, cmd->mrq, timeout); +#if defined(CONFIG_SDC_QTI) + if (cmd->data) + host->data_start_time = ktime_get(); +#endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); mmc_log_string(host->mmc, "updated ARGUMENT=0x%08x ARGUMENT_MODE=0x%08x COMMAND=0x%08x\n", @@ -3127,6 +3136,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { u32 command; +#if defined(CONFIG_SDC_QTI) + bool pr_msg = false; +#endif + /* CMD19 generates _only_ Buffer Read Ready interrupt */ if (intmask & SDHCI_INT_DATA_AVAIL) { command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)); @@ -3217,9 +3230,41 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) host->ops->adma_workaround(host, intmask); } - if (host->data->error) + if (host->data->error) { +#if defined(CONFIG_SDC_QTI) + if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT + | SDHCI_INT_DATA_END_BIT)) { + command = SDHCI_GET_CMD(sdhci_readw(host, + SDHCI_COMMAND)); + if ((command != MMC_SEND_TUNING_BLOCK_HS200) && + (command != MMC_SEND_TUNING_BLOCK)) + pr_msg = true; + } else { + pr_msg = true; + } + + if (host->mmc->ops->get_cd && + !host->mmc->ops->get_cd(host->mmc)) { + pr_msg = false; + pr_err("%s: Got data error(%d) during card removal\n", + mmc_hostname(host->mmc), host->data->error); + } + + if (pr_msg && __ratelimit(&host->dbg_dump_rs)) { + pr_err("%s: data txfr (0x%08x) error: %d after %lld ms\n", + mmc_hostname(host->mmc), intmask, + host->data->error, ktime_to_ms(ktime_sub( + ktime_get(), host->data_start_time))); + mmc_log_string(host->mmc, + "data txfr (0x%08x) error: %d after %lld ms\n", + intmask, host->data->error, + ktime_to_ms(ktime_sub(ktime_get(), + host->data_start_time))); + sdhci_dumpregs(host); + } +#endif sdhci_finish_data(host); - else { + } else { if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) sdhci_transfer_pio(host); @@ -3830,6 +3875,11 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, */ host->adma_table_cnt = SDHCI_MAX_SEGS * 2 + 1; +#if defined(CONFIG_SDC_QTI) + ratelimit_state_init(&host->dbg_dump_rs, SDHCI_DBG_DUMP_RS_INTERVAL, + SDHCI_DBG_DUMP_RS_BURST); +#endif + return host; } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index af6071b1de3d..7b7fa18a7644 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -15,7 +15,7 @@ #include #include #include - +#include #include /* @@ -616,6 +616,10 @@ struct sdhci_host { u64 data_timeout; +#if defined(CONFIG_SDC_QTI) + ktime_t data_start_time; + struct ratelimit_state dbg_dump_rs; +#endif unsigned long private[0] ____cacheline_aligned; }; From ea6dfe28c66ad3367e4276fa83ae843b19bd8b4b Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Wed, 22 Dec 2021 09:59:43 +0530 Subject: [PATCH 66/71] usb: f_qdss: Remove usb_request::num_mapped_sgs access The function drivers have no business accessing usb_request::num_mapped_sgs field. The UDC will use this field to cache the sgs mapped for a given request. Change-Id: I6a8c8c2e52a36caedfe977f517b4c04b1ad0045c Signed-off-by: Pavankumar Kondeti --- drivers/usb/gadget/function/f_qdss.c | 1 - include/linux/usb/usb_qdss.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index d92ff5dee4a9..4ae473b90280 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -811,7 +811,6 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) req->length = d_req->length; req->sg = d_req->sg; req->num_sgs = d_req->num_sgs; - req->num_mapped_sgs = d_req->num_mapped_sgs; reinit_completion(&qreq->write_done); if (req->sg) qdss_log("%s: req:%pK req->num_sgs:0x%x\n", diff --git a/include/linux/usb/usb_qdss.h b/include/linux/usb/usb_qdss.h index 0fa377c1a7fe..c1a16910b02c 100644 --- a/include/linux/usb/usb_qdss.h +++ b/include/linux/usb/usb_qdss.h @@ -21,7 +21,6 @@ struct qdss_request { void *context; struct scatterlist *sg; unsigned int num_sgs; - unsigned int num_mapped_sgs; }; struct usb_qdss_ch { From cb6bfc80a2de1ac19dc3042568a0b526c7dcfbb9 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Tue, 4 Jan 2022 13:33:48 +0530 Subject: [PATCH 67/71] arm64: defconfig: Default updates for genericarmv8 Due to changes in Kconfig default options, genericarmv8 is out of sync. Sync genericarmv8 defconfig to the latest code. Change-Id: I15d753b9d9429ac623fd7b7e7eaa936b5ed2a8b1 Signed-off-by: Pavankumar Kondeti --- arch/arm64/configs/vendor/genericarmv8-64_defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/configs/vendor/genericarmv8-64_defconfig b/arch/arm64/configs/vendor/genericarmv8-64_defconfig index 5f9e3752543e..2a4b21e494b7 100644 --- a/arch/arm64/configs/vendor/genericarmv8-64_defconfig +++ b/arch/arm64/configs/vendor/genericarmv8-64_defconfig @@ -198,12 +198,10 @@ CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_HID_APPLE is not set # CONFIG_HID_BELKIN is not set # CONFIG_HID_CHERRY is not set -# CONFIG_HID_CHICONY is not set # CONFIG_HID_CYPRESS is not set # CONFIG_HID_EZKEY is not set # CONFIG_HID_ITE is not set # CONFIG_HID_KENSINGTON is not set -# CONFIG_HID_LOGITECH is not set # CONFIG_HID_REDRAGON is not set # CONFIG_HID_MICROSOFT is not set # CONFIG_HID_MONTEREY is not set From 5582182dad3d11cc49503505faca95cea12dd19a Mon Sep 17 00:00:00 2001 From: Satish Kumar Kodishala Date: Wed, 22 Dec 2021 13:41:28 +0530 Subject: [PATCH 68/71] BTFM SLIM:Add support for pin connectivity test Add support for pin connectivity test for BT/FM slimbus Change-Id: Ia913bcb204f6cccc59790b9a4cef20cbd346f05c Signed-off-by: Satish Kumar Kodishala --- drivers/bluetooth/btfm_slim.c | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c index bba66b79fab4..46202a5ef1c0 100644 --- a/drivers/bluetooth/btfm_slim.c +++ b/drivers/bluetooth/btfm_slim.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,8 +22,13 @@ #include "btfm_slim.h" #include "btfm_slim_slave.h" +#define BT_CMD_SLIM_TEST 0xbfac #define DELAY_FOR_PORT_OPEN_MS (200) +struct class *btfm_slim_class; +static int btfm_slim_major; +struct btfmslim *btfm_slim_drv_data; + static bool btfm_is_port_opening_delayed = true; int btfm_slim_write(struct btfmslim *btfmslim, @@ -612,6 +618,25 @@ static int btfm_slim_get_dt_info(struct btfmslim *btfmslim) return ret; } +static long btfm_slim_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + BTFMSLIM_INFO(""); + switch (cmd) { + case BT_CMD_SLIM_TEST: + BTFMSLIM_INFO("cmd BT_CMD_SLIM_TEST, call btfm_slim_hw_init"); + ret = btfm_slim_hw_init(btfm_slim_drv_data); + break; + } + return ret; +} + +static const struct file_operations bt_dev_fops = { + .unlocked_ioctl = btfm_slim_ioctl, + .compat_ioctl = btfm_slim_ioctl, +}; + static int btfm_slim_probe(struct slim_device *slim) { int ret = 0; @@ -668,7 +693,36 @@ static int btfm_slim_probe(struct slim_device *slim) btfm_slim_unregister_codec(&slim->dev); goto free; } + + btfm_slim_drv_data = btfm_slim; + btfm_slim_major = register_chrdev(0, "btfm_slim", &bt_dev_fops); + if (btfm_slim_major < 0) { + BTFMSLIM_ERR("%s: failed to allocate char dev\n", __func__); + ret = -1; + goto register_err; + } + + btfm_slim_class = class_create(THIS_MODULE, "btfmslim-dev"); + if (IS_ERR(btfm_slim_class)) { + BTFMSLIM_ERR("%s: coudn't create class\n", __func__); + ret = -1; + goto class_err; + } + + if (device_create(btfm_slim_class, NULL, MKDEV(btfm_slim_major, 0), + NULL, "btfmslim") == NULL) { + BTFMSLIM_ERR("%s: failed to allocate char dev\n", __func__); + ret = -1; + goto device_err; + } return ret; + +device_err: + class_destroy(btfm_slim_class); +class_err: + unregister_chrdev(btfm_slim_major, "btfm_slim"); +register_err: + btfm_slim_unregister_codec(&slim->dev); free: slim_remove_device(&btfm_slim->slim_ifd); dealloc: From 4925b858acf890af2e73b0aa9b70c671317404ef Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Thu, 16 Dec 2021 16:07:31 +0530 Subject: [PATCH 69/71] usb: f_fs: Fix Double free from ffs_data_clear Suppose if the userspace using ffs failed to open ep0, it will issue a ep0_release and continuously try to do ep0_open until it gets through. The general operation of ep0_release is the it will destroy the epfile and free the structures. Whole thing follows this path: ffs_ep0_release ffs_data_reset ffs_data_clear kfree(epfiles) mark NULL kfree(raw_desc) raw_desc =NULL Now the last few steps of the release process is done without any mutex. In one functions we do kfree and another we mark NULL. This created a potential double free scenario, if a ep0_release process got preempted before kfree, meanwhile another ep0_release gets through and freed up the structures but didn't mark NULL and within that time the preempted process wakes up and tried to kfree again, due to structure not marked NULL will lead to double free/invalid free. Following is the illustration: CPU2 CPU3 ffs_ep0_release ffs_data_reset ffs_data_clear kfree(epfiles) epfiles = NULL --preempted-- ffs_ep0_release ffs_data_reset ffs_data_clear kfree(epfiles) epfiles = NULL kfree(ffs->raw_descs_data) kfree(ffs->raw_strings) kfree(ffs->stringtabs) --woke-up-- kfree(ffs->raw_descs_data) raw_desc =NULL Fix this by performing kfree and NULL operations under ffs_data_clear within a mutex lock. Change-Id: I1c8d92ff99c30165b06bafdd00bc9eb610f3bb76 Signed-off-by: Udipto Goswami --- drivers/usb/gadget/function/f_fs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 6b2237d0bc36..3647616f8d69 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2012,14 +2012,17 @@ static void ffs_data_clear(struct ffs_data *ffs) ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count); ffs->epfiles = NULL; } - mutex_unlock(&ffs->mutex); if (ffs->ffs_eventfd) eventfd_ctx_put(ffs->ffs_eventfd); kfree(ffs->raw_descs_data); + ffs->raw_descs_data = NULL; kfree(ffs->raw_strings); + ffs->raw_strings = NULL; kfree(ffs->stringtabs); + ffs->stringtabs = NULL; + mutex_unlock(&ffs->mutex); } static void ffs_data_reset(struct ffs_data *ffs) @@ -2031,10 +2034,7 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs_data_clear(ffs); - ffs->raw_descs_data = NULL; ffs->raw_descs = NULL; - ffs->raw_strings = NULL; - ffs->stringtabs = NULL; ffs->raw_descs_length = 0; ffs->fs_descs_count = 0; From a3bfceb5aff1c3374c8a163539a2ed6573475ce7 Mon Sep 17 00:00:00 2001 From: Mohammed Siddiq Date: Fri, 7 Jan 2022 11:45:53 +0530 Subject: [PATCH 70/71] cnss2: Add code to address boot timer debug corner case To handle corner case where mhi power on success and the boot debug timer handler execution(i.e dumping register) runs parellel. Even-though the timer is deleted as soon as mhi is powered on, as this handler is still running it is armed again. Use del_timer_sync which makes sure the timer is not queued and the handler is not running upon its exit. Change-Id: Ie428f3e8af8870018dfed99a5472afca14116bb3 Signed-off-by: Mohammed Siddiq --- drivers/net/wireless/cnss2/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 5237a9b55e6a..ccea088d6a95 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -1918,7 +1918,7 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv) jiffies + msecs_to_jiffies(BOOT_DEBUG_TIMEOUT_MS)); ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_ON); - del_timer(&pci_priv->boot_debug_timer); + del_timer_sync(&pci_priv->boot_debug_timer); if (ret == 0) cnss_wlan_adsp_pc_enable(pci_priv, false); From 39ae50f6adb26f1352bc2bb8304beb38750b8cba Mon Sep 17 00:00:00 2001 From: Abhishek Chauhan Date: Thu, 23 Sep 2021 09:41:36 -0700 Subject: [PATCH 71/71] arch: arm: defconfig: sdxlemur: Enable MACSEC config This config enables the MACSEC feature. Change-Id: Ida828ccac6e5bd4ba003bae87157bede10d793ca Signed-off-by: Abhishek Chauhan --- arch/arm/configs/vendor/sdxlemur.config | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/vendor/sdxlemur.config b/arch/arm/configs/vendor/sdxlemur.config index 679c473c6feb..8cdf99ab5a29 100644 --- a/arch/arm/configs/vendor/sdxlemur.config +++ b/arch/arm/configs/vendor/sdxlemur.config @@ -379,3 +379,4 @@ CONFIG_DRM=y CONFIG_GKI_HIDDEN_DRM_CONFIGS=y CONFIG_SDX_EXT_IPC=y # CONFIG_SCHED_DEBUG is not set +CONFIG_MACSEC=y