当前位置: 首页 > news >正文

linux pcie【7】- epf设备创建过程

本文基于kernel的pci-epf-test例程,介绍一下epf设备的创建过程,kernel版本为5.15。

我们先从pci_ep_cfs_init看起。690-699行,在Configfs里创建一个pci_ep子系统。701-718行,在pci_ep子系统下分别创建functions和controllers两个config_group。

677 static struct configfs_subsystem pci_ep_cfs_subsys = {
678     .su_group = {
679         .cg_item = {
680             .ci_namebuf = "pci_ep",
681             .ci_type = &pci_ep_type,
682         },
683     },
684     .su_mutex = __MUTEX_INITIALIZER(pci_ep_cfs_subsys.su_mutex),
685 };
686
687 static int __init pci_ep_cfs_init(void)
688 {
689     int ret;
690     struct config_group *root = &pci_ep_cfs_subsys.su_group;
691
692     config_group_init(root);
693
694     ret = configfs_register_subsystem(&pci_ep_cfs_subsys);
695     if (ret) {
696         pr_err("Error %d while registering subsystem %s\n",
697                ret, root->cg_item.ci_namebuf);
698         goto err;
699     }
700
701     functions_group = configfs_register_default_group(root, "functions",
702                               &pci_functions_type);
703     if (IS_ERR(functions_group)) {
704         ret = PTR_ERR(functions_group);
705         pr_err("Error %d while registering functions group\n",
706                ret);
707         goto err_functions_group;
708     }
709
710     controllers_group =
711         configfs_register_default_group(root, "controllers",
712                         &pci_controllers_type);
713     if (IS_ERR(controllers_group)) {
714         ret = PTR_ERR(controllers_group);
715         pr_err("Error %d while registering controllers group\n",
716                ret);
717         goto err_controllers_group;
718     }
719
720     return 0;
721
722 err_controllers_group:
723     configfs_unregister_default_group(functions_group);
724
725 err_functions_group:
726     configfs_unregister_subsystem(&pci_ep_cfs_subsys);
727
728 err:
729     return ret;
730 }
731 module_init(pci_ep_cfs_init);

参考前面介绍ep控制器驱动的文章,注册ep控制器阶段会调用到devm_pci_epc_create函数,在它内部会调用pci_ep_cfs_add_epc_group在controllers下创建一个控制器对应的config_group,比如51000000.pcie_ep。

261 struct config_group *pci_ep_cfs_add_epc_group(const char *name)
262 {
263     int ret;
264     struct pci_epc *epc;
265     struct config_group *group;
266     struct pci_epc_group *epc_group;
267
268     epc_group = kzalloc(sizeof(*epc_group), GFP_KERNEL);
269     if (!epc_group) {
270         ret = -ENOMEM;
271         goto err;
272     }
273
274     group = &epc_group->group;
275
276     config_group_init_type_name(group, name, &pci_epc_type);
277     ret = configfs_register_group(controllers_group, group);
278     if (ret) {
279         pr_err("failed to register configfs group for %s\n", name);
280         goto err_register_group;
281     }
282
283     epc = pci_epc_get(name);
284     if (IS_ERR(epc)) {
285         ret = PTR_ERR(epc);
286         goto err_epc_get;
287     }
288
289     epc_group->epc = epc;
290
291     return group;
292
293 err_epc_get:
294     configfs_unregister_group(group);
295
296 err_register_group:
297     kfree(epc_group);
298
299 err:
300     return ERR_PTR(ret);
301 }

我们再看一下pci_epf_driver的注册函数pci_epf_register_driver。410行,注册设备驱动,414行,添加driver对应的config_group,它最终调用pci_ep_cfs_add_epf_group添加config_group。

188 #define pci_epf_register_driver(driver)    \
189         __pci_epf_register_driver((driver), THIS_MODULE)389 /**
390  * __pci_epf_register_driver() - register a new PCI EPF driver
391  * @driver: structure representing PCI EPF driver
392  * @owner: the owner of the module that registers the PCI EPF driver
393  *
394  * Invoke to register a new PCI EPF driver.
395  */
396 int __pci_epf_register_driver(struct pci_epf_driver *driver,
397                   struct module *owner)
398 {
399     int ret;
400
401     if (!driver->ops)
402         return -EINVAL;
403
404     if (!driver->ops->bind || !driver->ops->unbind)
405         return -EINVAL;
406
407     driver->driver.bus = &pci_epf_bus_type;
408     driver->driver.owner = owner;
409
410     ret = driver_register(&driver->driver);
411     if (ret)
412         return ret;
413
414     pci_epf_add_cfs(driver);
415
416     return 0;
417 }
418 EXPORT_SYMBOL_GPL(__pci_epf_register_driver);

我们接着看一下pci_ep_cfs_add_epf_group,对于pci_epf_test驱动它会在前面的介绍的functions里添加一个名字为pci_epf_test的config_group。

627 static void pci_epf_drop(struct config_group *group, struct config_item *item)
628 {
629     config_item_put(item);
630 }
631
632 static struct configfs_group_operations pci_epf_group_ops = {
633     .make_group     = &pci_epf_make,
634     .drop_item      = &pci_epf_drop,
635 };
636
637 static const struct config_item_type pci_epf_group_type = {
638     .ct_group_ops   = &pci_epf_group_ops,
639     .ct_owner   = THIS_MODULE,
640 };
641
642 struct config_group *pci_ep_cfs_add_epf_group(const char *name)
643 {
644     struct config_group *group;
645
646     group = configfs_register_default_group(functions_group, name,
647                         &pci_epf_group_type);
648     if (IS_ERR(group))
649         pr_err("failed to register configfs group for %s function\n",
650                name);
651
652     return group;
653 }
654 EXPORT_SYMBOL(pci_ep_cfs_add_epf_group);

到这里我们的底层建筑基本都构建好了,我们从应用层操作pci_ep configfs的流程看一下一个epf设备时怎么创建并工作的。

通过下面的命令可以创建一个epf设备。 

 # mount -t configfs none /sys/kernel/config
 # cd /sys/kernel/config/pci_ep/
 # mkdir functions/pci_epf_test/func1

这谢指令执行完后,kernel里的pci_epf_make会被调用。572行,为epf_group分配空间。577行,从funcitons_idr里分配一个index,并将epf_group保存到对应的index下。586行,初始化epf_group->group,在我们能的例子里这里的name就是func1。588行,epf_name就是pci_epf_test.<index>。595行创建epf设备。607-609行,初始化并延迟调用一个delayed_work,它的回调函数为pci_epf_cfs_work,在pci_epf_cfs_work中主要是添加了primary和secondary两config_group, 这样一个epf就可以帮忙两个epc,这个功能主要是当soc上有两个ep控制器时,可以将它们作为一个ntb桥使用,用来连接两个host,drivers/pci/endpoint/functions/pci-epf-ntb.c 下有专门的固件驱动实现,感兴趣可以看一下,这里不过多介绍。

517 static struct config_group *pci_epf_type_make(struct config_group *group,
518                           const char *name)
519 {
520     struct pci_epf_group *epf_group = to_pci_epf_group(&group->cg_item);
521     struct config_group *epf_type_group;
522
523     epf_type_group = pci_epf_type_add_cfs(epf_group->epf, group);
524     return epf_type_group;
525 }
526
527 static void pci_epf_type_drop(struct config_group *group,
528                   struct config_item *item)
529 {
530     config_item_put(item);
531 }
532
533 static struct configfs_group_operations pci_epf_type_group_ops = {
534     .make_group     = &pci_epf_type_make,
535     .drop_item      = &pci_epf_type_drop,
536 };
537
538 static const struct config_item_type pci_epf_type = {
539     .ct_group_ops   = &pci_epf_type_group_ops,
540     .ct_item_ops    = &pci_epf_ops,
541     .ct_attrs   = pci_epf_attrs,
542     .ct_owner   = THIS_MODULE,
543 };
544
545 static void pci_epf_cfs_work(struct work_struct *work)
546 {
547     struct pci_epf_group *epf_group;
548     struct config_group *group;
549
550     epf_group = container_of(work, struct pci_epf_group, cfs_work.work);
551     group = pci_ep_cfs_add_primary_group(epf_group);
552     if (IS_ERR(group)) {
553         pr_err("failed to create 'primary' EPC interface\n");
554         return;
555     }
556
557     group = pci_ep_cfs_add_secondary_group(epf_group);
558     if (IS_ERR(group)) {
559         pr_err("failed to create 'secondary' EPC interface\n");
560         return;
561     }
562 }
563564 static struct config_group *pci_epf_make(struct config_group *group,
565                      const char *name)
566 {
567     struct pci_epf_group *epf_group;
568     struct pci_epf *epf;
569     char *epf_name;
570     int index, err;
571
572     epf_group = kzalloc(sizeof(*epf_group), GFP_KERNEL);
573     if (!epf_group)
574         return ERR_PTR(-ENOMEM);
575
576     mutex_lock(&functions_mutex);
577     index = idr_alloc(&functions_idr, epf_group, 0, 0, GFP_KERNEL);
578     mutex_unlock(&functions_mutex);
579     if (index < 0) {
580         err = index;
581         goto free_group;
582     }
583
584     epf_group->index = index;
585
586     config_group_init_type_name(&epf_group->group, name, &pci_epf_type);
587
588     epf_name = kasprintf(GFP_KERNEL, "%s.%d",
589                  group->cg_item.ci_name, epf_group->index);
590     if (!epf_name) {
591         err = -ENOMEM;
592         goto remove_idr;
593     }
594
595     epf = pci_epf_create(epf_name);
596     if (IS_ERR(epf)) {
597         pr_err("failed to create endpoint function device\n");
598         err = -EINVAL;
599         goto free_name;
600     }
601
602     epf->group = &epf_group->group;
603     epf_group->epf = epf;
604
605     kfree(epf_name);
606
607     INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work);
608     queue_delayed_work(system_wq, &epf_group->cfs_work,
609                msecs_to_jiffies(1));
610
611     return &epf_group->group;
612
613 free_name:
614     kfree(epf_name);
615
616 remove_idr:
617     mutex_lock(&functions_mutex);
618     idr_remove(&functions_idr, epf_group->index);
619     mutex_unlock(&functions_mutex);
620
621 free_group:
622     kfree(epf_group);
623
624     return ERR_PTR(err);
625 }

再看一下pci_epf_create。447-472行,分配并初始化pci_epf,注意设备的名字是pci_epf_test,它通过和前面介绍的pci_epf_driver的id_table匹配来绑定驱动。所以当474行执行后就绑定了前面介绍的pci_epf_test驱动,然后驱动的probe会被调用。到这里epf设备和epf驱动就都添加上了并且绑定成功。

440 struct pci_epf *pci_epf_create(const char *name)
441 {
442     int ret;
443     struct pci_epf *epf;
444     struct device *dev;
445     int len;
446
447     epf = kzalloc(sizeof(*epf), GFP_KERNEL);
448     if (!epf)
449         return ERR_PTR(-ENOMEM);
450
451     len = strchrnul(name, '.') - name;
452     epf->name = kstrndup(name, len, GFP_KERNEL);
453     if (!epf->name) {
454         kfree(epf);
455         return ERR_PTR(-ENOMEM);
456     }
457
458     /* VFs are numbered starting with 1. So set BIT(0) by default */
459     epf->vfunction_num_map = 1;
460     INIT_LIST_HEAD(&epf->pci_vepf);
461
462     dev = &epf->dev;
463     device_initialize(dev);
464     dev->bus = &pci_epf_bus_type;
465     dev->type = &pci_epf_type;
466     mutex_init(&epf->lock);
467
468     ret = dev_set_name(dev, "%s", name);
469     if (ret) {
470         put_device(dev);
471         return ERR_PTR(ret);
472     }
473
474     ret = device_add(dev);
475     if (ret) {
476         put_device(dev);
477         return ERR_PTR(ret);
478     }
479
480     return epf;
481 }

然后配置pci_epf_test设备。

​# echo 0x104c > functions/pci_epf_test/func1/vendorid
# echo 0xb500 > functions/pci_epf_test/func1/deviceid
# echo 16 > functions/pci_epf_test/func1/msi_interrupts
# echo 8 > functions/pci_epf_test/func1/msix_interrupts

通过下面的回调将这些配置写道pci_epf里:

319 static ssize_t pci_epf_##_name##_show(struct config_item *item, char *page)    \
320 {                                          \
321     struct pci_epf *epf = to_pci_epf_group(item)->epf;             \
322     if (WARN_ON_ONCE(!epf->header))                        \
323         return -EINVAL;                            \
324     return sprintf(page, "0x%04x\n", epf->header->_name);              \
325 }
326
327 #define PCI_EPF_HEADER_W_u32(_name)                        \
328 static ssize_t pci_epf_##_name##_store(struct config_item *item,           \
329                        const char *page, size_t len)           \
330 {                                          \
331     u32 val;                                   \
332     int ret;                                   \
333     struct pci_epf *epf = to_pci_epf_group(item)->epf;             \
334     if (WARN_ON_ONCE(!epf->header))                        \
335         return -EINVAL;                            \
336     ret = kstrtou32(page, 0, &val);                        \
337     if (ret)                                   \
338         return ret;                            \
339     epf->header->_name = val;                          \
340     return len;                                \
341 }
342
343 #define PCI_EPF_HEADER_W_u16(_name)                        \
344 static ssize_t pci_epf_##_name##_store(struct config_item *item,           \
345                        const char *page, size_t len)           \
346 {                                          \
347     u16 val;                                   \
348     int ret;                                   \
349     struct pci_epf *epf = to_pci_epf_group(item)->epf;             \
350     if (WARN_ON_ONCE(!epf->header))                        \
351         return -EINVAL;                            \
352     ret = kstrtou16(page, 0, &val);                        \
353     if (ret)                                   \
354         return ret;                            \
355     epf->header->_name = val;                          \
356     return len;                                \
357 }
358
359 #define PCI_EPF_HEADER_W_u8(_name)                         \
360 static ssize_t pci_epf_##_name##_store(struct config_item *item,           \
361                        const char *page, size_t len)           \
362 {                                          \
363     u8 val;                                    \
364     int ret;                                   \
365     struct pci_epf *epf = to_pci_epf_group(item)->epf;             \
366     if (WARN_ON_ONCE(!epf->header))                        \
367         return -EINVAL;                            \
368     ret = kstrtou8(page, 0, &val);                         \
369     if (ret)                                   \
370         return ret;                            \
371     epf->header->_name = val;                          \
372     return len;                                \
373 }
374
375 static ssize_t pci_epf_msi_interrupts_store(struct config_item *item,
376                         const char *page, size_t len)
377 {
378     u8 val;
379     int ret;
380
381     ret = kstrtou8(page, 0, &val);
382     if (ret)
383         return ret;
384
385     to_pci_epf_group(item)->epf->msi_interrupts = val;
386
387     return len;
388 }
389
390 static ssize_t pci_epf_msi_interrupts_show(struct config_item *item,
391                        char *page)
392 {
393     return sprintf(page, "%d\n",
394                to_pci_epf_group(item)->epf->msi_interrupts);
395 }
396
397 static ssize_t pci_epf_msix_interrupts_store(struct config_item *item,
398                          const char *page, size_t len)
399 {
400     u16 val;
401     int ret;
402
403     ret = kstrtou16(page, 0, &val);
404     if (ret)
405         return ret;
406
407     to_pci_epf_group(item)->epf->msix_interrupts = val;
408
409     return len;
410 }
411
412 static ssize_t pci_epf_msix_interrupts_show(struct config_item *item,
413                         char *page)
414 {
415     return sprintf(page, "%d\n",
416                to_pci_epf_group(item)->epf->msix_interrupts);
417 }

然后将pci_epf绑定到一个pci_epc上。

ln -s functions/pci_epf_test/func1 controllers/51000000.pcie_ep/

前面介绍创建epc时,会调用pci_ep_cfs_add_epc_group在controllers下创建一个控制器对应的config_group,它的config_item_type就是pci_epc_type。当将epf绑定到epc时,pci_epc_epf_link就会被调用。

250 static struct configfs_item_operations pci_epc_item_ops = {
251     .allow_link = pci_epc_epf_link,
252     .drop_link  = pci_epc_epf_unlink,
253 };
254
255 static const struct config_item_type pci_epc_type = {
256     .ct_item_ops    = &pci_epc_item_ops,
257     .ct_attrs   = pci_epc_attrs,
258     .ct_owner   = THIS_MODULE,
259 };

221行,pci_epc_add_epf主要是为epf和epc建立关系,主要是将epf->epc指令为对应的epc,并将epf加到epc的链表epc->pci_epf里。225行,pci_epf_bind里主要是会调用到epf对应的pci_epf_driver的bind回调,对于pci_epf_test的driver就是pci_epf_test_bind,可以看一下前面的文章它都干了啥。

212 static int pci_epc_epf_link(struct config_item *epc_item,
213                 struct config_item *epf_item)
214 {
215     int ret;
216     struct pci_epf_group *epf_group = to_pci_epf_group(epf_item);
217     struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
218     struct pci_epc *epc = epc_group->epc;
219     struct pci_epf *epf = epf_group->epf;
220
221     ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE);
222     if (ret)
223         return ret;
224
225     ret = pci_epf_bind(epf);
226     if (ret) {
227         pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
228         return ret;
229     }
230
231     return 0;
232 }

经过前面的步骤,epc的配置空间其实已经配置好了,最后就是将它连接到host上。通过下面的命令完成。

# echo 1 > controllers/51000000.pcie_ep/start

上面的命令执行后会调用pci_epc_start_store,对于新思控制器它最终会调用dw_pcie_ep_start回调来建立连接。

168 static ssize_t pci_epc_start_store(struct config_item *item, const char *page,
169                    size_t len)
170 {
171     int ret;
172     bool start;
173     struct pci_epc *epc;
174     struct pci_epc_group *epc_group = to_pci_epc_group(item);
175
176     epc = epc_group->epc;
177
178     ret = kstrtobool(page, &start);
179     if (ret)
180         return ret;
181
182     if (!start) {
183         pci_epc_stop(epc);
184         epc_group->start = 0;
185         return len;
186     }
187
188     ret = pci_epc_start(epc);
189     if (ret) {
190         dev_err(&epc->dev, "failed to start endpoint controller\n");
191         return -EINVAL;
192     }
193
194     epc_group->start = start;
195
196     return len;
197 }

相关文章:

  • Excel常用公式大全
  • 4.文件管理(文本、日志、Excel表)
  • 技术干货 | DAC静态参数计算全解析:从偏移误差到总未调整误差
  • 【Go语言基础】对齐边界与内存填充
  • davinci本地启动
  • Network Manager客户端制作小结
  • http2与websocket关系
  • NY339NY341美光固态闪存NW841NW843
  • RAG 升级之路:如何让问答机器人真正“智能”起来
  • 【网工】华为配置专题进阶篇④
  • 合并两个有序链表C++
  • Unity3D仿星露谷物语开发67之创建新的NPC
  • 变幻莫测:CoreData 中 Transformable 类型面面俱到(五)
  • 学习笔记丨AR≠VR:透视沉浸式技术的“虚实象限”法则
  • 【Golang面试题】Go语言实现请求频率限制
  • 记录:注册k8s cluster账号
  • NumPy玩转数据科学
  • Apollo:配置中心使用与介绍
  • C++11 Thread-Local Storage:从入门到精通
  • dify本地部署及添加ollama模型(ubuntu24.04)
  • 网站开发后台需要自己写吗/宁波网站推广联系方式
  • 网站建立软件/手机管家一键优化
  • 宜春seo网站推广/长沙靠谱seo优化
  • 网站开发服务转包合同范本/怎么看百度指数
  • wordpress 缺点/seo视频教程我要自学网
  • 波波网站建设/如何快速搭建一个网站