驱动-热插拔-kset_uevent_ops知识点

了解 热插拔-kset_uevent_ops知识点


前言

前面了解了驱动-热插拔-内核发送事件到用户空间-uevent 其实就是uevent 事件传递流程的过程,通过分析 kobject_uevent 函数API,了解了整个流程。 这里再次分析一下流程中 uevent_ops 结构体。

参考资料

驱动-热插拔-内核发送事件到用户空间-uevent
热插拔
内核发送事件到用户空间
完善kset_uevent_ops结构体

一、 uevent_ops 知识点

为什么在uevent事件中需要了解这个结构体

驱动-热插拔-内核发送事件到用户空间-uevent 篇中 我们分析了内核空间发送事件到用户空间带源码流程, 也初步分析了 uevent_ops 相关知识点,如下: 可以再次温习一下知识点。
在这里插入图片描述
继续看看发送事件源码 kobject_uevent 部分片段:

  • 如果存在uevent_ops 且有filter 方法,就会过滤一次filter(kobj)
  • 如果uevent_ops 存在且有name,则影响子系统name
    在这里插入图片描述

在这里插入图片描述

我们就是为了分析和了解uvent_ops 的实际应用和关联知识点

uevent_ops 结构体详情

struct kset_uevent_ops 是一个包含函数指针的结构体,它定义了kset(内核对象集合)如何控制和处理其成员 kobject 发出的 uevent。当 kobject 的状态发生变化(如添加、移除、绑定驱动等)时,内核会生成 uevent 来通知用户空间(例如 udev)。这个结构体提供了三个钩子函数(hook functions),允许 kset 的所有者“拦截”并“定制”这一过程。
其定义通常位于 include/linux/kobject.h 中:

struct kset_uevent_ops {
   
   
	int (* const filter)(const struct kobject *kobj);
	const char *(* const name)(const struct kobject *kobj);
	int (* const uevent)(const struct kobject *kobj, struct kobj_uevent_env *env);
};

下面对每个成员进行详细解释:

filter - 事件过滤器

原型: int (*filter)(const struct kobject *kobj);

作用: 决定是否允许为指定的 kobject 生成 uevent

应用场景: 用于过滤掉不需要通知用户空间的 kobject。例如,一个虚拟的或内部的 kobject,其状态变化对用户空间没有意义,就可以在此函数中返回 0 将其过滤掉。

name - 重命名子系统

原型: const char *(*name)(const struct kobject *kobj);

作用: 覆盖 uevent 中默认的 SUBSYSTEM 属性值。

返回值:

  • 字符串指针: 返回一个字符串,该字符串将作为 uevent 的 SUBSYSTEM= 字段的值。
  • NULL: 使用 kobject 所属的 kset 的名称作为默认的 SUBSYSTEM 值。

应用场景: 用于实现子系统的“重命名”或统一管理。例如,多个不同的 kset 可能希望它们发出的 uevent 在用户空间看来都来自同一个子系统,就可以通过这个钩子函数返回相同的名字。

uevent - 事件增强器

原型: int (*uevent)(const struct kobject *kobj, struct kobj_uevent_env *env);

作用:uevent 被发送到用户空间之前,向其添加额外的环境变量。这是最常用、最强大的一个钩子函数。

参数:

  • kobj: 触发事件的 kobject
  • env: 指向 struct kobj_uevent_env 的指针,该结构体包含了当前已准备的环境变量列表(如 ACTION, DEVPATH, SUBSYSTEM 等),并提供了添加新变量的 API(如 add_uevent_var)。

应用场景: 添加任何驱动或子系统需要的自定义信息。这是设备模型向 udev 传递关键信息的主要方式。例如:

  • 添加设备厂商ID、产品ID: add_uevent_var(env, "PRODUCT=%x/%x/%x", idVendor, idProduct, bcdDevice);
  • 添加设备序列号: add_uevent_var(env, "SERIAL=%s", serial);
  • 添加设备类型标志。

uevent_ops 在结构体中的实际应用

内核中 uevent 的发送核心函数是 kobject_uevent(或它的高级封装 kobject_uevent_env)。其基本流程如下:

事件触发: 某个内核代码调用 kobject_addkobject_del 或设备绑定驱动等,导致需要发送 uevent

查找 kset 和 ops: 内核向上遍历 kobject 的层次结构,找到其所属的 kset,并获取该 kset uevent_ops

调用 filter: 如果 kset 定义了 filter 操作,则调用它。如果 filter 返回 0,流程终止,uevent 不会被发送。

调用 name: 如果 kset 定义了 name 操作,则调用它来获取子系统名称;否则,使用 kset 的名称。

准备基础环境变量: 内核初始化 kobj_uevent_env 结构,添加一些标准变量,如:

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, 473 char *envp_ext[]) 474 { 475 struct kobj_uevent_env *env; 476 const char *action_string = kobject_actions[action]; 477 const char *devpath = NULL; 478 const char *subsystem; 479 struct kobject *top_kobj; 480 struct kset *kset; 481 const struct kset_uevent_ops *uevent_ops; 482 int i = 0; 483 int retval = 0; 484 485 /* 486 * Mark "remove" event done regardless of result, for some subsystems 487 * do not want to re-trigger "remove" event via automatic cleanup. 488 */ 489 if (action == KOBJ_REMOVE) 490 kobj->state_remove_uevent_sent = 1; 491 492 pr_debug("kobject: '%s' (%p): %s\n", 493 kobject_name(kobj), kobj, __func__); 494 495 /* search the kset we belong to */ 496 top_kobj = kobj; 497 while (!top_kobj->kset && top_kobj->parent) 498 top_kobj = top_kobj->parent; 499 500 if (!top_kobj->kset) { 501 pr_debug("kobject: '%s' (%p): %s: attempted to send uevent " 502 "without kset!\n", kobject_name(kobj), kobj, 503 __func__); 504 return -EINVAL; 505 } 506 507 kset = top_kobj->kset; 508 uevent_ops = kset->uevent_ops; 509 510 /* skip the event, if uevent_suppress is set*/ 511 if (kobj->uevent_suppress) { 512 pr_debug("kobject: '%s' (%p): %s: uevent_suppress " 513 "caused the event to drop!\n", 514 kobject_name(kobj), kobj, __func__); 515 return 0; 516 } 517 /* skip the event, if the filter returns zero. */ 518 if (uevent_ops && uevent_ops->filter) 519 if (!uevent_ops->filter(kobj)) { 520 pr_debug("kobject: '%s' (%p): %s: filter function " 521 "caused the event to drop!\n", 522 kobject_name(kobj), kobj, __func__); 523 return 0; 524 } 525 526 /* originating subsystem */ 527 if (uevent_ops && uevent_ops->name) 528 subsystem = uevent_ops->name(kobj); 529 else 530 subsystem = kobject_name(&kset->kobj); 531 if (!subsystem) { 532 pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the " 533 "event to drop!\n", kobject_name(kobj), kobj, 534 __func__); 535 return 0; 536 } 537 538 /* environment buffer */ 539 env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); 540 if (!env) 541 return -ENOMEM; 542 543 /* complete object path */ 544 devpath = kobject_get_path(kobj, GFP_KERNEL); 545 if (!devpath) { 546 retval = -ENOENT; 547 goto exit; 548 } 549 550 /* default keys */ 551 retval = add_uevent_var(env, "ACTION=%s", action_string); 552 if (retval) 553 goto exit; 554 retval = add_uevent_var(env, "DEVPATH=%s", devpath); 555 if (retval) 556 goto exit; 557 retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem); 558 if (retval) 559 goto exit; 560 561 /* keys passed in from the caller */ 562 if (envp_ext) { 563 for (i = 0; envp_ext[i]; i++) { 564 retval = add_uevent_var(env, "%s", envp_ext[i]); 565 if (retval) 566 goto exit; 567 } 568 } 569 570 /* let the kset specific function add its stuff */ 571 if (uevent_ops && uevent_ops->uevent) { 572 retval = uevent_ops->uevent(kobj, env); 573 if (retval) { 574 pr_debug("kobject: '%s' (%p): %s: uevent() returned " 575 "%d\n", kobject_name(kobj), kobj, 576 __func__, retval); 577 goto exit; 578 } 579 } 580 581 switch (action) { 582 case KOBJ_ADD: 583 /* 584 * Mark "add" event so we can make sure we deliver "remove" 585 * event to userspace during automatic cleanup. If 586 * the object did send an "add" event, "remove" will 587 * automatically generated by the core, if not already done 588 * by the caller. 589 */ 590 kobj->state_add_uevent_sent = 1; 591 break; 592 593 case KOBJ_UNBIND: 594 zap_modalias_env(env); 595 break; 596 597 default: 598 break; 599 } 600 601 mutex_lock(&uevent_sock_mutex); 602 /* we will send an event, so request a new sequence number */ 603 retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum); 604 if (retval) { 605 mutex_unlock(&uevent_sock_mutex); 606 goto exit; 607 } 608 retval = kobject_uevent_net_broadcast(kobj, env, action_string, 609 devpath); 610 mutex_unlock(&uevent_sock_mutex); 611 612 #ifdef CONFIG_UEVENT_HELPER 613 /* call uevent_helper, usually only enabled during early boot */ 614 if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { 615 struct subprocess_info *info; 616 617 retval = add_uevent_var(env, "HOME=/"); 618 if (retval) 619 goto exit; 620 retval = add_uevent_var(env, 621 "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); 622 if (retval) 623 goto exit; 624 retval = init_uevent_argv(env, subsystem); 625 if (retval) 626 goto exit; 627 628 retval = -ENOMEM; 629 info = call_usermodehelper_setup(env->argv[0], env->argv, 630 env->envp, GFP_KERNEL, 631 NULL, cleanup_uevent_env, env); 632 if (info) { 633 retval = call_usermodehelper_exec(info, UMH_NO_WAIT); 634 env = NULL; /* freed by cleanup_uevent_env */ 635 } 636 } 637 #endif 638 639 exit: 640 kfree(devpath); 641 kfree(env); 642 return retval; 643 }
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值