From 6b26ef81599afa1718f318388f100391a4d27400 Mon Sep 17 00:00:00 2001 From: Arunteja Reddy Gopireddy Date: Tue, 2 Sep 2025 17:32:05 +0530 Subject: [PATCH 1/6] msm: cvp: Fix for kernel address exposure vulnerability to user Driver allocates an object for session structure and then passes this address to user after modifing this address a bit using hash32_ptr function. This function does not hash the address properly and user can retrieve the kernel address back from the hashed value. Change-Id: I8a91a5e67a1019a848051ce7b325be921ace967d Signed-off-by: Arunteja Reddy Gopireddy --- drivers/media/platform/msm/cvp/cvp.c | 6 +- drivers/media/platform/msm/cvp/cvp_hfi.c | 56 +++++++++++++--- .../platform/msm/cvp/hfi_packetization.c | 19 ++++-- .../platform/msm/cvp/hfi_response_handler.c | 4 +- drivers/media/platform/msm/cvp/msm_cvp.c | 66 +++++++++++++++---- drivers/media/platform/msm/cvp/msm_cvp.h | 3 + drivers/media/platform/msm/cvp/msm_cvp_buf.c | 26 ++++---- .../media/platform/msm/cvp/msm_cvp_common.c | 45 ++++++++----- drivers/media/platform/msm/cvp/msm_cvp_core.c | 5 +- .../media/platform/msm/cvp/msm_cvp_internal.h | 4 ++ drivers/media/platform/msm/cvp/msm_cvp_synx.c | 3 +- 11 files changed, 173 insertions(+), 64 deletions(-) diff --git a/drivers/media/platform/msm/cvp/cvp.c b/drivers/media/platform/msm/cvp/cvp.c index 87adb4fbd975..76f7444a43b8 100644 --- a/drivers/media/platform/msm/cvp/cvp.c +++ b/drivers/media/platform/msm/cvp/cvp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -135,6 +135,8 @@ static int msm_cvp_initialize_core(struct platform_device *pdev, INIT_LIST_HEAD(&core->instances); mutex_init(&core->lock); mutex_init(&core->clk_lock); + mutex_init(&core->idr_mtx); + idr_init(&core->sess_idr); core->state = CVP_CORE_UNINIT; for (i = SYS_MSG_INDEX(SYS_MSG_START); @@ -506,6 +508,8 @@ static int msm_cvp_remove(struct platform_device *pdev) msm_cvp_free_platform_resources(&core->resources); sysfs_remove_group(&pdev->dev.kobj, &msm_cvp_core_attr_group); dev_set_drvdata(&pdev->dev, NULL); + idr_destroy(&core->sess_idr); + mutex_destroy(&core->idr_mtx); mutex_destroy(&core->lock); mutex_destroy(&core->clk_lock); kfree(core); diff --git a/drivers/media/platform/msm/cvp/cvp_hfi.c b/drivers/media/platform/msm/cvp/cvp_hfi.c index 22340456d275..df430d2235f7 100644 --- a/drivers/media/platform/msm/cvp/cvp_hfi.c +++ b/drivers/media/platform/msm/cvp/cvp_hfi.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -29,6 +30,7 @@ #include "cvp_hfi_helper.h" #include "cvp_hfi_io.h" #include "msm_cvp_dsp.h" +#include "msm_cvp.h" #define FIRMWARE_SIZE 0X00A00000 #define REG_ADDR_OFFSET_BITMASK 0x000FFFFF @@ -469,6 +471,7 @@ static int __dsp_suspend(struct iris_hfi_device *device, bool force, u32 flags) { int rc; struct cvp_hal_session *temp; + struct msm_cvp_inst *inst = NULL; if (msm_cvp_dsp_disable) return 0; @@ -480,9 +483,10 @@ static int __dsp_suspend(struct iris_hfi_device *device, bool force, u32 flags) /* don't suspend if cvp session is not paused */ if (!(temp->flags & SESSION_PAUSE)) { + inst = (struct msm_cvp_inst *)temp->session_id; dprintk(CVP_DSP, "%s: cvp session %x not paused\n", - __func__, hash32_ptr(temp)); + __func__, inst->sess_id); return -EBUSY; } } @@ -2224,12 +2228,17 @@ static void __session_clean(struct cvp_hal_session *session) { struct cvp_hal_session *temp, *next; struct iris_hfi_device *device; + struct msm_cvp_core *core = NULL; + struct msm_cvp_inst *inst = NULL; + void *tmp = NULL; if (!session || !session->device) { dprintk(CVP_WARN, "%s: invalid params\n", __func__); return; } device = session->device; + core = list_first_entry(&cvp_driver->cores, struct msm_cvp_core, list); + inst = (struct msm_cvp_inst *) session->session_id; dprintk(CVP_SESS, "deleted the session: %pK\n", session); /* * session might have been removed from the device list in @@ -2241,6 +2250,13 @@ static void __session_clean(struct cvp_hal_session *session) break; } } + /* Remove the IDR id assigned to this session */ + mutex_lock(&core->idr_mtx); + tmp = idr_remove(&core->sess_idr, inst->sess_id); + if (tmp != session) + dprintk(CVP_WARN, "%s: session\n", __func__); + mutex_unlock(&core->idr_mtx); + /* Poison the session handle with zeros */ *session = (struct cvp_hal_session){ {0} }; kfree(session); @@ -2278,6 +2294,9 @@ static int iris_hfi_session_init(void *device, void *session_id, struct cvp_hfi_cmd_sys_session_init_packet pkt; struct iris_hfi_device *dev; struct cvp_hal_session *s; + struct msm_cvp_core *core; + struct msm_cvp_inst *inst; + int id = 0; if (!device || !new_session) { dprintk(CVP_ERR, "%s - invalid input\n", __func__); @@ -2285,6 +2304,8 @@ static int iris_hfi_session_init(void *device, void *session_id, } dev = device; + core = list_first_entry(&cvp_driver->cores, struct msm_cvp_core, list); + inst = session_id; mutex_lock(&dev->lock); s = kzalloc(sizeof(*s), GFP_KERNEL); @@ -2295,15 +2316,35 @@ static int iris_hfi_session_init(void *device, void *session_id, s->session_id = session_id; s->device = dev; + + mutex_lock(&core->idr_mtx); + idr_preload(GFP_KERNEL); + + /* Need to think if we can use core->lock or dev->lock or need a + * different new lock for this? + */ + id = idr_alloc(&core->sess_idr, (void *)s, 0x7FFF0000, INT_MAX, GFP_NOWAIT); + idr_preload_end(); + mutex_unlock(&core->idr_mtx); + if (id < 0) { + dprintk(CVP_ERR, + "%s: idr allocation failed for session %pK of inst %pK\n", + __func__, s, session_id); + goto err_session_init_fail; + } + dprintk(CVP_SESS, - "%s: inst %pK, session %pK\n", __func__, session_id, s); + "%s: inst %pK, session %pK, idr_id = 0x%x\n", __func__, session_id, s, id); list_add_tail(&s->list, &dev->sess_head); __set_default_sys_properties(device); + inst->sess_id = id; + if (call_hfi_pkt_op(dev, session_init, &pkt, s)) { dprintk(CVP_ERR, "session_init: failed to create packet\n"); + inst->sess_id = 0x0000DEAD; goto err_session_init_fail; } @@ -2317,6 +2358,7 @@ static int iris_hfi_session_init(void *device, void *session_id, err_session_init_fail: if (s) __session_clean(s); + inst->sess_id = 0; *new_session = NULL; mutex_unlock(&dev->lock); return -EINVAL; @@ -2878,9 +2920,11 @@ static struct cvp_hal_session *__get_session(struct iris_hfi_device *device, u32 session_id) { struct cvp_hal_session *temp = NULL; + struct msm_cvp_inst *inst = NULL; list_for_each_entry(temp, &device->sess_head, list) { - if (session_id == hash32_ptr(temp)) + inst = (struct msm_cvp_inst *)temp->session_id; + if (session_id == inst->sess_id) return temp; } @@ -3059,6 +3103,7 @@ static int __response_handler(struct iris_hfi_device *device) /* Process the packet types that we're interested in */ process_system_msg(info, device, raw_packet); + /* This session_id is a double pointer to the idr_id of session */ session_id = get_session_id(info); /* * hfi_process_msg_packet provides a session_id that's a hashed @@ -3070,11 +3115,6 @@ static int __response_handler(struct iris_hfi_device *device) if (session_id) { struct cvp_hal_session *session = NULL; - if (upper_32_bits((uintptr_t)*session_id) != 0) { - dprintk(CVP_ERR, - "Upper 32-bits != 0 for sess_id=%pK\n", - *session_id); - } session = __get_session(device, (u32)(uintptr_t)*session_id); if (!session) { diff --git a/drivers/media/platform/msm/cvp/hfi_packetization.c b/drivers/media/platform/msm/cvp/hfi_packetization.c index 107e2d744fff..edb6caaf1d34 100644 --- a/drivers/media/platform/msm/cvp/hfi_packetization.c +++ b/drivers/media/platform/msm/cvp/hfi_packetization.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "hfi_packetization.h" @@ -208,7 +209,7 @@ inline int cvp_create_pkt_cmd_sys_session_init( pkt->size = sizeof(struct cvp_hfi_cmd_sys_session_init_packet); pkt->packet_type = HFI_CMD_SYS_SESSION_INIT; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; pkt->session_type = inst->prop.type; pkt->session_kmask = inst->prop.kernel_mask; pkt->session_prio = inst->prop.priority; @@ -266,13 +267,14 @@ int cvp_create_pkt_cmd_session_cmd(struct cvp_hal_session_cmd_pkt *pkt, int pkt_type, struct cvp_hal_session *session) { int rc = 0; + struct msm_cvp_inst *inst = session->session_id; if (!pkt) return -EINVAL; pkt->size = sizeof(struct cvp_hal_session_cmd_pkt); pkt->packet_type = pkt_type; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; return rc; } @@ -305,13 +307,14 @@ int cvp_create_pkt_cmd_session_set_buffers( { int rc = 0; struct cvp_hfi_cmd_session_set_buffers_packet *pkt; + struct msm_cvp_inst *inst = session->session_id; - if (!cmd || !session) + if (!cmd || !session || !inst) return -EINVAL; pkt = (struct cvp_hfi_cmd_session_set_buffers_packet *)cmd; pkt->packet_type = HFI_CMD_SESSION_CVP_SET_BUFFERS; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; pkt->buf_type.iova = iova; pkt->buf_type.size = size; pkt->size = sizeof(struct cvp_hfi_cmd_session_set_buffers_packet); @@ -324,13 +327,14 @@ int cvp_create_pkt_cmd_session_release_buffers( struct cvp_hal_session *session) { struct cvp_session_release_buffers_packet *pkt; + struct msm_cvp_inst *inst = session->session_id; - if (!cmd || !session) + if (!cmd || !session || !inst) return -EINVAL; pkt = (struct cvp_session_release_buffers_packet *)cmd; pkt->packet_type = HFI_CMD_SESSION_CVP_RELEASE_BUFFERS; - pkt->session_id = hash32_ptr(session); + pkt->session_id = inst->sess_id; pkt->num_buffers = 1; pkt->buffer_type = 0; pkt->size = sizeof(struct cvp_session_release_buffers_packet) + @@ -347,6 +351,7 @@ int cvp_create_pkt_cmd_session_send( int def_idx; struct cvp_hal_session_cmd_pkt *ptr = (struct cvp_hal_session_cmd_pkt *)in_pkt; + struct msm_cvp_inst *inst = session->session_id; if (!out_pkt || !in_pkt || !session) return -EINVAL; @@ -354,7 +359,7 @@ int cvp_create_pkt_cmd_session_send( if (ptr->size > MAX_HFI_PKT_SIZE * sizeof(unsigned int)) goto error_hfi_packet; - if (ptr->session_id != hash32_ptr(session)) + if (ptr->session_id != inst->sess_id) goto error_hfi_packet; def_idx = get_pkt_index(ptr); diff --git a/drivers/media/platform/msm/cvp/hfi_response_handler.c b/drivers/media/platform/msm/cvp/hfi_response_handler.c index 311f94106534..3c58c5d90b92 100644 --- a/drivers/media/platform/msm/cvp/hfi_response_handler.c +++ b/drivers/media/platform/msm/cvp/hfi_response_handler.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -462,7 +462,7 @@ static struct msm_cvp_inst *cvp_get_inst_from_id(struct msm_cvp_core *core, retry: if (mutex_trylock(&core->lock)) { list_for_each_entry(inst, &core->instances, list) { - if (hash32_ptr(inst->session) == session_id) { + if (inst->sess_id == session_id) { match = true; break; } diff --git a/drivers/media/platform/msm/cvp/msm_cvp.c b/drivers/media/platform/msm/cvp/msm_cvp.c index fefc06d6aee2..239ceb89acfa 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp.c +++ b/drivers/media/platform/msm/cvp/msm_cvp.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_cvp.h" @@ -14,6 +15,47 @@ struct cvp_power_level { unsigned long bw_sum; }; +void *get_sessObj_from_idr(struct msm_cvp_inst *inst) +{ + void *sessObj = NULL; + struct msm_cvp_core *core = NULL; + + if (!inst || !inst->core) { + dprintk(CVP_ERR, "%s: invalid params\n", __func__); + return NULL; + } + + core = inst->core; + mutex_lock(&core->idr_mtx); + sessObj = idr_find(&core->sess_idr, inst->sess_id); + mutex_unlock(&core->idr_mtx); + if (!sessObj) + dprintk(CVP_ERR, "%s: Could not find the sess obj for given idr id\n", + __func__); + + return sessObj; +} + +u32 get_sessId_from_idr(void *session) +{ + void *ptr = NULL; + u32 sess_id = -1; + struct msm_cvp_core *core = NULL; + + core = list_first_entry(&cvp_driver->cores, struct msm_cvp_core, list); + if (!session || !core) + return -EINVAL; + mutex_lock(&core->idr_mtx); + idr_for_each_entry(&core->sess_idr, ptr, sess_id) { + if (ptr == session) { + mutex_unlock(&core->idr_mtx); + return sess_id; + } + } + mutex_unlock(&core->idr_mtx); + return sess_id; +} + static int msm_cvp_get_session_info(struct msm_cvp_inst *inst, struct cvp_kmd_session_info *session) { @@ -30,7 +72,7 @@ static int msm_cvp_get_session_info(struct msm_cvp_inst *inst, return -ECONNRESET; s->cur_cmd_type = CVP_KMD_GET_SESSION_INFO; - session->session_id = hash32_ptr(inst->session); + session->session_id = inst->sess_id; dprintk(CVP_SESS, "%s: id 0x%x\n", __func__, session->session_id); s->cur_cmd_type = 0; @@ -1227,7 +1269,7 @@ static int msm_cvp_session_stop(struct msm_cvp_inst *inst, sq->state = QUEUE_STOP; pr_info(CVP_DBG_TAG "Stop session: %pK session_id = %d\n", - "sess", inst, hash32_ptr(inst->session)); + "sess", inst, inst->sess_id); spin_unlock(&sq->lock); wake_up_all(&inst->session_queue.wq); @@ -1251,7 +1293,7 @@ int msm_cvp_session_queue_stop(struct msm_cvp_inst *inst) sq->state = QUEUE_STOP; dprintk(CVP_SESS, "Stop session queue: %pK session_id = %d\n", - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); spin_unlock(&sq->lock); wake_up_all(&inst->session_queue.wq); @@ -1551,7 +1593,7 @@ static void cvp_clean_fence_queue(struct msm_cvp_inst *inst, int synx_state) ktid = f->pkt->client_data.kdata & (FENCE_BIT - 1); dprintk(CVP_SYNX, "%s: (%#x) flush frame %llu %llu wait_list\n", - __func__, hash32_ptr(inst->session), ktid, f->frame_id); + __func__, inst->sess_id, ktid, f->frame_id); list_del_init(&f->list); msm_cvp_unmap_frame(inst, f->pkt->client_data.kdata); @@ -1564,7 +1606,7 @@ static void cvp_clean_fence_queue(struct msm_cvp_inst *inst, int synx_state) ktid = f->pkt->client_data.kdata & (FENCE_BIT - 1); dprintk(CVP_SYNX, "%s: (%#x)flush frame %llu %llu sched_list\n", - __func__, hash32_ptr(inst->session), ktid, f->frame_id); + __func__, inst->sess_id, ktid, f->frame_id); cvp_cancel_synx(inst, CVP_INPUT_SYNX, f, synx_state); } @@ -1612,14 +1654,14 @@ static int cvp_flush_all(struct msm_cvp_inst *inst) return -ECONNRESET; dprintk(CVP_SESS, "session %llx (%#x)flush all starts\n", - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); q = &inst->fence_cmd_queue; hdev = inst->core->device; cvp_clean_fence_queue(inst, SYNX_STATE_SIGNALED_CANCEL); dprintk(CVP_SESS, "%s: (%#x) send flush to fw\n", - __func__, hash32_ptr(inst->session)); + __func__, inst->sess_id); /* Send flush to FW */ rc = call_hfi_op(hdev, session_flush, (void *)inst->session); @@ -1636,7 +1678,7 @@ static int cvp_flush_all(struct msm_cvp_inst *inst) __func__, rc); dprintk(CVP_SESS, "%s: (%#x) received flush from fw\n", - __func__, hash32_ptr(inst->session)); + __func__, inst->sess_id); exit: rc = cvp_drain_fence_sched_list(inst); @@ -1859,10 +1901,10 @@ int msm_cvp_session_deinit(struct msm_cvp_inst *inst) return -EINVAL; } dprintk(CVP_SESS, "%s: inst %pK (%#x)\n", __func__, - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); - session = (struct cvp_hal_session *)inst->session; - if (!session) + session = (struct cvp_hal_session *)get_sessObj_from_idr(inst); + if (!session || session != inst->session) return rc; rc = msm_cvp_comm_try_state(inst, MSM_CVP_CLOSE_DONE); @@ -1883,7 +1925,7 @@ int msm_cvp_session_init(struct msm_cvp_inst *inst) } dprintk(CVP_SESS, "%s: inst %pK (%#x)\n", __func__, - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); /* set default frequency */ inst->clk_data.core_id = 0; diff --git a/drivers/media/platform/msm/cvp/msm_cvp.h b/drivers/media/platform/msm/cvp/msm_cvp.h index b21864b46f1c..b8ae6068de35 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp.h +++ b/drivers/media/platform/msm/cvp/msm_cvp.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _MSM_CVP_H_ @@ -34,4 +35,6 @@ int msm_cvp_session_init(struct msm_cvp_inst *inst); int msm_cvp_session_deinit(struct msm_cvp_inst *inst); int msm_cvp_session_queue_stop(struct msm_cvp_inst *inst); int cvp_stop_clean_fence_queue(struct msm_cvp_inst *inst); +void *get_sessObj_from_idr(struct msm_cvp_inst *inst); +u32 get_sessId_from_idr(void *session); #endif diff --git a/drivers/media/platform/msm/cvp/msm_cvp_buf.c b/drivers/media/platform/msm/cvp/msm_cvp_buf.c index 995c111edb7d..c16eed33efd3 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_buf.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_buf.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_cvp_common.h" @@ -14,7 +14,7 @@ do { \ clear_bit(idx, &inst->dma_cache.usage_bitmap); \ dprintk(CVP_MEM, "clear %x bit %d dma_cache bitmap 0x%llx\n", \ - hash32_ptr(inst->session), smem->bitmap_index, \ + inst->sess_id, smem->bitmap_index, \ inst->dma_cache.usage_bitmap); \ } while (0) @@ -22,7 +22,7 @@ do { \ set_bit(idx, &inst->dma_cache.usage_bitmap); \ dprintk(CVP_MEM, "Set %x bit %d dma_cache bitmap 0x%llx\n", \ - hash32_ptr(inst->session), idx, \ + inst->sess_id, idx, \ inst->dma_cache.usage_bitmap); \ } while (0) @@ -36,7 +36,7 @@ void print_smem(u32 tag, const char *str, struct msm_cvp_inst *inst, if (smem->dma_buf) { dprintk(tag, "%s: %x : %s size %d flags %#x iova %#x idx %d ref %d", - str, hash32_ptr(inst->session), smem->dma_buf->name, + str, inst->sess_id, smem->dma_buf->name, smem->size, smem->flags, smem->device_addr, smem->bitmap_index, smem->refcount); } @@ -51,13 +51,13 @@ static void print_internal_buffer(u32 tag, const char *str, if (cbuf->smem->dma_buf) { dprintk(tag, "%s: %x : fd %d off %d %s size %d iova %#x", - str, hash32_ptr(inst->session), cbuf->fd, + str, inst->sess_id, cbuf->fd, cbuf->offset, cbuf->smem->dma_buf->name, cbuf->size, cbuf->smem->device_addr); } else { dprintk(tag, "%s: %x : idx %2d fd %d off %d size %d iova %#x", - str, hash32_ptr(inst->session), cbuf->fd, + str, inst->sess_id, cbuf->fd, cbuf->offset, cbuf->size, cbuf->smem->device_addr); } } @@ -77,7 +77,7 @@ void print_client_buffer(u32 tag, const char *str, dprintk(tag, "%s: %x : idx %2d fd %d off %d size %d type %d flags 0x%x\n", - str, hash32_ptr(inst->session), cbuf->index, cbuf->fd, + str, inst->sess_id, cbuf->index, cbuf->fd, cbuf->offset, cbuf->size, cbuf->type, cbuf->flags); } @@ -154,7 +154,7 @@ int msm_cvp_map_buf_dsp(struct msm_cvp_inst *inst, struct cvp_kmd_buffer *buf) } if (buf->index) { - rc = cvp_dsp_register_buffer(hash32_ptr(session), buf->fd, + rc = cvp_dsp_register_buffer(inst->sess_id, buf->fd, smem->dma_buf->size, buf->size, buf->offset, buf->index, (uint32_t)smem->device_addr); if (rc) { @@ -227,7 +227,7 @@ int msm_cvp_unmap_buf_dsp(struct msm_cvp_inst *inst, struct cvp_kmd_buffer *buf) } if (buf->index) { - rc = cvp_dsp_deregister_buffer(hash32_ptr(session), buf->fd, + rc = cvp_dsp_deregister_buffer(inst->sess_id, buf->fd, cbuf->smem->dma_buf->size, buf->size, buf->offset, buf->index, (uint32_t)cbuf->smem->device_addr); if (rc) { @@ -545,7 +545,7 @@ void msm_cvp_unmap_frame(struct msm_cvp_inst *inst, u64 ktid) ktid &= (FENCE_BIT - 1); dprintk(CVP_MEM, "%s: (%#x) unmap frame %llu\n", - __func__, hash32_ptr(inst->session), ktid); + __func__, inst->sess_id, ktid); found = false; mutex_lock(&inst->frames.lock); @@ -587,7 +587,7 @@ int msm_cvp_unmap_user_persist(struct msm_cvp_inst *inst, smem = pbuf->smem; dprintk(CVP_MEM, "unmap persist: %x %d %d %#x", - hash32_ptr(inst->session), pbuf->fd, + inst->sess_id, pbuf->fd, pbuf->size, smem->device_addr); if (smem->bitmap_index >= MAX_DMABUF_NUMS) { @@ -785,7 +785,7 @@ int msm_cvp_session_deinit_buffers(struct msm_cvp_inst *inst) list_for_each_entry_safe(cbuf, dummy, &inst->cvpdspbufs.list, list) { print_internal_buffer(CVP_MEM, "remove dspbufs", inst, cbuf); - rc = cvp_dsp_deregister_buffer(hash32_ptr(session), + rc = cvp_dsp_deregister_buffer(inst->sess_id, cbuf->fd, cbuf->smem->dma_buf->size, cbuf->size, cbuf->offset, cbuf->index, (uint32_t)cbuf->smem->device_addr); @@ -955,7 +955,7 @@ int cvp_release_arp_buffers(struct msm_cvp_inst *inst) if (buf->ownership == DRIVER) { dprintk(CVP_MEM, "%s: %x : fd %d %s size %d", - "free arp", hash32_ptr(inst->session), buf->fd, + "free arp", inst->sess_id, buf->fd, smem->dma_buf->name, buf->size); msm_cvp_smem_free(smem); kmem_cache_free(cvp_driver->smem_cache, smem); diff --git a/drivers/media/platform/msm/cvp/msm_cvp_common.c b/drivers/media/platform/msm/cvp/msm_cvp_common.c index 69787e51cc3d..22c8aa9441ad 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_common.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_common.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -195,23 +196,31 @@ struct msm_cvp_inst *cvp_get_inst_validate(struct msm_cvp_core *core, { int rc = 0; struct cvp_hfi_device *hdev; - struct msm_cvp_inst *s; + struct msm_cvp_inst *inst; + void *sessObj = NULL; - s = cvp_get_inst(core, session_id); - if (!s) { - dprintk(CVP_ERR, "%s session doesn't exit\n", + inst = cvp_get_inst(core, session_id); + if (!inst) { + dprintk(CVP_ERR, "%s Inst doesn't exit\n", __builtin_return_address(0)); return NULL; } - hdev = s->core->device; - rc = call_hfi_op(hdev, validate_session, s->session, __func__); - if (rc) { - cvp_put_inst(s); - s = NULL; + sessObj = get_sessObj_from_idr(inst); + if (!sessObj || sessObj != inst->session) { + dprintk(CVP_ERR, + "Either sessionObj is null or not matching with inst->session\n"); + return NULL; } - return s; + hdev = inst->core->device; + rc = call_hfi_op(hdev, validate_session, sessObj, __func__); + if (rc) { + cvp_put_inst(inst); + inst = NULL; + } + + return inst; } static void cvp_handle_session_cmd_done(enum hal_command_response cmd, @@ -486,7 +495,7 @@ static void handle_session_init_done(enum hal_command_response cmd, void *data) } dprintk(CVP_SESS, "%s: cvp session %#x\n", __func__, - hash32_ptr(inst->session)); + inst->sess_id); signal_session_msg_receipt(cmd, inst); cvp_put_inst(inst); @@ -568,7 +577,7 @@ static void handle_session_error(enum hal_command_response cmd, void *data) hdev = inst->core->device; dprintk(CVP_ERR, "Session error received for inst %pK session %x\n", - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); if (response->status == CVP_ERR_MAX_CLIENTS) { dprintk(CVP_WARN, "Too many clients, rejecting %pK", inst); @@ -901,7 +910,7 @@ static int msm_comm_session_abort(struct msm_cvp_inst *inst) abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE); dprintk(CVP_WARN, "%s: inst %pK session %x\n", __func__, - inst, hash32_ptr(inst->session)); + inst, inst->sess_id); rc = call_hfi_op(hdev, session_abort, (void *)inst->session); if (rc) { dprintk(CVP_ERR, @@ -914,7 +923,7 @@ static int msm_comm_session_abort(struct msm_cvp_inst *inst) inst->core->resources.msm_cvp_hw_rsp_timeout)); if (!rc) { dprintk(CVP_ERR, "%s: inst %pK session %x abort timed out\n", - __func__, inst, hash32_ptr(inst->session)); + __func__, inst, inst->sess_id); call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data); dump_hfi_queue(hdev->hfi_device_data); msm_cvp_comm_generate_sys_error(inst); @@ -1268,7 +1277,7 @@ int msm_cvp_comm_try_state(struct msm_cvp_inst *inst, int state) } dprintk(CVP_SESS, "Trying to move inst: %pK (%#x) from: %#x to %#x\n", - inst, hash32_ptr(inst->session), inst->state, state); + inst, inst->sess_id, inst->state, state); mutex_lock(&inst->sync_lock); if (inst->state == MSM_CVP_CORE_INVALID) { @@ -1281,7 +1290,7 @@ int msm_cvp_comm_try_state(struct msm_cvp_inst *inst, int state) flipped_state = get_flipped_state(inst->state, state); dprintk(CVP_SESS, "inst: %pK (%#x) flipped_state = %#x %x\n", - inst, hash32_ptr(inst->session), flipped_state, state); + inst, inst->sess_id, flipped_state, state); switch (flipped_state) { case MSM_CVP_CORE_UNINIT_DONE: case MSM_CVP_CORE_INIT: @@ -1491,7 +1500,7 @@ int msm_cvp_comm_kill_session(struct msm_cvp_inst *inst) return 0; } dprintk(CVP_WARN, "%s: inst %pK, session %x state %d\n", __func__, - inst, hash32_ptr(inst->session), inst->state); + inst, inst->sess_id, inst->state); /* * We're internally forcibly killing the session, if fw is aware of * the session send session_abort to firmware to clean up and release @@ -1503,7 +1512,7 @@ int msm_cvp_comm_kill_session(struct msm_cvp_inst *inst) if (rc) { dprintk(CVP_ERR, "%s: inst %pK session %x abort failed\n", - __func__, inst, hash32_ptr(inst->session)); + __func__, inst, inst->sess_id); change_cvp_inst_state(inst, MSM_CVP_CORE_INVALID); } else { change_cvp_inst_state(inst, MSM_CVP_CORE_UNINIT); diff --git a/drivers/media/platform/msm/cvp/msm_cvp_core.c b/drivers/media/platform/msm/cvp/msm_cvp_core.c index 5347eade1782..b98d43730988 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_core.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_core.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -165,7 +166,7 @@ void *msm_cvp_open(int core_id, int session_type) list_for_each_entry(inst, &core->instances, list) dprintk(CVP_ERR, "inst %pK, cmd %d id %d\n", inst, inst->cur_cmd_type, - hash32_ptr(inst->session)); + inst->sess_id); mutex_unlock(&core->lock); return NULL; @@ -369,7 +370,7 @@ int msm_cvp_destroy(struct msm_cvp_inst *inst) synx_uninitialize(inst->synx_session_id); pr_info(CVP_DBG_TAG "Closed cvp instance: %pK session_id = %d\n", - "sess", inst, hash32_ptr(inst->session)); + "sess", inst, inst->sess_id); if (inst->cur_cmd_type) dprintk(CVP_ERR, "deleted instance has pending cmd %d\n", inst->cur_cmd_type); diff --git a/drivers/media/platform/msm/cvp/msm_cvp_internal.h b/drivers/media/platform/msm/cvp/msm_cvp_internal.h index 533e16cfce56..31495ad77d35 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_internal.h +++ b/drivers/media/platform/msm/cvp/msm_cvp_internal.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef _MSM_CVP_INTERNAL_H_ @@ -290,6 +291,8 @@ struct msm_cvp_core { unsigned long curr_freq; struct cvp_cycle_info dyn_clk; atomic64_t kernel_trans_id; + struct idr sess_idr; + struct mutex idr_mtx; }; struct msm_cvp_inst { @@ -301,6 +304,7 @@ struct msm_cvp_inst { struct cvp_session_queue session_queue_fence; struct cvp_session_event event_handler; void *session; + u32 sess_id; enum instance_state state; struct msm_cvp_list freqs; struct msm_cvp_list persistbufs; diff --git a/drivers/media/platform/msm/cvp/msm_cvp_synx.c b/drivers/media/platform/msm/cvp/msm_cvp_synx.c index f70fb4013058..4580c582174b 100644 --- a/drivers/media/platform/msm/cvp/msm_cvp_synx.c +++ b/drivers/media/platform/msm/cvp/msm_cvp_synx.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "msm_cvp_common.h" @@ -20,7 +21,7 @@ void cvp_dump_fence_queue(struct msm_cvp_inst *inst) ssid = inst->synx_session_id; mutex_lock(&q->lock); dprintk(CVP_WARN, "inst %x fence q mode %d, ssid %d\n", - hash32_ptr(inst->session), q->mode, ssid.client_id); + inst->sess_id, q->mode, ssid.client_id); dprintk(CVP_WARN, "fence cmdq wait list:\n"); list_for_each_entry(f, &q->wait_list, list) { From c5a324ce0b79d0f5c6770c902852693ff72ab3b8 Mon Sep 17 00:00:00 2001 From: Kaushik Yalla Date: Thu, 9 Oct 2025 02:28:35 -0700 Subject: [PATCH 2/6] kgsl: Add buffer overflow check for perfcounter dynamic list Add buffer overflow check to ensure dynamic list updates do not exceed allocated buffer size, returning an error if overflow would occur. Change-Id: I5ef8ff91fda3879250cb848761fa01674fc59cf7 Signed-off-by: Shiv Kumar Signed-off-by: Kaushik Yalla Signed-off-by: Chandra Vamsi Yekkaluri --- drivers/gpu/msm/adreno_a6xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c index 048e33de599c..93bebbdb24fc 100644 --- a/drivers/gpu/msm/adreno_a6xx.c +++ b/drivers/gpu/msm/adreno_a6xx.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -2523,6 +2524,7 @@ int a6xx_perfcounter_update(struct adreno_device *adreno_dev, struct cpu_gpu_lock *lock = ptr; u32 *data = ptr + sizeof(*lock); int i, offset = 0; + u32 pending_pairs = 2; /* No of pairs to add: and */ if (cpu_gpu_lock(lock)) { cpu_gpu_unlock(lock); @@ -2546,6 +2548,13 @@ int a6xx_perfcounter_update(struct adreno_device *adreno_dev, offset += 2; } + /* Ensure there is enough space in the reglist buffer for new pairs */ + if ((offset + (pending_pairs * 2)) >= + (adreno_dev->pwrup_reglist->size / sizeof(u32))) { + cpu_gpu_unlock(lock); + return -ENOSPC; + } + /* * For all targets A6XX_RBBM_PERFCTR_CNTL needs to be the last entry, * so overwrite the existing A6XX_RBBM_PERFCNTL_CTRL and add it back to From 5cef717354325b214141c9c5b4a0f88d90589cf4 Mon Sep 17 00:00:00 2001 From: Akash Kumar Date: Thu, 30 Oct 2025 16:07:18 +0530 Subject: [PATCH 3/6] UPSTREAM: usb: gadget: configfs: Add frame-based frame format support Add support for frame-based frame format, which can be used to support multiple formats like H264 or H265, in addition to MJPEG and YUV frames. The frame-based format is set to H264 by default, but it can be updated to other formats by modifying the GUID through the guid configfs attribute. Different structures are used for all three formats, as H264 has a different structure compared to MJPEG and uncompressed formats. These structures will be passed to the frame make function based on the active format, using a common frame structure with additional parameters needed only for frame-based formats. These parameters are handled at runtime in the UVC driver. Signed-off-by: Akash Kumar Link: https://lore.kernel.org/r/20240927152138.31416-1-quic_akakum@quicinc.com Signed-off-by: Greg Kroah-Hartman Git-commit: 7b5a58952fc3b51905c2963647485565df1e5e26 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git. Change-Id: Iffef14deba1f7e22c74b89b72a252f23802549d4 Signed-off-by: Akash Kumar --- .../ABI/testing/configfs-usb-gadget-uvc | 65 ++++ drivers/usb/gadget/function/f_uvc.c | 10 +- drivers/usb/gadget/function/uvc_configfs.c | 349 +++++++++++++++++- drivers/usb/gadget/function/uvc_v4l2.c | 1 + include/uapi/linux/usb/video.h | 58 +++ 5 files changed, 469 insertions(+), 14 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc index 809765bd9573..5d4180aaf18e 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uvc +++ b/Documentation/ABI/testing/configfs-usb-gadget-uvc @@ -265,6 +265,71 @@ Description: Specific uncompressed frame descriptors bmCapabilities - still image support, fixed frame-rate support +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased +Date: Oct 2025 +KernelVersion: 5.4 +Description: Framebased format descriptors + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name +Date: Oct 2025 +KernelVersion: 5.4 +Description: Specific framebased format descriptors + + ================== ======================================= + bFormatIndex unique id for this format descriptor; + only defined after parent header is + linked into the streaming class; + read-only + bmaControls this format's data for bmaControls in + the streaming header + bmInterlaceFlags specifies interlace information, + read-only + bAspectRatioY the X dimension of the picture aspect + ratio, read-only + bAspectRatioX the Y dimension of the picture aspect + ratio, read-only + bDefaultFrameIndex optimum frame index for this stream + bBitsPerPixel number of bits per pixel used to + specify color in the decoded video + frame + guidFormat globally unique id used to identify + stream-encoding format + ================== ======================================= + +What: /config/usb-gadget/gadget/functions/uvc.name/streaming/framebased/name/name +Date: Sept 2024 +KernelVersion: 5.15 +Description: Specific framebased frame descriptors + + ========================= ===================================== + bFrameIndex unique id for this framedescriptor; + only defined after parent format is + linked into the streaming header; + read-only + dwFrameInterval indicates how frame interval can be + programmed; a number of values + separated by newline can be specified + dwDefaultFrameInterval the frame interval the device would + like to use as default + dwBytesPerLine Specifies the number of bytes per line + + of video for packed fixed frame size + formats, allowing the receiver to + perform stride alignment of the video. + If the bVariableSize value (above) is + TRUE (1), or if the format does not + permit such alignment, this value shall + be set to zero (0). + dwMaxBitRate the maximum bit rate at the shortest + frame interval in bps + dwMinBitRate the minimum bit rate at the longest + frame interval in bps + wHeight height of decoded bitmap frame in px + wWidth width of decoded bitmam frame in px + bmCapabilities still image support, fixed frame-rate + support + ========================= ===================================== + What: /config/usb-gadget/gadget/functions/uvc.name/streaming/header Date: Dec 2014 KernelVersion: 4.0 diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 094a88ff9a67..b4c08b4ed602 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -808,9 +808,9 @@ static struct usb_function_instance *uvc_alloc_inst(void) cd->wObjectiveFocalLengthMax = cpu_to_le16(0); cd->wOcularFocalLength = cpu_to_le16(0); cd->bControlSize = 3; - cd->bmControls[0] = 2; - cd->bmControls[1] = 0; - cd->bmControls[2] = 0; + cd->bmControls[0] = 62; + cd->bmControls[1] = 126; + cd->bmControls[2] = 10; pd = &opts->uvc_processing; pd->bLength = UVC_DT_PROCESSING_UNIT_SIZE(2); @@ -820,8 +820,8 @@ static struct usb_function_instance *uvc_alloc_inst(void) pd->bSourceID = 1; pd->wMaxMultiplier = cpu_to_le16(16*1024); pd->bControlSize = 2; - pd->bmControls[0] = 1; - pd->bmControls[1] = 0; + pd->bmControls[0] = 91; + pd->bmControls[1] = 23; pd->iProcessing = 0; pd->bmVideoStandards = 0; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 00fb58e50a15..5480a2988fe4 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -762,16 +762,19 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { /* ----------------------------------------------------------------------------- * streaming/uncompressed * streaming/mjpeg + * streaming/framebased */ static const char * const uvcg_format_names[] = { "uncompressed", "mjpeg", + "framebased" }; enum uvcg_format_type { UVCG_UNCOMPRESSED = 0, UVCG_MJPEG, + UVCG_FRAMEBASED, }; struct uvcg_format { @@ -1080,6 +1083,7 @@ struct uvcg_frame { u32 dw_max_video_frame_buffer_size; u32 dw_default_frame_interval; u8 b_frame_interval_type; + u32 dw_bytes_perline; } __attribute__((packed)) frame; u32 *dw_frame_interval; }; @@ -1190,6 +1194,7 @@ UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); +UVCG_FRAME_ATTR(dw_bytes_perline, dwBytesPerLine, 32); #undef UVCG_FRAME_ATTR @@ -1324,7 +1329,7 @@ end: UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); -static struct configfs_attribute *uvcg_frame_attrs[] = { +static struct configfs_attribute *uvcg_frame_attrs1[] = { &uvcg_frame_attr_b_frame_index, &uvcg_frame_attr_bm_capabilities, &uvcg_frame_attr_w_width, @@ -1337,12 +1342,32 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { NULL, }; -static const struct config_item_type uvcg_frame_type = { +static struct configfs_attribute *uvcg_frame_attrs2[] = { + &uvcg_frame_attr_b_frame_index, + &uvcg_frame_attr_bm_capabilities, + &uvcg_frame_attr_w_width, + &uvcg_frame_attr_w_height, + &uvcg_frame_attr_dw_min_bit_rate, + &uvcg_frame_attr_dw_max_bit_rate, + &uvcg_frame_attr_dw_max_video_frame_buffer_size, + &uvcg_frame_attr_dw_default_frame_interval, + &uvcg_frame_attr_dw_frame_interval, + &uvcg_frame_attr_dw_bytes_perline, + NULL, +}; + +static const struct config_item_type uvcg_frame_type1 = { .ct_item_ops = &uvcg_config_item_ops, - .ct_attrs = uvcg_frame_attrs, + .ct_attrs = uvcg_frame_attrs1, .ct_owner = THIS_MODULE, }; +static const struct config_item_type uvcg_frame_type2 = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_frame_attrs2, + .ct_owner = THIS_MODULE, +}; + static struct config_item *uvcg_frame_make(struct config_group *group, const char *name) { @@ -1363,6 +1388,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->frame.dw_max_bit_rate = 55296000; h->frame.dw_max_video_frame_buffer_size = 460800; h->frame.dw_default_frame_interval = 666666; + h->frame.dw_bytes_perline = 0; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -1375,6 +1401,9 @@ static struct config_item *uvcg_frame_make(struct config_group *group, } else if (fmt->type == UVCG_MJPEG) { h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; h->fmt_type = UVCG_MJPEG; + } else if (fmt->type == UVCG_FRAMEBASED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED; + h->fmt_type = UVCG_FRAMEBASED; } else { mutex_unlock(&opts->lock); kfree(h); @@ -1383,7 +1412,10 @@ static struct config_item *uvcg_frame_make(struct config_group *group, ++fmt->num_frames; mutex_unlock(&opts->lock); - config_item_init_type_name(&h->item, name, &uvcg_frame_type); + if (fmt->type == UVCG_FRAMEBASED) + config_item_init_type_name(&h->item, name, &uvcg_frame_type2); + else + config_item_init_type_name(&h->item, name, &uvcg_frame_type1); return &h->item; } @@ -1413,9 +1445,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) list_for_each_entry(ci, &fmt->cg_children, ci_entry) { struct uvcg_frame *frm; - if (ci->ci_type != &uvcg_frame_type) - continue; - frm = to_uvcg_frame(ci); frm->frame.b_frame_index = i++; } @@ -1856,6 +1885,260 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { .name = "mjpeg", }; +/* ----------------------------------------------------------------------------- + * streaming/framebased/ + */ + +struct uvcg_framebased { + struct uvcg_format fmt; + struct uvc_format_framebased desc; +}; + +static inline struct uvcg_framebased *to_uvcg_framebased(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_framebased, fmt); +} + +static struct configfs_group_operations uvcg_framebased_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_FRAMEBASED_ATTR_RO(cname, aname, bits) \ + static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; \ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = scnprintf(page, PAGE_SIZE, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +UVC_ATTR_RO(uvcg_framebased_, cname, aname) + +#define UVCG_FRAMEBASED_ATTR(cname, aname, bits) \ + static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = scnprintf(page, PAGE_SIZE, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_framebased_##cname##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +UVC_ATTR(uvcg_framebased_, cname, aname) + +UVCG_FRAMEBASED_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_bits_per_pixel, bBitsPerPixel, 8); +UVCG_FRAMEBASED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_FRAMEBASED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); + +#undef UVCG_FRAMEBASED_ATTR +#undef UVCG_FRAMEBASED_ATTR_RO + +static ssize_t uvcg_framebased_guid_format_show(struct config_item *item, + char *page) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_framebased_guid_format_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVC_ATTR(uvcg_framebased_, guid_format, guidFormat); + + static inline ssize_t +uvcg_framebased_bma_controls_show(struct config_item *item, char *page) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_show(&u->fmt, page); +} + + static inline ssize_t +uvcg_framebased_bma_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_store(&u->fmt, page, len); +} + +UVC_ATTR(uvcg_framebased_, bma_controls, bmaControls); + +static struct configfs_attribute *uvcg_framebased_attrs[] = { + &uvcg_framebased_attr_b_format_index, + &uvcg_framebased_attr_b_default_frame_index, + &uvcg_framebased_attr_b_bits_per_pixel, + &uvcg_framebased_attr_b_aspect_ratio_x, + &uvcg_framebased_attr_b_aspect_ratio_y, + &uvcg_framebased_attr_bm_interface_flags, + &uvcg_framebased_attr_bma_controls, + &uvcg_framebased_attr_guid_format, + NULL, +}; + +static const struct config_item_type uvcg_framebased_type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_group_ops, + .ct_attrs = uvcg_framebased_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_framebased_make(struct config_group *group, + const char *name) +{ + static char guid[] = { /*Declear frame based as H264 format*/ + 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_framebased *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); + + h->desc.bLength = UVC_DT_FORMAT_FRAMEBASED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 0; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + h->desc.bVariableSize = 1; + + h->fmt.type = UVCG_FRAMEBASED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_framebased_type); + + return &h->fmt.group; +} + +static struct configfs_group_operations uvcg_framebased_grp_ops = { + .make_group = uvcg_framebased_make, +}; + +static const struct uvcg_config_group_type uvcg_framebased_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "framebased", +}; + /* ----------------------------------------------------------------------------- * streaming/color_matching/default */ @@ -2001,6 +2284,7 @@ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, if (ret) return ret; grp = &f->fmt->group; + j = 0; list_for_each_entry(item, &grp->cg_children, ci_entry) { frm = to_uvcg_frame(item); ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); @@ -2049,6 +2333,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, container_of(fmt, struct uvcg_mjpeg, fmt); *size += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, fmt); + + *size += sizeof(f->desc); } else { return -EINVAL; } @@ -2059,6 +2348,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, int sz = sizeof(frm->dw_frame_interval); *size += sizeof(frm->frame); + /* + * framebased has duplicate member with uncompressed and + * mjpeg, so minus it + */ + *size -= sizeof(u32); *size += frm->frame.b_frame_interval_type * sz; } break; @@ -2069,6 +2363,27 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, return 0; } +static int __uvcg_copy_framebased_desc(void *dest, struct uvcg_frame *frm, + int sz) +{ + struct uvc_frame_framebased *desc = dest; + + desc->bLength = frm->frame.b_length; + desc->bDescriptorType = frm->frame.b_descriptor_type; + desc->bDescriptorSubType = frm->frame.b_descriptor_subtype; + desc->bFrameIndex = frm->frame.b_frame_index; + desc->bmCapabilities = frm->frame.bm_capabilities; + desc->wWidth = frm->frame.w_width; + desc->wHeight = frm->frame.w_height; + desc->dwMinBitRate = frm->frame.dw_min_bit_rate; + desc->dwMaxBitRate = frm->frame.dw_max_bit_rate; + desc->dwDefaultFrameInterval = frm->frame.dw_default_frame_interval; + desc->bFrameIntervalType = frm->frame.b_frame_interval_type; + desc->dwBytesPerLine = frm->frame.dw_bytes_perline; + + return 0; +} + /* * Fill an array of streaming descriptors. * @@ -2123,6 +2438,15 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, m->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &m->desc, sizeof(m->desc)); *dest += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, + fmt); + + f->desc.bFormatIndex = n + 1; + f->desc.bNumFrameDescriptors = fmt->num_frames; + memcpy(*dest, &f->desc, sizeof(f->desc)); + *dest += sizeof(f->desc); } else { return -EINVAL; } @@ -2132,8 +2456,11 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, struct uvcg_frame *frm = priv1; struct uvc_descriptor_header *h = *dest; - sz = sizeof(frm->frame); - memcpy(*dest, &frm->frame, sz); + sz = sizeof(frm->frame) - 4; + if (frm->fmt_type != UVCG_FRAMEBASED) + memcpy(*dest, &frm->frame, sz); + else + __uvcg_copy_framebased_desc(*dest, frm, sz); *dest += sz; sz = frm->frame.b_frame_interval_type * sizeof(*frm->dw_frame_interval); @@ -2145,6 +2472,9 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, else if (frm->fmt_type == UVCG_MJPEG) h->bLength = UVC_DT_FRAME_MJPEG_SIZE( frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_FRAMEBASED) + h->bLength = UVC_DT_FRAME_FRAMEBASED_SIZE( + frm->frame.b_frame_interval_type); } break; } @@ -2357,6 +2687,7 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { &uvcg_streaming_header_grp_type, &uvcg_uncompressed_grp_type, &uvcg_mjpeg_grp_type, + &uvcg_framebased_grp_type, &uvcg_color_matching_grp_type, &uvcg_streaming_class_grp_type, NULL, diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 93d0d0d2f75f..49f1f2ad134e 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -58,6 +58,7 @@ struct uvc_format { static struct uvc_format uvc_formats[] = { { 16, V4L2_PIX_FMT_YUYV }, { 0, V4L2_PIX_FMT_MJPEG }, + { 0, V4L2_PIX_FMT_H264 }, { 12, V4L2_PIX_FMT_YUV420 }, { 8, V4L2_PIX_FMT_GREY }, }; diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h index c58854fb7d94..c79b6049d9d7 100644 --- a/include/uapi/linux/usb/video.h +++ b/include/uapi/linux/usb/video.h @@ -597,5 +597,63 @@ struct UVC_FRAME_MJPEG(n) { \ __le32 dwFrameInterval[n]; \ } __attribute__ ((packed)) +/* Frame Based Payload - 3.1.1. Frame Based Video Format Descriptor */ +struct uvc_format_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFormatIndex; + __u8 bNumFrameDescriptors; + __u8 guidFormat[16]; + __u8 bBitsPerPixel; + __u8 bDefaultFrameIndex; + __u8 bAspectRatioX; + __u8 bAspectRatioY; + __u8 bmInterfaceFlags; + __u8 bCopyProtect; + __u8 bVariableSize; +} __attribute__((__packed__)); + +#define UVC_DT_FORMAT_FRAMEBASED_SIZE 28 + +/* Frame Based Payload - 3.1.2. Frame Based Video Frame Descriptor */ +struct uvc_frame_framebased { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwDefaultFrameInterval; + __u8 bFrameIntervalType; + __u32 dwBytesPerLine; + __u32 dwFrameInterval[]; +} __attribute__((__packed__)); + +#define UVC_DT_FRAME_FRAMEBASED_SIZE(n) (26+4*(n)) + +#define UVC_FRAME_FRAMEBASED(n) \ + uvc_frame_framebased_##n + +#define DECLARE_UVC_FRAME_FRAMEBASED(n) \ + struct UVC_FRAME_FRAMEBASED(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bFrameIndex; \ + __u8 bmCapabilities; \ + __u16 wWidth; \ + __u16 wHeight; \ + __u32 dwMinBitRate; \ + __u32 dwMaxBitRate; \ + __u32 dwDefaultFrameInterval; \ + __u8 bFrameIntervalType; \ + __u32 dwBytesPerLine; \ + __u32 dwFrameInterval[n]; \ + } __attribute__ ((packed)) + #endif /* __LINUX_USB_VIDEO_H */ From a3e616cc0d3d7a59818c35acfa7f8829def160b0 Mon Sep 17 00:00:00 2001 From: Desireddy Suresh Kumar Reddy Date: Fri, 9 Feb 2024 16:49:34 +0530 Subject: [PATCH 4/6] net: bridge: Fix for co-located mode Issue point: 1.qca-hyfi-bridge calls the HyFi-hooks for enabling the co-located mode. a. hyfi_bridge_get_dst and br_get_dst_hook functions are passed as arguments in a rcu_assign_pointer function. b. br_get_dst_hook implementations should be available in the br_dev_xmit kernel function. But currently br_get_dst_hook implementations are missing in br_dev_xmit. Fix: 1. For enabling co-located-agent-mode and ieee1905-packet-transfer this HyFi-hook (br_get_dst_hook) are made available in br_dev_xmit function. a. Added the br_get_dst_hook and get_dst_hook in the required files(net/bridge/br_input.c & net/bridge/br_device.c) Change-Id: Iac43ea347c83276971816029a940818e99752710 Signed-off-by: Desireddy Suresh Kumar Reddy --- include/linux/if_bridge.h | 4 ++++ net/bridge/br_device.c | 27 ++++++++++++++++++++++++--- net/bridge/br_input.c | 19 ++++++++++++++++++- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 9e2ad3b81690..e06623fa5b80 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -162,5 +162,9 @@ extern br_notify_hook_t __rcu *br_notify_hook; typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, struct sk_buff *skb); extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; +typedef struct net_bridge_port *br_get_dst_hook_t( + const struct net_bridge_port *src, + struct sk_buff **skb); +extern br_get_dst_hook_t __rcu *br_get_dst_hook; #endif #endif diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 00438505c175..ec4dee64357b 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -34,6 +34,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) const struct nf_br_ops *nf_ops; const unsigned char *dest; u16 vid = 0; +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + struct net_bridge_port *pdst; + br_get_dst_hook_t *get_dst_hook; +#endif if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) { kfree_skb(skb); @@ -82,6 +86,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) br_do_suppress_nd(skb, br, vid, NULL, msg); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + get_dst_hook = rcu_dereference(br_get_dst_hook); +#endif + dest = eth_hdr(skb)->h_dest; if (is_broadcast_ether_addr(dest)) { br_flood(br, skb, BR_PKT_BROADCAST, false, true); @@ -107,11 +115,24 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) br_multicast_flood(mdst, skb, false, true); else br_flood(br, skb, BR_PKT_MULTICAST, false, true); - } else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) { - br_forward(dst->dst, skb, false, true); } else { - br_flood(br, skb, BR_PKT_UNICAST, false, true); +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + pdst = __br_get(get_dst_hook, NULL, NULL, &skb); + if (pdst) { + if (!skb) + goto out; + br_forward(pdst, skb, false, true); + } else +#endif + { + dst = br_fdb_find_rcu(br, dest, vid); + if (dst) + br_forward(dst->dst, skb, false, true); + else + br_flood(br, skb, BR_PKT_UNICAST, false, true); + } } + out: rcu_read_unlock(); return NETDEV_TX_OK; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index ace461e94830..02dbaf13f591 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -33,6 +33,11 @@ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) /* Hook for external Multicast handler */ br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; EXPORT_SYMBOL(br_multicast_handle_hook); + +/* Hook for external forwarding logic */ +br_get_dst_hook_t __rcu *br_get_dst_hook __read_mostly; +EXPORT_SYMBOL_GPL(br_get_dst_hook); + #endif int br_pass_frame_up(struct sk_buff *skb) @@ -94,6 +99,8 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb struct net_bridge *br; #ifdef CONFIG_HYFI_BRIDGE_HOOKS br_multicast_handle_hook_t *multicast_handle_hook; + struct net_bridge_port *pdst = NULL; + br_get_dst_hook_t *get_dst_hook = rcu_dereference(br_get_dst_hook); #endif u16 vid = 0; @@ -168,7 +175,17 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb } break; case BR_PKT_UNICAST: - dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + pdst = __br_get(get_dst_hook, NULL, p, &skb); + if (pdst) { + if (!skb) + goto out; + } else +#endif + { + dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); + } + break; default: break; } From 3a1ebfc377d01f5123dd9509ba774666b38c7e0a Mon Sep 17 00:00:00 2001 From: Manoj Sekar Date: Mon, 26 Jun 2023 22:26:28 +0530 Subject: [PATCH 5/6] bridge: port structure members from 5.4 kernel port bridge related structure members from 5.4 kernel to 5.15 kernel required for EasyMesh Kernel modules. Change-Id: I74c934a0a6c96782f4e6c4ff99e26ab1b8b39168 Signed-off-by: Manoj Sekar --- net/bridge/br_private.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index c7130fff57a0..023ecc1f4494 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -195,6 +195,9 @@ struct net_bridge_fdb_entry { struct net_bridge_fdb_key key; struct hlist_node fdb_node; + unsigned char is_local:1, + is_static:1; + unsigned long flags; unsigned char offloaded:1; From 895cfcbb7afc51c581db9fb91146e95f8fd5d676 Mon Sep 17 00:00:00 2001 From: Manoj Sekar Date: Mon, 26 Jun 2023 20:31:54 +0530 Subject: [PATCH 6/6] net: Add netdevice notification for bridge activity This modification allows programs to get notified whenever a device is added to or removed from a bridge. This will be used by NSS Qdisc for updating bridge shaper configuration. Change-Id: I70e63c5b219d7ab022400741b2dc789cfef71ead Signed-off-by: Manoj Sekar --- include/linux/netdevice.h | 2 ++ net/bridge/br_if.c | 3 +++ net/core/dev.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7d048d775eff..04fde5ea195b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2554,6 +2554,8 @@ enum netdev_cmd { NETDEV_CVLAN_FILTER_DROP_INFO, NETDEV_SVLAN_FILTER_PUSH_INFO, NETDEV_SVLAN_FILTER_DROP_INFO, + NETDEV_BR_JOIN, + NETDEV_BR_LEAVE, }; const char *netdev_cmd_to_name(enum netdev_cmd cmd); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index ea0ddd513cc1..a5565b7dfb06 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -695,6 +695,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, br_set_gso_limits(br); kobject_uevent(&p->kobj, KOBJ_ADD); + call_netdevice_notifiers(NETDEV_BR_JOIN, dev); return 0; @@ -732,6 +733,8 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) if (!p || p->br != br) return -EINVAL; + call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); + /* Since more than one interface can be attached to a bridge, * there still maybe an alternate path for netconsole to use; * therefore there is no reason for a NETDEV_RELEASE event. diff --git a/net/core/dev.c b/net/core/dev.c index aad29ac5ac15..d4d84160b795 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1521,7 +1521,7 @@ const char *netdev_cmd_to_name(enum netdev_cmd cmd) N(UDP_TUNNEL_DROP_INFO) N(CHANGE_TX_QUEUE_LEN) N(CVLAN_FILTER_PUSH_INFO) N(CVLAN_FILTER_DROP_INFO) N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) - N(PRE_CHANGEADDR) + N(PRE_CHANGEADDR) N(BR_JOIN) N(BR_LEAVE) } #undef N return "UNKNOWN_NETDEV_EVENT";