| 0/0 |
2026/07/30 14:12 |
flow |
patching |
59m
Results: map[AckedBy:[] Fixes:map[AuthorEmail:tiwai@suse.de AuthorName:Takashi Iwai Hash:29ee7a4dddd5caa18d1cef000f20c6af43f762f1 Title:usb: gadget: midi2: Add configfs support] KernelBranch:master KernelCommit:f5098b6bae761e346ebcd9da7f95622c04733cff KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:usb: gadget: midi2: Fix memory leak in configfs default groups
When a USB function instance for midi2 is allocated, it dynamically
allocates ep_opts and block_opts structures and adds them as default groups
(ep.0 and block.0). While configfs removes the dentries for default groups
when the parent directory is removed, it does not automatically drop their
reference counts. This responsibility lies with the driver.
Because f_midi2_free_inst() and f_midi2_ep_opts_release() do not clean up
their default groups, the ep_opts and block_opts structures are leaked when
the function instance is destroyed.
Fix this by explicitly calling configfs_remove_default_groups() in the
respective release functions. This ensures that the reference counts of the
default groups are properly dropped, triggering the existing release
callbacks (f_midi2_ep_opts_release and f_midi2_block_opts_release) to
safely free the dynamically allocated memory.
Kmemleak reports:
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
...
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504
[inline]
f_midi2_alloc_inst+0x80/0x2c0
drivers/usb/gadget/function/f_midi2.c:2666
...
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
...
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342
[inline]
f_midi2_alloc_inst+0x16a/0x2c0
drivers/usb/gadget/function/f_midi2.c:2673
... PatchDiff:diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2473,6 +2473,7 @@ static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
@@ -2639,6 +2640,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
Recipients:[map[Email:christophe.jaillet@wanadoo.fr Name:Christophe JAILLET To:false] map[Email:gregkh@linuxfoundation.org Name:Greg Kroah-Hartman To:true] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:linux-usb@vger.kernel.org Name: To:true] map[Email:tiwai@suse.de Name:Takashi Iwai To:true]] ReportedBy:[] ReviewedBy:[] TestedBy:[]]
|
| 1/1 |
2026/07/30 14:12 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:f5098b6bae761e346ebcd9da7f95622c04733cff KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/07/30 14:12 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// mkdirat arguments: [
// fd: fd_dir (resource)
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 00} (length 0xe)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000080, "./my_configfs\000", 14);
syscall(__NR_mkdirat, /*fd=*/0xffffff9c, /*path=*/0x200000000080ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// mount arguments: [
// src: nil
// dst: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 00} (length 0xe)
// }
// type: ptr[in, buffer] {
// buffer: {63 6f 6e 66 69 67 66 73 00} (length 0x9)
// }
// flags: mount_flags = 0x0 (8 bytes)
// data: nil
// ]
memcpy((void*)0x200000000100, "./my_configfs\000", 14);
memcpy((void*)0x200000000140, "configfs\000", 9);
syscall(__NR_mount, /*src=*/0ul, /*dst=*/0x200000000100ul, /*type=*/0x200000000140ul, /*flags=*/0ul, /*data=*/0ul);
// mkdirat arguments: [
// fd: fd_dir (resource)
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 2f 75 73 62 5f 67 61 64 67 65 74 2f 67 31 00} (length 0x1c)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000180, "./my_configfs/usb_gadget/g1\000", 28);
syscall(__NR_mkdirat, /*fd=*/0xffffff9c, /*path=*/0x200000000180ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// mkdirat arguments: [
// fd: fd_dir (resource)
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 2f 75 73 62 5f 67 61 64 67 65 74 2f 67 31 2f 66 75 6e 63 74 69 6f 6e 73 2f 6d 69 64 69 32 2e 75 73 62 30 00} (length 0x31)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000200, "./my_configfs/usb_gadget/g1/functions/midi2.usb0\000", 49);
syscall(__NR_mkdirat, /*fd=*/0xffffff9c, /*path=*/0x200000000200ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// rmdir arguments: [
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 2f 75 73 62 5f 67 61 64 67 65 74 2f 67 31 2f 66 75 6e 63 74 69 6f 6e 73 2f 6d 69 64 69 32 2e 75 73 62 30 00} (length 0x31)
// }
// ]
memcpy((void*)0x200000000300, "./my_configfs/usb_gadget/g1/functions/midi2.usb0\000", 49);
syscall(__NR_rmdir, /*path=*/0x200000000300ul);
return 0;
}
]
|
| 3/1 |
2026/07/30 14:12 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/3735123449433f9e6f9fb5b55ddd9994cbcf15f0]
|
| 4/1 |
2026/07/30 14:12 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/f2406aa8130f610795c291eecb3c31a15ab218ca]
|
| 5/1 |
2026/07/30 14:12 |
action |
crash-reproducer |
0m
Results: map[OtherCrashReports:<nil> ReproducedBugTitle:memory leak in usb_get_function_instance ReproducedCrashReport:2026/07/30 13:30:35 executed programs: 5
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504 [inline]
f_midi2_alloc_inst+0x80/0x2c0 drivers/usb/gadget/function/f_midi2.c:2666
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342 [inline]
f_midi2_alloc_inst+0x16a/0x2c0 drivers/usb/gadget/function/f_midi2.c:2673
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF
ReproducedFaultInjection:]
|
| 6/1 |
2026/07/30 14:12 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/07/30 14:12 |
agent |
debugger |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
2026/07/30 13:30:35 executed programs: 5
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504 [inline]
f_midi2_alloc_inst+0x80/0x2c0 drivers/usb/gadget/function/f_midi2.c:2666
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342 [inline]
f_midi2_alloc_inst+0x16a/0x2c0 drivers/usb/gadget/function/f_midi2.c:2673
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF
The following C code is a draft of the vulnerable syscall sequence. Keep in mind that
it may lack the precise threading, sandboxing, and some arguments of a working reproducer:
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// mkdirat arguments: [
// fd: fd_dir (resource)
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 00} (length 0xe)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000080, "./my_configfs\000", 14);
syscall(__NR_mkdirat, /*fd=*/0xffffff9c, /*path=*/0x200000000080ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// mount arguments: [
// src: nil
// dst: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 00} (length 0xe)
// }
// type: ptr[in, buffer] {
// buffer: {63 6f 6e 66 69 67 66 73 00} (length 0x9)
// }
// flags: mount_flags = 0x0 (8 bytes)
// data: nil
// ]
memcpy((void*)0x200000000100, "./my_configfs\000", 14);
memcpy((void*)0x200000000140, "configfs\000", 9);
syscall(__NR_mount, /*src=*/0ul, /*dst=*/0x200000000100ul, /*type=*/0x200000000140ul, /*flags=*/0ul, /*data=*/0ul);
// mkdirat arguments: [
// fd: fd_dir (resource)
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 2f 75 73 62 5f 67 61 64 67 65 74 2f 67 31 00} (length 0x1c)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000180, "./my_configfs/usb_gadget/g1\000", 28);
syscall(__NR_mkdirat, /*fd=*/0xffffff9c, /*path=*/0x200000000180ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// mkdirat arguments: [
// fd: fd_dir (resource)
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 2f 75 73 62 5f 67 61 64 67 65 74 2f 67 31 2f 66 75 6e 63 74 69 6f 6e 73 2f 6d 69 64 69 32 2e 75 73 62 30 00} (length 0x31)
// }
// mode: open_mode = 0x1ff (8 bytes)
// ]
memcpy((void*)0x200000000200, "./my_configfs/usb_gadget/g1/functions/midi2.usb0\000", 49);
syscall(__NR_mkdirat, /*fd=*/0xffffff9c, /*path=*/0x200000000200ul, /*mode=S_IXOTH|S_IWOTH|S_IROTH|S_IXGRP|S_IWGRP|S_IRGRP|S_IXUSR|S_IWUSR|0x100*/0x1fful);
// rmdir arguments: [
// path: ptr[in, buffer] {
// buffer: {2e 2f 6d 79 5f 63 6f 6e 66 69 67 66 73 2f 75 73 62 5f 67 61 64 67 65 74 2f 67 31 2f 66 75 6e 63 74 69 6f 6e 73 2f 6d 69 64 69 32 2e 75 73 62 30 00} (length 0x31)
// }
// ]
memcpy((void*)0x200000000300, "./my_configfs/usb_gadget/g1/functions/midi2.usb0\000", 49);
syscall(__NR_rmdir, /*path=*/0x200000000300ul);
return 0;
}
Reply: The root cause of this memory leak is that the driver fails to drop the reference counts of its default configfs groups when the parent group is destroyed.
When a USB function instance for `midi2` is allocated via `f_midi2_alloc_inst()`, it dynamically allocates `ep_opts` and `block_opts` structures using `kzalloc_obj()`. These structures are then initialized as configfs groups and added as default groups to their respective parents:
- `ep.0` is added as a default group to the function instance group (`opts->func_inst.group`).
- `block.0` is added as a default group to the endpoint group (`ep_opts->group`).
When the user removes the configfs directory for the function instance (via `rmdir`), configfs removes the dentries for the default groups but **does not** automatically drop their reference counts. The responsibility of dropping the refcounts of default groups lies with the driver, typically by calling `configfs_remove_default_groups()` when the parent group is released.
However, `f_midi2_free_inst()` (which is called when the function instance's refcount reaches 0) only frees the `f_midi2_opts` structure and does not clean up its default groups. Similarly, `f_midi2_ep_opts_release()` does not clean up its default groups. As a result, the `ep_opts` and `block_opts` structures are leaked.
### How to fix it
To fix the leak, you need to explicitly remove the default groups and drop their reference counts before freeing the parent structures. This can be done by calling `configfs_remove_default_groups()` in the respective release functions.
1. In `f_midi2_free_inst()`, drop the default groups of the function instance (this will drop the refcount of `ep.0` and trigger `f_midi2_ep_opts_release`):
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group); /* ADD THIS */
kfree(opts->info.iface_name);
kfree(opts);
}
```
2. In `f_midi2_ep_opts_release()`, drop the default groups of the endpoint (this will drop the refcount of `block.0` and trigger `f_midi2_block_opts_release`):
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item)); /* ADD THIS */
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
With these changes, the hierarchy of default groups will be properly unrolled and freed when the function instance is destroyed, whether it's through configfs `rmdir` or an error path that directly calls `usb_put_function_instance()`.
|
| 8/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 7137
output: 26
thoughts: 121
Thoughts: **Investigating a Memory Leak in the MIDI2 USB Gadget Driver**
Okay, so I've got a memory leak to track down. It seems like we've got a problem with the `f_midi2_ep_opts_create` and `f_midi2_block_opts_create` functions in `drivers/usb/gadget/function/f_midi2.c`. The leak is specifically triggered when a USB function instance of type "midi2" is created and then deleted. The reproducer sets up a configfs directory for `midi2.usb0` and then promptly tears it down.
My gut tells me that the issue likely originates within the instantiation code, so let's start by digging into `f_midi2_alloc_inst` within that same file, `drivers/usb/gadget/function/f_midi2.c`. Time to see what's happening under the hood. Hopefully, I can spot the missed resource allocation or deallocation.
|
| 9/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_alloc_inst]
Results: map[SourceCode:2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
2656: return ERR_PTR(-ENOMEM);
2657:
2658: mutex_init(&opts->lock);
2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
2660: opts->info.process_ump = true;
2661: opts->info.static_block = true;
2662: opts->info.num_reqs = 32;
2663: opts->info.req_buf_size = 512;
2664:
2665: /* create the default ep */
2666: ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
2667: if (ret) {
2668: kfree(opts);
2669: return ERR_PTR(ret);
2670: }
2671:
2672: /* create the default block */
2673: ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
2674: if (ret) {
2675: kfree(ep_opts);
2676: kfree(opts);
2677: return ERR_PTR(ret);
2678: }
2679:
2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
2687: &f_midi2_ep_type);
2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
2689:
2690: config_group_init_type_name(&block_opts->group, "block.0",
2691: &f_midi2_block_type);
2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
2693:
2694: return &opts->func_inst;
2695: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 10/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 8330
output: 26
thoughts: 23
Thoughts: Here's my take on the problem:
**Root Cause Analysis - Memory Leak in Instance Deallocation**
Okay, so we've got a memory leak, and it's rearing its ugly head during instance deallocation. That immediately narrows the focus. If the leak were happening at any other time, I'd have a much wider search space. But since it's when the instance is being freed, that tells me the problem lies within the freeing process itself. My gut says let's zero in on `f_midi2_free_inst`. That function is the likely culprit. It's the point of contact, the last chance to clean up before the memory is released back to the system. Now I need to dive into the implementation of that function with a fine-toothed comb to see what's being missed or improperly handled. Gotta start with the code and see what it's doing with all the instance memory.
|
| 11/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_free_inst]
Results: map[SourceCode:2636: static void f_midi2_free_inst(struct usb_function_instance *f)
2637: {
2638: struct f_midi2_opts *opts;
2639:
2640: opts = container_of(f, struct f_midi2_opts, func_inst);
2641:
2642: kfree(opts->info.iface_name);
2643: kfree(opts);
2644: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 12/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 8744
output: 28
thoughts: 127
Thoughts: **Analyzing MIDI Gadget Instance Management**
Okay, so `f_midi2_free_inst` is pretty straightforward – it's just responsible for cleaning up memory. Specifically, it frees `opts->info.iface_name` and the `opts` structure itself. No surprises there. But, looking at the setup side, `f_midi2_alloc_inst` is where the interesting stuff happens. It's the one responsible for creating those `ep_opts` and `block_opts` structures. Ah, and I see those are created using `f_midi2_ep_opts_create` and `f_midi2_block_opts_create`. Those functions are probably crucial to understanding how the configfs groups are constructed. Definitely need to dive into `f_midi2_ep_opts_create` and `f_midi2_block_opts_create` to fully grasp the implementation.
|
| 13/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_ep_opts_create]
Results: map[SourceCode:2497: /* create a f_midi2_ep_opts instance */
2498: static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
2499: unsigned int index,
2500: struct f_midi2_ep_opts **ep_p)
2501: {
2502: struct f_midi2_ep_opts *ep_opts;
2503:
2504: ep_opts = kzalloc_obj(*ep_opts);
2505: if (!ep_opts)
2506: return -ENOMEM;
2507:
2508: ep_opts->opts = opts;
2509: ep_opts->index = index;
2510:
2511: /* set up the default values */
2512: ep_opts->info.protocol = 2;
2513: ep_opts->info.protocol_caps = 3;
2514:
2515: opts->eps[index] = ep_opts;
2516: *ep_p = ep_opts;
2517: return 0;
2518: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 14/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 9454
output: 28
thoughts: 13
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in MIDI processing and likely dealing with embedded systems or a similar constrained environment.
**My Analysis of `f_midi2_block_opts_create`**
Alright, so I see `f_midi2_block_opts_create`. This function name immediately suggests we're in the realm of creating options or parameters related to a MIDI 2.0 processing block. Given my experience, I'm already mentally running through the typical MIDI 2.0 data structures and processing requirements. I'm thinking about the common use cases: maybe this is setting up parameters for a filter, a generator, or some kind of real-time transformation on MIDI 2.0 data streams.
I'm focusing on the "block" aspect. That implies a modular design, probably for real-time applications where we're linking these "blocks" together to form a processing pipeline. Efficiency is key here. Memory allocation is a primary concern. The "_opts" suffix tells me this isn't the core of the function but more likely related to customization, allowing a user (or another part of the system) to tailor the operation of this processing block.
Now, I'm thinking about what kinds of options we might need to configure. Is this about setting buffer sizes, data format specifics (like 7-bit vs. 16-bit encoding, channel numbering schemes), control message ranges, clock divider, error handling behavior, perhaps interrupt enable flags if it's tightly coupled with hardware. Is it about choosing between different processing algorithms that the block implements? Does it support different input and output MIDI formats?
I'm guessing this function likely returns a handle or pointer to a data structure (or a struct) that holds all these option parameters. That's a common pattern for configuration routines. I'm also wondering if it has any associated functions for validation (to ensure option values are valid) and destruction (to free the memory allocated by the options structure). I'll need to dig into the function's definition and usage context to flesh out these assumptions.
|
| 15/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_block_opts_create]
Results: map[SourceCode:2328: /* create a f_midi2_block_opts instance for the given block number */
2329: static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
2330: unsigned int blk,
2331: struct f_midi2_block_opts **block_p)
2332: {
2333: struct f_midi2_block_opts *block_opts;
2334: int ret = 0;
2335:
2336: mutex_lock(&ep_opts->opts->lock);
2337: if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
2338: ret = -EBUSY;
2339: goto out;
2340: }
2341:
2342: block_opts = kzalloc_obj(*block_opts);
2343: if (!block_opts) {
2344: ret = -ENOMEM;
2345: goto out;
2346: }
2347:
2348: block_opts->ep = ep_opts;
2349: block_opts->id = blk;
2350:
2351: /* set up the default values */
2352: block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION;
2353: block_opts->info.first_group = 0;
2354: block_opts->info.num_groups = 1;
2355: block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH;
2356: block_opts->info.active = 1;
2357:
2358: ep_opts->blks[blk] = block_opts;
2359: *block_p = block_opts;
2360:
2361: out:
2362: mutex_unlock(&ep_opts->opts->lock);
2363: return ret;
2364: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 16/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 10545
output: 28
thoughts: 62
Thoughts: **Examining ConfigFS Group Handling**
Okay, hold on a second. I need to make sure I understand this correctly. `ep_opts` and `block_opts`... those are defined as configfs groups, aren't they? That's right, they are. Now the critical question pops up – when the configfs directory containing those groups gets removed, does the system automatically free those resources? I'm betting that's handled by the configfs machinery, but I need to confirm. Let's dig into the details and make sure. Let me go back and double-check those release functions – specifically, `f_midi2_ep_opts_release` and `f_midi2_block_opts_release`. Those should reveal the freeing process for these configfs groups. I need to be absolutely certain we are releasing these resources properly.
|
| 17/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_ep_opts_release]
Results: map[SourceCode:2472: static void f_midi2_ep_opts_release(struct config_item *item)
2473: {
2474: struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
2475:
2476: kfree(opts->info.ep_name);
2477: kfree(opts->info.product_id);
2478: kfree(opts);
2479: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 18/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 10984
output: 28
thoughts: 13
Thoughts: Okay, here's my interpretation of that thought process, summarized as you requested, in the first person, and assuming I'm an expert in this domain:
**My Immediate Assessment: `f_midi2_block_opts_release`**
Alright, so the focus is immediately zeroing in on `f_midi2_block_opts_release`. This likely means we're dealing with a function or process related to MIDI 2.0 block options, and specifically, the "release" aspect. My expert understanding tells me this is probably concerned with freeing up memory or resources associated with a block of MIDI data that's no longer needed, maybe at the end of a message, or after a specific block processing step.
My initial thoughts are:
* **Memory Management is Critical:** The use of "release" implies a dynamic allocation of memory or some resource. I need to understand what's being released. Is this raw memory, handles to hardware, some internal data structure? Improper release leads to memory leaks or resource exhaustion, both potentially disastrous in a real-time MIDI system. I'm already anticipating the potential use of `free()`, `delete`, or similar deallocation methods.
* **Context Matters:** The function name alone gives little information. I need to understand what context this release operation occurs within. Is it part of a larger MIDI parser, a specific MIDI block processing pipeline, or something else entirely? The surrounding code is key to understanding its purpose. I'll need to look at how the memory or resources were allocated to understand the proper release procedure.
* **Error Handling and Safety:** Given that we're talking about releasing, I need to know about error handling. What happens if the release fails? Does the system gracefully degrade, or does it crash? Are there checks for null pointers or invalid handles before trying to free resources? Robustness is essential in a real-time audio environment.
* **Concurrency:** If this is a multi-threaded system, I need to be concerned with thread safety. Is there any potential for race conditions where multiple threads could be attempting to access and release the same resources simultaneously? I'll be looking for mutexes, semaphores, or other synchronization primitives to protect the release operation.
* **Documentation Check:** I'll also be expecting documentation, of course. Well-written code will come with clear explanations of its purpose, usage, and any assumptions it makes. I hope there's details about the parameters that are passed into the function.
In short, my mind is already starting to probe for potential pitfalls: memory leaks, resource exhaustion, thread safety issues, and the overall reliability of the release mechanism in this MIDI 2.0 context. It's time to dig deeper into the surrounding code and documentation.
|
| 19/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_block_opts_release]
Results: map[SourceCode:2310: static void f_midi2_block_opts_release(struct config_item *item)
2311: {
2312: struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
2313:
2314: kfree(opts->info.name);
2315: kfree(opts);
2316: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 20/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 11754
output: 26
thoughts: 296
Thoughts: **Understanding ConfigFS Group Management in MIDI2 Driver**
Okay, so I'm digging into this MIDI2 driver code and trying to grasp how the ConfigFS items are handled, specifically their lifetime. I'm seeing that when these ConfigFS items are released, they're freed. Makes sense. But here's where it gets interesting. In `f_midi2_alloc_inst`, I see these `ep_opts` and `block_opts` are added as *default* groups. The code block clearly shows the initialization and addition:
```c
config_group_init_type_name(&opts->func_inst.group, "",
&f_midi2_func_type);
config_group_init_type_name(&ep_opts->group, "ep.0",
&f_midi2_ep_type);
configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
config_group_init_type_name(&block_opts->group, "block.0",
&f_midi2_block_type);
configfs_add_default_group(&block_opts->group, &ep_opts->group);
```
My initial thought was, if they are default groups, then ConfigFS should manage their lifecycle in conjunction with the parent group, right? It seems logical. But then I quickly realized that's probably not the entire story. It's more likely that `configfs_add_default_group` adds them to the parent's `default_groups` list, indicating some kind of relationship, but not necessarily a direct reference. But does `configfs_add_default_group` actually *take* a reference, or is it just adding a pointer? That's what I need to clarify. I need to understand how the default groups are freed and if there's any memory management aspect that is critical to get right. Time to check the `configfs_add_default_group` function itself and how these default groups are released. That should clear this up.
|
| 21/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_add_default_group]
Results: map[SourceCode: 109: static inline void configfs_add_default_group(struct config_group *new_group,
110: struct config_group *group)
111: {
112: list_add_tail(&new_group->group_entry, &group->default_groups);
113: }
SourceFile:include/linux/configfs.h]
|
| 22/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 12650
output: 26
thoughts: 31
Thoughts: Here's my take on those thoughts:
**Analyzing Group Release Behavior in MIDI2**
Okay, so I'm thinking about how MIDI2 handles group releases. Specifically, when a parent group is freed, does that automatically cascade down and release all the child/default groups associated with it? I need to clarify that behavior, which is a critical aspect of memory management in this context. It's really easy to get leaks if there's any ambiguity in how these hierarchical structures are cleaned up.
Let me refresh my memory on that `f_midi2_free_inst` function again, as I suspect that's where the core logic for group deallocation resides. Gotta make sure I'm crystal clear on exactly how it handles the parent-child relationships and whether it recursively frees the defaults. I know a careful look through the source code will give me the answer.
|
| 23/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_free_inst]
Results: map[SourceCode:2636: static void f_midi2_free_inst(struct usb_function_instance *f)
2637: {
2638: struct f_midi2_opts *opts;
2639:
2640: opts = container_of(f, struct f_midi2_opts, func_inst);
2641:
2642: kfree(opts->info.iface_name);
2643: kfree(opts);
2644: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 24/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 13059
output: 21
thoughts: 99
Thoughts: **Delving into USB Gadget Function Instance Release**
Okay, so I'm thinking through this USB gadget code. I need to understand how function instances are cleaned up. Hang on, `f_midi2_free_inst` - that's called when a `usb_function_instance` is released, right? But hold on... `opts->func_inst.group` is a `config_group`! That's a ConfigFS thing. So, the `usb_function_instance` and the ConfigFS structure are entangled.
My brain's telling me to trace the removal of the ConfigFS directory. When a directory goes poof in ConfigFS, `configfs_rmdir` gets called. Now I need to connect the dots. I need to either examine `configfs_rmdir` itself, or figure out how the USB gadget subsystem handles function instance release within that context. Let's jump into `drivers/usb/gadget/configfs.c` and figure this out. I'm sure the answer lies there.
|
| 25/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 601: static struct config_group *function_make(
602: struct config_group *group,
603: const char *name)
604: {
605: struct gadget_info *gi;
606: struct usb_function_instance *fi;
607: char buf[MAX_NAME_LEN];
608: char *func_name;
609: char *instance_name;
610: int ret;
611:
612: if (strlen(name) >= MAX_NAME_LEN)
613: return ERR_PTR(-ENAMETOOLONG);
614:
615: scnprintf(buf, MAX_NAME_LEN, "%s", name);
616:
617: func_name = buf;
618: instance_name = strchr(func_name, '.');
619: if (!instance_name) {
620: pr_err("Unable to locate . in FUNC.INSTANCE\n");
621: return ERR_PTR(-EINVAL);
622: }
623: *instance_name = '\0';
624: instance_name++;
625:
626: fi = usb_get_function_instance(func_name);
627: if (IS_ERR(fi))
628: return ERR_CAST(fi);
629:
630: ret = config_item_set_name(&fi->group.cg_item, "%s", name);
631: if (ret) {
632: usb_put_function_instance(fi);
633: return ERR_PTR(ret);
634: }
635: if (fi->set_inst_name) {
636: ret = fi->set_inst_name(fi, instance_name);
637: if (ret) {
638: usb_put_function_instance(fi);
639: return ERR_PTR(ret);
640: }
641: }
642:
643: gi = container_of(group, struct gadget_info, functions_group);
644:
645: mutex_lock(&gi->lock);
646: list_add_tail(&fi->cfs_list, &gi->available_func);
647: mutex_unlock(&gi->lock);
648: return &fi->group;
649: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 26/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 14136
output: 21
thoughts: 14
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**My Line of Reasoning: Directory Removal and Function Dropping**
Alright, so the core trigger is a directory being deleted. That action immediately kicks off my internal process to execute `function_drop`. I'm assuming here that `function_drop` is specifically designed to handle the cleanup and necessary operations tied to the lifecycle of this directory. It's almost certainly more than just a direct "drop" in the SQL sense, but a well defined, perhaps multi-step procedure.
Given my understanding of similar systems, my mind immediately jumps to a few key areas that `function_drop` likely addresses:
* **Dependency Management:** I'm assuming this directory held dependencies or relationships with other parts of the system or data store. `Function_drop` must ensure that any associated data, references, or links are updated, removed, or gracefully handled to maintain data integrity and consistency. Think cascading deletes, reference counting, dependency tracking, or metadata updates – I need to be *certain* that nothing gets orphaned or corrupted.
* **Resource Cleanup:** A directory's removal is not simple. It's very likely that this directory held resources of some kind that now need to be released. These may include file handles, memory allocations, database connections, or other system resources. Efficient resource cleanup is critical to prevent leaks or degraded performance.
* **Metadata Consistency:** The system's overall metadata *must* reflect the fact that the directory no longer exists. This probably involves updates to internal catalogs, indices, configuration files, and any other systems that track this directory's presence.
* **Error Handling and Rollback:** There needs to be a robust strategy for handling failures within `function_drop`. What happens if a step fails? Can the process be rolled back, partially completed, or does it leave things in an inconsistent state? Proper error logging and exception handling are paramount. I need to be able to trust this function to clean up correctly, every time.
* **Concurrency and Locking:** If multiple processes or threads can interact with this directory or related resources, `function_drop` will almost certainly have to employ some form of locking or synchronization to ensure atomicity and prevent race conditions. The degree of concurrency control is what I need to understand next.
In summary, when I see "directory removed, `function_drop` called," my thought process begins to dive into a detailed examination of what the system expects to happen. And I know the implication is an important cleanup operation with potentially significant consequences for data integrity and system stability.
|
| 27/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 651: static void function_drop(
652: struct config_group *group,
653: struct config_item *item)
654: {
655: struct usb_function_instance *fi = to_usb_function_instance(item);
656: struct gadget_info *gi;
657:
658: gi = container_of(group, struct gadget_info, functions_group);
659:
660: mutex_lock(&gi->lock);
661: list_del(&fi->cfs_list);
662: mutex_unlock(&gi->lock);
663: config_item_put(item);
664: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 28/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 14954
output: 26
thoughts: 145
Thoughts: **Delving into the `config_item_put` Process**
Okay, so I need to trace the lifecycle of a `config_item`. Specifically, I'm trying to understand how `config_item_put(item)` eventually leads to the release of resources associated with a `usb_function_instance`. I recall that the ultimate release happens through a specific function tied to the item. In the case of a `usb_function_instance`, I'm guessing it's something like `usb_function_instance_release` – or at least, that's what I'd expect.
Now, the piece I'm focusing on is how this connection is made. I see that `fi->group.cg_item` is relevant here, it has a type, and I need to figure out how it’s set. My mind jumps to the allocation code. Let's see... Ah, there it is! In `f_midi2_alloc_inst`, I see:
```c
config_group_init_type_name(&opts->func_inst.group, "",
&f_midi2_func_type);
```
This is where the link happens! `config_group_init_type_name` is key. And that means I now need to understand what `f_midi2_func_type` is. It will define the release function and other characteristics of the `config_item`. I'll examine that next.
|
| 29/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_func_type]
Results: map[SourceCode:2629: static const struct config_item_type f_midi2_func_type = {
2630: .ct_item_ops = &f_midi2_item_ops,
2631: .ct_group_ops = &f_midi2_group_ops,
2632: .ct_attrs = f_midi2_attrs,
2633: .ct_owner = THIS_MODULE,
2634: };
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 30/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 15598
output: 26
thoughts: 11
Thoughts: Okay, here's my interpretation of those thoughts, crafted with the understanding that I'm speaking to a fellow expert:
**Delving into MIDI Item Operations**
Alright, let's break down this `f_midi2_item_ops` function. Immediately, I'm thinking about the low-level MIDI data structures involved. Specifically, I imagine this deals with the fundamental manipulation of MIDI item events – perhaps insertion, deletion, modification, time-stretching, quantization, and the like, all the core operations one needs for serious MIDI editing. I'm assuming it handles the underlying data, probably dealing with the bitwise manipulation of MIDI messages efficiently. Is it optimized for specific hardware? Is it designed to be threading-friendly? We'll have to consider those aspects when we review the code. The main question on my mind is: Does this implementation provide sufficient flexibility for the wide range of use cases we need to support in this project?
|
| 31/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_item_ops]
Results: map[SourceCode:2620: static const struct configfs_item_operations f_midi2_item_ops = {
2621: .release = f_midi2_opts_release,
2622: };
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 32/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 15924
output: 26
thoughts: 11
Thoughts: Okay, here's my summary, in the first person, as an expert in the field would perceive the thought process:
**Diving into `f_midi2_opts_release`**
Alright, let's dissect this. "And `f_midi2_opts_release`"... my initial thought is, "Okay, this is about MIDI 2.0 and the options release, likely concerning a function or method involved in managing or releasing MIDI 2.0-specific option data or resources." I immediately start mentally categorizing this. Is this low-level, high-level, embedded, host, a library function, or a hardware driver? Based on the naming convention (`f_`), I'm leaning towards a C function, or something functionally similar, in a lower level library. I need more context to be sure, of course.
The "release" part instantly flags potential memory management issues. Is it about freeing allocated memory, resetting option states, closing a connection, or maybe restoring default MIDI 2.0 settings? It's all very dependent on the context and the overall architecture, but my mind is already running through common memory management pitfalls and potential mutex or semaphore interactions that could be involved if this is a shared resource.
I also wonder about the potential data structures involved. Is this handling single option values, or batches of options? Are we looking at individual messages or a complete initialization sequence? Does it take a pointer to data, and release that, or does it release options from a higher-level object? What about error handling? Are we expecting a return value to indicate success or failure, or are exceptions being thrown?
Now, the important part: what happens *before* this function is called? Is there an "allocate" or "initialize" counterpart? How is the option data set up in the first place? And after it's called, are there any further actions required, such as notifications or cleanup of other resources?
Essentially, I need to know the bigger picture. I'm already formulating questions about the function's signature, its dependencies, its intended purpose within the system, and its potential impact on the overall MIDI 2.0 implementation. This is going to be interesting.
|
| 33/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_opts_release]
Results: map[SourceCode:2613: static void f_midi2_opts_release(struct config_item *item)
2614: {
2615: struct f_midi2_opts *opts = to_f_midi2_opts(item);
2616:
2617: usb_put_function_instance(&opts->func_inst);
2618: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 34/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 16558
output: 23
thoughts: 131
Thoughts: **Investigating Resource Deallocation in USB Function Instances**
Okay, so `usb_put_function_instance` is the key. I see it calls `fi->free_func_inst(fi)` when the reference count drops to zero. That's standard practice. Now, I need to trace this specifically for the MIDI2 function. Ah, here it is, `f_midi2_free_inst` gets called. This function *does* free `opts`. But... wait a minute, what about the default option groups? Specifically, `ep_opts` and `block_opts`? Are those getting handled correctly? I have a feeling there's a potential leak or improper cleanup lurking there.
When the parent config group (`opts->func_inst.group`) gets `config_item_put` on it, does ConfigFS automatically release those default groups? I don't think so, I need to go deeper. Let's dig into the ConfigFS code. I need to check `configfs_rmdir` and `config_item_put` in `fs/configfs/item.c`. That's where the magic happens and I'll find out what's really going on with the default groups.
|
| 35/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:config_item_release]
Results: map[SourceCode: 135: static void config_item_release(struct kref *kref)
136: {
137: config_item_cleanup(container_of(kref, struct config_item, ci_kref));
138: }
SourceFile:fs/configfs/item.c]
|
| 36/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 17072
output: 23
thoughts: 8
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Thinking Through Config Item Cleanup**
Alright, let's break this down. The core issue here seems to revolve around the handling and maintenance of configuration items. Specifically, I'm focusing on the `config_item_cleanup` component or function. My immediate thoughts go to several key areas:
* **Necessity & Scope:** *Why* are we even cleaning up config items? Is this about stale data, redundant entries, security vulnerabilities related to outdated configurations, or perhaps optimization of resource usage? Knowing the *reason* dictates the approach. Is this a system-wide cleanup, focused on specific modules, or triggered by certain events? Defining the scope is critical to avoid impacting other components.
* **Identification Strategy:** How do we *identify* items that need to be cleaned? Are we relying on timestamps, version numbers, usage metrics, or perhaps some form of external validation? The accuracy of this identification process is paramount, especially if there are dependencies or cascading effects on the rest of the system. I need to be certain about the criteria for deletion.
* **Cleanup Mechanism:** What is the actual cleanup process? Is it a simple deletion, an archival, a re-validation, or a more complex series of steps? How will this cleanup interact with existing data models, permissions, and auditing systems? Any modification to the database needs to be carefully examined.
* **Error Handling and Rollback:** What happens if the cleanup fails midway? Do we have robust error handling and, crucially, a rollback mechanism to prevent data corruption or inconsistencies? Resilience is paramount, as a failed cleanup could lead to critical system issues.
* **Concurrency & Locking:** If this cleanup operation involves concurrent processing or database interactions, what measures are in place to ensure data integrity and avoid race conditions or deadlocks? The cleanup must operate correctly at any level of activity.
* **Performance Considerations:** How will this cleanup process affect system performance, especially during peak load? Are we dealing with potential bottlenecks or performance impacts that need to be optimized? Should it be scheduled off-peak, and how aggressive should the policy be?
* **Logging and Auditing:** Detailed logging and auditing are essential. We need to track *what* was cleaned, *when*, and *why*, along with any errors that occurred. This information is crucial for debugging, auditing, and meeting compliance requirements.
* **Testing:** Thorough testing, including unit tests, integration tests, and potentially even performance tests, is a non-negotiable. We need to validate the identification logic, the cleanup process itself, the error handling, and the rollback mechanism. Edge cases and corner conditions need to be rigorously examined.
In short, "config\_item\_cleanup" is the starting point for a cascade of design considerations, each requiring careful attention to detail. Ignoring any of these aspects could result in instability, data loss, or system-wide disruption.
|
| 37/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:config_item_cleanup]
Results: map[SourceCode: 117: static void config_item_cleanup(struct config_item *item)
118: {
119: const struct config_item_type *t = item->ci_type;
120: struct config_group *s = item->ci_group;
121: struct config_item *parent = item->ci_parent;
122:
123: pr_debug("config_item %s: cleaning up\n", config_item_name(item));
124: if (item->ci_name != item->ci_namebuf)
125: kfree(item->ci_name);
126: item->ci_name = NULL;
127: if (t && t->ct_item_ops && t->ct_item_ops->release)
128: t->ct_item_ops->release(item);
129: if (s)
130: config_group_put(s);
131: if (parent)
132: config_item_put(parent);
133: }
SourceFile:fs/configfs/item.c]
|
| 38/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 18053
output: 22
thoughts: 70
Thoughts: **My Current Analysis of the Configfs Group Cleanup**
Okay, hold on a second... It's just hit me: `config_item_cleanup` *doesn't* actually free the default groups. That feels... wrong. I need to be sure. My recollection is that those default groups are linked to `group->default_groups`. Now, where and how are these default groups being created?
Ah, right! The creation happens when a directory is established. Specifically, `configfs_mkdir` does the deed, setting up those initial groups. Time to dive into the code. I need to refresh my memory on `configfs_mkdir` to pinpoint precisely what's happening. Let's head over to `fs/configfs/dir.c` and dig in.
|
| 39/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode:1289: static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
1290: struct dentry *dentry, umode_t mode)
1291: {
1292: int ret = 0;
1293: int module_got = 0;
1294: struct config_group *group = NULL;
1295: struct config_item *item = NULL;
1296: struct config_item *parent_item;
1297: struct configfs_subsystem *subsys;
1298: struct configfs_dirent *sd;
1299: const struct config_item_type *type;
1300: struct module *subsys_owner = NULL, *new_item_owner = NULL;
1301: struct configfs_fragment *frag;
1302: struct name_snapshot n;
1303: const char *name;
1304:
1305: take_dentry_name_snapshot(&n, dentry);
1306: name = n.name.name;
1307:
1308: sd = dentry->d_parent->d_fsdata;
1309:
1310: /*
1311: * Fake invisibility if dir belongs to a group/default groups hierarchy
1312: * being attached
1313: */
1314: if (!configfs_dirent_is_ready(sd)) {
1315: ret = -ENOENT;
1316: goto out;
1317: }
1318:
1319: if (!(sd->s_type & CONFIGFS_USET_DIR)) {
1320: ret = -EPERM;
1321: goto out;
1322: }
1323:
1324: frag = new_fragment();
1325: if (!frag) {
1326: ret = -ENOMEM;
1327: goto out;
1328: }
1329:
1330: /* Get a working ref for the duration of this function */
1331: parent_item = configfs_get_config_item(dentry->d_parent);
1332: type = parent_item->ci_type;
1333: subsys = to_config_group(parent_item)->cg_subsys;
1334: BUG_ON(!subsys);
1335:
1336: if (!type || !type->ct_group_ops ||
1337: (!type->ct_group_ops->make_group &&
1338: !type->ct_group_ops->make_item)) {
1339: ret = -EPERM; /* Lack-of-mkdir returns -EPERM */
1340: goto out_put;
1341: }
1342:
1343: /*
1344: * The subsystem may belong to a different module than the item
1345: * being created. We don't want to safely pin the new item but
1346: * fail to pin the subsystem it sits under.
1347: */
1348: if (!subsys->su_group.cg_item.ci_type) {
1349: ret = -EINVAL;
1350: goto out_put;
1351: }
1352: subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
1353: if (!try_module_get(subsys_owner)) {
1354: ret = -EINVAL;
1355: goto out_put;
1356: }
1357:
1358: mutex_lock(&subsys->su_mutex);
1359: if (type->ct_group_ops->make_group) {
1360: group = type->ct_group_ops->make_group(to_config_group(parent_item), name);
1361: if (!group)
1362: group = ERR_PTR(-ENOMEM);
1363: if (!IS_ERR(group)) {
1364: link_group(to_config_group(parent_item), group);
1365: item = &group->cg_item;
1366: } else
1367: ret = PTR_ERR(group);
1368: } else {
1369: item = type->ct_group_ops->make_item(to_config_group(parent_item), name);
1370: if (!item)
1371: item = ERR_PTR(-ENOMEM);
1372: if (!IS_ERR(item))
1373: link_obj(parent_item, item);
1374: else
1375: ret = PTR_ERR(item);
1376: }
1377: mutex_unlock(&subsys->su_mutex);
1378:
1379: if (ret) {
1380: /*
1381: * If ret != 0, then link_obj() was never called.
1382: * There are no extra references to clean up.
1383: */
1384: goto out_subsys_put;
1385: }
1386:
1387: /*
1388: * link_obj() has been called (via link_group() for groups).
1389: * From here on out, errors must clean that up.
1390: */
1391:
1392: type = item->ci_type;
1393: if (!type) {
1394: ret = -EINVAL;
1395: goto out_unlink;
1396: }
1397:
1398: new_item_owner = type->ct_owner;
1399: if (!try_module_get(new_item_owner)) {
1400: ret = -EINVAL;
1401: goto out_unlink;
1402: }
1403:
1404: /*
1405: * I hate doing it this way, but if there is
1406: * an error, module_put() probably should
1407: * happen after any cleanup.
1408: */
1409: module_got = 1;
1410:
1411: /*
1412: * Make racing rmdir() fail if it did not tag parent with
1413: * CONFIGFS_USET_DROPPING
1414: * Note: if CONFIGFS_USET_DROPPING is already set, attach_group() will
1415: * fail and let rmdir() terminate correctly
1416: */
1417: spin_lock(&configfs_dirent_lock);
1418: /* This will make configfs_detach_prep() fail */
1419: sd->s_type |= CONFIGFS_USET_IN_MKDIR;
1420: spin_unlock(&configfs_dirent_lock);
1421:
1422: if (group)
1423: ret = configfs_attach_group(item, dentry, frag);
1424: else
1425: ret = configfs_attach_item(item, dentry, frag);
1426:
1427: spin_lock(&configfs_dirent_lock);
1428: sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
1429: if (!ret)
1430: configfs_dir_set_ready(dentry->d_fsdata);
1431: spin_unlock(&configfs_dirent_lock);
1432:
1433: out_unlink:
1434: if (ret) {
1435: /* Tear down everything we built up */
1436: mutex_lock(&subsys->su_mutex);
1437:
1438: client_disconnect_notify(parent_item, item);
1439: if (group)
1440: unlink_group(group);
1441: else
1442: unlink_obj(item);
1443: client_drop_item(parent_item, item);
1444:
1445: mutex_unlock(&subsys->su_mutex);
1446:
1447: if (module_got)
1448: module_put(new_item_owner);
1449: }
1450:
1451: out_subsys_put:
1452: if (ret)
1453: module_put(subsys_owner);
1454:
1455: out_put:
1456: /*
1457: * link_obj()/link_group() took a reference from child->parent,
1458: * so the parent is safely pinned. We can drop our working
1459: * reference.
1460: */
1461: config_item_put(parent_item);
1462: put_fragment(frag);
1463:
1464: out:
1465: release_dentry_name_snapshot(&n);
1466: return ERR_PTR(ret);
1467: }
SourceFile:fs/configfs/dir.c]
|
| 40/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 20901
output: 24
thoughts: 17
Thoughts: Here's my thought process, as if I were considering this myself:
**ConfigFS Group Attachment Point**
Okay, let me think this through... *Ah-ha!* Right, `configfs_attach_group`... that's where the default groups get hooked up. So, when the system or application or whatever it is is setting up, that's the key function to trace. It has to be - that's where the basic configuration comes in and associates groups. Let's see if I can find the default group attachment in here.
|
| 41/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_attach_group]
Results: map[SourceCode: 891: static int configfs_attach_group(struct config_item *item,
892: struct dentry *dentry,
893: struct configfs_fragment *frag)
894: {
895: int ret;
896: struct configfs_dirent *sd;
897:
898: ret = configfs_attach_item(item, dentry, frag);
899: if (!ret) {
900: sd = dentry->d_fsdata;
901: sd->s_type |= CONFIGFS_USET_DIR;
902:
903: /*
904: * FYI, we're faking mkdir in populate_groups()
905: * We must lock the group's inode to avoid races with the VFS
906: * which can already hit the inode and try to add/remove entries
907: * under it.
908: *
909: * We must also lock the inode to remove it safely in case of
910: * error, as rmdir() would.
911: */
912: inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
913: configfs_adjust_dir_dirent_depth_before_populate(sd);
914: ret = populate_groups(to_config_group(item), frag);
915: if (ret) {
916: configfs_detach_group(dentry);
917: d_inode(dentry)->i_flags |= S_DEAD;
918: dont_mount(dentry);
919: }
920: configfs_adjust_dir_dirent_depth_after_populate(sd);
921: inode_unlock(d_inode(dentry));
922: if (ret)
923: d_delete(dentry);
924: }
925:
926: return ret;
927: }
SourceFile:fs/configfs/dir.c]
|
| 42/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 21651
output: 21
thoughts: 6
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**My Reasoning for `populate_groups`**
Alright, I'm thinking about this `populate_groups` function, and how it fits into the larger architecture. My initial instinct is to consider its purpose. From what I see I must be populating groups in a database or a data structure somehow. It's likely a critical piece in initializing some kind of organizational framework. I need to drill down into the details. What exactly constitutes a "group" in this context? Is it based on attributes, relationships, user roles, some combination of these? Understanding the defining characteristics of a group will be key.
I must also think about the source of the data to populate these groups. Is it pulling from a single data source or multiple sources? Are the sources structured or unstructured? Are there any data cleaning or transformation steps involved before the population process? And how does this function handle edge cases or error conditions? Data integrity is paramount, so I have to think about validation and error handling to ensure I'm not creating corrupt or inconsistent groups.
Moreover, I'm already anticipating the performance implications. The sheer volume of data involved could be significant, so I must assess the complexity of the population algorithm. Is this a naive approach, or is there an opportunity for optimization? Can I leverage indexing, caching, or parallel processing to speed things up? I'm wondering what the time and space complexity of the process is.
Finally, how does `populate_groups` integrate with other components of the system? Are there any dependencies? How does it interact with other modules that might be responsible for group management, access control, or reporting? I must consider the entire workflow to make sure that this function fits seamlessly within the larger system.
|
| 43/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:populate_groups]
Results: map[SourceCode: 734: static int populate_groups(struct config_group *group,
735: struct configfs_fragment *frag)
736: {
737: struct dentry *parent = group->cg_item.ci_dentry;
738: struct config_group *new_group;
739:
740: list_for_each_entry(new_group, &group->default_groups, group_entry) {
741: int ret = create_default_group(parent, new_group, frag);
742: if (ret)
743: return ret;
744: }
745: return 0;
746: }
SourceFile:fs/configfs/dir.c]
|
| 44/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 22283
output: 23
thoughts: 8
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented in the first person:
**My Reasoning on Default Group Creation**
Right, so I'm thinking about the function to create a default group. This is a core component, something that sets up the initial environment. I need to make sure this is robust and handles all the edge cases properly. First, I have to consider the initial state of the system and make sure all default settings are applied correctly, I have to ensure the group has a unique ID and a meaningful, recognizable name. I'm also considering the permissions associated with this default group, and those permissions need to align with best practices for security and usability.
Let's see, what else? I should ensure it's integrated seamlessly into the existing workflow. Any dependencies on external services or configuration files need to be handled gracefully – error handling is critical here. If something goes wrong during creation, I need informative logging and perhaps a fallback mechanism or an exception that can be caught and handled gracefully upstream.
Furthermore, I have to think about how this interacts with the rest of the system. Does the creation trigger any downstream events or notifications? Does it update any caches or indexes? I'll need to consider those implications. And, of course, documentation. I need to make this function well-documented so that anyone coming along later (including myself in six months!) can easily understand its purpose and behavior. Ultimately, I'm aiming for a default group that is both useful out-of-the-box and easily configurable to meet specific needs.
|
| 45/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:create_default_group]
Results: map[SourceCode: 695: /*
696: * This fakes mkdir(2) on a default_groups[] entry. It
697: * creates a dentry, attachs it, and then does fixup
698: * on the sd->s_type.
699: *
700: * We could, perhaps, tweak our parent's ->mkdir for a minute and
701: * try using vfs_mkdir. Just a thought.
702: */
703: static int create_default_group(struct dentry *parent,
704: struct config_group *group,
705: struct configfs_fragment *frag)
706: {
707: int ret;
708: struct configfs_dirent *sd;
709: /* We trust the caller holds a reference to parent */
710: struct dentry *child;
711:
712: if (!group->cg_item.ci_name)
713: group->cg_item.ci_name = group->cg_item.ci_namebuf;
714:
715: ret = -ENOMEM;
716: child = d_alloc_name(parent, group->cg_item.ci_name);
717: if (child) {
718: d_add(child, NULL);
719:
720: ret = configfs_attach_group(&group->cg_item, child, frag);
721: if (!ret) {
722: sd = child->d_fsdata;
723: sd->s_type |= CONFIGFS_USET_DEFAULT;
724: } else {
725: BUG_ON(d_inode(child));
726: d_drop(child);
727: }
728: dput(child);
729: }
730:
731: return ret;
732: }
SourceFile:fs/configfs/dir.c]
|
| 46/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 23226
output: 23
thoughts: 35
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Navigating the Configuration Filesystem Group Removal**
Alright, let me unpack this in my head. `create_default_group` essentially lays the groundwork, right? It's the function that crafts the `dentry` structure for the default group, linking it seamlessly to the relevant group configuration. Standard stuff. The key trigger here, in the removal process, is `configfs_rmdir`. This is where it gets interesting, as this likely implies the deletion of the parent group. What happens when it is triggered from a parent directory? This implies recursive operations on the subtree. We are therefore responsible for making sure we can handle this gracefully.
|
| 47/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode:1469: static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
1470: {
1471: struct config_item *parent_item;
1472: struct config_item *item;
1473: struct configfs_subsystem *subsys;
1474: struct configfs_dirent *sd;
1475: struct configfs_fragment *frag;
1476: struct module *subsys_owner = NULL, *dead_item_owner = NULL;
1477: int ret;
1478:
1479: sd = dentry->d_fsdata;
1480: if (sd->s_type & CONFIGFS_USET_DEFAULT)
1481: return -EPERM;
1482:
1483: /* Get a working ref until we have the child */
1484: parent_item = configfs_get_config_item(dentry->d_parent);
1485: subsys = to_config_group(parent_item)->cg_subsys;
1486: BUG_ON(!subsys);
1487:
1488: if (!parent_item->ci_type) {
1489: config_item_put(parent_item);
1490: return -EINVAL;
1491: }
1492:
1493: /* configfs_mkdir() shouldn't have allowed this */
1494: BUG_ON(!subsys->su_group.cg_item.ci_type);
1495: subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
1496:
1497: /*
1498: * Ensure that no racing symlink() will make detach_prep() fail while
1499: * the new link is temporarily attached
1500: */
1501: do {
1502: struct dentry *wait;
1503:
1504: mutex_lock(&configfs_symlink_mutex);
1505: spin_lock(&configfs_dirent_lock);
1506: /*
1507: * Here's where we check for dependents. We're protected by
1508: * configfs_dirent_lock.
1509: * If no dependent, atomically tag the item as dropping.
1510: */
1511: ret = sd->s_dependent_count ? -EBUSY : 0;
1512: if (!ret) {
1513: ret = configfs_detach_prep(sd, &wait);
1514: if (ret)
1515: configfs_detach_rollback(sd);
1516: }
1517: spin_unlock(&configfs_dirent_lock);
1518: mutex_unlock(&configfs_symlink_mutex);
1519:
1520: if (ret) {
1521: if (ret != -EAGAIN) {
1522: config_item_put(parent_item);
1523: return ret;
1524: }
1525:
1526: /* Wait until the racing operation terminates */
1527: inode_lock(d_inode(wait));
1528: inode_unlock(d_inode(wait));
1529: dput(wait);
1530: }
1531: } while (ret == -EAGAIN);
1532:
1533: frag = sd->s_frag;
1534: if (down_write_killable(&frag->frag_sem)) {
1535: spin_lock(&configfs_dirent_lock);
1536: configfs_detach_rollback(sd);
1537: spin_unlock(&configfs_dirent_lock);
1538: config_item_put(parent_item);
1539: return -EINTR;
1540: }
1541: frag->frag_dead = true;
1542: up_write(&frag->frag_sem);
1543:
1544: /* Get a working ref for the duration of this function */
1545: item = configfs_get_config_item(dentry);
1546:
1547: /* Drop reference from above, item already holds one. */
1548: config_item_put(parent_item);
1549:
1550: if (item->ci_type)
1551: dead_item_owner = item->ci_type->ct_owner;
1552:
1553: if (sd->s_type & CONFIGFS_USET_DIR) {
1554: configfs_detach_group(dentry);
1555:
1556: mutex_lock(&subsys->su_mutex);
1557: client_disconnect_notify(parent_item, item);
1558: unlink_group(to_config_group(item));
1559: } else {
1560: configfs_detach_item(dentry);
1561:
1562: mutex_lock(&subsys->su_mutex);
1563: client_disconnect_notify(parent_item, item);
1564: unlink_obj(item);
1565: }
1566:
1567: client_drop_item(parent_item, item);
1568: mutex_unlock(&subsys->su_mutex);
1569:
1570: /* Drop our reference from above */
1571: config_item_put(item);
1572:
1573: module_put(dead_item_owner);
1574: module_put(subsys_owner);
1575:
1576: return 0;
1577: }
SourceFile:fs/configfs/dir.c]
|
| 48/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 25067
output: 24
thoughts: 22
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing `configfs_detach_group`**
Okay, so `configfs_detach_group` is the function responsible for detaching a group. That's the key operation I need to understand. Let's dig into it. I need to see exactly what's going on under the hood when a group is detached within the ConfigFS framework. What are the resources being released? What locks are being acquired and released? I suspect it's going to be doing cleanup on all the targets within that particular group, unlinking from the filesystem, and likely some reference counting adjustments. I should see how it handles potential errors during the unlinking process and how it ensures consistency. I also need to be mindful of any interaction with potential concurrent operations on the group or its children. Time to dive into the code!
|
| 49/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_detach_group]
Results: map[SourceCode: 884: /* Caller holds the mutex of the group's inode */
885: static void configfs_detach_group(struct dentry *dentry)
886: {
887: detach_groups(dentry);
888: configfs_detach_item(dentry);
889: }
SourceFile:fs/configfs/dir.c]
|
| 50/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 25420
output: 21
thoughts: 6
Thoughts: Okay, here's my interpretation of that thought process, summarized as you requested:
**Deconstructing and Understanding Group Dynamics**
Alright, let's break this down. When I see `detach_groups`, my mind immediately goes to the practical implications. I'm thinking about situations where we need to isolate or remove certain groups from a larger context. This could be for a variety of reasons, right? Maybe it's data privacy, perhaps it's resource allocation, or potentially for a more refined analysis where the presence of certain groups introduces unwanted noise or bias.
I'm considering the potential impact on downstream processes. What happens to the detached groups? Are they archived, deleted, or transferred elsewhere? The mechanics of *how* the detachment happens are paramount, are we talking about a hard split, a soft copy, or a more complex re-attribution? The specific implementation will hinge heavily on the specific dataset and its use case. I need to be thinking about data integrity and how we ensure consistency after the split. Any potential dependencies between the detached and remaining groups are important. We can't have a cascading failure.
I also need to evaluate the implications on any underlying models or algorithms that are working with this dataset, and the specific architecture of the system. I also think about how to define these "groups". The underlying grouping mechanism and the process for defining those groups is a critical factor and must be defined very carefully. I'm mentally checking what edge cases I should consider, along with how to maintain metadata and context during the detaching process. Basically, I'm already formulating a checklist for best practices to guarantee the change is well-managed.
|
| 51/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 664: static void detach_groups(struct dentry *dentry)
665: {
666: struct dentry *child;
667: struct configfs_dirent *parent_sd;
668: struct configfs_dirent *sd, *next;
669:
670: parent_sd = dentry->d_fsdata;
671: spin_lock(&configfs_dirent_lock);
672: for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
673: next = next_dirent(parent_sd, sd);
674: if (!(sd->s_type & CONFIGFS_USET_DEFAULT))
675: continue;
676:
677: child = dget(sd->s_dentry);
678: spin_unlock(&configfs_dirent_lock);
679:
680: inode_lock(d_inode(child));
681:
682: configfs_detach_group(child);
683: d_inode(child)->i_flags |= S_DEAD;
684: dont_mount(child);
685:
686: inode_unlock(d_inode(child));
687:
688: d_delete(child);
689: dput(child);
690: spin_lock(&configfs_dirent_lock);
691: }
692: spin_unlock(&configfs_dirent_lock);
693: }
SourceFile:fs/configfs/dir.c]
|
| 52/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 26259
output: 24
thoughts: 44
Thoughts: Okay, here's my interpretation of this thought process, keeping in mind my expertise:
**Diving into the ConfigFS Detachment Logic**
Alright, let's break this down. My mind immediately jumped into the details when I saw `detach_groups`. I know that means `configfs_detach_group(child)` is probably invoked in there somewhere, and that's going to lead us to the actual detachment functionality, the nuts and bolts. And yes, confirmed! `configfs_detach_group(child)` is indeed called, which then triggers `configfs_detach_item(child)`. That's the real core function I need to understand here. My experience tells me that's where the resource cleanup and structure unlinking are going to be handled. I need to refresh my memory on what that `child` is and how it ties into the overall ConfigFS tree structure, and then zoom in on `configfs_detach_item`'s internal workings. Let's see what that function does!
|
| 53/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_detach_item]
Results: map[SourceCode: 835: /* Caller holds the mutex of the item's inode */
836: static void configfs_detach_item(struct dentry *dentry)
837: {
838: detach_attrs(dentry);
839: configfs_remove_dir(dentry);
840: }
SourceFile:fs/configfs/dir.c]
|
| 54/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 26662
output: 24
thoughts: 130
Thoughts: Here's my thought process, as an expert in this area:
**Digging into ConfigFS Reference Counting**
Okay, so I'm looking at `configfs_detach_item` and something feels off. It doesn't seem to call `config_item_put` on the default groups, which strikes me as a potential memory leak or at least a reference counting inconsistency. That's a red flag. Let me see...
Now, I'm questioning how references are handled when we call `create_default_group`. Does it increment the refcount on the default group? I need to clarify that. Let's trace through that function again to be sure...
Okay, re-examining `create_default_group`... I see this line: `ret = configfs_attach_group(&group->cg_item, child, frag);`. So `create_default_group` calls `configfs_attach_group`. `configfs_attach_group` is interesting because it calls `configfs_attach_item`, which in turn calls `configfs_make_dirent`. My attention is now focused on the items.
Let's quickly check this. Does `configfs_attach_item` increment the reference count of the item? I need to be sure the item is properly accounted for in the system. I have to verify where these references are coming from. The goal is to make sure this item is accounted for and not leaked.
|
| 55/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_attach_item]
Results: map[SourceCode: 842: /*
843: * The goal is that configfs_attach_item() (and
844: * configfs_attach_group()) can be called from either the VFS or this
845: * module. That is, they assume that the items have been created,
846: * the dentry allocated, and the dcache is all ready to go.
847: *
848: * If they fail, they must clean up after themselves as if they
849: * had never been called. The caller (VFS or local function) will
850: * handle cleaning up the dcache bits.
851: *
852: * configfs_detach_group() and configfs_detach_item() behave similarly on
853: * the way out. They assume that the proper semaphores are held, they
854: * clean up the configfs items, and they expect their callers will
855: * handle the dcache bits.
856: */
857: static int configfs_attach_item(struct config_item *item,
858: struct dentry *dentry,
859: struct configfs_fragment *frag)
860: {
861: int ret;
862:
863: ret = configfs_create_dir(item, dentry, frag);
864: if (!ret) {
865: ret = populate_attrs(item);
866: if (ret) {
867: /*
868: * We are going to remove an inode and its dentry but
869: * the VFS may already have hit and used them. Thus,
870: * we must lock them as rmdir() would.
871: */
872: inode_lock(d_inode(dentry));
873: configfs_detach_item(dentry);
874: d_inode(dentry)->i_flags |= S_DEAD;
875: dont_mount(dentry);
876: inode_unlock(d_inode(dentry));
877: d_delete(dentry);
878: }
879: }
880:
881: return ret;
882: }
SourceFile:fs/configfs/dir.c]
|
| 56/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 27787
output: 24
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my take on `configfs_create_dir`, as if these are my immediate thoughts:
**Dissecting `configfs_create_dir`**
Alright, so we're looking at `configfs_create_dir`. Immediately, I'm thinking, this must be a core function for manipulating the configfs structure, probably responsible for... creating a directory, obviously! But the details are key here. I need to figure out the implications of that directory's existence within the configfs hierarchy. What exactly is being represented? Is it a device, an attribute grouping, a logical container of some kind?
The name suggests a directory, but configfs is all about virtual filesystems. So this directory isn't *really* a traditional filesystem directory, right? It's likely a virtual node that maps to an underlying data structure somewhere. The function will need to manage the metadata, the permissions, the links, everything required to make this new virtual directory function correctly within the configfs framework.
I'll be looking for things like:
* **Error handling:** What happens if the directory already exists? Or if the system is low on resources? What error codes are being returned and how are they being handled upstream? I expect robust error handling is crucial for configfs, as it's often a crucial user-space interface.
* **Security Context:** This likely involves setting the appropriate ownership and permissions for the new directory. This is critical for controlling access to whatever this directory represents. I'll need to understand how the security context is being established (root, particular user? What capabilities are required?)
* **Data Structure Association:** What underlying data structure in the kernel is linked to this directory? Is it a generic container or something specific to the device/interface/whatever configfs is modeling? How is that association maintained? And how are the relevant data structures being locked or managed to ensure atomicity and thread safety?
* **Lifecycle Management:** Is there a corresponding `configfs_remove_dir` or something similar? How does that work? What happens to the underlying data when the directory is removed? Are there any cleanup tasks required to free resources?
Ultimately, the goal is to fully understand how this `configfs_create_dir` function fits into the bigger picture of configfs, and how it is employed to build and structure the virtual filesystem.
|
| 57/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_create_dir]
Results: map[SourceCode: 293: static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
294: struct configfs_fragment *frag)
295: {
296: int error;
297: umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
298: struct dentry *p = dentry->d_parent;
299: struct inode *p_inode = d_inode(p);
300: struct inode *inode;
301:
302: BUG_ON(!item);
303:
304: error = configfs_make_dirent(p->d_fsdata, dentry, item, mode,
305: CONFIGFS_DIR | CONFIGFS_USET_CREATING,
306: frag);
307: if (unlikely(error))
308: return error;
309:
310: configfs_set_dir_dirent_depth(p->d_fsdata, dentry->d_fsdata);
311: inode = configfs_create(dentry, mode);
312: if (IS_ERR(inode))
313: goto out_remove;
314:
315: inode->i_op = &configfs_dir_inode_operations;
316: inode->i_fop = &configfs_dir_operations;
317: /* directory inodes start off with i_nlink == 2 (for "." entry) */
318: inc_nlink(inode);
319: d_make_persistent(dentry, inode);
320: inc_nlink(p_inode);
321: inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode));
322: item->ci_dentry = dentry;
323: return 0;
324:
325: out_remove:
326: configfs_put(dentry->d_fsdata);
327: configfs_remove_dirent(dentry);
328: return PTR_ERR(inode);
329: }
SourceFile:fs/configfs/dir.c]
|
| 58/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 28979
output: 25
thoughts: 35
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into the `configfs_make_dirent` Implementation**
Wait a second...I'm reviewing this `configfs` code, and something's bugging me. Specifically, I'm looking at `configfs_make_dirent` and it *doesn't* appear to take a reference on the `item` structure it's using? That seems unusual, even potentially problematic in a concurrent environment like this. I'd expect some form of reference counting or locking to ensure the `item` data remains valid while the directory entry is active. This smells like a subtle, but potentially nasty, race condition. I need to understand this better. Let's get right into the code for `configfs_make_dirent` and see what's actually happening under the hood. I need to confirm whether my concern is valid or if the surrounding code handles this implicitly somehow. Time to dig in and clarify this!
|
| 59/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_make_dirent]
Results: map[SourceCode: 253: int configfs_make_dirent(struct configfs_dirent * parent_sd,
254: struct dentry * dentry, void * element,
255: umode_t mode, int type, struct configfs_fragment *frag)
256: {
257: struct configfs_dirent * sd;
258:
259: sd = configfs_new_dirent(parent_sd, element, type, frag);
260: if (IS_ERR(sd))
261: return PTR_ERR(sd);
262:
263: sd->s_mode = mode;
264: sd->s_dentry = dentry;
265: if (dentry)
266: dentry->d_fsdata = configfs_get(sd);
267:
268: return 0;
269: }
SourceFile:fs/configfs/dir.c]
|
| 60/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 29542
output: 23
thoughts: 481
Thoughts: **Digging into ConfigFS Reference Counting**
Okay, so I'm focusing on how `configfs` handles references, specifically with default groups. My initial thought was about how `configfs_new_dirent` just sets `sd->s_element = element`. That *suggests* it doesn't take an explicit reference on the default group items themselves. And that's correct, they're part of the parent group.
Now I'm looking at `f_midi2_alloc_inst`. Specifically this code block:
```c
config_group_init_type_name(&opts->func_inst.group, "",
&f_midi2_func_type);
config_group_init_type_name(&ep_opts->group, "ep.0",
&f_midi2_ep_type);
configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
config_group_init_type_name(&block_opts->group, "block.0",
&f_midi2_block_type);
configfs_add_default_group(&block_opts->group, &ep_opts->group);
```
I'm following the lifetime here. When `opts->func_inst.group` is released, `f_midi2_opts_release` gets called which then calls `usb_put_function_instance(&opts->func_inst)`. That in turn calls `f_midi2_free_inst`, and that frees `opts`. Seems straightforward. But, what about `ep_opts` and `block_opts`? They're allocated using `kzalloc_obj` in `f_midi2_ep_opts_create` and `f_midi2_block_opts_create`. I see that they get freed in `f_midi2_ep_opts_release` and `f_midi2_block_opts_release`.
The crucial question is, *who* triggers those `*_opts_release` functions? It looks like `f_midi2_ep_opts_release` is called when the `ep_opts->group.cg_item` refcount hits zero. But who is *decrementing* that refcount? I need to trace the calls to `config_item_put` for the default groups to understand this. Essentially, does `configfs` automatically drop the reference of a default group when its parent group is released?
I'm going to revisit `config_item_cleanup` and carefully trace how the refcounts are being managed. That's where the answer is going to lie.
|
| 61/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:config_item_cleanup]
Results: map[SourceCode: 117: static void config_item_cleanup(struct config_item *item)
118: {
119: const struct config_item_type *t = item->ci_type;
120: struct config_group *s = item->ci_group;
121: struct config_item *parent = item->ci_parent;
122:
123: pr_debug("config_item %s: cleaning up\n", config_item_name(item));
124: if (item->ci_name != item->ci_namebuf)
125: kfree(item->ci_name);
126: item->ci_name = NULL;
127: if (t && t->ct_item_ops && t->ct_item_ops->release)
128: t->ct_item_ops->release(item);
129: if (s)
130: config_group_put(s);
131: if (parent)
132: config_item_put(parent);
133: }
SourceFile:fs/configfs/item.c]
|
| 62/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 30958
output: 24
thoughts: 236
Thoughts: **Analyzing the `config_item_cleanup` Process**
Okay, so I was thinking that `config_item_cleanup` might be responsible for iterating through and decrementing the reference counts of the `default_groups`, but that's not quite right. Wait a minute... if `ep_opts` is a default group belonging to `opts->func_inst.group`, then when `opts->func_inst.group` is initially created, `config_group_init_type_name` sets its refcount to 1, right? And checking further back, `config_group_init_type_name` in turn calls `config_item_init_type_name`, which is also what sets the refcount to 1. So, `ep_opts->group.cg_item` does indeed start with a refcount of 1.
Now, remembering what happens when a directory is made for `opts->func_inst.group` using `configfs_mkdir`, `populate_groups` comes into play and creates dentries for these default groups. Then, when the directory is removed using `configfs_rmdir`, this calls `configfs_detach_group`, which then calls `detach_groups`, which removes the dentries of default groups.
But, and here's the crucial question, does it actually *drop* the reference count of the default group items during that directory removal process? I need to check `configfs_detach_group` more closely to be sure.
|
| 63/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_detach_group]
Results: map[SourceCode: 884: /* Caller holds the mutex of the group's inode */
885: static void configfs_detach_group(struct dentry *dentry)
886: {
887: detach_groups(dentry);
888: configfs_detach_item(dentry);
889: }
SourceFile:fs/configfs/dir.c]
|
| 64/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 31656
output: 21
thoughts: 6
Thoughts: Okay, here's my interpretation of that thought process, summarized as you requested:
**Deconstructing and Understanding Group Dynamics**
Alright, let's break this down. When I see `detach_groups`, my mind immediately goes to the practical implications. I'm thinking about situations where we need to isolate or remove certain groups from a larger context. This could be for a variety of reasons, right? Maybe it's data privacy, perhaps it's resource allocation, or potentially for a more refined analysis where the presence of certain groups introduces unwanted noise or bias.
I'm considering the potential impact on downstream processes. What happens to the detached groups? Are they archived, deleted, or transferred elsewhere? The mechanics of *how* the detachment happens are paramount, are we talking about a hard split, a soft copy, or a more complex re-attribution? The specific implementation will hinge heavily on the specific dataset and its use case. I need to be thinking about data integrity and how we ensure consistency after the split. Any potential dependencies between the detached and remaining groups are important. We can't have a cascading failure.
I also need to evaluate the implications on any underlying models or algorithms that are working with this dataset, and the specific architecture of the system. I also think about how to define these "groups". The underlying grouping mechanism and the process for defining those groups is a critical factor and must be defined very carefully. I'm mentally checking what edge cases I should consider, along with how to maintain metadata and context during the detaching process. Basically, I'm already formulating a checklist for best practices to guarantee the change is well-managed.
|
| 65/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 664: static void detach_groups(struct dentry *dentry)
665: {
666: struct dentry *child;
667: struct configfs_dirent *parent_sd;
668: struct configfs_dirent *sd, *next;
669:
670: parent_sd = dentry->d_fsdata;
671: spin_lock(&configfs_dirent_lock);
672: for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
673: next = next_dirent(parent_sd, sd);
674: if (!(sd->s_type & CONFIGFS_USET_DEFAULT))
675: continue;
676:
677: child = dget(sd->s_dentry);
678: spin_unlock(&configfs_dirent_lock);
679:
680: inode_lock(d_inode(child));
681:
682: configfs_detach_group(child);
683: d_inode(child)->i_flags |= S_DEAD;
684: dont_mount(child);
685:
686: inode_unlock(d_inode(child));
687:
688: d_delete(child);
689: dput(child);
690: spin_lock(&configfs_dirent_lock);
691: }
692: spin_unlock(&configfs_dirent_lock);
693: }
SourceFile:fs/configfs/dir.c]
|
| 66/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 32495
output: 24
thoughts: 44
Thoughts: Okay, let's see. My mind's buzzing... I'm tracing through this code, and I see that `configfs_detach_group` ultimately calls `configfs_detach_item`, which then calls `configfs_remove_dir`. Hmm, now the question is: does `configfs_remove_dir` handle the necessary refcount dropping on the directory? If it doesn't, we have a memory leak situation. I need to dig deeper into the implementation of `configfs_remove_dir` to be absolutely certain it's doing its job correctly. It's a critical point for resource management in this context. I'll have to investigate that function's implementation next.
|
| 67/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_remove_dir]
Results: map[SourceCode: 410: static void configfs_remove_dir(struct dentry *d)
411: {
412: struct dentry * parent = dget(d->d_parent);
413:
414: configfs_remove_dirent(d);
415:
416: if (d_really_is_positive(d)) {
417: if (unlikely(simple_rmdir(d_inode(parent), d)))
418: pr_warn("remove_dir (%pd): attributes remain", d);
419: }
420:
421: pr_debug(" o %pd removing done (%d)\n", d, d_count(d));
422:
423: dput(parent);
424: }
SourceFile:fs/configfs/dir.c]
|
| 68/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 32964
output: 24
thoughts: 62
Thoughts: **Investigating ConfigFS Refcount Behavior**
Okay, so I'm hitting a roadblock. It's becoming clear that something isn't quite right with how ConfigFS is handling refcounts, specifically with default groups. My initial thought was that it should handle it, but I'm no longer sure. Who *is* responsible for decrementing the refcount on these default groups if ConfigFS isn't?
I need to dig deeper. To understand the pattern for this, I'm thinking I should look at how this is handled elsewhere. Let's see how other USB functions manage default groups, perhaps within the existing gadget framework. I'll take a look at `f_mass_storage.c` or `f_uvc.c` as potential starting points to see if they offer any clues. Perhaps there's a common pattern to how this is handled, and I've missed it.
|
| 69/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode:3494: static struct usb_function_instance *fsg_alloc_inst(void)
3495: {
3496: struct fsg_opts *opts;
3497: struct fsg_lun_config config;
3498: int rc;
3499:
3500: opts = kzalloc_obj(*opts);
3501: if (!opts)
3502: return ERR_PTR(-ENOMEM);
3503: mutex_init(&opts->lock);
3504: opts->func_inst.free_func_inst = fsg_free_inst;
3505: opts->common = fsg_common_setup(opts->common);
3506: if (IS_ERR(opts->common)) {
3507: rc = PTR_ERR(opts->common);
3508: goto release_opts;
3509: }
3510:
3511: rc = fsg_common_set_num_buffers(opts->common,
3512: CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
3513: if (rc)
3514: goto release_common;
3515:
3516: pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
3517:
3518: memset(&config, 0, sizeof(config));
3519: config.removable = true;
3520: rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
3521: (const char **)&opts->func_inst.group.cg_item.ci_name);
3522: if (rc)
3523: goto release_buffers;
3524:
3525: opts->lun0.lun = opts->common->luns[0];
3526: opts->lun0.lun_id = 0;
3527:
3528: config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);
3529:
3530: config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
3531: configfs_add_default_group(&opts->lun0.group, &opts->func_inst.group);
3532:
3533: return &opts->func_inst;
3534:
3535: release_buffers:
3536: fsg_common_free_buffers(opts->common);
3537: release_common:
3538: kfree(opts->common);
3539: release_opts:
3540: kfree(opts);
3541: return ERR_PTR(rc);
3542: }
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 70/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 34052
output: 24
thoughts: 9
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**My Deep Dive into `fsg_free_inst`**
Alright, let's break this down. When I see `fsg_free_inst`, my mind immediately jumps to memory management within, most likely, a complex system like a file system or a similar data structure. The "fsg" prefix strongly suggests a connection to a file system, while "free_inst" screams that we're dealing with freeing up instances – blocks, inodes, descriptors, whatever is relevant in this context. The context is really important to understand here, the function call alone doesn't tell us everything. It’s a pointer to some sort of memory deallocation function.
I immediately consider the implications. I know that if these resources aren't properly freed, we are risking memory leaks, which are death to any system. Even worse, if there's a double-free scenario, it could result in data corruption, and system crashes, as well.
I’m immediately wondering about the surrounding code. Is this function called deterministically, or is it triggered by some event, like error recovery or graceful shutdown? What is the function’s overall lifecycle and are there any checks to ensure we are actually cleaning up the memory that needs freeing? How does it interact with other memory management functions or locking mechanisms?
My immediate checklist would include:
* **Error Handling:** Are there checks for null pointers or invalid states before the free operation? Are exceptions being properly thrown to the calling function, or are we just letting the problem bubble up?
* **Synchronization:** How is concurrent access handled? Is there a mutex or some other locking mechanism to prevent race conditions during deallocation, especially in a multithreaded environment? Is there any reentrancy, where we might need to handle this?
* **Resource Tracking:** Is there some sort of accounting – a reference count, perhaps – to ensure that the instance is not freed prematurely while it’s still in use? This is vital in systems that deal with shared resources.
* **Integration:** How does this function interface with the rest of the file system's memory management or other system's memory management? What happens to the freed memory; is it returned to a general pool, or used in a special allocation routine?
My goal would be to gain a complete understanding of this function's role in the larger system. I’d want to map out its dependencies and implications, because improper use of `fsg_free_inst` is a ticking time bomb.
|
| 71/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode:3485: static void fsg_free_inst(struct usb_function_instance *fi)
3486: {
3487: struct fsg_opts *opts;
3488:
3489: opts = fsg_opts_from_func_inst(fi);
3490: fsg_common_release(opts->common);
3491: kfree(opts);
3492: }
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 72/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 34790
output: 28
thoughts: 161
Thoughts: **Analyzing the Data Structures in USB Gadget Driver Code**
Okay, let's break this down. First, I see that `fsg_opts` has `opts->lun0.group` embedded within it. This is important to note because it means that when the `opts` structure is freed, the memory for `lun0` (and consequently its group) is also released. This is standard practice, but it's crucial to confirm this implicit relationship to be certain of correct memory management.
However, the contrast that jumps out is in `f_midi2` where I recall `ep_opts` and `block_opts` are created using `kzalloc_obj`, which suggests dynamic allocation, not embedding. This is where I need to be *sure*.
I'm going to revisit `f_midi2_ep_opts_create` to ensure I have a complete picture of the memory allocation scheme. I need to fully understand how these structures are created, initialized, and ultimately, deallocated. I need to make sure I am not missing some details, in particular, what happens to the embedded `group` inside `lun0` when `fsg_opts` is used.
|
| 73/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_ep_opts_create]
Results: map[SourceCode:2497: /* create a f_midi2_ep_opts instance */
2498: static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
2499: unsigned int index,
2500: struct f_midi2_ep_opts **ep_p)
2501: {
2502: struct f_midi2_ep_opts *ep_opts;
2503:
2504: ep_opts = kzalloc_obj(*ep_opts);
2505: if (!ep_opts)
2506: return -ENOMEM;
2507:
2508: ep_opts->opts = opts;
2509: ep_opts->index = index;
2510:
2511: /* set up the default values */
2512: ep_opts->info.protocol = 2;
2513: ep_opts->info.protocol_caps = 3;
2514:
2515: opts->eps[index] = ep_opts;
2516: *ep_p = ep_opts;
2517: return 0;
2518: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 74/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 35591
output: 26
thoughts: 566
Thoughts: **Debugging Configfs Refcount Issues in MIDI2 Gadget Driver**
Okay, so I'm looking at this MIDI2 gadget driver and I've got a potential memory leak issue related to how `configfs` handles default groups. I see that the `f_midi2_ep_opts_release` function should be freeing the `f_midi2_ep_opts` structure, specifically allocated memory for the endpoint name and product ID, and the `opts` structure itself.
However, the question is, *does* `configfs` actually decrement the reference count of default groups so that `f_midi2_ep_opts_release` gets called? I need to verify how `config_item_cleanup` behaves because this is where items get released. Looking at `config_item_cleanup` again, I see that it calls the `release` function defined in the `config_item_type`, then decrements the parent and group refcounts using `config_item_put` and `config_group_put` respectively.
Now I'm remembering that `ep_opts` and `block_opts` are added to the `default_groups` list within the `func_inst.group`. When `func_inst.group` is released, does it automatically release the members of `default_groups`? No! Looking at `config_item_cleanup`, I don't see anything that handles releasing the `default_groups`.
So now I'm wondering if `f_midi2_free_inst` is supposed to clean up the default groups. If it does, then the `f_midi2_ep_opts_release` and `f_midi2_block_opts_release` functions might not be needed in the case of default groups.
But that's not quite right... because I also have to account for the possibility of these `ep_opts` and `block_opts` being created dynamically by the user using `mkdir` in configfs. I need to understand what `f_midi2_group_ops` does in relation to cleanup to make sure I'm not missing any reference count management here.
|
| 75/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_group_ops]
Results: map[SourceCode:2624: static const struct configfs_group_operations f_midi2_group_ops = {
2625: .make_group = f_midi2_opts_ep_make,
2626: .drop_item = f_midi2_opts_ep_drop,
2627: };
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 76/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 36759
output: 28
thoughts: 13
Thoughts: Okay, here's my summary, taking on the persona you requested:
**Deconstructing `f_midi2_opts_ep_make`**
Right, let's break this down. From what I gather, we're talking about a function, likely in a MIDI 2.0 context, judging by the naming convention. The "f_" prefix usually indicates a function, and "midi2_opts_ep_make" strongly suggests that we're dealing with the creation of something related to MIDI 2.0 options, probably for an endpoint or endpoint configuration. The use of "ep" definitely makes me think "endpoint," which fits perfectly with MIDI's communication model. I'm immediately interested in what "options" means here. Is this dealing with MIDI CI data? Capabilities negotiation? Or perhaps even configuration parameters? I'd need to dig deeper into the actual function definition and the context of its use to determine that precisely. Regardless, the "make" suffix signals that this function is likely responsible for either instantiating or constructing a data structure, or object, to represent these endpoint-specific options. It's probably returning a pointer or handle to this newly created structure, allowing me to interact with it later. I'll need to check for memory allocation and deallocation within this function to ensure there are no obvious memory leaks. I want to see how the inputs are handled, are there default options? And I'll need to know what happens in error cases. Let's see how this thing *works*.
|
| 77/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_opts_ep_make]
Results: map[SourceCode:2520: /* make_group callback for an EP */
2521: static struct config_group *
2522: f_midi2_opts_ep_make(struct config_group *group, const char *name)
2523: {
2524: struct f_midi2_opts *opts;
2525: struct f_midi2_ep_opts *ep_opts;
2526: unsigned int index;
2527: int ret;
2528:
2529: if (strncmp(name, "ep.", 3))
2530: return ERR_PTR(-EINVAL);
2531: ret = kstrtouint(name + 3, 10, &index);
2532: if (ret)
2533: return ERR_PTR(ret);
2534:
2535: opts = to_f_midi2_opts(&group->cg_item);
2536: if (index >= MAX_UMP_EPS)
2537: return ERR_PTR(-EINVAL);
2538: if (opts->eps[index])
2539: return ERR_PTR(-EBUSY);
2540: ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
2541: if (ret)
2542: return ERR_PTR(ret);
2543:
2544: config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
2545: return &ep_opts->group;
2546: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 78/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 37589
output: 28
thoughts: 78
Thoughts: Okay, here's my understanding, framed from my perspective as an expert:
**Dynamic Endpoint Option Handling in ConfigFS for MIDI2**
Alright, so the architecture here is that the `ep_opts` structure, which I assume holds all the endpoint-specific configuration options, is created dynamically. This is standard practice in many configfs implementations, offering flexibility. The key point is that its creation is *triggered* by something – likely user interaction with the configfs filesystem. Specifically, the dynamic creation occurs when `configfs_mkdir` calls `f_midi2_opts_ep_make`. This function is responsible for the actual memory allocation for `ep_opts`, and critically, it initializes the reference count to 1. This refcount management is, of course, absolutely crucial to prevent premature freeing.
Now, on the removal side, when the endpoint is no longer needed and is removed (probably via `rmdir` on the configfs entry), `configfs_rmdir` comes into play. It calls the counterpart function, `f_midi2_opts_ep_drop`. This suggests `f_midi2_opts_ep_drop` likely handles decrementing the refcount, and if the count hits zero, it frees the `ep_opts` structure entirely, releasing the memory. Makes sense.
This dynamic allocation/deallocation model, with the associated reference counting, is a pretty common pattern for resource management within a kernel environment like this, especially when dealing with potentially transient configuration objects. We need to be confident that it is being handled right to avoid memory leaks or double frees.
|
| 79/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_opts_ep_drop]
Results: map[SourceCode:2548: /* drop_item callback for an EP */
2549: static void
2550: f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
2551: {
2552: struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
2553:
2554: mutex_lock(&ep_opts->opts->lock);
2555: ep_opts->opts->eps[ep_opts->index] = NULL;
2556: mutex_unlock(&ep_opts->opts->lock);
2557: config_item_put(item);
2558: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 80/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 38257
output: 26
thoughts: 189
Thoughts: **My Investigation into Default Group Destruction in the MIDI2 ConfigFS**
Okay, so I'm digging into this `f_midi2_opts_ep_drop` function, and it's calling `config_item_put(item)`. Standard stuff, that should drop the refcount and trigger `f_midi2_ep_opts_release` to free the item. But wait... there's a wrinkle with the default groups. I know these aren't your typical objects. They're special. I remember now: `configfs_rmdir` returns `-EPERM` for default groups! I quickly recall the code snippet:
```c
sd = dentry->d_fsdata;
if (sd->s_type & CONFIGFS_USET_DEFAULT)
return -EPERM;
```
Essentially, those default groups are never removed directly with an `rmdir` call. So they aren't cleaned up by the usual path. They're only cleaned up when their parent group is taken down. That makes sense, in principle. But then the question hits me: if `config_item_cleanup` isn't dropping their refcount, *who is*? That's the critical question here! I need to trace the object lifetime. I know `config_item_put` handles refcounting, so if its not the direct call, where is it happening? I better check out `f_midi2_free_inst` and see what it's doing. Let's see if that provides any answers.
|
| 81/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_free_inst]
Results: map[SourceCode:2636: static void f_midi2_free_inst(struct usb_function_instance *f)
2637: {
2638: struct f_midi2_opts *opts;
2639:
2640: opts = container_of(f, struct f_midi2_opts, func_inst);
2641:
2642: kfree(opts->info.iface_name);
2643: kfree(opts);
2644: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 82/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 38976
output: 24
thoughts: 257
Thoughts: **Memory Leak Suspicions in MIDI2 Instance Freeing**
Okay, hold on a second. I'm taking a close look at the `f_midi2_free_inst` function. My initial impression was that it was handling the cleanup, but it appears my understanding was flawed. It seems that `f_midi2_free_inst` *only* frees the `opts` structure and not `ep_opts` and `block_opts`.
Now, if I recall correctly, `ep_opts` and `block_opts` were allocated within `f_midi2_alloc_inst` using `kzalloc_obj`, or something similar.
Looking back at the allocation code in `f_midi2_alloc_inst`, that's correct, `kzalloc_obj` was used for `ep_opts` and `block_opts`. If `f_midi2_free_inst` isn't freeing these, then we have a classic memory leak on our hands! This is a serious problem.
I need to confirm this. Is there anywhere else within this driver that these default group structures are freed? I'm going to quickly check `f_uvc.c` for some clues on how default groups might be handled. It's a related driver and might have similar logic. Need to make sure I don't overlook any subtleties.
|
| 83/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 904: static void uvc_free_inst(struct usb_function_instance *f)
905: {
906: struct f_uvc_opts *opts = fi_to_f_uvc_opts(f);
907:
908: mutex_destroy(&opts->lock);
909: kfree(opts);
910: }
SourceFile:drivers/usb/gadget/function/f_uvc.c]
|
| 84/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 39689
output: 24
thoughts: 29
Thoughts: Here's my thought process, as if I were analyzing that code:
**Thinking Through Resource Management in `libuvc`**
Okay, hold on a minute. I'm seeing that `uvc_free_inst` is *also* responsible for freeing `opts`. Now, I need to make sure I fully understand the allocation and deallocation lifecycle of these `opts` structures. It seems like the core issue might be where `opts` are allocated.
To confirm my understanding, I'm going to jump over to the `uvc_alloc_inst` function. I need to trace this back. I want to pinpoint the initial allocation and then verify there's no double-freeing or memory leaks happening with this `opts` structure, specifically. Gotta get this straight in my head.
|
| 85/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode: 912: static struct usb_function_instance *uvc_alloc_inst(void)
913: {
914: struct f_uvc_opts *opts;
915: struct uvc_camera_terminal_descriptor *cd;
916: struct uvc_processing_unit_descriptor *pd;
917: struct uvc_output_terminal_descriptor *od;
918: struct uvc_descriptor_header **ctl_cls;
919: int ret;
920:
921: opts = kzalloc_obj(*opts);
922: if (!opts)
923: return ERR_PTR(-ENOMEM);
924: opts->func_inst.free_func_inst = uvc_free_inst;
925: mutex_init(&opts->lock);
926:
927: cd = &opts->uvc_camera_terminal;
928: cd->bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3);
929: cd->bDescriptorType = USB_DT_CS_INTERFACE;
930: cd->bDescriptorSubType = UVC_VC_INPUT_TERMINAL;
931: cd->bTerminalID = 1;
932: cd->wTerminalType = cpu_to_le16(0x0201);
933: cd->bAssocTerminal = 0;
934: cd->iTerminal = 0;
935: cd->wObjectiveFocalLengthMin = cpu_to_le16(0);
936: cd->wObjectiveFocalLengthMax = cpu_to_le16(0);
937: cd->wOcularFocalLength = cpu_to_le16(0);
938: cd->bControlSize = 3;
939: cd->bmControls[0] = 2;
940: cd->bmControls[1] = 0;
941: cd->bmControls[2] = 0;
942:
943: pd = &opts->uvc_processing;
944: pd->bLength = UVC_DT_PROCESSING_UNIT_SIZE(2);
945: pd->bDescriptorType = USB_DT_CS_INTERFACE;
946: pd->bDescriptorSubType = UVC_VC_PROCESSING_UNIT;
947: pd->bUnitID = 2;
948: pd->bSourceID = 1;
949: pd->wMaxMultiplier = cpu_to_le16(16*1024);
950: pd->bControlSize = 2;
951: pd->bmControls[0] = 1;
952: pd->bmControls[1] = 0;
953: pd->iProcessing = 0;
954: pd->bmVideoStandards = 0;
955:
956: od = &opts->uvc_output_terminal;
957: od->bLength = UVC_DT_OUTPUT_TERMINAL_SIZE;
958: od->bDescriptorType = USB_DT_CS_INTERFACE;
959: od->bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL;
960: od->bTerminalID = 3;
961: od->wTerminalType = cpu_to_le16(0x0101);
962: od->bAssocTerminal = 0;
963: od->bSourceID = 2;
964: od->iTerminal = 0;
965:
966: /*
967: * With the ability to add XUs to the UVC function graph, we need to be
968: * able to allocate unique unit IDs to them. The IDs are 1-based, with
969: * the CT, PU and OT above consuming the first 3.
970: */
971: opts->last_unit_id = 3;
972:
973: /* Prepare fs control class descriptors for configfs-based gadgets */
974: ctl_cls = opts->uvc_fs_control_cls;
975: ctl_cls[0] = NULL; /* assigned elsewhere by configfs */
976: ctl_cls[1] = (struct uvc_descriptor_header *)cd;
977: ctl_cls[2] = (struct uvc_descriptor_header *)pd;
978: ctl_cls[3] = (struct uvc_descriptor_header *)od;
979: ctl_cls[4] = NULL; /* NULL-terminate */
980: opts->fs_control =
981: (const struct uvc_descriptor_header * const *)ctl_cls;
982:
983: /* Prepare hs control class descriptors for configfs-based gadgets */
984: ctl_cls = opts->uvc_ss_control_cls;
985: ctl_cls[0] = NULL; /* assigned elsewhere by configfs */
986: ctl_cls[1] = (struct uvc_descriptor_header *)cd;
987: ctl_cls[2] = (struct uvc_descriptor_header *)pd;
988: ctl_cls[3] = (struct uvc_descriptor_header *)od;
989: ctl_cls[4] = NULL; /* NULL-terminate */
990: opts->ss_control =
991: (const struct uvc_descriptor_header * const *)ctl_cls;
992:
993: INIT_LIST_HEAD(&opts->extension_units);
994:
995: opts->streaming_interval = 1;
996: opts->streaming_maxpacket = 1024;
997: snprintf(opts->function_name, sizeof(opts->function_name), "UVC Camera");
998:
999: ret = uvcg_attach_configfs(opts);
1000: if (ret < 0) {
1001: kfree(opts);
1002: return ERR_PTR(ret);
1003: }
1004:
1005: return &opts->func_inst;
1006: }
SourceFile:drivers/usb/gadget/function/f_uvc.c]
|
| 86/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 41681
output: 25
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `uvcg_attach_configfs`**
Right, let's take a look at `uvcg_attach_configfs`. I'm immediately interested. The name itself suggests a critical function related to attaching something - likely a virtual console device or related resources - to a `configfs` instance. This is core infrastructure stuff. I need to understand the relationship it establishes, the resource management it handles, and any implications for security or performance. My mind immediately begins to formulate potential questions: Does it handle error checking? How does it integrate with the kernel's device model? Is it properly synchronized to handle concurrent requests? And most importantly, how do they handle the attachment/detachment of the ConfigFS mountpoint? Is there proper error handling if the mounting fails? I wonder if there are any race conditions it might introduce? Time to get into the details of the code and the context in which it's used. This feels like an area where subtle bugs can have serious consequences. I hope it has thorough documentation and robust error handling!
|
| 87/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvcg_attach_configfs]
Results: map[SourceCode:3822: int uvcg_attach_configfs(struct f_uvc_opts *opts)
3823: {
3824: int ret;
3825:
3826: config_group_init_type_name(&opts->func_inst.group, uvc_func_type.name,
3827: &uvc_func_type.type);
3828:
3829: ret = uvcg_config_create_children(&opts->func_inst.group,
3830: &uvc_func_type);
3831: if (ret < 0)
3832: config_group_put(&opts->func_inst.group);
3833:
3834: return ret;
3835: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 88/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 42216
output: 26
thoughts: 11
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Navigating the `uvcg_config_create_children` Function**
Alright, let's break down this `uvcg_config_create_children` function. From the name alone, I can infer this is critical for setting up some kind of hierarchical configuration structure, likely related to a *uvcg* – whatever that specific naming convention implies in this context. Immediately, I'm thinking about the data structures involved: are we talking about a tree, a graph, or something more complex? What's the parent-child relationship defined as? Are we dealing with pointers, references, or some form of IDs to manage these connections? I need to understand the underlying model to properly grasp what this function *does*.
My first question will likely be around error handling. What happens if a child creation fails? Does it propagate an error, return a specific code, or log the failure and continue? Understanding the robustness of this function is paramount. I'll need to know about resource management. Does it handle memory allocation and deallocation for the child nodes? Are there any potential memory leaks or race conditions?
I am also thinking about what specific children will be created, and the parameters needed to initialize them. I'll have to consider how those child configs are initialized – are they default values, copied from the parent, or entirely new configurations? I need to understand the implications of default configurations, and how the values are generated.
Next, I'll need to think about the performance implications. How many children are we potentially creating? Is this a recursive operation, or is it iterative? How does the function scale with a large number of children? Are there any optimizations in place to handle large trees efficiently? I'll probably be looking for some kind of benchmark or complexity analysis to gauge the performance characteristics.
Finally, I'll want to trace how this function integrates with the rest of the configuration system. Does it trigger any events, update any internal state, or interact with other modules? I need to understand the broader context to make sure this function behaves correctly within the overall system. In short, it seems this is one of those functions where attention to detail is essential for a robust and performant implementation. I will start by thoroughly analyzing the existing code, and then try to think about some of the edge-case scenarios.
|
| 89/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvcg_config_create_children]
Results: map[SourceCode: 138: static int uvcg_config_create_children(struct config_group *group,
139: const struct uvcg_config_group_type *type)
140: {
141: const struct uvcg_config_group_type **child;
142: int ret;
143:
144: if (type->create_children)
145: return type->create_children(group);
146:
147: for (child = type->children; child && *child; ++child) {
148: ret = uvcg_config_create_group(group, *child);
149: if (ret < 0)
150: return ret;
151: }
152:
153: return 0;
154: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 90/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 43033
output: 26
thoughts: 11
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Diving into `uvcg_config_create_group`**
Right, let's break this down. `uvcg_config_create_group`… that's the function responsible for spinning up a new configuration group, right? It's the cornerstone for managing a set of related configurations, crucial for any system that needs to handle multiple, independent (or at least, logically separated) parameter sets. We're talking about the fundamental building block here. So, what are the implicit assumptions? What underlying data structures are being used? Is it relying on a mutex for thread safety, or using some more sophisticated lock-free mechanism? And how is it handling errors? Does it return a status code, throw an exception, or maybe log an error?
I need to understand its relationship to other config modules in this system. Is this a top-level function, or does it call other lower-level config management functions? Does it use a shared global state, or is it isolated to the current process? What's the impact on performance and reliability?
What validation checks are built in? Does it handle invalid group names, or malformed configuration data? What about resource exhaustion? Does it have a good error-handling policy?
I should really run a few scenarios in my head, thinking through different usage patterns. What happens if I try to create a group with a name that already exists? What if the system is under heavy load? Are there any potential race conditions I should be aware of? And most importantly, is this design scalable? Can it handle thousands of config groups without falling apart? Time to dig into the implementation details and make sure things are sound.
|
| 91/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvcg_config_create_group]
Results: map[SourceCode: 156: static int uvcg_config_create_group(struct config_group *parent,
157: const struct uvcg_config_group_type *type)
158: {
159: struct config_group *group;
160:
161: group = kzalloc_obj(*group);
162: if (!group)
163: return -ENOMEM;
164:
165: config_group_init_type_name(group, type->name, &type->type);
166: configfs_add_default_group(group, parent);
167:
168: return uvcg_config_create_children(group, type);
169: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 92/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 43694
output: 26
thoughts: 44
Thoughts: Okay, here's my interpretation of those thoughts, crafted with the perspective of someone deeply familiar with kernel-level memory management:
**Memory Leak Alert: Group Configuration Object**
Alright, hang on a second. I'm staring at this `uvcg_config_create_group` function...and it's *allocating* the `group` structure using `kzalloc_obj`. My spidey senses are tingling. That usually means a chunk of memory is allocated and zeroed out. Good, good. But... where's the corresponding free? `kzalloc_obj` means `kfree_obj` needs to happen eventually. We can't just leave that memory dangling. This smells like a classic memory leak in the making.
Let's not jump to conclusions, though. I need to track down the cleanup procedure for this `group` object. Let's start the hunt for `uvcg_config_item_release`. That’s likely where the object is freed, but I need to make sure!
|
| 93/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvcg_config_item_release]
Results: map[SourceCode: 124: static void uvcg_config_item_release(struct config_item *item)
125: {
126: struct config_group *group = to_config_group(item);
127:
128: kfree(group);
129: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 94/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 44102
output: 26
thoughts: 89
Thoughts: **Delving into the `uvcg_config_item_release` Rabbit Hole**
Okay, hold on a second! This `uvcg_config_item_release` function... that's the one that actually frees the group, right? It's the key to the cleanup process. But the immediate question that pops into my head is: who's calling *this* function? I need to trace the call chain. Ah, right, it's the release function associated with the item type itself. Makes sense.
But wait a minute... here's a potential snag. If configfs doesn't automatically decrement the reference count of the default groups, then how on earth does `uvcg_config_item_release` even get triggered? There must be some other mechanism at play. Right, I need to investigate the function responsible for removing children. Let's dig into `uvcg_config_remove_children` and see what it reveals. This seems like the most logical next step to understand how that release function is actually called.
|
| 95/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvcg_config_remove_children]
Results: map[SourceCode: 171: static void uvcg_config_remove_children(struct config_group *group)
172: {
173: struct config_group *child, *n;
174:
175: list_for_each_entry_safe(child, n, &group->default_groups, group_entry) {
176: list_del(&child->group_entry);
177: uvcg_config_remove_children(child);
178: config_item_put(&child->cg_item);
179: }
180: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 96/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 44637
output: 26
thoughts: 60
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Analyzing `uvcg_config_remove_children`**
Right, let me see... I'm looking at this `uvcg_config_remove_children` function, and it seems like it's taking a recursive approach to deleting configuration items. First, it iterates through `default_groups`, removes them – presumably from some internal structure or list – then *recursively* calls itself. The key thing here is, *before* returning up the call stack, after handling the children it then calls `config_item_put(&child->cg_item)`. That seems important for freeing resources associated with the individual config items, likely deallocating memory or releasing references.
The recursion suggests it's dealing with a hierarchical configuration structure, which makes sense. I have to think about what this looks like... a tree-like structure probably, where each node can have child nodes represented in `default_groups`. Now, what I really need to understand is what's the entry point? **Who actually calls this function in the first place**? Understanding the callers will give me a lot of insight into the context and purpose of this whole process. Where is the trigger? Is it triggered by a command? Is it a part of a shutdown procedure? That's what I need to dig into next.
|
| 97/2 |
2026/07/30 14:12 |
tool |
codesearch-find-references |
0m
Args: map[Name:uvcg_config_remove_children]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uvc_func_item_release SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:3639] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uvcg_config_remove_children SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:177]] TruncatedOutput:false]
|
| 98/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 45157
output: 26
thoughts: 13
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Internal Monologue on `uvc_func_item_release`**
Alright, so `uvc_func_item_release` is being called. That's the key. That function... right. That means we're in the final stages of cleaning up after a UVC function item. The context is crucial here. We're likely dealing with deallocation, resource freeing, potentially removing it from a list or hash table, and generally tidying up the internal structures associated with that particular item. Let me mentally run through the typical scenarios. Is it a descriptor, control, endpoint? Each type has a unique clean-up process, and the specific `release` implementation within the UVC driver will vary depending on the item's role.
We have to make sure there's no dangling pointer, and that any resources allocated by this specific item are freed back to the system. There could be associated mutexes or spinlocks that need to be released to avoid deadlocks. Memory leaks are a definite concern here, and I'll need to double-check that every allocation has a corresponding deallocation. I also hope it's well-synchronized because releasing something with multiple threads can create race conditions very easily, that is the most critical area of concern.
The calling context here is also critical, it will inform my mental checklist, and make sure that this is the correct moment to clear the item. This is generally the last opportunity for this UVC function item to exit gracefully. If there is a problem, the system is going to come to a grinding halt.
Okay, let's trace this call.
|
| 99/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:uvc_func_item_release]
Results: map[SourceCode:3635: static void uvc_func_item_release(struct config_item *item)
3636: {
3637: struct f_uvc_opts *opts = to_f_uvc_opts(item);
3638:
3639: uvcg_config_remove_children(to_config_group(item));
3640: usb_put_function_instance(&opts->func_inst);
3641: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 100/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 45715
output: 26
thoughts: 43
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Digging into Refcounting in USB Gadget Drivers**
Right, so I'm looking at this `uvc` driver, and it's making a pretty clear move by explicitly *removing* and *dropping* the reference count of its default groups within its release function. Seems straightforward enough – manage those resources meticulously during the teardown. Now, the question that springs to mind is, what's the story with `f_midi2`? I wonder if it follows the same principle, or if there's a different approach. Let's see... looking into `f_midi2_opts_release`... I expect a similar process to happen, but I need to see to verify.
|
| 101/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_opts_release]
Results: map[SourceCode:2613: static void f_midi2_opts_release(struct config_item *item)
2614: {
2615: struct f_midi2_opts *opts = to_f_midi2_opts(item);
2616:
2617: usb_put_function_instance(&opts->func_inst);
2618: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 102/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 46083
output: 23
thoughts: 862
Thoughts: **Analyzing a Potential Memory Leak in the ConfigFS Structure**
Okay, here's what's running through my head. I've got this code, and it seems like there might be a memory leak in how these default groups are handled. Specifically, it looks like the refcounts of the default groups aren't being properly dropped when the parent is released.
I'm focusing on these lines:
```c
config_group_init_type_name(&ep_opts->group, "ep.0",
&f_midi2_ep_type);
configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
config_group_init_type_name(&block_opts->group, "block.0",
&f_midi2_block_type);
configfs_add_default_group(&block_opts->group, &ep_opts->group);
```
So, `ep_opts` and `block_opts` are these default groups. `opts->func_inst.group` has `ep_opts->group` as a default group and `ep_opts->group` has `block_opts->group` as a default group. My concern is that when `opts->func_inst.group` gets released - which triggers `f_midi2_opts_release` and eventually `usb_put_function_instance`, freeing `opts` - `ep_opts` and `block_opts` are left hanging. They're leaked!
Now, how do I fix this? I have a couple of options. I could iterate through the `default_groups` and decrement their refcounts, similar to how we handle child removals in `uvcg_config_remove_children`. Or, I could just call `config_item_put` directly on `ep_opts` and `block_opts` in the `f_midi2_free_inst` or `f_midi2_opts_release` functions.
Ah, but wait... where are `ep_opts` and `block_opts` even created? They are created in the `f_midi2_alloc_inst`:
```c
/* create the default ep */
ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
}
/* create the default block */
ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
if (ret) {
kfree(ep_opts);
kfree(opts);
return ERR_PTR(ret);
}
```
Okay, `ep_opts` is stored in `opts->eps[0]` and `block_opts` is stored in `ep_opts->blks[0]`.
But hold on, can there be *other* `ep_opts` and `block_opts` created dynamically? Yes! There's a `make_group` callback, `f_midi2_opts_ep_make`, for EPs. This gets called by `configfs_mkdir`. When one is created dynamically, refcount is set to 1. When they are removed via `rmdir`, `configfs_rmdir` calls `f_midi2_opts_ep_drop`, which calls `config_item_put(item)`. So dynamically created ones are properly freed when `rmdir` is called.
Here's the key question, though: what happens if the user creates an `ep_opts` dynamically, and then removes the parent group (`opts->func_inst.group`) without removing the dynamic `ep_opts` first?
But wait... configfs doesn't allow removing a directory if it has children!
```c
ret = sd->s_dependent_count ? -EBUSY : 0;
```
Now, `sd->s_dependent_count` isn't about children. I have to verify that `configfs_rmdir` checks that the directory is empty. I will check `configfs_rmdir` again.
|
| 103/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args:
Results: map[SourceCode:1469: static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
1470: {
1471: struct config_item *parent_item;
1472: struct config_item *item;
1473: struct configfs_subsystem *subsys;
1474: struct configfs_dirent *sd;
1475: struct configfs_fragment *frag;
1476: struct module *subsys_owner = NULL, *dead_item_owner = NULL;
1477: int ret;
1478:
1479: sd = dentry->d_fsdata;
1480: if (sd->s_type & CONFIGFS_USET_DEFAULT)
1481: return -EPERM;
1482:
1483: /* Get a working ref until we have the child */
1484: parent_item = configfs_get_config_item(dentry->d_parent);
1485: subsys = to_config_group(parent_item)->cg_subsys;
1486: BUG_ON(!subsys);
1487:
1488: if (!parent_item->ci_type) {
1489: config_item_put(parent_item);
1490: return -EINVAL;
1491: }
1492:
1493: /* configfs_mkdir() shouldn't have allowed this */
1494: BUG_ON(!subsys->su_group.cg_item.ci_type);
1495: subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
1496:
1497: /*
1498: * Ensure that no racing symlink() will make detach_prep() fail while
1499: * the new link is temporarily attached
1500: */
1501: do {
1502: struct dentry *wait;
1503:
1504: mutex_lock(&configfs_symlink_mutex);
1505: spin_lock(&configfs_dirent_lock);
1506: /*
1507: * Here's where we check for dependents. We're protected by
1508: * configfs_dirent_lock.
1509: * If no dependent, atomically tag the item as dropping.
1510: */
1511: ret = sd->s_dependent_count ? -EBUSY : 0;
1512: if (!ret) {
1513: ret = configfs_detach_prep(sd, &wait);
1514: if (ret)
1515: configfs_detach_rollback(sd);
1516: }
1517: spin_unlock(&configfs_dirent_lock);
1518: mutex_unlock(&configfs_symlink_mutex);
1519:
1520: if (ret) {
1521: if (ret != -EAGAIN) {
1522: config_item_put(parent_item);
1523: return ret;
1524: }
1525:
1526: /* Wait until the racing operation terminates */
1527: inode_lock(d_inode(wait));
1528: inode_unlock(d_inode(wait));
1529: dput(wait);
1530: }
1531: } while (ret == -EAGAIN);
1532:
1533: frag = sd->s_frag;
1534: if (down_write_killable(&frag->frag_sem)) {
1535: spin_lock(&configfs_dirent_lock);
1536: configfs_detach_rollback(sd);
1537: spin_unlock(&configfs_dirent_lock);
1538: config_item_put(parent_item);
1539: return -EINTR;
1540: }
1541: frag->frag_dead = true;
1542: up_write(&frag->frag_sem);
1543:
1544: /* Get a working ref for the duration of this function */
1545: item = configfs_get_config_item(dentry);
1546:
1547: /* Drop reference from above, item already holds one. */
1548: config_item_put(parent_item);
1549:
1550: if (item->ci_type)
1551: dead_item_owner = item->ci_type->ct_owner;
1552:
1553: if (sd->s_type & CONFIGFS_USET_DIR) {
1554: configfs_detach_group(dentry);
1555:
1556: mutex_lock(&subsys->su_mutex);
1557: client_disconnect_notify(parent_item, item);
1558: unlink_group(to_config_group(item));
1559: } else {
1560: configfs_detach_item(dentry);
1561:
1562: mutex_lock(&subsys->su_mutex);
1563: client_disconnect_notify(parent_item, item);
1564: unlink_obj(item);
1565: }
1566:
1567: client_drop_item(parent_item, item);
1568: mutex_unlock(&subsys->su_mutex);
1569:
1570: /* Drop our reference from above */
1571: config_item_put(item);
1572:
1573: module_put(dead_item_owner);
1574: module_put(subsys_owner);
1575:
1576: return 0;
1577: }
SourceFile:fs/configfs/dir.c]
|
| 104/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 49522
output: 24
thoughts: 17
Thoughts: Here's my take on the thought process, as if it were my own:
**Ah-ha! A Potential Optimization**
Okay, so I'm digging into this `configfs_detach_prep` function. I was just reviewing the code, and a lightbulb just went off. Hang on a second... is this what I think it is? Yep, there it is! `configfs_detach_prep` explicitly checks if the target directory is empty before proceeding. This is crucial! That emptiness check is practically a free performance optimization waiting to happen. It avoids the overhead of further processing if the detachment is happening on an already-empty directory. I should make a note of this to revisit it later. It might be possible to leverage this check in some higher-level scenarios.
|
| 105/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_detach_prep]
Results: map[SourceCode: 494: /*
495: * Only subdirectories count here. Files (CONFIGFS_NOT_PINNED) are
496: * attributes and are removed by rmdir(). We recurse, setting
497: * CONFIGFS_USET_DROPPING on all children that are candidates for
498: * default detach.
499: * If there is an error, the caller will reset the flags via
500: * configfs_detach_rollback().
501: */
502: static int configfs_detach_prep(struct configfs_dirent *parent_sd, struct dentry **wait)
503: {
504: struct configfs_dirent *sd;
505: int ret;
506:
507: /* Mark that we're trying to drop the group */
508: parent_sd->s_type |= CONFIGFS_USET_DROPPING;
509:
510: ret = -EBUSY;
511: if (parent_sd->s_links)
512: goto out;
513:
514: ret = 0;
515: list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
516: if (!sd->s_element ||
517: (sd->s_type & CONFIGFS_NOT_PINNED))
518: continue;
519: if (sd->s_type & CONFIGFS_USET_DEFAULT) {
520: /* Abort if racing with mkdir() */
521: if (sd->s_type & CONFIGFS_USET_IN_MKDIR) {
522: if (wait)
523: *wait= dget(sd->s_dentry);
524: return -EAGAIN;
525: }
526:
527: /*
528: * Yup, recursive. If there's a problem, blame
529: * deep nesting of default_groups
530: */
531: ret = configfs_detach_prep(sd, wait);
532: if (!ret)
533: continue;
534: } else
535: ret = -ENOTEMPTY;
536:
537: break;
538: }
539:
540: out:
541: return ret;
542: }
SourceFile:fs/configfs/dir.c]
|
| 106/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 50455
output: 28
thoughts: 662
Thoughts: **Debugging a Potential Resource Leak in the MIDI2 Function Driver**
Okay, so I've been digging into this `configfs` interaction and I think I've found a subtle but potentially critical leak. It seems that `configfs_detach_prep` returns `-ENOTEMPTY` when trying to detach a group that *isn't* a default group, which forces the user to manually remove dynamically created child objects first. The user is supposed to remove the dynamically created `ep_opts` and `block_opts` objects before removing `opts->func_inst.group`. When a dynamically created `ep_opts` is removed, the `f_midi2_opts_ep_drop` function is called, which then invokes `config_item_put(item)`.
However, the handling of *default* groups is different. `configfs_detach_prep` simply recurses into them and sets `CONFIGFS_USET_DROPPING`. Then `configfs_detach_group` removes their dentries, but crucially, it *doesn't* drop their refcount. This means default groups are leaked unless the parent group's release function handles this! Now, this is where things get interesting. In the `uvc` driver, `uvc_func_item_release` correctly handles this by calling `uvcg_config_remove_children`, which *does* drop the refcount of default groups.
But in `f_midi2`, `f_midi2_opts_release` *doesn't* do this! This means we have a resource leak of these default groups. So, I need to figure out where to drop the refcount for these default groups in either `f_midi2_opts_release` or `f_midi2_free_inst`.
Let's see, `ep_opts` and `block_opts` are created in `f_midi2_alloc_inst`. Hmm...
```c
/* create the default ep */
ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
}
/* create the default block */
ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
if (ret) {
kfree(ep_opts);
kfree(opts);
return ERR_PTR(ret);
}
```
Could I just call `config_item_put(&ep_opts->group.cg_item)` and `config_item_put(&block_opts->group.cg_item)` in `f_midi2_free_inst` and call it a day?
Wait a second. `ep_opts` is a default group of `opts->func_inst.group`, and `block_opts` is a default group of `ep_opts->group`. If I call `config_item_put(&ep_opts->group.cg_item)`, it'll call `f_midi2_ep_opts_release` which *frees* `ep_opts`. Okay, so far, so good. But that also means `f_midi2_ep_opts_release` frees the `ep_opts`. What about `block_opts`? If `ep_opts` is freed, `block_opts` is still leaked because `f_midi2_ep_opts_release` does not free `block_opts`! I need to investigate `f_midi2_ep_opts_release`.
|
| 107/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_ep_opts_release]
Results: map[SourceCode:2472: static void f_midi2_ep_opts_release(struct config_item *item)
2473: {
2474: struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
2475:
2476: kfree(opts->info.ep_name);
2477: kfree(opts->info.product_id);
2478: kfree(opts);
2479: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 108/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 52098
output: 21
thoughts: 129
Thoughts: **Reflecting on Memory Management in Configuration Structures**
Okay, so I'm looking at this code, and it's clear we're not properly freeing `block_opts` here. That's a definite oversight. Now, let me think...right, `block_opts` *is* essentially a default group within `ep_opts`. This suggests that the function `f_midi2_ep_opts_release` needs to do the same kind of cleanup, dropping the reference count of its default groups, as `uvcg_config_remove_children` does. That seems like the right approach.
Wait, let's step back a moment. `uvcg_config_remove_children` is a general function, it's designed to iterate through the `default_groups` list and decrease the reference count of each of those children. Could we leverage that, or perhaps there is a function in configfs that is designed to do this specific task? The question now is, does `configfs` have a function that directly addresses removing default groups? I need to check.
|
| 109/2 |
2026/07/30 14:12 |
tool |
codesearch-find-references |
0m
Args:
Results: map[References:<nil> TruncatedOutput:false]
|
| 110/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 52493
output: 17
thoughts: 18
Thoughts: Okay, here's my thought process on that:
**Uh Oh, Wrong Tool! Quick Rethink**
Hang on a second... I think I jumped the gun there. I grabbed the wrong tool for the job, didn't I? Instead of that initial approach, I need to focus on a more direct way to find what I'm looking for. Let's see...I need to quickly locate information related to "default_groups." That's the key term. A good, old-fashioned `grep` should be perfect for this! It'll be a fast and efficient way to scan for those particular strings. Right, `grep` for `default_groups` it is. That should do the trick!
|
| 111/2 |
2026/07/30 14:12 |
tool |
grepper |
0m
Args: map[Expression:default_groups]
Results: map[Output:
Full output is too long, showing 500 out of 1221 lines.
Use more precise expression if possible.
Documentation/core-api/kobject.rst=297=kobj_type::
--
Documentation/core-api/kobject.rst-301- const struct sysfs_ops *sysfs_ops;
Documentation/core-api/kobject.rst:302: const struct attribute_group **default_groups;
Documentation/core-api/kobject.rst-303- const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
--
Documentation/core-api/kobject.rst=314=release() method for this type of kobject. The other two fields (sysfs_ops
Documentation/core-api/kobject.rst:315:and default_groups) control how objects of this type are represented in
Documentation/core-api/kobject.rst-316-sysfs; they are beyond the scope of this document.
Documentation/core-api/kobject.rst-317-
Documentation/core-api/kobject.rst:318:The default_groups pointer is a list of default attributes that will be
Documentation/core-api/kobject.rst-319-automatically created for any kobject that is registered with this ktype.
--
Documentation/filesystems/configfs.rst=265=child item::
--
Documentation/filesystems/configfs.rst-270- struct configfs_subsystem *cg_subsys;
Documentation/filesystems/configfs.rst:271: struct list_head default_groups;
Documentation/filesystems/configfs.rst-272- struct list_head group_entry;
--
Documentation/translations/zh_CN/core-api/kobject.rst=231=kobject_put()以避免错误的发生是一个很好的做法。
--
Documentation/translations/zh_CN/core-api/kobject.rst-260- const struct sysfs_ops *sysfs_ops;
Documentation/translations/zh_CN/core-api/kobject.rst:261: const struct attribute_group **default_groups;
Documentation/translations/zh_CN/core-api/kobject.rst-262- const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
--
Documentation/translations/zh_CN/core-api/kobject.rst=269=kobject_init()或kobject_init_and_add()时必须指定一个指向该结构的
--
Documentation/translations/zh_CN/core-api/kobject.rst-272-当然,kobj_type结构中的release字段是指向这种类型的kobject的release()
Documentation/translations/zh_CN/core-api/kobject.rst:273:方法的一个指针。另外两个字段(sysfs_ops 和 default_groups)控制这种
Documentation/translations/zh_CN/core-api/kobject.rst-274-类型的对象如何在 sysfs 中被表示;它们超出了本文的范围。
Documentation/translations/zh_CN/core-api/kobject.rst-275-
Documentation/translations/zh_CN/core-api/kobject.rst:276:default_groups 指针是一个默认属性的列表,它将为任何用这个 ktype 注册
Documentation/translations/zh_CN/core-api/kobject.rst-277-的 kobject 自动创建。
--
arch/powerpc/kernel/cacheinfo.c=709=static struct kobj_attribute cache_shared_cpu_list_attr =
--
arch/powerpc/kernel/cacheinfo.c-712-/* Attributes which should always be created -- the kobject/sysfs core
arch/powerpc/kernel/cacheinfo.c:713: * does this automatically via kobj_type->default_groups. This is the
arch/powerpc/kernel/cacheinfo.c-714- * minimum data required to uniquely identify a cache.
--
arch/powerpc/kernel/cacheinfo.c=739=static const struct kobj_type cache_index_type = {
--
arch/powerpc/kernel/cacheinfo.c-741- .sysfs_ops = &cache_index_ops,
arch/powerpc/kernel/cacheinfo.c:742: .default_groups = cache_index_default_groups,
arch/powerpc/kernel/cacheinfo.c-743-};
--
arch/powerpc/kernel/secvar-sysfs.c=129=static const struct kobj_type secvar_ktype = {
arch/powerpc/kernel/secvar-sysfs.c-130- .sysfs_ops = &kobj_sysfs_ops,
arch/powerpc/kernel/secvar-sysfs.c:131: .default_groups = secvar_attr_groups,
arch/powerpc/kernel/secvar-sysfs.c-132-};
--
arch/powerpc/platforms/powernv/opal-dump.c=214=static const struct kobj_type dump_ktype = {
--
arch/powerpc/platforms/powernv/opal-dump.c-216- .release = &dump_release,
arch/powerpc/platforms/powernv/opal-dump.c:217: .default_groups = dump_default_groups,
arch/powerpc/platforms/powernv/opal-dump.c-218-};
--
arch/powerpc/platforms/powernv/opal-elog.c=148=static const struct kobj_type elog_ktype = {
--
arch/powerpc/platforms/powernv/opal-elog.c-150- .release = &elog_release,
arch/powerpc/platforms/powernv/opal-elog.c:151: .default_groups = elog_default_groups,
arch/powerpc/platforms/powernv/opal-elog.c-152-};
--
arch/powerpc/platforms/pseries/vas-sysfs.c=166=static const struct kobj_type vas_def_attr_type = {
--
arch/powerpc/platforms/pseries/vas-sysfs.c-168- .sysfs_ops = &vas_sysfs_ops,
arch/powerpc/platforms/pseries/vas-sysfs.c:169: .default_groups = vas_def_capab_groups,
arch/powerpc/platforms/pseries/vas-sysfs.c-170-};
--
arch/powerpc/platforms/pseries/vas-sysfs.c=172=static const struct kobj_type vas_qos_attr_type = {
--
arch/powerpc/platforms/pseries/vas-sysfs.c-174- .sysfs_ops = &vas_sysfs_ops,
arch/powerpc/platforms/pseries/vas-sysfs.c:175: .default_groups = vas_qos_capab_groups,
arch/powerpc/platforms/pseries/vas-sysfs.c-176-};
--
arch/sh/kernel/cpu/sh4/sq.c=334=static struct kobj_type ktype_percpu_entry = {
arch/sh/kernel/cpu/sh4/sq.c-335- .sysfs_ops = &sq_sysfs_ops,
arch/sh/kernel/cpu/sh4/sq.c:336: .default_groups = sq_sysfs_groups,
arch/sh/kernel/cpu/sh4/sq.c-337-};
--
arch/x86/kernel/cpu/mce/amd.c=1038=static const struct kobj_type threshold_ktype = {
arch/x86/kernel/cpu/mce/amd.c-1039- .sysfs_ops = &threshold_ops,
arch/x86/kernel/cpu/mce/amd.c:1040: .default_groups = default_groups,
arch/x86/kernel/cpu/mce/amd.c-1041- .release = threshold_block_release,
--
arch/x86/platform/efi/runtime-map.c=96=static const struct kobj_type __refconst map_ktype = {
arch/x86/platform/efi/runtime-map.c-97- .sysfs_ops = &map_attr_ops,
arch/x86/platform/efi/runtime-map.c:98: .default_groups = def_groups,
arch/x86/platform/efi/runtime-map.c-99- .release = map_release,
--
block/blk-crypto-sysfs.c=154=static const struct kobj_type blk_crypto_ktype = {
block/blk-crypto-sysfs.c:155: .default_groups = blk_crypto_attr_groups,
block/blk-crypto-sysfs.c-156- .sysfs_ops = &blk_crypto_attr_ops,
--
block/blk-ia-ranges.c=78=static const struct kobj_type blk_ia_range_ktype = {
block/blk-ia-ranges.c-79- .sysfs_ops = &blk_ia_range_sysfs_ops,
block/blk-ia-ranges.c:80: .default_groups = blk_ia_range_groups,
block/blk-ia-ranges.c-81- .release = blk_ia_range_sysfs_nop_release,
--
block/blk-mq-sysfs.c=137=static const struct kobj_type blk_mq_hw_ktype = {
block/blk-mq-sysfs.c-138- .sysfs_ops = &blk_mq_hw_sysfs_ops,
block/blk-mq-sysfs.c:139: .default_groups = default_hw_ctx_groups,
block/blk-mq-sysfs.c-140- .release = blk_mq_hw_sysfs_release,
--
block/blk-sysfs.c=925=const struct kobj_type blk_queue_ktype = {
block/blk-sysfs.c:926: .default_groups = blk_queue_attr_groups,
block/blk-sysfs.c-927- .sysfs_ops = &queue_sysfs_ops,
--
drivers/acpi/cppc_acpi.c=234=static const struct kobj_type cppc_ktype = {
drivers/acpi/cppc_acpi.c-235- .sysfs_ops = &kobj_sysfs_ops,
drivers/acpi/cppc_acpi.c:236: .default_groups = cppc_groups,
drivers/acpi/cppc_acpi.c-237-};
--
drivers/acpi/device_sysfs.c=81=static const struct kobj_type acpi_data_node_ktype = {
drivers/acpi/device_sysfs.c-82- .sysfs_ops = &acpi_data_node_sysfs_ops,
drivers/acpi/device_sysfs.c:83: .default_groups = acpi_data_node_default_groups,
drivers/acpi/device_sysfs.c-84- .release = acpi_data_node_release,
--
drivers/acpi/sysfs.c=973=static const struct kobj_type acpi_hotplug_profile_ktype = {
drivers/acpi/sysfs.c-974- .sysfs_ops = &kobj_sysfs_ops,
drivers/acpi/sysfs.c:975: .default_groups = hotplug_profile_groups,
drivers/acpi/sysfs.c-976-};
--
drivers/base/cacheinfo.c=836=static const struct attribute_group cache_default_group = {
--
drivers/base/cacheinfo.c-840-
drivers/base/cacheinfo.c:841:static const struct attribute_group *cache_default_groups[] = {
drivers/base/cacheinfo.c-842- &cache_default_group,
--
drivers/base/cacheinfo.c=859=cache_get_attribute_groups(struct cacheinfo *this_leaf)
--
drivers/base/cacheinfo.c-864- if (!priv_group)
drivers/base/cacheinfo.c:865: return cache_default_groups;
drivers/base/cacheinfo.c-866-
--
drivers/block/rnbd/rnbd-clt-sysfs.c=485=static const struct kobj_type rnbd_dev_ktype = {
drivers/block/rnbd/rnbd-clt-sysfs.c-486- .sysfs_ops = &kobj_sysfs_ops,
drivers/block/rnbd/rnbd-clt-sysfs.c:487: .default_groups = rnbd_dev_groups,
drivers/block/rnbd/rnbd-clt-sysfs.c-488- .release = rnbd_dev_release,
--
drivers/cpufreq/cpufreq.c=1021=static const struct kobj_type ktype_cpufreq = {
drivers/cpufreq/cpufreq.c-1022- .sysfs_ops = &sysfs_ops,
drivers/cpufreq/cpufreq.c:1023: .default_groups = cpufreq_groups,
drivers/cpufreq/cpufreq.c-1024- .release = cpufreq_sysfs_release,
--
drivers/cpufreq/cpufreq_conservative.c=317=static struct dbs_governor cs_governor = {
drivers/cpufreq/cpufreq_conservative.c-318- .gov = CPUFREQ_DBS_GOVERNOR_INITIALIZER("conservative"),
drivers/cpufreq/cpufreq_conservative.c:319: .kobj_type = { .default_groups = cs_groups },
drivers/cpufreq/cpufreq_conservative.c-320- .gov_dbs_update = cs_dbs_update,
--
drivers/cpufreq/cpufreq_ondemand.c=375=static struct dbs_governor od_dbs_gov = {
drivers/cpufreq/cpufreq_ondemand.c-376- .gov = CPUFREQ_DBS_GOVERNOR_INITIALIZER("ondemand"),
drivers/cpufreq/cpufreq_ondemand.c:377: .kobj_type = { .default_groups = od_groups },
drivers/cpufreq/cpufreq_ondemand.c-378- .gov_dbs_update = od_dbs_update,
--
drivers/cpuidle/sysfs.c=457=static const struct kobj_type ktype_state_cpuidle = {
drivers/cpuidle/sysfs.c-458- .sysfs_ops = &cpuidle_state_sysfs_ops,
drivers/cpuidle/sysfs.c:459: .default_groups = cpuidle_state_default_groups,
drivers/cpuidle/sysfs.c-460- .release = cpuidle_state_sysfs_release,
--
drivers/cpuidle/sysfs.c=604=static const struct kobj_type ktype_driver_cpuidle = {
drivers/cpuidle/sysfs.c-605- .sysfs_ops = &cpuidle_driver_sysfs_ops,
drivers/cpuidle/sysfs.c:606: .default_groups = cpuidle_driver_default_groups,
drivers/cpuidle/sysfs.c-607- .release = cpuidle_driver_sysfs_release,
--
drivers/dma/ioat/sysfs.c=169=const struct kobj_type ioat_ktype = {
drivers/dma/ioat/sysfs.c-170- .sysfs_ops = &ioat_sysfs_ops,
drivers/dma/ioat/sysfs.c:171: .default_groups = ioat_groups,
drivers/dma/ioat/sysfs.c-172-};
--
drivers/edac/edac_device_sysfs.c=213=static struct kobj_type ktype_device_ctrl = {
--
drivers/edac/edac_device_sysfs.c-215- .sysfs_ops = &device_ctl_info_ops,
drivers/edac/edac_device_sysfs.c:216: .default_groups = device_ctrl_groups,
drivers/edac/edac_device_sysfs.c-217-};
--
drivers/edac/edac_device_sysfs.c=398=static struct kobj_type ktype_instance_ctrl = {
--
drivers/edac/edac_device_sysfs.c-400- .sysfs_ops = &device_instance_ops,
drivers/edac/edac_device_sysfs.c:401: .default_groups = device_instance_groups,
drivers/edac/edac_device_sysfs.c-402-};
--
drivers/edac/edac_device_sysfs.c=481=static struct kobj_type ktype_block_ctrl = {
--
drivers/edac/edac_device_sysfs.c-483- .sysfs_ops = &device_block_ops,
drivers/edac/edac_device_sysfs.c:484: .default_groups = device_block_groups,
drivers/edac/edac_device_sysfs.c-485-};
--
drivers/edac/edac_pci_sysfs.c=146=static struct kobj_type ktype_pci_instance = {
--
drivers/edac/edac_pci_sysfs.c-148- .sysfs_ops = &pci_instance_ops,
drivers/edac/edac_pci_sysfs.c:149: .default_groups = pci_instance_groups,
drivers/edac/edac_pci_sysfs.c-150-};
--
drivers/edac/edac_pci_sysfs.c=329=static struct kobj_type ktype_edac_pci_main_kobj = {
--
drivers/edac/edac_pci_sysfs.c-331- .sysfs_ops = &edac_pci_sysfs_ops,
drivers/edac/edac_pci_sysfs.c:332: .default_groups = edac_pci_groups,
drivers/edac/edac_pci_sysfs.c-333-};
--
drivers/firmware/dmi-sysfs.c=307=static const struct kobj_type dmi_system_event_log_ktype = {
--
drivers/firmware/dmi-sysfs.c-309- .sysfs_ops = &dmi_sysfs_specialize_attr_ops,
drivers/firmware/dmi-sysfs.c:310: .default_groups = dmi_sysfs_sel_groups,
drivers/firmware/dmi-sysfs.c-311-};
--
drivers/firmware/dmi-sysfs.c=564=static const struct kobj_type dmi_sysfs_entry_ktype = {
--
drivers/firmware/dmi-sysfs.c-566- .sysfs_ops = &dmi_sysfs_attr_ops,
drivers/firmware/dmi-sysfs.c:567: .default_groups = dmi_sysfs_entry_groups,
drivers/firmware/dmi-sysfs.c-568-};
--
drivers/firmware/efi/esrt.c=153=static const struct kobj_type esre1_ktype = {
--
drivers/firmware/efi/esrt.c-155- .sysfs_ops = &esre_attr_ops,
drivers/firmware/efi/esrt.c:156: .default_groups = esre1_groups,
drivers/firmware/efi/esrt.c-157-};
--
drivers/firmware/memmap.c=119=static const struct kobj_type memmap_ktype = {
--
drivers/firmware/memmap.c-121- .sysfs_ops = &memmap_attr_ops,
drivers/firmware/memmap.c:122: .default_groups = def_groups,
drivers/firmware/memmap.c-123-};
--
drivers/firmware/qemu_fw_cfg.c=454=static const struct kobj_type fw_cfg_sysfs_entry_ktype = {
drivers/firmware/qemu_fw_cfg.c:455: .default_groups = fw_cfg_sysfs_entry_groups,
drivers/firmware/qemu_fw_cfg.c-456- .sysfs_ops = &fw_cfg_sysfs_attr_ops,
--
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c=1016=static const struct kobj_type ip_hw_instance_ktype = {
--
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c-1018- .sysfs_ops = &ip_hw_instance_sysfs_ops,
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c:1019: .default_groups = ip_hw_instance_groups,
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c-1020-};
--
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c=1098=static const struct kobj_type ip_die_entry_ktype = {
--
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c-1100- .sysfs_ops = &ip_die_entry_sysfs_ops,
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c:1101: .default_groups = ip_die_entry_groups,
drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c-1102-};
--
drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c=773=static const struct kobj_type xcp_cfg_res_sysfs_ktype = {
drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c-774- .sysfs_ops = &xcp_cfg_res_sysfs_ops,
drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c:775: .default_groups = xcp_cfg_res_sysfs_groups,
drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c-776-};
--
drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c=440=static const struct kobj_type amdgpu_xgmi_hive_type = {
--
drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c-442- .sysfs_ops = &amdgpu_xgmi_hive_ops,
drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c:443: .default_groups = amdgpu_xgmi_hive_groups,
drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c-444-};
--
drivers/gpu/drm/amd/amdkfd/kfd_process.c=497=static const struct kobj_type procfs_queue_type = {
drivers/gpu/drm/amd/amdkfd/kfd_process.c-498- .sysfs_ops = &procfs_queue_ops,
drivers/gpu/drm/amd/amdkfd/kfd_process.c:499: .default_groups = procfs_queue_groups,
drivers/gpu/drm/amd/amdkfd/kfd_process.c-500-};
--
drivers/gpu/drm/i915/gt/intel_gt_sysfs.c=75=static const struct kobj_type kobj_gt_type = {
--
drivers/gpu/drm/i915/gt/intel_gt_sysfs.c-77- .sysfs_ops = &kobj_sysfs_ops,
drivers/gpu/drm/i915/gt/intel_gt_sysfs.c:78: .default_groups = id_groups,
drivers/gpu/drm/i915/gt/intel_gt_sysfs.c-79-};
--
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c=484=static const struct kobj_type xe_sriov_dev_ktype = {
--
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c-486- .sysfs_ops = &xe_sriov_dev_sysfs_ops,
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c:487: .default_groups = xe_sriov_dev_attr_groups,
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c-488-};
--
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c=490=static const struct kobj_type xe_sriov_vf_ktype = {
--
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c-492- .sysfs_ops = &xe_sriov_vf_sysfs_ops,
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c:493: .default_groups = xe_sriov_vf_attr_groups,
drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c-494-};
--
drivers/infiniband/core/cma_configfs.c=318=static void drop_cma_dev(struct config_group *cgroup, struct config_item *item)
--
drivers/infiniband/core/cma_configfs.c-324-
drivers/infiniband/core/cma_configfs.c:325: configfs_remove_default_groups(&cma_dev_group->ports_group);
drivers/infiniband/core/cma_configfs.c:326: configfs_remove_default_groups(&cma_dev_group->device_group);
drivers/infiniband/core/cma_configfs.c-327- config_item_put(item);
--
drivers/infiniband/core/sysfs.c=727=static struct kobj_type port_type = {
--
drivers/infiniband/core/sysfs.c-729- .sysfs_ops = &port_sysfs_ops,
drivers/infiniband/core/sysfs.c:730: .default_groups = port_default_groups,
drivers/infiniband/core/sysfs.c-731-};
--
drivers/infiniband/hw/usnic/usnic_ib_sysfs.c=247=static struct kobj_type usnic_ib_qpn_type = {
drivers/infiniband/hw/usnic/usnic_ib_sysfs.c-248- .sysfs_ops = &usnic_ib_qpn_sysfs_ops,
drivers/infiniband/hw/usnic/usnic_ib_sysfs.c:249: .default_groups = usnic_ib_qpn_default_groups,
drivers/infiniband/hw/usnic/usnic_ib_sysfs.c-250-};
--
drivers/md/bcache/sysfs.h=6=const struct kobj_type type ## _ktype = { \
--
drivers/md/bcache/sysfs.h-11- }), \
drivers/md/bcache/sysfs.h:12: .default_groups = type ## _groups \
drivers/md/bcache/sysfs.h-13-}
--
drivers/md/dm-sysfs.c=119=static const struct kobj_type dm_ktype = {
drivers/md/dm-sysfs.c-120- .sysfs_ops = &dm_sysfs_ops,
drivers/md/dm-sysfs.c:121: .default_groups = dm_groups,
drivers/md/dm-sysfs.c-122- .release = dm_kobject_release,
--
drivers/md/md.c=3785=static const struct kobj_type rdev_ktype = {
--
drivers/md/md.c-3787- .sysfs_ops = &rdev_sysfs_ops,
drivers/md/md.c:3788: .default_groups = rdev_default_groups,
drivers/md/md.c-3789-};
--
drivers/md/md.c=6170=static const struct kobj_type md_ktype = {
--
drivers/md/md.c-6172- .sysfs_ops = &md_sysfs_ops,
drivers/md/md.c:6173: .default_groups = md_default_groups,
drivers/md/md.c-6174-};
--
drivers/net/ethernet/ibm/ibmveth.c=2127=static struct kobj_type ktype_veth_pool = {
--
drivers/net/ethernet/ibm/ibmveth.c-2129- .sysfs_ops = &veth_pool_ops,
drivers/net/ethernet/ibm/ibmveth.c:2130: .default_groups = veth_pool_groups,
drivers/net/ethernet/ibm/ibmveth.c-2131-};
--
drivers/parisc/pdc_stable.c=486=static const struct kobj_type ktype_pdcspath = {
drivers/parisc/pdc_stable.c-487- .sysfs_ops = &pdcspath_attr_ops,
drivers/parisc/pdc_stable.c:488: .default_groups = paths_subsys_groups,
drivers/parisc/pdc_stable.c-489-};
--
drivers/pci/slot.c=110=static const struct attribute_group pci_slot_default_group = {
--
drivers/pci/slot.c-113-
drivers/pci/slot.c:114:static const struct attribute_group *pci_slot_default_groups[] = {
drivers/pci/slot.c-115- &pci_slot_default_group,
--
drivers/pci/slot.c=122=static const struct kobj_type pci_slot_ktype = {
--
drivers/pci/slot.c-124- .release = &pci_slot_release,
drivers/pci/slot.c:125: .default_groups = pci_slot_default_groups,
drivers/pci/slot.c-126-};
--
drivers/platform/x86/intel/pmt/discovery.c=282=static const struct kobj_type pmt_feature_rmid_ktype = {
--
drivers/platform/x86/intel/pmt/discovery.c-284- .release = pmt_feature_release,
drivers/platform/x86/intel/pmt/discovery.c:285: .default_groups = pmt_feature_rmid_groups,
drivers/platform/x86/intel/pmt/discovery.c-286-};
--
drivers/platform/x86/intel/pmt/discovery.c=298=static const struct kobj_type pmt_feature_watcher_ktype = {
--
drivers/platform/x86/intel/pmt/discovery.c-300- .release = pmt_feature_release,
drivers/platform/x86/intel/pmt/discovery.c:301: .default_groups = pmt_feature_watcher_groups,
drivers/platform/x86/intel/pmt/discovery.c-302-};
--
drivers/platform/x86/intel/pmt/discovery.c=313=static const struct kobj_type pmt_feature_command_ktype = {
--
drivers/platform/x86/intel/pmt/discovery.c-315- .release = pmt_feature_release,
drivers/platform/x86/intel/pmt/discovery.c:316: .default_groups = pmt_feature_command_groups,
drivers/platform/x86/intel/pmt/discovery.c-317-};
--
drivers/platform/x86/intel/pmt/discovery.c=326=static const struct kobj_type pmt_feature_guids_ktype = {
--
drivers/platform/x86/intel/pmt/discovery.c-328- .release = pmt_feature_release,
drivers/platform/x86/intel/pmt/discovery.c:329: .default_groups = pmt_feature_guids_groups,
drivers/platform/x86/intel/pmt/discovery.c-330-};
--
drivers/platform/x86/lenovo/think-lmi.c=1264=static const struct kobj_type tlmi_attr_setting_ktype = {
--
drivers/platform/x86/lenovo/think-lmi.c-1266- .sysfs_ops = &kobj_sysfs_ops,
drivers/platform/x86/lenovo/think-lmi.c:1267: .default_groups = tlmi_attr_groups,
drivers/platform/x86/lenovo/think-lmi.c-1268-};
--
drivers/platform/x86/lenovo/think-lmi.c=1270=static const struct kobj_type tlmi_pwd_setting_ktype = {
--
drivers/platform/x86/lenovo/think-lmi.c-1272- .sysfs_ops = &kobj_sysfs_ops,
drivers/platform/x86/lenovo/think-lmi.c:1273: .default_groups = auth_attr_groups,
drivers/platform/x86/lenovo/think-lmi.c-1274-};
--
drivers/platform/x86/uv_sysfs.c=207=static const struct kobj_type hub_attr_type = {
--
drivers/platform/x86/uv_sysfs.c-209- .sysfs_ops = &hub_sysfs_ops,
drivers/platform/x86/uv_sysfs.c:210: .default_groups = uv_hub_groups,
drivers/platform/x86/uv_sysfs.c-211-};
--
drivers/platform/x86/uv_sysfs.c=359=static const struct kobj_type uv_port_attr_type = {
--
drivers/platform/x86/uv_sysfs.c-361- .sysfs_ops = &uv_port_sysfs_ops,
drivers/platform/x86/uv_sysfs.c:362: .default_groups = uv_port_groups,
drivers/platform/x86/uv_sysfs.c-363-};
--
drivers/power/supply/ab8500_fg.c=2533=static const struct kobj_type ab8500_fg_ktype = {
drivers/power/supply/ab8500_fg.c-2534- .sysfs_ops = &ab8500_fg_sysfs_ops,
drivers/power/supply/ab8500_fg.c:2535: .default_groups = ab8500_fg_groups,
drivers/power/supply/ab8500_fg.c-2536-};
--
drivers/s390/block/dasd_devmap.c=2535=static struct kobj_type path_attr_type = {
drivers/s390/block/dasd_devmap.c-2536- .release = dasd_path_release,
drivers/s390/block/dasd_devmap.c:2537: .default_groups = paths_info_groups,
drivers/s390/block/dasd_devmap.c-2538- .sysfs_ops = &kobj_sysfs_ops,
--
drivers/s390/char/sclp_sd.c=456=static struct kobj_type sclp_sd_file_ktype = {
--
drivers/s390/char/sclp_sd.c-458- .release = sclp_sd_file_release,
drivers/s390/char/sclp_sd.c:459: .default_groups = sclp_sd_file_default_groups,
drivers/s390/char/sclp_sd.c-460-};
--
drivers/soc/hisilicon/kunpeng_hccs.c=1040=static const struct kobj_type hccs_port_type = {
drivers/soc/hisilicon/kunpeng_hccs.c-1041- .sysfs_ops = &hccs_comm_ops,
drivers/soc/hisilicon/kunpeng_hccs.c:1042: .default_groups = hccs_port_default_groups,
drivers/soc/hisilicon/kunpeng_hccs.c-1043-};
--
drivers/soc/hisilicon/kunpeng_hccs.c=1112=static const struct kobj_type hccs_die_type = {
drivers/soc/hisilicon/kunpeng_hccs.c-1113- .sysfs_ops = &hccs_comm_ops,
drivers/soc/hisilicon/kunpeng_hccs.c:1114: .default_groups = hccs_die_default_groups,
drivers/soc/hisilicon/kunpeng_hccs.c-1115-};
--
drivers/soc/hisilicon/kunpeng_hccs.c=1215=static const struct kobj_type hccs_chip_type = {
drivers/soc/hisilicon/kunpeng_hccs.c-1216- .sysfs_ops = &hccs_comm_ops,
drivers/soc/hisilicon/kunpeng_hccs.c:1217: .default_groups = hccs_chip_default_groups,
drivers/soc/hisilicon/kunpeng_hccs.c-1218-};
--
drivers/staging/greybus/audio_manager_module.c=146=static const struct kobj_type gb_audio_module_type = {
--
drivers/staging/greybus/audio_manager_module.c-148- .release = gb_audio_module_release,
drivers/staging/greybus/audio_manager_module.c:149: .default_groups = gb_audio_module_default_groups,
drivers/staging/greybus/audio_manager_module.c-150-};
--
drivers/target/target_core_configfs.c=267=static void target_core_deregister_fabric(
--
drivers/target/target_core_configfs.c-283-
drivers/target/target_core_configfs.c:284: configfs_remove_default_groups(&tf->tf_group);
drivers/target/target_core_configfs.c-285- config_item_put(item);
--
drivers/target/target_core_configfs.c=3422=static struct config_group *target_core_make_subdev(
--
drivers/target/target_core_configfs.c-3485- */
drivers/target/target_core_configfs.c:3486: target_stat_setup_dev_default_groups(dev);
drivers/target/target_core_configfs.c-3487-
--
drivers/target/target_core_configfs.c=3502=static void target_core_drop_subdev(
--
drivers/target/target_core_configfs.c-3514-
drivers/target/target_core_configfs.c:3515: configfs_remove_default_groups(&dev->dev_stat_grps.stat_group);
drivers/target/target_core_configfs.c:3516: configfs_remove_default_groups(&dev->t10_alua.alua_tg_pt_gps_group);
drivers/target/target_core_configfs.c-3517-
--
drivers/target/target_core_configfs.c-3523-
drivers/target/target_core_configfs.c:3524: configfs_remove_default_groups(dev_cg);
drivers/target/target_core_configfs.c-3525-
--
drivers/target/target_core_configfs.c=3843=static void __exit target_core_exit_configfs(void)
drivers/target/target_core_configfs.c-3844-{
drivers/target/target_core_configfs.c:3845: configfs_remove_default_groups(&alua_lu_gps_group);
drivers/target/target_core_configfs.c:3846: configfs_remove_default_groups(&alua_group);
drivers/target/target_core_configfs.c:3847: configfs_remove_default_groups(&target_core_hbagroup);
drivers/target/target_core_configfs.c-3848-
drivers/target/target_core_configfs.c-3849- /*
drivers/target/target_core_configfs.c:3850: * We expect subsys->su_group.default_groups to be released
drivers/target/target_core_configfs.c-3851- * by configfs subsystem provider logic..
--
drivers/target/target_core_fabric_configfs.c=265=static struct config_group *target_fabric_make_mappedlun(
--
drivers/target/target_core_fabric_configfs.c-315-
drivers/target/target_core_fabric_configfs.c:316: target_stat_setup_mappedlun_default_groups(lacl);
drivers/target/target_core_fabric_configfs.c-317-
--
drivers/target/target_core_fabric_configfs.c=326=static void target_fabric_drop_mappedlun(
--
drivers/target/target_core_fabric_configfs.c-332-
drivers/target/target_core_fabric_configfs.c:333: configfs_remove_default_groups(&lacl->ml_stat_grps.stat_group);
drivers/target/target_core_fabric_configfs.c:334: configfs_remove_default_groups(&lacl->se_lun_group);
drivers/target/target_core_fabric_configfs.c-335-
--
drivers/target/target_core_fabric_configfs.c=339=static void target_fabric_nacl_base_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-343-
drivers/target/target_core_fabric_configfs.c:344: configfs_remove_default_groups(&se_nacl->acl_fabric_stat_group);
drivers/target/target_core_fabric_configfs.c-345- core_tpg_del_initiator_node_acl(se_nacl);
--
drivers/target/target_core_fabric_configfs.c=357=TF_CIT_SETUP_DRV(tpg_nacl_base, &target_fabric_nacl_base_item_ops,
--
drivers/target/target_core_fabric_configfs.c-364- * This is used as a placeholder for struct se_node_acl->acl_fabric_stat_group
drivers/target/target_core_fabric_configfs.c:365: * to allow fabrics access to ->acl_fabric_stat_group->default_groups[]
drivers/target/target_core_fabric_configfs.c-366- */
--
drivers/target/target_core_fabric_configfs.c=373=static struct config_group *target_fabric_make_nodeacl(
--
drivers/target/target_core_fabric_configfs.c-411- if (ret) {
drivers/target/target_core_fabric_configfs.c:412: configfs_remove_default_groups(&se_nacl->acl_fabric_stat_group);
drivers/target/target_core_fabric_configfs.c-413- core_tpg_del_initiator_node_acl(se_nacl);
--
drivers/target/target_core_fabric_configfs.c=421=static void target_fabric_drop_nodeacl(
--
drivers/target/target_core_fabric_configfs.c-427-
drivers/target/target_core_fabric_configfs.c:428: configfs_remove_default_groups(&se_nacl->acl_group);
drivers/target/target_core_fabric_configfs.c-429-
--
drivers/target/target_core_fabric_configfs.c=740=static struct config_group *target_fabric_make_lun(
--
drivers/target/target_core_fabric_configfs.c-771-
drivers/target/target_core_fabric_configfs.c:772: target_stat_setup_port_default_groups(lun);
drivers/target/target_core_fabric_configfs.c-773-
--
drivers/target/target_core_fabric_configfs.c=777=static void target_fabric_drop_lun(
]
|
| 112/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 64481
output: 26
thoughts: 22
Thoughts: Here's my take on the thought process, tailored for an expert audience:
**Investigating `configfs_remove_default_groups`**
Okay, good news! `configfs_remove_default_groups` is definitely a thing. I remember vaguely, or maybe I've seen it in some other context, so that's a positive start. Now, the natural next step is to dive right into its definition. I need to understand its exact purpose within the configfs framework, the arguments it takes, and of course, what side effects it might introduce. I'll need to look at how it interacts with the broader namespace logic, file permissions, and perhaps the handling of group membership within the virtual filesystem. My focus now will be its precise function and relationship to the overall configuration management process. Understanding this will be critical to the immediate task.
|
| 113/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:configfs_remove_default_groups]
Results: map[SourceCode: 748: void configfs_remove_default_groups(struct config_group *group)
749: {
750: struct config_group *g, *n;
751:
752: list_for_each_entry_safe(g, n, &group->default_groups, group_entry) {
753: list_del(&g->group_entry);
754: config_item_put(&g->cg_item);
755: }
756: }
SourceFile:fs/configfs/dir.c]
|
| 114/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 64879
output: 28
thoughts: 249
Thoughts: **My Analysis of Default Group Removal in ConfigFS**
Okay, so `configfs_remove_default_groups` seems straightforward enough. It iterates through `default_groups`, removes them from the list, and then calls `config_item_put`. Standard refcount management. But, hang on... didn't I see something similar elsewhere? Ah, yes, `uvcg_config_remove_children` also removes things, but it does it recursively. Hmm, does `configfs_remove_default_groups` handle recursion? No, it doesn't.
Now, I remember `f_midi2` has this hierarchical structure of default groups: we're dealing with `opts->func_inst.group`, `ep_opts->group`, and `block_opts->group`. If I call `configfs_remove_default_groups` on `&opts->func_inst.group`, that's going to drop the refcount of `ep_opts->group`. That triggers `f_midi2_ep_opts_release`, I know this because that's where I setup the release code.
The issue is that if I then call `configfs_remove_default_groups` on `&ep_opts->group` *within* `f_midi2_ep_opts_release`, it would drop the refcount of `block_opts->group`. This will then trigger `f_midi2_block_opts_release`. It's a cascading effect down the hierarchy.
Let me just double-check the logic in `f_midi2_ep_opts_release` again to be sure I'm not missing anything. Gotta make sure this refcounting and release dance is clean.
|
| 115/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:f_midi2_ep_opts_release]
Results: map[SourceCode:2472: static void f_midi2_ep_opts_release(struct config_item *item)
2473: {
2474: struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
2475:
2476: kfree(opts->info.ep_name);
2477: kfree(opts->info.product_id);
2478: kfree(opts);
2479: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 116/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 65689
output: 30
thoughts: 323
Thoughts: **Debugging a Potential Leak in MIDI2 Functionality**
Okay, so I'm trying to track down this memory leak, and I'm thinking about where we're releasing resources in the MIDI2 code. My initial thought is, if I add `configfs_remove_default_groups(to_config_group(item))` to both `f_midi2_ep_opts_release` and `f_midi2_opts_release`, would that plug the hole? It seems like the obvious place to start since we're dealing with configfs items and groups here.
Hold on, though. Looking closer at `f_midi2_opts_release`, I see that it's called when `opts->func_inst.group` is released. That's good, that's what I want. Now, looking at `f_midi2_ep_opts_release`, I see some `kfree`s to handle individual properties being freed. The `configfs_remove_default_groups` should probably be here too. Okay, so that should address the configfs group removal.
Wait a second...is `configfs_remove_default_groups` actually exported? I need to check the headers to make sure I can even *call* that function. That's a key detail I need to confirm before I even consider making the change. If it's not exported, then I need to figure out what the correct function call is to remove those configfs groups.
|
| 117/2 |
2026/07/30 14:12 |
tool |
grepper |
0m
Error:
Args: map[Expression:EXPORT_SYMBOL_GPL\(configfs_remove_default_groups\)]
Results:
|
| 118/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 66371
output: 26
thoughts: 20
Thoughts: Okay, here's my summary, taking the role of the expert:
**Investigating `configfs_remove_default_groups` and its Symbol Export**
Right, let's dive into this. The task at hand is to analyze the `EXPORT_SYMBOL` status of the function `configfs_remove_default_groups`. I need to understand if it's visible, accessible, and potentially usable (or abused) outside of its intended scope. My immediate thought is, "Is this exposed intentionally, or is it a potential internal leakage?" That distinction is crucial. It tells us about the design choices and the dependency relationships within the configfs subsystem. Knowing if it's exported also directly impacts security considerations. If it is exported then it is available for other drivers or modules to use, and if it is not exported, it is not.
|
| 119/2 |
2026/07/30 14:12 |
tool |
grepper |
0m
Args: map[Expression:EXPORT_SYMBOL.*configfs_remove_default_groups]
Results: map[Output:fs/configfs/dir.c=748=void configfs_remove_default_groups(struct config_group *group)
--
fs/configfs/dir.c-756-}
fs/configfs/dir.c:757:EXPORT_SYMBOL(configfs_remove_default_groups);
fs/configfs/dir.c-758-
]
|
| 120/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 66694
output: 25
thoughts: 3113
Thoughts: **Analysis of `f_midi2` Device Option Release and Resource Management**
Okay, so I've been reviewing the logic around the creation, release, and possible leaks of `ep_opts` and `block_opts` structures within the `f_midi2` device driver, especially in the context of configfs. My initial thought process is to trace where these objects are created and how their lifecycles are managed, looking for potential use-after-free or double-free scenarios.
First, I confirm the initial creation of default endpoint and block options using `f_midi2_ep_opts_create` and `f_midi2_block_opts_create`. I see that on failure, the appropriate frees are called. I then note the use of `config_group_init_type_name` and the creation of the default groups. I also check that the refcounts are correctly managed with respect to `kfree` and dynamic allocation. If `f_midi2_block_opts_create` fails, it correctly frees `ep_opts` and `opts`. The crucial thing here is to ensure the relationships between the configfs groups and the underlying data structures. `config_group_init_type_name` creates a reference to these.
I then focus on the scenario where a user creates and subsequently removes a configfs directory. If the allocation function `f_midi2_alloc_inst` fails after the structures are created in configfs, the release functions (`f_midi2_opts_release`, `f_midi2_ep_opts_release`, `f_midi2_block_opts_release`) become relevant.
I need to confirm the use of `configfs_remove_default_groups` to ensure all children are released. I think that adding it to `f_midi2_opts_release` is necessary because `opts` is the root group. The crucial point here is whether `f_midi2_ep_opts_release` and `f_midi2_block_opts_release` are doing the right thing in terms of freeing resources and avoiding double-frees.
My next line of thinking involved the dynamic creation of endpoints and blocks via `mkdir`. I determined the corresponding `make_group` and `drop_item` callbacks (`f_midi2_opts_ep_make`, `f_midi2_opts_ep_drop`, `f_midi2_opts_block_make`, and `f_midi2_opts_block_drop`). I then followed the code flow in these drop functions.
I realize that dynamically created `ep_opts` and `block_opts` do *not* have default groups, and are removed via `rmdir` directly by the user.
Then, I focused on the scenario of `f_midi2_opts_release` versus `usb_put_function_instance`, ensuring I account for both paths. I see that there is a different release path for dynamically created instances through `usb_put_function_instance`. I confirm that `usb_put_function_instance` calls `f_midi2_free_inst` and that this path bypasses `config_item_put` and `f_midi2_opts_release` completely. This is the crucial point to make sure there are no leaks.
So in summary, the only release path goes through `usb_put_function_instance`, which then leads to `f_midi2_free_inst`. This seems to avoid the need to handle the release of default groups separately. This needs to be carefully documented.
|
| 121/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Args: map[Name:usb_put_function_instance]
Results: map[SourceCode: 69: void usb_put_function_instance(struct usb_function_instance *fi)
70: {
71: struct module *mod;
72:
73: if (!fi)
74: return;
75:
76: mod = fi->fd->mod;
77: fi->free_func_inst(fi);
78: module_put(mod);
79: }
SourceFile:drivers/usb/gadget/functions.c]
|
| 122/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 70743
output: 22
thoughts: 2036
Thoughts: **Analyzing USB Function Instance Lifecycle and Potential Memory Leaks**
Okay, so I'm thinking through how `usb_put_function_instance` interacts with freeing function instances, specifically looking at how we prevent memory leaks. I've realized that the standard call chain `usb_put_function_instance` -> `fi->free_func_inst(fi)` is central, but the details are subtle. The critical thing to consider is whether we are dealing with a configfs-managed instance, or one handled directly.
My initial thought was, if `f_midi2_free_inst` doesn't free `ep_opts` and `block_opts`, we'll have a leak if `usb_put_function_instance` is called directly. However, if `config_group_init_type_name` initialized `fi->group`, it starts with a refcount of 1. If the `function_make` succeeds, it returns `&fi->group` and configfs takes over the refcount of `fi->group`. When the configfs directory is removed, `function_drop` is called, and `config_item_put` drops the refcount of `fi->group.cg_item`. When it reaches 0, `f_midi2_opts_release` is called, and then `usb_put_function_instance` is called again, triggering the intended `f_midi2_free_inst` call.
So, `f_midi2_free_inst` is ALWAYS called, regardless of the path. However, `f_midi2_opts_release` is ONLY called if configfs took over. Here's the key: if `usb_put_function_instance` is called directly (for example, in an error path in `function_make`, or by legacy gadget drivers) `f_midi2_opts_release` is NEVER called! Therefore if we put `configfs_remove_default_groups` in `f_midi2_opts_release`, it would be skipped in the direct path and the default groups will be leaked.
The conclusion is that we MUST free `ep_opts` and `block_opts` in `f_midi2_free_inst`. However, if configfs is still holding references to them, we might be in trouble. But because `f_midi2_free_inst` is called when the refcount of `fi` has reached 0, and if the path was through configfs, `config_item_put` had reached 0, so configfs is no longer holding a reference. So, the question remains about default groups. If we call `configfs_remove_default_groups(&opts->func_inst.group)` in `f_midi2_free_inst`, this would drop the refcount of the default groups, and then we could free. Following this logic we would then add `configfs_remove_default_groups(&opts->group)` in the `f_midi2_ep_opts_release` (and similar release functions).
I then considered how UVC handles this. It looks like it calls `uvcg_config_remove_children` from `uvc_func_item_release` NOT from `uvc_free_inst`. This removes children from the default groups. `uvc_alloc_inst` calls `uvcg_attach_configfs` to allocate the default groups and add them to `default_groups`. In the direct `usb_put_function_instance` case, `uvc_free_inst` is called which *only* frees `opts` not the default groups! This means the default groups are LEAKED!
So, `usb_put_function_instance` can be called directly in error paths (like if `config_item_set_name` fails) which means in the current uvc implementation, the default groups *are* leaked. Note, because `uvc` can be used by legacy drivers, the problem is more general. This is a very clear example of the problem, and a critical bug. It suggests that my `f_midi2` design, with the call to `configfs_remove_default_groups` in `f_midi2_free_inst` is correct.
|
| 123/2 |
2026/07/30 14:12 |
tool |
codesearch-definition-source |
0m
Error: requested entity does not exist
Args:
Results: map[SourceCode: SourceFile:]
|
| 124/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 73731
output: 21
thoughts: 30
Thoughts: Here's my thought process, as if I were considering this search:
**Diving into the Webcam Driver**
Okay, so I need to find where `usb_get_function_instance` is used within the legacy webcam driver. My initial instinct is to target `drivers/usb/gadget/legacy/webcam.c`. That seems like the most logical place to start, given the function name and the context of USB gadget drivers. I'm expecting this function deals with obtaining a function instance, which, in a gadget context, probably relates to how the webcam driver exposes itself as a USB function. Let's see what we can find. I'll get to it.
|
| 125/2 |
2026/07/30 14:12 |
tool |
grepper |
0m
Args: map[Expression:usb_get_function_instance]
Results: map[Output:Documentation/usb/gadget_configfs.rst=369=The concepts described above translate to USB gadgets like this:
--
Documentation/usb/gadget_configfs.rst-395-5. Function's code is written in such a way that it uses
Documentation/usb/gadget_configfs.rst:396: usb_get_function_instance(), which, in turn, calls request_module. So,
Documentation/usb/gadget_configfs.rst-397- provided that modprobe works, modules for particular functions are loaded
--
drivers/usb/gadget/configfs.c=601=static struct config_group *function_make(
--
drivers/usb/gadget/configfs.c-625-
drivers/usb/gadget/configfs.c:626: fi = usb_get_function_instance(func_name);
drivers/usb/gadget/configfs.c-627- if (IS_ERR(fi))
--
drivers/usb/gadget/functions.c=12=static struct usb_function_instance *try_get_usb_function_instance(const char *name)
--
drivers/usb/gadget/functions.c-38-
drivers/usb/gadget/functions.c:39:struct usb_function_instance *usb_get_function_instance(const char *name)
drivers/usb/gadget/functions.c-40-{
--
drivers/usb/gadget/functions.c-54-}
drivers/usb/gadget/functions.c:55:EXPORT_SYMBOL_GPL(usb_get_function_instance);
drivers/usb/gadget/functions.c-56-
--
drivers/usb/gadget/legacy/acm_ms.c=152=static int acm_ms_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/acm_ms.c-158-
drivers/usb/gadget/legacy/acm_ms.c:159: f_acm_inst = usb_get_function_instance("acm");
drivers/usb/gadget/legacy/acm_ms.c-160- if (IS_ERR(f_acm_inst))
--
drivers/usb/gadget/legacy/acm_ms.c-162-
drivers/usb/gadget/legacy/acm_ms.c:163: fi_msg = usb_get_function_instance("mass_storage");
drivers/usb/gadget/legacy/acm_ms.c-164- if (IS_ERR(fi_msg)) {
--
drivers/usb/gadget/legacy/audio.c=253=static int audio_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/audio.c-268-#ifndef CONFIG_GADGET_UAC1
drivers/usb/gadget/legacy/audio.c:269: fi_uac2 = usb_get_function_instance("uac2");
drivers/usb/gadget/legacy/audio.c-270- if (IS_ERR(fi_uac2))
--
drivers/usb/gadget/legacy/audio.c-273-#ifndef CONFIG_GADGET_UAC1_LEGACY
drivers/usb/gadget/legacy/audio.c:274: fi_uac1 = usb_get_function_instance("uac1");
drivers/usb/gadget/legacy/audio.c-275-#else
drivers/usb/gadget/legacy/audio.c:276: fi_uac1 = usb_get_function_instance("uac1_legacy");
drivers/usb/gadget/legacy/audio.c-277-#endif
--
drivers/usb/gadget/legacy/cdc2.c=138=static int cdc_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/cdc2.c-149-
drivers/usb/gadget/legacy/cdc2.c:150: fi_ecm = usb_get_function_instance("ecm");
drivers/usb/gadget/legacy/cdc2.c-151- if (IS_ERR(fi_ecm))
--
drivers/usb/gadget/legacy/cdc2.c-161-
drivers/usb/gadget/legacy/cdc2.c:162: fi_serial = usb_get_function_instance("acm");
drivers/usb/gadget/legacy/cdc2.c-163- if (IS_ERR(fi_serial)) {
--
drivers/usb/gadget/legacy/ether.c=304=static int eth_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/ether.c-315- /* EEM */
drivers/usb/gadget/legacy/ether.c:316: fi_eem = usb_get_function_instance("eem");
drivers/usb/gadget/legacy/ether.c-317- if (IS_ERR(fi_eem))
--
drivers/usb/gadget/legacy/ether.c-329-
drivers/usb/gadget/legacy/ether.c:330: fi_ecm = usb_get_function_instance("ecm");
drivers/usb/gadget/legacy/ether.c-331- if (IS_ERR(fi_ecm))
--
drivers/usb/gadget/legacy/ether.c-341-
drivers/usb/gadget/legacy/ether.c:342: fi_geth = usb_get_function_instance("geth");
drivers/usb/gadget/legacy/ether.c-343- if (IS_ERR(fi_geth))
--
drivers/usb/gadget/legacy/ether.c-378-
drivers/usb/gadget/legacy/ether.c:379: fi_rndis = usb_get_function_instance("rndis");
drivers/usb/gadget/legacy/ether.c-380- if (IS_ERR(fi_rndis)) {
--
drivers/usb/gadget/legacy/g_ffs.c=177=static int __init gfs_init(void)
--
drivers/usb/gadget/legacy/g_ffs.c-205- for (i = 0; i < func_num; i++) {
drivers/usb/gadget/legacy/g_ffs.c:206: fi_ffs[i] = usb_get_function_instance("ffs");
drivers/usb/gadget/legacy/g_ffs.c-207- if (IS_ERR(fi_ffs[i])) {
--
drivers/usb/gadget/legacy/g_ffs.c=308=static int gfs_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/g_ffs.c-320-
drivers/usb/gadget/legacy/g_ffs.c:321: fi_ecm = usb_get_function_instance("ecm");
drivers/usb/gadget/legacy/g_ffs.c-322- if (IS_ERR(fi_ecm))
--
drivers/usb/gadget/legacy/g_ffs.c-328-
drivers/usb/gadget/legacy/g_ffs.c:329: fi_geth = usb_get_function_instance("geth");
drivers/usb/gadget/legacy/g_ffs.c-330- if (IS_ERR(fi_geth))
--
drivers/usb/gadget/legacy/g_ffs.c-339- {
drivers/usb/gadget/legacy/g_ffs.c:340: fi_rndis = usb_get_function_instance("rndis");
drivers/usb/gadget/legacy/g_ffs.c-341- if (IS_ERR(fi_rndis)) {
--
drivers/usb/gadget/legacy/gmidi.c=142=static int midi_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/gmidi.c-146-
drivers/usb/gadget/legacy/gmidi.c:147: fi_midi = usb_get_function_instance("midi");
drivers/usb/gadget/legacy/gmidi.c-148- if (IS_ERR(fi_midi))
--
drivers/usb/gadget/legacy/hid.c=133=static int hid_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/hid.c-144- list_for_each_entry(iter_n, &hidg_func_list, node) {
drivers/usb/gadget/legacy/hid.c:145: iter_n->fi = usb_get_function_instance("hid");
drivers/usb/gadget/legacy/hid.c-146- if (IS_ERR(iter_n->fi)) {
--
drivers/usb/gadget/legacy/mass_storage.c=139=static int msg_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/mass_storage.c-144-
drivers/usb/gadget/legacy/mass_storage.c:145: fi_msg = usb_get_function_instance("mass_storage");
drivers/usb/gadget/legacy/mass_storage.c-146- if (IS_ERR(fi_msg))
--
drivers/usb/gadget/legacy/multi.c=294=static int multi_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/multi.c-313-#ifdef CONFIG_USB_G_MULTI_CDC
drivers/usb/gadget/legacy/multi.c:314: fi_ecm = usb_get_function_instance("ecm");
drivers/usb/gadget/legacy/multi.c-315- if (IS_ERR(fi_ecm))
--
drivers/usb/gadget/legacy/multi.c-327-#ifdef USB_ETH_RNDIS
drivers/usb/gadget/legacy/multi.c:328: fi_rndis = usb_get_function_instance("rndis");
drivers/usb/gadget/legacy/multi.c-329- if (IS_ERR(fi_rndis)) {
--
drivers/usb/gadget/legacy/multi.c-359- /* set up serial link layer */
drivers/usb/gadget/legacy/multi.c:360: fi_acm = usb_get_function_instance("acm");
drivers/usb/gadget/legacy/multi.c-361- if (IS_ERR(fi_acm)) {
--
drivers/usb/gadget/legacy/multi.c-366- /* set up mass storage function */
drivers/usb/gadget/legacy/multi.c:367: fi_msg = usb_get_function_instance("mass_storage");
drivers/usb/gadget/legacy/multi.c-368- if (IS_ERR(fi_msg)) {
--
drivers/usb/gadget/legacy/ncm.c=127=static int gncm_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/ncm.c-132-
drivers/usb/gadget/legacy/ncm.c:133: f_ncm_inst = usb_get_function_instance("ncm");
drivers/usb/gadget/legacy/ncm.c-134- if (IS_ERR(f_ncm_inst))
--
drivers/usb/gadget/legacy/nokia.c=267=static int nokia_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/nokia.c-287-
drivers/usb/gadget/legacy/nokia.c:288: fi_phonet = usb_get_function_instance("phonet");
drivers/usb/gadget/legacy/nokia.c-289- if (IS_ERR(fi_phonet))
--
drivers/usb/gadget/legacy/nokia.c-291-
drivers/usb/gadget/legacy/nokia.c:292: fi_obex1 = usb_get_function_instance("obex");
drivers/usb/gadget/legacy/nokia.c-293- if (IS_ERR(fi_obex1))
--
drivers/usb/gadget/legacy/nokia.c-295-
drivers/usb/gadget/legacy/nokia.c:296: fi_obex2 = usb_get_function_instance("obex");
drivers/usb/gadget/legacy/nokia.c-297- if (IS_ERR(fi_obex2))
--
drivers/usb/gadget/legacy/nokia.c-299-
drivers/usb/gadget/legacy/nokia.c:300: fi_acm = usb_get_function_instance("acm");
drivers/usb/gadget/legacy/nokia.c-301- if (IS_ERR(fi_acm)) {
--
drivers/usb/gadget/legacy/nokia.c-305-
drivers/usb/gadget/legacy/nokia.c:306: fi_ecm = usb_get_function_instance("ecm");
drivers/usb/gadget/legacy/nokia.c-307- if (IS_ERR(fi_ecm)) {
--
drivers/usb/gadget/legacy/nokia.c-311-
drivers/usb/gadget/legacy/nokia.c:312: fi_msg = usb_get_function_instance("mass_storage");
drivers/usb/gadget/legacy/nokia.c-313- if (IS_ERR(fi_msg)) {
--
drivers/usb/gadget/legacy/printer.c=139=static int printer_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/printer.c-143-
drivers/usb/gadget/legacy/printer.c:144: fi_printer = usb_get_function_instance("printer");
drivers/usb/gadget/legacy/printer.c-145- if (IS_ERR(fi_printer))
--
drivers/usb/gadget/legacy/serial.c=143=static int serial_register_ports(struct usb_composite_dev *cdev,
--
drivers/usb/gadget/legacy/serial.c-154-
drivers/usb/gadget/legacy/serial.c:155: fi_serial[i] = usb_get_function_instance(f_name);
drivers/usb/gadget/legacy/serial.c-156- if (IS_ERR(fi_serial[i])) {
--
drivers/usb/gadget/legacy/tcm_usb_gadget.c=140=static int __init usb_target_gadget_init(void)
--
drivers/usb/gadget/legacy/tcm_usb_gadget.c-143-
drivers/usb/gadget/legacy/tcm_usb_gadget.c:144: fi_tcm = usb_get_function_instance("tcm");
drivers/usb/gadget/legacy/tcm_usb_gadget.c-145- if (IS_ERR(fi_tcm))
--
drivers/usb/gadget/legacy/webcam.c=525=webcam_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/webcam.c-529-
drivers/usb/gadget/legacy/webcam.c:530: fi_uvc = usb_get_function_instance("uvc");
drivers/usb/gadget/legacy/webcam.c-531- if (IS_ERR(fi_uvc))
--
drivers/usb/gadget/legacy/zero.c=274=static int zero_bind(struct usb_composite_dev *cdev)
--
drivers/usb/gadget/legacy/zero.c-293-
drivers/usb/gadget/legacy/zero.c:294: func_inst_ss = usb_get_function_instance("SourceSink");
drivers/usb/gadget/legacy/zero.c-295- if (IS_ERR(func_inst_ss))
--
drivers/usb/gadget/legacy/zero.c-313-
drivers/usb/gadget/legacy/zero.c:314: func_inst_lb = usb_get_function_instance("Loopback");
drivers/usb/gadget/legacy/zero.c-315- if (IS_ERR(func_inst_lb)) {
--
include/linux/usb/composite.h=591=void usb_put_function(struct usb_function *f);
include/linux/usb/composite.h:592:struct usb_function_instance *usb_get_function_instance(const char *name);
include/linux/usb/composite.h-593-struct usb_function *usb_get_function(struct usb_function_instance *fi);
]
|
| 126/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 78262
output: 22
thoughts: 2500
Thoughts: **Analysis of Default Group Management in USB Gadget Functions**
Okay, so I've been digging into the USB gadget function (`g_webcam.c` in particular) and I've identified a potential resource leak in how default groups are handled, specifically in the `uvc` module and, importantly, how this issue applies to `f_midi2`.
My initial thought process was: I saw that `g_webcam.c` uses `usb_get_function_instance("uvc")`. If a later `webcam_bind` fails, `usb_put_function_instance(fi_uvc)` is called which, in turn, calls `uvc_free_inst`. `uvc_free_inst` frees `opts`, but I noticed it *doesn't* free the default groups. I immediately checked the code again, and, confirmed my initial read. `uvc_free_inst` does NOT drop the refcount on those default groups. This is a potential leak.
The key observation is that if `uvc_free_inst` doesn't clean up the default groups, and `usb_put_function_instance` is called directly, those groups are leaked. However, `uvc_func_item_release` *should* call `uvcg_config_remove_children`, which would drop the refcount. *But*, if `usb_put_function_instance` is called directly, that release function is *never* called, because the configfs item was never exposed to userspace or its refcount was never managed by configfs. The initial refcount of the config group, which is initialized by `config_group_init_type_name` to 1, remains untouched! Thus, the default groups are leaked.
This is a known issue.
Now, let's turn to `f_midi2`. Can we fix the leak in `f_midi2` by calling `configfs_remove_default_groups` in `f_midi2_free_inst`? If `f_midi2_opts_release` also calls `configfs_remove_default_groups`, what happens? If it is safe to call the function in both release paths, then the code could be made more resilient to use. If `f_midi2_opts_release` calls it, the default group list is emptied, so calling it in `f_midi2_free_inst` is a no-op, meaning it is safe.
Let's walk through the logic. If we *only* call `configfs_remove_default_groups` in `f_midi2_free_inst` and not `f_midi2_opts_release`, what happens? When `opts->func_inst.group` is released, `f_midi2_opts_release` is called, which in turn calls `usb_put_function_instance`, which then calls `f_midi2_free_inst`. In `f_midi2_free_inst`, we'll call `configfs_remove_default_groups(&opts->func_inst.group)`. This will drop the refcount of `ep_opts->group`. Next, `f_midi2_ep_opts_release` is called. If that function calls `configfs_remove_default_groups(to_config_group(item))`, the refcount of `block_opts->group` is dropped. Then, `f_midi2_block_opts_release` is called (which frees `block_opts`), then `f_midi2_ep_opts_release` (which frees `ep_opts`), then `f_midi2_free_inst` (which frees `opts`). This all works perfectly!
I looked at the failure paths. What if `f_midi2_alloc_inst` fails after `f_midi2_block_opts_create`? It fails by freeing `ep_opts` and `opts`. Since `config_group_init_type_name` hasn't been called on `ep_opts->group` or `opts->func_inst.group`, those pointers are just raw memory, and `kfree` is the correct action. If `f_midi2_ep_opts_create` also never calls `config_group_init_type_name`, we are fine.
What if `f_midi2_alloc_inst` succeeds, but `usb_put_function_instance` is called directly? `f_midi2_free_inst` is called. It calls `configfs_remove_default_groups(&opts->func_inst.group)`. `config_group_init_type_name` initializes `default_groups` list, and `configfs_add_
|
| 127/2 |
2026/07/30 14:12 |
tool |
grepper |
0m
Args: map[Expression:configfs_remove_default_groups]
Results: map[Output:drivers/infiniband/core/cma_configfs.c=318=static void drop_cma_dev(struct config_group *cgroup, struct config_item *item)
--
drivers/infiniband/core/cma_configfs.c-324-
drivers/infiniband/core/cma_configfs.c:325: configfs_remove_default_groups(&cma_dev_group->ports_group);
drivers/infiniband/core/cma_configfs.c:326: configfs_remove_default_groups(&cma_dev_group->device_group);
drivers/infiniband/core/cma_configfs.c-327- config_item_put(item);
--
drivers/target/target_core_configfs.c=267=static void target_core_deregister_fabric(
--
drivers/target/target_core_configfs.c-283-
drivers/target/target_core_configfs.c:284: configfs_remove_default_groups(&tf->tf_group);
drivers/target/target_core_configfs.c-285- config_item_put(item);
--
drivers/target/target_core_configfs.c=3502=static void target_core_drop_subdev(
--
drivers/target/target_core_configfs.c-3514-
drivers/target/target_core_configfs.c:3515: configfs_remove_default_groups(&dev->dev_stat_grps.stat_group);
drivers/target/target_core_configfs.c:3516: configfs_remove_default_groups(&dev->t10_alua.alua_tg_pt_gps_group);
drivers/target/target_core_configfs.c-3517-
--
drivers/target/target_core_configfs.c-3523-
drivers/target/target_core_configfs.c:3524: configfs_remove_default_groups(dev_cg);
drivers/target/target_core_configfs.c-3525-
--
drivers/target/target_core_configfs.c=3843=static void __exit target_core_exit_configfs(void)
drivers/target/target_core_configfs.c-3844-{
drivers/target/target_core_configfs.c:3845: configfs_remove_default_groups(&alua_lu_gps_group);
drivers/target/target_core_configfs.c:3846: configfs_remove_default_groups(&alua_group);
drivers/target/target_core_configfs.c:3847: configfs_remove_default_groups(&target_core_hbagroup);
drivers/target/target_core_configfs.c-3848-
--
drivers/target/target_core_fabric_configfs.c=326=static void target_fabric_drop_mappedlun(
--
drivers/target/target_core_fabric_configfs.c-332-
drivers/target/target_core_fabric_configfs.c:333: configfs_remove_default_groups(&lacl->ml_stat_grps.stat_group);
drivers/target/target_core_fabric_configfs.c:334: configfs_remove_default_groups(&lacl->se_lun_group);
drivers/target/target_core_fabric_configfs.c-335-
--
drivers/target/target_core_fabric_configfs.c=339=static void target_fabric_nacl_base_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-343-
drivers/target/target_core_fabric_configfs.c:344: configfs_remove_default_groups(&se_nacl->acl_fabric_stat_group);
drivers/target/target_core_fabric_configfs.c-345- core_tpg_del_initiator_node_acl(se_nacl);
--
drivers/target/target_core_fabric_configfs.c=373=static struct config_group *target_fabric_make_nodeacl(
--
drivers/target/target_core_fabric_configfs.c-411- if (ret) {
drivers/target/target_core_fabric_configfs.c:412: configfs_remove_default_groups(&se_nacl->acl_fabric_stat_group);
drivers/target/target_core_fabric_configfs.c-413- core_tpg_del_initiator_node_acl(se_nacl);
--
drivers/target/target_core_fabric_configfs.c=421=static void target_fabric_drop_nodeacl(
--
drivers/target/target_core_fabric_configfs.c-427-
drivers/target/target_core_fabric_configfs.c:428: configfs_remove_default_groups(&se_nacl->acl_group);
drivers/target/target_core_fabric_configfs.c-429-
--
drivers/target/target_core_fabric_configfs.c=777=static void target_fabric_drop_lun(
--
drivers/target/target_core_fabric_configfs.c-783-
drivers/target/target_core_fabric_configfs.c:784: configfs_remove_default_groups(&lun->port_stat_grps.stat_group);
drivers/target/target_core_fabric_configfs.c:785: configfs_remove_default_groups(&lun->lun_group);
drivers/target/target_core_fabric_configfs.c-786-
--
drivers/target/target_core_fabric_configfs.c=979=static void target_fabric_drop_tpg(
--
drivers/target/target_core_fabric_configfs.c-985-
drivers/target/target_core_fabric_configfs.c:986: configfs_remove_default_groups(&se_tpg->tpg_group);
drivers/target/target_core_fabric_configfs.c-987- config_item_put(item);
--
drivers/target/target_core_fabric_configfs.c=990=static void target_fabric_release_wwn(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-995-
drivers/target/target_core_fabric_configfs.c:996: configfs_remove_default_groups(&wwn->fabric_stat_group);
drivers/target/target_core_fabric_configfs.c:997: configfs_remove_default_groups(&wwn->param_group);
drivers/target/target_core_fabric_configfs.c-998- tf->tf_ops->fabric_drop_wwn(wwn);
--
drivers/target/target_core_fabric_configfs.c=1160=static void target_fabric_drop_wwn(
--
drivers/target/target_core_fabric_configfs.c-1166-
drivers/target/target_core_fabric_configfs.c:1167: configfs_remove_default_groups(&wwn->wwn_group);
drivers/target/target_core_fabric_configfs.c-1168- config_item_put(item);
--
fs/configfs/dir.c=734=static int populate_groups(struct config_group *group,
--
fs/configfs/dir.c-747-
fs/configfs/dir.c:748:void configfs_remove_default_groups(struct config_group *group)
fs/configfs/dir.c-749-{
--
fs/configfs/dir.c-756-}
fs/configfs/dir.c:757:EXPORT_SYMBOL(configfs_remove_default_groups);
fs/configfs/dir.c-758-
--
fs/dlm/config.c=457=static void drop_cluster(struct config_group *g, struct config_item *i)
--
fs/dlm/config.c-460-
fs/dlm/config.c:461: configfs_remove_default_groups(&cl->group);
fs/dlm/config.c-462-
--
fs/dlm/config.c=507=static void drop_space(struct config_group *g, struct config_item *i)
--
fs/dlm/config.c-512-
fs/dlm/config.c:513: configfs_remove_default_groups(&sp->group);
fs/dlm/config.c-514- config_item_put(i);
--
fs/ocfs2/cluster/nodemanager.c=733=static void o2nm_cluster_group_drop_item(struct config_group *group, struct config_item *item)
--
fs/ocfs2/cluster/nodemanager.c-739-
fs/ocfs2/cluster/nodemanager.c:740: configfs_remove_default_groups(&cluster->cl_group);
fs/ocfs2/cluster/nodemanager.c-741- config_item_put(item);
--
include/linux/configfs.h=245=void configfs_unregister_group(struct config_group *group);
include/linux/configfs.h-246-
include/linux/configfs.h:247:void configfs_remove_default_groups(struct config_group *group);
include/linux/configfs.h-248-
]
|
| 128/2 |
2026/07/30 14:12 |
llm |
debugger |
0m
Model:
Tokens:
input: 84251
output: 744
thoughts: 2285
Thoughts: **Analysis of a ConfigFS Memory Leak in f_midi2**
Okay, so I've been digging into this memory leak reported in the `f_midi2` gadget driver, and I think I've got a handle on it. First, I confirmed that the issue is indeed rooted in how `configfs` handles default groups. I know from experience that other modules, like those in `drivers/infiniband/core`, `drivers/target`, `fs/dlm`, and `fs/ocfs2`, also use `configfs` for this purpose and call `configfs_remove_default_groups` to clean things up. This gives me a good point of reference.
The leak stems from `f_midi2_alloc_inst`, which creates and adds `ep.0` and `block.0` configfs groups as default groups under the function instance. These groups, and the associated data structures (`f_midi2_ep_opts` and `f_midi2_block_opts`) are then never properly released when the configfs directory is removed or the function instance is released. The hex dump from the crash report confirms that the leaked objects are the `ep_opts` and `block_opts` structures. The names `ep.0` and `block.0` are the configfs group names stored in `ci_namebuf`, so those names being present helps me confirm this is indeed the correct area to examine.
I realized the critical oversight is that `configfs` *doesn't* automatically drop the refcounts of these default groups when the parent is removed. We need to explicitly handle it within the driver. So, the fix should involve calling `configfs_remove_default_groups` at the appropriate points. Specifically:
* In `f_midi2_free_inst`, before freeing the `f_midi2_opts` structure, I need to add `configfs_remove_default_groups(&opts->func_inst.group)`. This should trigger the release of `ep.0` and `block.0`.
* And in `f_midi2_ep_opts_release`, I need to add `configfs_remove_default_groups(to_config_group(item))` before `kfree(opts)`.
I quickly double-checked the flow. Since `f_midi2_opts_release` calls `usb_put_function_instance`, which then calls `f_midi2_free_inst`, I only need to add the call to `configfs_remove_default_groups` in `f_midi2_free_inst`.
I reviewed the code, and I'm confident that this covers all the default groups added in `f_midi2_alloc_inst`. There are no others. I also made sure dynamically allocated strings like `info.iface_name`, `info.ep_name`, `info.product_id` and the `info.name` are freed in the appropriate release functions. So, no other leaks there.
I've also looked at `f_uvc.c` as a comparison, as it's been the subject of a similar discussion, but `f_uvc.c` does call `uvcg_config_remove_children` in `uvc_func_item_release` after all. And `f_midi2` doesn't even have an equivalent call to handle cases of direct function instance release!
Therefore, I'm confident that these additions will resolve the memory leak by ensuring the refcounts of the default groups are properly dropped, triggering their release functions and freeing the associated structures.
|
| 129/1 |
2026/07/30 14:12 |
agent |
history-explorer |
28m
Model:
Instruction:
You are an experienced Linux kernel developer researching prior art for fixing a kernel bug.
You are given a bug explanation. This explanation details the root cause of the bug resulting
from debugging, but does not provide the final fix strategy. Your goal is to explore how
similar bugs were fixed in the past in the same subsystem or files.
CRITICAL: Do NOT attempt to debug the issue further or write a patch for it yourself.
Your ONLY objective is to research and provide the necessary historical context.
Use the git-log tool with the Since parameter set to "3 years" to focus on recent history.
Search for commits that address issues with similar root causes (e.g. similar missing locks,
incorrect refcounting, or similar error path bugs) in the affected files.
Your final reply must summarize your findings: what idioms, locking rules, or common patterns
should be followed when writing a fix for this bug based on how previous similar bugs were addressed.
If you find no relevant past fixes, clearly state that.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
2026/07/30 13:30:35 executed programs: 5
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504 [inline]
f_midi2_alloc_inst+0x80/0x2c0 drivers/usb/gadget/function/f_midi2.c:2666
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342 [inline]
f_midi2_alloc_inst+0x16a/0x2c0 drivers/usb/gadget/function/f_midi2.c:2673
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF
The explanation of the root cause of the bug is:
The root cause of this memory leak is that the driver fails to drop the reference counts of its default configfs groups when the parent group is destroyed.
When a USB function instance for `midi2` is allocated via `f_midi2_alloc_inst()`, it dynamically allocates `ep_opts` and `block_opts` structures using `kzalloc_obj()`. These structures are then initialized as configfs groups and added as default groups to their respective parents:
- `ep.0` is added as a default group to the function instance group (`opts->func_inst.group`).
- `block.0` is added as a default group to the endpoint group (`ep_opts->group`).
When the user removes the configfs directory for the function instance (via `rmdir`), configfs removes the dentries for the default groups but **does not** automatically drop their reference counts. The responsibility of dropping the refcounts of default groups lies with the driver, typically by calling `configfs_remove_default_groups()` when the parent group is released.
However, `f_midi2_free_inst()` (which is called when the function instance's refcount reaches 0) only frees the `f_midi2_opts` structure and does not clean up its default groups. Similarly, `f_midi2_ep_opts_release()` does not clean up its default groups. As a result, the `ep_opts` and `block_opts` structures are leaked.
### How to fix it
To fix the leak, you need to explicitly remove the default groups and drop their reference counts before freeing the parent structures. This can be done by calling `configfs_remove_default_groups()` in the respective release functions.
1. In `f_midi2_free_inst()`, drop the default groups of the function instance (this will drop the refcount of `ep.0` and trigger `f_midi2_ep_opts_release`):
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group); /* ADD THIS */
kfree(opts->info.iface_name);
kfree(opts);
}
```
2. In `f_midi2_ep_opts_release()`, drop the default groups of the endpoint (this will drop the refcount of `block.0` and trigger `f_midi2_block_opts_release`):
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item)); /* ADD THIS */
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
With these changes, the hierarchy of default groups will be properly unrolled and freed when the function instance is destroyed, whether it's through configfs `rmdir` or an error path that directly calls `usb_put_function_instance()`.
Reply: Based on the historical research of similar bugs in the kernel, here is a summary of the findings and the recommended patterns for fixing this issue.
### Findings
The root cause of the bug is that dynamically allocated configfs default groups are not automatically cleaned up when their parent is destroyed. While configfs removes the dentries, it does not automatically drop the reference counts of the default groups. If the driver does not explicitly drop these references, the configfs items (and their dynamically allocated memory) will leak.
In the past, similar issues in the USB gadget subsystem and other configfs users were addressed using two main patterns:
1. **Manually dropping references recursively:**
In commit `efbf0af70b4f` ("usb: gadget: uvc: configfs: Allocate groups dynamically"), the UVC gadget driver was refactored to dynamically allocate its configfs groups. To prevent memory leaks, the developers introduced a custom `uvcg_config_remove_children()` function. This function manually iterates over the `default_groups` list of a parent, removes each child from the list, recursively cleans up the child's children, and finally calls `config_item_put()` to drop the reference count and trigger the release function.
2. **Manually freeing the child's memory in the parent's release function:**
In commit `ff74745e6d3d` ("usb: gadget: configfs: Fix memory leak of interface directory data"), a memory leak of the `os_desc` default group in the RNDIS function was fixed. Because the `os_desc` group did not have a dedicated configfs `release` callback, the developers fixed the leak by saving a pointer to the dynamically allocated group and explicitly calling `kfree()` on it inside the parent's release function (`rndis_free_inst`).
A similar fix was applied outside the USB subsystem in commit `3d2825c8c610` ("fs: dlm: fix configfs memory leak"), where a default group's memory was manually freed in the parent's release function because `configfs_remove_default_groups()` alone wouldn't free the memory without a release callback.
### Recommended Fix Strategy
For the `f_midi2` bug, the proposed fix of calling `configfs_remove_default_groups()` is the most idiomatic and robust approach.
Unlike the RNDIS or DLM bugs mentioned above, the `ep.0` and `block.0` groups in `f_midi2` **already have** proper configfs release callbacks implemented (`f_midi2_ep_opts_release` and `f_midi2_block_opts_release` respectively), which correctly call `kfree()`.
Therefore, you do not need to manually `kfree()` the child structures or write a custom recursive cleanup function like UVC did. You should follow the pattern of explicitly unrolling the default groups by calling `configfs_remove_default_groups()` in the respective parent's release functions:
- Call it in `f_midi2_free_inst()` to drop the refcount of `ep.0`.
- Call it in `f_midi2_ep_opts_release()` to drop the refcount of `block.0`.
This will correctly drop the reference counts, trigger the existing release callbacks, and safely free the dynamically allocated memory in the correct order.
|
| 130/2 |
2026/07/30 14:12 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 6080
output: 37
thoughts: 204
Thoughts: Okay, let's see what we've got here.
**Analyzing a Memory Leak Fix**
Right, so we're dealing with a memory leak in `drivers/usb/gadget/function/f_midi2.c`. The root cause is pretty clear: missing calls to `configfs_remove_default_groups()` within the release functions of configfs items or groups. Specifically, we need to add that call in `f_midi2_free_inst()` and `f_midi2_ep_opts_release()`. Seems straightforward enough.
Now, before I just blindly implement the fix, I want to make sure we're addressing the *actual* problem and not just a symptom. Let's see if we can find similar issues that have been addressed in the past. We need to identify any prior fixes for similar `configfs` memory leaks, especially within related subsystems or files. I'll focus our search on `drivers/usb/gadget/`, `drivers/usb/`, or the kernel more generally for any occurrences of missing calls in similar functions.
Okay, let's use `git log`. Given the potential for this kind of bug to have existed for a while, I'll go back at least three years with `Since="3 years"`. I'll focus on diffs containing `configfs_remove_default_groups`. I'm pretty sure that should give us what we need to get a clear perspective on this. Let's see what we find.
|
| 131/2 |
2026/07/30 14:12 |
tool |
git-log |
4m
Args: map[CodeRegexp:configfs_remove_default_groups Count:20 Since:3 years]
Results:
|
| 132/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 6652
output: 22
thoughts: 54
Thoughts: Okay, here's my thought process on that query:
**Digging into the `configfs_remove_default_groups` Query**
Hmm, no hits for `configfs_remove_default_groups` in the last three years? That's... interesting. It's either an indication that this particular function isn't frequently used, or, more likely, my search parameters (specifically, the regex I employed) are off. Before I start going down a rabbit hole, the logical first step is to definitively determine if the function *even exists* within the codebase. It could be that it's been deprecated, renamed, or isn't part of the active project I'm focusing on. So, let me just double-check that the function itself is actually defined before I start trying to determine when it was used.
|
| 133/2 |
2026/07/30 14:17 |
tool |
grepper |
0m
Args: map[Expression:configfs_remove_default_groups]
Results: map[Output:drivers/infiniband/core/cma_configfs.c=318=static void drop_cma_dev(struct config_group *cgroup, struct config_item *item)
--
drivers/infiniband/core/cma_configfs.c-324-
drivers/infiniband/core/cma_configfs.c:325: configfs_remove_default_groups(&cma_dev_group->ports_group);
drivers/infiniband/core/cma_configfs.c:326: configfs_remove_default_groups(&cma_dev_group->device_group);
drivers/infiniband/core/cma_configfs.c-327- config_item_put(item);
--
drivers/target/target_core_configfs.c=267=static void target_core_deregister_fabric(
--
drivers/target/target_core_configfs.c-283-
drivers/target/target_core_configfs.c:284: configfs_remove_default_groups(&tf->tf_group);
drivers/target/target_core_configfs.c-285- config_item_put(item);
--
drivers/target/target_core_configfs.c=3502=static void target_core_drop_subdev(
--
drivers/target/target_core_configfs.c-3514-
drivers/target/target_core_configfs.c:3515: configfs_remove_default_groups(&dev->dev_stat_grps.stat_group);
drivers/target/target_core_configfs.c:3516: configfs_remove_default_groups(&dev->t10_alua.alua_tg_pt_gps_group);
drivers/target/target_core_configfs.c-3517-
--
drivers/target/target_core_configfs.c-3523-
drivers/target/target_core_configfs.c:3524: configfs_remove_default_groups(dev_cg);
drivers/target/target_core_configfs.c-3525-
--
drivers/target/target_core_configfs.c=3843=static void __exit target_core_exit_configfs(void)
drivers/target/target_core_configfs.c-3844-{
drivers/target/target_core_configfs.c:3845: configfs_remove_default_groups(&alua_lu_gps_group);
drivers/target/target_core_configfs.c:3846: configfs_remove_default_groups(&alua_group);
drivers/target/target_core_configfs.c:3847: configfs_remove_default_groups(&target_core_hbagroup);
drivers/target/target_core_configfs.c-3848-
--
drivers/target/target_core_fabric_configfs.c=326=static void target_fabric_drop_mappedlun(
--
drivers/target/target_core_fabric_configfs.c-332-
drivers/target/target_core_fabric_configfs.c:333: configfs_remove_default_groups(&lacl->ml_stat_grps.stat_group);
drivers/target/target_core_fabric_configfs.c:334: configfs_remove_default_groups(&lacl->se_lun_group);
drivers/target/target_core_fabric_configfs.c-335-
--
drivers/target/target_core_fabric_configfs.c=339=static void target_fabric_nacl_base_release(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-343-
drivers/target/target_core_fabric_configfs.c:344: configfs_remove_default_groups(&se_nacl->acl_fabric_stat_group);
drivers/target/target_core_fabric_configfs.c-345- core_tpg_del_initiator_node_acl(se_nacl);
--
drivers/target/target_core_fabric_configfs.c=373=static struct config_group *target_fabric_make_nodeacl(
--
drivers/target/target_core_fabric_configfs.c-411- if (ret) {
drivers/target/target_core_fabric_configfs.c:412: configfs_remove_default_groups(&se_nacl->acl_fabric_stat_group);
drivers/target/target_core_fabric_configfs.c-413- core_tpg_del_initiator_node_acl(se_nacl);
--
drivers/target/target_core_fabric_configfs.c=421=static void target_fabric_drop_nodeacl(
--
drivers/target/target_core_fabric_configfs.c-427-
drivers/target/target_core_fabric_configfs.c:428: configfs_remove_default_groups(&se_nacl->acl_group);
drivers/target/target_core_fabric_configfs.c-429-
--
drivers/target/target_core_fabric_configfs.c=777=static void target_fabric_drop_lun(
--
drivers/target/target_core_fabric_configfs.c-783-
drivers/target/target_core_fabric_configfs.c:784: configfs_remove_default_groups(&lun->port_stat_grps.stat_group);
drivers/target/target_core_fabric_configfs.c:785: configfs_remove_default_groups(&lun->lun_group);
drivers/target/target_core_fabric_configfs.c-786-
--
drivers/target/target_core_fabric_configfs.c=979=static void target_fabric_drop_tpg(
--
drivers/target/target_core_fabric_configfs.c-985-
drivers/target/target_core_fabric_configfs.c:986: configfs_remove_default_groups(&se_tpg->tpg_group);
drivers/target/target_core_fabric_configfs.c-987- config_item_put(item);
--
drivers/target/target_core_fabric_configfs.c=990=static void target_fabric_release_wwn(struct config_item *item)
--
drivers/target/target_core_fabric_configfs.c-995-
drivers/target/target_core_fabric_configfs.c:996: configfs_remove_default_groups(&wwn->fabric_stat_group);
drivers/target/target_core_fabric_configfs.c:997: configfs_remove_default_groups(&wwn->param_group);
drivers/target/target_core_fabric_configfs.c-998- tf->tf_ops->fabric_drop_wwn(wwn);
--
drivers/target/target_core_fabric_configfs.c=1160=static void target_fabric_drop_wwn(
--
drivers/target/target_core_fabric_configfs.c-1166-
drivers/target/target_core_fabric_configfs.c:1167: configfs_remove_default_groups(&wwn->wwn_group);
drivers/target/target_core_fabric_configfs.c-1168- config_item_put(item);
--
fs/configfs/dir.c=734=static int populate_groups(struct config_group *group,
--
fs/configfs/dir.c-747-
fs/configfs/dir.c:748:void configfs_remove_default_groups(struct config_group *group)
fs/configfs/dir.c-749-{
--
fs/configfs/dir.c-756-}
fs/configfs/dir.c:757:EXPORT_SYMBOL(configfs_remove_default_groups);
fs/configfs/dir.c-758-
--
fs/dlm/config.c=457=static void drop_cluster(struct config_group *g, struct config_item *i)
--
fs/dlm/config.c-460-
fs/dlm/config.c:461: configfs_remove_default_groups(&cl->group);
fs/dlm/config.c-462-
--
fs/dlm/config.c=507=static void drop_space(struct config_group *g, struct config_item *i)
--
fs/dlm/config.c-512-
fs/dlm/config.c:513: configfs_remove_default_groups(&sp->group);
fs/dlm/config.c-514- config_item_put(i);
--
fs/ocfs2/cluster/nodemanager.c=733=static void o2nm_cluster_group_drop_item(struct config_group *group, struct config_item *item)
--
fs/ocfs2/cluster/nodemanager.c-739-
fs/ocfs2/cluster/nodemanager.c:740: configfs_remove_default_groups(&cluster->cl_group);
fs/ocfs2/cluster/nodemanager.c-741- config_item_put(item);
--
include/linux/configfs.h=245=void configfs_unregister_group(struct config_group *group);
include/linux/configfs.h-246-
include/linux/configfs.h:247:void configfs_remove_default_groups(struct config_group *group);
include/linux/configfs.h-248-
]
|
| 134/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 9345
output: 22
thoughts: 136
Thoughts: **Investigating USB Gadget Driver Default Group Handling**
Alright, I'm diving into this `configfs_remove_default_groups` function. My initial scan shows its utilization within the `drivers/infiniband`, `drivers/target`, `fs/dlm`, and `fs/ocfs2` directories. That's a good starting point. But I immediately think, what about the USB gadget drivers? My mind instinctively goes there.
Let me just quickly search the `drivers/usb/gadget/` directory for that function name… Hmm, no results. Interesting. Now I'm really curious. If `configfs_remove_default_groups` isn't used, how are those USB gadget functions managing default groups? Are they handling it differently? Or are they perhaps relying on some other mechanism entirely?
Okay, let's back up and broaden the search. Let's see if other USB gadget functions even *use* default groups at all. I need to understand the fundamental approach they are taking to this aspect of the configuration. Let's explore.
|
| 135/2 |
2026/07/30 14:17 |
tool |
grepper |
0m
Args: map[Expression:configfs_add_default_group]
Results: map[Output:Documentation/filesystems/configfs.rst=449=A configfs subsystem specifies default groups by adding them using the
Documentation/filesystems/configfs.rst:450:configfs_add_default_group() function to the parent config_group
Documentation/filesystems/configfs.rst-451-structure. Each added group is populated in the configfs tree at the same
--
drivers/block/null_blk/main.c=657=static void nullb_add_fault_config(struct nullb_device *dev)
--
drivers/block/null_blk/main.c-662-
drivers/block/null_blk/main.c:663: configfs_add_default_group(&dev->timeout_config.group, &dev->group);
drivers/block/null_blk/main.c:664: configfs_add_default_group(&dev->requeue_config.group, &dev->group);
drivers/block/null_blk/main.c:665: configfs_add_default_group(&dev->init_hctx_fault_config.group, &dev->group);
drivers/block/null_blk/main.c-666-}
--
drivers/gpu/drm/vkms/vkms_configfs.c=358=static struct config_group *make_plane_group(struct config_group *group,
--
drivers/gpu/drm/vkms/vkms_configfs.c-388- &plane_possible_crtcs_group_type);
drivers/gpu/drm/vkms/vkms_configfs.c:389: configfs_add_default_group(&plane->possible_crtcs_group,
drivers/gpu/drm/vkms/vkms_configfs.c-390- &plane->group);
--
drivers/gpu/drm/vkms/vkms_configfs.c=474=static struct config_group *make_encoder_group(struct config_group *group,
--
drivers/gpu/drm/vkms/vkms_configfs.c-505- &encoder_possible_crtcs_group_type);
drivers/gpu/drm/vkms/vkms_configfs.c:506: configfs_add_default_group(&encoder->possible_crtcs_group,
drivers/gpu/drm/vkms/vkms_configfs.c-507- &encoder->group);
--
drivers/gpu/drm/vkms/vkms_configfs.c=641=static struct config_group *make_connector_group(struct config_group *group,
--
drivers/gpu/drm/vkms/vkms_configfs.c-672- &connector_possible_encoders_group_type);
drivers/gpu/drm/vkms/vkms_configfs.c:673: configfs_add_default_group(&connector->possible_encoders_group,
drivers/gpu/drm/vkms/vkms_configfs.c-674- &connector->group);
--
drivers/gpu/drm/vkms/vkms_configfs.c=763=static struct config_group *make_device_group(struct config_group *group,
--
drivers/gpu/drm/vkms/vkms_configfs.c-787- &plane_group_type);
drivers/gpu/drm/vkms/vkms_configfs.c:788: configfs_add_default_group(&dev->planes_group, &dev->group);
drivers/gpu/drm/vkms/vkms_configfs.c-789-
--
drivers/gpu/drm/vkms/vkms_configfs.c-791- &crtc_group_type);
drivers/gpu/drm/vkms/vkms_configfs.c:792: configfs_add_default_group(&dev->crtcs_group, &dev->group);
drivers/gpu/drm/vkms/vkms_configfs.c-793-
--
drivers/gpu/drm/vkms/vkms_configfs.c-795- &encoder_group_type);
drivers/gpu/drm/vkms/vkms_configfs.c:796: configfs_add_default_group(&dev->encoders_group, &dev->group);
drivers/gpu/drm/vkms/vkms_configfs.c-797-
--
drivers/gpu/drm/vkms/vkms_configfs.c-799- &connector_group_type);
drivers/gpu/drm/vkms/vkms_configfs.c:800: configfs_add_default_group(&dev->connectors_group, &dev->group);
drivers/gpu/drm/vkms/vkms_configfs.c-801-
--
drivers/gpu/drm/xe/xe_configfs.c=982=static struct config_group *xe_config_make_device_group(struct config_group *group,
--
drivers/gpu/drm/xe/xe_configfs.c-1048- config_group_init_type_name(&dev->sriov, "sriov", &xe_config_sriov_type);
drivers/gpu/drm/xe/xe_configfs.c:1049: configfs_add_default_group(&dev->sriov, &dev->group);
drivers/gpu/drm/xe/xe_configfs.c-1050- }
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c=178=static int cscfg_add_preset_groups(struct cscfg_fs_config *cfg_view)
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-199- &cscfg_config_preset_type);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c:200: configfs_add_default_group(&cfg_fs_preset->group, &cfg_view->group);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-201- }
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c=322=static int cscfg_create_params_group_items(struct cscfg_feature_desc *feat_desc,
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-338- &cscfg_param_view_type);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c:339: configfs_add_default_group(¶m_item->group, params_group);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-340- }
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c=344=static struct config_group *cscfg_create_feature_group(struct cscfg_feature_desc *feat_desc)
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-374- config_group_init_type_name(params_group, "params", params_group_type);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c:375: configfs_add_default_group(params_group, &feat_view->group);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-376- item_err = cscfg_create_params_group_items(feat_desc, params_group);
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c=451=int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr)
--
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-471- config_group_init(&cscfg_configs_grp);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c:472: configfs_add_default_group(&cscfg_configs_grp, &subsys->su_group);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-473-
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-474- config_group_init(&cscfg_features_grp);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c:475: configfs_add_default_group(&cscfg_features_grp, &subsys->su_group);
drivers/hwtracing/coresight/coresight-syscfg-configfs.c-476-
--
drivers/infiniband/core/cma_configfs.c=199=static int make_cma_ports(struct cma_dev_group *cma_dev_group,
--
drivers/infiniband/core/cma_configfs.c-226- &cma_port_group_type);
drivers/infiniband/core/cma_configfs.c:227: configfs_add_default_group(&ports[i].group,
drivers/infiniband/core/cma_configfs.c-228- &cma_dev_group->ports_group);
--
drivers/infiniband/core/cma_configfs.c=276=static struct config_group *make_cma_dev(struct config_group *group,
--
drivers/infiniband/core/cma_configfs.c-304- &cma_device_group_type);
drivers/infiniband/core/cma_configfs.c:305: configfs_add_default_group(&cma_dev_group->ports_group,
drivers/infiniband/core/cma_configfs.c-306- &cma_dev_group->device_group);
--
drivers/net/netconsole.c=1409=static void init_target_config_group(struct netconsole_target *nt,
--
drivers/net/netconsole.c-1414- &userdata_type);
drivers/net/netconsole.c:1415: configfs_add_default_group(&nt->userdata_group, &nt->group);
drivers/net/netconsole.c-1416-}
--
drivers/nvme/target/configfs.c=1038=static void nvmet_add_passthru_group(struct nvmet_subsys *subsys)
--
drivers/nvme/target/configfs.c-1041- "passthru", &nvmet_passthru_type);
drivers/nvme/target/configfs.c:1042: configfs_add_default_group(&subsys->passthru_group,
drivers/nvme/target/configfs.c-1043- &subsys->group);
--
drivers/nvme/target/configfs.c=1734=static struct config_group *nvmet_subsys_make(struct config_group *group,
--
drivers/nvme/target/configfs.c-1756- "namespaces", &nvmet_namespaces_type);
drivers/nvme/target/configfs.c:1757: configfs_add_default_group(&subsys->namespaces_group, &subsys->group);
drivers/nvme/target/configfs.c-1758-
--
drivers/nvme/target/configfs.c-1760- "allowed_hosts", &nvmet_allowed_hosts_type);
drivers/nvme/target/configfs.c:1761: configfs_add_default_group(&subsys->allowed_hosts_group,
drivers/nvme/target/configfs.c-1762- &subsys->group);
--
drivers/nvme/target/configfs.c=2039=static struct config_group *nvmet_ports_make(struct config_group *group,
--
drivers/nvme/target/configfs.c-2084- "subsystems", &nvmet_port_subsys_type);
drivers/nvme/target/configfs.c:2085: configfs_add_default_group(&port->subsys_group, &port->group);
drivers/nvme/target/configfs.c-2086-
--
drivers/nvme/target/configfs.c-2088- "referrals", &nvmet_referrals_type);
drivers/nvme/target/configfs.c:2089: configfs_add_default_group(&port->referrals_group, &port->group);
drivers/nvme/target/configfs.c-2090-
--
drivers/nvme/target/configfs.c-2092- "ana_groups", &nvmet_ana_groups_type);
drivers/nvme/target/configfs.c:2093: configfs_add_default_group(&port->ana_groups_group, &port->group);
drivers/nvme/target/configfs.c-2094-
--
drivers/nvme/target/configfs.c-2099- &nvmet_ana_group_type);
drivers/nvme/target/configfs.c:2100: configfs_add_default_group(&port->ana_default_group.group,
drivers/nvme/target/configfs.c-2101- &port->ana_groups_group);
--
drivers/nvme/target/configfs.c=2363=int __init nvmet_init_configfs(void)
--
drivers/nvme/target/configfs.c-2371- "subsystems", &nvmet_subsystems_type);
drivers/nvme/target/configfs.c:2372: configfs_add_default_group(&nvmet_subsystems_group,
drivers/nvme/target/configfs.c-2373- &nvmet_configfs_subsystem.su_group);
--
drivers/nvme/target/configfs.c-2376- "ports", &nvmet_ports_type);
drivers/nvme/target/configfs.c:2377: configfs_add_default_group(&nvmet_ports_group,
drivers/nvme/target/configfs.c-2378- &nvmet_configfs_subsystem.su_group);
--
drivers/nvme/target/configfs.c-2381- "hosts", &nvmet_hosts_type);
drivers/nvme/target/configfs.c:2382: configfs_add_default_group(&nvmet_hosts_group,
drivers/nvme/target/configfs.c-2383- &nvmet_configfs_subsystem.su_group);
--
drivers/pci/endpoint/pci-ep-cfs.c=97=static struct config_group
--
drivers/pci/endpoint/pci-ep-cfs.c-104- &pci_secondary_epc_type);
drivers/pci/endpoint/pci-ep-cfs.c:105: configfs_add_default_group(secondary_epc_group, &epf_group->group);
drivers/pci/endpoint/pci-ep-cfs.c-106-
--
drivers/pci/endpoint/pci-ep-cfs.c=161=static struct config_group
--
drivers/pci/endpoint/pci-ep-cfs.c-167- &pci_primary_epc_type);
drivers/pci/endpoint/pci-ep-cfs.c:168: configfs_add_default_group(primary_epc_group, &epf_group->group);
drivers/pci/endpoint/pci-ep-cfs.c-169-
--
drivers/pci/endpoint/pci-ep-cfs.c=558=static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group)
--
drivers/pci/endpoint/pci-ep-cfs.c-572-
drivers/pci/endpoint/pci-ep-cfs.c:573: configfs_add_default_group(group, &epf_group->group);
drivers/pci/endpoint/pci-ep-cfs.c-574-}
--
drivers/target/iscsi/iscsi_target_configfs.c=725=static int lio_target_init_nodeacl(struct se_node_acl *se_nacl,
--
drivers/target/iscsi/iscsi_target_configfs.c-731- "iscsi_sess_stats", &iscsi_stat_sess_cit);
drivers/target/iscsi/iscsi_target_configfs.c:732: configfs_add_default_group(&acl->node_stat_grps.iscsi_sess_stats_group,
drivers/target/iscsi/iscsi_target_configfs.c-733- &se_nacl->acl_fabric_stat_group);
--
drivers/target/iscsi/iscsi_target_configfs.c=1202=static void lio_target_add_wwn_groups(struct se_wwn *wwn)
--
drivers/target/iscsi/iscsi_target_configfs.c-1207- "iscsi_instance", &iscsi_stat_instance_cit);
drivers/target/iscsi/iscsi_target_configfs.c:1208: configfs_add_default_group(&tiqn->tiqn_stat_grps.iscsi_instance_group,
drivers/target/iscsi/iscsi_target_configfs.c-1209- &tiqn->tiqn_wwn.fabric_stat_group);
--
drivers/target/iscsi/iscsi_target_configfs.c-1212- "iscsi_sess_err", &iscsi_stat_sess_err_cit);
drivers/target/iscsi/iscsi_target_configfs.c:1213: configfs_add_default_group(&tiqn->tiqn_stat_grps.iscsi_sess_err_group,
drivers/target/iscsi/iscsi_target_configfs.c-1214- &tiqn->tiqn_wwn.fabric_stat_group);
--
drivers/target/iscsi/iscsi_target_configfs.c-1217- "iscsi_tgt_attr", &iscsi_stat_tgt_attr_cit);
drivers/target/iscsi/iscsi_target_configfs.c:1218: configfs_add_default_group(&tiqn->tiqn_stat_grps.iscsi_tgt_attr_group,
drivers/target/iscsi/iscsi_target_configfs.c-1219- &tiqn->tiqn_wwn.fabric_stat_group);
--
drivers/target/iscsi/iscsi_target_configfs.c-1222- "iscsi_login_stats", &iscsi_stat_login_cit);
drivers/target/iscsi/iscsi_target_configfs.c:1223: configfs_add_default_group(&tiqn->tiqn_stat_grps.iscsi_login_stats_group,
drivers/target/iscsi/iscsi_target_configfs.c-1224- &tiqn->tiqn_wwn.fabric_stat_group);
--
drivers/target/iscsi/iscsi_target_configfs.c-1227- "iscsi_logout_stats", &iscsi_stat_logout_cit);
drivers/target/iscsi/iscsi_target_configfs.c:1228: configfs_add_default_group(&tiqn->tiqn_stat_grps.iscsi_logout_stats_group,
drivers/target/iscsi/iscsi_target_configfs.c-1229- &tiqn->tiqn_wwn.fabric_stat_group);
--
drivers/target/target_core_configfs.c=182=static struct config_group *target_core_register_fabric(
--
drivers/target/target_core_configfs.c-256- &tf->tf_discovery_cit);
drivers/target/target_core_configfs.c:257: configfs_add_default_group(&tf->tf_disc_group, &tf->tf_group);
drivers/target/target_core_configfs.c-258-
--
drivers/target/target_core_configfs.c=3422=static struct config_group *target_core_make_subdev(
--
drivers/target/target_core_configfs.c-3444- &tb->tb_dev_action_cit);
drivers/target/target_core_configfs.c:3445: configfs_add_default_group(&dev->dev_action_group, &dev->dev_group);
drivers/target/target_core_configfs.c-3446-
--
drivers/target/target_core_configfs.c-3448- &tb->tb_dev_attrib_cit);
drivers/target/target_core_configfs.c:3449: configfs_add_default_group(&dev->dev_attrib.da_group, &dev->dev_group);
drivers/target/target_core_configfs.c-3450-
--
drivers/target/target_core_configfs.c-3452- &tb->tb_dev_pr_cit);
drivers/target/target_core_configfs.c:3453: configfs_add_default_group(&dev->dev_pr_group, &dev->dev_group);
drivers/target/target_core_configfs.c-3454-
--
drivers/target/target_core_configfs.c-3456- &tb->tb_dev_wwn_cit);
drivers/target/target_core_configfs.c:3457: configfs_add_default_group(&dev->t10_wwn.t10_wwn_group,
drivers/target/target_core_configfs.c-3458- &dev->dev_group);
--
drivers/target/target_core_configfs.c-3461- "alua", &tb->tb_dev_alua_tg_pt_gps_cit);
drivers/target/target_core_configfs.c:3462: configfs_add_default_group(&dev->t10_alua.alua_tg_pt_gps_group,
drivers/target/target_core_configfs.c-3463- &dev->dev_group);
--
drivers/target/target_core_configfs.c-3466- "statistics", &tb->tb_dev_stat_cit);
drivers/target/target_core_configfs.c:3467: configfs_add_default_group(&dev->dev_stat_grps.stat_group,
drivers/target/target_core_configfs.c-3468- &dev->dev_group);
--
drivers/target/target_core_configfs.c-3479- "default_tg_pt_gp", &target_core_alua_tg_pt_gp_cit);
drivers/target/target_core_configfs.c:3480: configfs_add_default_group(&tg_pt_gp->tg_pt_gp_group,
drivers/target/target_core_configfs.c-3481- &dev->t10_alua.alua_tg_pt_gps_group);
--
drivers/target/target_core_configfs.c=3744=static int __init target_core_init_configfs(void)
--
drivers/target/target_core_configfs.c-3765- &target_core_cit);
drivers/target/target_core_configfs.c:3766: configfs_add_default_group(&target_core_hbagroup, &subsys->su_group);
drivers/target/target_core_configfs.c-3767-
--
drivers/target/target_core_configfs.c-3771- config_group_init_type_name(&alua_group, "alua", &target_core_alua_cit);
drivers/target/target_core_configfs.c:3772: configfs_add_default_group(&alua_group, &target_core_hbagroup);
drivers/target/target_core_configfs.c-3773-
--
drivers/target/target_core_configfs.c-3779- &target_core_alua_lu_gps_cit);
drivers/target/target_core_configfs.c:3780: configfs_add_default_group(&alua_lu_gps_group, &alua_group);
drivers/target/target_core_configfs.c-3781-
--
drivers/target/target_core_configfs.c-3792- &target_core_alua_lu_gp_cit);
drivers/target/target_core_configfs.c:3793: configfs_add_default_group(&lu_gp->lu_gp_group, &alua_lu_gps_group);
drivers/target/target_core_configfs.c-3794-
--
drivers/target/target_core_fabric_configfs.c=265=static struct config_group *target_fabric_make_mappedlun(
--
drivers/target/target_core_fabric_configfs.c-312- "statistics", &tf->tf_tpg_mappedlun_stat_cit);
drivers/target/target_core_fabric_configfs.c:313: configfs_add_default_group(&lacl->ml_stat_grps.stat_group,
drivers/target/target_core_fabric_configfs.c-314- &lacl->se_lun_group);
--
drivers/target/target_core_fabric_configfs.c=373=static struct config_group *target_fabric_make_nodeacl(
--
drivers/target/target_core_fabric_configfs.c-390- &tf->tf_tpg_nacl_attrib_cit);
drivers/target/target_core_fabric_configfs.c:391: configfs_add_default_group(&se_nacl->acl_attrib_group,
drivers/target/target_core_fabric_configfs.c-392- &se_nacl->acl_group);
--
drivers/target/target_core_fabric_configfs.c-395- &tf->tf_tpg_nacl_auth_cit);
drivers/target/target_core_fabric_configfs.c:396: configfs_add_default_group(&se_nacl->acl_auth_group,
drivers/target/target_core_fabric_configfs.c-397- &se_nacl->acl_group);
--
drivers/target/target_core_fabric_configfs.c-400- &tf->tf_tpg_nacl_param_cit);
drivers/target/target_core_fabric_configfs.c:401: configfs_add_default_group(&se_nacl->acl_param_group,
drivers/target/target_core_fabric_configfs.c-402- &se_nacl->acl_group);
--
drivers/target/target_core_fabric_configfs.c-405- "fabric_statistics", &tf->tf_tpg_nacl_stat_cit);
drivers/target/target_core_fabric_configfs.c:406: configfs_add_default_group(&se_nacl->acl_fabric_stat_group,
drivers/target/target_core_fabric_configfs.c-407- &se_nacl->acl_group);
--
drivers/target/target_core_fabric_configfs.c=740=static struct config_group *target_fabric_make_lun(
--
drivers/target/target_core_fabric_configfs.c-768- "statistics", &tf->tf_tpg_port_stat_cit);
drivers/target/target_core_fabric_configfs.c:769: configfs_add_default_group(&lun->port_stat_grps.stat_group,
drivers/target/target_core_fabric_configfs.c-770- &lun->lun_group);
--
drivers/target/target_core_fabric_configfs.c=926=static struct config_group *target_fabric_make_tpg(
--
drivers/target/target_core_fabric_configfs.c-947- &tf->tf_tpg_lun_cit);
drivers/target/target_core_fabric_configfs.c:948: configfs_add_default_group(&se_tpg->tpg_lun_group,
drivers/target/target_core_fabric_configfs.c-949- &se_tpg->tpg_group);
--
drivers/target/target_core_fabric_configfs.c-952- &tf->tf_tpg_np_cit);
drivers/target/target_core_fabric_configfs.c:953: configfs_add_default_group(&se_tpg->tpg_np_group,
drivers/target/target_core_fabric_configfs.c-954- &se_tpg->tpg_group);
--
drivers/target/target_core_fabric_configfs.c-957- &tf->tf_tpg_nacl_cit);
drivers/target/target_core_fabric_configfs.c:958: configfs_add_default_group(&se_tpg->tpg_acl_group,
drivers/target/target_core_fabric_configfs.c-959- &se_tpg->tpg_group);
--
drivers/target/target_core_fabric_configfs.c-962- &tf->tf_tpg_attrib_cit);
drivers/target/target_core_fabric_configfs.c:963: configfs_add_default_group(&se_tpg->tpg_attrib_group,
drivers/target/target_core_fabric_configfs.c-964- &se_tpg->tpg_group);
--
drivers/target/target_core_fabric_configfs.c-967- &tf->tf_tpg_auth_cit);
drivers/target/target_core_fabric_configfs.c:968: configfs_add_default_group(&se_tpg->tpg_auth_group,
drivers/target/target_core_fabric_configfs.c-969- &se_tpg->tpg_group);
--
drivers/target/target_core_fabric_configfs.c-972- &tf->tf_tpg_param_cit);
drivers/target/target_core_fabric_configfs.c:973: configfs_add_default_group(&se_tpg->tpg_param_group,
drivers/target/target_core_fabric_configfs.c-974- &se_tpg->tpg_group);
--
drivers/target/target_core_fabric_configfs.c=1125=static struct config_group *target_fabric_make_wwn(
--
drivers/target/target_core_fabric_configfs.c-1148- &tf->tf_wwn_fabric_stats_cit);
drivers/target/target_core_fabric_configfs.c:1149: configfs_add_default_group(&wwn->fabric_stat_group, &wwn->wwn_group);
drivers/target/target_core_fabric_configfs.c-1150-
--
drivers/target/target_core_fabric_configfs.c-1152- &tf->tf_wwn_param_cit);
drivers/target/target_core_fabric_configfs.c:1153: configfs_add_default_group(&wwn->param_group, &wwn->wwn_group);
drivers/target/target_core_fabric_configfs.c-1154-
--
drivers/target/target_core_stat.c=396=void target_stat_setup_dev_default_groups(struct se_device *dev)
--
drivers/target/target_core_stat.c-399- "scsi_dev", &target_stat_scsi_dev_cit);
drivers/target/target_core_stat.c:400: configfs_add_default_group(&dev->dev_stat_grps.scsi_dev_group,
drivers/target/target_core_stat.c-401- &dev->dev_stat_grps.stat_group);
--
drivers/target/target_core_stat.c-404- "scsi_tgt_dev", &target_stat_scsi_tgt_dev_cit);
drivers/target/target_core_stat.c:405: configfs_add_default_group(&dev->dev_stat_grps.scsi_tgt_dev_group,
drivers/target/target_core_stat.c-406- &dev->dev_stat_grps.stat_group);
--
drivers/target/target_core_stat.c-409- "scsi_lu", &target_stat_scsi_lu_cit);
drivers/target/target_core_stat.c:410: configfs_add_default_group(&dev->dev_stat_grps.scsi_lu_group,
drivers/target/target_core_stat.c-411- &dev->dev_stat_grps.stat_group);
--
drivers/target/target_core_stat.c=803=void target_stat_setup_port_default_groups(struct se_lun *lun)
--
drivers/target/target_core_stat.c-806- "scsi_port", &target_stat_scsi_port_cit);
drivers/target/target_core_stat.c:807: configfs_add_default_group(&lun->port_stat_grps.scsi_port_group,
drivers/target/target_core_stat.c-808- &lun->port_stat_grps.stat_group);
--
drivers/target/target_core_stat.c-811- "scsi_tgt_port", &target_stat_scsi_tgt_port_cit);
drivers/target/target_core_stat.c:812: configfs_add_default_group(&lun->port_stat_grps.scsi_tgt_port_group,
drivers/target/target_core_stat.c-813- &lun->port_stat_grps.stat_group);
--
drivers/target/target_core_stat.c-816- "scsi_transport", &target_stat_scsi_transport_cit);
drivers/target/target_core_stat.c:817: configfs_add_default_group(&lun->port_stat_grps.scsi_transport_group,
drivers/target/target_core_stat.c-818- &lun->port_stat_grps.stat_group);
--
drivers/target/target_core_stat.c=1301=void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *lacl)
--
drivers/target/target_core_stat.c-1304- "scsi_auth_intr", &target_stat_scsi_auth_intr_cit);
drivers/target/target_core_stat.c:1305: configfs_add_default_group(&lacl->ml_stat_grps.scsi_auth_intr_group,
drivers/target/target_core_stat.c-1306- &lacl->ml_stat_grps.stat_group);
--
drivers/target/target_core_stat.c-1309- "scsi_att_intr_port", &target_stat_scsi_att_intr_port_cit);
drivers/target/target_core_stat.c:1310: configfs_add_default_group(&lacl->ml_stat_grps.scsi_att_intr_port_group,
drivers/target/target_core_stat.c-1311- &lacl->ml_stat_grps.stat_group);
--
drivers/usb/gadget/configfs.c=696=static struct config_group *config_desc_make(
--
drivers/usb/gadget/configfs.c-748- &gadget_config_name_strings_type);
drivers/usb/gadget/configfs.c:749: configfs_add_default_group(&cfg->strings_group, &cfg->group);
drivers/usb/gadget/configfs.c-750-
--
drivers/usb/gadget/configfs.c=1525=struct config_group *usb_os_desc_prepare_interf_dir(
--
drivers/usb/gadget/configfs.c-1549- config_group_init_type_name(os_desc_group, "os_desc", os_desc_type);
drivers/usb/gadget/configfs.c:1550: configfs_add_default_group(os_desc_group, parent);
drivers/usb/gadget/configfs.c-1551-
--
drivers/usb/gadget/configfs.c-1563- names[n_interf]);
drivers/usb/gadget/configfs.c:1564: configfs_add_default_group(&d->group, os_desc_group);
drivers/usb/gadget/configfs.c-1565- }
--
drivers/usb/gadget/configfs.c=1988=static struct config_group *gadgets_make(
--
drivers/usb/gadget/configfs.c-2001- &functions_type);
drivers/usb/gadget/configfs.c:2002: configfs_add_default_group(&gi->functions_group, &gi->group);
drivers/usb/gadget/configfs.c-2003-
--
drivers/usb/gadget/configfs.c-2005- &config_desc_type);
drivers/usb/gadget/configfs.c:2006: configfs_add_default_group(&gi->configs_group, &gi->group);
drivers/usb/gadget/configfs.c-2007-
--
drivers/usb/gadget/configfs.c-2009- &gadget_language_strings_type);
drivers/usb/gadget/configfs.c:2010: configfs_add_default_group(&gi->strings_group, &gi->group);
drivers/usb/gadget/configfs.c-2011-
--
drivers/usb/gadget/configfs.c-2013- &os_desc_type);
drivers/usb/gadget/configfs.c:2014: configfs_add_default_group(&gi->os_desc_group, &gi->group);
drivers/usb/gadget/configfs.c-2015-
--
drivers/usb/gadget/configfs.c-2017- &webusb_type);
drivers/usb/gadget/configfs.c:2018: configfs_add_default_group(&gi->webusb_group, &gi->group);
drivers/usb/gadget/configfs.c-2019-
--
drivers/usb/gadget/function/f_mass_storage.c=3494=static struct usb_function_instance *fsg_alloc_inst(void)
--
drivers/usb/gadget/function/f_mass_storage.c-3530- config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
drivers/usb/gadget/function/f_mass_storage.c:3531: configfs_add_default_group(&opts->lun0.group, &opts->func_inst.group);
drivers/usb/gadget/function/f_mass_storage.c-3532-
--
drivers/usb/gadget/function/f_midi2.c=2647=static struct usb_function_instance *f_midi2_alloc_inst(void)
--
drivers/usb/gadget/function/f_midi2.c-2687- &f_midi2_ep_type);
drivers/usb/gadget/function/f_midi2.c:2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
drivers/usb/gadget/function/f_midi2.c-2689-
--
drivers/usb/gadget/function/f_midi2.c-2691- &f_midi2_block_type);
drivers/usb/gadget/function/f_midi2.c:2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
drivers/usb/gadget/function/f_midi2.c-2693-
--
drivers/usb/gadget/function/uvc_configfs.c=156=static int uvcg_config_create_group(struct config_group *parent,
--
drivers/usb/gadget/function/uvc_configfs.c-165- config_group_init_type_name(group, type->name, &type->type);
drivers/usb/gadget/function/uvc_configfs.c:166: configfs_add_default_group(group, parent);
drivers/usb/gadget/function/uvc_configfs.c-167-
--
drivers/usb/gadget/function/uvc_configfs.c=1432=static int uvcg_control_class_create_children(struct config_group *parent)
--
drivers/usb/gadget/function/uvc_configfs.c-1447- &uvcg_control_class_type);
drivers/usb/gadget/function/uvc_configfs.c:1448: configfs_add_default_group(&group->group, parent);
drivers/usb/gadget/function/uvc_configfs.c-1449- }
--
drivers/usb/gadget/function/uvc_configfs.c=3096=static int uvcg_color_matching_create_children(struct config_group *parent)
--
drivers/usb/gadget/function/uvc_configfs.c-3112- &uvcg_color_matching_type);
drivers/usb/gadget/function/uvc_configfs.c:3113: configfs_add_default_group(&color_match->group, parent);
drivers/usb/gadget/function/uvc_configfs.c-3114-
--
drivers/usb/gadget/function/uvc_configfs.c=3548=static int uvcg_streaming_class_create_children(struct config_group *parent)
--
drivers/usb/gadget/function/uvc_configfs.c-3563- &uvcg_streaming_class_type);
drivers/usb/gadget/function/uvc_configfs.c:3564: configfs_add_default_group(&group->group, parent);
drivers/usb/gadget/function/uvc_configfs.c-3565- }
--
fs/dlm/config.c=422=static struct config_group *make_cluster(struct config_group *g,
--
fs/dlm/config.c-442-
fs/dlm/config.c:443: configfs_add_default_group(&sps->ss_group, &cl->group);
fs/dlm/config.c:444: configfs_add_default_group(&cms->cs_group, &cl->group);
fs/dlm/config.c-445-
--
fs/dlm/config.c=478=static struct config_group *make_space(struct config_group *g, const char *name)
--
fs/dlm/config.c-491- config_group_init_type_name(&nds->ns_group, "nodes", &nodes_type);
fs/dlm/config.c:492: configfs_add_default_group(&nds->ns_group, &sp->group);
fs/dlm/config.c-493-
--
fs/ocfs2/cluster/nodemanager.c=686=static struct config_group *o2nm_cluster_group_make_group(struct config_group *group,
--
fs/ocfs2/cluster/nodemanager.c-705- &o2nm_cluster_type);
fs/ocfs2/cluster/nodemanager.c:706: configfs_add_default_group(&ns->ns_group, &cluster->cl_group);
fs/ocfs2/cluster/nodemanager.c-707-
--
fs/ocfs2/cluster/nodemanager.c-709- &o2nm_node_group_type);
fs/ocfs2/cluster/nodemanager.c:710: configfs_add_default_group(o2hb_group, &cluster->cl_group);
fs/ocfs2/cluster/nodemanager.c-711-
--
include/linux/configfs.h=105=extern struct config_item *config_group_find_item(struct config_group *,
--
include/linux/configfs.h-108-
include/linux/configfs.h:109:static inline void configfs_add_default_group(struct config_group *new_group,
include/linux/configfs.h-110- struct config_group *group)
]
|
| 136/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 21383
output: 44
thoughts: 40
Thoughts: Okay, here's my take on those thoughts, framed from my perspective:
**Diving into the ConfigFS Nuances**
Right, so I see `f_mass_storage.c` leveraging `configfs_add_default_group`. My first instinct, as always, is to follow the entire lifecycle, so I'm immediately interested in how they're handling the counterpart, `configfs_remove_default_groups`. Understanding the add is only half the battle; knowing the removal process is *critical* to ensuring proper resource cleanup and preventing any lingering issues. I need to dig into the relationship between these two to fully grasp the design philosophy and potential implications within the mass storage framework. I'm keen to see if there are any subtle nuances in that relationship that could cause problems down the line. It's time to check what's going on with the removal.
|
| 137/2 |
2026/07/30 14:17 |
tool |
grepper |
0m
Error:
Args: map[Expression:configfs_remove_default_groups PathPrefix:drivers/usb/gadget/function/f_mass_storage.c]
Results:
|
| 138/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 21665
output: 46
thoughts: 36
Thoughts: Okay, let's see. My first thought is: **Hang on a second, that's not right**. I was under the impression that `f_mass_storage.c` should be using `configfs_remove_default_groups`. But now I'm questioning that assumption. I need to be absolutely sure. This warrants a closer look, especially focusing on how the storage instance is actually cleaned up and freed within this module. I need to trace the memory management in this specific context to confirm. I'm going to dig into the freeing process itself.
|
| 139/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_free_inst]
Results: map[SourceCode:3485: static void fsg_free_inst(struct usb_function_instance *fi)
3486: {
3487: struct fsg_opts *opts;
3488:
3489: opts = fsg_opts_from_func_inst(fi);
3490: fsg_common_release(opts->common);
3491: kfree(opts);
3492: }
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 140/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 22019
output: 46
thoughts: 41
Thoughts: Okay, let me think this through.
**Investigating a Potential Resource Leak in the File Storage Gadget (FSG)**
Alright, so I'm looking at `fsg_free_inst` and something immediately strikes me as odd. I'm wondering why this function doesn't seem to be calling `configfs_remove_default_groups`. That's a red flag. Could it potentially be leaking resources in some way? I need to dig deeper. I need to figure out *what* this module is doing, and if it's de-allocating everything correctly.
My next step is to examine `fsg_lun_drop`. That feels like the logical place to start tracing the resource cleanup path associated with a logical unit number (LUN) within the FSG context. I need to understand what's happening there, specifically, and see if the missing call to `configfs_remove_default_groups` becomes relevant in its control flow. I need to know if `fsg_lun_drop` *should* be handling those cleanup steps, or if the initial suspicion of a leak is incorrect. This is going to require careful inspection of the code.
|
| 141/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_lun_drop]
Results: map[SourceCode:3349: static void fsg_lun_drop(struct config_group *group, struct config_item *item)
3350: {
3351: struct fsg_lun_opts *lun_opts;
3352: struct fsg_opts *fsg_opts;
3353:
3354: lun_opts = to_fsg_lun_opts(item);
3355: fsg_opts = to_fsg_opts(&group->cg_item);
3356:
3357: mutex_lock(&fsg_opts->lock);
3358: if (fsg_opts->refcnt) {
3359: struct config_item *gadget;
3360:
3361: gadget = group->cg_item.ci_parent->ci_parent;
3362: unregister_gadget_item(gadget);
3363: }
3364:
3365: fsg_common_remove_lun(lun_opts->lun);
3366: fsg_opts->common->luns[lun_opts->lun_id] = NULL;
3367: lun_opts->lun_id = 0;
3368: mutex_unlock(&fsg_opts->lock);
3369:
3370: config_item_put(item);
3371: }
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 142/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 22761
output: 46
thoughts: 34
Thoughts: Okay, let me think this through.
**Analyzing the Default Group Assignment in `fsg_alloc_inst`**
Hmm, hold on a second...it just hit me. I think I've spotted something potentially significant. I recall that `fsg_alloc_inst` automatically includes `lun.0` as a default group. This default group addition could have implications further down the line. I'd better confirm my recollection and understand the mechanics in greater detail. I need to take a look at the `fsg_alloc_inst` function specifically. Let's dig into that and see how this default group assignment is actually implemented. I want to confirm exactly how it's being handled and any knock-on effects that might result.
|
| 143/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_alloc_inst]
Results: map[SourceCode:3494: static struct usb_function_instance *fsg_alloc_inst(void)
3495: {
3496: struct fsg_opts *opts;
3497: struct fsg_lun_config config;
3498: int rc;
3499:
3500: opts = kzalloc_obj(*opts);
3501: if (!opts)
3502: return ERR_PTR(-ENOMEM);
3503: mutex_init(&opts->lock);
3504: opts->func_inst.free_func_inst = fsg_free_inst;
3505: opts->common = fsg_common_setup(opts->common);
3506: if (IS_ERR(opts->common)) {
3507: rc = PTR_ERR(opts->common);
3508: goto release_opts;
3509: }
3510:
3511: rc = fsg_common_set_num_buffers(opts->common,
3512: CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS);
3513: if (rc)
3514: goto release_common;
3515:
3516: pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
3517:
3518: memset(&config, 0, sizeof(config));
3519: config.removable = true;
3520: rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0",
3521: (const char **)&opts->func_inst.group.cg_item.ci_name);
3522: if (rc)
3523: goto release_buffers;
3524:
3525: opts->lun0.lun = opts->common->luns[0];
3526: opts->lun0.lun_id = 0;
3527:
3528: config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type);
3529:
3530: config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type);
3531: configfs_add_default_group(&opts->lun0.group, &opts->func_inst.group);
3532:
3533: return &opts->func_inst;
3534:
3535: release_buffers:
3536: fsg_common_free_buffers(opts->common);
3537: release_common:
3538: kfree(opts->common);
3539: release_opts:
3540: kfree(opts);
3541: return ERR_PTR(rc);
3542: }
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 144/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 23808
output: 40
thoughts: 140
Thoughts: **Reflecting on Memory Management in `f_midi2`**
Okay, hold on a second... I was getting tripped up thinking about freeing `opts->lun0`, but then I realized it's part of the main `opts` structure, so that's handled implicitly. My focus needs to be on the dynamic allocation within `f_midi2`. Ah, yes, `kzalloc_obj()` is used for `ep_opts` and `block_opts`, which means they definitely need explicit freeing. The key is tracking their reference counts. If the count drops to zero, then they need to be freed. Now, I vaguely recall something about default groups. If they're added as default groups, their refcount will increase. Did `configfs_add_default_group` increment the refcount? Need to double-check that. Let me look at `configfs_add_default_group` to confirm. It's time to verify this and ensure everything is properly cleaned up.
|
| 145/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/configfs.h Name:configfs_add_default_group]
Results: map[SourceCode: 109: static inline void configfs_add_default_group(struct config_group *new_group,
110: struct config_group *group)
111: {
112: list_add_tail(&new_group->group_entry, &group->default_groups);
113: }
SourceFile:include/linux/configfs.h]
|
| 146/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 24315
output: 35
thoughts: 75
Thoughts: **Analyzing ConfigFS Group Management**
Okay, so I'm focusing on how `configfs_add_default_group` works. It's becoming clear that this function *only* adds to the `default_groups` list. It doesn't appear to handle any refcounting itself. That's the key.
This means the reference count for those added groups will need to be managed elsewhere. The crucial point now is when and how the reference count might actually get incremented. My gut feeling is that it might happen during the population of the parent group within the configfs structure. The action I need to check is if `populate_groups`, the function called during this population, plays a role in managing that refcount.
I’m now going to dig into the `populate_groups` function, specifically within `fs/configfs/dir.c` to see how it handles things, including checking its role in reference counting.
|
| 147/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:populate_groups]
Results: map[SourceCode: 734: static int populate_groups(struct config_group *group,
735: struct configfs_fragment *frag)
736: {
737: struct dentry *parent = group->cg_item.ci_dentry;
738: struct config_group *new_group;
739:
740: list_for_each_entry(new_group, &group->default_groups, group_entry) {
741: int ret = create_default_group(parent, new_group, frag);
742: if (ret)
743: return ret;
744: }
745: return 0;
746: }
SourceFile:fs/configfs/dir.c]
|
| 148/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 24847
output: 37
thoughts: 11
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deeply familiar with code and system design:
**Checking `create_default_group` Functionality**
Alright, the mental checklist begins. First, let's zero in on that `create_default_group` function. I need to quickly assess what it *should* be doing and whether it's doing it correctly. Is this the right spot to start debugging, reviewing, or extending a system? Am I looking at this function due to a bug report, a performance issue, or as part of a larger refactor?
My immediate thoughts are, what are the implicit contracts this function honors?
* Does it handle error conditions gracefully?
* What is the expected behavior if a default group already exists?
* How is this group defined?
* Where is this defined group used?
* Are there race conditions to consider during concurrent access?
* Are there any security implications of default group creation and assignment?
I'm thinking about the function's dependencies – what other functions or data structures does it rely on? Are those dependencies well-defined, and is there a possibility for a cascading effect of errors?
I'm not going to immediately jump into the code. Instead, I'll probably start with a quick mental walkthrough, tracing the expected execution flow and edge cases to ensure the function aligns with the overall system design. From there, I'll determine if I need to dive into the code directly, look at unit tests, or examine related documentation.
|
| 149/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:create_default_group]
Results: map[SourceCode: 695: /*
696: * This fakes mkdir(2) on a default_groups[] entry. It
697: * creates a dentry, attachs it, and then does fixup
698: * on the sd->s_type.
699: *
700: * We could, perhaps, tweak our parent's ->mkdir for a minute and
701: * try using vfs_mkdir. Just a thought.
702: */
703: static int create_default_group(struct dentry *parent,
704: struct config_group *group,
705: struct configfs_fragment *frag)
706: {
707: int ret;
708: struct configfs_dirent *sd;
709: /* We trust the caller holds a reference to parent */
710: struct dentry *child;
711:
712: if (!group->cg_item.ci_name)
713: group->cg_item.ci_name = group->cg_item.ci_namebuf;
714:
715: ret = -ENOMEM;
716: child = d_alloc_name(parent, group->cg_item.ci_name);
717: if (child) {
718: d_add(child, NULL);
719:
720: ret = configfs_attach_group(&group->cg_item, child, frag);
721: if (!ret) {
722: sd = child->d_fsdata;
723: sd->s_type |= CONFIGFS_USET_DEFAULT;
724: } else {
725: BUG_ON(d_inode(child));
726: d_drop(child);
727: }
728: dput(child);
729: }
730:
731: return ret;
732: }
SourceFile:fs/configfs/dir.c]
|
| 150/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 25803
output: 38
thoughts: 12
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Diving into `configfs_attach_group`**
Okay, time to dissect `configfs_attach_group`. I need to refresh my understanding of how this function fits into the overall configfs architecture, specifically when we're attaching a new group. My mental model needs a quick update. I'm immediately thinking about the implications for object lifecycle management within the filesystem. Is this where the permissions are enforced? How does this interact with the various attribute operations, and does it mean we need to re-evaluate the object based on its parent? Is the function handling reference counting correctly? A quick review of the relevant source code is definitely in order. I'll need to pay close attention to the error handling paths as well, especially regarding resource allocation and cleanup. I anticipate there might be some nuanced locking considerations around the attach process, and that's something I don't want to overlook. Let's get to it.
|
| 151/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_attach_group]
Results: map[SourceCode: 891: static int configfs_attach_group(struct config_item *item,
892: struct dentry *dentry,
893: struct configfs_fragment *frag)
894: {
895: int ret;
896: struct configfs_dirent *sd;
897:
898: ret = configfs_attach_item(item, dentry, frag);
899: if (!ret) {
900: sd = dentry->d_fsdata;
901: sd->s_type |= CONFIGFS_USET_DIR;
902:
903: /*
904: * FYI, we're faking mkdir in populate_groups()
905: * We must lock the group's inode to avoid races with the VFS
906: * which can already hit the inode and try to add/remove entries
907: * under it.
908: *
909: * We must also lock the inode to remove it safely in case of
910: * error, as rmdir() would.
911: */
912: inode_lock_nested(d_inode(dentry), I_MUTEX_CHILD);
913: configfs_adjust_dir_dirent_depth_before_populate(sd);
914: ret = populate_groups(to_config_group(item), frag);
915: if (ret) {
916: configfs_detach_group(dentry);
917: d_inode(dentry)->i_flags |= S_DEAD;
918: dont_mount(dentry);
919: }
920: configfs_adjust_dir_dirent_depth_after_populate(sd);
921: inode_unlock(d_inode(dentry));
922: if (ret)
923: d_delete(dentry);
924: }
925:
926: return ret;
927: }
SourceFile:fs/configfs/dir.c]
|
| 152/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 26653
output: 38
thoughts: 12
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into `configfs_attach_item`**
Alright, time to dig into `configfs_attach_item`. Seems like a critical point, right? Understanding how items are attached is fundamental to grasping the whole ConfigFS architecture. I need to make sure I'm solid on the mechanics of this function. Let's see... what are the key data structures involved at this stage? Is it primarily about linking the filesystem inode and dentry, or are there more complex operations happening behind the scenes, like attribute initialization or permission checks? My intuition says it’s more than just a simple inode/dentry connection, especially given the flexibility ConfigFS offers. I need to refresh my memory on the interactions with the backing storage, if any, and the implications for attribute handling. This could be a good point to look at how different item types are handled differently. Let's start breaking down the code and see what nuances pop up.
|
| 153/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_attach_item]
Results: map[SourceCode: 842: /*
843: * The goal is that configfs_attach_item() (and
844: * configfs_attach_group()) can be called from either the VFS or this
845: * module. That is, they assume that the items have been created,
846: * the dentry allocated, and the dcache is all ready to go.
847: *
848: * If they fail, they must clean up after themselves as if they
849: * had never been called. The caller (VFS or local function) will
850: * handle cleaning up the dcache bits.
851: *
852: * configfs_detach_group() and configfs_detach_item() behave similarly on
853: * the way out. They assume that the proper semaphores are held, they
854: * clean up the configfs items, and they expect their callers will
855: * handle the dcache bits.
856: */
857: static int configfs_attach_item(struct config_item *item,
858: struct dentry *dentry,
859: struct configfs_fragment *frag)
860: {
861: int ret;
862:
863: ret = configfs_create_dir(item, dentry, frag);
864: if (!ret) {
865: ret = populate_attrs(item);
866: if (ret) {
867: /*
868: * We are going to remove an inode and its dentry but
869: * the VFS may already have hit and used them. Thus,
870: * we must lock them as rmdir() would.
871: */
872: inode_lock(d_inode(dentry));
873: configfs_detach_item(dentry);
874: d_inode(dentry)->i_flags |= S_DEAD;
875: dont_mount(dentry);
876: inode_unlock(d_inode(dentry));
877: d_delete(dentry);
878: }
879: }
880:
881: return ret;
882: }
SourceFile:fs/configfs/dir.c]
|
| 154/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 27577
output: 38
thoughts: 18
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**A Potential Data Race Alert!**
Alright, hang on a second. I'm just tracing through this `configfs` code, and something's pinging my radar. I'm seeing that `configfs_create_dir` function... and it's calling `config_item_get`. That little detail is sparking a concern in my mind.
Now, from what I know about this system, if `config_item_get` is being used to increase a reference count within the directory creation process, and if `configfs_create_dir` is not handling potential concurrent access correctly… then we might have a subtle, hard-to-reproduce, data race issue on our hands.
My initial thought is to look at how `config_item_get` and `configfs_create_dir` interact specifically: is the new directory properly guarded against concurrent destruction, or are we perhaps creating a window where another thread or process could free the item while we're in the middle of initialization or metadata setup?
I need to analyze the surrounding locking mechanisms within `configfs_create_dir` and the overall context of that `config_item_get` call very carefully. Are there adequate spinlocks, mutexes, or RCU guarantees in place to protect the data structure? This might be something to look out for. I'm going to dig a little deeper into this.
|
| 155/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_create_dir]
Results: map[SourceCode: 293: static int configfs_create_dir(struct config_item *item, struct dentry *dentry,
294: struct configfs_fragment *frag)
295: {
296: int error;
297: umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
298: struct dentry *p = dentry->d_parent;
299: struct inode *p_inode = d_inode(p);
300: struct inode *inode;
301:
302: BUG_ON(!item);
303:
304: error = configfs_make_dirent(p->d_fsdata, dentry, item, mode,
305: CONFIGFS_DIR | CONFIGFS_USET_CREATING,
306: frag);
307: if (unlikely(error))
308: return error;
309:
310: configfs_set_dir_dirent_depth(p->d_fsdata, dentry->d_fsdata);
311: inode = configfs_create(dentry, mode);
312: if (IS_ERR(inode))
313: goto out_remove;
314:
315: inode->i_op = &configfs_dir_inode_operations;
316: inode->i_fop = &configfs_dir_operations;
317: /* directory inodes start off with i_nlink == 2 (for "." entry) */
318: inc_nlink(inode);
319: d_make_persistent(dentry, inode);
320: inc_nlink(p_inode);
321: inode_set_mtime_to_ts(p_inode, inode_set_ctime_current(p_inode));
322: item->ci_dentry = dentry;
323: return 0;
324:
325: out_remove:
326: configfs_put(dentry->d_fsdata);
327: configfs_remove_dirent(dentry);
328: return PTR_ERR(inode);
329: }
SourceFile:fs/configfs/dir.c]
|
| 156/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 28605
output: 39
thoughts: 20
Thoughts: Here's my attempt at summarizing that thought process, tailored for an expert audience:
**Digging into the configfs Internals**
Okay, hold on a second. I'm following the flow here, tracing this `configfs_make_dirent` function, and it seems... hang on... am I reading this right? It's calling `config_item_get` inside? That's... interesting. Why would we be incrementing the reference count of the configfs item *within* the dirent creation function? Typically you’d expect the item to be "gotten" (and thus ref-counted) at a higher level, like within the actual VFS operations that use the dirent. This feels like it might introduce an unexpected order-of-operations dependency or a potential refcount imbalance if a different part of the code needs to use this dirent. I need to understand this call flow and the lifetime guarantees of these dirents much better now. This could have some important implications for concurrent access and resource management within configfs. Time to delve deeper...
|
| 157/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_make_dirent]
Results: map[SourceCode: 253: int configfs_make_dirent(struct configfs_dirent * parent_sd,
254: struct dentry * dentry, void * element,
255: umode_t mode, int type, struct configfs_fragment *frag)
256: {
257: struct configfs_dirent * sd;
258:
259: sd = configfs_new_dirent(parent_sd, element, type, frag);
260: if (IS_ERR(sd))
261: return PTR_ERR(sd);
262:
263: sd->s_mode = mode;
264: sd->s_dentry = dentry;
265: if (dentry)
266: dentry->d_fsdata = configfs_get(sd);
267:
268: return 0;
269: }
SourceFile:fs/configfs/dir.c]
|
| 158/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 29175
output: 39
thoughts: 13
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Diving into `configfs_new_dirent`**
Okay, so we're looking at `configfs_new_dirent`. Time to refresh my mental model of that function. I need to quickly recall how this piece slots into the broader ConfigFS ecosystem. What are the key data structures being manipulated here? It's where the magic of creating new directory entries happens, so I'm expecting to see calls related to inode allocation, possibly `dentry` creation, and definitely some attribute setting for the new entry. Let's see if there are any specific locking considerations in this function, especially if it deals with concurrent accesses to the ConfigFS filesystem. I want to make sure the implementation handles the race conditions in the file system.
|
| 159/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_new_dirent]
Results: map[SourceCode: 184: /*
185: * Allocates a new configfs_dirent and links it to the parent configfs_dirent
186: */
187: static struct configfs_dirent *configfs_new_dirent(struct configfs_dirent *parent_sd,
188: void *element, int type,
189: struct configfs_fragment *frag)
190: {
191: struct configfs_dirent * sd;
192:
193: sd = kmem_cache_zalloc(configfs_dir_cachep, GFP_KERNEL);
194: if (!sd)
195: return ERR_PTR(-ENOMEM);
196:
197: atomic_set(&sd->s_count, 1);
198: INIT_LIST_HEAD(&sd->s_children);
199: sd->s_element = element;
200: sd->s_type = type;
201: configfs_init_dirent_depth(sd);
202: spin_lock(&configfs_dirent_lock);
203: if (parent_sd->s_type & CONFIGFS_USET_DROPPING) {
204: spin_unlock(&configfs_dirent_lock);
205: kmem_cache_free(configfs_dir_cachep, sd);
206: return ERR_PTR(-ENOENT);
207: }
208: sd->s_frag = get_fragment(frag);
209:
210: /*
211: * configfs_lookup scans only for unpinned items. s_children is
212: * partitioned so that configfs_lookup can bail out early.
213: * CONFIGFS_PINNED and CONFIGFS_NOT_PINNED are not symmetrical. readdir
214: * cursors still need to be inserted at the front of the list.
215: */
216: if (sd->s_type & CONFIGFS_PINNED)
217: list_add_tail(&sd->s_sibling, &parent_sd->s_children);
218: else
219: list_add(&sd->s_sibling, &parent_sd->s_children);
220: spin_unlock(&configfs_dirent_lock);
221:
222: return sd;
223: }
SourceFile:fs/configfs/dir.c]
|
| 160/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 30103
output: 36
thoughts: 47
Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing:
**Digging into the ConfigFS Creation Process**
Alright, let's trace this directory creation. So, `configfs_attach_group` uses `configfs_attach_item`, and that's the function that actually creates the directory entry. Makes sense. Now I'm wondering, where is this `config_item_get` function called? I'm trying to understand the lifecycle of these config items. Knowing that will give me context as to *why* they are being created. Let me jump over to the `configfs_mkdir` implementation. That seems like a logical spot to find clues, since `mkdir` is fundamentally about creating directories. I bet I'll find something there that will help me understand the relationship.
|
| 161/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_mkdir]
Results: map[SourceCode:1289: static struct dentry *configfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
1290: struct dentry *dentry, umode_t mode)
1291: {
1292: int ret = 0;
1293: int module_got = 0;
1294: struct config_group *group = NULL;
1295: struct config_item *item = NULL;
1296: struct config_item *parent_item;
1297: struct configfs_subsystem *subsys;
1298: struct configfs_dirent *sd;
1299: const struct config_item_type *type;
1300: struct module *subsys_owner = NULL, *new_item_owner = NULL;
1301: struct configfs_fragment *frag;
1302: struct name_snapshot n;
1303: const char *name;
1304:
1305: take_dentry_name_snapshot(&n, dentry);
1306: name = n.name.name;
1307:
1308: sd = dentry->d_parent->d_fsdata;
1309:
1310: /*
1311: * Fake invisibility if dir belongs to a group/default groups hierarchy
1312: * being attached
1313: */
1314: if (!configfs_dirent_is_ready(sd)) {
1315: ret = -ENOENT;
1316: goto out;
1317: }
1318:
1319: if (!(sd->s_type & CONFIGFS_USET_DIR)) {
1320: ret = -EPERM;
1321: goto out;
1322: }
1323:
1324: frag = new_fragment();
1325: if (!frag) {
1326: ret = -ENOMEM;
1327: goto out;
1328: }
1329:
1330: /* Get a working ref for the duration of this function */
1331: parent_item = configfs_get_config_item(dentry->d_parent);
1332: type = parent_item->ci_type;
1333: subsys = to_config_group(parent_item)->cg_subsys;
1334: BUG_ON(!subsys);
1335:
1336: if (!type || !type->ct_group_ops ||
1337: (!type->ct_group_ops->make_group &&
1338: !type->ct_group_ops->make_item)) {
1339: ret = -EPERM; /* Lack-of-mkdir returns -EPERM */
1340: goto out_put;
1341: }
1342:
1343: /*
1344: * The subsystem may belong to a different module than the item
1345: * being created. We don't want to safely pin the new item but
1346: * fail to pin the subsystem it sits under.
1347: */
1348: if (!subsys->su_group.cg_item.ci_type) {
1349: ret = -EINVAL;
1350: goto out_put;
1351: }
1352: subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
1353: if (!try_module_get(subsys_owner)) {
1354: ret = -EINVAL;
1355: goto out_put;
1356: }
1357:
1358: mutex_lock(&subsys->su_mutex);
1359: if (type->ct_group_ops->make_group) {
1360: group = type->ct_group_ops->make_group(to_config_group(parent_item), name);
1361: if (!group)
1362: group = ERR_PTR(-ENOMEM);
1363: if (!IS_ERR(group)) {
1364: link_group(to_config_group(parent_item), group);
1365: item = &group->cg_item;
1366: } else
1367: ret = PTR_ERR(group);
1368: } else {
1369: item = type->ct_group_ops->make_item(to_config_group(parent_item), name);
1370: if (!item)
1371: item = ERR_PTR(-ENOMEM);
1372: if (!IS_ERR(item))
1373: link_obj(parent_item, item);
1374: else
1375: ret = PTR_ERR(item);
1376: }
1377: mutex_unlock(&subsys->su_mutex);
1378:
1379: if (ret) {
1380: /*
1381: * If ret != 0, then link_obj() was never called.
1382: * There are no extra references to clean up.
1383: */
1384: goto out_subsys_put;
1385: }
1386:
1387: /*
1388: * link_obj() has been called (via link_group() for groups).
1389: * From here on out, errors must clean that up.
1390: */
1391:
1392: type = item->ci_type;
1393: if (!type) {
1394: ret = -EINVAL;
1395: goto out_unlink;
1396: }
1397:
1398: new_item_owner = type->ct_owner;
1399: if (!try_module_get(new_item_owner)) {
1400: ret = -EINVAL;
1401: goto out_unlink;
1402: }
1403:
1404: /*
1405: * I hate doing it this way, but if there is
1406: * an error, module_put() probably should
1407: * happen after any cleanup.
1408: */
1409: module_got = 1;
1410:
1411: /*
1412: * Make racing rmdir() fail if it did not tag parent with
1413: * CONFIGFS_USET_DROPPING
1414: * Note: if CONFIGFS_USET_DROPPING is already set, attach_group() will
1415: * fail and let rmdir() terminate correctly
1416: */
1417: spin_lock(&configfs_dirent_lock);
1418: /* This will make configfs_detach_prep() fail */
1419: sd->s_type |= CONFIGFS_USET_IN_MKDIR;
1420: spin_unlock(&configfs_dirent_lock);
1421:
1422: if (group)
1423: ret = configfs_attach_group(item, dentry, frag);
1424: else
1425: ret = configfs_attach_item(item, dentry, frag);
1426:
1427: spin_lock(&configfs_dirent_lock);
1428: sd->s_type &= ~CONFIGFS_USET_IN_MKDIR;
1429: if (!ret)
1430: configfs_dir_set_ready(dentry->d_fsdata);
1431: spin_unlock(&configfs_dirent_lock);
1432:
1433: out_unlink:
1434: if (ret) {
1435: /* Tear down everything we built up */
1436: mutex_lock(&subsys->su_mutex);
1437:
1438: client_disconnect_notify(parent_item, item);
1439: if (group)
1440: unlink_group(group);
1441: else
1442: unlink_obj(item);
1443: client_drop_item(parent_item, item);
1444:
1445: mutex_unlock(&subsys->su_mutex);
1446:
1447: if (module_got)
1448: module_put(new_item_owner);
1449: }
1450:
1451: out_subsys_put:
1452: if (ret)
1453: module_put(subsys_owner);
1454:
1455: out_put:
1456: /*
1457: * link_obj()/link_group() took a reference from child->parent,
1458: * so the parent is safely pinned. We can drop our working
1459: * reference.
1460: */
1461: config_item_put(parent_item);
1462: put_fragment(frag);
1463:
1464: out:
1465: release_dentry_name_snapshot(&n);
1466: return ERR_PTR(ret);
1467: }
SourceFile:fs/configfs/dir.c]
|
| 162/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 32951
output: 37
thoughts: 244
Thoughts: **Refcount Confusion in ConfigFS Group Creation**
Okay, so I'm tracing this `configfs_mkdir` call, and it's leading me down a rabbit hole. It calls `configfs_attach_group`, which then calls `populate_groups`, and that function calls `create_default_group`. The kicker? `create_default_group` *also* calls `configfs_attach_group`! This smells like a recursive loop waiting to happen, but that's not my main concern right now.
My immediate question is: where is the refcount being handled? I need to ensure proper object lifecycle management. `configfs_attach_group` calls `configfs_attach_item`, which calls a series of functions that eventually calls `configfs_new_dirent`. Then I can see that `configfs_new_dirent` sets `sd->s_count` to 1, but I'm looking for the kref.
I went back and checked `configfs_attach_item` to see if that function was incrementing `item->ci_kref`. No such luck, and `configfs_create_dir` doesn't seem to be doing it either. So, I have to find where `item->ci_kref` is incremented.
Then I realized, `configfs_mkdir` actually calls `make_group` or `make_item`, and these functions return the items with an initial refcount of 1, so the new object created from `configfs_mkdir` function call will already have a refcount of one.
However, default groups are handled differently. They are created with an existing `group` object that is passed to `create_default_group`. This seems like a potential issue, so I'm going to re-examine that function again.
|
| 163/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:create_default_group]
Results: map[SourceCode: 695: /*
696: * This fakes mkdir(2) on a default_groups[] entry. It
697: * creates a dentry, attachs it, and then does fixup
698: * on the sd->s_type.
699: *
700: * We could, perhaps, tweak our parent's ->mkdir for a minute and
701: * try using vfs_mkdir. Just a thought.
702: */
703: static int create_default_group(struct dentry *parent,
704: struct config_group *group,
705: struct configfs_fragment *frag)
706: {
707: int ret;
708: struct configfs_dirent *sd;
709: /* We trust the caller holds a reference to parent */
710: struct dentry *child;
711:
712: if (!group->cg_item.ci_name)
713: group->cg_item.ci_name = group->cg_item.ci_namebuf;
714:
715: ret = -ENOMEM;
716: child = d_alloc_name(parent, group->cg_item.ci_name);
717: if (child) {
718: d_add(child, NULL);
719:
720: ret = configfs_attach_group(&group->cg_item, child, frag);
721: if (!ret) {
722: sd = child->d_fsdata;
723: sd->s_type |= CONFIGFS_USET_DEFAULT;
724: } else {
725: BUG_ON(d_inode(child));
726: d_drop(child);
727: }
728: dput(child);
729: }
730:
731: return ret;
732: }
SourceFile:fs/configfs/dir.c]
|
| 164/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 34203
output: 35
thoughts: 99
Thoughts: **Analyzing the Data Structure Creation Process**
Okay, so I'm tracing the data structure creation flow here, specifically within the ConfigFS framework. The initial assumption I had was that `create_default_group` directly invokes `config_item_get`, but that doesn't seem to be the case. I need to rethink the path it takes.
Now, `configfs_attach_group` seems like the more likely entry point. This function calls `configfs_attach_item`, which itself calls `configfs_create_dir`. This then leads to `configfs_make_dirent`, which is interesting. It's creating directory entries, which is a key part of ConfigFS's behavior.
Hold on, I see a possible connection through `configfs_attach_item` to `populate_attrs`. It seems there's a chance that `populate_attrs` *might* call `config_item_get`. That would make more sense in the overall architecture. I need to confirm this. The details around attribute population is critical to understanding how the data is actually accessed.
So, let's dive into the implementation of `populate_attrs` to understand this process fully. Let's see if that's where the critical linkage exists.
|
| 165/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:populate_attrs]
Results: map[SourceCode: 621: static int populate_attrs(struct config_item *item)
622: {
623: const struct config_item_type *t = item->ci_type;
624: const struct configfs_group_operations *ops;
625: struct configfs_attribute *attr;
626: struct configfs_bin_attribute *bin_attr;
627: int error = 0;
628: int i;
629:
630: if (!t)
631: return -EINVAL;
632:
633: ops = t->ct_group_ops;
634:
635: if (t->ct_attrs) {
636: for (i = 0; (attr = t->ct_attrs[i]) != NULL; i++) {
637: if (ops && ops->is_visible && !ops->is_visible(item, attr, i))
638: continue;
639:
640: error = configfs_create_file(item, attr);
641: if (error)
642: return error;
643: }
644: }
645: if (t->ct_bin_attrs) {
646: for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) {
647: if (ops && ops->is_bin_visible && !ops->is_bin_visible(item, bin_attr, i))
648: continue;
649:
650: error = configfs_create_bin_file(item, bin_attr);
651: if (error)
652: return error;
653: }
654: }
655:
656: return 0;
657: }
SourceFile:fs/configfs/dir.c]
|
| 166/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 35164
output: 37
thoughts: 28
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating Default Group Configuration in configfs**
Alright, hang on a second. I'm trying to trace the lifecycle of these default group configurations, and I've hit a bit of a snag. I was following the trail, and now I've lost the specific call to `config_item_get` that should be happening for default groups. I need to figure out where that reference count increase occurs.
Let's see... since we're dealing with default group removal, it's highly likely that the relevant code will be in `configfs_rmdir`. This is where the cleanup process for a directory in configfs would be triggered, including the default group configuration. That's the most logical place to start digging. Time to dive into that function and see what it reveals about the reference counting for those default settings. I bet the crucial detail about the reference counting of the config items associated with the default group removal must be handled during the cleanup of the directory tree.
|
| 167/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_rmdir]
Results: map[SourceCode:1469: static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
1470: {
1471: struct config_item *parent_item;
1472: struct config_item *item;
1473: struct configfs_subsystem *subsys;
1474: struct configfs_dirent *sd;
1475: struct configfs_fragment *frag;
1476: struct module *subsys_owner = NULL, *dead_item_owner = NULL;
1477: int ret;
1478:
1479: sd = dentry->d_fsdata;
1480: if (sd->s_type & CONFIGFS_USET_DEFAULT)
1481: return -EPERM;
1482:
1483: /* Get a working ref until we have the child */
1484: parent_item = configfs_get_config_item(dentry->d_parent);
1485: subsys = to_config_group(parent_item)->cg_subsys;
1486: BUG_ON(!subsys);
1487:
1488: if (!parent_item->ci_type) {
1489: config_item_put(parent_item);
1490: return -EINVAL;
1491: }
1492:
1493: /* configfs_mkdir() shouldn't have allowed this */
1494: BUG_ON(!subsys->su_group.cg_item.ci_type);
1495: subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
1496:
1497: /*
1498: * Ensure that no racing symlink() will make detach_prep() fail while
1499: * the new link is temporarily attached
1500: */
1501: do {
1502: struct dentry *wait;
1503:
1504: mutex_lock(&configfs_symlink_mutex);
1505: spin_lock(&configfs_dirent_lock);
1506: /*
1507: * Here's where we check for dependents. We're protected by
1508: * configfs_dirent_lock.
1509: * If no dependent, atomically tag the item as dropping.
1510: */
1511: ret = sd->s_dependent_count ? -EBUSY : 0;
1512: if (!ret) {
1513: ret = configfs_detach_prep(sd, &wait);
1514: if (ret)
1515: configfs_detach_rollback(sd);
1516: }
1517: spin_unlock(&configfs_dirent_lock);
1518: mutex_unlock(&configfs_symlink_mutex);
1519:
1520: if (ret) {
1521: if (ret != -EAGAIN) {
1522: config_item_put(parent_item);
1523: return ret;
1524: }
1525:
1526: /* Wait until the racing operation terminates */
1527: inode_lock(d_inode(wait));
1528: inode_unlock(d_inode(wait));
1529: dput(wait);
1530: }
1531: } while (ret == -EAGAIN);
1532:
1533: frag = sd->s_frag;
1534: if (down_write_killable(&frag->frag_sem)) {
1535: spin_lock(&configfs_dirent_lock);
1536: configfs_detach_rollback(sd);
1537: spin_unlock(&configfs_dirent_lock);
1538: config_item_put(parent_item);
1539: return -EINTR;
1540: }
1541: frag->frag_dead = true;
1542: up_write(&frag->frag_sem);
1543:
1544: /* Get a working ref for the duration of this function */
1545: item = configfs_get_config_item(dentry);
1546:
1547: /* Drop reference from above, item already holds one. */
1548: config_item_put(parent_item);
1549:
1550: if (item->ci_type)
1551: dead_item_owner = item->ci_type->ct_owner;
1552:
1553: if (sd->s_type & CONFIGFS_USET_DIR) {
1554: configfs_detach_group(dentry);
1555:
1556: mutex_lock(&subsys->su_mutex);
1557: client_disconnect_notify(parent_item, item);
1558: unlink_group(to_config_group(item));
1559: } else {
1560: configfs_detach_item(dentry);
1561:
1562: mutex_lock(&subsys->su_mutex);
1563: client_disconnect_notify(parent_item, item);
1564: unlink_obj(item);
1565: }
1566:
1567: client_drop_item(parent_item, item);
1568: mutex_unlock(&subsys->su_mutex);
1569:
1570: /* Drop our reference from above */
1571: config_item_put(item);
1572:
1573: module_put(dead_item_owner);
1574: module_put(subsys_owner);
1575:
1576: return 0;
1577: }
SourceFile:fs/configfs/dir.c]
|
| 168/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 37080
output: 35
thoughts: 45
Thoughts: Okay, let's see...
**Deconstructing the ConfigFS Removal Process**
Right, so I'm tracing the removal of a directory in `configfs`. It looks like `configfs_rmdir` is the starting point. That function calls `configfs_detach_group`, which is interesting. And *that* in turn calls `configfs_detach_item` and then, crucially, `detach_groups`. I need to understand how the system detaches these groups, as that likely affects how all the items are removed. Let's delve into that `detach_groups` function. It's the lynchpin, it would seem, of the entire removal sequence, so that's where my focus needs to be now.
|
| 169/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:detach_groups]
Results: map[SourceCode: 664: static void detach_groups(struct dentry *dentry)
665: {
666: struct dentry *child;
667: struct configfs_dirent *parent_sd;
668: struct configfs_dirent *sd, *next;
669:
670: parent_sd = dentry->d_fsdata;
671: spin_lock(&configfs_dirent_lock);
672: for (sd = next_dirent(parent_sd, NULL); sd; sd = next) {
673: next = next_dirent(parent_sd, sd);
674: if (!(sd->s_type & CONFIGFS_USET_DEFAULT))
675: continue;
676:
677: child = dget(sd->s_dentry);
678: spin_unlock(&configfs_dirent_lock);
679:
680: inode_lock(d_inode(child));
681:
682: configfs_detach_group(child);
683: d_inode(child)->i_flags |= S_DEAD;
684: dont_mount(child);
685:
686: inode_unlock(d_inode(child));
687:
688: d_delete(child);
689: dput(child);
690: spin_lock(&configfs_dirent_lock);
691: }
692: spin_unlock(&configfs_dirent_lock);
693: }
SourceFile:fs/configfs/dir.c]
|
| 170/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 37788
output: 38
thoughts: 52
Thoughts: Here's my summary, as the expert:
**Tracing the ConfigFS Detachment Process**
Okay, hold on a second. I need to refresh my understanding of how this detachment is handled within ConfigFS. My mental model is telling me that `detach_groups` is going to call `configfs_detach_group` for the default group. I better confirm that assumption. Yep, that's what's happening. Alright, `configfs_detach_group` then calls `configfs_detach_item`. Interesting. I should probably quickly check the implementation of `configfs_detach_item` to fully grasp the low-level details of that operation before I dig any deeper. No point in wasting time with the high level if I'm missing some small detail.
|
| 171/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_detach_item]
Results: map[SourceCode: 835: /* Caller holds the mutex of the item's inode */
836: static void configfs_detach_item(struct dentry *dentry)
837: {
838: detach_attrs(dentry);
839: configfs_remove_dir(dentry);
840: }
SourceFile:fs/configfs/dir.c]
|
| 172/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 38161
output: 38
thoughts: 19
Thoughts: Here's my attempt at summarizing that thought process, tailored for an expert audience:
**Kernel Subsystem Debugging - A ConfigFS Question**
Okay, so I'm digging into this ConfigFS code, trying to understand the lifecycle of these items. I was just looking at `configfs_remove_dir`, and something jumped out at me. Wait a second... `configfs_remove_dir`? That function, which is supposed to be *removing* a directory in the ConfigFS hierarchy... is it actually calling `config_item_put`? That's not what I initially expected. I usually think about directory removal as a filesystem-level operation like a kind of rmdir, not the final release of the resource. `config_item_put` seems to handle reference counting and resource deallocation. I need to understand this better. Is this where the actual cleanup of the ConfigFS item happens? Or is `configfs_remove_dir` handling a higher level of operations? I really need to check the call chain and double-check how these functions interact.
|
| 173/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_remove_dir]
Results: map[SourceCode: 410: static void configfs_remove_dir(struct dentry *d)
411: {
412: struct dentry * parent = dget(d->d_parent);
413:
414: configfs_remove_dirent(d);
415:
416: if (d_really_is_positive(d)) {
417: if (unlikely(simple_rmdir(d_inode(parent), d)))
418: pr_warn("remove_dir (%pd): attributes remain", d);
419: }
420:
421: pr_debug(" o %pd removing done (%d)\n", d, d_count(d));
422:
423: dput(parent);
424: }
SourceFile:fs/configfs/dir.c]
|
| 174/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 38696
output: 39
thoughts: 20
Thoughts: Here's my take on those thoughts:
**A Quick Cross-Check**
Okay, so I'm digging into this `configfs_remove_dirent` function and something's pinging my radar. Hold on... it *calls* `config_item_put`? That's a key interaction. I need to make sure I understand the full lifecycle here. Is this an intentional cleanup step, or potentially a hidden resource leak point? `config_item_put` is how we decrement the reference count, right? I need to quickly verify the intended semantics and ensure there are no potential race conditions or double-frees related to this put. I should trace the context and other call paths that lead to an item put. I'll need to double check the reference counting strategy that's in play here. Is the timing correct? Let's verify and be certain.
|
| 175/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_remove_dirent]
Results: map[SourceCode: 271: static void configfs_remove_dirent(struct dentry *dentry)
272: {
273: struct configfs_dirent *sd = dentry->d_fsdata;
274:
275: if (!sd)
276: return;
277: spin_lock(&configfs_dirent_lock);
278: list_del_init(&sd->s_sibling);
279: spin_unlock(&configfs_dirent_lock);
280: configfs_put(sd);
281: }
SourceFile:fs/configfs/dir.c]
|
| 176/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 39133
output: 36
thoughts: 17
Thoughts: Here's my attempt at summarizing that thought process, tailored for an expert audience:
**Digging into the configfs Internals**
Okay, hold on a second. I'm tracing through the `configfs` code, and I just saw a call to `configfs_put`. My mental model of the reference counting in `configfs` had `configfs_put` as a direct release for the filesystem objects, but now I'm seeing it go down a level and call `config_item_put`. That's unexpected, and I need to check my understanding. It makes me question where the primary ownership lies and how the refcounting is actually implemented in this part of the kernel. Is `configfs` simply a higher-level interface that relies on lower-level `config_item` to be deallocated? I need to review the architecture documentation to confirm this, but right now I'm processing this newly surfaced detail.
|
| 177/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_put]
Results: map[SourceCode: 152: static inline void configfs_put(struct configfs_dirent * sd)
153: {
154: WARN_ON(!atomic_read(&sd->s_count));
155: if (atomic_dec_and_test(&sd->s_count))
156: release_configfs_dirent(sd);
157: }
SourceFile:fs/configfs/configfs_internal.h]
|
| 178/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 39518
output: 39
thoughts: 13
Thoughts: Here's a summary of my thought process, as requested:
**Checking `release_configfs_dirent`**
Okay, let's dive in. The task at hand is to understand and potentially work with the `release_configfs_dirent` function. My initial thought process is to establish its purpose within the broader scope of ConfigFS. What exactly is this function designed to do? My expert knowledge immediately leans towards the idea that it's handling the cleanup or relinquishing of a directory entry within the ConfigFS filesystem. Likely related to reference counting or memory management. I want to quickly verify my initial assumption. I'm already anticipating considerations around locking mechanisms if this is a critical cleanup function, and I'll keep this front of mind. I'll need to remember to check for any potential race conditions and the context in which this function is invoked. Is it called during unmount operations? Is it called when a ConfigFS object is destroyed? Understanding the calling context is paramount to understanding its implications for concurrency and overall system stability. I also need to ascertain what parameters it takes to be sure of its intent, and ensure that I know if these parameters are used. From there, I'll need to figure out what happens, such as if it frees memory, decrements reference counters, or updates other internal data structures. This is where the core logic of the function lies. I'll want to check if the implementation is optimized in any way, maybe using inline functions. And of course, I'll pay close attention to potential error handling and any return values, to understand if there's any important status information this function passes back up the call chain.
|
| 179/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:release_configfs_dirent]
Results: map[SourceCode: 134: static inline void release_configfs_dirent(struct configfs_dirent * sd)
135: {
136: if (!(sd->s_type & CONFIGFS_ROOT)) {
137: kfree(sd->s_iattr);
138: put_fragment(sd->s_frag);
139: kmem_cache_free(configfs_dir_cachep, sd);
140: }
141: }
SourceFile:fs/configfs/configfs_internal.h]
|
| 180/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 40079
output: 40
thoughts: 129
Thoughts: **Investigating the `config_item_put` Usage in ConfigFS Default Group Removal**
Okay, so I'm trying to wrap my head around the memory management in this `configfs` context. Specifically, I'm focusing on how default groups are handled. First, I need to figure out where `config_item_put` is being called, because that's crucial for releasing resources.
Right, I see that `configfs_rmdir` *does* call `config_item_put(item)` at the very end. That's for explicit `rmdir` operations. But that's not quite what I'm looking for regarding default groups.
Aha! `detach_groups` handles the detach operation for default groups, and it calls `configfs_detach_group`. But, and here's the catch, I don't see `config_item_put` being called directly within `detach_groups` after `configfs_detach_group`! This is where things get interesting.
So, if `detach_groups` doesn't call `config_item_put`, how in the world is the memory for the default group actually being freed? It's a fundamental question. This implies that there is a different process responsible for the memory deallocation.
I'd better check `configfs_remove_default_groups` now. That feels like the next logical place to look.
|
| 181/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_remove_default_groups]
Results: map[SourceCode: 748: void configfs_remove_default_groups(struct config_group *group)
749: {
750: struct config_group *g, *n;
751:
752: list_for_each_entry_safe(g, n, &group->default_groups, group_entry) {
753: list_del(&g->group_entry);
754: config_item_put(&g->cg_item);
755: }
756: }
SourceFile:fs/configfs/dir.c]
|
| 182/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 40723
output: 40
thoughts: 59
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience:
**Default Group Handling in ConfigFS: A Quick Puzzle**
Right, so I'm digging into `configfs_remove_default_groups`. The first thing that pops out is that it's meticulously calling `config_item_put` for each default group. That's good, expected behavior for releasing a reference, but it immediately raises a question in my mind. Where are those initial references coming from? When exactly are we *getting* those references in the first place? It's the standard ref-counting dance.
Let me refresh my memory of the counterpart function. Let's trace that logic back to `configfs_add_default_group`. I'll need to go over the calling context there to confirm my understanding of the reference count management. I'm probably missing a subtlety about how the initial ref is acquired and when these groups are created, and `configfs_add_default_group` should explain this.
|
| 183/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/configfs.h Name:configfs_add_default_group]
Results: map[SourceCode: 109: static inline void configfs_add_default_group(struct config_group *new_group,
110: struct config_group *group)
111: {
112: list_add_tail(&new_group->group_entry, &group->default_groups);
113: }
SourceFile:include/linux/configfs.h]
|
| 184/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 41155
output: 46
thoughts: 236
Thoughts: **Analyzing a Potential ConfigFS Leak**
Okay, so I'm digging into this `configfs` code, and something's bugging me. I was reviewing how the default group is handled, and I just realized something. `configfs_add_default_group` doesn't seem to increment the reference count of the config item. But, hold on a second... `config_group_init_type_name` *does* call `config_item_init_type_name`, and that function *does* set the refcount to 1! So, the default group is created with a refcount of 1 initially.
Now, when `configfs_remove_default_groups` is called, it correctly calls `config_item_put`, which should decrement the refcount to 0, and that's when the release function should be triggered, right? But if `configfs_remove_default_groups` *isn't* called... then that default group's refcount never gets dropped to zero, and that sounds like a classic leak!
Okay, let's step back a moment. I need to be sure. Let's look at `f_mass_storage.c` as a good example. I know that uses `configfs_add_default_group` for `lun.0`. But... it *doesn't* call `configfs_remove_default_groups`! So why isn't `lun.0` leaking? That doesn't make any sense. I'm going to have to check `fsg_free_inst` again very closely.
|
| 185/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_free_inst]
Results: map[SourceCode:3485: static void fsg_free_inst(struct usb_function_instance *fi)
3486: {
3487: struct fsg_opts *opts;
3488:
3489: opts = fsg_opts_from_func_inst(fi);
3490: fsg_common_release(opts->common);
3491: kfree(opts);
3492: }
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 186/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 41935
output: 46
thoughts: 136
Thoughts: **Memory Management Quandary**
Okay, so I'm looking at this code and I see `opts` being freed with `kfree(opts)`. Seems straightforward. But, hold on a second... `opts->lun0` is embedded directly within `opts`. Right, so `kfree(opts)` *should* also free `opts->lun0` automatically. That makes sense.
But what about the configfs item? Is it detached from opts? My concern is that if `configfs_remove_default_groups` isn't called, then `lun.0`'s refcount will never hit zero. And if that refcount never reaches zero, then its associated release function won't be called, which could lead to a leak or dangling pointer.
Hold on a second, does `lun.0` even *have* a release function? I'd better check the definition of `fsg_lun_type` to make absolutely sure. I need to understand what resources that lun is holding to see if this is actually a problem or if my fears are unfounded. Okay, let's look at `fsg_lun_type`.
|
| 187/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_lun_type]
Results: map[SourceCode:3284: static const struct config_item_type fsg_lun_type = {
3285: .ct_item_ops = &fsg_lun_item_ops,
3286: .ct_attrs = fsg_lun_attrs,
3287: .ct_owner = THIS_MODULE,
3288: };
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 188/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 42493
output: 48
thoughts: 14
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary:
**Diving into `fsg_lun_item_ops`**
Alright, time to dig in. The immediate thing that jumps out is that I need to check `fsg_lun_item_ops`. This suggests we're likely dealing with something related to LUN operations within an FSG context. My gut tells me this is probably where the core logic for handling reads, writes, and potentially other interactions with the storage device resides, but it's not a certainty until I've seen the code. I need to understand how the file system interacts with, or uses, the functions this struct contains. Are we talking about direct I/O, buffered I/O, or something more complex like scatter-gather operations? What other structs and functions does it call? Knowing the specifics of the filesystem in question would also be useful context. I'll need to focus on what methods it provides, if there's any sophisticated locking employed and what data structures this `fsg_lun_item_ops` uses, especially in regards to its interaction with the underlying block device. I need to be thinking about potential performance bottlenecks, areas for optimization, and any possible race conditions or data consistency issues within this critical component. Let's see what we've got.
|
| 189/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_lun_item_ops]
Results: map[SourceCode:3164: static const struct configfs_item_operations fsg_lun_item_ops = {
3165: .release = fsg_lun_attr_release,
3166: };
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 190/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 42937
output: 48
thoughts: 14
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing `fsg_lun_attr_release`**
Alright, let's dive into this `fsg_lun_attr_release` function. What are the key aspects I need to immediately consider? I need to analyze how this function interacts with the overall LUN attribute management system. Specifically, is this a critical point of potential resource leaks or race conditions? I need to quickly recall the data structures involved—is it interacting directly with the LUN object itself, or are there intermediary attribute containers? I will need to consider whether it's dealing with reference counting, and if so, how. Are there any known areas of concern with the underlying SCSI driver or file system interaction related to this release? I'm thinking about the implications of failure scenarios within this function. What error handling is in place, and are there potential scenarios where attributes might be partially released or not released at all, leading to system instability down the line? I need to look for any cross-thread interactions or locking primitives used within this function or those functions called by it. Lastly, I'll need to refresh my memory on any recent patches or changes that have touched this area, so I can ensure this function is still performing as intended.
|
| 191/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_lun_attr_release]
Results: map[SourceCode:3156: static void fsg_lun_attr_release(struct config_item *item)
3157: {
3158: struct fsg_lun_opts *lun_opts;
3159:
3160: lun_opts = to_fsg_lun_opts(item);
3161: kfree(lun_opts);
3162: }
SourceFile:drivers/usb/gadget/function/f_mass_storage.c]
|
| 192/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 43410
output: 44
thoughts: 63
Thoughts: **A Moment of Doubt, Then Clarity**
Okay, hold on a second! I'm seeing `fsg_lun_attr_release` being called, and it's calling `kfree(lun_opts)`! Alarm bells are starting to ring because I was pretty sure `lun.0` was embedded within `opts` structure, but if `opts` is freed, then so is `lun.0`, right? Wait... no, hold on. Is my understanding of the memory layout incorrect? Maybe `lun.0` *isn't* embedded within `opts`? Before I start down the wrong path, I need to verify how `fsg_opts` is structured to confirm my mental model. Time to double-check that part of the code.
|
| 193/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_mass_storage.c Name:fsg_opts]
Results: map[SourceCode: 70: struct fsg_opts {
71: struct fsg_common *common;
72: struct usb_function_instance func_inst;
73: struct fsg_lun_opts lun0;
74: struct config_group *default_groups[2];
75: bool no_configfs; /* for legacy gadgets */
76:
77: /*
78: * Read/write access to configfs attributes is handled by configfs.
79: *
80: * This is to protect the data from concurrent access by read/write
81: * and create symlink/remove symlink.
82: */
83: struct mutex lock;
84: int refcnt;
85: };
SourceFile:drivers/usb/gadget/function/f_mass_storage.h]
|
| 194/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 43923
output: 48
thoughts: 353
Thoughts: **My Analysis of a Potential USB Gadget Driver Issue**
Okay, let's break this down. It looks like there's a problem with how `lun0` is being handled in the `fsg_opts` structure. I see that `lun0` is embedded directly within `fsg_opts`, which means when `fsg_lun_attr_release` is called, it might try to free an interior pointer. That's a red flag! The code calls `kfree(lun_opts)` where `lun_opts` is essentially pointing to `&opts->lun0`. I need to verify that `lun0` is even being released correctly.
My initial thought is that the leak is prevented because the refcount is never dropped to zero. However, `opts` is eventually freed in `fsg_free_inst` with `kfree(opts)`. So, the entire memory region gets freed. The memory itself isn't *leaked* in the traditional sense because it's being freed. But the refcount associated with the configfs item *is* being leaked. This is especially problematic because the configfs item remains in its list due to `configfs_remove_default_groups` never being called.
But if `opts` is being freed, then even the list head that contains the configfs item should be freed as well. Ah, since the *whole* `opts` is being freed, the list head gets freed as well. This makes me consider a potential problem: for `f_midi2`, `ep_opts`, and `block_opts` are dynamically allocated! Therefore, if their refcounts are leaked, the memory itself will be leaked! I need to investigate this further. I'll need to check if there are other commits in similar USB gadget functions that fix this exact issue of missing `configfs_remove_default_groups` calls. This could be a recurring pattern.
|
| 195/2 |
2026/07/30 14:17 |
tool |
git-log |
0m
Args: map[CodeRegexp:configfs_remove_default_groups Count:20 PathPrefix:drivers/usb/ Since:10 years]
Results:
|
| 196/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 44743
output: 48
thoughts: 101
Thoughts: **USB Driver Deep Dive**
Okay, hold on a second. I was just reviewing the USB driver code and I noticed something odd. `configfs_remove_default_groups` seems to be completely unused within the entire `drivers/usb/` directory. That's a bit surprising, I figured it would be used at least somewhere, given its name and purpose.
Alright, let's back up a bit. If we're not removing default groups, are we at least *adding* any? I'll need to check the codebase to see where `configfs_add_default_group` *is* actually being used within the `drivers/usb/` directory.
Okay, after a quick grep, it looks like `f_mass_storage.c`, `f_midi2.c`, `uvc_configfs.c`, and even `configfs.c` are using the add function. Interesting. Let's delve into `uvc_configfs.c` first. It's the most likely one to have something significant. I want to understand *why* they need to add default groups in that specific module.
|
| 197/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_config_create_group]
Results: map[SourceCode: 156: static int uvcg_config_create_group(struct config_group *parent,
157: const struct uvcg_config_group_type *type)
158: {
159: struct config_group *group;
160:
161: group = kzalloc_obj(*group);
162: if (!group)
163: return -ENOMEM;
164:
165: config_group_init_type_name(group, type->name, &type->type);
166: configfs_add_default_group(group, parent);
167:
168: return uvcg_config_create_children(group, type);
169: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 198/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 45383
output: 48
thoughts: 46
Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing:
**Memory Lane: Group Management in `uvcg_config`**
Alright, hang on a second. I'm looking at this `uvcg_config_create_group` function and it's starting to trigger some alarms. Specifically, I'm noting that it's dynamically allocating `group` and adding it to the default group. That's a classic setup for potential memory leaks, isn't it? My immediate question is: *how* is this allocated memory being freed? We can't just let it float around out there.
Let's dive deeper and track its lifecycle. My instinct tells me the cleanup will be in the `uvcg_config_remove_children` function. It's the logical place to look; this is where we'd expect the dismantling of the structure, and hopefully, the deallocation of any dynamically allocated memory associated with the created groups. Time to confirm my suspicion... let's see what's happening in there. I'm going to follow the logic and confirm my suspicions.
|
| 199/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/uvc_configfs.c Name:uvcg_config_remove_children]
Results: map[SourceCode: 171: static void uvcg_config_remove_children(struct config_group *group)
172: {
173: struct config_group *child, *n;
174:
175: list_for_each_entry_safe(child, n, &group->default_groups, group_entry) {
176: list_del(&child->group_entry);
177: uvcg_config_remove_children(child);
178: config_item_put(&child->cg_item);
179: }
180: }
SourceFile:drivers/usb/gadget/function/uvc_configfs.c]
|
| 200/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 45914
output: 40
thoughts: 70
Thoughts: Here's my summary, as the expert:
**Refactoring Opportunity Spotted: Simplifying Default Group Removal**
Okay, so I'm looking at this `uvcg_config_remove_children` function, and it's doing something that immediately rings a bell. It's manually looping through `default_groups`, removing each one, and then calling `config_item_put` on them. That whole dance… wait a minute! This is *exactly* the responsibility of `configfs_remove_default_groups`. I know I've seen that function before, so let me quickly double-check the implementation of `configfs_remove_default_groups` again. It has to be more efficient. This is likely a prime candidate for refactoring to reduce duplicated code.
|
| 201/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_remove_default_groups]
Results: map[SourceCode: 748: void configfs_remove_default_groups(struct config_group *group)
749: {
750: struct config_group *g, *n;
751:
752: list_for_each_entry_safe(g, n, &group->default_groups, group_entry) {
753: list_del(&g->group_entry);
754: config_item_put(&g->cg_item);
755: }
756: }
SourceFile:fs/configfs/dir.c]
|
| 202/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 46360
output: 39
thoughts: 36
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Recursive Configuration Removal Confirmed - Quick Verification Required**
Yes! My initial hunch is correct, `uvcg_config_remove_children` indeed handles the recursive cleanup of the USB gadget configuration. That's precisely what I was hoping to see. It’s the standard approach, and it's nice to have a dedicated function for it.
However, I'm always cautious, especially with something so fundamental to device driver behavior. Before I commit to assuming this is *the* right function, a quick sanity check is warranted. Let's pull up `drivers/usb/gadget/configfs.c` for a rapid review. I want to confirm my understanding aligns with the actual implementation and make sure there are no obvious omissions or potential gotchas in its operation. Gotta confirm the fundamentals before building on this.
|
| 203/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:gadgets_drop]
Results: map[SourceCode:2058: static void gadgets_drop(struct config_group *group, struct config_item *item)
2059: {
2060: config_item_put(item);
2061: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 204/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 46716
output: 39
thoughts: 12
Thoughts: Here's my take on those thoughts:
**Default Groups and the `gadgets_make` Function**
Okay, so I'm processing this... it just clicked. The `gadgets_make` function – the one we're using to instantiate these new gadget objects – it automatically adds default groups, doesn't it? I was so focused on the individual gadget creation process that I totally missed that subtle but crucial detail in its behavior. That's actually pretty convenient, now that I think about it. It handles a common initialization step without explicitly calling out each gadget's group affiliation in the creation step. That's pretty clever, actually; it keeps the instantiation code cleaner and reduces redundancy. I'll need to remember that the next time I'm digging into a similar system; this default group assignment is going to save me some lines, and some mental overhead.
|
| 205/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:gadgets_make]
Results: map[SourceCode:1988: static struct config_group *gadgets_make(
1989: struct config_group *group,
1990: const char *name)
1991: {
1992: struct gadget_info *gi;
1993:
1994: gi = kzalloc_obj(*gi);
1995: if (!gi)
1996: return ERR_PTR(-ENOMEM);
1997:
1998: config_group_init_type_name(&gi->group, name, &gadget_root_type);
1999:
2000: config_group_init_type_name(&gi->functions_group, "functions",
2001: &functions_type);
2002: configfs_add_default_group(&gi->functions_group, &gi->group);
2003:
2004: config_group_init_type_name(&gi->configs_group, "configs",
2005: &config_desc_type);
2006: configfs_add_default_group(&gi->configs_group, &gi->group);
2007:
2008: config_group_init_type_name(&gi->strings_group, "strings",
2009: &gadget_language_strings_type);
2010: configfs_add_default_group(&gi->strings_group, &gi->group);
2011:
2012: config_group_init_type_name(&gi->os_desc_group, "os_desc",
2013: &os_desc_type);
2014: configfs_add_default_group(&gi->os_desc_group, &gi->group);
2015:
2016: config_group_init_type_name(&gi->webusb_group, "webusb",
2017: &webusb_type);
2018: configfs_add_default_group(&gi->webusb_group, &gi->group);
2019:
2020: gi->composite.bind = configfs_do_nothing;
2021: gi->composite.unbind = configfs_do_nothing;
2022: gi->composite.suspend = NULL;
2023: gi->composite.resume = NULL;
2024: gi->composite.max_speed = USB_SPEED_SUPER_PLUS;
2025:
2026: spin_lock_init(&gi->spinlock);
2027: mutex_init(&gi->lock);
2028: INIT_LIST_HEAD(&gi->string_list);
2029: INIT_LIST_HEAD(&gi->available_func);
2030:
2031: composite_init_dev(&gi->cdev);
2032: gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
2033: gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
2034: gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());
2035:
2036: gi->composite.gadget_driver = configfs_driver_template;
2037:
2038: gi->composite.gadget_driver.driver.name = kasprintf(GFP_KERNEL,
2039: "configfs-gadget.%s", name);
2040: if (!gi->composite.gadget_driver.driver.name)
2041: goto err;
2042:
2043: gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
2044: gi->composite.name = gi->composite.gadget_driver.function;
2045:
2046: if (!gi->composite.gadget_driver.function)
2047: goto out_free_driver_name;
2048:
2049: return &gi->group;
2050:
2051: out_free_driver_name:
2052: kfree(gi->composite.gadget_driver.driver.name);
2053: err:
2054: kfree(gi);
2055: return ERR_PTR(-ENOMEM);
2056: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 206/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 48127
output: 43
thoughts: 33
Thoughts: Okay, let me think this through.
**Analyzing Group Management in `gadgets_make` and Related Functions**
Alright, so `gadgets_make` automatically adds these default groups, and that's the standard practice. The key question now becomes: Where and *how* are those groups subsequently removed, if at all? I need to understand the lifecycle of these default groups.
My immediate next step is to examine the `gadget_info_attr_release` function. That seems like the most logical place to start looking for the removal mechanism. It’s where information about the gadget would likely be released, which could include freeing up the default groups. I'll need to meticulously review the code within `gadget_info_attr_release` to see if there's group removal logic. I need to figure this out and confirm if there are any race conditions or unintended consequences.
|
| 207/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:gadget_info_attr_release]
Results: map[SourceCode: 400: static void gadget_info_attr_release(struct config_item *item)
401: {
402: struct gadget_info *gi = to_gadget_info(item);
403:
404: WARN_ON(!list_empty(&gi->cdev.configs));
405: WARN_ON(!list_empty(&gi->string_list));
406: WARN_ON(!list_empty(&gi->available_func));
407: kfree(gi->composite.gadget_driver.function);
408: kfree(gi->composite.gadget_driver.driver.name);
409: kfree(gi);
410: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 208/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 48614
output: 32
thoughts: 71
Thoughts: **Investigating a Potential Memory Leak**
Okay, hold on a second. I just realized something about how `gadget_info_attr_release` functions. It doesn't actually call `configfs_remove_default_groups`. That's unexpected. My immediate concern is that this could lead to a memory leak. I'm thinking, "Wait, is this code clean? Something isn't adding up".
So, the next question is, if `gadget_info_attr_release` isn't handling the cleanup of these default groups, where is it happening? Where is that function even being called? Is it possible that `configfs_remove_default_groups` is being invoked in another part of the codebase? I need to verify that; I want to be 100% sure. I'm going to revisit the implementation of `configfs_remove_default_groups` to confirm how its lifecycle is managed and figure out where the deallocation is actually happening.
|
| 209/2 |
2026/07/30 14:17 |
tool |
grepper |
0m
Error:
Args: map[Expression:configfs_remove_default_groups PathPrefix:drivers/usb/]
Results:
|
| 210/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 48941
output: 32
thoughts: 42
Thoughts: Okay, let's see. My first thought is: **Hang on a minute, `configfs_remove_default_groups` isn't actually employed within the `drivers/usb/` directory at all! That's a red flag.**
I need to circle back and double-check my assumptions. Let's revisit `uvcg_config_remove_children` and give it a fresh look. I must have missed something or misunderstood a dependency in the initial pass. I'm going to carefully re-examine that function, paying close attention to any call sites or implications within the USB subsystem. I need to ensure my understanding of the removal process is accurate, given that the expected function isn't used where I thought.
|
| 211/2 |
2026/07/30 14:17 |
tool |
grepper |
0m
Args: map[Expression:uvcg_config_remove_children PathPrefix:drivers/usb/]
Results: map[Output:drivers/usb/gadget/function/uvc_configfs.c=156=static int uvcg_config_create_group(struct config_group *parent,
--
drivers/usb/gadget/function/uvc_configfs.c-170-
drivers/usb/gadget/function/uvc_configfs.c:171:static void uvcg_config_remove_children(struct config_group *group)
drivers/usb/gadget/function/uvc_configfs.c-172-{
--
drivers/usb/gadget/function/uvc_configfs.c-176- list_del(&child->group_entry);
drivers/usb/gadget/function/uvc_configfs.c:177: uvcg_config_remove_children(child);
drivers/usb/gadget/function/uvc_configfs.c-178- config_item_put(&child->cg_item);
--
drivers/usb/gadget/function/uvc_configfs.c=3635=static void uvc_func_item_release(struct config_item *item)
--
drivers/usb/gadget/function/uvc_configfs.c-3638-
drivers/usb/gadget/function/uvc_configfs.c:3639: uvcg_config_remove_children(to_config_group(item));
drivers/usb/gadget/function/uvc_configfs.c-3640- usb_put_function_instance(&opts->func_inst);
]
|
| 212/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 49559
output: 47
thoughts: 709
Thoughts: **Analyzing Potential Memory Leaks in ConfigFS Gadgets**
Okay, so I'm digging into this `uvc_configfs.c` code and something just clicked. It looks like `uvc_func_item_release` handles cleaning up children, but I need to consider what happens in `gadget_info_attr_release`. Specifically, if this release function *doesn't* remove default groups, is there a leak? Let's trace this...
Right, `gadgets_make` adds these default groups to `gi->group`, which is the root of the gadget. When the gadget gets removed with `rmdir`, `configfs_rmdir` gets called. This then calls `configfs_detach_group`, which in turn calls `detach_groups`. *This* is where it gets interesting...`detach_groups` iterates over `default_groups`, and calls `configfs_detach_group`, but critically, it *doesn't* call `config_item_put` on them!
So, if `detach_groups` doesn't do the refcount decrement, who does? I have to go back to `configfs_rmdir` and work through the logic. If it gets called on a group, it drops the refcount of the group itself. But what about the default groups that were added using `configfs_add_default_group` and are in the `default_groups` list?
Let me think... when `populate_groups` is called, it creates the dentries and increments the refcount, right? Actually, hold on. I remember `create_default_group` calling `configfs_attach_group`, which calls `configfs_attach_item`, which calls `configfs_create_dir`, which calls `configfs_make_dirent`, which calls `configfs_new_dirent`. None of these *actually* increment the refcount of the default group's `config_item`!
This is a problem. If `populate_groups` doesn't increment it, then the initial refcount of a default group is just 1, from `config_group_init_type_name`. Now, when the *parent* group is released, its `release` function gets called. If *that* function doesn't call `config_item_put` on the default groups, their refcount remains at 1, and we have a leak.
Now I'm looking at `gadget_info_attr_release` again. Does it call `config_item_put` on its default groups? Since `gi->functions_group`, `gi->configs_group`, etc., are *embedded* in `struct gadget_info`, then when `kfree(gi)` happens, their memory is freed! Since they are embedded, they don't need individual freeing. But that doesn't fix it, their refcount is still at 1. Wait, if the refcount is always 1, is that a problem? If the default group doesn't have dynamically allocated memory, it isn't a memory leak per se, but it's an issue with the refcounting. The problem is dynamic allocation! So it *would* be a leak *if* the default groups allocated memory, like `f_midi2` does with `ep_opts` and `block_opts` using `kzalloc_obj()`. If that refcount never drops to 0, that's definitely a leak! I should check the `f_midi2_alloc_inst` function.
|
| 213/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_alloc_inst]
Results: map[SourceCode:2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
2656: return ERR_PTR(-ENOMEM);
2657:
2658: mutex_init(&opts->lock);
2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
2660: opts->info.process_ump = true;
2661: opts->info.static_block = true;
2662: opts->info.num_reqs = 32;
2663: opts->info.req_buf_size = 512;
2664:
2665: /* create the default ep */
2666: ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
2667: if (ret) {
2668: kfree(opts);
2669: return ERR_PTR(ret);
2670: }
2671:
2672: /* create the default block */
2673: ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
2674: if (ret) {
2675: kfree(ep_opts);
2676: kfree(opts);
2677: return ERR_PTR(ret);
2678: }
2679:
2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
2687: &f_midi2_ep_type);
2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
2689:
2690: config_group_init_type_name(&block_opts->group, "block.0",
2691: &f_midi2_block_type);
2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
2693:
2694: return &opts->func_inst;
2695: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 214/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 51909
output: 47
thoughts: 34
Thoughts: Okay, here's my interpretation of those thoughts, presented as a first-person expert summary:
**The Allocation Dilemma**
Ah, right, so *those* are dynamically allocated. `ep_opts` and `block_opts`... yes, they're definitely being allocated on the heap, not the stack. That's good to keep in mind, because that has significant implications for memory management. I have to be careful about when and how they're deallocated.
And then there's `f_midi2_free_inst`. It's crucial to remember that its sole responsibility is to free `opts`. I need to ensure that the lifetime of those option structures is managed correctly, and that the function handling deallocation understands the relationships between the data structures. Otherwise, I risk leaks or, even worse, using freed memory. I must double-check how these are linked to other data.
|
| 215/2 |
2026/07/30 14:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_free_inst]
Results: map[SourceCode:2636: static void f_midi2_free_inst(struct usb_function_instance *f)
2637: {
2638: struct f_midi2_opts *opts;
2639:
2640: opts = container_of(f, struct f_midi2_opts, func_inst);
2641:
2642: kfree(opts->info.iface_name);
2643: kfree(opts);
2644: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 216/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 52351
output: 44
thoughts: 221
Thoughts: **Analyzing a ConfigFS Memory Leak in the USB MIDI Gadget Driver**
Okay, so it looks like `ep_opts` and `block_opts` are leaking – not good. The immediate fix is clear: call `configfs_remove_default_groups` within `f_midi2_free_inst` and `f_midi2_ep_opts_release`. Seems straightforward enough, but the prompt wants more.
Right, the goal is to leverage past fixes for similar issues within this subsystem or specific files. It's about learning the patterns. So, I need to dig into the commit history of `drivers/usb/gadget/function/f_midi2.c` and potentially the broader `drivers/usb/gadget/` directory. I'm focusing on commits that address memory leaks, specifically those related to ConfigFS refcounting errors, or bugs manifesting in incorrect error paths. The aim is to identify established idioms, locking rules, or common patterns the developers have used when crafting fixes for these types of bugs. Hopefully, there will be lessons learned!
|
| 217/2 |
2026/07/30 14:17 |
tool |
git-log |
0m
Args: map[Count:20 MessageRegexps:[memory leak] PathPrefix:drivers/usb/gadget/ Since:3 years]
Results: map[Output:c8547c74988e usb: gadget: net2280: Fix double free in probe error path
e4f5ce990818 usb: gadget: f_eem: Fix memory leak in eem_unwrap
62783c30d78a USB: gadget: f_hid: Fix memory leak in hidg_bind error path
da1668997052 usb: gadget: f_midi: fix MIDI Streaming descriptor lengths
94d9bf671ae3 usb: gadget: f_tcm: Don't prepare BOT write request twice
]
|
| 218/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 52997
output: 27
thoughts: 32
Thoughts: Okay, here's my interpretation of those thoughts, summarized in the first person, assuming the context is something I'm intimately familiar with as an expert:
**Internal Code Review: Checking Key Hashes**
Right, let's just quickly zero in on `62783c30d78a` and `e4f5ce990818`. Those seem to be two critical hashes, likely related to either a critical configuration setting, code commit, or data record within the system. My gut tells me these could be key identifiers within the security log. Time to refresh my memory on what they likely represent, and quickly correlate it against known anomalies. Let's see if those correlate with events I have seen. I'm going to start with the audit logs. From there, I'll go to the build system, and then the repository. I'm hoping I can refresh my memory on these items very quickly.
|
| 219/2 |
2026/07/30 14:17 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 62783c30d78aecf9810dae46fd4d11420ad38b74
Author: Yuhao Jiang <danisjiang@gmail.com>
Date: Mon Jun 23 17:48:44 2025 +0800
USB: gadget: f_hid: Fix memory leak in hidg_bind error path
In hidg_bind(), if alloc_workqueue() fails after usb_assign_descriptors()
has successfully allocated the USB descriptors, the current error handling
does not call usb_free_all_descriptors() to free the allocated descriptors,
resulting in a memory leak.
Restructure the error handling by adding proper cleanup labels:
- fail_free_all: cleans up workqueue and descriptors
- fail_free_descs: cleans up descriptors only
- fail: original cleanup for earlier failures
This ensures that allocated resources are properly freed in reverse order
of their allocation, preventing the memory leak when alloc_workqueue() fails.
Fixes: a139c98f760ef ("USB: gadget: f_hid: Add GET_REPORT via userspace IOCTL")
Cc: stable@vger.kernel.org
Signed-off-by: Yuhao Jiang <danisjiang@gmail.com>
Link: https://lore.kernel.org/r/20250623094844.244977-1-danisjiang@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 97a62b926415..8e1d1e884050 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -1278,18 +1278,19 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (!hidg->workqueue) {
status = -ENOMEM;
- goto fail;
+ goto fail_free_descs;
}
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
status = cdev_device_add(&hidg->cdev, &hidg->dev);
if (status)
- goto fail_free_descs;
+ goto fail_free_all;
return 0;
-fail_free_descs:
+fail_free_all:
destroy_workqueue(hidg->workqueue);
+fail_free_descs:
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
]
|
| 220/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 54023
output: 26
thoughts: 17
Thoughts: Okay, here's my interpretation of that.
**Analyzing Commit e4f5ce990818**
Right, let's dive in. First, I need to check out commit `e4f5ce990818`. That hash tells me this is a specific snapshot in the codebase's history. I'm going to pull it up and immediately start thinking about context. What was happening around this commit in terms of development? What features were being worked on, what bug reports were open, and what part of the system does this touch? I'll probably start by looking at the commit message itself. Beyond the message, I'll need to immediately look at the diff. What files were changed, and how were they changed? Are we talking about a small, isolated fix, or a larger refactor that could have ripple effects? And I know a commit hash is a unique ID of a changeset.
|
| 221/2 |
2026/07/30 14:17 |
tool |
git-show |
0m
Args:
Results: map[Output:commit e4f5ce990818d37930cd9fb0be29eee0553c59d9
Author: Kuen-Han Tsai <khtsai@google.com>
Date: Mon Nov 3 20:17:59 2025 +0800
usb: gadget: f_eem: Fix memory leak in eem_unwrap
The existing code did not handle the failure case of usb_ep_queue in the
command path, potentially leading to memory leaks.
Improve error handling to free all allocated resources on usb_ep_queue
failure. This patch continues to use goto logic for error handling, as the
existing error handling is complex and not easily adaptable to auto-cleanup
helpers.
kmemleak results:
unreferenced object 0xffffff895a512300 (size 240):
backtrace:
slab_post_alloc_hook+0xbc/0x3a4
kmem_cache_alloc+0x1b4/0x358
skb_clone+0x90/0xd8
eem_unwrap+0x1cc/0x36c
unreferenced object 0xffffff8a157f4000 (size 256):
backtrace:
slab_post_alloc_hook+0xbc/0x3a4
__kmem_cache_alloc_node+0x1b4/0x2dc
kmalloc_trace+0x48/0x140
dwc3_gadget_ep_alloc_request+0x58/0x11c
usb_ep_alloc_request+0x40/0xe4
eem_unwrap+0x204/0x36c
unreferenced object 0xffffff8aadbaac00 (size 128):
backtrace:
slab_post_alloc_hook+0xbc/0x3a4
__kmem_cache_alloc_node+0x1b4/0x2dc
__kmalloc+0x64/0x1a8
eem_unwrap+0x218/0x36c
unreferenced object 0xffffff89ccef3500 (size 64):
backtrace:
slab_post_alloc_hook+0xbc/0x3a4
__kmem_cache_alloc_node+0x1b4/0x2dc
kmalloc_trace+0x48/0x140
eem_unwrap+0x238/0x36c
Fixes: 4249d6fbc10f ("usb: gadget: eem: fix echo command packet response issue")
Cc: stable@kernel.org
Signed-off-by: Kuen-Han Tsai <khtsai@google.com>
Link: https://patch.msgid.link/20251103121814.1559719-1-khtsai@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 6de81ea17274..edbbadad6138 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -477,8 +477,13 @@ static int eem_unwrap(struct gether *port,
req->complete = eem_cmd_complete;
req->zero = 1;
req->context = ctx;
- if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
+ if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC)) {
DBG(cdev, "echo response queue fail\n");
+ kfree(ctx);
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ dev_kfree_skb_any(skb2);
+ }
break;
case 1: /* echo response */
]
|
| 222/2 |
2026/07/30 14:17 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 55357
output: 38
thoughts: 36
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, and delivered as a first-person summary:
**Memory Leak Deep Dive: Familiar Terrain**
Right, these seem to be classic, run-of-the-mill memory leaks, nothing particularly exotic to begin with. The initial instinct is to dig deeper into the code. The immediate next step is to perform a thorough search. A good starting point would be to search for instances of `configfs_remove_default_groups` across the entire kernel's history. This targeted search strategy will likely yield prior attempts to address similar memory management issues. The idea is to quickly leverage existing solutions, identify recurring patterns, and hopefully, find a precedent that will help quickly solve the issue at hand. Understanding what fixes have been employed in the past will give valuable context and save me time.
|
| 223/2 |
2026/07/30 14:17 |
tool |
git-log |
10m
Error: git log timed out. Please specify a tighter search scope (e.g. by providing a PathPrefix).
Args: map[CodeRegexp:configfs_remove_default_groups Count:20 Since:10 years]
Results:
|
| 224/2 |
2026/07/30 14:27 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 55643
output: 48
thoughts: 22
Thoughts: Okay, here's my thought process summarized from that prompt, tailored for an expert audience:
**Investigating `configfs_remove_default_groups` in the Target Subsystem**
Alright, let's dive into this. The objective is to understand how `configfs_remove_default_groups` is used, and in what context within the target subsystem (`drivers/target/`). My immediate instinct is to pinpoint its exact location and purpose. I'm assuming, given the name, it likely handles the removal of some default group configuration related to a ConfigFS object. This could be critical for security or resource management, depending on the specific implementation within the target framework. It is likely that this function is part of the clean up process.
Knowing how the ConfigFS is used in the Target subsystem is crucial. I'm thinking about the different target drivers (LIO, etc.) and how they might utilize ConfigFS for exporting various target related attributes. Does this function interact with user-created targets, or only remove a default that the kernel provides? Understanding the dependencies is going to be key, and I’ll need to trace the call graph to see what objects or other kernel calls it is dependent on.
|
| 225/2 |
2026/07/30 14:27 |
tool |
git-log |
0m
Args: map[CodeRegexp:configfs_remove_default_groups Count:20 PathPrefix:drivers/target/ Since:10 years]
Results: map[Output:39ae3edda325 scsi: target: core: Make completion affinity configurable
]
|
| 226/2 |
2026/07/30 14:27 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 55998
output: 24
thoughts: 15
Thoughts: Okay, here's my attempt at summarizing the thought process, as requested, aimed at an expert user:
**Assessing the Significance of `39ae3edda325`**
Alright, let's take a look at this `39ae3edda325`. Immediately, the format screams something potentially crucial. Is it a hash? A version identifier? A commit hash? Based on the structure, it looks like a fairly standard hexadecimal string, so my initial assumption is that we are looking at something cryptographic or version based. I need more context to know for certain. I'm going to need to figure out the environment this identifier comes from to determine its significance. Is this within a log file? Code repository? Database? It could represent a file, a block of data, or a change.
If it *is* a hash, the next thing I'd want to determine is the hashing algorithm used. SHA-256? MD5? Something custom? That's going to dictate the level of trust I can place in its integrity and whether it's practically reversible or not. If it's a version identifier, I'll need to locate the metadata to determine the software or file it belongs to.
Ultimately, I need to understand *where* this value is coming from and what its intended purpose is. Once I figure that out, I can make a judgement on how important it is.
|
| 227/2 |
2026/07/30 14:27 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 39ae3edda325e9cf9e978c9788affe88231f3b34
Author: Mike Christie <michael.christie@oracle.com>
Date: Sat Feb 27 11:00:06 2021 -0600
scsi: target: core: Make completion affinity configurable
It may not always be best to complete the IO on same CPU as it was
submitted on. This commit allows userspace to configure it.
This has been useful for vhost-scsi where we have a single thread for
submissions and completions. If we force the completion on the submission
CPU we may be adding conflicts with what the user has setup in the lower
levels with settings like the block layer rq_affinity or the driver's IRQ
or softirq (the network's rps_cpus value) settings.
We may also want to set it up where the vhost thread runs on CPU N and does
its submissions/completions there, and then have LIO do its completion
booking on CPU M, but can't configure the lower levels due to issues like
using dm-multipath with lots of paths (the path selector can throw commands
all over the system because it's only taking into account latency/throughput
at its level).
The new setting is in:
/sys/kernel/config/target/$fabric/$target/param/cmd_completion_affinity
Writing:
-1 -> Gives the current default behavior of completing on the
submission CPU.
-2 -> Completes the cmd on the CPU the lower layers sent it to us from.
> 0 -> Completes on the CPU userspace has specified.
Link: https://lore.kernel.org/r/20210227170006.5077-26-michael.christie@oracle.com
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
index ee85602213f7..fc7edc04ee09 100644
--- a/drivers/target/target_core_fabric_configfs.c
+++ b/drivers/target/target_core_fabric_configfs.c
@@ -892,6 +892,7 @@ static void target_fabric_release_wwn(struct config_item *item)
struct target_fabric_configfs *tf = wwn->wwn_tf;
configfs_remove_default_groups(&wwn->fabric_stat_group);
+ configfs_remove_default_groups(&wwn->param_group);
tf->tf_ops->fabric_drop_wwn(wwn);
}
@@ -918,6 +919,57 @@ TF_CIT_SETUP(wwn_fabric_stats, NULL, NULL, NULL);
/* End of tfc_wwn_fabric_stats_cit */
+static ssize_t
+target_fabric_wwn_cmd_completion_affinity_show(struct config_item *item,
+ char *page)
+{
+ struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
+ param_group);
+ return sprintf(page, "%d\n",
+ wwn->cmd_compl_affinity == WORK_CPU_UNBOUND ?
+ SE_COMPL_AFFINITY_CURR_CPU : wwn->cmd_compl_affinity);
+}
+
+static ssize_t
+target_fabric_wwn_cmd_completion_affinity_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct se_wwn *wwn = container_of(to_config_group(item), struct se_wwn,
+ param_group);
+ int compl_val;
+
+ if (kstrtoint(page, 0, &compl_val))
+ return -EINVAL;
+
+ switch (compl_val) {
+ case SE_COMPL_AFFINITY_CPUID:
+ wwn->cmd_compl_affinity = compl_val;
+ break;
+ case SE_COMPL_AFFINITY_CURR_CPU:
+ wwn->cmd_compl_affinity = WORK_CPU_UNBOUND;
+ break;
+ default:
+ if (compl_val < 0 || compl_val >= nr_cpu_ids ||
+ !cpu_online(compl_val)) {
+ pr_err("Command completion value must be between %d and %d or an online CPU.\n",
+ SE_COMPL_AFFINITY_CPUID,
+ SE_COMPL_AFFINITY_CURR_CPU);
+ return -EINVAL;
+ }
+ wwn->cmd_compl_affinity = compl_val;
+ }
+
+ return count;
+}
+CONFIGFS_ATTR(target_fabric_wwn_, cmd_completion_affinity);
+
+static struct configfs_attribute *target_fabric_wwn_param_attrs[] = {
+ &target_fabric_wwn_attr_cmd_completion_affinity,
+ NULL,
+};
+
+TF_CIT_SETUP(wwn_param, NULL, NULL, target_fabric_wwn_param_attrs);
+
/* Start of tfc_wwn_cit */
static struct config_group *target_fabric_make_wwn(
@@ -937,6 +989,7 @@ static struct config_group *target_fabric_make_wwn(
if (!wwn || IS_ERR(wwn))
return ERR_PTR(-EINVAL);
+ wwn->cmd_compl_affinity = SE_COMPL_AFFINITY_CPUID;
wwn->wwn_tf = tf;
config_group_init_type_name(&wwn->wwn_group, name, &tf->tf_tpg_cit);
@@ -945,6 +998,10 @@ static struct config_group *target_fabric_make_wwn(
&tf->tf_wwn_fabric_stats_cit);
configfs_add_default_group(&wwn->fabric_stat_group, &wwn->wwn_group);
+ config_group_init_type_name(&wwn->param_group, "param",
+ &tf->tf_wwn_param_cit);
+ configfs_add_default_group(&wwn->param_group, &wwn->wwn_group);
+
if (tf->tf_ops->add_wwn_groups)
tf->tf_ops->add_wwn_groups(wwn);
return &wwn->wwn_group;
@@ -974,6 +1031,7 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf)
target_fabric_setup_discovery_cit(tf);
target_fabric_setup_wwn_cit(tf);
target_fabric_setup_wwn_fabric_stats_cit(tf);
+ target_fabric_setup_wwn_param_cit(tf);
target_fabric_setup_tpg_cit(tf);
target_fabric_setup_tpg_base_cit(tf);
target_fabric_setup_tpg_port_cit(tf);
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index 56f841fd7f04..a343bcfa2180 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -34,6 +34,7 @@ struct target_fabric_configfs {
struct config_item_type tf_discovery_cit;
struct config_item_type tf_wwn_cit;
struct config_item_type tf_wwn_fabric_stats_cit;
+ struct config_item_type tf_wwn_param_cit;
struct config_item_type tf_tpg_cit;
struct config_item_type tf_tpg_base_cit;
struct config_item_type tf_tpg_lun_cit;
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 1245c288d3bf..a75591c929c0 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -857,7 +857,8 @@ static bool target_cmd_interrupted(struct se_cmd *cmd)
/* May be called from interrupt context so must not sleep. */
void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
{
- int success;
+ struct se_wwn *wwn = cmd->se_sess->se_tpg->se_tpg_wwn;
+ int success, cpu;
unsigned long flags;
if (target_cmd_interrupted(cmd))
@@ -884,7 +885,13 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
INIT_WORK(&cmd->work, success ? target_complete_ok_work :
target_complete_failure_work);
- queue_work_on(cmd->cpuid, target_completion_wq, &cmd->work);
+
+ if (wwn->cmd_compl_affinity == SE_COMPL_AFFINITY_CPUID)
+ cpu = cmd->cpuid;
+ else
+ cpu = wwn->cmd_compl_affinity;
+
+ queue_work_on(cpu, target_completion_wq, &cmd->work);
}
EXPORT_SYMBOL(target_complete_cmd);
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index cf445c3a551a..d1f7d2a45354 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -944,11 +944,20 @@ static inline struct se_portal_group *param_to_tpg(struct config_item *item)
tpg_param_group);
}
+enum {
+ /* Use se_cmd's cpuid for completion */
+ SE_COMPL_AFFINITY_CPUID = -1,
+ /* Complete on current CPU */
+ SE_COMPL_AFFINITY_CURR_CPU = -2,
+};
+
struct se_wwn {
struct target_fabric_configfs *wwn_tf;
void *priv;
struct config_group wwn_group;
struct config_group fabric_stat_group;
+ struct config_group param_group;
+ int cmd_compl_affinity;
};
static inline void atomic_inc_mb(atomic_t *v)
]
|
| 228/2 |
2026/07/30 14:27 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 59252
output: 33
thoughts: 71
Thoughts: **Analyzing the Commit's Impact**
Okay, so this commit *introduces* a call to `configfs_remove_default_groups(&wwn->param_group)`. It's pretty straightforward: they added a new default group, `param_group`, and now they're cleaning it up properly. It's an *addition*, not a fix for a pre-existing oversight, which is a key distinction to keep in mind for future references.
I'm curious if this new functionality has been part of any broader discussions. I'll take a quick look through the commit messages to see if there's any mention of `configfs_remove_default_groups`. That might provide some context on the motivation and design decisions behind this change.
|
| 229/2 |
2026/07/30 14:28 |
tool |
git-log |
0m
Args: map[MessageRegexps:[configfs_remove_default_groups] Since:10 years]
Results: map[Output:3d2825c8c610 fs: dlm: fix configfs memory leak
]
|
| 230/2 |
2026/07/30 14:28 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 59552
output: 27
thoughts: 18
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing the Commit Hash**
Right, let's take a look at this commit hash, `3d2825c8c610`. Immediately, my mind starts sifting through the possibilities. Based on the format, it's a Git commit hash. The exact context, of course, is key. Is this in my current project? Or something I’m reviewing? That influences how deeply I'll dig initially.
My first thought is, where does this come from? Is it an issue someone reported? A colleague's code? My own recent work? That helps dictate the investigative strategy. I'll need to know the origin to really understand its significance.
Assuming I'm familiar with the codebase, my next step is likely to fire up the relevant tools: the Git client. I'll probably start with a `git show` to quickly glance at the commit message, the author, and the diff. That quick overview should give me the core context. If it's a simple, localized change, that might be enough to understand it. However, if the diff is complex or impacts a crucial area, I'll need to dig deeper.
I'll also keep in mind the potential impact. Are there associated tests? Any related pull requests? Does this relate to any known vulnerabilities or performance issues? I can't just look at the code in isolation.
If the hash isn't immediately familiar, I might perform a `git log --grep` search around it to understand where it appears in history, and maybe look at nearby commits that might give more context. This also helps with the context.
Ultimately, my goal is to grasp the intent of the change, how it was implemented, and the potential consequences. Armed with that knowledge, I can evaluate its correctness, its impact on the system, and its potential for improvement. It's a fundamental part of the review process, or just everyday software maintenance. Okay, time to check it out.
|
| 231/2 |
2026/07/30 14:28 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 3d2825c8c6105b0f36f3ff72760799fa2e71420e
Author: Alexander Aring <aahringo@redhat.com>
Date: Thu Aug 27 15:02:51 2020 -0400
fs: dlm: fix configfs memory leak
This patch fixes the following memory detected by kmemleak and umount
gfs2 filesystem which removed the last lockspace:
unreferenced object 0xffff9264f482f600 (size 192):
comm "dlm_controld", pid 325, jiffies 4294690276 (age 48.136s)
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 6e 6f 64 65 73 00 00 00 ........nodes...
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
backtrace:
[<00000000060481d7>] make_space+0x41/0x130
[<000000008d905d46>] configfs_mkdir+0x1a2/0x5f0
[<00000000729502cf>] vfs_mkdir+0x155/0x210
[<000000000369bcf1>] do_mkdirat+0x6d/0x110
[<00000000cc478a33>] do_syscall_64+0x33/0x40
[<00000000ce9ccf01>] entry_SYSCALL_64_after_hwframe+0x44/0xa9
The patch just remembers the "nodes" entry pointer in space as I think
it's created as subdirectory when parent "spaces" is created. In
function drop_space() we will lost the pointer reference to nds because
configfs_remove_default_groups(). However as this subdirectory is always
available when "spaces" exists it will just be freed when "spaces" will be
freed.
Signed-off-by: Alexander Aring <aahringo@redhat.com>
Signed-off-by: David Teigland <teigland@redhat.com>
diff --git a/fs/dlm/config.c b/fs/dlm/config.c
index 47f0b98b707f..f33a7e4ae917 100644
--- a/fs/dlm/config.c
+++ b/fs/dlm/config.c
@@ -221,6 +221,7 @@ struct dlm_space {
struct list_head members;
struct mutex members_lock;
int members_count;
+ struct dlm_nodes *nds;
};
struct dlm_comms {
@@ -430,6 +431,7 @@ static struct config_group *make_space(struct config_group *g, const char *name)
INIT_LIST_HEAD(&sp->members);
mutex_init(&sp->members_lock);
sp->members_count = 0;
+ sp->nds = nds;
return &sp->group;
fail:
@@ -451,6 +453,7 @@ static void drop_space(struct config_group *g, struct config_item *i)
static void release_space(struct config_item *i)
{
struct dlm_space *sp = config_item_to_space(i);
+ kfree(sp->nds);
kfree(sp);
}
]
|
| 232/2 |
2026/07/30 14:28 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 61074
output: 35
thoughts: 201
Thoughts: **Analyzing a Memory Leak Fix**
Okay, so I'm looking at commit `3d2825c8c610`, and it's flagged as fixing a memory leak in `fs/dlm/config.c`. My initial reaction is, "Right, let's see what they did." The commit message mentions `drop_space()` and a potential issue with `configfs_remove_default_groups()`. It's saying we could lose the pointer reference to `nds` because of that function, but since the subdirectory is "always available," it will just be freed when "spaces" is. My immediate thought is, "Okay, but let's break this down." `configfs_remove_default_groups()` implies a refcount drop on a default group, which then *should* trigger a release function. If that release function doesn't handle freeing the memory properly, we've got a leak.
Now I'm connecting the dots: in `fs/dlm/config.c`, `nds` is allocated within `make_space()` and is added as a default group. My immediate question is, "Did `nds` *have* a proper release function that would free its memory?" I don't see one in the original commit message. This sounds like an oversight. I'd better dive into that commit itself and confirm the fix. I'll check `fs/dlm/config.c` now and verify if the fix addresses the missing release function for `nds`.
|
| 233/2 |
2026/07/30 14:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/dlm/config.c Name:make_space]
Results: map[SourceCode: 478: static struct config_group *make_space(struct config_group *g, const char *name)
479: {
480: struct dlm_space *sp = NULL;
481: struct dlm_nodes *nds = NULL;
482:
483: sp = kzalloc_obj(struct dlm_space, GFP_NOFS);
484: nds = kzalloc_obj(struct dlm_nodes, GFP_NOFS);
485:
486: if (!sp || !nds)
487: goto fail;
488:
489: config_group_init_type_name(&sp->group, name, &space_type);
490:
491: config_group_init_type_name(&nds->ns_group, "nodes", &nodes_type);
492: configfs_add_default_group(&nds->ns_group, &sp->group);
493:
494: INIT_LIST_HEAD(&sp->members);
495: INIT_LIST_HEAD(&sp->members_gone);
496: mutex_init(&sp->members_lock);
497: sp->members_count = 0;
498: sp->nds = nds;
499: return &sp->group;
500:
501: fail:
502: kfree(sp);
503: kfree(nds);
504: return ERR_PTR(-ENOMEM);
505: }
SourceFile:fs/dlm/config.c]
|
| 234/2 |
2026/07/30 14:28 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 62089
output: 35
thoughts: 46
Thoughts: Okay, let me think this through.
**Analyzing Potential Memory Leaks in the ConfigFS Space Driver**
Alright, hang on a second. This `drop_space` function is calling `configfs_remove_default_groups(&sp->group);`. That looks normal enough, but something's nagging at me. It's the `nds` object. It seems that the release function doesn't actually free the `nds` structure. That's a red flag. I need to be absolutely certain of this. Let me double-check the definition and usage of the `nodes_type` variable. I want to see how `nds` is handled within that context. Perhaps I'm missing something, or maybe there's an internal reference that's not obvious. This could indicate a significant memory leak, and I need to identify where that object is expected to be freed. Time to dig in!
|
| 235/2 |
2026/07/30 14:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/dlm/config.c Name:nodes_type]
Results: map[SourceCode: 395: static const struct config_item_type nodes_type = {
396: .ct_group_ops = &nodes_ops,
397: .ct_owner = THIS_MODULE,
398: };
SourceFile:fs/dlm/config.c]
|
| 236/2 |
2026/07/30 14:28 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 62454
output: 47
thoughts: 126
Thoughts: **Analyzing the `nodes_type` Refcount Issue**
Okay, hold on a second... It just hit me: `nodes_type` doesn't *have* a release function! That's a problem. So, when `configfs_remove_default_groups` decrements the reference count to zero, `nds` isn't actually getting freed. That explains the issue. Right, so I see how they addressed it – they moved the freeing of `nds` to `release_space`, which is the release function of the *parent* object. That's a reasonable workaround, but I need to make sure I understand the implications for my current work.
Now, let's apply this to the current context. In my scenario, with `f_midi2.c`, I'm dealing with `f_midi2_ep_type` and `f_midi2_block_type`. The critical question is whether *these* types have appropriate release functions. I need to verify that those do the equivalent of what `release_space` did in their case, ensuring proper cleanup. Let's start by digging into `f_midi2_ep_type`.
|
| 237/2 |
2026/07/30 14:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_ep_type]
Results: map[SourceCode:2490: static const struct config_item_type f_midi2_ep_type = {
2491: .ct_item_ops = &f_midi2_ep_item_ops,
2492: .ct_group_ops = &f_midi2_ep_group_ops,
2493: .ct_attrs = f_midi2_ep_attrs,
2494: .ct_owner = THIS_MODULE,
2495: };
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 238/2 |
2026/07/30 14:28 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 63040
output: 49
thoughts: 16
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field and approaching a problem:
**Checking `f_midi2_ep_item_ops`**
Alright, time to dive in. My immediate thought is, "Okay, let's see what's going on with `f_midi2_ep_item_ops`." This function or module, whatever it is, is key to the current issue, or it's simply a place I know well and makes sense to check at this point. I need to get a clear picture of its behavior. Is it causing the bottleneck? Is it behaving as expected in the context of the rest of the system? I'll probably start by looking at its inputs and outputs, then trace its execution. Specifically, I'll be looking for any signs of performance bottlenecks or unexpected behavior related to how it handles MIDI data or interacts with the end-point item operations. Let's see if this answers the issue at hand.
|
| 239/2 |
2026/07/30 14:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_ep_item_ops]
Results: map[SourceCode:2481: static const struct configfs_item_operations f_midi2_ep_item_ops = {
2482: .release = f_midi2_ep_opts_release,
2483: };
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 240/2 |
2026/07/30 14:28 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 63413
output: 49
thoughts: 16
Thoughts: Here's a summary of my thought process, as if I were the one thinking it:
**Checking `f_midi2_ep_opts_release`**
Okay, time to take a look at `f_midi2_ep_opts_release`. I need to ensure this function is behaving as expected, especially in the current context. Let me quickly refresh my memory on what it's responsible for. Is it handling the release of endpoint options related to MIDI 2.0? Right, it's about cleaning up resources or signaling the end of a session, likely after a MIDI 2.0 exchange or configuration. I'll need to verify its interactions with the endpoint manager and resource allocation logic. Specifically, I'll be looking for potential memory leaks, dangling pointers, or incorrect state transitions after this function is called. Are all allocated resources properly freed? Is there correct error handling in case of failures during the release process? I need to check for any race conditions if multiple threads might be calling this function. Beyond the basics, I'll also glance at the surrounding code to ensure its called in all the necessary locations and not called in any unexpected ones. This is critical for maintaining stability and preventing unexpected behaviour in the system. Alright, let's start digging into the implementation and run a few tests.
|
| 241/2 |
2026/07/30 14:28 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_ep_opts_release]
Results: map[SourceCode:2472: static void f_midi2_ep_opts_release(struct config_item *item)
2473: {
2474: struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
2475:
2476: kfree(opts->info.ep_name);
2477: kfree(opts->info.product_id);
2478: kfree(opts);
2479: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 242/2 |
2026/07/30 14:28 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 63919
output: 38
thoughts: 200
Thoughts: **My Analysis of the Memory Leak in the MIDI Driver**
Okay, it clicks! I see now – `f_midi2_ep_opts_release` *does* properly free the `opts` structure. So if `configfs_remove_default_groups` is invoked, we're golden; the memory is released. The crux of the issue, however, is that `configfs_remove_default_groups` *isn't* being called when it should be. The memory leaks because of this.
Right, the prompt is guiding me towards a more systematic approach. It wants me to hunt for precedent. I need to dig into past commits within this subsystem or related files, looking for fixes to similar memory management issues. The goal is to identify common patterns, locking rules, or idioms that were employed to resolve analogous bugs. The prompt is specifically looking for similar missing locks, incorrect reference counting, or similar error path bugs in the code. I must now focus on finding commit messages related to `configfs_remove_default_groups`, or similar removal functions, specifically where they were introduced to address memory leaks or resource management problems. If I find nothing, I need to be clear and state that. This is the path to a solid and effective fix.
|
| 243/2 |
2026/07/30 14:28 |
tool |
git-log |
10m
Error: git log timed out. Please specify a tighter search scope (e.g. by providing a PathPrefix).
Args: map[CodeRegexp:configfs_remove_default_groups Count:20 Since:15 years]
Results:
|
| 244/2 |
2026/07/30 14:38 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 64455
output: 51
thoughts: 25
Thoughts: Here's my thought process, as requested:
**Navigating the USB Gadget Landscape for Configuration Removal**
Okay, so I need to find where the system handles the removal of default groups within the USB gadget configuration system. My instinct tells me this is likely handled within the `drivers/usb/gadget/` directory, considering this is the kernel's central location for USB gadget-related code. Specifically, the function I'm after, `configfs_remove_default_groups`, seems like a pretty clear indicator of what I should search for. Let me go ahead and start digging in that directory. I'll need to use my usual tools to pinpoint the exact location. Hopefully, the naming is straightforward enough that it leads me directly to the relevant files.
|
| 245/2 |
2026/07/30 14:39 |
tool |
git-log |
0m
Args: map[CodeRegexp:configfs_remove_default_groups Count:20 PathPrefix:drivers/usb/gadget/ Since:15 years]
Results:
|
| 246/2 |
2026/07/30 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 64704
output: 51
thoughts: 90
Thoughts: Okay, here's my thought process on this, distilled into a first-person summary:
**USB Gadget Driver Configuration Oversight**
Alright, I'm digging into this `configfs_remove_default_groups` situation. First off, I'm a bit puzzled. No recent commits in the `drivers/usb/gadget/` directory are actually *using* `configfs_remove_default_groups`. That's a red flag. If they aren't using this method, how are they dealing with equivalent problems? Are they just letting the underlying filesystem's default configurations stand, and, if so, is this creating potential vulnerabilities?
Okay, let's step back and re-examine `uvcg_config_remove_children`. I know I've seen it before, but perhaps I missed something. I need to understand if this is related.
Okay, I need to understand when `uvcg_config_remove_children` was added; understanding that timeline is critical to understanding the bigger picture.
|
| 247/2 |
2026/07/30 14:39 |
tool |
git-log |
0m
Args: map[CodeRegexp:uvcg_config_remove_children Count:20 PathPrefix:drivers/usb/gadget/ Since:15 years]
Results: map[Output:efbf0af70b4f usb: gadget: uvc: configfs: Allocate groups dynamically
]
|
| 248/2 |
2026/07/30 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 65097
output: 24
thoughts: 15
Thoughts: Okay, let's dive into this. Here's my thought process on this task:
**Analyzing the Hash `efbf0af70b4f`**
Alright, the instruction says to "check `efbf0af70b4f`." My initial thought is, this looks like a cryptographic hash, likely a result of some hashing algorithm. Without any context, the first thing I'd do is *assume* it's a hexadecimal representation. I need to figure out the likely algorithm used. Given its length (12 characters), it's probably not MD5 (32 chars) or SHA-256 (64 chars). It could be something shorter, like a truncated SHA variant, or maybe something custom. I would probably first try to identify it with a online hash identifier. I would also try to understand its likely purpose: is it a file hash, password hash, or something else entirely? Then, I would try a quick check in my internal databases for known hashes, in case I’ve seen this before. I'd also consider if this hash might be a partial fragment of a larger hash and need to be looked at in context. It's a starting point and I need to do some more digging before drawing any conclusions.
|
| 249/2 |
2026/07/30 14:39 |
tool |
git-show |
0m
Args:
Results: map[Output:commit efbf0af70b4f7ee6ed1533ed0d905255c0545e08
Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Date: Thu May 24 17:49:34 2018 +0300
usb: gadget: uvc: configfs: Allocate groups dynamically
The UVC configfs implementation creates all groups as global static
variables. This prevents creation of multiple UVC function instances,
as they would all require their own configfs group instances.
Fix this by allocating all groups dynamically. To avoid duplicating code
around, extend the config_item_type structure with group name and
children, and implement helper functions to create children
automatically for most groups.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index d8ce7868fe22..95cb1b5f5ffe 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -792,6 +792,7 @@ static struct usb_function_instance *uvc_alloc_inst(void)
struct uvc_output_terminal_descriptor *od;
struct uvc_color_matching_descriptor *md;
struct uvc_descriptor_header **ctl_cls;
+ int ret;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
@@ -868,7 +869,12 @@ static struct usb_function_instance *uvc_alloc_inst(void)
opts->streaming_interval = 1;
opts->streaming_maxpacket = 1024;
- uvcg_attach_configfs(opts);
+ ret = uvcg_attach_configfs(opts);
+ if (ret < 0) {
+ kfree(opts);
+ return ERR_PTR(ret);
+ }
+
return &opts->func_inst;
}
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 8d513cc6fb8c..ae722549eabc 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -41,6 +41,71 @@ static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
func_inst.group);
}
+struct uvcg_config_group_type {
+ struct config_item_type type;
+ const char *name;
+ const struct uvcg_config_group_type **children;
+ int (*create_children)(struct config_group *group);
+};
+
+static void uvcg_config_item_release(struct config_item *item)
+{
+ struct config_group *group = to_config_group(item);
+
+ kfree(group);
+}
+
+static struct configfs_item_operations uvcg_config_item_ops = {
+ .release = uvcg_config_item_release,
+};
+
+static int uvcg_config_create_group(struct config_group *parent,
+ const struct uvcg_config_group_type *type);
+
+static int uvcg_config_create_children(struct config_group *group,
+ const struct uvcg_config_group_type *type)
+{
+ const struct uvcg_config_group_type **child;
+ int ret;
+
+ if (type->create_children)
+ return type->create_children(group);
+
+ for (child = type->children; child && *child; ++child) {
+ ret = uvcg_config_create_group(group, *child);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int uvcg_config_create_group(struct config_group *parent,
+ const struct uvcg_config_group_type *type)
+{
+ struct config_group *group;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ config_group_init_type_name(group, type->name, &type->type);
+ configfs_add_default_group(group, parent);
+
+ return uvcg_config_create_children(group, type);
+}
+
+static void uvcg_config_remove_children(struct config_group *group)
+{
+ struct config_group *child, *n;
+
+ list_for_each_entry_safe(child, n, &group->default_groups, group_entry) {
+ list_del(&child->group_entry);
+ uvcg_config_remove_children(child);
+ config_item_put(&child->cg_item);
+ }
+}
+
/* -----------------------------------------------------------------------------
* control/header/<NAME>
* control/header
@@ -137,6 +202,7 @@ static struct configfs_attribute *uvcg_control_header_attrs[] = {
};
static const struct config_item_type uvcg_control_header_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_attrs = uvcg_control_header_attrs,
.ct_owner = THIS_MODULE,
};
@@ -161,32 +227,23 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
return &h->item;
}
-static void uvcg_control_header_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_control_header *h = to_uvcg_control_header(item);
-
- kfree(h);
-}
-
-static struct config_group uvcg_control_header_grp;
-
static struct configfs_group_operations uvcg_control_header_grp_ops = {
.make_item = uvcg_control_header_make,
- .drop_item = uvcg_control_header_drop,
};
-static const struct config_item_type uvcg_control_header_grp_type = {
- .ct_group_ops = &uvcg_control_header_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_control_header_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_control_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "header",
};
/* -----------------------------------------------------------------------------
* control/processing/default
*/
-static struct config_group uvcg_default_processing_grp;
-
#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \
static ssize_t uvcg_default_processing_##cname##_show( \
struct config_item *item, char *page) \
@@ -265,27 +322,35 @@ static struct configfs_attribute *uvcg_default_processing_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_processing_type = {
- .ct_attrs = uvcg_default_processing_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_processing_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_processing_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
/* -----------------------------------------------------------------------------
* control/processing
*/
-static struct config_group uvcg_processing_grp;
-
-static const struct config_item_type uvcg_processing_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_processing_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "processing",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_processing_type,
+ NULL,
+ },
};
/* -----------------------------------------------------------------------------
* control/terminal/camera/default
*/
-static struct config_group uvcg_default_camera_grp;
-
#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \
static ssize_t uvcg_default_camera_##cname##_show( \
struct config_item *item, char *page) \
@@ -375,27 +440,35 @@ static struct configfs_attribute *uvcg_default_camera_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_camera_type = {
- .ct_attrs = uvcg_default_camera_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_camera_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_camera_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
/* -----------------------------------------------------------------------------
* control/terminal/camera
*/
-static struct config_group uvcg_camera_grp;
-
-static const struct config_item_type uvcg_camera_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_camera_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "camera",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_camera_type,
+ NULL,
+ },
};
/* -----------------------------------------------------------------------------
* control/terminal/output/default
*/
-static struct config_group uvcg_default_output_grp;
-
#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \
static ssize_t uvcg_default_output_##cname##_show( \
struct config_item *item, char *page) \
@@ -446,47 +519,68 @@ static struct configfs_attribute *uvcg_default_output_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_output_type = {
- .ct_attrs = uvcg_default_output_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_output_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_output_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
/* -----------------------------------------------------------------------------
* control/terminal/output
*/
-static struct config_group uvcg_output_grp;
-
-static const struct config_item_type uvcg_output_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_output_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "output",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_output_type,
+ NULL,
+ },
};
/* -----------------------------------------------------------------------------
* control/terminal
*/
-static struct config_group uvcg_terminal_grp;
-
-static const struct config_item_type uvcg_terminal_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_terminal_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "terminal",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_camera_grp_type,
+ &uvcg_output_grp_type,
+ NULL,
+ },
};
/* -----------------------------------------------------------------------------
* control/class/{fs|ss}
*/
-static struct config_group uvcg_control_class_fs_grp;
-static struct config_group uvcg_control_class_ss_grp;
+struct uvcg_control_class_group {
+ struct config_group group;
+ const char *name;
+};
static inline struct uvc_descriptor_header
**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o)
{
- struct config_group *group = to_config_group(i);
+ struct uvcg_control_class_group *group =
+ container_of(i, struct uvcg_control_class_group,
+ group.cg_item);
- if (group == &uvcg_control_class_fs_grp)
+ if (!strcmp(group->name, "fs"))
return o->uvc_fs_control_cls;
- if (group == &uvcg_control_class_ss_grp)
+ if (!strcmp(group->name, "ss"))
return o->uvc_ss_control_cls;
return NULL;
@@ -570,6 +664,7 @@ static void uvcg_control_class_drop_link(struct config_item *src,
}
static struct configfs_item_operations uvcg_control_class_item_ops = {
+ .release = uvcg_config_item_release,
.allow_link = uvcg_control_class_allow_link,
.drop_link = uvcg_control_class_drop_link,
};
@@ -583,20 +678,54 @@ static const struct config_item_type uvcg_control_class_type = {
* control/class
*/
-static struct config_group uvcg_control_class_grp;
+static int uvcg_control_class_create_children(struct config_group *parent)
+{
+ static const char * const names[] = { "fs", "ss" };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ struct uvcg_control_class_group *group;
-static const struct config_item_type uvcg_control_class_grp_type = {
- .ct_owner = THIS_MODULE,
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ group->name = names[i];
+
+ config_group_init_type_name(&group->group, group->name,
+ &uvcg_control_class_type);
+ configfs_add_default_group(&group->group, parent);
+ }
+
+ return 0;
+}
+
+static const struct uvcg_config_group_type uvcg_control_class_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "class",
+ .create_children = uvcg_control_class_create_children,
};
/* -----------------------------------------------------------------------------
* control
*/
-static struct config_group uvcg_control_grp;
-
-static const struct config_item_type uvcg_control_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_control_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "control",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_control_header_grp_type,
+ &uvcg_processing_grp_type,
+ &uvcg_terminal_grp_type,
+ &uvcg_control_class_grp_type,
+ NULL,
+ },
};
/* -----------------------------------------------------------------------------
@@ -604,12 +733,9 @@ static const struct config_item_type uvcg_control_grp_type = {
* streaming/mjpeg
*/
-static struct config_group uvcg_uncompressed_grp;
-static struct config_group uvcg_mjpeg_grp;
-
-static struct config_item *fmt_parent[] = {
- &uvcg_uncompressed_grp.cg_item,
- &uvcg_mjpeg_grp.cg_item,
+static const char * const uvcg_format_names[] = {
+ "uncompressed",
+ "mjpeg",
};
enum uvcg_format_type {
@@ -735,10 +861,22 @@ static int uvcg_streaming_header_allow_link(struct config_item *src,
goto out;
}
- for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i)
- if (target->ci_parent == fmt_parent[i])
+ /*
+ * Linking is only allowed to direct children of the format nodes
+ * (streaming/uncompressed or streaming/mjpeg nodes). First check that
+ * the grand-parent of the target matches the grand-parent of the source
+ * (the streaming node), and then verify that the target parent is a
+ * format node.
+ */
+ if (src->ci_parent->ci_parent != target->ci_parent->ci_parent)
+ goto out;
+
+ for (i = 0; i < ARRAY_SIZE(uvcg_format_names); ++i) {
+ if (!strcmp(target->ci_parent->ci_name, uvcg_format_names[i]))
break;
- if (i == ARRAY_SIZE(fmt_parent))
+ }
+
+ if (i == ARRAY_SIZE(uvcg_format_names))
goto out;
target_fmt = container_of(to_config_group(target), struct uvcg_format,
@@ -798,8 +936,9 @@ static void uvcg_streaming_header_drop_link(struct config_item *src,
}
static struct configfs_item_operations uvcg_streaming_header_item_ops = {
- .allow_link = uvcg_streaming_header_allow_link,
- .drop_link = uvcg_streaming_header_drop_link,
+ .release = uvcg_config_item_release,
+ .allow_link = uvcg_streaming_header_allow_link,
+ .drop_link = uvcg_streaming_header_drop_link,
};
#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \
@@ -875,24 +1014,17 @@ static struct config_item
return &h->item;
}
-static void uvcg_streaming_header_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_streaming_header *h = to_uvcg_streaming_header(item);
-
- kfree(h);
-}
-
-static struct config_group uvcg_streaming_header_grp;
-
static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
.make_item = uvcg_streaming_header_make,
- .drop_item = uvcg_streaming_header_drop,
};
-static const struct config_item_type uvcg_streaming_header_grp_type = {
- .ct_group_ops = &uvcg_streaming_header_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_streaming_header_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_streaming_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "header",
};
/* -----------------------------------------------------------------------------
@@ -900,6 +1032,8 @@ static const struct config_item_type uvcg_streaming_header_grp_type = {
*/
struct uvcg_frame {
+ struct config_item item;
+ enum uvcg_format_type fmt_type;
struct {
u8 b_length;
u8 b_descriptor_type;
@@ -915,8 +1049,6 @@ struct uvcg_frame {
u8 b_frame_interval_type;
} __attribute__((packed)) frame;
u32 *dw_frame_interval;
- enum uvcg_format_type fmt_type;
- struct config_item item;
};
static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
@@ -1143,6 +1275,7 @@ static struct configfs_attribute *uvcg_frame_attrs[] = {
};
static const struct config_item_type uvcg_frame_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_attrs = uvcg_frame_attrs,
.ct_owner = THIS_MODULE,
};
@@ -1194,7 +1327,6 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
{
- struct uvcg_frame *h = to_uvcg_frame(item);
struct uvcg_format *fmt;
struct f_uvc_opts *opts;
struct config_item *opts_item;
@@ -1205,8 +1337,9 @@ static void uvcg_frame_drop(struct config_group *group, struct config_item *item
mutex_lock(&opts->lock);
fmt = to_uvcg_format(&group->cg_item);
--fmt->num_frames;
- kfree(h);
mutex_unlock(&opts->lock);
+
+ config_item_put(item);
}
/* -----------------------------------------------------------------------------
@@ -1415,6 +1548,7 @@ static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
};
static const struct config_item_type uvcg_uncompressed_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_group_ops = &uvcg_uncompressed_group_ops,
.ct_attrs = uvcg_uncompressed_attrs,
.ct_owner = THIS_MODULE,
@@ -1451,22 +1585,17 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
return &h->fmt.group;
}
-static void uvcg_uncompressed_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_uncompressed *h = to_uvcg_uncompressed(item);
-
- kfree(h);
-}
-
static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
.make_group = uvcg_uncompressed_make,
- .drop_item = uvcg_uncompressed_drop,
};
-static const struct config_item_type uvcg_uncompressed_grp_type = {
- .ct_group_ops = &uvcg_uncompressed_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_uncompressed_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "uncompressed",
};
/* -----------------------------------------------------------------------------
@@ -1618,6 +1747,7 @@ static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
};
static const struct config_item_type uvcg_mjpeg_type = {
+ .ct_item_ops = &uvcg_config_item_ops,
.ct_group_ops = &uvcg_mjpeg_group_ops,
.ct_attrs = uvcg_mjpeg_attrs,
.ct_owner = THIS_MODULE,
@@ -1648,30 +1778,23 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
return &h->fmt.group;
}
-static void uvcg_mjpeg_drop(struct config_group *group,
- struct config_item *item)
-{
- struct uvcg_mjpeg *h = to_uvcg_mjpeg(item);
-
- kfree(h);
-}
-
static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
.make_group = uvcg_mjpeg_make,
- .drop_item = uvcg_mjpeg_drop,
};
-static const struct config_item_type uvcg_mjpeg_grp_type = {
- .ct_group_ops = &uvcg_mjpeg_grp_ops,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_group_ops = &uvcg_mjpeg_grp_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "mjpeg",
};
/* -----------------------------------------------------------------------------
* streaming/color_matching/default
*/
-static struct config_group uvcg_default_color_matching_grp;
-
#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \
static ssize_t uvcg_default_color_matching_##cname##_show( \
struct config_item *item, char *page) \
@@ -1719,41 +1842,54 @@ static struct configfs_attribute *uvcg_default_color_matching_attrs[] = {
NULL,
};
-static const struct config_item_type uvcg_default_color_matching_type = {
- .ct_attrs = uvcg_default_color_matching_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_default_color_matching_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_attrs = uvcg_default_color_matching_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "default",
};
/* -----------------------------------------------------------------------------
* streaming/color_matching
*/
-static struct config_group uvcg_color_matching_grp;
-
-static const struct config_item_type uvcg_color_matching_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_color_matching_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "color_matching",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_default_color_matching_type,
+ NULL,
+ },
};
/* -----------------------------------------------------------------------------
* streaming/class/{fs|hs|ss}
*/
-static struct config_group uvcg_streaming_class_fs_grp;
-static struct config_group uvcg_streaming_class_hs_grp;
-static struct config_group uvcg_streaming_class_ss_grp;
+struct uvcg_streaming_class_group {
+ struct config_group group;
+ const char *name;
+};
static inline struct uvc_descriptor_header
***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o)
{
- struct config_group *group = to_config_group(i);
+ struct uvcg_streaming_class_group *group =
+ container_of(i, struct uvcg_streaming_class_group,
+ group.cg_item);
- if (group == &uvcg_streaming_class_fs_grp)
+ if (!strcmp(group->name, "fs"))
return &o->uvc_fs_streaming_cls;
- if (group == &uvcg_streaming_class_hs_grp)
+ if (!strcmp(group->name, "hs"))
return &o->uvc_hs_streaming_cls;
- if (group == &uvcg_streaming_class_ss_grp)
+ if (!strcmp(group->name, "ss"))
return &o->uvc_ss_streaming_cls;
return NULL;
@@ -2074,6 +2210,7 @@ static void uvcg_streaming_class_drop_link(struct config_item *src,
}
static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+ .release = uvcg_config_item_release,
.allow_link = uvcg_streaming_class_allow_link,
.drop_link = uvcg_streaming_class_drop_link,
};
@@ -2087,35 +2224,71 @@ static const struct config_item_type uvcg_streaming_class_type = {
* streaming/class
*/
-static struct config_group uvcg_streaming_class_grp;
+static int uvcg_streaming_class_create_children(struct config_group *parent)
+{
+ static const char * const names[] = { "fs", "hs", "ss" };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ struct uvcg_streaming_class_group *group;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ group->name = names[i];
-static const struct config_item_type uvcg_streaming_class_grp_type = {
- .ct_owner = THIS_MODULE,
+ config_group_init_type_name(&group->group, group->name,
+ &uvcg_streaming_class_type);
+ configfs_add_default_group(&group->group, parent);
+ }
+
+ return 0;
+}
+
+static const struct uvcg_config_group_type uvcg_streaming_class_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "class",
+ .create_children = uvcg_streaming_class_create_children,
};
/* -----------------------------------------------------------------------------
* streaming
*/
-static struct config_group uvcg_streaming_grp;
-
-static const struct config_item_type uvcg_streaming_grp_type = {
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvcg_streaming_grp_type = {
+ .type = {
+ .ct_item_ops = &uvcg_config_item_ops,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "streaming",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_streaming_header_grp_type,
+ &uvcg_uncompressed_grp_type,
+ &uvcg_mjpeg_grp_type,
+ &uvcg_color_matching_grp_type,
+ &uvcg_streaming_class_grp_type,
+ NULL,
+ },
};
/* -----------------------------------------------------------------------------
* UVC function
*/
-static void uvc_attr_release(struct config_item *item)
+static void uvc_func_item_release(struct config_item *item)
{
struct f_uvc_opts *opts = to_f_uvc_opts(item);
+ uvcg_config_remove_children(to_config_group(item));
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations uvc_item_ops = {
- .release = uvc_attr_release,
+static struct configfs_item_operations uvc_func_item_ops = {
+ .release = uvc_func_item_release,
};
#define UVCG_OPTS_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
@@ -2183,123 +2356,31 @@ static struct configfs_attribute *uvc_attrs[] = {
NULL,
};
-static const struct config_item_type uvc_func_type = {
- .ct_item_ops = &uvc_item_ops,
- .ct_attrs = uvc_attrs,
- .ct_owner = THIS_MODULE,
+static const struct uvcg_config_group_type uvc_func_type = {
+ .type = {
+ .ct_item_ops = &uvc_func_item_ops,
+ .ct_attrs = uvc_attrs,
+ .ct_owner = THIS_MODULE,
+ },
+ .name = "",
+ .children = (const struct uvcg_config_group_type*[]) {
+ &uvcg_control_grp_type,
+ &uvcg_streaming_grp_type,
+ NULL,
+ },
};
int uvcg_attach_configfs(struct f_uvc_opts *opts)
{
- config_group_init_type_name(&uvcg_control_header_grp,
- "header",
- &uvcg_control_header_grp_type);
-
- config_group_init_type_name(&uvcg_default_processing_grp,
- "default", &uvcg_default_processing_type);
- config_group_init_type_name(&uvcg_processing_grp,
- "processing", &uvcg_processing_grp_type);
- configfs_add_default_group(&uvcg_default_processing_grp,
- &uvcg_processing_grp);
-
- config_group_init_type_name(&uvcg_default_camera_grp,
- "default", &uvcg_default_camera_type);
- config_group_init_type_name(&uvcg_camera_grp,
- "camera", &uvcg_camera_grp_type);
- configfs_add_default_group(&uvcg_default_camera_grp,
- &uvcg_camera_grp);
-
- config_group_init_type_name(&uvcg_default_output_grp,
- "default", &uvcg_default_output_type);
- config_group_init_type_name(&uvcg_output_grp,
- "output", &uvcg_output_grp_type);
- configfs_add_default_group(&uvcg_default_output_grp,
- &uvcg_output_grp);
-
- config_group_init_type_name(&uvcg_terminal_grp,
- "terminal", &uvcg_terminal_grp_type);
- configfs_add_default_group(&uvcg_camera_grp,
- &uvcg_terminal_grp);
- configfs_add_default_group(&uvcg_output_grp,
- &uvcg_terminal_grp);
-
- config_group_init_type_name(&uvcg_control_class_fs_grp,
- "fs", &uvcg_control_class_type);
- config_group_init_type_name(&uvcg_control_class_ss_grp,
- "ss", &uvcg_control_class_type);
- config_group_init_type_name(&uvcg_control_class_grp,
- "class",
- &uvcg_control_class_grp_type);
- configfs_add_default_group(&uvcg_control_class_fs_grp,
- &uvcg_control_class_grp);
- configfs_add_default_group(&uvcg_control_class_ss_grp,
- &uvcg_control_class_grp);
-
- config_group_init_type_name(&uvcg_control_grp,
- "control",
- &uvcg_control_grp_type);
- configfs_add_default_group(&uvcg_control_header_grp,
- &uvcg_control_grp);
- configfs_add_default_group(&uvcg_processing_grp,
- &uvcg_control_grp);
- configfs_add_default_group(&uvcg_terminal_grp,
- &uvcg_control_grp);
- configfs_add_default_group(&uvcg_control_class_grp,
- &uvcg_control_grp);
-
- config_group_init_type_name(&uvcg_streaming_header_grp,
- "header",
- &uvcg_streaming_header_grp_type);
- config_group_init_type_name(&uvcg_uncompressed_grp,
- "uncompressed",
- &uvcg_uncompressed_grp_type);
- config_group_init_type_name(&uvcg_mjpeg_grp,
- "mjpeg",
- &uvcg_mjpeg_grp_type);
- config_group_init_type_name(&uvcg_default_color_matching_grp,
- "default",
- &uvcg_default_color_matching_type);
- config_group_init_type_name(&uvcg_color_matching_grp,
- "color_matching",
- &uvcg_color_matching_grp_type);
- configfs_add_default_group(&uvcg_default_color_matching_grp,
- &uvcg_color_matching_grp);
-
- config_group_init_type_name(&uvcg_streaming_class_fs_grp,
- "fs", &uvcg_streaming_class_type);
- config_group_init_type_name(&uvcg_streaming_class_hs_grp,
- "hs", &uvcg_streaming_class_type);
- config_group_init_type_name(&uvcg_streaming_class_ss_grp,
- "ss", &uvcg_streaming_class_type);
- config_group_init_type_name(&uvcg_streaming_class_grp,
- "class", &uvcg_streaming_class_grp_type);
- configfs_add_default_group(&uvcg_streaming_class_fs_grp,
- &uvcg_streaming_class_grp);
- configfs_add_default_group(&uvcg_streaming_class_hs_grp,
- &uvcg_streaming_class_grp);
- configfs_add_default_group(&uvcg_streaming_class_ss_grp,
- &uvcg_streaming_class_grp);
-
- config_group_init_type_name(&uvcg_streaming_grp,
- "streaming", &uvcg_streaming_grp_type);
- configfs_add_default_group(&uvcg_streaming_header_grp,
- &uvcg_streaming_grp);
- configfs_add_default_group(&uvcg_uncompressed_grp,
- &uvcg_streaming_grp);
- configfs_add_default_group(&uvcg_mjpeg_grp,
- &uvcg_streaming_grp);
- configfs_add_default_group(&uvcg_color_matching_grp,
- &uvcg_streaming_grp);
- configfs_add_default_group(&uvcg_streaming_class_grp,
- &uvcg_streaming_grp);
-
- config_group_init_type_name(&opts->func_inst.group,
- "",
- &uvc_func_type);
- configfs_add_default_group(&uvcg_control_grp,
- &opts->func_inst.group);
- configfs_add_default_group(&uvcg_streaming_grp,
- &opts->func_inst.group);
+ int ret;
- return 0;
+ config_group_init_type_name(&opts->func_inst.group, uvc_func_type.name,
+ &uvc_func_type.type);
+
+ ret = uvcg_config_create_children(&opts->func_inst.group,
+ &uvc_func_type);
+ if (ret < 0)
+ config_group_put(&opts->func_inst.group);
+
+ return ret;
}
]
|
| 250/2 |
2026/07/30 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 76743
output: 26
thoughts: 116
Thoughts: **Delving into the `uvcg_config_remove_children` Implementation**
Okay, so it clicked! `uvcg_config_remove_children`, that method, the one introduced in `efbf0af70b4f`, was all about the group allocation strategy. It seems like the switch from static groups to dynamically allocated ones necessitated a cleanup mechanism. Makes perfect sense. Dynamic allocation, dynamic deallocation – you have to manage those resources, and you have to free them when you're done with them.
Initially, I suspected `configfs_remove_default_groups` wasn't accessible or perhaps not applicable here. That's why they likely rolled their own solution. But wait a second, let me rethink...wasn't that function *exported*? Let's verify, just to be absolutely sure. Okay, I'll need to check the source code in `fs/configfs/dir.c`. I need to ensure my assumptions are all correct.
|
| 251/2 |
2026/07/30 14:39 |
tool |
grepper |
0m
Args: map[Expression:EXPORT_SYMBOL.*configfs_remove_default_groups]
Results: map[Output:fs/configfs/dir.c=748=void configfs_remove_default_groups(struct config_group *group)
--
fs/configfs/dir.c-756-}
fs/configfs/dir.c:757:EXPORT_SYMBOL(configfs_remove_default_groups);
fs/configfs/dir.c-758-
]
|
| 252/2 |
2026/07/30 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 77193
output: 40
thoughts: 73
Thoughts: **Examining the ConfigFS Removal Logic**
Okay, so the system *does* export, that much is clear. However, I'm digging into the lower levels of this `uvc_configfs.c` file and I've noticed something. The folks who wrote this implemented their own `uvcg_config_remove_children` function specifically to handle the recursive removal of children. Interesting.
That's a bit of a red flag; why implement custom recursive behavior when ConfigFS offers core functionality? Then it hits me, I need to double-check that `configfs_remove_default_groups`. Wait, maybe I'm misremembering its function. Let me go back and give it a thorough once-over, to verify the recursive behavior (or lack thereof). This discrepancy is important for the overall design.
|
| 253/2 |
2026/07/30 14:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_remove_default_groups]
Results: map[SourceCode: 748: void configfs_remove_default_groups(struct config_group *group)
749: {
750: struct config_group *g, *n;
751:
752: list_for_each_entry_safe(g, n, &group->default_groups, group_entry) {
753: list_del(&g->group_entry);
754: config_item_put(&g->cg_item);
755: }
756: }
SourceFile:fs/configfs/dir.c]
|
| 254/2 |
2026/07/30 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 77652
output: 35
thoughts: 576
Thoughts: **My Analysis of the `configfs_remove_default_groups` Bug and Past Fixes**
Okay, so I've got this bug where `configfs_remove_default_groups` isn't fully removing nested default groups. I see that it only removes direct children and doesn't cascade down. But if you call it on the parent, the refcount of the child drops. If the refcount goes to zero, the child's `release` function is called. And, aha! If that `release` function then *itself* calls `configfs_remove_default_groups` on *its* children, we get the recursive cleanup we need. The bug report outlines this perfectly: the fix drops the default groups in a cascading fashion within the `release` functions of nested configfs instances. This confirms my understanding.
Now, the task is to find similar fixes in the USB gadget subsystem. Initially, I thought there were no relevant commits using `configfs_remove_default_groups` directly within `drivers/usb/gadget/`. However, I need to broaden my search and think conceptually. Ah-ha! I've found something! While it's not a direct call to `configfs_remove_default_groups`, `uvcg_config_remove_children` from the commit `efbf0af70b4f` ("usb: gadget: uvc: configfs: Allocate groups dynamically") is essentially doing the *same thing*! It's recursively removing and dropping refcounts of dynamically allocated default groups. This is the same pattern of nested release functions calling their own group removal. This is exactly what I was looking for!
So, the pattern is:
1. Identify the need for recursive cleanup of dynamically allocated default groups within the configfs structure.
2. In the `release` function, call the cleanup function that removes all default groups.
3. The cleanup function should call `configfs_remove_default_groups` on all child nodes, which will trigger their `release` functions and cascading cleanup.
I also checked other recent additions like `f_midi2.c` and older files like `f_mass_storage.c` for similar issues to see if there were more fixes I could leverage for guidance, so I would have a holistic understanding of how to address and avoid such bugs in the future, however, I found that the `uvcg_config_remove_children` pattern should be sufficient to write a fix for this bug.
|
| 255/2 |
2026/07/30 14:39 |
tool |
git-log |
0m
Args: map[Count:1 PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:224fb8661f66 usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
]
|
| 256/2 |
2026/07/30 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 78834
output: 27
thoughts: 63
Thoughts: **Investigating the Origin of `f_midi2.c`**
Okay, so I need to trace the introduction of `f_midi2.c`. The first thing that jumps to mind is that it was likely introduced in the commit `01037389c23f`, which seems to be titled "usb: gadget: Add UMP / MIDI 2.0 gadget driver." I'll need to confirm that this is indeed the commit where the file was added. Time to delve into the details of that commit and see what I can find.
|
| 257/2 |
2026/07/30 14:39 |
tool |
git-show |
0m
Error: git show failed: fatal: Not a valid object name 01037389c23f^{commit}
Args:
Results:
|
| 258/2 |
2026/07/30 14:39 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 79085
output: 32
thoughts: 34
Thoughts: Okay, here's my thought process as if I were considering the scenario:
**Finding That Specific Commit**
Hmm, I need to find that specific commit, but I'm drawing a blank on the exact hash right now. No worries, I'll take a targeted approach. Let me see... I'll search for the relevant keywords to jog my memory and narrow down the possibilities. Okay, let's try searching for something like "usb: gadget: Add UMP / MIDI 2.0 gadget driver". That should be a pretty specific and unique enough search term that it will hopefully surface the commit I'm after, or at least a manageable set of results that I can quickly scan. This approach should get me back on track pretty quickly.
|
| 259/2 |
2026/07/30 14:40 |
tool |
git-log |
0m
Args: map[MessageRegexps:[usb: gadget: Add UMP / MIDI 2.0 gadget driver]]
Results:
|
| 260/2 |
2026/07/30 14:40 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 79321
output: 36
thoughts: 16
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into the `f_midi2.c` History**
Okay, first things first, let's take a look at the commit history for `f_midi2.c`. I need to get a clear picture of the evolution of this module. Understanding the chronological changes is key before I can even begin to assess its current state or potential for modifications. I'm thinking I'll start with the standard `git log` commands, maybe `--graph` to visualize branching. I'll probably also add in some filters like `--author=` if I'm specifically looking for work done by certain contributors, or `--since=` and `--until=` if I want to focus on a particular timeframe. This should give me a solid foundation.
|
| 261/2 |
2026/07/30 14:40 |
tool |
git-log |
0m
Args: map[Count:10 PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:224fb8661f66 usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
189f164e573e Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
32a92f8c8932 Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
e715bc42e337 usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
116e79c679a1 usb: gadget: midi2: Fix MIDI2 IN EP max packet size
21d8525d2e06 usb: gadget: midi2: Fix missing UMP group attributes initialization
dd33993a9721 ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
82937056967d usb: gadget: midi2: Fix interpretation of is_midi1 bits
]
|
| 262/2 |
2026/07/30 14:40 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 79846
output: 36
thoughts: 7
Thoughts: Okay, I'm ready to dive into that text! I'll approach this as if these are *my* thoughts, as an expert in my field, distilling the essence and focusing on what matters most.
**Initial Considerations and Direction**
Alright, the initial prompt is simple enough. It's time to gather more information, to dig deeper into the context. This "Let's get more history" indicates a desire for a more comprehensive understanding. It's a call for the foundational background knowledge, whatever that history may entail. It suggests that a superficial understanding won't suffice; we need depth and context to proceed effectively. My mind is already starting to formulate the key questions: What domain are we in? What historical events or factors are relevant? What's the specific question we're trying to answer, and how does this history inform that?
|
| 263/2 |
2026/07/30 14:40 |
tool |
git-log |
0m
Args: map[Count:20 PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:224fb8661f66 usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
189f164e573e Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
32a92f8c8932 Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
e715bc42e337 usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
116e79c679a1 usb: gadget: midi2: Fix MIDI2 IN EP max packet size
21d8525d2e06 usb: gadget: midi2: Fix missing UMP group attributes initialization
dd33993a9721 ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
82937056967d usb: gadget: midi2: Fix interpretation of is_midi1 bits
7f72d17359e5 usb: gadget: function: remove redundant else statement
d9c61bb33fbb usb: gadget: function: move u_f.h to include/linux/usb/func_utils.h
228a953e61d6 usb: gadget: midi2: Fix the response for FB info with block 0xff
3eb27d3e32c7 usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup
1cb9ba5e61e8 usb: gadget: add missing MODULE_DESCRIPTION() macros
46decc82ffd5 usb: gadget: unconditionally allocate hs/ss descriptor in bind operation
6bb75eb9ff3a usb: gadget: midi2: fix missing unlock in f_midi2_block_opts_create()
a85ff0db48c3 usb: gadget: midi2: More flexible MIDI 1.0 configuration
1b437d2fb3c1 usb: gadget: midi2: Add "Operation Mode" control
d6468be779af usb: gadget: midi2: MIDI 1.0 interface (altset 0) support
]
|
| 264/2 |
2026/07/30 14:40 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 80641
output: 37
thoughts: 16
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Initial Commit Thoughts: `f_midi2.c`**
Okay, time to kick things off. Let's get that first commit of `f_midi2.c` in the bag. I'm thinking clean, minimal, and functional – we can always iterate, but a solid foundation is crucial. The goal is a buildable structure first. We'll outline the basic components and ensure they compile cleanly. This initial commit needs to reflect a coherent, if minimal, design. Think header inclusion, any essential typedefs, maybe even stubs for the core functions to signal what's planned. I should also consider how to make it easy to extend for future additions. I need to keep the architecture of this library in mind as I begin, especially any existing interfaces I need to use, and any future ones I need to create.
|
| 265/2 |
2026/07/30 14:40 |
tool |
git-log |
0m
Args: map[Count:100 PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:224fb8661f66 usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
189f164e573e Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
32a92f8c8932 Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
e715bc42e337 usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
116e79c679a1 usb: gadget: midi2: Fix MIDI2 IN EP max packet size
21d8525d2e06 usb: gadget: midi2: Fix missing UMP group attributes initialization
dd33993a9721 ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
82937056967d usb: gadget: midi2: Fix interpretation of is_midi1 bits
7f72d17359e5 usb: gadget: function: remove redundant else statement
d9c61bb33fbb usb: gadget: function: move u_f.h to include/linux/usb/func_utils.h
228a953e61d6 usb: gadget: midi2: Fix the response for FB info with block 0xff
3eb27d3e32c7 usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup
1cb9ba5e61e8 usb: gadget: add missing MODULE_DESCRIPTION() macros
46decc82ffd5 usb: gadget: unconditionally allocate hs/ss descriptor in bind operation
6bb75eb9ff3a usb: gadget: midi2: fix missing unlock in f_midi2_block_opts_create()
a85ff0db48c3 usb: gadget: midi2: More flexible MIDI 1.0 configuration
1b437d2fb3c1 usb: gadget: midi2: Add "Operation Mode" control
d6468be779af usb: gadget: midi2: MIDI 1.0 interface (altset 0) support
856fa444b098 usb: gadget: midi2: Dynamically create MIDI 1.0 altset descriptors
29ee7a4dddd5 usb: gadget: midi2: Add configfs support
8b645922b223 usb: gadget: Add support for USB MIDI 2.0 function driver
]
|
| 266/2 |
2026/07/30 14:40 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 81540
output: 24
thoughts: 34
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Analyzing the ConfigFS Integration**
Right, so `29ee7a4dddd5`—that's the commit ID, right? I immediately recognize that one: configfs support. Good. Always interested to see how that's being integrated. Let me pull that up. I need to dive into `29ee7a4dddd5` and see precisely what they've done. I'm keen to understand the nuances of this implementation. Things to consider: how well have they handled the security implications of exposing this interface? Is there proper error handling in place, or are they leaving that to a higher level? Did they consider existing frameworks, or is it a new custom implementation? A thorough review is in order. Let's get to it.
|
| 267/2 |
2026/07/30 14:40 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 29ee7a4dddd5caa18d1cef000f20c6af43f762f1
Author: Takashi Iwai <tiwai@suse.de>
Date: Tue Jul 25 08:22:01 2023 +0200
usb: gadget: midi2: Add configfs support
This patch adds the support of configfs to MIDI 2.0 function driver
for users to allow configuring the UMP Endpoint and Function Blocks
more flexibly. The configuration is in a tree form. The top-most
contains some card-level configurations. UMP Endpoints are defined in
subdirectories (ep.0, ep.1, etc) that contain Endpoint-specific
configurations such as ep_name, etc. And, UMP Function Blocks are
defined in the subdirectories (block.0, block.1, etc) under EP
subdirectory. As default, the driver creates a single UMP Endpoint
(ep.0) and a single Function Block (block.0) to work in a minimalistic
manner. User can modify those attributes freely to fit with the
demands.
When multiple Function Blocks are required, user can create another
directory as block.1, block.2, and so on (up to block.31). A block.*
directory can be deleted dynamically, too. A caveat is that the block
number has to be continuous.
Similarly, when multiple UMP Endpoints are required, user can create
another directory as ep.1, ep.2, up to ep.3.
Also, some driver behavior can be controlled in the card top-level
configs. e.g. you can pass process_ump=0 to disable the processing of
UMP Stream messages. This would be equivalent with the older MIDI 2.0
spec that doesn't support UMP v1.1 features.
The configfs interface checks upper- / lower-bound of input values,
and more sanity checks are performed at binding.
Attributes can't be changed any longer once when the instance is
linked to UDC.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://lore.kernel.org/r/20230725062206.9674-3-tiwai@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi2 b/Documentation/ABI/testing/configfs-usb-gadget-midi2
new file mode 100644
index 000000000000..a3a036d784c7
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-midi2
@@ -0,0 +1,52 @@
+What: /config/usb-gadget/gadget/functions/midi2.name
+Date: Jul 2023
+KernelVersion: 6.6
+Description:
+ The attributes:
+
+ ============ ===============================================
+ process_ump Flag to process UMP Stream messages (0 or 1)
+ static_block Flag for static blocks (0 or 1)
+ iface_name MIDI interface name string
+ ============ ===============================================
+
+What: /config/usb-gadget/gadget/functions/midi2.name/ep.number
+Date: Jul 2023
+KernelVersion: 6.6
+Description:
+ This group contains a UMP Endpoint configuration.
+ A new Endpoint starts from 0, and can be up to 3.
+
+ The attributes:
+
+ ============= ===============================================
+ protocol_caps MIDI protocol capabilities (1, 2 or 3 for both)
+ protocol Default MIDI protocol (1 or 2)
+ ep_name UMP Endpoint name string
+ product_id Product ID string
+ manufacturer Manufacture ID (24 bit)
+ family Device family ID (16 bit)
+ model Device model ID (16 bit)
+ sw_revision Software Revision (32 bit)
+ ============= ===============================================
+
+What: /config/usb-gadget/gadget/functions/midi2.name/ep.number/block.number
+Date: Jul 2023
+KernelVersion: 6.6
+Description:
+ This group contains a UMP Function Block configuration.
+ A new block starts from 0, and can be up to 31.
+
+ The attributes:
+
+ =============== ===============================================
+ name Function Block name string
+ direction 1: input, 2: output, 3: bidirectional
+ first_group The first UMP Group number (0-15)
+ num_groups The number of groups in this FB (1-16)
+ ui_hint 0: unknown, 1: receiver, 2: sender, 3: both
+ midi_ci_verison Supported MIDI-CI version number (8 bit)
+ is_midi1 Legacy MIDI 1.0 device (0, 1 or 2)
+ sysex8_streams Max number of SysEx8 streams (8 bit)
+ active Active FB flag (0 or 1)
+ =============== ===============================================
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 848cb3150deb..c68a6fa0d237 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -292,7 +292,7 @@ static struct usb_interface_descriptor midi2_midi2_if_desc = {
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0, // to be filled
.bAlternateSetting = 1,
- .bNumEndpoints = 2,
+ .bNumEndpoints = 2, // to be filled
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
.bInterfaceProtocol = 0,
@@ -1403,6 +1403,8 @@ static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f)
midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id;
}
+ midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2;
+
/* audio interface */
status = usb_interface_id(c, f);
if (status < 0)
@@ -1510,16 +1512,274 @@ static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
}
+/*
+ * ConfigFS interface
+ */
+
+/* type conversion helpers */
+static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_opts,
+ func_inst.group);
+}
+
+static inline struct f_midi2_ep_opts *
+to_f_midi2_ep_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_ep_opts,
+ group);
+}
+
+static inline struct f_midi2_block_opts *
+to_f_midi2_block_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_block_opts,
+ group);
+}
+
+/* trim the string to be usable for EP and FB name strings */
+static void make_name_string(char *s)
+{
+ char *p;
+
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+
+ p = s + strlen(s);
+ for (; p > s && isspace(*p); p--)
+ *p = 0;
+}
+
+/* configfs helpers: generic show/store for unisnged int */
+static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts,
+ u32 val, const char *format, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, format, val);
+ mutex_unlock(&opts->lock);
+ return result;
+}
+
+static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts,
+ u32 *valp, u32 minval, u32 maxval,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 val;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &val);
+ if (ret)
+ goto end;
+ if (val < minval || val > maxval) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ *valp = val;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/* generic store for bool */
+static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
+ bool *valp, const char *page, size_t len)
+{
+ int ret;
+ bool val;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtobool(page, &val);
+ if (ret)
+ goto end;
+ *valp = val;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/* generic show/store for string */
+static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
+ const char *str, char *page)
+{
+ int result = 0;
+
+ mutex_lock(&opts->lock);
+ if (str)
+ result = scnprintf(page, PAGE_SIZE, "%s\n", str);
+ mutex_unlock(&opts->lock);
+ return result;
+}
+
+static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts,
+ const char **strp, size_t maxlen,
+ const char *page, size_t len)
+{
+ char *c;
+ int ret;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ c = kstrndup(page, min(len, maxlen), GFP_KERNEL);
+ if (!c) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ kfree(*strp);
+ make_name_string(c);
+ *strp = c;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/*
+ * Definitions for UMP Block config
+ */
+
+/* define an uint option for block */
+#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval) \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+ char *page) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
+ format "\n", page); \
+} \
+ \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\
+ minval, maxval, page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+/* define a boolean option for block */
+#define F_MIDI2_BLOCK_BOOL_OPT(name) \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+ char *page) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
+ "%u\n", page); \
+} \
+ \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3);
+F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16);
+F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3);
+F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1);
+F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255);
+F_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2);
+F_MIDI2_BLOCK_BOOL_OPT(active);
+
+static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
+ char *page)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page);
+}
+
+static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128,
+ page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_block_opts_, name);
+
+static struct configfs_attribute *f_midi2_block_attrs[] = {
+ &f_midi2_block_opts_attr_direction,
+ &f_midi2_block_opts_attr_first_group,
+ &f_midi2_block_opts_attr_num_groups,
+ &f_midi2_block_opts_attr_ui_hint,
+ &f_midi2_block_opts_attr_midi_ci_version,
+ &f_midi2_block_opts_attr_sysex8_streams,
+ &f_midi2_block_opts_attr_is_midi1,
+ &f_midi2_block_opts_attr_active,
+ &f_midi2_block_opts_attr_name,
+ NULL,
+};
+
+static void f_midi2_block_opts_release(struct config_item *item)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ kfree(opts->info.name);
+ kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_block_item_ops = {
+ .release = f_midi2_block_opts_release,
+};
+
+static const struct config_item_type f_midi2_block_type = {
+ .ct_item_ops = &f_midi2_block_item_ops,
+ .ct_attrs = f_midi2_block_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
/* create a f_midi2_block_opts instance for the given block number */
static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
unsigned int blk,
struct f_midi2_block_opts **block_p)
{
struct f_midi2_block_opts *block_opts;
+ int ret = 0;
+
+ mutex_lock(&ep_opts->opts->lock);
+ if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
+ ret = -EBUSY;
+ goto out;
+ }
block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL);
- if (!block_opts)
- return -ENOMEM;
+ if (!block_opts) {
+ ret = -ENOMEM;
+ goto out;
+ }
block_opts->ep = ep_opts;
block_opts->id = blk;
@@ -1533,9 +1793,143 @@ static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
ep_opts->blks[blk] = block_opts;
*block_p = block_opts;
- return 0;
+
+ mutex_unlock(&ep_opts->opts->lock);
+ out:
+ return ret;
}
+/* make_group callback for a block */
+static struct config_group *
+f_midi2_opts_block_make(struct config_group *group, const char *name)
+{
+ struct f_midi2_ep_opts *ep_opts;
+ struct f_midi2_block_opts *block_opts;
+ unsigned int blk;
+ int ret;
+
+ if (strncmp(name, "block.", 6))
+ return ERR_PTR(-EINVAL);
+ ret = kstrtouint(name + 6, 10, &blk);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ep_opts = to_f_midi2_ep_opts(&group->cg_item);
+
+ if (blk >= SNDRV_UMP_MAX_BLOCKS)
+ return ERR_PTR(-EINVAL);
+ if (ep_opts->blks[blk])
+ return ERR_PTR(-EBUSY);
+ ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
+ if (ret)
+ return ERR_PTR(ret);
+
+ config_group_init_type_name(&block_opts->group, name,
+ &f_midi2_block_type);
+ return &block_opts->group;
+}
+
+/* drop_item callback for a block */
+static void
+f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
+{
+ struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item);
+
+ mutex_lock(&block_opts->ep->opts->lock);
+ block_opts->ep->blks[block_opts->id] = NULL;
+ mutex_unlock(&block_opts->ep->opts->lock);
+ config_item_put(item);
+}
+
+/*
+ * Definitions for UMP Endpoint config
+ */
+
+/* define an uint option for EP */
+#define F_MIDI2_EP_OPT(name, format, minval, maxval) \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_uint_show(opts->opts, opts->info.name, \
+ format "\n", page); \
+} \
+ \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len)\
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_uint_store(opts->opts, &opts->info.name, \
+ minval, maxval, page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+/* define a string option for EP */
+#define F_MIDI2_EP_STR_OPT(name, maxlen) \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\
+} \
+ \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+F_MIDI2_EP_OPT(protocol, "0x%x", 1, 2);
+F_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3);
+F_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff);
+F_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff);
+F_MIDI2_EP_STR_OPT(ep_name, 128);
+F_MIDI2_EP_STR_OPT(product_id, 128);
+
+static struct configfs_attribute *f_midi2_ep_attrs[] = {
+ &f_midi2_ep_opts_attr_protocol,
+ &f_midi2_ep_opts_attr_protocol_caps,
+ &f_midi2_ep_opts_attr_ep_name,
+ &f_midi2_ep_opts_attr_product_id,
+ &f_midi2_ep_opts_attr_manufacturer,
+ &f_midi2_ep_opts_attr_family,
+ &f_midi2_ep_opts_attr_model,
+ &f_midi2_ep_opts_attr_sw_revision,
+ NULL,
+};
+
+static void f_midi2_ep_opts_release(struct config_item *item)
+{
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+
+ kfree(opts->info.ep_name);
+ kfree(opts->info.product_id);
+ kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_ep_item_ops = {
+ .release = f_midi2_ep_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_ep_group_ops = {
+ .make_group = f_midi2_opts_block_make,
+ .drop_item = f_midi2_opts_block_drop,
+};
+
+static const struct config_item_type f_midi2_ep_type = {
+ .ct_item_ops = &f_midi2_ep_item_ops,
+ .ct_group_ops = &f_midi2_ep_group_ops,
+ .ct_attrs = f_midi2_ep_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
/* create a f_midi2_ep_opts instance */
static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
unsigned int index,
@@ -1559,7 +1953,119 @@ static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
return 0;
}
+/* make_group callback for an EP */
+static struct config_group *
+f_midi2_opts_ep_make(struct config_group *group, const char *name)
+{
+ struct f_midi2_opts *opts;
+ struct f_midi2_ep_opts *ep_opts;
+ unsigned int index;
+ int ret;
+
+ if (strncmp(name, "ep.", 3))
+ return ERR_PTR(-EINVAL);
+ ret = kstrtouint(name + 3, 10, &index);
+ if (ret)
+ return ERR_PTR(ret);
+
+ opts = to_f_midi2_opts(&group->cg_item);
+ if (index >= MAX_UMP_EPS)
+ return ERR_PTR(-EINVAL);
+ if (opts->eps[index])
+ return ERR_PTR(-EBUSY);
+ ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
+ if (ret)
+ return ERR_PTR(ret);
+
+ config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
+ return &ep_opts->group;
+}
+
+/* drop_item callback for an EP */
+static void
+f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
+{
+ struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
+
+ mutex_lock(&ep_opts->opts->lock);
+ ep_opts->opts->eps[ep_opts->index] = NULL;
+ mutex_unlock(&ep_opts->opts->lock);
+ config_item_put(item);
+}
+
+/*
+ * Definitions for card config
+ */
+
+/* define a bool option for card */
+#define F_MIDI2_BOOL_OPT(name) \
+static ssize_t f_midi2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_opts *opts = to_f_midi2_opts(item); \
+ return f_midi2_opts_uint_show(opts, opts->info.name, \
+ "%u\n", page); \
+} \
+ \
+static ssize_t f_midi2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_opts *opts = to_f_midi2_opts(item); \
+ return f_midi2_opts_bool_store(opts, &opts->info.name, \
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_opts_, name)
+
+F_MIDI2_BOOL_OPT(process_ump);
+F_MIDI2_BOOL_OPT(static_block);
+
+static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
+ char *page)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ return f_midi2_opts_str_show(opts, opts->info.iface_name, page);
+}
+
+static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128,
+ page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_opts_, iface_name);
+
+static struct configfs_attribute *f_midi2_attrs[] = {
+ &f_midi2_opts_attr_process_ump,
+ &f_midi2_opts_attr_static_block,
+ &f_midi2_opts_attr_iface_name,
+ NULL
+};
+
+static void f_midi2_opts_release(struct config_item *item)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_midi2_item_ops = {
+ .release = f_midi2_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_group_ops = {
+ .make_group = f_midi2_opts_ep_make,
+ .drop_item = f_midi2_opts_ep_drop,
+};
+
static const struct config_item_type f_midi2_func_type = {
+ .ct_item_ops = &f_midi2_item_ops,
+ .ct_group_ops = &f_midi2_group_ops,
+ .ct_attrs = f_midi2_attrs,
.ct_owner = THIS_MODULE,
};
@@ -1569,11 +2075,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
- /* we have only one EP and one FB */
- if (opts->eps[0]) {
- kfree(opts->eps[0]->blks[0]);
- kfree(opts->eps[0]);
- }
+ kfree(opts->info.iface_name);
kfree(opts);
}
@@ -1596,6 +2098,7 @@ static struct usb_function_instance *f_midi2_alloc_inst(void)
opts->info.num_reqs = 32;
opts->info.req_buf_size = 512;
+ /* create the default ep */
ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
if (ret) {
kfree(opts);
@@ -1612,6 +2115,15 @@ static struct usb_function_instance *f_midi2_alloc_inst(void)
config_group_init_type_name(&opts->func_inst.group, "",
&f_midi2_func_type);
+
+ config_group_init_type_name(&ep_opts->group, "ep.0",
+ &f_midi2_ep_type);
+ configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
+
+ config_group_init_type_name(&block_opts->group, "block.0",
+ &f_midi2_block_type);
+ configfs_add_default_group(&block_opts->group, &ep_opts->group);
+
return &opts->func_inst;
}
@@ -1630,12 +2142,58 @@ static void f_midi2_free(struct usb_function *f)
container_of(f->fi, struct f_midi2_opts, func_inst));
}
+/* verify the parameters set up via configfs;
+ * return the number of EPs or a negative error
+ */
+static int verify_parameters(struct f_midi2_opts *opts)
+{
+ int i, j, num_eps, num_blks;
+ struct f_midi2_ep_info *ep;
+ struct f_midi2_block_info *bp;
+
+ for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps];
+ num_eps++)
+ ;
+ if (!num_eps) {
+ pr_err("f_midi2: No EP is defined\n");
+ return -EINVAL;
+ }
+
+ num_blks = 0;
+ for (i = 0; i < num_eps; i++) {
+ ep = &opts->eps[i]->info;
+ if (!(ep->protocol_caps & ep->protocol)) {
+ pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n",
+ ep->protocol, ep->protocol_caps, i);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j];
+ j++, num_blks++) {
+ bp = &opts->eps[i]->blks[j]->info;
+ if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) {
+ pr_err("f_midi2: Invalid group definitions for block %d:%d\n",
+ i, j);
+ return -EINVAL;
+ }
+ }
+ }
+ if (!num_blks) {
+ pr_err("f_midi2: No block is defined\n");
+ return -EINVAL;
+ }
+
+ return num_eps;
+}
+
/* gadget alloc callback */
static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
{
struct f_midi2 *midi2;
struct f_midi2_opts *opts;
- int i;
+ struct f_midi2_ep *ep;
+ struct f_midi2_block *bp;
+ int i, num_eps, blk;
midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL);
if (!midi2)
@@ -1643,6 +2201,12 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
opts = container_of(fi, struct f_midi2_opts, func_inst);
mutex_lock(&opts->lock);
+ num_eps = verify_parameters(opts);
+ if (num_eps < 0) {
+ mutex_unlock(&opts->lock);
+ kfree(midi2);
+ return ERR_PTR(num_eps);
+ }
++opts->refcnt;
mutex_unlock(&opts->lock);
@@ -1658,17 +2222,20 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
midi2->func.free_func = f_midi2_free;
midi2->info = opts->info;
+ midi2->num_eps = num_eps;
- /* fixed 1 UMP EP and 1 UMP FB as of now */
- midi2->num_eps = 1;
- midi2->midi2_eps[0].info = opts->eps[0]->info;
- midi2->midi2_eps[0].card = midi2;
- midi2->midi2_eps[0].num_blks = 1;
- midi2->midi2_eps[0].blks[0].info = opts->eps[0]->blks[0]->info;
- midi2->midi2_eps[0].blks[0].gtb_id = 1;
-
- for (i = 0; i < midi2->num_eps; i++)
- midi2->total_blocks += midi2->midi2_eps[i].num_blks;
+ for (i = 0; i < num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ ep->info = opts->eps[i]->info;
+ ep->card = midi2;
+ for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS &&
+ opts->eps[i]->blks[blk]; blk++) {
+ bp = &ep->blks[blk];
+ ep->num_blks++;
+ bp->info = opts->eps[i]->blks[blk]->info;
+ bp->gtb_id = ++midi2->total_blocks;
+ }
+ }
midi2->string_defs = kcalloc(midi2->total_blocks + 1,
sizeof(*midi2->string_defs), GFP_KERNEL);
@@ -1678,10 +2245,18 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
}
if (opts->info.iface_name && *opts->info.iface_name)
- midi2->string_defs[0].s = opts->info.iface_name;
+ midi2->string_defs[STR_IFACE].s = opts->info.iface_name;
else
- midi2->string_defs[0].s = ump_ep_name(&midi2->midi2_eps[0]);
- midi2->string_defs[1].s = ump_fb_name(&midi2->midi2_eps[0].blks[0].info);
+ midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]);
+
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ for (blk = 0; blk < ep->num_blks; blk++) {
+ bp = &ep->blks[blk];
+ midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s =
+ ump_fb_name(&bp->info);
+ }
+ }
return &midi2->func;
}
]
|
| 268/2 |
2026/07/30 14:40 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 92115
output: 44
thoughts: 530
Thoughts: **Analyzing a Potential ConfigFS Leak in USB Gadget Driver**
Okay, let's break this down. My initial thought is that commit `29ee7a4dddd5` introduced the problem. It added `configfs_add_default_group` for `ep.0` and `block.0` *and* `f_midi2_free_inst`, but crucially, it *didn't* call `configfs_remove_default_groups` in the `f_midi2_free_inst` path. That feels like a likely culprit for a leak.
Now, the prompt wants me to see how *similar* problems were fixed in the past within this subsystem. That means looking for patterns. The focus is "missing locks, incorrect refcounting, or similar error path bugs."
Given the prompt, I'm already mentally searching for situations where default groups were dynamically created and leaked because `configfs_remove_default_groups` wasn't properly called or handled children recursively. I immediately think of `efbf0af70b4f`, since that introduced dynamic allocation of groups and added `uvcg_config_remove_children` to handle their cleanup, exactly what I suspect is going on here. But, wait, that *didn't* fix a bug, it introduced the *mechanism* for the dynamic allocation and cleanup. So it might be related, but not a direct fix for *this* issue.
I need to see if there are other commits fixing a *missing* `configfs_remove_default_groups`. I did a quick search for `configfs_remove_default_groups` within `drivers/usb/gadget/` but came up empty. Expanding my search across the *entire* kernel, I did find `3d2825c8c610` ("fs: dlm: fix configfs memory leak"). That one addressed a memory leak where `configfs_remove_default_groups` didn't free memory if a child lacked a release function. But, in *this* specific case, `f_midi2_ep_type` and `f_midi2_block_type` *do* have release functions. So `configfs_remove_default_groups` *should* clean things up properly here. It seems like the core issue is the missing call to begin with.
Now, just to be thorough, I'm going to search for any other commits in `drivers/usb/gadget/` that mention "configfs" and "leak". It will be good to see if there are any other situations addressed.
|
| 269/2 |
2026/07/30 14:41 |
tool |
git-log |
0m
Args: map[MessageRegexps:[configfs leak] PathPrefix:drivers/usb/gadget/ Since:10 years]
Results: map[Output:70a3288a7586 usb: gadget: f_hid: fix refcount leak on error path
86f3daed59bc usb: gadget: uvc: configfs: Drop leaked references to config items
ff74745e6d3d usb: gadget: configfs: Fix memory leak of interface directory data
]
|
| 270/2 |
2026/07/30 14:41 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 93336
output: 24
thoughts: 15
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're mine:
**Checking `86f3daed59bc`**
Right, let's take a look at `86f3daed59bc`. That looks like a commit hash, doesn't it? Given my expertise, I immediately start running through possibilities. Is it from a repository I'm familiar with? Is this related to a specific project? My mind's already racing, trying to recall relevant contexts. I might have seen this in a log, a build report, or maybe even in a recent email thread. The number alone isn't telling me much, but it *signals* something specific, something worth investigating. My first impulse is to try and pinpoint its origin – is it a local branch? A remote one? And what tools do I have access to that will allow me to track this down efficiently? A quick `git log` lookup sounds like a good starting point. Or, depending on where I saw it, perhaps a more targeted search within the relevant codebase. This could be anything from a critical bug fix to a major feature addition, so it’s essential to approach it with the appropriate level of analysis, and depending on the context in which it was spotted, I might need to consider the level of urgency.
|
| 271/2 |
2026/07/30 14:41 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 86f3daed59bceb4fa7981d85e89f63ebbae1d561
Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Date: Thu Aug 2 00:14:00 2018 +0300
usb: gadget: uvc: configfs: Drop leaked references to config items
Some of the .allow_link() and .drop_link() operations implementations
call config_group_find_item() and then leak the reference to the
returned item. Fix this by dropping those references where needed.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index dbc95c9558de..8d513cc6fb8c 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -529,6 +529,7 @@ static int uvcg_control_class_allow_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
return ret;
}
@@ -564,6 +565,7 @@ static void uvcg_control_class_drop_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
}
@@ -2026,6 +2028,7 @@ static int uvcg_streaming_class_allow_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
return ret;
}
@@ -2066,6 +2069,7 @@ static void uvcg_streaming_class_drop_link(struct config_item *src,
unlock:
mutex_unlock(&opts->lock);
out:
+ config_item_put(header);
mutex_unlock(su_mutex);
}
]
|
| 272/2 |
2026/07/30 14:41 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 94309
output: 26
thoughts: 31
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Diving into `config_group_find_item()`**
Right, this looks like we're digging into the guts of how this configuration system works. Specifically, we're talking about the `config_group_find_item()` function. That's a key piece; it's the mechanism that actually locates a specific configuration item within a group, likely based on some sort of key or identifier. Understanding its behavior is critical for debugging, performance optimization, and general maintenance. I need to be sure I'm crystal clear on this function.
Let's see… looks like we're referencing a specific commit, `ff74745e6d3d`. This is smart. A specific commit means we can pin down *exactly* what state the code was in at that point. Makes the review that much cleaner. I need to understand what this commit did to this specific function - were there bug fixes, new features, or performance improvements? This commit might reveal some subtle but important nuances or design decisions. Time to dive in and understand the changes.
|
| 273/2 |
2026/07/30 14:41 |
tool |
git-show |
0m
Args:
Results: map[Output:commit ff74745e6d3d97a865eda8c1f3fd29c13b79f0cc
Author: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Date: Sat Sep 30 08:54:52 2017 -0700
usb: gadget: configfs: Fix memory leak of interface directory data
Kmemleak checking configuration reports a memory leak in
usb_os_desc_prepare_interf_dir function when rndis function
instance is freed and then allocated again. For example, this
happens with FunctionFS driver with RNDIS function enabled
when "ffs-test" test application is run several times in a row.
The data for intermediate "os_desc" group for interface directories
is allocated as a single VLA chunk and (after a change of default
groups handling) is not ever freed and actually not stored anywhere
besides inside a list of default groups of a parent group.
The fix is to make usb_os_desc_prepare_interf_dir function return
a pointer to allocated data (as a pointer to the first VLA item)
instead of (an unused) integer and to make the caller component
(currently the only one is RNDIS function) responsible for storing
the pointer and freeing the memory when appropriate.
Fixes: 1ae1602de028 ("configfs: switch ->default groups to a linked list")
Cc: stable@vger.kernel.org
Signed-off-by: Andrew Gabbasov <andrew_gabbasov@mentor.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index a22a892de7b7..aeb9f3c40521 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1143,11 +1143,12 @@ static struct configfs_attribute *interf_grp_attrs[] = {
NULL
};
-int usb_os_desc_prepare_interf_dir(struct config_group *parent,
- int n_interf,
- struct usb_os_desc **desc,
- char **names,
- struct module *owner)
+struct config_group *usb_os_desc_prepare_interf_dir(
+ struct config_group *parent,
+ int n_interf,
+ struct usb_os_desc **desc,
+ char **names,
+ struct module *owner)
{
struct config_group *os_desc_group;
struct config_item_type *os_desc_type, *interface_type;
@@ -1159,7 +1160,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent,
char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
if (!vlabuf)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group);
os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type);
@@ -1184,7 +1185,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent,
configfs_add_default_group(&d->group, os_desc_group);
}
- return 0;
+ return os_desc_group;
}
EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h
index 36c468c4f5e9..540d5e92ed22 100644
--- a/drivers/usb/gadget/configfs.h
+++ b/drivers/usb/gadget/configfs.h
@@ -5,11 +5,12 @@
void unregister_gadget_item(struct config_item *item);
-int usb_os_desc_prepare_interf_dir(struct config_group *parent,
- int n_interf,
- struct usb_os_desc **desc,
- char **names,
- struct module *owner);
+struct config_group *usb_os_desc_prepare_interf_dir(
+ struct config_group *parent,
+ int n_interf,
+ struct usb_os_desc **desc,
+ char **names,
+ struct module *owner);
static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item)
{
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index e1d5853ef1e4..c7c5b3ce1d98 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -908,6 +908,7 @@ static void rndis_free_inst(struct usb_function_instance *f)
free_netdev(opts->net);
}
+ kfree(opts->rndis_interf_group); /* single VLA chunk */
kfree(opts);
}
@@ -916,6 +917,7 @@ static struct usb_function_instance *rndis_alloc_inst(void)
struct f_rndis_opts *opts;
struct usb_os_desc *descs[1];
char *names[1];
+ struct config_group *rndis_interf_group;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
@@ -940,8 +942,14 @@ static struct usb_function_instance *rndis_alloc_inst(void)
names[0] = "rndis";
config_group_init_type_name(&opts->func_inst.group, "",
&rndis_func_type);
- usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
- names, THIS_MODULE);
+ rndis_interf_group =
+ usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
+ names, THIS_MODULE);
+ if (IS_ERR(rndis_interf_group)) {
+ rndis_free_inst(&opts->func_inst);
+ return ERR_CAST(rndis_interf_group);
+ }
+ opts->rndis_interf_group = rndis_interf_group;
return &opts->func_inst;
}
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index a35ee3c2545d..efdb7ac381d9 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -26,6 +26,7 @@ struct f_rndis_opts {
bool bound;
bool borrowed_net;
+ struct config_group *rndis_interf_group;
struct usb_os_desc rndis_os_desc;
char rndis_ext_compat_id[16];
]
|
| 274/2 |
2026/07/30 14:41 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 96647
output: 732
thoughts: 765
Thoughts: **Analyzing the ConfigFS Memory Leak: A Path to the Correct Solution**
Right, I've got it. That commit `ff74745e6d3d` ("usb: gadget: configfs: Fix memory leak of interface directory data") describes *precisely* the situation I'm looking at. The message outlines a scenario where data for an "os_desc" group within an interface directory was being allocated as a VLA chunk and never freed because the default group handling changed, and the memory wasn't being managed properly.
They fixed it by manually freeing the memory of the default group (`kfree(opts->rndis_interf_group);`) directly. My initial thought was: Why didn't they use `configfs_remove_default_groups()`? Ah, because `configfs_remove_default_groups()` drops the refcount and calls the release function. However, the `os_desc` group *didn't* have a release function! In their situation, if there wasn't a release function, then `configfs_remove_default_groups()` *wouldn't* free the memory, so the manual `kfree()` was the right solution.
But, here's the crucial difference: in *our* case, `f_midi2_ep_type` and `f_midi2_block_type` *do* have `release` functions! So the correct fix for *us* is to use `configfs_remove_default_groups()`.
Let's break down what I've discovered:
1. The core issue is that dynamically allocated configfs default groups aren't automatically freed when their parent is removed because their reference counts aren't dropped.
2. I've found similar bugs fixed in two primary ways:
a. Manually removing the default groups and dropping refcounts. An example is commit `efbf0af70b4f` ("usb: gadget: uvc: configfs: Allocate groups dynamically"). Here, `uvcg_config_remove_children()` was introduced to manually iterate through `default_groups`, remove them from the list, and call `config_item_put()` recursively.
b. Manually freeing the memory of the default groups. Commit `ff74745e6d3d` ("usb: gadget: configfs: Fix memory leak of interface directory data") is one such example. The `os_desc` default group memory was freed with `kfree()` within the parent's release function because the group lacked a configfs release function.
3. I've also seen a similar issue, outside the USB gadget space, in `3d2825c8c610` ("fs: dlm: fix configfs memory leak"), where memory was manually freed in the parent's release function.
4. Crucially, because `f_midi2_ep_type` and `f_midi2_block_type` already have `release` functions (`f_midi2_ep_opts_release` and `f_midi2_block_opts_release`), the right approach here is to call `configfs_remove_default_groups()` in the parent's release function. This will reduce the refcounts of the default groups, which in turn invokes their respective release functions. These will free the memory correctly, and we'll avoid any need for manual list manipulation or direct calls to `kfree()` on child objects. That's the key to resolving this memory leak!
|
| 275/1 |
2026/07/30 14:41 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp1348468677]
|
| 276/1 |
2026/07/30 14:41 |
loop |
|
23m
|
| 277/2 |
2026/07/30 14:41 |
iteration |
0 |
23m
|
| 278/3 |
2026/07/30 14:41 |
agent |
patch-generator |
3m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
2026/07/30 13:30:35 executed programs: 5
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504 [inline]
f_midi2_alloc_inst+0x80/0x2c0 drivers/usb/gadget/function/f_midi2.c:2666
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342 [inline]
f_midi2_alloc_inst+0x16a/0x2c0 drivers/usb/gadget/function/f_midi2.c:2673
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF
The explanation of the root cause of the bug is:
The root cause of this memory leak is that the driver fails to drop the reference counts of its default configfs groups when the parent group is destroyed.
When a USB function instance for `midi2` is allocated via `f_midi2_alloc_inst()`, it dynamically allocates `ep_opts` and `block_opts` structures using `kzalloc_obj()`. These structures are then initialized as configfs groups and added as default groups to their respective parents:
- `ep.0` is added as a default group to the function instance group (`opts->func_inst.group`).
- `block.0` is added as a default group to the endpoint group (`ep_opts->group`).
When the user removes the configfs directory for the function instance (via `rmdir`), configfs removes the dentries for the default groups but **does not** automatically drop their reference counts. The responsibility of dropping the refcounts of default groups lies with the driver, typically by calling `configfs_remove_default_groups()` when the parent group is released.
However, `f_midi2_free_inst()` (which is called when the function instance's refcount reaches 0) only frees the `f_midi2_opts` structure and does not clean up its default groups. Similarly, `f_midi2_ep_opts_release()` does not clean up its default groups. As a result, the `ep_opts` and `block_opts` structures are leaked.
### How to fix it
To fix the leak, you need to explicitly remove the default groups and drop their reference counts before freeing the parent structures. This can be done by calling `configfs_remove_default_groups()` in the respective release functions.
1. In `f_midi2_free_inst()`, drop the default groups of the function instance (this will drop the refcount of `ep.0` and trigger `f_midi2_ep_opts_release`):
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group); /* ADD THIS */
kfree(opts->info.iface_name);
kfree(opts);
}
```
2. In `f_midi2_ep_opts_release()`, drop the default groups of the endpoint (this will drop the refcount of `block.0` and trigger `f_midi2_block_opts_release`):
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item)); /* ADD THIS */
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
With these changes, the hierarchy of default groups will be properly unrolled and freed when the function instance is destroyed, whether it's through configfs `rmdir` or an error path that directly calls `usb_put_function_instance()`.
Historical context on how similar bugs were fixed in the past:
Based on the historical research of similar bugs in the kernel, here is a summary of the findings and the recommended patterns for fixing this issue.
### Findings
The root cause of the bug is that dynamically allocated configfs default groups are not automatically cleaned up when their parent is destroyed. While configfs removes the dentries, it does not automatically drop the reference counts of the default groups. If the driver does not explicitly drop these references, the configfs items (and their dynamically allocated memory) will leak.
In the past, similar issues in the USB gadget subsystem and other configfs users were addressed using two main patterns:
1. **Manually dropping references recursively:**
In commit `efbf0af70b4f` ("usb: gadget: uvc: configfs: Allocate groups dynamically"), the UVC gadget driver was refactored to dynamically allocate its configfs groups. To prevent memory leaks, the developers introduced a custom `uvcg_config_remove_children()` function. This function manually iterates over the `default_groups` list of a parent, removes each child from the list, recursively cleans up the child's children, and finally calls `config_item_put()` to drop the reference count and trigger the release function.
2. **Manually freeing the child's memory in the parent's release function:**
In commit `ff74745e6d3d` ("usb: gadget: configfs: Fix memory leak of interface directory data"), a memory leak of the `os_desc` default group in the RNDIS function was fixed. Because the `os_desc` group did not have a dedicated configfs `release` callback, the developers fixed the leak by saving a pointer to the dynamically allocated group and explicitly calling `kfree()` on it inside the parent's release function (`rndis_free_inst`).
A similar fix was applied outside the USB subsystem in commit `3d2825c8c610` ("fs: dlm: fix configfs memory leak"), where a default group's memory was manually freed in the parent's release function because `configfs_remove_default_groups()` alone wouldn't free the memory without a release callback.
### Recommended Fix Strategy
For the `f_midi2` bug, the proposed fix of calling `configfs_remove_default_groups()` is the most idiomatic and robust approach.
Unlike the RNDIS or DLM bugs mentioned above, the `ep.0` and `block.0` groups in `f_midi2` **already have** proper configfs release callbacks implemented (`f_midi2_ep_opts_release` and `f_midi2_block_opts_release` respectively), which correctly call `kfree()`.
Therefore, you do not need to manually `kfree()` the child structures or write a custom recursive cleanup function like UVC did. You should follow the pattern of explicitly unrolling the default groups by calling `configfs_remove_default_groups()` in the respective parent's release functions:
- Call it in `f_midi2_free_inst()` to drop the refcount of `ep.0`.
- Call it in `f_midi2_ep_opts_release()` to drop the refcount of `block.0`.
This will correctly drop the reference counts, trigger the existing release callbacks, and safely free the dynamically allocated memory in the correct order.
Reply: I have implemented the fix for the memory leak in the `f_midi2` USB gadget function driver.
### What I did
I added calls to `configfs_remove_default_groups()` in the release functions of the configfs groups that have default groups attached to them:
1. In `f_midi2_free_inst()`, I added `configfs_remove_default_groups(&opts->func_inst.group);` to drop the reference count of the default `ep.0` group.
2. In `f_midi2_ep_opts_release()`, I added `configfs_remove_default_groups(to_config_group(item));` to drop the reference count of the default `block.0` group.
### Why I did it
When a USB function instance for `midi2` is allocated, it dynamically allocates `ep_opts` and `block_opts` structures and adds them as default groups (`ep.0` and `block.0`). While configfs removes the dentries for default groups when the parent directory is removed, it does not automatically drop their reference counts. This responsibility lies with the driver.
By explicitly calling `configfs_remove_default_groups()` in the respective release functions, we ensure that the reference counts of the default groups are properly dropped. This correctly triggers the existing release callbacks (`f_midi2_ep_opts_release` and `f_midi2_block_opts_release`), which safely free the dynamically allocated memory in the correct order.
This approach is robust and idiomatic. It also safely handles dynamically created groups (e.g., `ep.1` created via `mkdir`), because their `default_groups` list is properly initialized and empty, making `configfs_remove_default_groups()` a safe no-op for them.
|
| 279/4 |
2026/07/30 14:41 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 7272
output: 34
thoughts: 143
Thoughts: **Memory Leak in USB MIDI Gadget Driver**
Okay, so I've got a problem with a memory leak, specifically in `drivers/usb/gadget/function/f_midi2.c`. It looks like we're not properly cleaning up the configfs groups when they're released. The root cause appears to be the omission of `configfs_remove_default_groups()` calls in the release functions of those configfs groups.
The suggestion is pretty straightforward: add `configfs_remove_default_groups(&opts->func_inst.group);` to `f_midi2_free_inst()` and `configfs_remove_default_groups(to_config_group(item));` to `f_midi2_ep_opts_release()`. This should plug the leak. Time to dig into `drivers/usb/gadget/function/f_midi2.c` and confirm the proposed solution. Let's see if this aligns with my understanding of the configfs cleanup process.
|
| 280/4 |
2026/07/30 14:42 |
tool |
codesearch-file-index |
0m
Args: map[SourceFile:drivers/usb/gadget/function/f_midi2.c]
Results: map[Entities:[map[Kind:function Name:append_config] map[Kind:function Name:append_configs] map[Kind:function Name:append_midi1_in_jack] map[Kind:function Name:append_midi1_out_jack] map[Kind:function Name:assign_block_descriptors] map[Kind:function Name:do_f_midi2_free] map[Kind:function Name:f_midi2_alloc] map[Kind:function Name:f_midi2_alloc_ep_reqs] map[Kind:function Name:f_midi2_alloc_inst] map[Kind:function Name:f_midi2_bind] map[Kind:function Name:f_midi2_block_opts_active_show] map[Kind:function Name:f_midi2_block_opts_active_store] map[Kind:function Name:f_midi2_block_opts_create] map[Kind:function Name:f_midi2_block_opts_direction_show] map[Kind:function Name:f_midi2_block_opts_direction_store] map[Kind:function Name:f_midi2_block_opts_first_group_show] map[Kind:function Name:f_midi2_block_opts_first_group_store] map[Kind:function Name:f_midi2_block_opts_is_midi1_show] map[Kind:function Name:f_midi2_block_opts_is_midi1_store] map[Kind:function Name:f_midi2_block_opts_midi1_first_group_show] map[Kind:function Name:f_midi2_block_opts_midi1_first_group_store] map[Kind:function Name:f_midi2_block_opts_midi1_num_groups_show] map[Kind:function Name:f_midi2_block_opts_midi1_num_groups_store] map[Kind:function Name:f_midi2_block_opts_midi_ci_version_show] map[Kind:function Name:f_midi2_block_opts_midi_ci_version_store] map[Kind:function Name:f_midi2_block_opts_name_show] map[Kind:function Name:f_midi2_block_opts_name_store] map[Kind:function Name:f_midi2_block_opts_num_groups_show] map[Kind:function Name:f_midi2_block_opts_num_groups_store] map[Kind:function Name:f_midi2_block_opts_release] map[Kind:function Name:f_midi2_block_opts_sysex8_streams_show] map[Kind:function Name:f_midi2_block_opts_sysex8_streams_store] map[Kind:function Name:f_midi2_block_opts_ui_hint_show] map[Kind:function Name:f_midi2_block_opts_ui_hint_store] map[Kind:function Name:f_midi2_create_card] map[Kind:function Name:f_midi2_create_usb_configs] map[Kind:function Name:f_midi2_disable] map[Kind:function Name:f_midi2_drop_reqs] map[Kind:function Name:f_midi2_ep_in_complete] map[Kind:function Name:f_midi2_ep_opts_create] map[Kind:function Name:f_midi2_ep_opts_ep_name_show] map[Kind:function Name:f_midi2_ep_opts_ep_name_store] map[Kind:function Name:f_midi2_ep_opts_family_show] map[Kind:function Name:f_midi2_ep_opts_family_store] map[Kind:function Name:f_midi2_ep_opts_manufacturer_show] map[Kind:function Name:f_midi2_ep_opts_manufacturer_store] map[Kind:function Name:f_midi2_ep_opts_model_show] map[Kind:function Name:f_midi2_ep_opts_model_store] map[Kind:function Name:f_midi2_ep_opts_product_id_show] map[Kind:function Name:f_midi2_ep_opts_product_id_store] map[Kind:function Name:f_midi2_ep_opts_protocol_caps_show] map[Kind:function Name:f_midi2_ep_opts_protocol_caps_store] map[Kind:function Name:f_midi2_ep_opts_protocol_show] map[Kind:function Name:f_midi2_ep_opts_protocol_store] map[Kind:function Name:f_midi2_ep_opts_release] map[Kind:function Name:f_midi2_ep_opts_sw_revision_show] map[Kind:function Name:f_midi2_ep_opts_sw_revision_store] map[Kind:function Name:f_midi2_ep_out_complete] map[Kind:function Name:f_midi2_free] map[Kind:function Name:f_midi2_free_card] map[Kind:function Name:f_midi2_free_ep] map[Kind:function Name:f_midi2_free_ep_reqs] map[Kind:function Name:f_midi2_free_inst] map[Kind:function Name:f_midi2_free_usb_configs] map[Kind:function Name:f_midi2_get_alt] map[Kind:function Name:f_midi2_init_ep] map[Kind:function Name:f_midi2_init_midi2_ep_in] map[Kind:function Name:f_midi2_init_midi2_ep_out] map[Kind:function Name:f_midi2_midi1_ep_in_complete] map[Kind:function Name:f_midi2_midi1_ep_out_complete] map[Kind:function Name:f_midi2_operation_mode_get] map[Kind:function Name:f_midi2_operation_mode_info] map[Kind:function Name:f_midi2_opts_block_drop] map[Kind:function Name:f_midi2_opts_block_make] map[Kind:function Name:f_midi2_opts_bool_store] map[Kind:function Name:f_midi2_opts_ep_drop] map[Kind:function Name:f_midi2_opts_ep_make] map[Kind:function Name:f_midi2_opts_iface_name_show] map[Kind:function Name:f_midi2_opts_iface_name_store] map[Kind:function Name:f_midi2_opts_process_ump_show] map[Kind:function Name:f_midi2_opts_process_ump_store] map[Kind:function Name:f_midi2_opts_release] map[Kind:function Name:f_midi2_opts_static_block_show] map[Kind:function Name:f_midi2_opts_static_block_store] map[Kind:function Name:f_midi2_opts_str_show] map[Kind:function Name:f_midi2_opts_str_store] map[Kind:function Name:f_midi2_opts_uint_show] map[Kind:function Name:f_midi2_opts_uint_store] map[Kind:function Name:f_midi2_queue_out_reqs] map[Kind:function Name:f_midi2_set_alt] map[Kind:function Name:f_midi2_setup] map[Kind:function Name:f_midi2_start_ep] map[Kind:function Name:f_midi2_start_eps] map[Kind:function Name:f_midi2_stop_eps] map[Kind:function Name:f_midi2_ump_close] map[Kind:function Name:f_midi2_ump_drain] map[Kind:function Name:f_midi2_ump_open] map[Kind:function Name:f_midi2_ump_trigger] map[Kind:function Name:f_midi2_unbind] map[Kind:function Name:fill_midi1_cable_mapping] map[Kind:function Name:fill_midi1_pending_buf] map[Kind:function Name:fill_midi2_class_desc] map[Kind:function Name:get_empty_request] map[Kind:function Name:make_name_string] map[Kind:function Name:midi2mod_exit] map[Kind:function Name:midi2mod_init] map[Kind:function Name:process_midi1_byte] map[Kind:function Name:process_midi1_pending_buf] map[Kind:function Name:process_midi1_transmit] map[Kind:function Name:process_ump] map[Kind:function Name:process_ump_stream_msg] map[Kind:function Name:process_ump_transmit] map[Kind:function Name:put_empty_request] map[Kind:function Name:queue_request_ep_in] map[Kind:function Name:queue_request_ep_raw] map[Kind:function Name:reply_ep_in] map[Kind:function Name:reply_ump_stream_ep_config] map[Kind:function Name:reply_ump_stream_ep_device] map[Kind:function Name:reply_ump_stream_ep_info] map[Kind:function Name:reply_ump_stream_ep_name] map[Kind:function Name:reply_ump_stream_ep_pid] map[Kind:function Name:reply_ump_stream_fb_info] map[Kind:function Name:reply_ump_stream_fb_name] map[Kind:function Name:reply_ump_stream_string] map[Kind:function Name:reverse_dir] map[Kind:function Name:to_f_midi2_block_opts] map[Kind:function Name:to_f_midi2_ep_opts] map[Kind:function Name:to_f_midi2_opts] map[Kind:function Name:ump_ep_name] map[Kind:function Name:ump_fb_name] map[Kind:function Name:ump_product_id] map[Kind:function Name:ump_to_usb_dir] map[Kind:function Name:verify_parameters] map[Kind:struct Name:f_midi2] map[Kind:struct Name:f_midi2_block] map[Kind:struct Name:f_midi2_ep] map[Kind:struct Name:f_midi2_midi1_port] map[Kind:struct Name:f_midi2_req_ctx] map[Kind:struct Name:f_midi2_usb_config] map[Kind:struct Name:f_midi2_usb_ep] map[Kind:struct Name:midi1_cable_mapping] map[Kind:struct Name:uac1_ac_header_descriptor_1] map[Kind:struct Name:usb_midi_out_jack_descriptor_1] map[Kind:struct Name:usb_ms20_endpoint_descriptor_32] map[Kind:struct Name:usb_ms_endpoint_descriptor_16] map[Kind:global_variable Name:__UNIQUE_ID_addressable_midi2mod_init_542] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_540] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_543] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_544] map[Kind:global_variable Name:__UNIQUE_ID_modinfo_545] map[Kind:global_variable Name:__exitcall_midi2mod_exit] map[Kind:global_variable Name:f_midi2_attrs] map[Kind:global_variable Name:f_midi2_block_attrs] map[Kind:global_variable Name:f_midi2_block_item_ops] map[Kind:global_variable Name:f_midi2_block_opts_attr_active] map[Kind:global_variable Name:f_midi2_block_opts_attr_direction] map[Kind:global_variable Name:f_midi2_block_opts_attr_first_group] map[Kind:global_variable Name:f_midi2_block_opts_attr_is_midi1] map[Kind:global_variable Name:f_midi2_block_opts_attr_midi1_first_group] map[Kind:global_variable Name:f_midi2_block_opts_attr_midi1_num_groups] map[Kind:global_variable Name:f_midi2_block_opts_attr_midi_ci_version] map[Kind:global_variable Name:f_midi2_block_opts_attr_name] map[Kind:global_variable Name:f_midi2_block_opts_attr_num_groups] map[Kind:global_variable Name:f_midi2_block_opts_attr_sysex8_streams] map[Kind:global_variable Name:f_midi2_block_opts_attr_ui_hint] map[Kind:global_variable Name:f_midi2_block_type] map[Kind:global_variable Name:f_midi2_desc_mutex] map[Kind:global_variable Name:f_midi2_ep_attrs] map[Kind:global_variable Name:f_midi2_ep_group_ops] map[Kind:global_variable Name:f_midi2_ep_item_ops] map[Kind:global_variable Name:f_midi2_ep_opts_attr_ep_name] map[Kind:global_variable Name:f_midi2_ep_opts_attr_family] map[Kind:global_variable Name:f_midi2_ep_opts_attr_manufacturer] map[Kind:global_variable Name:f_midi2_ep_opts_attr_model] map[Kind:global_variable Name:f_midi2_ep_opts_attr_product_id] map[Kind:global_variable Name:f_midi2_ep_opts_attr_protocol] map[Kind:global_variable Name:f_midi2_ep_opts_attr_protocol_caps] map[Kind:global_variable Name:f_midi2_ep_opts_attr_sw_revision] map[Kind:global_variable Name:f_midi2_ep_type] map[Kind:global_variable Name:f_midi2_func_type] map[Kind:global_variable Name:f_midi2_group_ops] map[Kind:global_variable Name:f_midi2_item_ops] map[Kind:global_variable Name:f_midi2_opts_attr_iface_name] map[Kind:global_variable Name:f_midi2_opts_attr_process_ump] map[Kind:global_variable Name:f_midi2_opts_attr_static_block] map[Kind:global_variable Name:f_midi2_ump_ops] map[Kind:global_variable Name:gtb_desc] map[Kind:global_variable Name:gtb_header_desc] map[Kind:global_variable Name:midi2_audio_class_desc] map[Kind:global_variable Name:midi2_audio_descs] map[Kind:global_variable Name:midi2_audio_if_desc] map[Kind:global_variable Name:midi2_midi1_class_desc] map[Kind:global_variable Name:midi2_midi1_descs] map[Kind:global_variable Name:midi2_midi1_ep_in_class_desc] map[Kind:global_variable Name:midi2_midi1_ep_in_desc] map[Kind:global_variable Name:midi2_midi1_ep_in_descs] map[Kind:global_variable Name:midi2_midi1_ep_in_ss_comp_desc] map[Kind:global_variable Name:midi2_midi1_ep_in_ss_descs] map[Kind:global_variable Name:midi2_midi1_ep_out_class_desc] map[Kind:global_variable Name:midi2_midi1_ep_out_desc] map[Kind:global_variable Name:midi2_midi1_ep_out_descs] map[Kind:global_variable Name:midi2_midi1_ep_out_ss_comp_desc] map[Kind:global_variable Name:midi2_midi1_ep_out_ss_descs] map[Kind:global_variable Name:midi2_midi1_if_desc] map[Kind:global_variable Name:midi2_midi2_class_desc] map[Kind:global_variable Name:midi2_midi2_descs] map[Kind:global_variable Name:midi2_midi2_ep_in_ss_comp_desc] map[Kind:global_variable Name:midi2_midi2_ep_out_ss_comp_desc] map[Kind:global_variable Name:midi2_midi2_if_desc] map[Kind:global_variable Name:midi2usb_func] map[Kind:global_variable Name:operation_mode_ctl]]]
|
| 281/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 12277
output: 47
thoughts: 28
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Diving into MIDI-related Memory Management**
Alright, let's zero in on these two functions: `f_midi2_free_inst` and `f_midi2_ep_opts_release`. Immediately, the naming convention suggests these are intimately tied to memory management, particularly the release or freeing of resources related to MIDI 2.0 implementations. Given my expertise, I suspect `f_midi2_free_inst` likely handles the deallocation of an "instance," probably a structure or object representing a MIDI 2.0 instrument or endpoint. I'd anticipate it would involve recursive freeing of any dynamically allocated memory within that instance, ensuring no memory leaks. The emphasis on "free" strongly implies it's the counterpart to some prior "alloc" or "new" call.
`f_midi2_ep_opts_release`, on the other hand, the "ep_opts" leads me to believe this function is tied to the release of options or settings related to a MIDI 2.0 endpoint (EP). Perhaps we are talking about configuration parameters, capabilities, or other EP-specific metadata. It's likely involved with cleaning up data structures holding these options, maybe by freeing individual option elements, or a larger container holding them. It's a key function for preventing resource exhaustion when working with multiple MIDI devices or configurations.
Now I need to go deeper. What are the data structures these functions are meant to operate on? And most importantly, how do these functions interact with each other to guarantee that memory is managed safely and efficiently? I'm already formulating some questions in my head: What is the exact scope of these "instances" and "endpoint options"? Are there reference counting mechanisms or garbage collection strategies at play? What kind of error handling is implemented to manage potential issues during freeing or release operations? This is important. These two functions are the keys to a stable and robust implementation. Let's see if the code confirms my initial assessment.
|
| 282/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_free_inst]
Results: map[SourceCode:2636: static void f_midi2_free_inst(struct usb_function_instance *f)
2637: {
2638: struct f_midi2_opts *opts;
2639:
2640: opts = container_of(f, struct f_midi2_opts, func_inst);
2641:
2642: kfree(opts->info.iface_name);
2643: kfree(opts);
2644: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 283/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 12947
output: 49
thoughts: 13
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, and delivered as a first-person summary:
**Digging into Release Optimization**
Alright, so I'm thinking about `f_midi2_ep_opts_release`. This function, I assume, is crucial for handling the release of something within a MIDI 2.0 endpoint options context. I'm immediately trying to unpack what precisely is being released. Is it resources? Memory allocated for some data structure? Handles to external devices or services? Or perhaps metadata? The "opts" part suggests there are several configurable options associated with this "release" operation. My mind is already running through potential scenarios: the graceful shutdown of a MIDI connection, deallocating buffer space after a transmission, closing a handle to a device that is no longer being used, and so on.
I need to understand the implications of this release. Are we talking about a simple deallocation, or a more involved process? Does this release function handle error conditions? Does it signal anything to other parts of the system? Are there potential race conditions I need to worry about if multiple threads are vying for the same resources? Does this release function need to free the structure and all its sub-elements in a well-defined way, or just the parent structure? I'm already envisioning potential problems with dangling pointers or memory leaks if this isn't handled meticulously. This release function could be critical. I must deeply understand its operation.
|
| 284/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_ep_opts_release]
Results: map[SourceCode:2472: static void f_midi2_ep_opts_release(struct config_item *item)
2473: {
2474: struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
2475:
2476: kfree(opts->info.ep_name);
2477: kfree(opts->info.product_id);
2478: kfree(opts);
2479: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 285/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 13484
output: 49
thoughts: 83
Thoughts: Here's my thought process, as if I were reviewing this code:
**Cleaning Up ConfigFS Resources in the MIDI Driver**
Okay, so I need to make some resource cleanup changes in the MIDI driver. Specifically, I'm thinking about how the `configfs` framework is managed, and I need to ensure that the default groups are properly removed to avoid any potential leaks or dangling pointers. I'm going to add `configfs_remove_default_groups(&opts->func_inst.group);` to the `f_midi2_free_inst` function. That seems straightforward enough to handle the instance's group.
Then, for the endpoint options, I need to add `configfs_remove_default_groups(to_config_group(item));` in the `f_midi2_ep_opts_release` function. This should take care of releasing the default group associated with those options. I need to make sure I am using the right group in each case, but this seems correct.
Now, before I commit these changes, it's a good practice to scan the rest of the file and confirm I haven't missed any other similar release functions where the `configfs` default groups need cleanup. I'll quickly look for other functions that handle resource release in the `f_midi2` driver file, just to be thorough. It's best to be systematic about this type of cleanup to prevent future problems.
|
| 286/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_block_opts_release]
Results: map[SourceCode:2310: static void f_midi2_block_opts_release(struct config_item *item)
2311: {
2312: struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
2313:
2314: kfree(opts->info.name);
2315: kfree(opts);
2316: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 287/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 14069
output: 49
thoughts: 38
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of MIDI 2.0 block option management:
**Digging into MIDI 2.0 Block Option Creation and Release**
Right, so `f_midi2_block_opts_release` doesn't seem to be setting any default groups. That’s a key observation. I need to understand why and how the groups are being handled, because that's fundamental to how the block options interact. It's likely that default groups are being set elsewhere, probably in the creation function or perhaps even in a related API call. Therefore, I need to understand how groups are being established in the block option structure, before the option block is being released. My next logical step is to dive right into the `f_midi2_block_opts_create` function. That's where I would expect to find the mechanism that sets up any initial state, including those crucial group assignments that `release` seemingly takes for granted. I'll need to confirm if it has default group configurations. Let's see what's happening in there.
|
| 288/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_block_opts_create]
Results: map[SourceCode:2328: /* create a f_midi2_block_opts instance for the given block number */
2329: static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
2330: unsigned int blk,
2331: struct f_midi2_block_opts **block_p)
2332: {
2333: struct f_midi2_block_opts *block_opts;
2334: int ret = 0;
2335:
2336: mutex_lock(&ep_opts->opts->lock);
2337: if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
2338: ret = -EBUSY;
2339: goto out;
2340: }
2341:
2342: block_opts = kzalloc_obj(*block_opts);
2343: if (!block_opts) {
2344: ret = -ENOMEM;
2345: goto out;
2346: }
2347:
2348: block_opts->ep = ep_opts;
2349: block_opts->id = blk;
2350:
2351: /* set up the default values */
2352: block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION;
2353: block_opts->info.first_group = 0;
2354: block_opts->info.num_groups = 1;
2355: block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH;
2356: block_opts->info.active = 1;
2357:
2358: ep_opts->blks[blk] = block_opts;
2359: *block_p = block_opts;
2360:
2361: out:
2362: mutex_unlock(&ep_opts->opts->lock);
2363: return ret;
2364: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 289/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 14999
output: 49
thoughts: 46
Thoughts: **Digging into MIDI 2.0 Endpoint Option Initialization**
Okay, so I'm looking at `f_midi2_block_opts_create` here, and something's bugging me. It doesn't appear to be handling the default group setup for the `block_opts` structure. That's a bit unusual. I would have expected a routine like this to initialize *everything* related to its target. Let me just double check my assumption by quickly cross-referencing this routine's behavior with the structure's counterpart, `f_midi2_ep_opts_create`. This ought to clarify whether the design is intentionally distributed between two methods or if there’s a missing piece here. It is definitely worth looking into.
|
| 290/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_ep_opts_create]
Results: map[SourceCode:2497: /* create a f_midi2_ep_opts instance */
2498: static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
2499: unsigned int index,
2500: struct f_midi2_ep_opts **ep_p)
2501: {
2502: struct f_midi2_ep_opts *ep_opts;
2503:
2504: ep_opts = kzalloc_obj(*ep_opts);
2505: if (!ep_opts)
2506: return -ENOMEM;
2507:
2508: ep_opts->opts = opts;
2509: ep_opts->index = index;
2510:
2511: /* set up the default values */
2512: ep_opts->info.protocol = 2;
2513: ep_opts->info.protocol_caps = 3;
2514:
2515: opts->eps[index] = ep_opts;
2516: *ep_p = ep_opts;
2517: return 0;
2518: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 291/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 15612
output: 47
thoughts: 21
Thoughts: Here's my thought process, as requested:
**Debugging MIDI Group Allocation**
Okay, so I'm trying to figure out where these default MIDI groups are being added. My first instinct is to dive into the allocation code, specifically the `f_midi2_alloc_inst` function. It makes sense to start there, as that's where new instances are created. I'll need to carefully examine the logic within that function to trace the flow and identify the exact points where group membership is being assigned, especially the initial default groups. Hopefully, it's a straightforward conditional or loop, and I can quickly pinpoint the culprit! I'll be looking for clues like calls to group management functions or hardcoded assignments.
|
| 292/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_alloc_inst]
Results: map[SourceCode:2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
2656: return ERR_PTR(-ENOMEM);
2657:
2658: mutex_init(&opts->lock);
2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
2660: opts->info.process_ump = true;
2661: opts->info.static_block = true;
2662: opts->info.num_reqs = 32;
2663: opts->info.req_buf_size = 512;
2664:
2665: /* create the default ep */
2666: ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
2667: if (ret) {
2668: kfree(opts);
2669: return ERR_PTR(ret);
2670: }
2671:
2672: /* create the default block */
2673: ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
2674: if (ret) {
2675: kfree(ep_opts);
2676: kfree(opts);
2677: return ERR_PTR(ret);
2678: }
2679:
2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
2687: &f_midi2_ep_type);
2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
2689:
2690: config_group_init_type_name(&block_opts->group, "block.0",
2691: &f_midi2_block_type);
2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
2693:
2694: return &opts->func_inst;
2695: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 293/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 16666
output: 26
thoughts: 163
Thoughts: **Analyzing ConfigFS Group Management in MIDI2 Driver**
Okay, so looking at this, I've tracked down the `configfs_add_default_group` function call within the `f_midi2_alloc_inst` function. Right, so `ep.0` is being added to `opts->func_inst.group`, and then `block.0` gets added to `ep_opts->group`. That makes sense. Now, the cleanup. When `f_midi2_free_inst` gets called, the driver should be calling `configfs_remove_default_groups(&opts->func_inst.group);`. Excellent. And on the endpoint option release with `f_midi2_ep_opts_release`, we need to call `configfs_remove_default_groups(to_config_group(item));`. This seems consistent so far. Just to be completely thorough though, I should double check to see if `configfs_add_default_group` is being used anywhere else in the code base. Gotta eliminate any potential leaks or unexpected behavior.
|
| 294/4 |
2026/07/30 14:42 |
tool |
codesearch-find-references |
0m
Args: map[Name:configfs_add_default_group]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:config_desc_make SourceFile:drivers/usb/gadget/configfs.c SourceLine:749] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f_midi2_alloc_inst SourceFile:drivers/usb/gadget/function/f_midi2.c SourceLine:2688] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:f_midi2_alloc_inst SourceFile:drivers/usb/gadget/function/f_midi2.c SourceLine:2692] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:fsg_alloc_inst SourceFile:drivers/usb/gadget/function/f_mass_storage.c SourceLine:3531] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgets_make SourceFile:drivers/usb/gadget/configfs.c SourceLine:2002] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgets_make SourceFile:drivers/usb/gadget/configfs.c SourceLine:2006] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgets_make SourceFile:drivers/usb/gadget/configfs.c SourceLine:2010] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgets_make SourceFile:drivers/usb/gadget/configfs.c SourceLine:2014] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:gadgets_make SourceFile:drivers/usb/gadget/configfs.c SourceLine:2018] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_cluster SourceFile:fs/dlm/config.c SourceLine:443] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_cluster SourceFile:fs/dlm/config.c SourceLine:444] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_cma_dev SourceFile:drivers/infiniband/core/cma_configfs.c SourceLine:305] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_cma_ports SourceFile:drivers/infiniband/core/cma_configfs.c SourceLine:227] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_connector_group SourceFile:drivers/gpu/drm/vkms/vkms_configfs.c SourceLine:673] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_device_group SourceFile:drivers/gpu/drm/vkms/vkms_configfs.c SourceLine:788] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_device_group SourceFile:drivers/gpu/drm/vkms/vkms_configfs.c SourceLine:792] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_device_group SourceFile:drivers/gpu/drm/vkms/vkms_configfs.c SourceLine:796] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_device_group SourceFile:drivers/gpu/drm/vkms/vkms_configfs.c SourceLine:800] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_encoder_group SourceFile:drivers/gpu/drm/vkms/vkms_configfs.c SourceLine:506] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_plane_group SourceFile:drivers/gpu/drm/vkms/vkms_configfs.c SourceLine:389] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:make_space SourceFile:fs/dlm/config.c SourceLine:492] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nullb_add_fault_config SourceFile:drivers/block/null_blk/main.c SourceLine:663] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nullb_add_fault_config SourceFile:drivers/block/null_blk/main.c SourceLine:664] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nullb_add_fault_config SourceFile:drivers/block/null_blk/main.c SourceLine:665] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_init_configfs SourceFile:drivers/nvme/target/configfs.c SourceLine:2372] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_init_configfs SourceFile:drivers/nvme/target/configfs.c SourceLine:2377] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_init_configfs SourceFile:drivers/nvme/target/configfs.c SourceLine:2382] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_ports_make SourceFile:drivers/nvme/target/configfs.c SourceLine:2085] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_ports_make SourceFile:drivers/nvme/target/configfs.c SourceLine:2089] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_ports_make SourceFile:drivers/nvme/target/configfs.c SourceLine:2093] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_ports_make SourceFile:drivers/nvme/target/configfs.c SourceLine:2100] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_subsys_make SourceFile:drivers/nvme/target/configfs.c SourceLine:1757] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_subsys_make SourceFile:drivers/nvme/target/configfs.c SourceLine:1761] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:o2nm_cluster_group_make_group SourceFile:fs/ocfs2/cluster/nodemanager.c SourceLine:706] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:o2nm_cluster_group_make_group SourceFile:fs/ocfs2/cluster/nodemanager.c SourceLine:710] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_init_configfs SourceFile:drivers/target/target_core_configfs.c SourceLine:3766] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_init_configfs SourceFile:drivers/target/target_core_configfs.c SourceLine:3772] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_init_configfs SourceFile:drivers/target/target_core_configfs.c SourceLine:3780] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_init_configfs SourceFile:drivers/target/target_core_configfs.c SourceLine:3793] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_make_subdev SourceFile:drivers/target/target_core_configfs.c SourceLine:3445] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_make_subdev SourceFile:drivers/target/target_core_configfs.c SourceLine:3449] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_make_subdev SourceFile:drivers/target/target_core_configfs.c SourceLine:3453] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_make_subdev SourceFile:drivers/target/target_core_configfs.c SourceLine:3457] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_make_subdev SourceFile:drivers/target/target_core_configfs.c SourceLine:3462] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_make_subdev SourceFile:drivers/target/target_core_configfs.c SourceLine:3467] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_make_subdev SourceFile:drivers/target/target_core_configfs.c SourceLine:3480] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_core_register_fabric SourceFile:drivers/target/target_core_configfs.c SourceLine:257] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_lun SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:769] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_mappedlun SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:313] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_nodeacl SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:391] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_nodeacl SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:396] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_nodeacl SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:401] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_nodeacl SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:406] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_tpg SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:948] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_tpg SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:953] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_tpg SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:958] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_tpg SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:963] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_tpg SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:968] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_tpg SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:973] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_wwn SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:1149] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_fabric_make_wwn SourceFile:drivers/target/target_core_fabric_configfs.c SourceLine:1153] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_dev_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:400] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_dev_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:405] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_dev_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:410] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_mappedlun_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:1305] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_mappedlun_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:1310] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_port_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:807] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_port_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:812] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:target_stat_setup_port_default_groups SourceFile:drivers/target/target_core_stat.c SourceLine:817] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_os_desc_prepare_interf_dir SourceFile:drivers/usb/gadget/configfs.c SourceLine:1550] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:usb_os_desc_prepare_interf_dir SourceFile:drivers/usb/gadget/configfs.c SourceLine:1564] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uvcg_color_matching_create_children SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:3113] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uvcg_config_create_group SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:166] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uvcg_control_class_create_children SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:1448] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:uvcg_streaming_class_create_children SourceFile:drivers/usb/gadget/function/uvc_configfs.c SourceLine:3564]] TruncatedOutput:false]
|
| 295/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21435
output: 49
thoughts: 73
Thoughts: **Examining the MIDI2 Driver's Configuration**
Okay, so I'm looking into the `f_midi2.c` file and specifically focusing on calls to `configfs_add_default_group`. My primary interest here is how the default configuration group for MIDI2 is being handled. I recall that `configfs_add_default_group` only appears to be called within `f_midi2_alloc_inst`. This seems straightforward, but I want to be thorough. I need to make sure I haven't missed something.
Now, I'm going to shift my focus and examine `f_midi2_opts_ep_make` and `f_midi2_opts_block_make`. I'll need to double check how these functions interact with the configfs setup, just in case there's any implicit or indirect handling of the default group that I missed. I want a complete picture.
|
| 296/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_opts_ep_make]
Results: map[SourceCode:2520: /* make_group callback for an EP */
2521: static struct config_group *
2522: f_midi2_opts_ep_make(struct config_group *group, const char *name)
2523: {
2524: struct f_midi2_opts *opts;
2525: struct f_midi2_ep_opts *ep_opts;
2526: unsigned int index;
2527: int ret;
2528:
2529: if (strncmp(name, "ep.", 3))
2530: return ERR_PTR(-EINVAL);
2531: ret = kstrtouint(name + 3, 10, &index);
2532: if (ret)
2533: return ERR_PTR(ret);
2534:
2535: opts = to_f_midi2_opts(&group->cg_item);
2536: if (index >= MAX_UMP_EPS)
2537: return ERR_PTR(-EINVAL);
2538: if (opts->eps[index])
2539: return ERR_PTR(-EBUSY);
2540: ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
2541: if (ret)
2542: return ERR_PTR(ret);
2543:
2544: config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
2545: return &ep_opts->group;
2546: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 297/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 22223
output: 49
thoughts: 182
Thoughts: **Analyzing MIDI Endpoint Creation in the Configuration Filesystem**
Okay, so `f_midi2_opts_ep_make` is responsible for creating a new endpoint group, which is good. But it seems like it *doesn't* automatically attach any default groups to this new endpoint, and that's the part that's sparking questions. I'm recalling that `f_midi2_alloc_inst` *does* add `block.0` to `ep.0` as a default. That's the established pattern.
Now, here's where my concern lies: if a user creates an endpoint directory directly, using `mkdir` say to make `ep.1`, is the `block.0` group automatically included? It's crucial for the default functionality we're expecting.
A quick review of the code tells me `f_midi2_opts_ep_make` doesn't call `configfs_add_default_group`. So, if the endpoint is created without the main function, it seems very likely that `ep.1` created via `mkdir` won't have `block.0` by default. And that's not the behavior we want.
I need to confirm this. I think the next logical step would be to review `f_midi2_opts_block_make` just to be sure.
|
| 298/4 |
2026/07/30 14:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_opts_block_make]
Results: map[SourceCode:2366: /* make_group callback for a block */
2367: static struct config_group *
2368: f_midi2_opts_block_make(struct config_group *group, const char *name)
2369: {
2370: struct f_midi2_ep_opts *ep_opts;
2371: struct f_midi2_block_opts *block_opts;
2372: unsigned int blk;
2373: int ret;
2374:
2375: if (strncmp(name, "block.", 6))
2376: return ERR_PTR(-EINVAL);
2377: ret = kstrtouint(name + 6, 10, &blk);
2378: if (ret)
2379: return ERR_PTR(ret);
2380:
2381: ep_opts = to_f_midi2_ep_opts(&group->cg_item);
2382:
2383: if (blk >= SNDRV_UMP_MAX_BLOCKS)
2384: return ERR_PTR(-EINVAL);
2385: if (ep_opts->blks[blk])
2386: return ERR_PTR(-EBUSY);
2387: ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
2388: if (ret)
2389: return ERR_PTR(ret);
2390:
2391: config_group_init_type_name(&block_opts->group, name,
2392: &f_midi2_block_type);
2393: return &block_opts->group;
2394: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 299/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23246
output: 219
thoughts: 74
Thoughts: Okay, let's break this down.
**Addressing Default Group Management in MIDI 2.0 Instance Allocation and Release**
Right, so I've pinpointed the crux of the issue. The function `f_midi2_alloc_inst` is the *sole* responsible party for adding default groups to the MIDI 2.0 instance. Consequently, `f_midi2_free_inst` and `f_midi2_ep_opts_release` absolutely *must* handle their removal during the deallocation process. That’s the key here – ensuring these defaults are properly cleaned up.
Therefore, the logical next step is to get in there and edit `f_midi2_free_inst` and `f_midi2_ep_opts_release`. It's pretty straightforward: I need to add the code to remove these defaults during the appropriate stages of those functions. This feels like a quick and surgical change.
|
| 300/4 |
2026/07/30 14:42 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
kfree(opts->info.iface_name);
kfree(opts);
} NewCode:static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
} SourceFile:drivers/usb/gadget/function/f_midi2.c]
Results:
|
| 301/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 23743
output: 229
thoughts: 14
Thoughts: Okay, here's my summary, taking on the persona you've described:
**Analyzing `f_midi2_ep_opts_release`**
Alright, let's dive into this `f_midi2_ep_opts_release` function. My initial thought is to assess its role within the larger context of the MIDI 2.0 endpoint protocol. It's a "release" function, so I'm immediately thinking memory management, resource cleanup, and potentially de-allocation. Given my experience, I'm expecting this to be crucial for ensuring endpoint stability and preventing memory leaks or dangling pointers. I'll need to scrutinize how it handles different data structures associated with the endpoint options. Is it freeing individual option structures? Are there nested allocations that need to be recursively released? What about potential synchronization concerns? Are there any mutexes or other concurrency primitives involved to protect shared resources during the release process? I need to look for any potential for double-frees or use-after-free vulnerabilities. Beyond the simple mechanics of deallocation, I'm going to consider any side effects or dependencies it might have on the endpoint state. Does calling this function put the endpoint in a specific state? Are there any important state transitions that need to be accounted for? Overall, a robust implementation of this function is vital for the integrity of the endpoint.
|
| 302/4 |
2026/07/30 14:42 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
} NewCode:static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
} SourceFile:drivers/usb/gadget/function/f_midi2.c]
Results:
|
| 303/4 |
2026/07/30 14:42 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24273
output: 30
thoughts: 7
Thoughts: Okay, I'm ready to dive into that "diff" and give you my expert take. Here's my thought process:
**Diving into the Code Change**
Alright, a diff is in front of me, which means we're looking at a comparison between two versions of some code, config, or data. My first instinct is to zoom in on the most crucial areas of the changes – those likely to have the biggest impact, or where I expect potential problems. I immediately start scanning for:
* **Core Logic Alterations:** What fundamental algorithms or data structures have been modified? That's always my primary focus.
* **Dependency Updates:** Any changes to external libraries or APIs? Those can introduce breaking changes or security vulnerabilities if not carefully vetted.
* **Performance Implications:** Is the change likely to speed things up, slow things down, or introduce any new bottlenecks? I'm looking for anything that could impact throughput or latency.
* **Security Concerns:** Did the change touch any areas related to authentication, authorization, or data handling? Any red flags there are going to get my immediate attention.
* **Error Handling:** Are there any new error conditions, and how are they being handled? Proper error management is critical, and often an afterthought that can be improved.
* **Edge Cases:** I'm mentally testing the changes against scenarios. Any potential edge cases I can see? The diff is likely to show what the developer tested, so I can use that to formulate my own tests.
* **Stylistic Changes:** Does the code follow our style guide? Consistency is crucial for maintainability.
Now, as I go through the diff, I'm already formulating test cases, potential follow-up questions, and likely refactor suggestions in my head. I'm going to look for any area that is not quite consistent with the standard or the project's standards. My review is going to address what the code does, and what it *should* do, as well as considering if the change is a complete solution.
|
| 304/4 |
2026/07/30 14:43 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2466,20 +2466,21 @@ static struct configfs_attribute *f_midi2_ep_attrs[] = {
&f_midi2_ep_opts_attr_family,
&f_midi2_ep_opts_attr_model,
&f_midi2_ep_opts_attr_sw_revision,
NULL,
};
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
static const struct configfs_item_operations f_midi2_ep_item_ops = {
.release = f_midi2_ep_opts_release,
};
static const struct configfs_group_operations f_midi2_ep_group_ops = {
@@ -2632,20 +2633,21 @@ static const struct config_item_type f_midi2_func_type = {
.ct_attrs = f_midi2_attrs,
.ct_owner = THIS_MODULE,
};
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
/* gadget alloc_inst */
static struct usb_function_instance *f_midi2_alloc_inst(void)
{
struct f_midi2_opts *opts;
struct f_midi2_ep_opts *ep_opts;
struct f_midi2_block_opts *block_opts;
]
|
| 305/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 25386
output: 47
thoughts: 546
Thoughts: **My Analysis of the MIDI Function Instance Removal**
Okay, the diff looks good at first glance. But I need to be absolutely certain this memory management is correct, especially with ConfigFS. Let's trace the calls, starting with the removal of default groups using `configfs_remove_default_groups(&opts->func_inst.group)`.
Right, `configfs_remove_default_groups` iterates through the `default_groups` and calls `config_item_put` on each. Good, standard practice. Now, when `config_item_put` decrements the reference count to zero, the item's `release` function is called, which is defined in `ci_type->ct_item_ops->release(item)`.
For the `ep.0` item, the `ci_type` is `f_midi2_ep_type`, and the associated operations, `ct_item_ops`, are defined by `f_midi2_ep_item_ops`. And, as I recall, `.release` within that structure is `f_midi2_ep_opts_release`. So, yes, `f_midi2_ep_opts_release` *will* be called.
Now, inside `f_midi2_ep_opts_release`, we have *another* call to `configfs_remove_default_groups`, this time operating on the `to_config_group(item)` – which would be `ep.0` from above. This will iterate over `ep.0`'s `default_groups`, which is just `block.0`. So, `config_item_put` gets called on `block.0`.
For `block.0`, the `ci_type` is `f_midi2_block_type`, and `ct_item_ops` is `f_midi2_block_item_ops`. This has `.release = f_midi2_block_opts_release`. Therefore, `f_midi2_block_opts_release` gets called, and as I recall from its implementation, it frees `block_opts`, which is the correct behaviour. Seems solid, but let's be thorough.
Hold on a minute. What about other default groups within `opts->func_inst.group`? I need to rule out any unexpected behaviour. No, upon double-checking, only `ep.0` is added to `opts->func_inst.group` as a default group. And within `ep.0`, only `block.0` is added as a default group. So, that's clean.
Alright, let's look for other places `configfs_remove_default_groups` is called or *should* be called to confirm a clean state overall. It's called in `f_midi2_free_inst`, as expected, and also within `f_midi2_ep_opts_release`, which we've already traced.
One last thing, let me just quickly scan `f_midi2_alloc_inst` again. I want to make sure no other function instances are setting default groups that are not handled as intended. *I will check for the usage of default groups.*
|
| 306/4 |
2026/07/30 14:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_alloc_inst]
Results: map[SourceCode:2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
2656: return ERR_PTR(-ENOMEM);
2657:
2658: mutex_init(&opts->lock);
2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
2660: opts->info.process_ump = true;
2661: opts->info.static_block = true;
2662: opts->info.num_reqs = 32;
2663: opts->info.req_buf_size = 512;
2664:
2665: /* create the default ep */
2666: ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
2667: if (ret) {
2668: kfree(opts);
2669: return ERR_PTR(ret);
2670: }
2671:
2672: /* create the default block */
2673: ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
2674: if (ret) {
2675: kfree(ep_opts);
2676: kfree(opts);
2677: return ERR_PTR(ret);
2678: }
2679:
2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
2687: &f_midi2_ep_type);
2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
2689:
2690: config_group_init_type_name(&block_opts->group, "block.0",
2691: &f_midi2_block_type);
2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
2693:
2694: return &opts->func_inst;
2695: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 307/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 27501
output: 49
thoughts: 974
Thoughts: **Analyzing Memory Management in MIDI2 USB Gadget Driver**
Okay, let's break down this memory management logic. Initially, I was concerned about potential double frees or use-after-free scenarios in the MIDI2 gadget driver.
First, I looked at the creation process. `f_midi2_ep_opts_create` can fail, in which case it returns an error and frees `opts`. `f_midi2_block_opts_create` can also fail, in which case it frees both `ep_opts` and `opts`. I noticed that `f_midi2_ep_opts_create` allocates `ep_opts` and assigns it to `opts->eps[0]`. If `f_midi2_block_opts_create` fails, it frees `ep_opts`, but `opts->eps[0]` still pointed to the freed memory. However, `opts` is immediately freed afterward, so that potential dangling pointer doesn't matter in the failure case. That's good.
Looking at the code for these functions, I can see that `f_midi2_ep_opts_create` initializes `ep_opts`, assigning it to `opts->eps[index]` and also to `*ep_p`. This is followed by the `f_midi2_block_opts_create` that can fail, and then freeing both `ep_opts` and `opts` in that failure case. Everything checks out here.
Next, I considered a successful allocation followed by a deallocation sequence. If `f_midi2_alloc_inst` succeeds, and later `f_midi2_free_inst` is called, `f_midi2_free_inst` calls `configfs_remove_default_groups` on `opts->func_inst.group`. This triggers a chain of events: the refcount of `ep.0` drops, calling `f_midi2_ep_opts_release`, which then calls `configfs_remove_default_groups` on `to_config_group(item)` (i.e., `block.0`), which in turn calls `f_midi2_block_opts_release`. The `f_midi2_block_opts_release` frees `block_opts`, and then `f_midi2_ep_opts_release` frees `ep_opts`. Finally, `f_midi2_free_inst` frees `opts`. I was worried that `opts->eps[0]` would still point to a freed `ep_opts`, and `ep_opts->blks[0]` would still point to a freed `block_opts`. After reviewing the code and the order of operations, however, `opts` is freed last. Since `opts` is freed at the end of the deallocation, all the associated memory within `opts`, including the `opts->eps` array, becomes invalid. It's safe since `opts` is no longer used.
I was also thinking about dynamically created EPs and blocks. If a user creates a new endpoint `ep.1` via `mkdir`, `f_midi2_opts_ep_make` is invoked, which calls `f_midi2_ep_opts_create`. Then, when the user removes `ep.1` via `rmdir`, `f_midi2_opts_ep_drop` would be called. This should all be handled by configfs.
Therefore, the memory management appears sound, as long as `opts` is freed at the end. All memory is correctly freed in all cases.
|
| 308/4 |
2026/07/30 14:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_opts_ep_drop]
Results: map[SourceCode:2548: /* drop_item callback for an EP */
2549: static void
2550: f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
2551: {
2552: struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
2553:
2554: mutex_lock(&ep_opts->opts->lock);
2555: ep_opts->opts->eps[ep_opts->index] = NULL;
2556: mutex_unlock(&ep_opts->opts->lock);
2557: config_item_put(item);
2558: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 309/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 29520
output: 37
thoughts: 460
Thoughts: **Analyzing the Lifecycle of Default ConfigFS Groups**
Okay, so I'm looking at how we handle the destruction of MIDI device configurations within ConfigFS, specifically focusing on these default groups. It looks like `f_midi2_opts_ep_drop` is used to remove an endpoint, setting `ep_opts->opts->eps[ep_opts->index] = NULL;` and then calling `config_item_put(item)`. But here's the crucial point: this function *isn't* used for the default endpoint, `ep.0`. Since `ep.0` isn't created via `mkdir`, its lifecycle is different.
When `f_midi2_free_inst` is called, it triggers `configfs_remove_default_groups`, which calls `config_item_put` on `ep.0`. This means the call goes straight to `f_midi2_ep_opts_release`. Critically, `opts->eps[0]` isn't explicitly set to NULL in this path. However, because the entire `opts` structure is being freed, it becomes a moot point.
Next, I'm thinking about `block.0`, which is a default group belonging to `ep.0`. When `f_midi2_ep_opts_release` also calls `configfs_remove_default_groups`, it calls `config_item_put` on `block.0`. This call ends up in `f_midi2_block_opts_release`. Here too, `ep_opts->blks[0]` isn't set to NULL. Just like the `ep.0` case, `ep_opts` is being freed.
Now, the important question is: what happens if a user attempts to remove these default groups, using `rmdir`? The initial thought is that `rmdir` on `block.0` *should* be blocked, given its default nature. Wait, is it? Isn't there something about how ConfigFS handles default groups? My gut says no, ConfigFS should not allow `rmdir` on default groups, but I need to make sure. I thought maybe ConfigFS does in some cases? No, no, it should return an error, either `-ENOENT` or `-EPERM`. I need to verify how `configfs_rmdir` behaves. I need to refresh myself on how this whole thing works.
|
| 310/4 |
2026/07/30 14:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_rmdir]
Results: map[SourceCode:1469: static int configfs_rmdir(struct inode *dir, struct dentry *dentry)
1470: {
1471: struct config_item *parent_item;
1472: struct config_item *item;
1473: struct configfs_subsystem *subsys;
1474: struct configfs_dirent *sd;
1475: struct configfs_fragment *frag;
1476: struct module *subsys_owner = NULL, *dead_item_owner = NULL;
1477: int ret;
1478:
1479: sd = dentry->d_fsdata;
1480: if (sd->s_type & CONFIGFS_USET_DEFAULT)
1481: return -EPERM;
1482:
1483: /* Get a working ref until we have the child */
1484: parent_item = configfs_get_config_item(dentry->d_parent);
1485: subsys = to_config_group(parent_item)->cg_subsys;
1486: BUG_ON(!subsys);
1487:
1488: if (!parent_item->ci_type) {
1489: config_item_put(parent_item);
1490: return -EINVAL;
1491: }
1492:
1493: /* configfs_mkdir() shouldn't have allowed this */
1494: BUG_ON(!subsys->su_group.cg_item.ci_type);
1495: subsys_owner = subsys->su_group.cg_item.ci_type->ct_owner;
1496:
1497: /*
1498: * Ensure that no racing symlink() will make detach_prep() fail while
1499: * the new link is temporarily attached
1500: */
1501: do {
1502: struct dentry *wait;
1503:
1504: mutex_lock(&configfs_symlink_mutex);
1505: spin_lock(&configfs_dirent_lock);
1506: /*
1507: * Here's where we check for dependents. We're protected by
1508: * configfs_dirent_lock.
1509: * If no dependent, atomically tag the item as dropping.
1510: */
1511: ret = sd->s_dependent_count ? -EBUSY : 0;
1512: if (!ret) {
1513: ret = configfs_detach_prep(sd, &wait);
1514: if (ret)
1515: configfs_detach_rollback(sd);
1516: }
1517: spin_unlock(&configfs_dirent_lock);
1518: mutex_unlock(&configfs_symlink_mutex);
1519:
1520: if (ret) {
1521: if (ret != -EAGAIN) {
1522: config_item_put(parent_item);
1523: return ret;
1524: }
1525:
1526: /* Wait until the racing operation terminates */
1527: inode_lock(d_inode(wait));
1528: inode_unlock(d_inode(wait));
1529: dput(wait);
1530: }
1531: } while (ret == -EAGAIN);
1532:
1533: frag = sd->s_frag;
1534: if (down_write_killable(&frag->frag_sem)) {
1535: spin_lock(&configfs_dirent_lock);
1536: configfs_detach_rollback(sd);
1537: spin_unlock(&configfs_dirent_lock);
1538: config_item_put(parent_item);
1539: return -EINTR;
1540: }
1541: frag->frag_dead = true;
1542: up_write(&frag->frag_sem);
1543:
1544: /* Get a working ref for the duration of this function */
1545: item = configfs_get_config_item(dentry);
1546:
1547: /* Drop reference from above, item already holds one. */
1548: config_item_put(parent_item);
1549:
1550: if (item->ci_type)
1551: dead_item_owner = item->ci_type->ct_owner;
1552:
1553: if (sd->s_type & CONFIGFS_USET_DIR) {
1554: configfs_detach_group(dentry);
1555:
1556: mutex_lock(&subsys->su_mutex);
1557: client_disconnect_notify(parent_item, item);
1558: unlink_group(to_config_group(item));
1559: } else {
1560: configfs_detach_item(dentry);
1561:
1562: mutex_lock(&subsys->su_mutex);
1563: client_disconnect_notify(parent_item, item);
1564: unlink_obj(item);
1565: }
1566:
1567: client_drop_item(parent_item, item);
1568: mutex_unlock(&subsys->su_mutex);
1569:
1570: /* Drop our reference from above */
1571: config_item_put(item);
1572:
1573: module_put(dead_item_owner);
1574: module_put(subsys_owner);
1575:
1576: return 0;
1577: }
SourceFile:fs/configfs/dir.c]
|
| 311/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 32156
output: 38
thoughts: 184
Thoughts: **Analyzing the `rmdir` Behavior in ConfigFS**
Okay, so I see that if a "default" group is set, the system will return `-EPERM` if someone tries to `rmdir` it. That makes sense; we wouldn't want those getting deleted accidentally.
But, a new thought is popping up: What happens if the user attempts to `rmdir` the *function instance* itself? That's a directory created using `mkdir` within configfs, right? I'm picturing the process. If someone runs `rmdir` on the instance, `configfs_rmdir` will be called. Then `configfs_rmdir` calls `configfs_detach_group`, which is supposed to remove the dentries of the default groups. Then `client_drop_item` is called, which calls `f_midi2_func_type`'s `drop_item`.
Okay, I need to trace this... Wait, that function instance directory is created by `function_make` in `drivers/usb/gadget/configfs.c`. Yes! Let's go check what is in `function_make` now.
|
| 312/4 |
2026/07/30 14:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:function_make]
Results: map[SourceCode: 601: static struct config_group *function_make(
602: struct config_group *group,
603: const char *name)
604: {
605: struct gadget_info *gi;
606: struct usb_function_instance *fi;
607: char buf[MAX_NAME_LEN];
608: char *func_name;
609: char *instance_name;
610: int ret;
611:
612: if (strlen(name) >= MAX_NAME_LEN)
613: return ERR_PTR(-ENAMETOOLONG);
614:
615: scnprintf(buf, MAX_NAME_LEN, "%s", name);
616:
617: func_name = buf;
618: instance_name = strchr(func_name, '.');
619: if (!instance_name) {
620: pr_err("Unable to locate . in FUNC.INSTANCE\n");
621: return ERR_PTR(-EINVAL);
622: }
623: *instance_name = '\0';
624: instance_name++;
625:
626: fi = usb_get_function_instance(func_name);
627: if (IS_ERR(fi))
628: return ERR_CAST(fi);
629:
630: ret = config_item_set_name(&fi->group.cg_item, "%s", name);
631: if (ret) {
632: usb_put_function_instance(fi);
633: return ERR_PTR(ret);
634: }
635: if (fi->set_inst_name) {
636: ret = fi->set_inst_name(fi, instance_name);
637: if (ret) {
638: usb_put_function_instance(fi);
639: return ERR_PTR(ret);
640: }
641: }
642:
643: gi = container_of(group, struct gadget_info, functions_group);
644:
645: mutex_lock(&gi->lock);
646: list_add_tail(&fi->cfs_list, &gi->available_func);
647: mutex_unlock(&gi->lock);
648: return &fi->group;
649: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 313/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 33356
output: 38
thoughts: 20
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in this field, and speaking from the first-person perspective:
**Garbage Collection and Function Instance Destruction**
Alright, so the core of what's happening here is pretty straightforward, but the nuances are important. My thought process is triggered by a call to `rmdir` on a function instance. Instantly, I recognize that this signifies the process of removing a directory entry that represents this function instance. Behind the scenes, the operating system or runtime is managing the instance's lifecycle and, critically, its eventual destruction. So, the key is what happens when that directory entry gets nuked.
The fact that `function_drop` is specifically called when `rmdir` is invoked is telling. It immediately flags to me that we're dealing with a sophisticated memory management or resource-handling scheme. `function_drop` isn't just a generic cleanup function; it's intricately linked to the destruction trigger – the *removal* of the symbolic representation of the function instance in the file system. It suggests that there are potentially custom cleanup procedures for the memory that the instance lives in, which could have been managed directly or implicitly, or possibly allocated in a place that is not easily auto-cleaned. This is very important.
My assumption is that `function_drop` is *the* place where all necessary resources associated with that specific function instance are explicitly released. This could include things like closing any file handles the function might have been using, deallocating any dynamically allocated memory specific to this instance, or releasing any other operating system resources it holds. The call to `rmdir` acts as the trigger to initiate these cleanup steps. It's a clean, deterministic way to signal the end of the instance's life and ensure no lingering artifacts remain.
|
| 314/4 |
2026/07/30 14:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/configfs.c Name:function_drop]
Results: map[SourceCode: 651: static void function_drop(
652: struct config_group *group,
653: struct config_item *item)
654: {
655: struct usb_function_instance *fi = to_usb_function_instance(item);
656: struct gadget_info *gi;
657:
658: gi = container_of(group, struct gadget_info, functions_group);
659:
660: mutex_lock(&gi->lock);
661: list_del(&fi->cfs_list);
662: mutex_unlock(&gi->lock);
663: config_item_put(item);
664: }
SourceFile:drivers/usb/gadget/configfs.c]
|
| 315/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 34027
output: 47
thoughts: 95
Thoughts: **Analyzing the `config_item_put` Process**
Okay, so I'm looking at this `config_item_put(item)` call, and my understanding is that this will decrement the reference count of this function instance. Once that refcount hits zero, that's when the `usb_function_instance`'s release function gets triggered. Hang on, wasn't `usb_function_instance` actually a `config_group`? Right, it is. And its `ci_type` is linked to `f_midi2_func_type`. Alright, so if the release function is happening, I need to double-check what `f_midi2_func_type`'s release function is actually doing. We need to be sure that the release function in `f_midi2_func_type` is doing the cleanup properly.
|
| 316/4 |
2026/07/30 14:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_func_type]
Results: map[SourceCode:2629: static const struct config_item_type f_midi2_func_type = {
2630: .ct_item_ops = &f_midi2_item_ops,
2631: .ct_group_ops = &f_midi2_group_ops,
2632: .ct_attrs = f_midi2_attrs,
2633: .ct_owner = THIS_MODULE,
2634: };
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 317/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 34510
output: 47
thoughts: 11
Thoughts: Okay, here's my interpretation of those thoughts, crafted with the understanding that I'm speaking to a fellow expert:
**Delving into MIDI Item Operations**
Alright, let's break down this `f_midi2_item_ops` function. Immediately, I'm thinking about the low-level MIDI data structures involved. Specifically, I imagine this deals with the fundamental manipulation of MIDI item events – perhaps insertion, deletion, modification, time-stretching, quantization, and the like, all the core operations one needs for serious MIDI editing. I'm assuming it handles the underlying data, probably dealing with the bitwise manipulation of MIDI messages efficiently. Is it optimized for specific hardware? Is it designed to be threading-friendly? We'll have to consider those aspects when we review the code. The main question on my mind is: Does this implementation provide sufficient flexibility for the wide range of use cases we need to support in this project?
|
| 318/4 |
2026/07/30 14:43 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_item_ops]
Results: map[SourceCode:2620: static const struct configfs_item_operations f_midi2_item_ops = {
2621: .release = f_midi2_opts_release,
2622: };
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 319/4 |
2026/07/30 14:43 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 34857
output: 47
thoughts: 11
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Analyzing `f_midi2_opts_release`**
Alright, let's dissect this. When I see `f_midi2_opts_release`, my mind immediately goes to the implications for a MIDI 2.0 implementation. This screams resource management, specifically dealing with the memory allocated for option structures related to MIDI 2.0 messages. It’s likely a function designed to free up those resources. I'm assuming "opts" refers to some kind of options struct – perhaps containing parameters, flags, or extended data related to specific MIDI 2.0 messages or features.
The "release" part indicates that this isn't about *allocating* the options. It's the inverse. We're talking about deallocating, clearing, or resetting these option data structures. Why? Likely because we're done processing a particular MIDI 2.0 message, a buffer is being freed, or some specific workflow phase is complete. We need to prevent memory leaks and ensure resources are handled correctly. I would look at the calling context of this function. I would be very interested to know *what* it's releasing. Knowing the structure of the "opts" struct is key. Are we talking about dynamic memory or references to internal data? Is there a need for zeroing memory here? If so, why and how? What's the potential for dangling pointers or double frees if there are linked structures within the options?
Given the context of MIDI 2.0, I'd expect this function to play a critical role in managing the new, potentially more complex, data payloads that MIDI 2.0 introduces, especially for features that add more parameters, such as the increased resolution of new 2.0 messages. The efficiency of this function is going to be important to prevent processing delays. A poorly implemented `f_midi2_opts_release` could quickly become a bottleneck in time-critical MIDI processing pipelines. So, I would want to ensure it is very performant. I would also investigate any error handling associated with this release. What happens if release fails?
|
| 320/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_opts_release]
Results: map[SourceCode:2613: static void f_midi2_opts_release(struct config_item *item)
2614: {
2615: struct f_midi2_opts *opts = to_f_midi2_opts(item);
2616:
2617: usb_put_function_instance(&opts->func_inst);
2618: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 321/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 35510
output: 41
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Deep Dive into `usb_put_function_instance`**
Right, let's unpack `usb_put_function_instance`. My understanding is that this is where we're going to be freeing up resources associated with a USB function instance. We're assuming it's been initialized and allocated elsewhere, likely by a framework or specific driver. The critical aspect here is ensuring proper cleanup. We need to look carefully for:
* **Reference Counting:** Is this a reference-counted structure? If so, we're likely decrementing a counter, and the actual deallocation might only happen when it hits zero. This is crucial to prevent double-frees or prematurely freeing resources that are still in use by other parts of the system. I need to trace back to where the instance was initially created (`usb_get_function_instance` or equivalent) to confirm the reference counting scheme.
* **Resource Deallocation:** What specific resources are being managed within this function instance? Think endpoints, descriptors, possibly control requests. We must ensure each resource is freed in the correct order, avoiding dangling pointers. I'll need to know which internal structures it handles and follow the freeing logic.
* **Error Handling:** What happens if deallocation fails? Are there checks for the return values of lower-level free functions? Robust error handling is essential, because a failure here could lead to memory leaks or, worse, system instability. I'd expect to see some logging, but importantly, do we have any back-off mechanisms? If not, why not?
* **Synchronization:** Are there any concurrent accesses to the function instance data? This is a critical area. We'll need to check for any mutexes or spinlocks protecting the data structure. Improper locking could result in race conditions and data corruption. The code's context within the USB stack will determine what synchronization primitives are appropriate.
* **Callback Handling:** Does this function instance have any associated callbacks or event handlers? If so, we need to consider how these are unregistered or deactivated during the cleanup process to avoid use-after-free conditions. Pay special attention to the order in which callbacks are disabled relative to other resource deallocations. This is typically a major source of bugs.
* **Device State:** The device state at the time of calling `usb_put_function_instance` will need consideration. If the device has been disconnected or reset, there may be special considerations for deallocating resources that can no longer be reached.
In essence, `usb_put_function_instance` is a critical function, potentially right at the heart of our USB system's reliability, and it demands thorough analysis to ensure we're not introducing vulnerabilities or inefficiencies.
|
| 322/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/functions.c Name:usb_put_function_instance]
Results: map[SourceCode: 69: void usb_put_function_instance(struct usb_function_instance *fi)
70: {
71: struct module *mod;
72:
73: if (!fi)
74: return;
75:
76: mod = fi->fd->mod;
77: fi->free_func_inst(fi);
78: module_put(mod);
79: }
SourceFile:drivers/usb/gadget/functions.c]
|
| 323/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 36316
output: 47
thoughts: 156
Thoughts: **My Analysis of the USB MIDI Function Instance Cleanup**
Okay, so `usb_put_function_instance` is the key. That call triggers `fi->free_func_inst(fi);`. Right, and for `f_midi2`, `free_func_inst` translates to `f_midi2_free_inst`. So far, so good – the code is calling `f_midi2_free_inst`. That's where we added `configfs_remove_default_groups(&opts->func_inst.group);`. Excellent! That's precisely what we need to correctly decrement the reference counts of those default groups. Problem solved... or is it?
Hmm, I need to consider a potential edge case. What if `f_midi2_alloc_inst` fails *after* a successful `configfs_add_default_group`? We could have a memory leak situation. Best to revisit `f_midi2_alloc_inst` and check my assumptions. Let's dig back into that code.
|
| 324/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_alloc_inst]
Results: map[SourceCode:2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
2656: return ERR_PTR(-ENOMEM);
2657:
2658: mutex_init(&opts->lock);
2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
2660: opts->info.process_ump = true;
2661: opts->info.static_block = true;
2662: opts->info.num_reqs = 32;
2663: opts->info.req_buf_size = 512;
2664:
2665: /* create the default ep */
2666: ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
2667: if (ret) {
2668: kfree(opts);
2669: return ERR_PTR(ret);
2670: }
2671:
2672: /* create the default block */
2673: ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
2674: if (ret) {
2675: kfree(ep_opts);
2676: kfree(opts);
2677: return ERR_PTR(ret);
2678: }
2679:
2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
2687: &f_midi2_ep_type);
2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
2689:
2690: config_group_init_type_name(&block_opts->group, "block.0",
2691: &f_midi2_block_type);
2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
2693:
2694: return &opts->func_inst;
2695: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 325/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 37575
output: 49
thoughts: 212
Thoughts: **Analyzing Potential Resource Leaks in the MIDI2 ConfigFS Implementation**
Okay, so I'm looking at this `f_midi2_alloc_inst` function, and the way it interacts with `configfs_add_default_group`. I know that `configfs_add_default_group` is designed to be infallible; it's a `void` return, so there's no way it can actually fail. That means, following that call, `f_midi2_alloc_inst` *shouldn't* fail due to the `configfs_add_default_group` call itself.
But wait a minute... what about `config_group_init_type_name`? That one also returns `void`. Damn, it looks like there aren't any failure paths *directly* after `configfs_add_default_group` either. This seems a little too clean...
I need to make sure there aren't any subtle leaks elsewhere in `f_midi2.c` related to configfs group allocations. Let's do a quick scan of the code for any other points where we are allocating configfs groups. Okay, it looks like `f_midi2_opts_ep_make` and `f_midi2_opts_block_make` are the other key functions that allocate these groups. These are triggered by configfs when the user uses `mkdir` to create things.
Now, when a user removes those created groups using `rmdir`, configfs calls `f_midi2_opts_ep_drop` and `f_midi2_opts_block_drop`, respectively. So, if we are properly dropping them, there is no leak. Let's start by checking `f_midi2_opts_block_drop`.
|
| 326/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_opts_block_drop]
Results: map[SourceCode:2396: /* drop_item callback for a block */
2397: static void
2398: f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
2399: {
2400: struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item);
2401:
2402: mutex_lock(&block_opts->ep->opts->lock);
2403: block_opts->ep->blks[block_opts->id] = NULL;
2404: mutex_unlock(&block_opts->ep->opts->lock);
2405: config_item_put(item);
2406: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 327/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 38450
output: 47
thoughts: 419
Thoughts: **My Analysis of the MIDI2 Driver Cleanup**
Okay, so I've been thinking about this refcount issue in the MIDI2 driver and the memory leaks associated with it. From what I can see, when we're dealing with dynamically created groups, dropping the refcount correctly triggers the `f_midi2_block_opts_release` function, and this in turn, frees the `block_opts` structure. Good. That part is working as expected. This means our dynamically created groups are being freed correctly.
The problem, as I understand it, seems to be isolated to those default groups - the ones that aren't automatically released by configfs. Thinking it through, are there any other default groups? No, it's just `ep.0` and `block.0`.
Let's quickly review that fix. I'm going over it again to make sure I've got a solid handle on it:
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
```
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
This perfectly aligns with the initial explanation and the proposed solution. We're using `configfs_remove_default_groups` to address those default groups. This is the correct approach.
Now, I need to confirm something. Is `configfs_remove_default_groups` safe to call if there *aren't* any default groups? Let me check… yep, it verifies if the list is empty before doing anything. Good, that's reassuring.
Wait... I see `to_config_group(item)` is used in `f_midi2_ep_opts_release`. Just checking, is `item` embedded in a `struct config_group`? Let me look at `struct f_midi2_ep_opts` to be sure... Okay, all good. The plan works. I feel confident in this.
|
| 328/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_ep_opts]
Results: map[SourceCode: 59: struct f_midi2_ep_opts {
60: struct config_group group;
61: unsigned int index;
62: struct f_midi2_ep_info info;
63: struct f_midi2_block_opts *blks[SNDRV_UMP_MAX_BLOCKS];
64: struct f_midi2_opts *opts;
65: };
SourceFile:drivers/usb/gadget/function/u_midi2.h]
|
| 329/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 39648
output: 37
thoughts: 61
Thoughts: Okay, let me think this through.
**Analyzing the `configfs` Structure**
Right, so `group` is definitely the starting point. And if I recall correctly, `to_config_group(item)` is where we're going to retrieve the `config_group` struct from a given item. That's going to use `container_of(item, struct config_group, cg_item)`. Okay, that makes sense. We're using the member `cg_item` to get a pointer to the containing structure. Wait a second, just need to be sure... I believe that `to_config_group` itself is defined within `include/linux/configfs.h`. Yep, good to confirm that.
|
| 330/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/configfs.h Name:to_config_group]
Results: map[SourceCode: 90: static inline struct config_group *to_config_group(struct config_item *item)
91: {
92: return item ? container_of(item,struct config_group,cg_item) : NULL;
93: }
SourceFile:include/linux/configfs.h]
|
| 331/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40001
output: 40
thoughts: 351
Thoughts: **Reviewing the `configfs_remove_default_groups` Implementation and Scope**
Okay, the initial fix is confirmed. Now, my focus shifts to ensuring thoroughness. Are there any other locations within the relevant codebase where a similar oversight might exist? Specifically, is there anywhere else in the `f_midi2` code that should call `configfs_remove_default_groups`? Let's consider `f_midi2_block_opts_release`. Does this function involve any default groups? No, it looks like `f_midi2_block_opts_create` doesn't call `configfs_add_default_group` either.
The next question is, are there any other functions in this module that interact with default groups? Let me scan `f_midi2.c` for `configfs_add_default_group`. I've already done that, and it's only called within `f_midi2_alloc_inst`.
Alright, I should briefly consider if this type of bug is common. Based on the prompt, it seems that I am only supposed to check similar cases within the *same* file. We're not attempting a kernel-wide fix, just local improvements. Given I've already reviewed all instances of `configfs_add_default_group` in `f_midi2.c`, I believe I've met the prompt requirements.
Hmm, one last consideration... what if `f_midi2_ep_opts_release` is called for a dynamically created endpoint, something like `ep.1`? Do those have default groups? Ah, dynamically created endpoints don't have default groups, so that's not a concern. Now, will `configfs_remove_default_groups` even *handle* being called with no default groups? Let me quickly review the implementation to be sure.
|
| 332/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/dir.c Name:configfs_remove_default_groups]
Results: map[SourceCode: 748: void configfs_remove_default_groups(struct config_group *group)
749: {
750: struct config_group *g, *n;
751:
752: list_for_each_entry_safe(g, n, &group->default_groups, group_entry) {
753: list_del(&g->group_entry);
754: config_item_put(&g->cg_item);
755: }
756: }
SourceFile:fs/configfs/dir.c]
|
| 333/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40958
output: 41
thoughts: 59
Thoughts: **Analyzing Group Initialization**
Okay, so I'm looking at this code and it's looping through `group->default_groups`. Pretty straightforward. If that list is empty, it just skips over the whole thing, which is fine, no big deal. But then a question pops up: hold on, what about dynamically created groups? Is `group->default_groups` properly initialized for *those*? I've got to make sure this isn't leaving things in a weird state. Time to double-check the `config_group_init_type_name` function to see how those dynamic groups are actually set up initially. I need to understand that initialization sequence to be confident.
|
| 334/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/item.c Name:config_group_init_type_name]
Results: map[SourceCode: 92: void config_group_init_type_name(struct config_group *group, const char *name,
93: const struct config_item_type *type)
94: {
95: config_item_set_name(&group->cg_item, "%s", name);
96: group->cg_item.ci_type = type;
97: config_group_init(group);
98: }
SourceFile:fs/configfs/item.c]
|
| 335/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 41350
output: 37
thoughts: 8
Thoughts: Okay, here's my summary, in the first person, as an expert in my field, based on the implied task of summarizing thoughts related to a `config_group_init` process (though I have no specific text, I'm inferring from the function name what the focus would likely be):
**Diving Deep into Initialization & Configuration Groups**
Alright, so the core of what's probably on my mind here is the *initialization* of a configuration group. We're talking about a process that, based on the function naming, is dedicated to setting up a distinct collection of configuration parameters. My first instinct is to think about the design considerations. What's the goal of this `config_group_init`? Is it to create a new configuration, or to prepare an existing one for use? Understanding the intended usage scenario is key.
Then I get into the nuts and bolts of the function. I'm immediately running through the necessary steps in my head: memory allocation for the config group data structure, setting default values (or loading them from some source), handling any required dependencies between parameters, and validating initial inputs. What's the error handling strategy? Should it return an error code if something goes wrong, or use exceptions, or a combination? We need robust error handling.
I'm also considering the implications for multi-threaded applications. Is this initialization function thread-safe? Does it need synchronization primitives to protect shared resources? Performance is always a factor, so I'm thinking about efficient algorithms for copying or initializing data structures, especially if these configs are large. We need to be able to use the configs quickly, so we can't let initialization time drag down our execution.
From a practical perspective, I start contemplating how this initialization ties into the broader application architecture. Is `config_group_init` a low-level utility, or part of a higher-level configuration management framework? What are the integration points with other modules and components? What kind of external data sources does it interact with? The data will need to be accessible and easily used by any other feature or functionality.
Finally, I'm thinking about testability. How can I write unit tests for `config_group_init` to ensure it works correctly under various conditions? What are the edge cases I need to consider? I'll probably be thinking of how to mock dependencies and verify that the config group is initialized to a predictable state. I need to be able to quickly test and identify any issues or bugs in the initialization.
|
| 336/4 |
2026/07/30 14:44 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/configfs/item.c Name:config_group_init]
Results: map[SourceCode: 153: /**
154: * config_group_init - initialize a group for use
155: * @group: config_group
156: */
157: void config_group_init(struct config_group *group)
158: {
159: config_item_init(&group->cg_item);
160: INIT_LIST_HEAD(&group->cg_children);
161: INIT_LIST_HEAD(&group->default_groups);
162: }
SourceFile:fs/configfs/item.c]
|
| 337/4 |
2026/07/30 14:44 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 42095
output: 47
thoughts: 355
Thoughts: **Reviewing the MIDI2 Module Cleanup**
Okay, so I've confirmed that `INIT_LIST_HEAD(&group->default_groups);` *is* indeed called. That means `group->default_groups` is initialized correctly and should be empty when we start. Therefore, running `configfs_remove_default_groups` on a dynamically created group should be entirely safe. No issues there.
Let me just quickly re-examine the code changes to be absolutely sure. Okay, here we have:
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
```
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
Now, hold on... is there anything I'm missing? Ah, wait a minute! What about `f_midi2_free_inst`? Is there a possibility that `f_midi2_alloc_inst` could fail somewhere down the line and directly call `kfree(opts)` *without* calling `f_midi2_free_inst` first, thereby bypassing the necessary `configfs_remove_default_groups` cleanup? I'd better double-check `f_midi2_alloc_inst` just to be completely certain.
|
| 338/4 |
2026/07/30 14:45 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/usb/gadget/function/f_midi2.c Name:f_midi2_alloc_inst]
Results: map[SourceCode:2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
2656: return ERR_PTR(-ENOMEM);
2657:
2658: mutex_init(&opts->lock);
2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
2660: opts->info.process_ump = true;
2661: opts->info.static_block = true;
2662: opts->info.num_reqs = 32;
2663: opts->info.req_buf_size = 512;
2664:
2665: /* create the default ep */
2666: ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
2667: if (ret) {
2668: kfree(opts);
2669: return ERR_PTR(ret);
2670: }
2671:
2672: /* create the default block */
2673: ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
2674: if (ret) {
2675: kfree(ep_opts);
2676: kfree(opts);
2677: return ERR_PTR(ret);
2678: }
2679:
2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
2687: &f_midi2_ep_type);
2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
2689:
2690: config_group_init_type_name(&block_opts->group, "block.0",
2691: &f_midi2_block_type);
2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
2693:
2694: return &opts->func_inst;
2695: }
SourceFile:drivers/usb/gadget/function/f_midi2.c]
|
| 339/4 |
2026/07/30 14:45 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 43771
output: 30
thoughts: 974
Thoughts: **Analysis of Memory Management in the MIDI2 ConfigFS Implementation**
Okay, let's break this down. My primary concern is ensuring that we're properly freeing dynamically allocated memory, especially in the context of `configfs`. It's all about reference counting and making sure we don't leak resources.
First, I looked at what happens when `f_midi2_ep_opts_create` fails. It calls `kfree(opts)`. At that point, the `opts->func_inst.group.default_groups` isn't initialized, so no problem there, `kfree` is perfectly safe because `configfs_remove_default_groups` isn't called. Similarly, if `f_midi2_block_opts_create` fails, it calls `kfree(ep_opts)` and `kfree(opts)`. The same logic applies: `ep_opts->group.default_groups` isn't initialized, so again, it's safe.
Then I realized that `config_group_init_type_name` initializes `default_groups`. This is called *after* `f_midi2_block_opts_create`. Therefore, if `f_midi2_block_opts_create` fails, the `default_groups` *aren't* initialized, and `kfree` is called directly, which is still safe. We are fine.
However, I started thinking, what about `f_midi2_free_inst`? That's called when the function instance is freed. Crucially, `f_midi2_alloc_inst` has already succeeded by this point, so `default_groups` *are* initialized. This means that calling `configfs_remove_default_groups` in `f_midi2_free_inst` is safe, and *necessary*.
Now, is `f_midi2_free_inst` called anywhere else? No, it's assigned to `opts->func_inst.free_func_inst` and is only called by `usb_put_function_instance`. So, it's only ever called when the function instance is freed, as intended.
Next, `f_midi2_ep_opts_release` needs examining. It's assigned to `f_midi2_ep_item_ops.release`. This is called by configfs when the refcount of the EP group reaches 0. Now, the `default_groups` are initialized, either by `config_group_init_type_name` in `f_midi2_alloc_inst` or by `f_midi2_opts_ep_make`. This implies `configfs_remove_default_groups` is safe to call.
The question then becomes, what if `f_midi2_opts_ep_make` fails *after* `config_group_init_type_name`? It doesn't. The code is structured like this:
```c
ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
if (ret)
return ERR_PTR(ret);
config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
return &ep_opts->group;
```
So, it's safe because `config_group_init_type_name` is always called *before* the return that might cause a failure.
The same analysis goes for `f_midi2_opts_block_make`:
```c
ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
if (ret)
return ERR_PTR(ret);
config_group_init_type_name(&block_opts->group, name,
&f_midi2_block_type);
return &block_opts->group;
```
It's fine.
**Conclusion:** I think the fix is complete and correct. We need to add `configfs_remove_default_groups` to `f_midi2_free_inst` and `f_midi2_ep_opts_release` to correctly drop the references to the default groups. This ensures the dynamically allocated memory for the default groups is freed when the parent group is destroyed. This explicitly unrolls the default groups. Since the `ep.0` and `block.0` groups *already* have release callbacks, `configfs_remove_default_groups` will correctly call them, freeing memory in the right order. Critically, dynamically created groups (e.g. `ep.1`) don't have default groups, but `configfs_remove_
|
| 340/4 |
2026/07/30 14:45 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2466,20 +2466,21 @@ static struct configfs_attribute *f_midi2_ep_attrs[] = {
&f_midi2_ep_opts_attr_family,
&f_midi2_ep_opts_attr_model,
&f_midi2_ep_opts_attr_sw_revision,
NULL,
};
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
static const struct configfs_item_operations f_midi2_ep_item_ops = {
.release = f_midi2_ep_opts_release,
};
static const struct configfs_group_operations f_midi2_ep_group_ops = {
@@ -2632,20 +2633,21 @@ static const struct config_item_type f_midi2_func_type = {
.ct_attrs = f_midi2_attrs,
.ct_owner = THIS_MODULE,
};
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
/* gadget alloc_inst */
static struct usb_function_instance *f_midi2_alloc_inst(void)
{
struct f_midi2_opts *opts;
struct f_midi2_ep_opts *ep_opts;
struct f_midi2_block_opts *block_opts;
]
|
| 341/4 |
2026/07/30 14:45 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 46447
output: 390
thoughts: 14
Thoughts: Here's my take on the situation:
**Finalizing the Response: It's Ready**
Alright, the diff is absolutely spot-on. No lingering doubts, no need for further tweaking – everything aligns perfectly. The analysis is complete, the integration is smooth, and the potential implications have been rigorously considered. I'm satisfied; I can confidently move forward. It's time to craft the definitive response, integrating all the insights and drawing a clear and concise conclusion. I'm ready to articulate the final answer, knowing I've covered all the necessary bases.
|
| 342/3 |
2026/07/30 14:45 |
action |
test-patch |
20m
Results: map[PatchDiff:diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2473,6 +2473,7 @@ static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
@@ -2639,6 +2640,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
TestError:]
|
| 343/1 |
2026/07/30 15:05 |
action |
apply-patch |
0m
Results:
|
| 344/1 |
2026/07/30 15:05 |
action |
init-style-items |
0m
Results:
|
| 345/1 |
2026/07/30 15:05 |
loop |
|
3m
|
| 346/2 |
2026/07/30 15:05 |
iteration |
0 |
3m
|
| 347/3 |
2026/07/30 15:05 |
agent |
patch-formatter |
3m
Model:
Instruction:
You are an expert Linux kernel developer tasked with formatting a kernel patch.
Your objective is purely formatting: you must ensure the patch complies with the kernel's coding style,
conforms to the surrounding code rules, and passes checkpatch.pl, while preserving the code logic exactly as it is.
You should stop once the requested formatting changes are done and checkpatch.pl is happy.
Do not question the requested changes unless they are obviously wrong.
If the code already conforms to the requested changes, surrounding code rules,
and checkpatch.pl is happy, you should just finish your task.
WARNING: The clang-format tool may break the formatting of the surrounding code (like manual alignment).
Use it with caution. We want to make the change fit into the existing formatting as much as possible.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The current patch diff is:
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2473,6 +2473,7 @@ static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
@@ -2639,6 +2640,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
Use the provided tools to format the patch.
Reply: The patch has been analyzed and verified. It is already fully compliant with the Linux kernel's coding style, conforms to the surrounding code rules, and passes `checkpatch.pl` with zero errors and zero warnings:
```
total: 0 errors, 0 warnings, 14 lines checked
Your patch has no obvious style problems and is ready for submission.
```
No further formatting changes are required.
|
| 348/4 |
2026/07/30 15:05 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 4637
output: 39
thoughts: 50
Thoughts: **Kernel Patch Formatting - My Approach**
Okay, so the user wants a nicely formatted kernel patch, got it. My immediate thought is to make sure we do this right. Before I even look at the formatting though, let's establish a baseline of quality. I need to ensure the patch itself is sound and meets all the required checks.
First things first, I will use `checkpatch` to do a thorough code style and linting analysis. That way I have a clear report of issues with the provided patch, before I start formatting. Then, *after* `checkpatch` is clean, I can look at the diff itself. I'll use `patch-diff` to get a precise view of the modifications made. Armed with both, I'll move forward.
|
| 349/4 |
2026/07/30 15:06 |
tool |
patch-diff |
0m
Error:
Args: map[file:drivers/usb/gadget/function/f_midi2.c]
|
| 350/4 |
2026/07/30 15:06 |
tool |
checkpatch |
0m
Results: map[Output:total: 0 errors, 0 warnings, 14 lines checked
Your patch has no obvious style problems and is ready for submission.
]
|
| 351/4 |
2026/07/30 15:06 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 4946
output: 88
thoughts: 245
Thoughts: **Analyzing the `patch-diff` Function and Associated Code**
Okay, so I'm looking at this `patch-diff` function and it seems the documentation has a minor discrepancy I need to sort out. It *says* the `File` parameter is optional, that if empty, the diff will include all changes. But the schema is indicating that `File` is *required*, but can be an empty string, `"`. That's a bit confusing, but I think I've got it - I can call the function with either an empty string for the `File` parameter, or a specific filename. I'll test it out both ways. For the specific file, I'll use `"drivers/usb/gadget/function/f_midi2.c"`.
Before that, I ran `checkpatch.pl` on my changes, and it's reporting zero errors and warnings, which is always encouraging! The output also mentioned "14 lines checked".
To be thorough, let's now examine that specific file, `"drivers/usb/gadget/function/f_midi2.c"` around the lines of 2473 and 2639, just to double-check formatting and ensure I haven't introduced any subtle style violations that `checkpatch.pl` might have missed. I'll read around those lines for any context that might raise a flag.
|
| 352/4 |
2026/07/30 15:06 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c FirstLine:2460 LineCount:30]
Results: map[Contents:2460: static struct configfs_attribute *f_midi2_ep_attrs[] = {
2461: &f_midi2_ep_opts_attr_protocol,
2462: &f_midi2_ep_opts_attr_protocol_caps,
2463: &f_midi2_ep_opts_attr_ep_name,
2464: &f_midi2_ep_opts_attr_product_id,
2465: &f_midi2_ep_opts_attr_manufacturer,
2466: &f_midi2_ep_opts_attr_family,
2467: &f_midi2_ep_opts_attr_model,
2468: &f_midi2_ep_opts_attr_sw_revision,
2469: NULL,
2470: };
2471:
2472: static void f_midi2_ep_opts_release(struct config_item *item)
2473: {
2474: struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
2475:
2476: kfree(opts->info.ep_name);
2477: kfree(opts->info.product_id);
2478: kfree(opts);
2479: }
2480:
2481: static const struct configfs_item_operations f_midi2_ep_item_ops = {
2482: .release = f_midi2_ep_opts_release,
2483: };
2484:
2485: static const struct configfs_group_operations f_midi2_ep_group_ops = {
2486: .make_group = f_midi2_opts_block_make,
2487: .drop_item = f_midi2_opts_block_drop,
2488: };
2489:
]
|
| 353/4 |
2026/07/30 15:06 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c FirstLine:2625 LineCount:30]
Results: map[Contents:2625: .make_group = f_midi2_opts_ep_make,
2626: .drop_item = f_midi2_opts_ep_drop,
2627: };
2628:
2629: static const struct config_item_type f_midi2_func_type = {
2630: .ct_item_ops = &f_midi2_item_ops,
2631: .ct_group_ops = &f_midi2_group_ops,
2632: .ct_attrs = f_midi2_attrs,
2633: .ct_owner = THIS_MODULE,
2634: };
2635:
2636: static void f_midi2_free_inst(struct usb_function_instance *f)
2637: {
2638: struct f_midi2_opts *opts;
2639:
2640: opts = container_of(f, struct f_midi2_opts, func_inst);
2641:
2642: kfree(opts->info.iface_name);
2643: kfree(opts);
2644: }
2645:
2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
]
|
| 354/4 |
2026/07/30 15:06 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 6587
output: 30
thoughts: 81
Thoughts: **Analyzing the Current Patch and Repository Status**
Okay, first things first, let me pull up the user's provided patch diff. Let's see... the user has stated: "The current patch diff is: ...". Right, got it.
Now, before I start integrating anything or making any recommendations, I need to know: has this patch already been applied? Is it redundant? Let's check the repository's status. I'll need to use `patch-diff` and test with the appropriate file locations. Let's start with a blanket check, using `File: ""`, and then if necessary I'll drill down to specifically check `drivers/usb/gadget/function/f_midi2.c`, depending on the results. Gotta be methodical about this.
|
| 355/4 |
2026/07/30 15:07 |
tool |
patch-diff |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2466,20 +2466,21 @@ static struct configfs_attribute *f_midi2_ep_attrs[] = {
&f_midi2_ep_opts_attr_family,
&f_midi2_ep_opts_attr_model,
&f_midi2_ep_opts_attr_sw_revision,
NULL,
};
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
static const struct configfs_item_operations f_midi2_ep_item_ops = {
.release = f_midi2_ep_opts_release,
};
static const struct configfs_group_operations f_midi2_ep_group_ops = {
@@ -2632,20 +2633,21 @@ static const struct config_item_type f_midi2_func_type = {
.ct_attrs = f_midi2_attrs,
.ct_owner = THIS_MODULE,
};
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
/* gadget alloc_inst */
static struct usb_function_instance *f_midi2_alloc_inst(void)
{
struct f_midi2_opts *opts;
struct f_midi2_ep_opts *ep_opts;
struct f_midi2_block_opts *block_opts;
]
|
| 356/4 |
2026/07/30 15:07 |
llm |
patch-formatter |
1m
Model:
Tokens:
input: 7511
output: 15
thoughts: 384
Thoughts: **My Assessment of the Patch's Status**
Alright, the patch is in the repository, that's a good start. My immediate concern is whether this is the *only* change that has been made. I need to make sure there are no other accidental modifications lurking, or any unexpected side-effects.
First, I'm going to rerun `checkpatch` just to be absolutely sure. Okay, the output looks clean: `total: 0 errors, 0 warnings, 14 lines checked`. It confirms: `"Your patch has no obvious style problems and is ready for submission."`
But even with a clean bill of health from `checkpatch`, I'm still feeling cautious. Let's take a closer look at the code snippets provided just to double-check:
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
and:
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
```
The code seems fine, but there are potentially more changes. Let's make sure by going back to `git`. I need to make sure I'm not overlooking anything. The key now is using `patch-diff` with `File: ""` to show *all* changes made. I'm going to run that command to get the definitive answer on whether there are any other unintended modifications associated with this patch. This is the only way to be completely sure.
|
| 357/4 |
2026/07/30 15:08 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2466,20 +2466,21 @@ static struct configfs_attribute *f_midi2_ep_attrs[] = {
&f_midi2_ep_opts_attr_family,
&f_midi2_ep_opts_attr_model,
&f_midi2_ep_opts_attr_sw_revision,
NULL,
};
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
static const struct configfs_item_operations f_midi2_ep_item_ops = {
.release = f_midi2_ep_opts_release,
};
static const struct configfs_group_operations f_midi2_ep_group_ops = {
@@ -2632,20 +2633,21 @@ static const struct config_item_type f_midi2_func_type = {
.ct_attrs = f_midi2_attrs,
.ct_owner = THIS_MODULE,
};
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
/* gadget alloc_inst */
static struct usb_function_instance *f_midi2_alloc_inst(void)
{
struct f_midi2_opts *opts;
struct f_midi2_ep_opts *ep_opts;
struct f_midi2_block_opts *block_opts;
]
|
| 358/4 |
2026/07/30 15:08 |
llm |
patch-formatter |
0m
Model:
Tokens:
input: 9035
output: 86
thoughts: 833
Thoughts: **Analysis of the Provided Patch**
Okay, so the user has given me a patch, and my job is straightforward: ensure it adheres to kernel coding style, matches the surrounding code, and passes `checkpatch.pl`, without altering the logic. First, I verified that the changes *exactly* match the diff.
I was about to dive into a deeper style check, or even a full `checkpatch` run on the whole file, but hold on, let's go back and re-read the instructions! My focus is solely on formatting; I'm not supposed to question the changes unless they are obviously incorrect. If the requested changes are already compliant, I'm done.
A quick review of the surrounding code reveals the pattern for blank lines after variable declarations, specifically in `f_midi2_ep_opts_release` and `f_midi2_free_inst`. The patch *does* introduce that blank line in `f_midi2_free_inst`, matching what's already present in other similar functions. I even double-checked the *original* code for the same function to confirm the existing style.
I did, however, pause briefly to consider if there were other files or functions that might require similar changes, but the instructions are clear: if everything's good with this patch, I'm finished.
So, let's see... the instructions say, "If the code already conforms to the requested changes, surrounding code rules, and checkpatch.pl is happy, you should just finish your task." I've confirmed that the patch is applied, `checkpatch.pl` *should* be happy, and the formatting is exactly what's expected according to the surrounding code rules.
Just to be absolutely sure, I did a final scan for any stray trailing whitespace or other minor issues and, just for kicks, I ran `checkpatch` again, and indeed, it reported "0 errors, 0 warnings, 14 lines checked" and the delightful "Your patch has no obvious style problems and is ready for submission."
Fantastic! I'm completely satisfied that this patch is perfectly formatted and compliant. Time to give the user a clear and concise report.
|
| 359/3 |
2026/07/30 15:09 |
action |
test-patch-inplace |
0m
Results: map[PatchDiff:diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2473,6 +2473,7 @@ static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
@@ -2639,6 +2640,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
TestError:]
|
| 360/3 |
2026/07/30 15:09 |
action |
run-checkpatch |
0m
Results: map[CheckpatchOutput:total: 0 errors, 0 warnings, 14 lines checked
Your patch has no obvious style problems and is ready for submission.
NeedRefinement:false]
|
| 361/1 |
2026/07/30 15:09 |
agent |
fixes-finder |
2m
Model:
Results: map[FixesHash:29ee7a4dddd5caa18d1cef000f20c6af43f762f1]
Instruction:
You are an experienced Linux kernel developer tasked with identifying the commit
that introduced the bug being fixed. Identifying the correct buggy commit is crucial
for proper kernel maintenance (backporting to stable trees, etc.).
Your investigation strategy:
1. Examine the patch that fixes the bug. Use git tools (like git-log or git-blame)
to trace the history of the lines or functions modified by the patch.
2. Analyze the stack trace in the crash report. Identify the key files and functions
involved in the crash and investigate their history to see when the problematic
logic was introduced.
3. Compare the bug explanation with the commit history to find the point where
the described logic error first appeared.
A bug is typically introduced when a piece of code is first written, or when
a refactoring changed its logic in a way that introduced the bug.
Trace the history of relevant symbols or find when specific code patterns were introduced/removed.
You must provide exactly one bug-introducing commit hash.
If you are unable to confidently determine the bug-introducing commit after investigation,
return an empty string rather than guessing.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The crash is:
2026/07/30 13:30:35 executed programs: 5
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504 [inline]
f_midi2_alloc_inst+0x80/0x2c0 drivers/usb/gadget/function/f_midi2.c:2666
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342 [inline]
f_midi2_alloc_inst+0x16a/0x2c0 drivers/usb/gadget/function/f_midi2.c:2673
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF
The explanation of the root cause is:
The root cause of this memory leak is that the driver fails to drop the reference counts of its default configfs groups when the parent group is destroyed.
When a USB function instance for `midi2` is allocated via `f_midi2_alloc_inst()`, it dynamically allocates `ep_opts` and `block_opts` structures using `kzalloc_obj()`. These structures are then initialized as configfs groups and added as default groups to their respective parents:
- `ep.0` is added as a default group to the function instance group (`opts->func_inst.group`).
- `block.0` is added as a default group to the endpoint group (`ep_opts->group`).
When the user removes the configfs directory for the function instance (via `rmdir`), configfs removes the dentries for the default groups but **does not** automatically drop their reference counts. The responsibility of dropping the refcounts of default groups lies with the driver, typically by calling `configfs_remove_default_groups()` when the parent group is released.
However, `f_midi2_free_inst()` (which is called when the function instance's refcount reaches 0) only frees the `f_midi2_opts` structure and does not clean up its default groups. Similarly, `f_midi2_ep_opts_release()` does not clean up its default groups. As a result, the `ep_opts` and `block_opts` structures are leaked.
### How to fix it
To fix the leak, you need to explicitly remove the default groups and drop their reference counts before freeing the parent structures. This can be done by calling `configfs_remove_default_groups()` in the respective release functions.
1. In `f_midi2_free_inst()`, drop the default groups of the function instance (this will drop the refcount of `ep.0` and trigger `f_midi2_ep_opts_release`):
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group); /* ADD THIS */
kfree(opts->info.iface_name);
kfree(opts);
}
```
2. In `f_midi2_ep_opts_release()`, drop the default groups of the endpoint (this will drop the refcount of `block.0` and trigger `f_midi2_block_opts_release`):
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item)); /* ADD THIS */
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
With these changes, the hierarchy of default groups will be properly unrolled and freed when the function instance is destroyed, whether it's through configfs `rmdir` or an error path that directly calls `usb_put_function_instance()`.
The patch that fixes the bug is:
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2473,6 +2473,7 @@ static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
@@ -2639,6 +2640,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
Search for the commit(s) that introduced this bug.
|
| 362/2 |
2026/07/30 15:09 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 6539
output: 45
thoughts: 164
Thoughts: **Pinpointing the Memory Leak in f_midi2.c**
Alright, let's get down to brass tacks. We've got a memory leak on our hands in `drivers/usb/gadget/function/f_midi2.c`, and my initial assessment points the finger at the absence of `configfs_remove_default_groups` within the `f_midi2_ep_opts_release` and `f_midi2_free_inst` functions. Clearly, we need to trace the lineage of this code to identify the offending commit.
My plan of attack is straightforward: I'll dive into the Git history of `drivers/usb/gadget/function/f_midi2.c`. I'm going to leverage `git log` to pinpoint the exact commits that brought `f_midi2_ep_opts_release` and `f_midi2_free_inst` into the fold. Knowing their genesis will illuminate when the potential oversight might have been introduced, and allow us to verify that hypothesis. With the relevant commit range identified, I can then zero in on any changes to memory management within those functions to find the culprit.
|
| 363/2 |
2026/07/30 15:09 |
tool |
git-log |
0m
Error: SymbolName and PathPrefix cannot be used together
Args: map[PathPrefix:drivers/usb/gadget/function/f_midi2.c SymbolName:f_midi2_free_inst]
Results:
|
| 364/2 |
2026/07/30 15:09 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 7017
output: 45
thoughts: 14
Thoughts: Okay, here's my summary, tailored as requested:
**Navigating the Codebase Effectively**
Right, let's see. Thinking through this, it feels like the most elegant approach to this particular problem involves a judicious pairing of tools. I need to be able to pinpoint a particular definition. `SourcePath` seems like the obvious starting point. I can use that to locate the file, the specific location in the filesystem. But, that's not enough on its own. I also need to be able to identify the correct code element within that file. That's where `SymbolName` comes in. Together, `SourcePath` and `SymbolName` offer a powerful and targeted way to home in on the specific piece of code I'm after. This combination should yield the exact location and definition I require. Yes, that's the path I should follow.
|
| 365/2 |
2026/07/30 15:09 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/usb/gadget/function/f_midi2.c SymbolName:f_midi2_free_inst]
Results: map[Output:29ee7a4dddd5 usb: gadget: midi2: Add configfs support
8b645922b223 usb: gadget: Add support for USB MIDI 2.0 function driver
]
|
| 366/2 |
2026/07/30 15:09 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 7320
output: 27
thoughts: 35
Thoughts: Okay, let's dive into commit `8b645922b223 usb: gadget: Add support for USB MIDI 2.0 function driver`.
**Analyzing the USB MIDI 2.0 Gadget Driver Commit**
Right, so I see this commit aims to introduce support for USB MIDI 2.0 within the gadget framework. This is crucial; USB MIDI 2.0 is the direction the industry is heading in, and if we're serious about our embedded music projects, this is a must-have. I need to understand how well they've implemented the new features.
First, I'll examine how they've structured the driver within the existing gadget framework. Are they using the standard USB descriptors effectively? Have they clearly separated the MIDI 2.0 specific parts from the generic USB transport layer? Good, clean separation is key for maintainability and future upgrades. I'll need to check the descriptor handling – does it accurately reflect the requirements of the MIDI 2.0 specification? Specifically, I'll be looking for support of Universal MIDI Packets (UMP) and any associated data formats.
Next, I'll want to assess the endpoint management. Are they leveraging the asynchronous transfer capabilities of USB 2.0 and/or 3.0? This is critical for low-latency MIDI data transmission. Ideally, they'll have optimized the buffering and interrupt handling to minimize overhead. I'll need to check the interrupt in and out functions to see if they are doing this correctly.
Finally, I'll check how they've handled error conditions and edge cases. MIDI, by its nature, is real-time. Losing a packet or introducing delays is unacceptable. Is the code robust to different clock speeds and USB transfer sizes? Are there sufficient error checks and recovery mechanisms in place? It's all about ensuring the system is rock solid. I need to be sure this is going to play well with all the other devices it may be connected to.
|
| 367/2 |
2026/07/30 15:09 |
tool |
git-show |
0m
Args:
Results: map[Output:
Full output is too long, showing 1000 out of 1877 lines.
[commit 8b645922b22303cec4628dbbbf6c8553d1cdec87
Author: Takashi Iwai <tiwai@suse.de>
Date: Tue Jul 25 08:22:00 2023 +0200
usb: gadget: Add support for USB MIDI 2.0 function driver
This patch adds the support for USB MIDI 2.0 gadget function driver.
The driver emulates a USB MIDI 2.0 interface with one or more UMP
Endpoints, where each of UMP Endpoint is a pair of MIDI Endpoints for
handling MIDI 2.0 UMP packets. When the function driver is bound, the
driver creates an ALSA card object with UMP rawmidi devices. This is
a kind of loop-back where the incoming and upcoming UMP packets
from/to the MIDI 2.0 UMP Endpoints are transferred as-is. In
addition, legacy (MIDI 1.0) rawmidi devices are created, so that
legacy applications can work in the gadget side, too.
When a USB MIDI 2.0 gadget interface appears, the connected host can
use it with the snd-usb-audio driver where MIDI 2.0 support is
enabled. Both gadget and connected hosts will have the similar UMP
Endpoint and Function Block (or Group Terminal Block) information.
Slight differences are the direction and UI-hint bits; it's due to the
nature of gadget driver, and the input/output direction is swapped in
both sides (the input for gadget is the output for host, and vice
versa).
The driver supports the brand-new UMP v1.1 feature, including the UMP
Stream message handling for providing UMP Endpoint and Function Block
information as well as dealing with the MIDI protocol switch. The
driver responds to UMP Stream messages by itself. OTOH, MIDI-CI
message handling isn't implemented in the kernel driver; it should be
processed in the user-space through the loopback UMP device.
As of this patch, the whole configuration is fixed, providing only one
bidirectional UMP Endpoint containing a single FB/GTB with a single
UMP Group. The configuration will be dynamically changeable in the
following patches.
The traditional MIDI 1.0 is still provided in the altset 0 (which is
mandatory per spec). But it's only about the configuration, and no
actual I/O will be running for the altset 0 as of this patch. The
proper support MIDI 1.0 altset will follow in later patches, too.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://lore.kernel.org/r/20230725062206.9674-2-tiwai@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 336db8f92afa..b3592bcb0f96 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -208,6 +208,9 @@ config USB_F_UVC
config USB_F_MIDI
tristate
+config USB_F_MIDI2
+ tristate
+
config USB_F_HID
tristate
@@ -436,6 +439,21 @@ config USB_CONFIGFS_F_MIDI
connections can then be made on the gadget system, using
ALSA's aconnect utility etc.
+config USB_CONFIGFS_F_MIDI2
+ bool "MIDI 2.0 function"
+ depends on USB_CONFIGFS
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_UMP
+ select SND_UMP_LEGACY_RAWMIDI
+ select USB_F_MIDI2
+ help
+ The MIDI 2.0 function driver provides the generic emulated
+ USB MIDI 2.0 interface, looped back to ALSA UMP rawmidi
+ device on the gadget host. It supports UMP 1.1 spec and
+ responds UMP Stream messages for UMP Endpoint and Function
+ Block information / configuration.
+
config USB_CONFIGFS_F_HID
bool "HID function"
depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 5d3a6cf02218..87917a7d4a9b 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -44,6 +44,8 @@ usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
+usb_f_midi2-y := f_midi2.o
+obj-$(CONFIG_USB_F_MIDI2) += usb_f_midi2.o
usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
usb_f_printer-y := f_printer.o
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
new file mode 100644
index 000000000000..848cb3150deb
--- /dev/null
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -0,0 +1,1691 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_midi2.c -- USB MIDI 2.0 class function driver
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/ump.h>
+#include <sound/ump_msg.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi-v2.h>
+
+#include "u_f.h"
+#include "u_midi2.h"
+
+struct f_midi2;
+struct f_midi2_ep;
+struct f_midi2_usb_ep;
+
+/* Context for each USB request */
+struct f_midi2_req_ctx {
+ struct f_midi2_usb_ep *usb_ep; /* belonging USB EP */
+ unsigned int index; /* array index: 0-31 */
+ struct usb_request *req; /* assigned request */
+};
+
+/* Resources for a USB Endpoint */
+struct f_midi2_usb_ep {
+ struct f_midi2 *card; /* belonging card */
+ struct f_midi2_ep *ep; /* belonging UMP EP (optional) */
+ struct usb_ep *usb_ep; /* assigned USB EP */
+ void (*complete)(struct usb_ep *usb_ep, struct usb_request *req);
+ unsigned long free_reqs; /* bitmap for unused requests */
+ unsigned int num_reqs; /* number of allocated requests */
+ struct f_midi2_req_ctx *reqs; /* request context array */
+};
+
+/* Resources for UMP Function Block (and USB Group Terminal Block) */
+struct f_midi2_block {
+ struct f_midi2_block_info info; /* FB info, copied from configfs */
+ struct snd_ump_block *fb; /* assigned FB */
+ unsigned int gtb_id; /* assigned GTB id */
+ unsigned int string_id; /* assigned string id */
+};
+
+/* Resources for UMP Endpoint */
+struct f_midi2_ep {
+ struct snd_ump_endpoint *ump; /* assigned UMP EP */
+ struct f_midi2 *card; /* belonging MIDI 2.0 device */
+
+ struct f_midi2_ep_info info; /* UMP EP info, copied from configfs */
+ unsigned int num_blks; /* number of FBs */
+ struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS]; /* UMP FBs */
+
+ struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */
+ struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */
+};
+
+/* indices for USB strings */
+enum {
+ STR_IFACE = 0,
+ STR_GTB1 = 1,
+};
+
+/* 1-based GTB id to string id */
+#define gtb_to_str_id(id) (STR_GTB1 + (id) - 1)
+
+/* operation mode */
+enum {
+ MIDI_OP_MODE_UNSET, /* no altset set yet */
+ MIDI_OP_MODE_MIDI1, /* MIDI 1.0 (altset 0) is used */
+ MIDI_OP_MODE_MIDI2, /* MIDI 2.0 (altset 1) is used */
+};
+
+/* Resources for MIDI 2.0 Device */
+struct f_midi2 {
+ struct usb_function func;
+ struct usb_gadget *gadget;
+ struct snd_card *card;
+
+ /* MIDI 1.0 in/out USB EPs */
+ struct f_midi2_usb_ep midi1_ep_in;
+ struct f_midi2_usb_ep midi1_ep_out;
+
+ int midi_if; /* USB MIDI interface number */
+ int operation_mode; /* current operation mode */
+
+ spinlock_t queue_lock;
+
+ struct f_midi2_card_info info; /* card info, copied from configfs */
+
+ unsigned int num_eps;
+ struct f_midi2_ep midi2_eps[MAX_UMP_EPS];
+
+ unsigned int total_blocks; /* total number of blocks of all EPs */
+ struct usb_string *string_defs;
+ struct usb_string *strings;
+};
+
+#define func_to_midi2(f) container_of(f, struct f_midi2, func)
+
+/* get EP name string */
+static const char *ump_ep_name(const struct f_midi2_ep *ep)
+{
+ return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget";
+}
+
+/* get EP product ID string */
+static const char *ump_product_id(const struct f_midi2_ep *ep)
+{
+ return ep->info.product_id ? ep->info.product_id : "Unique Product ID";
+}
+
+/* get FB name string */
+static const char *ump_fb_name(const struct f_midi2_block_info *info)
+{
+ return info->name ? info->name : "MIDI 2.0 Gadget I/O";
+}
+
+/*
+ * USB Descriptor Definitions
+ */
+/* GTB header descriptor */
+static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = {
+ .bLength = sizeof(gtb_header_desc),
+ .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
+ .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK_HEADER,
+ .wTotalLength = __cpu_to_le16(0x12), // to be filled
+};
+
+/* GTB descriptor template: most items are replaced dynamically */
+static struct usb_ms20_gr_trm_block_descriptor gtb_desc = {
+ .bLength = sizeof(gtb_desc),
+ .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
+ .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK,
+ .bGrpTrmBlkID = 0x01,
+ .bGrpTrmBlkType = USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL,
+ .nGroupTrm = 0x00,
+ .nNumGroupTrm = 1,
+ .iBlockItem = 0,
+ .bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_64,
+ .wMaxInputBandwidth = 0,
+ .wMaxOutputBandwidth = 0,
+};
+
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32);
+
+#define EP_MAX_PACKET_INT 8
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor midi2_audio_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0,
+};
+
+static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = 0x01,
+ .bcdADC = __cpu_to_le16(0x0100),
+ .wTotalLength = __cpu_to_le16(0x0009),
+ .bInCollection = 0x01,
+ .baInterfaceNr = { 0x01 }, // to be filled
+};
+
+/* MIDI 1.0 Streaming Interface (altset 0) */
+static struct usb_interface_descriptor midi2_midi1_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi1_class_desc = {
+ .bLength = 0x07,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __cpu_to_le16(0x0100),
+ .wTotalLength = __cpu_to_le16(0x41), // to be calculated
+};
+
+/* MIDI 1.0 IN (Embedded) Jack */
+static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack1_desc = {
+ .bLength = 0x06,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = 0x01,
+ .iJack = 0,
+};
+
+/* MIDI 1.0 IN (External) Jack */
+static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack2_desc = {
+ .bLength = 0x06,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = 0x02,
+ .iJack = 0,
+};
+
+/* MIDI 1.0 OUT (Embedded) Jack */
+static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack1_desc = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = 0x03,
+ .bNrInputPins = 1,
+ .pins = { { 0x02, 0x01 } },
+ .iJack = 0,
+};
+
+/* MIDI 1.0 OUT (External) Jack */
+static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack2_desc = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = 0x04,
+ .bNrInputPins = 1,
+ .pins = { { 0x01, 0x01 } },
+ .iJack = 0,
+};
+
+/* MIDI 1.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT | 0, // set up dynamically
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_out_class_desc = {
+ .bLength = 0x05,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = { 0x01 },
+};
+
+/* MIDI 1.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN | 0, // set up dynamically
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_in_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_in_class_desc = {
+ .bLength = 0x05,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = { 0x03 },
+};
+
+/* MIDI 2.0 Streaming Interface (altset 1) */
+static struct usb_interface_descriptor midi2_midi2_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi2_class_desc = {
+ .bLength = 0x07,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __cpu_to_le16(0x0200),
+ .wTotalLength = __cpu_to_le16(0x07),
+};
+
+/* MIDI 2.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS];
+
+/* MIDI 2.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi2_ep_in_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS];
+
+/* Arrays of descriptors to be created */
+static void *midi2_audio_descs[] = {
+ &midi2_audio_if_desc,
+ &midi2_audio_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_descs[] = {
+ &midi2_midi1_if_desc,
+ &midi2_midi1_class_desc,
+ &midi2_midi1_in_jack1_desc,
+ &midi2_midi1_in_jack2_desc,
+ &midi2_midi1_out_jack1_desc,
+ &midi2_midi1_out_jack2_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_descs[] = {
+ &midi2_midi1_ep_out_desc,
+ &midi2_midi1_ep_out_class_desc,
+ &midi2_midi1_ep_in_desc,
+ &midi2_midi1_ep_in_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_ss_descs[] = {
+ &midi2_midi1_ep_out_desc,
+ &midi2_midi1_ep_out_ss_comp_desc,
+ &midi2_midi1_ep_out_class_desc,
+ &midi2_midi1_ep_in_desc,
+ &midi2_midi1_ep_in_ss_comp_desc,
+ &midi2_midi1_ep_in_class_desc,
+ NULL
+};
+
+static void *midi2_midi2_descs[] = {
+ &midi2_midi2_if_desc,
+ &midi2_midi2_class_desc,
+ NULL
+};
+
+/*
+ * USB request handling
+ */
+
+/* get an empty request for the given EP */
+static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep)
+{
+ struct usb_request *req = NULL;
+ unsigned long flags;
+ int index;
+
+ spin_lock_irqsave(&usb_ep->card->queue_lock, flags);
+ if (!usb_ep->free_reqs)
+ goto unlock;
+ index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs);
+ if (index >= usb_ep->num_reqs)
+ goto unlock;
+ req = usb_ep->reqs[index].req;
+ if (!req)
+ goto unlock;
+ clear_bit(index, &usb_ep->free_reqs);
+ req->length = 0;
+ unlock:
+ spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags);
+ return req;
+}
+
+/* put the empty request back */
+static void put_empty_request(struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags);
+ set_bit(ctx->index, &ctx->usb_ep->free_reqs);
+ spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags);
+}
+
+/*
+ * UMP v1.1 Stream message handling
+ */
+
+/* queue a request to UMP EP; request is either queued or freed after this */
+static int queue_request_ep_raw(struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ int err;
+
+ req->complete = ctx->usb_ep->complete;
+ err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC);
+ if (err) {
+ put_empty_request(req);
+ return err;
+ }
+ return 0;
+}
+
+/* queue a request with endianness conversion */
+static int queue_request_ep_in(struct usb_request *req)
+{
+ /* UMP packets have to be converted to little-endian */
+ cpu_to_le32_array((u32 *)req->buf, req->length >> 2);
+ return queue_request_ep_raw(req);
+}
+
+/* reply a UMP packet via EP-in */
+static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct usb_request *req;
+
+ req = get_empty_request(usb_ep);
+ if (!req)
+ return -ENOSPC;
+
+ req->length = len;
+ memcpy(req->buf, buf, len);
+ return queue_request_ep_in(req);
+}
+
+/* reply a UMP stream EP info */
+static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_ep_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_EP_INFO,
+ .ump_version_major = 0x01,
+ .ump_version_minor = 0x01,
+ .num_function_blocks = ep->num_blks,
+ .static_function_block = !!ep->card->info.static_block,
+ .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 |
+ UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* reply a UMP EP device info */
+static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_devince_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
+ .manufacture_id = ep->info.manufacturer,
+ .family_lsb = ep->info.family & 0xff,
+ .family_msb = (ep->info.family >> 8) & 0xff,
+ .model_lsb = ep->info.model & 0xff,
+ .model_msb = (ep->info.model >> 8) & 0xff,
+ .sw_revision = ep->info.sw_revision,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+#define UMP_STREAM_PKT_BYTES 16 /* UMP stream packet size = 16 bytes*/
+#define UMP_STREAM_EP_STR_OFF 2 /* offset of name string for EP info */
+#define UMP_STREAM_FB_STR_OFF 3 /* offset of name string for FB info */
+
+/* Helper to replay a string */
+static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name,
+ unsigned int type, unsigned int extra,
+ unsigned int start_ofs)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct f_midi2 *midi2 = ep->card;
+ struct usb_request *req;
+ unsigned int pos;
+ u32 *buf;
+
+ if (!*name)
+ return;
+ req = get_empty_request(usb_ep);
+ if (!req)
+ return;
+
+ buf = (u32 *)req->buf;
+ pos = start_ofs;
+ for (;;) {
+ if (pos == start_ofs) {
+ memset(buf, 0, UMP_STREAM_PKT_BYTES);
+ buf[0] = ump_stream_compose(type, 0) | extra;
+ }
+ buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8);
+ if (!*name) {
+ if (req->length)
+ buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26;
+ req->length += UMP_STREAM_PKT_BYTES;
+ break;
+ }
+ if (++pos == UMP_STREAM_PKT_BYTES) {
+ if (!req->length)
+ buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26;
+ else
+ buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26;
+ req->length += UMP_STREAM_PKT_BYTES;
+ if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES)
+ break;
+ buf += 4;
+ pos = start_ofs;
+ }
+ }
+
+ if (req->length)
+ queue_request_ep_in(req);
+ else
+ put_empty_request(req);
+}
+
+/* Reply a UMP EP name string */
+static void reply_ump_stream_ep_name(struct f_midi2_ep *ep)
+{
+ reply_ump_stream_string(ep, ump_ep_name(ep),
+ UMP_STREAM_MSG_STATUS_EP_NAME, 0,
+ UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP product ID string */
+static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep)
+{
+ reply_ump_stream_string(ep, ump_product_id(ep),
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0,
+ UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP stream config */
+static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_stream_cfg rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
+ };
+
+ if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
+ SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
+ else
+ rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a UMP FB info */
+static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk)
+{
+ struct f_midi2_block_info *b = &ep->blks[blk].info;
+ struct snd_ump_stream_msg_fb_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_FB_INFO,
+ .active = !!b->active,
+ .function_block_id = blk,
+ .ui_hint = b->ui_hint,
+ .midi_10 = b->is_midi1,
+ .direction = b->direction,
+ .first_group = b->first_group,
+ .num_groups = b->num_groups,
+ .midi_ci_version = b->midi_ci_version,
+ .sysex8_streams = b->sysex8_streams,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a FB name string */
+static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk)
+{
+ reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info),
+ UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8,
+ UMP_STREAM_FB_STR_OFF);
+}
+
+/* Process a UMP Stream message */
+static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
+{
+ struct f_midi2 *midi2 = ep->card;
+ unsigned int format, status, blk;
+
+ format = ump_stream_message_format(*data);
+ status = ump_stream_message_status(*data);
+ switch (status) {
+ case UMP_STREAM_MSG_STATUS_EP_DISCOVERY:
+ if (format)
+ return; // invalid
+ if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO)
+ reply_ump_stream_ep_info(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO)
+ reply_ump_stream_ep_device(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME)
+ reply_ump_stream_ep_name(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID)
+ reply_ump_stream_ep_pid(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG)
+ reply_ump_stream_ep_config(ep);
+ return;
+ case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
+ if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
+ ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ DBG(midi2, "Switching Protocol to MIDI2\n");
+ } else {
+ ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ DBG(midi2, "Switching Protocol to MIDI1\n");
+ }
+ snd_ump_switch_protocol(ep->ump, ep->info.protocol);
+ reply_ump_stream_ep_config(ep);
+ return;
+ case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
+ if (format)
+ return; // invalid
+ blk = (*data >> 8) & 0xff;
+ if (blk >= ep->num_blks)
+ return;
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
+ reply_ump_stream_fb_info(ep, blk);
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
+ reply_ump_stream_fb_name(ep, blk);
+ return;
+ }
+}
+
+/* Process UMP messages included in a USB request */
+static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req)
+{
+ const u32 *data = (u32 *)req->buf;
+ int len = req->actual >> 2;
+ const u32 *in_buf = ep->ump->input_buf;
+
+ for (; len > 0; len--, data++) {
+ if (snd_ump_receive_ump_val(ep->ump, *data) <= 0)
+ continue;
+ if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM)
+ process_ump_stream_msg(ep, in_buf);
+ }
+}
+
+/*
+ * MIDI 2.0 UMP USB request handling
+ */
+
+/* complete handler for UMP EP-out requests */
+static void f_midi2_ep_out_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2_ep *ep = ctx->usb_ep->ep;
+ struct f_midi2 *midi2 = ep->card;
+ int status = req->status;
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ goto error;
+ }
+
+ /* convert to UMP packet in native endianness */
+ le32_to_cpu_array((u32 *)req->buf, req->actual >> 2);
+
+ if (midi2->info.process_ump)
+ process_ump(ep, req);
+
+ snd_ump_receive(ep->ump, req->buf, req->actual & ~3);
+
+ if (midi2->operation_mode != MIDI_OP_MODE_MIDI2)
+ goto error;
+
+ if (queue_request_ep_raw(req))
+ goto error;
+ return;
+
+ error:
+ put_empty_request(req);
+}
+
+/* Transmit UMP packets received from user-space to the gadget */
+static void process_ump_transmit(struct f_midi2_ep *ep)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct f_midi2 *midi2 = ep->card;
+ struct usb_request *req;
+ int len;
+
+ if (!usb_ep->usb_ep->enabled)
+ return;
+
+ for (;;) {
+ req = get_empty_request(usb_ep);
+ if (!req)
+ break;
+ len = snd_ump_transmit(ep->ump, (u32 *)req->buf,
+ midi2->info.req_buf_size);
+ if (len <= 0) {
+ put_empty_request(req);
+ break;
+ }
+
+ req->length = len;
+ if (queue_request_ep_in(req) < 0)
+ break;
+ }
+}
+
+/* Complete handler for UMP EP-in requests */
+static void f_midi2_ep_in_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2_ep *ep = ctx->usb_ep->ep;
+ struct f_midi2 *midi2 = ep->card;
+ int status = req->status;
+
+ put_empty_request(req);
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ return;
+ }
+
+ process_ump_transmit(ep);
+}
+
+/* Start MIDI EP */
+static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep,
+ struct usb_function *fn)
+{
+ int err;
+
+ usb_ep_disable(usb_ep->usb_ep);
+ err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep);
+ if (err)
+ return err;
+ return usb_ep_enable(usb_ep->usb_ep);
+}
+
+/* Drop pending requests */
+static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ int i;
+
+ if (!usb_ep->num_reqs)
+ return;
+
+ for (i = 0; i < usb_ep->num_reqs; i++) {
+ if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) {
+ usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req);
+ set_bit(i, &usb_ep->free_reqs);
+ }
+ }
+}
+
+/* Allocate requests for the given EP */
+static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ struct f_midi2 *midi2 = usb_ep->card;
+ int i;
+
+ if (!usb_ep->reqs)
+ return -EINVAL;
+
+ for (i = 0; i < midi2->info.num_reqs; i++) {
+ if (usb_ep->reqs[i].req)
+ continue;
+ usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep,
+ midi2->info.req_buf_size);
+ if (!usb_ep->reqs[i].req)
+ return -ENOMEM;
+ usb_ep->reqs[i].req->context = &usb_ep->reqs[i];
+ }
+ return 0;
+}
+
+/* Free allocated requests */
+static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ struct f_midi2 *midi2 = usb_ep->card;
+ int i;
+
+ for (i = 0; i < midi2->info.num_reqs; i++) {
+ if (!usb_ep->reqs[i].req)
+ continue;
+ free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req);
+ usb_ep->reqs[i].req = NULL;
+ }
+}
+
+/* Initialize EP */
+static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
+ struct f_midi2_usb_ep *usb_ep,
+ void *desc, int num_reqs,
+ void (*complete)(struct usb_ep *usb_ep,
+ struct usb_request *req))
+{
+ int i;
+
+ usb_ep->card = midi2;
+ usb_ep->ep = ep;
+ usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc);
+ if (!usb_ep->usb_ep)
+ return -ENODEV;
+ usb_ep->complete = complete;
+
+ if (num_reqs) {
+ usb_ep->reqs = kcalloc(num_reqs, sizeof(*usb_ep->reqs),
+ GFP_KERNEL);
+ if (!usb_ep->reqs)
+ return -ENOMEM;
+ for (i = 0; i < num_reqs; i++) {
+ usb_ep->reqs[i].index = i;
+ usb_ep->reqs[i].usb_ep = usb_ep;
+ set_bit(i, &usb_ep->free_reqs);
+ usb_ep->num_reqs++;
+ }
+ }
+
+ return 0;
+}
+
+/* Free EP */
+static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep)
+{
+ f_midi2_drop_reqs(usb_ep);
+
+ f_midi2_free_ep_reqs(usb_ep);
+
+ kfree(usb_ep->reqs);
+ usb_ep->num_reqs = 0;
+ usb_ep->free_reqs = 0;
+ usb_ep->reqs = NULL;
+}
+
+/* Queue requests for EP-out at start */
+static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ int i, err;
+
+ for (i = 0; i < usb_ep->num_reqs; i++) {
+ if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req)
+ continue;
+ usb_ep->reqs[i].req->complete = usb_ep->complete;
+ err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req,
+ GFP_ATOMIC);
+ if (!err)
+ clear_bit(i, &usb_ep->free_reqs);
+ }
+}
+
+/*
+ * Gadget Function callbacks
+ */
+
+/* gadget function set_alt callback */
+static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf,
+ unsigned int alt)
+{
+ struct f_midi2 *midi2 = func_to_midi2(fn);
+ struct f_midi2_ep *ep;
+ int i, op_mode, err;
+
+ if (intf != midi2->midi_if || alt > 1)
+ return 0;
+
+ if (alt == 0)
+ op_mode = MIDI_OP_MODE_MIDI1;
+ else if (alt == 1)
+ op_mode = MIDI_OP_MODE_MIDI2;
+ else
+ op_mode = MIDI_OP_MODE_UNSET;
+
+ if (midi2->operation_mode == op_mode)
+ return 0;
+
]
]
|
| 368/2 |
2026/07/30 15:09 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 20311
output: 43
thoughts: 99
Thoughts: **Analyzing Potential Memory Leaks in MIDI Driver Code**
Okay, so commit `8b645922b22303cec4628dbbbf6c8553d1cdec87` seems interesting. It looks like it introduced `f_midi2_free_inst` and `f_midi2_ep_opts_release`, which is probably related to cleaning up MIDI 2.0 instances. My initial thought is to zero in on `f_midi2_free_inst` and check for potential memory leaks, specifically around the `configfs` interaction. We all know how easy it is to miss a `configfs_remove_default_groups` call, and if that's the case we could have a nasty memory leak. I'll need to dive into the source code of that function within this commit and look for any clues. Let's see what's what...
|
| 369/2 |
2026/07/30 15:09 |
tool |
grepper |
0m
Args: map[Expression:f_midi2_free_inst PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:drivers/usb/gadget/function/f_midi2.c=2629=static const struct config_item_type f_midi2_func_type = {
--
drivers/usb/gadget/function/f_midi2.c-2635-
drivers/usb/gadget/function/f_midi2.c:2636:static void f_midi2_free_inst(struct usb_function_instance *f)
drivers/usb/gadget/function/f_midi2.c-2637-{
--
drivers/usb/gadget/function/f_midi2.c=2647=static struct usb_function_instance *f_midi2_alloc_inst(void)
--
drivers/usb/gadget/function/f_midi2.c-2658- mutex_init(&opts->lock);
drivers/usb/gadget/function/f_midi2.c:2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
drivers/usb/gadget/function/f_midi2.c-2660- opts->info.process_ump = true;
]
|
| 370/2 |
2026/07/30 15:09 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 20946
output: 48
thoughts: 43
Thoughts: Here's my analysis of the provided information, as if these were my own thoughts:
**Dissecting `f_midi2_free_inst` Implementation**
Okay, so I need to dive into the `f_midi2_free_inst` function. The context suggests it's within the `f_midi2.c` file, specifically within the USB gadget driver code, and I'm guessing around line 2636. I want to see how this function handles deallocation of a MIDI 2.0 instance. I need to understand what resources it's freeing and in what order. I'm assuming it handles device removal or disconnection. I'm looking for memory leaks, improper locking (especially with those USB gadget drivers - you gotta be careful), or any potential race conditions. I need to verify that it's gracefully shutting down all the associated endpoints and releasing any device-specific data structures. My focus is on resource management. I'll check to see if it frees the `midi2_inst` struct and any associated buffer allocated memory. It's also important to check it handles any potential errors.
|
| 371/2 |
2026/07/30 15:09 |
tool |
read-file |
0m
Error: argument "FirstLine" has wrong type: got string, want int
Args: map[File:drivers/usb/gadget/function/f_midi2.c FirstLine:2636 LineCount:20]
|
| 372/2 |
2026/07/30 15:09 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 21303
output: 44
thoughts: 11
Thoughts: Okay, here's the summary, rendered as your internal thought process:
**Integer Data Type Verification**
Alright, let's take a look at this. Immediately, my initial assessment of the data type associated with `FirstLine` is, it is critical that this is an integer. Ensuring that this variable is defined and utilized with proper type is essential for computation and subsequent analysis. I can now begin verifying if the current instantiation and all subsequent operations, throughout the program, will be handled correctly with the assigned datatype. The value of `FirstLine` must be rigorously checked and compared.
|
| 373/2 |
2026/07/30 15:09 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c FirstLine:2636 LineCount:20]
Results: map[Contents:2636: static void f_midi2_free_inst(struct usb_function_instance *f)
2637: {
2638: struct f_midi2_opts *opts;
2639:
2640: opts = container_of(f, struct f_midi2_opts, func_inst);
2641:
2642: kfree(opts->info.iface_name);
2643: kfree(opts);
2644: }
2645:
2646: /* gadget alloc_inst */
2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
]
|
| 374/2 |
2026/07/30 15:09 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 21781
output: 45
thoughts: 42
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as an expert in the field:
**Digging into the MIDI Function Driver**
Alright, so `f_midi2_free_inst` *doesn't* contain the `configfs_remove_default_groups` function, which is interesting. Given that we're talking about resource cleanup, that's a key omission to be aware of. Let's shift our focus and investigate `f_midi2_ep_opts_release` instead. It seems that might be where we'll find the removal/cleanup logic that's missing from the previous function. I'll need to see how the endpoint options are released and whether the default groups are handled there.
|
| 375/2 |
2026/07/30 15:10 |
tool |
grepper |
0m
Args: map[Expression:f_midi2_ep_opts_release PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:drivers/usb/gadget/function/f_midi2.c=2460=static struct configfs_attribute *f_midi2_ep_attrs[] = {
--
drivers/usb/gadget/function/f_midi2.c-2471-
drivers/usb/gadget/function/f_midi2.c:2472:static void f_midi2_ep_opts_release(struct config_item *item)
drivers/usb/gadget/function/f_midi2.c-2473-{
--
drivers/usb/gadget/function/f_midi2.c=2481=static const struct configfs_item_operations f_midi2_ep_item_ops = {
drivers/usb/gadget/function/f_midi2.c:2482: .release = f_midi2_ep_opts_release,
drivers/usb/gadget/function/f_midi2.c-2483-};
]
|
| 376/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 22272
output: 44
thoughts: 16
Thoughts: Okay, let's dive into this. Here's my thought process as I approach reading this:
**Analyzing `f_midi2_ep_opts_release`**
Alright, I'm starting with `f_midi2_ep_opts_release`. Immediately, I'm thinking: this function name is pretty descriptive, which is a good sign. The `f_` prefix likely indicates it's a function. `midi2` suggests this deals with MIDI 2.0, which is important to know contextually – it's going to be a different paradigm than older MIDI implementations. The `ep` part… that's got me guessing. Could be endpoint, or maybe even event processing. Then, the `opts_release` part... Options are being released? That could imply freeing memory associated with some option settings, or possibly releasing a lock or resource related to those options.
So, overall, my initial mental model is that this function likely pertains to releasing resources or configurations tied to MIDI 2.0 endpoint options. The devil, as always, will be in the details. I'm expecting to see some memory deallocation, perhaps some synchronization primitives being manipulated, or possibly even the handling of specific MIDI 2.0-related data structures. I'll be looking for potential error conditions and how they're handled – a well-written release function would be defensive and robust. Let's dig in and see if my hypotheses hold true.
|
| 377/2 |
2026/07/30 15:10 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c FirstLine:2472 LineCount:15]
Results: map[Contents:2472: static void f_midi2_ep_opts_release(struct config_item *item)
2473: {
2474: struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
2475:
2476: kfree(opts->info.ep_name);
2477: kfree(opts->info.product_id);
2478: kfree(opts);
2479: }
2480:
2481: static const struct configfs_item_operations f_midi2_ep_item_ops = {
2482: .release = f_midi2_ep_opts_release,
2483: };
2484:
2485: static const struct configfs_group_operations f_midi2_ep_group_ops = {
2486: .make_group = f_midi2_opts_block_make,
]
|
| 378/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 22906
output: 44
thoughts: 221
Thoughts: **Debugging the MIDI2 Gadget Driver Release**
Okay, so I've pinpointed that `f_midi2_ep_opts_release` is also overlooking the removal of `configfs_remove_default_groups`. That's a classic oversight. Now, I recall the introduction of these functions. Looking back at commit `8b645922b22303cec4628dbbbf6c8553d1cdec87`, that's the one. It brought in both the functions *and* this bug, seems like.
Wait, before I go any further, I need to verify something. Was `configfs_remove_default_groups` originally part of this commit, or was it added later and subsequently removed? Let me double-check the history. Right, the commit `8b645922b22303cec4628dbbbf6c8553d1cdec87` is indeed where the `drivers/usb/gadget/function/f_midi2.c` file was added.
Now I need to re-evaluate what's happening. I should double check if the `f_midi2_alloc_inst` function in `8b645922b22303cec4628dbbbf6c8553d1cdec87` actually *adds* the default groups. Once I have established this, I will be able to form a robust plan for remediation.
|
| 379/2 |
2026/07/30 15:10 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c FirstLine:2647 LineCount:40]
Results: map[Contents:2647: static struct usb_function_instance *f_midi2_alloc_inst(void)
2648: {
2649: struct f_midi2_opts *opts;
2650: struct f_midi2_ep_opts *ep_opts;
2651: struct f_midi2_block_opts *block_opts;
2652: int ret;
2653:
2654: opts = kzalloc_obj(*opts);
2655: if (!opts)
2656: return ERR_PTR(-ENOMEM);
2657:
2658: mutex_init(&opts->lock);
2659: opts->func_inst.free_func_inst = f_midi2_free_inst;
2660: opts->info.process_ump = true;
2661: opts->info.static_block = true;
2662: opts->info.num_reqs = 32;
2663: opts->info.req_buf_size = 512;
2664:
2665: /* create the default ep */
2666: ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
2667: if (ret) {
2668: kfree(opts);
2669: return ERR_PTR(ret);
2670: }
2671:
2672: /* create the default block */
2673: ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
2674: if (ret) {
2675: kfree(ep_opts);
2676: kfree(opts);
2677: return ERR_PTR(ret);
2678: }
2679:
2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
]
|
| 380/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 24141
output: 44
thoughts: 18
Thoughts: Okay, here's my attempt at summarizing the thought process, as if I were having these thoughts myself, and tailored for an expert audience.
**Diving Back into `f_midi2_alloc_inst`**
Alright, let's take another look at `f_midi2_alloc_inst`. I'm going to re-familiarize myself with its specifics. This is likely related to instrument allocation within the MIDI 2.0 framework, which always presents interesting challenges in terms of resource management and potential concurrency issues. I need to refresh my understanding of how the code handles memory, particularly when creating or releasing instrument instances. I'm focusing on that function specifically, because it's a critical point for understanding how new instances are being managed, which is fundamental to how this framework scales. I'm going to pay extra close attention to how it handles potential allocation failures, and how it deals with the initialization of the underlying instrument structures. I'll be looking for possible bottlenecks or any areas where optimizations might be applicable. Understanding this allocation process is a key element, and will help me be more proficient.
|
| 381/2 |
2026/07/30 15:10 |
tool |
read-file |
0m
Args: map[File:drivers/usb/gadget/function/f_midi2.c FirstLine:2680 LineCount:20]
Results: map[Contents:2680: /* set up the default MIDI1 (that is mandatory) */
2681: block_opts->info.midi1_num_groups = 1;
2682:
2683: config_group_init_type_name(&opts->func_inst.group, "",
2684: &f_midi2_func_type);
2685:
2686: config_group_init_type_name(&ep_opts->group, "ep.0",
2687: &f_midi2_ep_type);
2688: configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
2689:
2690: config_group_init_type_name(&block_opts->group, "block.0",
2691: &f_midi2_block_type);
2692: configfs_add_default_group(&block_opts->group, &ep_opts->group);
2693:
2694: return &opts->func_inst;
2695: }
2696:
2697: static void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts)
2698: {
2699: mutex_lock(&opts->lock);
]
|
| 382/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 24804
output: 31
thoughts: 141
Thoughts: **Analyzing a Recent Regression**
Okay, so I've traced down a potential regression. It looks like `configfs_add_default_group` *is* indeed called within `f_midi2_alloc_inst` for `ep.0` and `block.0`, which is as expected. What's concerning, however, is that `f_midi2_free_inst` and `f_midi2_ep_opts_release` *don't* seem to have the corresponding `configfs_remove_default_groups` calls. This is a classic leak scenario.
Given this discrepancy, my gut tells me the bug was likely introduced with that specific commit: `8b645922b22303cec4628dbbbf6c8553d1cdec87`. Time to investigate further and confirm this.
But before I jump to conclusions, I need to be thorough. I'll quickly check if any other commits in this file might have inadvertently introduced similar issues around resource management. A broader look might reveal related, overlooked problems. Let's dig in a bit more.
|
| 383/2 |
2026/07/30 15:10 |
tool |
git-log |
0m
Args: map[PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:224fb8661f66 usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
189f164e573e Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
32a92f8c8932 Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
e715bc42e337 usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
116e79c679a1 usb: gadget: midi2: Fix MIDI2 IN EP max packet size
21d8525d2e06 usb: gadget: midi2: Fix missing UMP group attributes initialization
dd33993a9721 ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
82937056967d usb: gadget: midi2: Fix interpretation of is_midi1 bits
]
|
| 384/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 25522
output: 36
thoughts: 157
Thoughts: **Investigating the Missing Commit**
Okay, so I'm looking into this code and I've identified `8b645922b22303cec4628dbbbf6c8553d1cdec87` as the commit that introduced `drivers/usb/gadget/function/f_midi2.c`. But hold on, the output I just got from `git-log` doesn't seem to include that specific SHA. That's odd, and it warrants a closer look.
Let me quickly verify that `8b645922b22303cec4628dbbbf6c8553d1cdec87` is indeed present in the history. It's possible I've misinterpreted something, or that there's a filtering issue somewhere. I'll need to confirm its existence before I can move forward with the analysis. Let's see if it's there.
|
| 385/2 |
2026/07/30 15:10 |
tool |
git-log |
0m
Args: map[Count:20 PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:224fb8661f66 usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
189f164e573e Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
32a92f8c8932 Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
e715bc42e337 usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
116e79c679a1 usb: gadget: midi2: Fix MIDI2 IN EP max packet size
21d8525d2e06 usb: gadget: midi2: Fix missing UMP group attributes initialization
dd33993a9721 ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
82937056967d usb: gadget: midi2: Fix interpretation of is_midi1 bits
7f72d17359e5 usb: gadget: function: remove redundant else statement
d9c61bb33fbb usb: gadget: function: move u_f.h to include/linux/usb/func_utils.h
228a953e61d6 usb: gadget: midi2: Fix the response for FB info with block 0xff
3eb27d3e32c7 usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup
1cb9ba5e61e8 usb: gadget: add missing MODULE_DESCRIPTION() macros
46decc82ffd5 usb: gadget: unconditionally allocate hs/ss descriptor in bind operation
6bb75eb9ff3a usb: gadget: midi2: fix missing unlock in f_midi2_block_opts_create()
a85ff0db48c3 usb: gadget: midi2: More flexible MIDI 1.0 configuration
1b437d2fb3c1 usb: gadget: midi2: Add "Operation Mode" control
d6468be779af usb: gadget: midi2: MIDI 1.0 interface (altset 0) support
]
|
| 386/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 26504
output: 36
thoughts: 7
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Prioritizing Velocity in Development**
Alright, that's a quick thought, and it's telling me to refocus on **increasing our commit frequency**. This isn't just about raw number of commits, of course. For someone in my position, "more commits" translates into a more nuanced set of considerations. It suggests we might be able to improve our:
* **Iteration speed:** Are we working in too-large batches? Do we need to break down tasks into smaller, more manageable units to demonstrate consistent progress and facilitate faster feedback cycles?
* **Code Review Efficiency:** A higher commit rate can also facilitate more focused code reviews. Small, self-contained changes are easier to understand and approve. Large pull requests, in comparison, can stagnate for longer periods of time.
* **Risk Mitigation:** Frequent commits serve as smaller, safer checkpoints. If there's a problem, we can identify when that problem was introduced more readily, allowing for easier rollbacks or debugging.
* **Collaboration and Visibility:** More commits can translate into greater transparency about project progress and active engagement with the codebase. They also contribute to more granular history, which can be useful for debugging and tracing changes.
* **Team Dynamics:** A focus on a higher commit frequency could also be meant to improve the team's sense of momentum. If we aren't seeing frequent code merges, it is easy to assume that progress isn't happening. I should consider how my team is seeing our progress.
Ultimately, I need to interpret this directive based on the current context. It's not about arbitrary metrics. It's about optimizing our workflow to accelerate development without sacrificing quality or introducing unnecessary overhead. So, my action plan is to identify bottlenecks, improve iteration size, and consider if my team is on pace.
|
| 387/2 |
2026/07/30 15:10 |
tool |
git-log |
0m
Args: map[Count:50 PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:224fb8661f66 usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
189f164e573e Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
32a92f8c8932 Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
e715bc42e337 usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
116e79c679a1 usb: gadget: midi2: Fix MIDI2 IN EP max packet size
21d8525d2e06 usb: gadget: midi2: Fix missing UMP group attributes initialization
dd33993a9721 ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
82937056967d usb: gadget: midi2: Fix interpretation of is_midi1 bits
7f72d17359e5 usb: gadget: function: remove redundant else statement
d9c61bb33fbb usb: gadget: function: move u_f.h to include/linux/usb/func_utils.h
228a953e61d6 usb: gadget: midi2: Fix the response for FB info with block 0xff
3eb27d3e32c7 usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup
1cb9ba5e61e8 usb: gadget: add missing MODULE_DESCRIPTION() macros
46decc82ffd5 usb: gadget: unconditionally allocate hs/ss descriptor in bind operation
6bb75eb9ff3a usb: gadget: midi2: fix missing unlock in f_midi2_block_opts_create()
a85ff0db48c3 usb: gadget: midi2: More flexible MIDI 1.0 configuration
1b437d2fb3c1 usb: gadget: midi2: Add "Operation Mode" control
d6468be779af usb: gadget: midi2: MIDI 1.0 interface (altset 0) support
856fa444b098 usb: gadget: midi2: Dynamically create MIDI 1.0 altset descriptors
29ee7a4dddd5 usb: gadget: midi2: Add configfs support
8b645922b223 usb: gadget: Add support for USB MIDI 2.0 function driver
]
|
| 388/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 27601
output: 24
thoughts: 49
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating the MIDI Gadget ConfigFS Implementation**
Right, so that commit ID, `29ee7a4dddd5`, immediately rings a bell. *That* was the one that brought in ConfigFS support for the USB MIDI gadget. I need to refresh my memory on the details of that implementation. Let's pull up that specific commit and dissect it. I want to see how they structured the ConfigFS interface, what parameters are exposed, and, most importantly, if the design aligns with best practices and considerations for robustness and flexibility. Were the right error checks included? Does it play nice with other gadget configurations? Time to dive in.
|
| 389/2 |
2026/07/30 15:10 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 29ee7a4dddd5caa18d1cef000f20c6af43f762f1
Author: Takashi Iwai <tiwai@suse.de>
Date: Tue Jul 25 08:22:01 2023 +0200
usb: gadget: midi2: Add configfs support
This patch adds the support of configfs to MIDI 2.0 function driver
for users to allow configuring the UMP Endpoint and Function Blocks
more flexibly. The configuration is in a tree form. The top-most
contains some card-level configurations. UMP Endpoints are defined in
subdirectories (ep.0, ep.1, etc) that contain Endpoint-specific
configurations such as ep_name, etc. And, UMP Function Blocks are
defined in the subdirectories (block.0, block.1, etc) under EP
subdirectory. As default, the driver creates a single UMP Endpoint
(ep.0) and a single Function Block (block.0) to work in a minimalistic
manner. User can modify those attributes freely to fit with the
demands.
When multiple Function Blocks are required, user can create another
directory as block.1, block.2, and so on (up to block.31). A block.*
directory can be deleted dynamically, too. A caveat is that the block
number has to be continuous.
Similarly, when multiple UMP Endpoints are required, user can create
another directory as ep.1, ep.2, up to ep.3.
Also, some driver behavior can be controlled in the card top-level
configs. e.g. you can pass process_ump=0 to disable the processing of
UMP Stream messages. This would be equivalent with the older MIDI 2.0
spec that doesn't support UMP v1.1 features.
The configfs interface checks upper- / lower-bound of input values,
and more sanity checks are performed at binding.
Attributes can't be changed any longer once when the instance is
linked to UDC.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://lore.kernel.org/r/20230725062206.9674-3-tiwai@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi2 b/Documentation/ABI/testing/configfs-usb-gadget-midi2
new file mode 100644
index 000000000000..a3a036d784c7
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-midi2
@@ -0,0 +1,52 @@
+What: /config/usb-gadget/gadget/functions/midi2.name
+Date: Jul 2023
+KernelVersion: 6.6
+Description:
+ The attributes:
+
+ ============ ===============================================
+ process_ump Flag to process UMP Stream messages (0 or 1)
+ static_block Flag for static blocks (0 or 1)
+ iface_name MIDI interface name string
+ ============ ===============================================
+
+What: /config/usb-gadget/gadget/functions/midi2.name/ep.number
+Date: Jul 2023
+KernelVersion: 6.6
+Description:
+ This group contains a UMP Endpoint configuration.
+ A new Endpoint starts from 0, and can be up to 3.
+
+ The attributes:
+
+ ============= ===============================================
+ protocol_caps MIDI protocol capabilities (1, 2 or 3 for both)
+ protocol Default MIDI protocol (1 or 2)
+ ep_name UMP Endpoint name string
+ product_id Product ID string
+ manufacturer Manufacture ID (24 bit)
+ family Device family ID (16 bit)
+ model Device model ID (16 bit)
+ sw_revision Software Revision (32 bit)
+ ============= ===============================================
+
+What: /config/usb-gadget/gadget/functions/midi2.name/ep.number/block.number
+Date: Jul 2023
+KernelVersion: 6.6
+Description:
+ This group contains a UMP Function Block configuration.
+ A new block starts from 0, and can be up to 31.
+
+ The attributes:
+
+ =============== ===============================================
+ name Function Block name string
+ direction 1: input, 2: output, 3: bidirectional
+ first_group The first UMP Group number (0-15)
+ num_groups The number of groups in this FB (1-16)
+ ui_hint 0: unknown, 1: receiver, 2: sender, 3: both
+ midi_ci_verison Supported MIDI-CI version number (8 bit)
+ is_midi1 Legacy MIDI 1.0 device (0, 1 or 2)
+ sysex8_streams Max number of SysEx8 streams (8 bit)
+ active Active FB flag (0 or 1)
+ =============== ===============================================
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 848cb3150deb..c68a6fa0d237 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -292,7 +292,7 @@ static struct usb_interface_descriptor midi2_midi2_if_desc = {
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0, // to be filled
.bAlternateSetting = 1,
- .bNumEndpoints = 2,
+ .bNumEndpoints = 2, // to be filled
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
.bInterfaceProtocol = 0,
@@ -1403,6 +1403,8 @@ static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f)
midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id;
}
+ midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2;
+
/* audio interface */
status = usb_interface_id(c, f);
if (status < 0)
@@ -1510,16 +1512,274 @@ static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
}
+/*
+ * ConfigFS interface
+ */
+
+/* type conversion helpers */
+static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_opts,
+ func_inst.group);
+}
+
+static inline struct f_midi2_ep_opts *
+to_f_midi2_ep_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_ep_opts,
+ group);
+}
+
+static inline struct f_midi2_block_opts *
+to_f_midi2_block_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi2_block_opts,
+ group);
+}
+
+/* trim the string to be usable for EP and FB name strings */
+static void make_name_string(char *s)
+{
+ char *p;
+
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+
+ p = s + strlen(s);
+ for (; p > s && isspace(*p); p--)
+ *p = 0;
+}
+
+/* configfs helpers: generic show/store for unisnged int */
+static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts,
+ u32 val, const char *format, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, format, val);
+ mutex_unlock(&opts->lock);
+ return result;
+}
+
+static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts,
+ u32 *valp, u32 minval, u32 maxval,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 val;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &val);
+ if (ret)
+ goto end;
+ if (val < minval || val > maxval) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ *valp = val;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/* generic store for bool */
+static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
+ bool *valp, const char *page, size_t len)
+{
+ int ret;
+ bool val;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtobool(page, &val);
+ if (ret)
+ goto end;
+ *valp = val;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/* generic show/store for string */
+static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
+ const char *str, char *page)
+{
+ int result = 0;
+
+ mutex_lock(&opts->lock);
+ if (str)
+ result = scnprintf(page, PAGE_SIZE, "%s\n", str);
+ mutex_unlock(&opts->lock);
+ return result;
+}
+
+static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts,
+ const char **strp, size_t maxlen,
+ const char *page, size_t len)
+{
+ char *c;
+ int ret;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ c = kstrndup(page, min(len, maxlen), GFP_KERNEL);
+ if (!c) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ kfree(*strp);
+ make_name_string(c);
+ *strp = c;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+/*
+ * Definitions for UMP Block config
+ */
+
+/* define an uint option for block */
+#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval) \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+ char *page) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
+ format "\n", page); \
+} \
+ \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\
+ minval, maxval, page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+/* define a boolean option for block */
+#define F_MIDI2_BLOCK_BOOL_OPT(name) \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+ char *page) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name, \
+ "%u\n", page); \
+} \
+ \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item); \
+ return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3);
+F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16);
+F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3);
+F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1);
+F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255);
+F_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2);
+F_MIDI2_BLOCK_BOOL_OPT(active);
+
+static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
+ char *page)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page);
+}
+
+static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128,
+ page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_block_opts_, name);
+
+static struct configfs_attribute *f_midi2_block_attrs[] = {
+ &f_midi2_block_opts_attr_direction,
+ &f_midi2_block_opts_attr_first_group,
+ &f_midi2_block_opts_attr_num_groups,
+ &f_midi2_block_opts_attr_ui_hint,
+ &f_midi2_block_opts_attr_midi_ci_version,
+ &f_midi2_block_opts_attr_sysex8_streams,
+ &f_midi2_block_opts_attr_is_midi1,
+ &f_midi2_block_opts_attr_active,
+ &f_midi2_block_opts_attr_name,
+ NULL,
+};
+
+static void f_midi2_block_opts_release(struct config_item *item)
+{
+ struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+ kfree(opts->info.name);
+ kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_block_item_ops = {
+ .release = f_midi2_block_opts_release,
+};
+
+static const struct config_item_type f_midi2_block_type = {
+ .ct_item_ops = &f_midi2_block_item_ops,
+ .ct_attrs = f_midi2_block_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
/* create a f_midi2_block_opts instance for the given block number */
static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
unsigned int blk,
struct f_midi2_block_opts **block_p)
{
struct f_midi2_block_opts *block_opts;
+ int ret = 0;
+
+ mutex_lock(&ep_opts->opts->lock);
+ if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
+ ret = -EBUSY;
+ goto out;
+ }
block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL);
- if (!block_opts)
- return -ENOMEM;
+ if (!block_opts) {
+ ret = -ENOMEM;
+ goto out;
+ }
block_opts->ep = ep_opts;
block_opts->id = blk;
@@ -1533,9 +1793,143 @@ static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
ep_opts->blks[blk] = block_opts;
*block_p = block_opts;
- return 0;
+
+ mutex_unlock(&ep_opts->opts->lock);
+ out:
+ return ret;
}
+/* make_group callback for a block */
+static struct config_group *
+f_midi2_opts_block_make(struct config_group *group, const char *name)
+{
+ struct f_midi2_ep_opts *ep_opts;
+ struct f_midi2_block_opts *block_opts;
+ unsigned int blk;
+ int ret;
+
+ if (strncmp(name, "block.", 6))
+ return ERR_PTR(-EINVAL);
+ ret = kstrtouint(name + 6, 10, &blk);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ep_opts = to_f_midi2_ep_opts(&group->cg_item);
+
+ if (blk >= SNDRV_UMP_MAX_BLOCKS)
+ return ERR_PTR(-EINVAL);
+ if (ep_opts->blks[blk])
+ return ERR_PTR(-EBUSY);
+ ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
+ if (ret)
+ return ERR_PTR(ret);
+
+ config_group_init_type_name(&block_opts->group, name,
+ &f_midi2_block_type);
+ return &block_opts->group;
+}
+
+/* drop_item callback for a block */
+static void
+f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
+{
+ struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item);
+
+ mutex_lock(&block_opts->ep->opts->lock);
+ block_opts->ep->blks[block_opts->id] = NULL;
+ mutex_unlock(&block_opts->ep->opts->lock);
+ config_item_put(item);
+}
+
+/*
+ * Definitions for UMP Endpoint config
+ */
+
+/* define an uint option for EP */
+#define F_MIDI2_EP_OPT(name, format, minval, maxval) \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_uint_show(opts->opts, opts->info.name, \
+ format "\n", page); \
+} \
+ \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len)\
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_uint_store(opts->opts, &opts->info.name, \
+ minval, maxval, page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+/* define a string option for EP */
+#define F_MIDI2_EP_STR_OPT(name, maxlen) \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\
+} \
+ \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item); \
+ return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+F_MIDI2_EP_OPT(protocol, "0x%x", 1, 2);
+F_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3);
+F_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff);
+F_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff);
+F_MIDI2_EP_STR_OPT(ep_name, 128);
+F_MIDI2_EP_STR_OPT(product_id, 128);
+
+static struct configfs_attribute *f_midi2_ep_attrs[] = {
+ &f_midi2_ep_opts_attr_protocol,
+ &f_midi2_ep_opts_attr_protocol_caps,
+ &f_midi2_ep_opts_attr_ep_name,
+ &f_midi2_ep_opts_attr_product_id,
+ &f_midi2_ep_opts_attr_manufacturer,
+ &f_midi2_ep_opts_attr_family,
+ &f_midi2_ep_opts_attr_model,
+ &f_midi2_ep_opts_attr_sw_revision,
+ NULL,
+};
+
+static void f_midi2_ep_opts_release(struct config_item *item)
+{
+ struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+
+ kfree(opts->info.ep_name);
+ kfree(opts->info.product_id);
+ kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_ep_item_ops = {
+ .release = f_midi2_ep_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_ep_group_ops = {
+ .make_group = f_midi2_opts_block_make,
+ .drop_item = f_midi2_opts_block_drop,
+};
+
+static const struct config_item_type f_midi2_ep_type = {
+ .ct_item_ops = &f_midi2_ep_item_ops,
+ .ct_group_ops = &f_midi2_ep_group_ops,
+ .ct_attrs = f_midi2_ep_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
/* create a f_midi2_ep_opts instance */
static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
unsigned int index,
@@ -1559,7 +1953,119 @@ static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
return 0;
}
+/* make_group callback for an EP */
+static struct config_group *
+f_midi2_opts_ep_make(struct config_group *group, const char *name)
+{
+ struct f_midi2_opts *opts;
+ struct f_midi2_ep_opts *ep_opts;
+ unsigned int index;
+ int ret;
+
+ if (strncmp(name, "ep.", 3))
+ return ERR_PTR(-EINVAL);
+ ret = kstrtouint(name + 3, 10, &index);
+ if (ret)
+ return ERR_PTR(ret);
+
+ opts = to_f_midi2_opts(&group->cg_item);
+ if (index >= MAX_UMP_EPS)
+ return ERR_PTR(-EINVAL);
+ if (opts->eps[index])
+ return ERR_PTR(-EBUSY);
+ ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
+ if (ret)
+ return ERR_PTR(ret);
+
+ config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
+ return &ep_opts->group;
+}
+
+/* drop_item callback for an EP */
+static void
+f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
+{
+ struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
+
+ mutex_lock(&ep_opts->opts->lock);
+ ep_opts->opts->eps[ep_opts->index] = NULL;
+ mutex_unlock(&ep_opts->opts->lock);
+ config_item_put(item);
+}
+
+/*
+ * Definitions for card config
+ */
+
+/* define a bool option for card */
+#define F_MIDI2_BOOL_OPT(name) \
+static ssize_t f_midi2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_midi2_opts *opts = to_f_midi2_opts(item); \
+ return f_midi2_opts_uint_show(opts, opts->info.name, \
+ "%u\n", page); \
+} \
+ \
+static ssize_t f_midi2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi2_opts *opts = to_f_midi2_opts(item); \
+ return f_midi2_opts_bool_store(opts, &opts->info.name, \
+ page, len); \
+} \
+ \
+CONFIGFS_ATTR(f_midi2_opts_, name)
+
+F_MIDI2_BOOL_OPT(process_ump);
+F_MIDI2_BOOL_OPT(static_block);
+
+static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
+ char *page)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ return f_midi2_opts_str_show(opts, opts->info.iface_name, page);
+}
+
+static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128,
+ page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_opts_, iface_name);
+
+static struct configfs_attribute *f_midi2_attrs[] = {
+ &f_midi2_opts_attr_process_ump,
+ &f_midi2_opts_attr_static_block,
+ &f_midi2_opts_attr_iface_name,
+ NULL
+};
+
+static void f_midi2_opts_release(struct config_item *item)
+{
+ struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_midi2_item_ops = {
+ .release = f_midi2_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_group_ops = {
+ .make_group = f_midi2_opts_ep_make,
+ .drop_item = f_midi2_opts_ep_drop,
+};
+
static const struct config_item_type f_midi2_func_type = {
+ .ct_item_ops = &f_midi2_item_ops,
+ .ct_group_ops = &f_midi2_group_ops,
+ .ct_attrs = f_midi2_attrs,
.ct_owner = THIS_MODULE,
};
@@ -1569,11 +2075,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
- /* we have only one EP and one FB */
- if (opts->eps[0]) {
- kfree(opts->eps[0]->blks[0]);
- kfree(opts->eps[0]);
- }
+ kfree(opts->info.iface_name);
kfree(opts);
}
@@ -1596,6 +2098,7 @@ static struct usb_function_instance *f_midi2_alloc_inst(void)
opts->info.num_reqs = 32;
opts->info.req_buf_size = 512;
+ /* create the default ep */
ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
if (ret) {
kfree(opts);
@@ -1612,6 +2115,15 @@ static struct usb_function_instance *f_midi2_alloc_inst(void)
config_group_init_type_name(&opts->func_inst.group, "",
&f_midi2_func_type);
+
+ config_group_init_type_name(&ep_opts->group, "ep.0",
+ &f_midi2_ep_type);
+ configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
+
+ config_group_init_type_name(&block_opts->group, "block.0",
+ &f_midi2_block_type);
+ configfs_add_default_group(&block_opts->group, &ep_opts->group);
+
return &opts->func_inst;
}
@@ -1630,12 +2142,58 @@ static void f_midi2_free(struct usb_function *f)
container_of(f->fi, struct f_midi2_opts, func_inst));
}
+/* verify the parameters set up via configfs;
+ * return the number of EPs or a negative error
+ */
+static int verify_parameters(struct f_midi2_opts *opts)
+{
+ int i, j, num_eps, num_blks;
+ struct f_midi2_ep_info *ep;
+ struct f_midi2_block_info *bp;
+
+ for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps];
+ num_eps++)
+ ;
+ if (!num_eps) {
+ pr_err("f_midi2: No EP is defined\n");
+ return -EINVAL;
+ }
+
+ num_blks = 0;
+ for (i = 0; i < num_eps; i++) {
+ ep = &opts->eps[i]->info;
+ if (!(ep->protocol_caps & ep->protocol)) {
+ pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n",
+ ep->protocol, ep->protocol_caps, i);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j];
+ j++, num_blks++) {
+ bp = &opts->eps[i]->blks[j]->info;
+ if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) {
+ pr_err("f_midi2: Invalid group definitions for block %d:%d\n",
+ i, j);
+ return -EINVAL;
+ }
+ }
+ }
+ if (!num_blks) {
+ pr_err("f_midi2: No block is defined\n");
+ return -EINVAL;
+ }
+
+ return num_eps;
+}
+
/* gadget alloc callback */
static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
{
struct f_midi2 *midi2;
struct f_midi2_opts *opts;
- int i;
+ struct f_midi2_ep *ep;
+ struct f_midi2_block *bp;
+ int i, num_eps, blk;
midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL);
if (!midi2)
@@ -1643,6 +2201,12 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
opts = container_of(fi, struct f_midi2_opts, func_inst);
mutex_lock(&opts->lock);
+ num_eps = verify_parameters(opts);
+ if (num_eps < 0) {
+ mutex_unlock(&opts->lock);
+ kfree(midi2);
+ return ERR_PTR(num_eps);
+ }
++opts->refcnt;
mutex_unlock(&opts->lock);
@@ -1658,17 +2222,20 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
midi2->func.free_func = f_midi2_free;
midi2->info = opts->info;
+ midi2->num_eps = num_eps;
- /* fixed 1 UMP EP and 1 UMP FB as of now */
- midi2->num_eps = 1;
- midi2->midi2_eps[0].info = opts->eps[0]->info;
- midi2->midi2_eps[0].card = midi2;
- midi2->midi2_eps[0].num_blks = 1;
- midi2->midi2_eps[0].blks[0].info = opts->eps[0]->blks[0]->info;
- midi2->midi2_eps[0].blks[0].gtb_id = 1;
-
- for (i = 0; i < midi2->num_eps; i++)
- midi2->total_blocks += midi2->midi2_eps[i].num_blks;
+ for (i = 0; i < num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ ep->info = opts->eps[i]->info;
+ ep->card = midi2;
+ for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS &&
+ opts->eps[i]->blks[blk]; blk++) {
+ bp = &ep->blks[blk];
+ ep->num_blks++;
+ bp->info = opts->eps[i]->blks[blk]->info;
+ bp->gtb_id = ++midi2->total_blocks;
+ }
+ }
midi2->string_defs = kcalloc(midi2->total_blocks + 1,
sizeof(*midi2->string_defs), GFP_KERNEL);
@@ -1678,10 +2245,18 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
}
if (opts->info.iface_name && *opts->info.iface_name)
- midi2->string_defs[0].s = opts->info.iface_name;
+ midi2->string_defs[STR_IFACE].s = opts->info.iface_name;
else
- midi2->string_defs[0].s = ump_ep_name(&midi2->midi2_eps[0]);
- midi2->string_defs[1].s = ump_fb_name(&midi2->midi2_eps[0].blks[0].info);
+ midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]);
+
+ for (i = 0; i < midi2->num_eps; i++) {
+ ep = &midi2->midi2_eps[i];
+ for (blk = 0; blk < ep->num_blks; blk++) {
+ bp = &ep->blks[blk];
+ midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s =
+ ump_fb_name(&bp->info);
+ }
+ }
return &midi2->func;
}
]
|
| 390/2 |
2026/07/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 38168
output: 45
thoughts: 580
Thoughts: **Analyzing the MIDI2 Configfs Integration**
Okay, so I'm looking at commit `29ee7a4dddd5caa18d1cef000f20c6af43f762f1` – this is the one that introduced configfs support for the MIDI2 function. I see the key change is within `f_midi2_alloc_inst`. It now initializes and adds groups to the configfs hierarchy using `config_group_init_type_name` and `configfs_add_default_group`. Specifically, it creates "ep.0" and "block.0" groups within the configfs structure. This is pretty standard for integrating a function with configfs – define the groups and their associated types.
The interesting part is the modification to `f_midi2_free_inst`. Originally, it looks like this function was responsible for freeing the `eps` and `blks` that were allocated. The commit removes those lines. My initial concern here is that there's no corresponding `configfs_remove_default_groups` call added within `f_midi2_free_inst` in this commit. Now that the configfs is integrated, this could leak the default groups. I need to make sure this is being handled correctly elsewhere.
I also notice that the `f_midi2_ep_opts_release` function was added in this commit. I suspect that this is where the resources associated with the configfs groups are being cleaned up. Since I'm not seeing it in `f_midi2_free_inst`, I need to confirm this. I will verify if this function has been appropriately implemented. Let me check the subsequent commits to see if `8b645922b22303cec4628dbbbf6c8553d1cdec87` added this functionality.
|
| 391/2 |
2026/07/30 15:11 |
tool |
grepper |
0m
Args: map[Expression:f_midi2_ep_opts_release PathPrefix:drivers/usb/gadget/function/f_midi2.c]
Results: map[Output:drivers/usb/gadget/function/f_midi2.c=2460=static struct configfs_attribute *f_midi2_ep_attrs[] = {
--
drivers/usb/gadget/function/f_midi2.c-2471-
drivers/usb/gadget/function/f_midi2.c:2472:static void f_midi2_ep_opts_release(struct config_item *item)
drivers/usb/gadget/function/f_midi2.c-2473-{
--
drivers/usb/gadget/function/f_midi2.c=2481=static const struct configfs_item_operations f_midi2_ep_item_ops = {
drivers/usb/gadget/function/f_midi2.c:2482: .release = f_midi2_ep_opts_release,
drivers/usb/gadget/function/f_midi2.c-2483-};
]
|
| 392/2 |
2026/07/30 15:11 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 39443
output: 49
thoughts: 109
Thoughts: **Checking for Recent Code Changes**
Okay, so I just did a `grep` in the current working tree, but I need to make sure I'm looking at the right commits. The current search might be missing something. Specifically, I need to check if commit `8b645922b22303cec4628dbbbf6c8553d1cdec87` actually introduced or modified `f_midi2_ep_opts_release`. Let me pull up the diff for that commit again to get a clear picture. Gotta be precise here; I don't want to waste time chasing ghosts.
|
| 393/2 |
2026/07/30 15:11 |
tool |
git-show |
0m
Args: map[Commit:8b645922b22303cec4628dbbbf6c8553d1cdec87]
Results: map[Output:
Full output is too long, showing 1000 out of 1877 lines.
[commit 8b645922b22303cec4628dbbbf6c8553d1cdec87
Author: Takashi Iwai <tiwai@suse.de>
Date: Tue Jul 25 08:22:00 2023 +0200
usb: gadget: Add support for USB MIDI 2.0 function driver
This patch adds the support for USB MIDI 2.0 gadget function driver.
The driver emulates a USB MIDI 2.0 interface with one or more UMP
Endpoints, where each of UMP Endpoint is a pair of MIDI Endpoints for
handling MIDI 2.0 UMP packets. When the function driver is bound, the
driver creates an ALSA card object with UMP rawmidi devices. This is
a kind of loop-back where the incoming and upcoming UMP packets
from/to the MIDI 2.0 UMP Endpoints are transferred as-is. In
addition, legacy (MIDI 1.0) rawmidi devices are created, so that
legacy applications can work in the gadget side, too.
When a USB MIDI 2.0 gadget interface appears, the connected host can
use it with the snd-usb-audio driver where MIDI 2.0 support is
enabled. Both gadget and connected hosts will have the similar UMP
Endpoint and Function Block (or Group Terminal Block) information.
Slight differences are the direction and UI-hint bits; it's due to the
nature of gadget driver, and the input/output direction is swapped in
both sides (the input for gadget is the output for host, and vice
versa).
The driver supports the brand-new UMP v1.1 feature, including the UMP
Stream message handling for providing UMP Endpoint and Function Block
information as well as dealing with the MIDI protocol switch. The
driver responds to UMP Stream messages by itself. OTOH, MIDI-CI
message handling isn't implemented in the kernel driver; it should be
processed in the user-space through the loopback UMP device.
As of this patch, the whole configuration is fixed, providing only one
bidirectional UMP Endpoint containing a single FB/GTB with a single
UMP Group. The configuration will be dynamically changeable in the
following patches.
The traditional MIDI 1.0 is still provided in the altset 0 (which is
mandatory per spec). But it's only about the configuration, and no
actual I/O will be running for the altset 0 as of this patch. The
proper support MIDI 1.0 altset will follow in later patches, too.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://lore.kernel.org/r/20230725062206.9674-2-tiwai@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 336db8f92afa..b3592bcb0f96 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -208,6 +208,9 @@ config USB_F_UVC
config USB_F_MIDI
tristate
+config USB_F_MIDI2
+ tristate
+
config USB_F_HID
tristate
@@ -436,6 +439,21 @@ config USB_CONFIGFS_F_MIDI
connections can then be made on the gadget system, using
ALSA's aconnect utility etc.
+config USB_CONFIGFS_F_MIDI2
+ bool "MIDI 2.0 function"
+ depends on USB_CONFIGFS
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_UMP
+ select SND_UMP_LEGACY_RAWMIDI
+ select USB_F_MIDI2
+ help
+ The MIDI 2.0 function driver provides the generic emulated
+ USB MIDI 2.0 interface, looped back to ALSA UMP rawmidi
+ device on the gadget host. It supports UMP 1.1 spec and
+ responds UMP Stream messages for UMP Endpoint and Function
+ Block information / configuration.
+
config USB_CONFIGFS_F_HID
bool "HID function"
depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 5d3a6cf02218..87917a7d4a9b 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -44,6 +44,8 @@ usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
+usb_f_midi2-y := f_midi2.o
+obj-$(CONFIG_USB_F_MIDI2) += usb_f_midi2.o
usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
usb_f_printer-y := f_printer.o
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
new file mode 100644
index 000000000000..848cb3150deb
--- /dev/null
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -0,0 +1,1691 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_midi2.c -- USB MIDI 2.0 class function driver
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/ump.h>
+#include <sound/ump_msg.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi-v2.h>
+
+#include "u_f.h"
+#include "u_midi2.h"
+
+struct f_midi2;
+struct f_midi2_ep;
+struct f_midi2_usb_ep;
+
+/* Context for each USB request */
+struct f_midi2_req_ctx {
+ struct f_midi2_usb_ep *usb_ep; /* belonging USB EP */
+ unsigned int index; /* array index: 0-31 */
+ struct usb_request *req; /* assigned request */
+};
+
+/* Resources for a USB Endpoint */
+struct f_midi2_usb_ep {
+ struct f_midi2 *card; /* belonging card */
+ struct f_midi2_ep *ep; /* belonging UMP EP (optional) */
+ struct usb_ep *usb_ep; /* assigned USB EP */
+ void (*complete)(struct usb_ep *usb_ep, struct usb_request *req);
+ unsigned long free_reqs; /* bitmap for unused requests */
+ unsigned int num_reqs; /* number of allocated requests */
+ struct f_midi2_req_ctx *reqs; /* request context array */
+};
+
+/* Resources for UMP Function Block (and USB Group Terminal Block) */
+struct f_midi2_block {
+ struct f_midi2_block_info info; /* FB info, copied from configfs */
+ struct snd_ump_block *fb; /* assigned FB */
+ unsigned int gtb_id; /* assigned GTB id */
+ unsigned int string_id; /* assigned string id */
+};
+
+/* Resources for UMP Endpoint */
+struct f_midi2_ep {
+ struct snd_ump_endpoint *ump; /* assigned UMP EP */
+ struct f_midi2 *card; /* belonging MIDI 2.0 device */
+
+ struct f_midi2_ep_info info; /* UMP EP info, copied from configfs */
+ unsigned int num_blks; /* number of FBs */
+ struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS]; /* UMP FBs */
+
+ struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */
+ struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */
+};
+
+/* indices for USB strings */
+enum {
+ STR_IFACE = 0,
+ STR_GTB1 = 1,
+};
+
+/* 1-based GTB id to string id */
+#define gtb_to_str_id(id) (STR_GTB1 + (id) - 1)
+
+/* operation mode */
+enum {
+ MIDI_OP_MODE_UNSET, /* no altset set yet */
+ MIDI_OP_MODE_MIDI1, /* MIDI 1.0 (altset 0) is used */
+ MIDI_OP_MODE_MIDI2, /* MIDI 2.0 (altset 1) is used */
+};
+
+/* Resources for MIDI 2.0 Device */
+struct f_midi2 {
+ struct usb_function func;
+ struct usb_gadget *gadget;
+ struct snd_card *card;
+
+ /* MIDI 1.0 in/out USB EPs */
+ struct f_midi2_usb_ep midi1_ep_in;
+ struct f_midi2_usb_ep midi1_ep_out;
+
+ int midi_if; /* USB MIDI interface number */
+ int operation_mode; /* current operation mode */
+
+ spinlock_t queue_lock;
+
+ struct f_midi2_card_info info; /* card info, copied from configfs */
+
+ unsigned int num_eps;
+ struct f_midi2_ep midi2_eps[MAX_UMP_EPS];
+
+ unsigned int total_blocks; /* total number of blocks of all EPs */
+ struct usb_string *string_defs;
+ struct usb_string *strings;
+};
+
+#define func_to_midi2(f) container_of(f, struct f_midi2, func)
+
+/* get EP name string */
+static const char *ump_ep_name(const struct f_midi2_ep *ep)
+{
+ return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget";
+}
+
+/* get EP product ID string */
+static const char *ump_product_id(const struct f_midi2_ep *ep)
+{
+ return ep->info.product_id ? ep->info.product_id : "Unique Product ID";
+}
+
+/* get FB name string */
+static const char *ump_fb_name(const struct f_midi2_block_info *info)
+{
+ return info->name ? info->name : "MIDI 2.0 Gadget I/O";
+}
+
+/*
+ * USB Descriptor Definitions
+ */
+/* GTB header descriptor */
+static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = {
+ .bLength = sizeof(gtb_header_desc),
+ .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
+ .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK_HEADER,
+ .wTotalLength = __cpu_to_le16(0x12), // to be filled
+};
+
+/* GTB descriptor template: most items are replaced dynamically */
+static struct usb_ms20_gr_trm_block_descriptor gtb_desc = {
+ .bLength = sizeof(gtb_desc),
+ .bDescriptorType = USB_DT_CS_GR_TRM_BLOCK,
+ .bDescriptorSubtype = USB_MS_GR_TRM_BLOCK,
+ .bGrpTrmBlkID = 0x01,
+ .bGrpTrmBlkType = USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL,
+ .nGroupTrm = 0x00,
+ .nNumGroupTrm = 1,
+ .iBlockItem = 0,
+ .bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_64,
+ .wMaxInputBandwidth = 0,
+ .wMaxOutputBandwidth = 0,
+};
+
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32);
+
+#define EP_MAX_PACKET_INT 8
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor midi2_audio_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0,
+};
+
+static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = 0x01,
+ .bcdADC = __cpu_to_le16(0x0100),
+ .wTotalLength = __cpu_to_le16(0x0009),
+ .bInCollection = 0x01,
+ .baInterfaceNr = { 0x01 }, // to be filled
+};
+
+/* MIDI 1.0 Streaming Interface (altset 0) */
+static struct usb_interface_descriptor midi2_midi1_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi1_class_desc = {
+ .bLength = 0x07,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __cpu_to_le16(0x0100),
+ .wTotalLength = __cpu_to_le16(0x41), // to be calculated
+};
+
+/* MIDI 1.0 IN (Embedded) Jack */
+static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack1_desc = {
+ .bLength = 0x06,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = 0x01,
+ .iJack = 0,
+};
+
+/* MIDI 1.0 IN (External) Jack */
+static struct usb_midi_in_jack_descriptor midi2_midi1_in_jack2_desc = {
+ .bLength = 0x06,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = 0x02,
+ .iJack = 0,
+};
+
+/* MIDI 1.0 OUT (Embedded) Jack */
+static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack1_desc = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = 0x03,
+ .bNrInputPins = 1,
+ .pins = { { 0x02, 0x01 } },
+ .iJack = 0,
+};
+
+/* MIDI 1.0 OUT (External) Jack */
+static struct usb_midi_out_jack_descriptor_1 midi2_midi1_out_jack2_desc = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = 0x04,
+ .bNrInputPins = 1,
+ .pins = { { 0x01, 0x01 } },
+ .iJack = 0,
+};
+
+/* MIDI 1.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT | 0, // set up dynamically
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_out_class_desc = {
+ .bLength = 0x05,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = { 0x01 },
+};
+
+/* MIDI 1.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN | 0, // set up dynamically
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_in_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_1 midi2_midi1_ep_in_class_desc = {
+ .bLength = 0x05,
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = { 0x03 },
+};
+
+/* MIDI 2.0 Streaming Interface (altset 1) */
+static struct usb_interface_descriptor midi2_midi2_if_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0, // to be filled
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi2_class_desc = {
+ .bLength = 0x07,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __cpu_to_le16(0x0200),
+ .wTotalLength = __cpu_to_le16(0x07),
+};
+
+/* MIDI 2.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS];
+
+/* MIDI 2.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = {
+ .bLength = sizeof(midi2_midi2_ep_in_ss_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS];
+
+/* Arrays of descriptors to be created */
+static void *midi2_audio_descs[] = {
+ &midi2_audio_if_desc,
+ &midi2_audio_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_descs[] = {
+ &midi2_midi1_if_desc,
+ &midi2_midi1_class_desc,
+ &midi2_midi1_in_jack1_desc,
+ &midi2_midi1_in_jack2_desc,
+ &midi2_midi1_out_jack1_desc,
+ &midi2_midi1_out_jack2_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_descs[] = {
+ &midi2_midi1_ep_out_desc,
+ &midi2_midi1_ep_out_class_desc,
+ &midi2_midi1_ep_in_desc,
+ &midi2_midi1_ep_in_class_desc,
+ NULL
+};
+
+static void *midi2_midi1_ep_ss_descs[] = {
+ &midi2_midi1_ep_out_desc,
+ &midi2_midi1_ep_out_ss_comp_desc,
+ &midi2_midi1_ep_out_class_desc,
+ &midi2_midi1_ep_in_desc,
+ &midi2_midi1_ep_in_ss_comp_desc,
+ &midi2_midi1_ep_in_class_desc,
+ NULL
+};
+
+static void *midi2_midi2_descs[] = {
+ &midi2_midi2_if_desc,
+ &midi2_midi2_class_desc,
+ NULL
+};
+
+/*
+ * USB request handling
+ */
+
+/* get an empty request for the given EP */
+static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep)
+{
+ struct usb_request *req = NULL;
+ unsigned long flags;
+ int index;
+
+ spin_lock_irqsave(&usb_ep->card->queue_lock, flags);
+ if (!usb_ep->free_reqs)
+ goto unlock;
+ index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs);
+ if (index >= usb_ep->num_reqs)
+ goto unlock;
+ req = usb_ep->reqs[index].req;
+ if (!req)
+ goto unlock;
+ clear_bit(index, &usb_ep->free_reqs);
+ req->length = 0;
+ unlock:
+ spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags);
+ return req;
+}
+
+/* put the empty request back */
+static void put_empty_request(struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags);
+ set_bit(ctx->index, &ctx->usb_ep->free_reqs);
+ spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags);
+}
+
+/*
+ * UMP v1.1 Stream message handling
+ */
+
+/* queue a request to UMP EP; request is either queued or freed after this */
+static int queue_request_ep_raw(struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ int err;
+
+ req->complete = ctx->usb_ep->complete;
+ err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC);
+ if (err) {
+ put_empty_request(req);
+ return err;
+ }
+ return 0;
+}
+
+/* queue a request with endianness conversion */
+static int queue_request_ep_in(struct usb_request *req)
+{
+ /* UMP packets have to be converted to little-endian */
+ cpu_to_le32_array((u32 *)req->buf, req->length >> 2);
+ return queue_request_ep_raw(req);
+}
+
+/* reply a UMP packet via EP-in */
+static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct usb_request *req;
+
+ req = get_empty_request(usb_ep);
+ if (!req)
+ return -ENOSPC;
+
+ req->length = len;
+ memcpy(req->buf, buf, len);
+ return queue_request_ep_in(req);
+}
+
+/* reply a UMP stream EP info */
+static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_ep_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_EP_INFO,
+ .ump_version_major = 0x01,
+ .ump_version_minor = 0x01,
+ .num_function_blocks = ep->num_blks,
+ .static_function_block = !!ep->card->info.static_block,
+ .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 |
+ UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* reply a UMP EP device info */
+static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_devince_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
+ .manufacture_id = ep->info.manufacturer,
+ .family_lsb = ep->info.family & 0xff,
+ .family_msb = (ep->info.family >> 8) & 0xff,
+ .model_lsb = ep->info.model & 0xff,
+ .model_msb = (ep->info.model >> 8) & 0xff,
+ .sw_revision = ep->info.sw_revision,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+#define UMP_STREAM_PKT_BYTES 16 /* UMP stream packet size = 16 bytes*/
+#define UMP_STREAM_EP_STR_OFF 2 /* offset of name string for EP info */
+#define UMP_STREAM_FB_STR_OFF 3 /* offset of name string for FB info */
+
+/* Helper to replay a string */
+static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name,
+ unsigned int type, unsigned int extra,
+ unsigned int start_ofs)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct f_midi2 *midi2 = ep->card;
+ struct usb_request *req;
+ unsigned int pos;
+ u32 *buf;
+
+ if (!*name)
+ return;
+ req = get_empty_request(usb_ep);
+ if (!req)
+ return;
+
+ buf = (u32 *)req->buf;
+ pos = start_ofs;
+ for (;;) {
+ if (pos == start_ofs) {
+ memset(buf, 0, UMP_STREAM_PKT_BYTES);
+ buf[0] = ump_stream_compose(type, 0) | extra;
+ }
+ buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8);
+ if (!*name) {
+ if (req->length)
+ buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26;
+ req->length += UMP_STREAM_PKT_BYTES;
+ break;
+ }
+ if (++pos == UMP_STREAM_PKT_BYTES) {
+ if (!req->length)
+ buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26;
+ else
+ buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26;
+ req->length += UMP_STREAM_PKT_BYTES;
+ if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES)
+ break;
+ buf += 4;
+ pos = start_ofs;
+ }
+ }
+
+ if (req->length)
+ queue_request_ep_in(req);
+ else
+ put_empty_request(req);
+}
+
+/* Reply a UMP EP name string */
+static void reply_ump_stream_ep_name(struct f_midi2_ep *ep)
+{
+ reply_ump_stream_string(ep, ump_ep_name(ep),
+ UMP_STREAM_MSG_STATUS_EP_NAME, 0,
+ UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP product ID string */
+static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep)
+{
+ reply_ump_stream_string(ep, ump_product_id(ep),
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0,
+ UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP stream config */
+static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
+{
+ struct snd_ump_stream_msg_stream_cfg rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
+ };
+
+ if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
+ SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
+ else
+ rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a UMP FB info */
+static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk)
+{
+ struct f_midi2_block_info *b = &ep->blks[blk].info;
+ struct snd_ump_stream_msg_fb_info rep = {
+ .type = UMP_MSG_TYPE_STREAM,
+ .status = UMP_STREAM_MSG_STATUS_FB_INFO,
+ .active = !!b->active,
+ .function_block_id = blk,
+ .ui_hint = b->ui_hint,
+ .midi_10 = b->is_midi1,
+ .direction = b->direction,
+ .first_group = b->first_group,
+ .num_groups = b->num_groups,
+ .midi_ci_version = b->midi_ci_version,
+ .sysex8_streams = b->sysex8_streams,
+ };
+
+ reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a FB name string */
+static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk)
+{
+ reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info),
+ UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8,
+ UMP_STREAM_FB_STR_OFF);
+}
+
+/* Process a UMP Stream message */
+static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
+{
+ struct f_midi2 *midi2 = ep->card;
+ unsigned int format, status, blk;
+
+ format = ump_stream_message_format(*data);
+ status = ump_stream_message_status(*data);
+ switch (status) {
+ case UMP_STREAM_MSG_STATUS_EP_DISCOVERY:
+ if (format)
+ return; // invalid
+ if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO)
+ reply_ump_stream_ep_info(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO)
+ reply_ump_stream_ep_device(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME)
+ reply_ump_stream_ep_name(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID)
+ reply_ump_stream_ep_pid(ep);
+ if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG)
+ reply_ump_stream_ep_config(ep);
+ return;
+ case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
+ if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
+ ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ DBG(midi2, "Switching Protocol to MIDI2\n");
+ } else {
+ ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ DBG(midi2, "Switching Protocol to MIDI1\n");
+ }
+ snd_ump_switch_protocol(ep->ump, ep->info.protocol);
+ reply_ump_stream_ep_config(ep);
+ return;
+ case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
+ if (format)
+ return; // invalid
+ blk = (*data >> 8) & 0xff;
+ if (blk >= ep->num_blks)
+ return;
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
+ reply_ump_stream_fb_info(ep, blk);
+ if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
+ reply_ump_stream_fb_name(ep, blk);
+ return;
+ }
+}
+
+/* Process UMP messages included in a USB request */
+static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req)
+{
+ const u32 *data = (u32 *)req->buf;
+ int len = req->actual >> 2;
+ const u32 *in_buf = ep->ump->input_buf;
+
+ for (; len > 0; len--, data++) {
+ if (snd_ump_receive_ump_val(ep->ump, *data) <= 0)
+ continue;
+ if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM)
+ process_ump_stream_msg(ep, in_buf);
+ }
+}
+
+/*
+ * MIDI 2.0 UMP USB request handling
+ */
+
+/* complete handler for UMP EP-out requests */
+static void f_midi2_ep_out_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2_ep *ep = ctx->usb_ep->ep;
+ struct f_midi2 *midi2 = ep->card;
+ int status = req->status;
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ goto error;
+ }
+
+ /* convert to UMP packet in native endianness */
+ le32_to_cpu_array((u32 *)req->buf, req->actual >> 2);
+
+ if (midi2->info.process_ump)
+ process_ump(ep, req);
+
+ snd_ump_receive(ep->ump, req->buf, req->actual & ~3);
+
+ if (midi2->operation_mode != MIDI_OP_MODE_MIDI2)
+ goto error;
+
+ if (queue_request_ep_raw(req))
+ goto error;
+ return;
+
+ error:
+ put_empty_request(req);
+}
+
+/* Transmit UMP packets received from user-space to the gadget */
+static void process_ump_transmit(struct f_midi2_ep *ep)
+{
+ struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+ struct f_midi2 *midi2 = ep->card;
+ struct usb_request *req;
+ int len;
+
+ if (!usb_ep->usb_ep->enabled)
+ return;
+
+ for (;;) {
+ req = get_empty_request(usb_ep);
+ if (!req)
+ break;
+ len = snd_ump_transmit(ep->ump, (u32 *)req->buf,
+ midi2->info.req_buf_size);
+ if (len <= 0) {
+ put_empty_request(req);
+ break;
+ }
+
+ req->length = len;
+ if (queue_request_ep_in(req) < 0)
+ break;
+ }
+}
+
+/* Complete handler for UMP EP-in requests */
+static void f_midi2_ep_in_complete(struct usb_ep *usb_ep,
+ struct usb_request *req)
+{
+ struct f_midi2_req_ctx *ctx = req->context;
+ struct f_midi2_ep *ep = ctx->usb_ep->ep;
+ struct f_midi2 *midi2 = ep->card;
+ int status = req->status;
+
+ put_empty_request(req);
+
+ if (status) {
+ DBG(midi2, "%s complete error %d: %d/%d\n",
+ usb_ep->name, status, req->actual, req->length);
+ return;
+ }
+
+ process_ump_transmit(ep);
+}
+
+/* Start MIDI EP */
+static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep,
+ struct usb_function *fn)
+{
+ int err;
+
+ usb_ep_disable(usb_ep->usb_ep);
+ err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep);
+ if (err)
+ return err;
+ return usb_ep_enable(usb_ep->usb_ep);
+}
+
+/* Drop pending requests */
+static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ int i;
+
+ if (!usb_ep->num_reqs)
+ return;
+
+ for (i = 0; i < usb_ep->num_reqs; i++) {
+ if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) {
+ usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req);
+ set_bit(i, &usb_ep->free_reqs);
+ }
+ }
+}
+
+/* Allocate requests for the given EP */
+static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ struct f_midi2 *midi2 = usb_ep->card;
+ int i;
+
+ if (!usb_ep->reqs)
+ return -EINVAL;
+
+ for (i = 0; i < midi2->info.num_reqs; i++) {
+ if (usb_ep->reqs[i].req)
+ continue;
+ usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep,
+ midi2->info.req_buf_size);
+ if (!usb_ep->reqs[i].req)
+ return -ENOMEM;
+ usb_ep->reqs[i].req->context = &usb_ep->reqs[i];
+ }
+ return 0;
+}
+
+/* Free allocated requests */
+static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ struct f_midi2 *midi2 = usb_ep->card;
+ int i;
+
+ for (i = 0; i < midi2->info.num_reqs; i++) {
+ if (!usb_ep->reqs[i].req)
+ continue;
+ free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req);
+ usb_ep->reqs[i].req = NULL;
+ }
+}
+
+/* Initialize EP */
+static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
+ struct f_midi2_usb_ep *usb_ep,
+ void *desc, int num_reqs,
+ void (*complete)(struct usb_ep *usb_ep,
+ struct usb_request *req))
+{
+ int i;
+
+ usb_ep->card = midi2;
+ usb_ep->ep = ep;
+ usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc);
+ if (!usb_ep->usb_ep)
+ return -ENODEV;
+ usb_ep->complete = complete;
+
+ if (num_reqs) {
+ usb_ep->reqs = kcalloc(num_reqs, sizeof(*usb_ep->reqs),
+ GFP_KERNEL);
+ if (!usb_ep->reqs)
+ return -ENOMEM;
+ for (i = 0; i < num_reqs; i++) {
+ usb_ep->reqs[i].index = i;
+ usb_ep->reqs[i].usb_ep = usb_ep;
+ set_bit(i, &usb_ep->free_reqs);
+ usb_ep->num_reqs++;
+ }
+ }
+
+ return 0;
+}
+
+/* Free EP */
+static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep)
+{
+ f_midi2_drop_reqs(usb_ep);
+
+ f_midi2_free_ep_reqs(usb_ep);
+
+ kfree(usb_ep->reqs);
+ usb_ep->num_reqs = 0;
+ usb_ep->free_reqs = 0;
+ usb_ep->reqs = NULL;
+}
+
+/* Queue requests for EP-out at start */
+static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+ int i, err;
+
+ for (i = 0; i < usb_ep->num_reqs; i++) {
+ if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req)
+ continue;
+ usb_ep->reqs[i].req->complete = usb_ep->complete;
+ err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req,
+ GFP_ATOMIC);
+ if (!err)
+ clear_bit(i, &usb_ep->free_reqs);
+ }
+}
+
+/*
+ * Gadget Function callbacks
+ */
+
+/* gadget function set_alt callback */
+static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf,
+ unsigned int alt)
+{
+ struct f_midi2 *midi2 = func_to_midi2(fn);
+ struct f_midi2_ep *ep;
+ int i, op_mode, err;
+
+ if (intf != midi2->midi_if || alt > 1)
+ return 0;
+
+ if (alt == 0)
+ op_mode = MIDI_OP_MODE_MIDI1;
+ else if (alt == 1)
+ op_mode = MIDI_OP_MODE_MIDI2;
+ else
+ op_mode = MIDI_OP_MODE_UNSET;
+
+ if (midi2->operation_mode == op_mode)
+ return 0;
+
]
]
|
| 394/2 |
2026/07/30 15:11 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 52254
output: 49
thoughts: 2044
Thoughts: **Analysis of MIDI2 ConfigFS Initialization and Cleanup**
Okay, so it looks like the `f_midi2_ep_opts_release` function wasn't present in the initial commit `8b645922b22303cec4628dbbbf6c8553d1cdec87`, and I needed to verify this for my investigation. I just confirmed that this commit didn't include the function.
Now, looking at the diffs, I can see that in commit `8b645922b22303cec4628dbbbf6c8553d1cdec87`, `f_midi2_alloc_inst` and `f_midi2_free_inst` were pretty straightforward, allocating and freeing the endpoint and block structures directly without configfs. They were simple allocations and frees for the single endpoint and feedback block. However, there were NO configfs default groups (`ep.0` and `block.0`).
Then, the introduction of the configfs default groups happened in `29ee7a4dddd5caa18d1cef000f20c6af43f762f1`. This patch added the calls to `configfs_add_default_group` within `f_midi2_alloc_inst` for the `ep.0` and `block.0` groups. At the same time, `f_midi2_ep_opts_release` was also introduced to free endpoint specific resources. Furthermore, the `f_midi2_free_inst` function had its ep and block cleanup logic removed as a direct result of these ConfigFS changes.
Here's the problem: The original commit, `29ee7a4dddd5caa18d1cef000f20c6af43f762f1`, added the configuration logic but *did not* include the corresponding `configfs_remove_default_groups` calls in the release functions (`f_midi2_ep_opts_release` and `f_midi2_free_inst`). That means the configfs groups were created but not correctly removed.
Given this, a separate patch (which I don't have here) should, and will have to, add the missing `configfs_remove_default_groups` calls to the `f_midi2_ep_opts_release` and `f_midi2_free_inst` functions. This is precisely what's needed to fix the resource leak caused by the incomplete configfs integration in the `29ee7a4dddd5caa18d1cef000f20c6af43f762f1` commit. I just confirmed this to be the case based on the changes to those functions and the fact that `29ee7a4dddd5caa18d1cef000f20c6af43f762f1` is indeed the commit hash introducing the problematic code.
|
| 395/2 |
2026/07/30 15:11 |
tool |
set-results |
0m
Args: map[FixesHash:29ee7a4dddd5caa18d1cef000f20c6af43f762f1]
Results: map[FixesHash:29ee7a4dddd5caa18d1cef000f20c6af43f762f1]
|
| 396/1 |
2026/07/30 15:11 |
action |
format-fixes |
0m
Results: map[Fixes:map[AuthorEmail:tiwai@suse.de AuthorName:Takashi Iwai Hash:29ee7a4dddd5caa18d1cef000f20c6af43f762f1 Title:usb: gadget: midi2: Add configfs support]]
|
| 397/1 |
2026/07/30 15:11 |
action |
get-maintainers |
0m
Results: map[Recipients:[map[Email:christophe.jaillet@wanadoo.fr Name:Christophe JAILLET To:false] map[Email:gregkh@linuxfoundation.org Name:Greg Kroah-Hartman To:true] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:linux-usb@vger.kernel.org Name: To:true] map[Email:tiwai@suse.de Name:Takashi Iwai To:true]]]
|
| 398/1 |
2026/07/30 15:11 |
action |
get-recent-commits |
0m
Results: map[RecentCommits:usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
usb: gadget: midi2: Fix MIDI2 IN EP max packet size
usb: gadget: midi2: Fix missing UMP group attributes initialization
ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
usb: gadget: midi2: Fix interpretation of is_midi1 bits
usb: gadget: function: remove redundant else statement
usb: gadget: function: move u_f.h to include/linux/usb/func_utils.h
usb: gadget: midi2: Fix the response for FB info with block 0xff
usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup
usb: gadget: add missing MODULE_DESCRIPTION() macros
usb: gadget: unconditionally allocate hs/ss descriptor in bind operation
usb: gadget: midi2: fix missing unlock in f_midi2_block_opts_create()
usb: gadget: midi2: More flexible MIDI 1.0 configuration
usb: gadget: midi2: Add "Operation Mode" control
usb: gadget: midi2: MIDI 1.0 interface (altset 0) support
]
|
| 399/1 |
2026/07/30 15:11 |
agent |
description-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.
The one-line summary should describe the change being made, rather than mention the tool that
detected the bug.
The description must not contain lines starting with '#' because they will dropped by git as comments.
The description must not contain lines starting with '--' or '---' (including inline code diffs)
because they may confuse git/patch utilities.
Your final reply should contain only the text of the commit description.
The one-line summary must be not longer than 72 characters.
IMPORTANT: Do not wrap lines manually (e.g., at 80 characters); we will reformat the text
automatically, so keep paragraphs as single lines without newlines.
Generally try to phrase the description without mentioning syzkaller
(avoid phrases like "the bug was triggered by syzkaller" or "the bug was triggered by fuzzer", etc).
How the bug was triggered is generally an irrelevant detail.
Any bug triggered by a fuzzer can also be triggered by a malicious user, or a buggy program.
If the crash is reported by a sanitizer (e.g., KASAN, KMSAN, lockdep), include the relevant
parts of the sanitizer output to illustrate the problem. Exclude less relevant sections,
as the stack trace can be very long. Describe the execution path that leads to the manifestation
of the kernel bug.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
2026/07/30 13:30:35 executed programs: 5
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504 [inline]
f_midi2_alloc_inst+0x80/0x2c0 drivers/usb/gadget/function/f_midi2.c:2666
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
kmemleak_alloc_recursive include/linux/kmemleak.h:44 [inline]
slab_post_alloc_hook mm/slub.c:4597 [inline]
slab_alloc_node mm/slub.c:4917 [inline]
__kmalloc_cache_noprof+0x1be/0x3f0 mm/slub.c:5485
_kmalloc_noprof include/linux/slab.h:988 [inline]
_kzalloc_noprof include/linux/slab.h:1309 [inline]
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342 [inline]
f_midi2_alloc_inst+0x16a/0x2c0 drivers/usb/gadget/function/f_midi2.c:2673
try_get_usb_function_instance drivers/usb/gadget/functions.c:28 [inline]
usb_get_function_instance+0x6f/0x1c0 drivers/usb/gadget/functions.c:44
function_make+0xcf/0x210 drivers/usb/gadget/configfs.c:626
configfs_mkdir+0x26e/0x540 fs/configfs/dir.c:1360
vfs_mkdir+0x204/0x320 fs/namei.c:5276
filename_mkdirat+0x14e/0x2e0 fs/namei.c:5309
__do_sys_mkdirat fs/namei.c:5330 [inline]
__se_sys_mkdirat+0x30/0xd0 fs/namei.c:5327
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14f/0x3c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
connection error: failed to recv *flatrpc.ExecutorMessageRawT: EOF
The explanation of the root cause of the bug is:
The root cause of this memory leak is that the driver fails to drop the reference counts of its default configfs groups when the parent group is destroyed.
When a USB function instance for `midi2` is allocated via `f_midi2_alloc_inst()`, it dynamically allocates `ep_opts` and `block_opts` structures using `kzalloc_obj()`. These structures are then initialized as configfs groups and added as default groups to their respective parents:
- `ep.0` is added as a default group to the function instance group (`opts->func_inst.group`).
- `block.0` is added as a default group to the endpoint group (`ep_opts->group`).
When the user removes the configfs directory for the function instance (via `rmdir`), configfs removes the dentries for the default groups but **does not** automatically drop their reference counts. The responsibility of dropping the refcounts of default groups lies with the driver, typically by calling `configfs_remove_default_groups()` when the parent group is released.
However, `f_midi2_free_inst()` (which is called when the function instance's refcount reaches 0) only frees the `f_midi2_opts` structure and does not clean up its default groups. Similarly, `f_midi2_ep_opts_release()` does not clean up its default groups. As a result, the `ep_opts` and `block_opts` structures are leaked.
### How to fix it
To fix the leak, you need to explicitly remove the default groups and drop their reference counts before freeing the parent structures. This can be done by calling `configfs_remove_default_groups()` in the respective release functions.
1. In `f_midi2_free_inst()`, drop the default groups of the function instance (this will drop the refcount of `ep.0` and trigger `f_midi2_ep_opts_release`):
```c
static void f_midi2_free_inst(struct usb_function_instance *f)
{
struct f_midi2_opts *opts;
opts = container_of(f, struct f_midi2_opts, func_inst);
configfs_remove_default_groups(&opts->func_inst.group); /* ADD THIS */
kfree(opts->info.iface_name);
kfree(opts);
}
```
2. In `f_midi2_ep_opts_release()`, drop the default groups of the endpoint (this will drop the refcount of `block.0` and trigger `f_midi2_block_opts_release`):
```c
static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
configfs_remove_default_groups(to_config_group(item)); /* ADD THIS */
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
}
```
With these changes, the hierarchy of default groups will be properly unrolled and freed when the function instance is destroyed, whether it's through configfs `rmdir` or an error path that directly calls `usb_put_function_instance()`.
The diff of the bug fix is:
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 19fdac024..ec4266ecc 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2473,6 +2473,7 @@ static void f_midi2_ep_opts_release(struct config_item *item)
{
struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+ configfs_remove_default_groups(to_config_group(item));
kfree(opts->info.ep_name);
kfree(opts->info.product_id);
kfree(opts);
@@ -2639,6 +2640,7 @@ static void f_midi2_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_midi2_opts, func_inst);
+ configfs_remove_default_groups(&opts->func_inst.group);
kfree(opts->info.iface_name);
kfree(opts);
}
Additional description of the patch:
I have implemented the fix for the memory leak in the `f_midi2` USB gadget function driver.
### What I did
I added calls to `configfs_remove_default_groups()` in the release functions of the configfs groups that have default groups attached to them:
1. In `f_midi2_free_inst()`, I added `configfs_remove_default_groups(&opts->func_inst.group);` to drop the reference count of the default `ep.0` group.
2. In `f_midi2_ep_opts_release()`, I added `configfs_remove_default_groups(to_config_group(item));` to drop the reference count of the default `block.0` group.
### Why I did it
When a USB function instance for `midi2` is allocated, it dynamically allocates `ep_opts` and `block_opts` structures and adds them as default groups (`ep.0` and `block.0`). While configfs removes the dentries for default groups when the parent directory is removed, it does not automatically drop their reference counts. This responsibility lies with the driver.
By explicitly calling `configfs_remove_default_groups()` in the respective release functions, we ensure that the reference counts of the default groups are properly dropped. This correctly triggers the existing release callbacks (`f_midi2_ep_opts_release` and `f_midi2_block_opts_release`), which safely free the dynamically allocated memory in the correct order.
This approach is robust and idiomatic. It also safely handles dynamically created groups (e.g., `ep.1` created via `mkdir`), because their `default_groups` list is properly initialized and empty, making `configfs_remove_default_groups()` a safe no-op for them.
Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc.
usb: gadget: functionfs: Use safer strscpy() instead of strcpy()
Convert remaining multi-line kmalloc_obj/flex GFP_KERNEL uses
Convert more 'alloc_obj' cases to default GFP_KERNEL arguments
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
usb: gadget: Constify struct configfs_item_operations and configfs_group_operations
usb: gadget: midi2: Fix MIDI2 IN EP max packet size
usb: gadget: midi2: Fix missing UMP group attributes initialization
ALSA: ump: Fix a typo of snd_ump_stream_msg_device_info
usb: gadget: midi2: Fix interpretation of is_midi1 bits
usb: gadget: function: remove redundant else statement
usb: gadget: function: move u_f.h to include/linux/usb/func_utils.h
usb: gadget: midi2: Fix the response for FB info with block 0xff
usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup
usb: gadget: add missing MODULE_DESCRIPTION() macros
usb: gadget: unconditionally allocate hs/ss descriptor in bind operation
usb: gadget: midi2: fix missing unlock in f_midi2_block_opts_create()
usb: gadget: midi2: More flexible MIDI 1.0 configuration
usb: gadget: midi2: Add "Operation Mode" control
usb: gadget: midi2: MIDI 1.0 interface (altset 0) support
Reply: usb: gadget: midi2: Fix memory leak in configfs default groups
When a USB function instance for midi2 is allocated, it dynamically
allocates ep_opts and block_opts structures and adds them as default groups
(ep.0 and block.0). While configfs removes the dentries for default groups
when the parent directory is removed, it does not automatically drop their
reference counts. This responsibility lies with the driver.
Because f_midi2_free_inst() and f_midi2_ep_opts_release() do not clean up
their default groups, the ep_opts and block_opts structures are leaked when
the function instance is destroyed.
Fix this by explicitly calling configfs_remove_default_groups() in the
respective release functions. This ensures that the reference counts of the
default groups are properly dropped, triggering the existing release
callbacks (f_midi2_ep_opts_release and f_midi2_block_opts_release) to
safely free the dynamically allocated memory.
Kmemleak reports:
BUG: memory leak
unreferenced object 0xffff88810aacb200 (size 512):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 b2 ac 0a 81 88 ff ff 65 70 2e 30 00 00 00 00 ........ep.0....
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc f2efe6a4):
...
f_midi2_ep_opts_create drivers/usb/gadget/function/f_midi2.c:2504
[inline]
f_midi2_alloc_inst+0x80/0x2c0
drivers/usb/gadget/function/f_midi2.c:2666
...
BUG: memory leak
unreferenced object 0xffff8881083d9e00 (size 256):
comm "syz.0.19", pid 6225, jiffies 4294944049
hex dump (first 32 bytes):
08 9e 3d 08 81 88 ff ff 62 6c 6f 63 6b 2e 30 00 ..=.....block.0.
00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
backtrace (crc cc37bc9e):
...
f_midi2_block_opts_create drivers/usb/gadget/function/f_midi2.c:2342
[inline]
f_midi2_alloc_inst+0x16a/0x2c0
drivers/usb/gadget/function/f_midi2.c:2673
...
|
| 400/2 |
2026/07/30 15:11 |
llm |
description-generator |
0m
Model:
Tokens:
input: 7277
output: 754
thoughts: 1773
Thoughts: **usb: gadget: midi2: Fix memory leak in configfs default groups**
Okay, so I've been digging into this MIDI2 USB gadget driver, and I think I've tracked down a pretty nasty memory leak. It seems the core of the problem lies within how we're handling the ConfigFS default groups.
When we allocate a USB function instance for MIDI2, the driver dynamically creates `ep_opts` and `block_opts` structures. It then adds these as default groups within ConfigFS, specifically named `ep.0` and `block.0`. Now, ConfigFS is pretty good at cleaning up the dentries for these default groups when their parent directory goes away. However, it *doesn't* automatically decrement the reference counts for those groups. That's our responsibility as the driver authors.
The problem is, `f_midi2_free_inst()` and `f_midi2_ep_opts_release()` were missing this crucial step. They weren't cleaning up their default groups properly. Because of this oversight, the `ep_opts` and `block_opts` structures were getting leaked every time a function instance was destroyed. They'd just sit there, unreferenced, consuming memory.
The fix, and it's a pretty straightforward one, involves explicitly calling `configfs_remove_default_groups()` in both `f_midi2_free_inst()` and `f_midi2_ep_opts_release()`. This ensures that the reference counts for those default groups are dropped when they should be. This in turn triggers the existing release callbacks we have in place – `f_midi2_ep_opts_release` and `f_midi2_block_opts_release` – to safely release the dynamically allocated memory. Problem solved!
I've got the kmemleak reports showing the evidence clearly: We're seeing "unreferenced object" reports for both `ep.0` and `block.0`, indicating the leak. The backtraces point directly to `f_midi2_ep_opts_create` and `f_midi2_block_opts_create` during instance allocation, just as suspected. Based on the related commits, I'm confident this resolves an ongoing issue.
|