저는 여러 개의 PCIe 데이터 수집 보드를 호스팅하는 Linux 서버 기반 시스템을 개발 중입니다. 각 보드에는 확장된 PCIe 구성 공간에 고유한 장치 일련 번호가 있습니다. 이러한 카드는 UIO 장치로 처리됩니다. 불행히도 보드가 때때로 무작위 순서로 나열되는 것 같습니다. 시스템이 올바르게 작동하려면 각 보드와 영구적으로 연결된 이름을 가진 기호 링크를 생성해야 합니다.
자연스러운 해결책은 udev를 사용하는 것입니다(예:http://reactivated.net/writing_udev_rules.html). 그러나 udev는 장치 속성 간 장치 일련 번호 기능을 제공하지 않습니다. 다음 명령을 사용하여 테스트할 수 있습니다.
#udevadm info --attribute-walk /dev/uio0
[...]
looking at device '/devices/pci0000:00/0000:00:03.0/uio/uio0':
KERNEL=="uio0"
SUBSYSTEM=="uio"
DRIVER==""
ATTR{event}=="0"
ATTR{name}=="uio_pci_generic"
ATTR{power/async}=="disabled"
ATTR{power/control}=="auto"
ATTR{power/runtime_active_kids}=="0"
ATTR{power/runtime_active_time}=="0"
ATTR{power/runtime_enabled}=="disabled"
ATTR{power/runtime_status}=="unsupported"
ATTR{power/runtime_suspended_time}=="0"
ATTR{power/runtime_usage}=="0"
ATTR{version}=="0.01.0"
looking at parent device '/devices/pci0000:00/0000:00:03.0':
KERNELS=="0000:00:03.0"
SUBSYSTEMS=="pci"
DRIVERS=="uio_pci_generic"
ATTRS{ari_enabled}=="0"
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x00ff00"
ATTRS{consistent_dma_mask_bits}=="32"
ATTRS{current_link_speed}=="Unknown"
ATTRS{current_link_width}=="0"
ATTRS{d3cold_allowed}=="0"
ATTRS{device}=="0x3342"
ATTRS{dma_mask_bits}=="32"
ATTRS{driver_override}=="(null)"
ATTRS{enable}=="1"
ATTRS{irq}=="23"
ATTRS{local_cpulist}=="0"
ATTRS{local_cpus}=="1"
ATTRS{max_link_speed}=="Unknown"
ATTRS{max_link_width}=="255"
ATTRS{msi_bus}=="1"
ATTRS{numa_node}=="-1"
ATTRS{power/async}=="enabled"
ATTRS{power/control}=="on"
ATTRS{power/runtime_active_kids}=="0"
ATTRS{power/runtime_active_time}=="696133"
ATTRS{power/runtime_enabled}=="forbidden"
ATTRS{power/runtime_status}=="active"
ATTRS{power/runtime_suspended_time}=="0"
ATTRS{power/runtime_usage}=="2"
ATTRS{revision}=="0x00"
ATTRS{subsystem_device}=="0x1100"
ATTRS{subsystem_vendor}=="0x1af4"
ATTRS{vendor}=="0xabba"
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
ATTRS{power/async}=="enabled"
ATTRS{power/control}=="auto"
ATTRS{power/runtime_active_kids}=="7"
ATTRS{power/runtime_active_time}=="0"
ATTRS{power/runtime_enabled}=="disabled"
ATTRS{power/runtime_status}=="unsupported"
ATTRS{power/runtime_suspended_time}=="0"
ATTRS{power/runtime_usage}=="0"
udevadm
연관된 상위 PCIe 장치 가 발견되었지만 해당 장치 일련 번호가 표시되지 않습니다.
DSN은 lspci를 사용하여 읽을 수 있습니다.
# lspci -vvv -s 0000:00:03.0
[...]
Capabilities: [100 v1] Device Serial Number 12-34-56-78-90-ab-cd-ef
[...]
이를 바탕으로 나는 작동하는 솔루션을 만들었습니다. 첫 번째 부분은 파일에 저장된 udev 규칙입니다 /etc/udev/rules.d/30-daq.rules
.
SUBSYSTEM=="uio" PROGRAM="/opt/bin/uio_namer" SYMLINK="%c"
PCIe 보드의 DSN은 스크립트를 통해 찾을 수 있으며 /opt/bin/uio_namer
이는 솔루션의 두 번째 부분입니다.
#!/bin/sh
DEVID=abba:3342
SLOTNAME=`basename \`readlink /sys/${DEVPATH}/device\``
DLSPCI=`lspci -vvv -s ${SLOTNAME}`
#Check if it is our device
if [ -z "`echo ${DLSPCI} | grep Device | grep ${DEVID}`" ] ; then
# It is UIO device associated with other PCIe device
# don't create the symlink for it
exit 1
fi
DSNCAP=`lspci -vvv -s ${SLOTNAME} | grep "Device Serial Number" `
echo daq/${DSNCAP##*" "}
또한 스크립트는 다른 PCIe 보드용으로 생성된 UIO를 필터링합니다. "우리" 보드의 경우 장치 파일에 대한 심볼릭 링크가 보드의 일련 번호로 /dev/daq 디렉토리에 생성됩니다. 예를 들어:/dev/daq/12-34-56-78-90-ab-cd-ef
이 솔루션은 PCIe DAQ 보드 모델이 추가된 QEMU에서 테스트되었습니다. 이것은 일이다. 그러나 나는 이것이 최적이 아니라고 생각하며 여기에 문제가 있습니다.
- udev 규칙에서 상위 장치의 속성에 액세스할 수 있는 방법이 있습니까?
- 내 솔루션은 이에 의존합니다
lspci -vvv
. 형식을 변경하면 솔루션이 더 이상 작동하지 않습니다. "기계 판독 가능 출력"에 "-m" 옵션을 사용하면 DSN이 인쇄되지 않으므로 도움이 되지 않습니다. PCIe 장치의 DSN을 안정적인 형식으로 안정적으로 출력할 수 있는 유틸리티가 있습니까?
답변1
두 번째 질문에 대한 답도 찾았습니다. PCIe 장치의 일련 번호를 표시하는 간단한 애플리케이션을 만들었습니다. lspci
도우미 스크립트의 호출을 대체하는 데 사용할 수 있습니다 . 또한 lspci
이전 솔루션을 유효하지 않게 만드는 출력 형식의 변경 위험을 제거합니다. 원천:
/*
Simple application displaying the PCIe Device Serial Number
Written by Wojciech M. Zabolotny ([email protected] or [email protected])
2021.05.23
This code is heavily based on the examples provided in the pciutils package
https://github.com/pciutils/pciutils therefore I release it under the same
license: GNU GENERAL PUBLIC LICENSE Version 2, June 1991
You may compile it with:
gcc -o pcidsn pcidsn.c -lpci
*/
#include <stdio.h>
#include <pci/pci.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
void __attribute__((noreturn))
die(char *msg, ...)
{
va_list args;
va_start(args, msg);
fprintf(stderr, "pcisdn: ");
vfprintf(stderr, msg, args);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char * argv[])
{
struct pci_access *pacc;
struct pci_filter filter;
struct pci_dev *dev;
struct pci_cap *pdsn;
unsigned int c;
char namebuf[1024], *name;
char * msg = NULL;
pacc = pci_alloc(); /* Get the pci_access structure */
pacc->error = die;
pci_filter_init(pacc,&filter);
if (msg=pci_filter_parse_slot(&filter,argv[1])) {
die("Wrong slot definition: %s",msg);
};
pci_init(pacc); /* Initialize the PCI library */
pci_scan_bus(pacc); /* We want to get the list of devices */
for (dev=pacc->devices; dev; dev=dev->next) /* Iterate over all devices */
{
if(pci_filter_match(&filter,dev)) { /* Select only our slot */
pdsn = pci_find_cap(dev, PCI_EXT_CAP_ID_DSN, PCI_CAP_EXTENDED);
if(pdsn) {
uint32_t lw = pci_read_long(dev, pdsn->addr+4);
uint32_t hw = pci_read_long(dev, pdsn->addr+8);
printf("%8.8lx%8.8lx\n",hw,lw);
}
}
}
pci_cleanup(pacc); /* Close everything */
return 0;
}
다음과 같이 컴파일할 수 있습니다.
gcc -o pcidsn pcidsn.c -lpci
생성된 애플리케이션은 시스템 경로를 통해 사용할 수 있어야 합니다. /opt/bin/uio_namer
그런 다음 도우미 스크립트를 다음과 같이 수정해야 합니다.
#!/bin/sh
set -e
SLOTNAME=`basename \`readlink /sys/${DEVPATH}/device\``
DSNCAP=`pcidsn ${SLOTNAME}`
echo daq/${DSNCAP##*" "}
udev 규칙은 /etc/udev/rules.d/30-daq.rules
다음과 같이 설명될 수 있습니다.
SUBSYSTEM=="uio" ACTION=="add" ATTRS{vendor}=="0xabba" ATTRS{device}=="0x3342" PROGRAM="/opt/bin/uio_namer" SYMLINK="%c"
답변2
첫 번째 질문에 대한 답을 찾았습니다. 존재하다http://reactivated.net/writing_udev_rules.html다음과 같은 말이 있습니다.
ATTRS - 장치의 sysfs 속성 또는 상위 장치의 sysfs 속성과 일치합니다.
따라서 PCI 공급업체 및 장치 속성에 대한 규칙도 상위 장치와 일치할 수 있습니다.
udev 규칙은 /etc/udev/rules.d/30-daq.rules
다음과 같이 수정될 수 있습니다:
SUBSYSTEM=="uio" ACTION=="add" ATTRS{vendor}=="0xabba" ATTRS{device}=="0x3342" PROGRAM="/opt/bin/uio_namer" SYMLINK="%c"
도우미 스크립트는 /opt/bin/uio_namer
다음과 같이 단순화될 수 있습니다.
#!/bin/sh
SLOTNAME=`basename \`readlink /sys/${DEVPATH}/device\``
DSNCAP=`lspci -vvv -s ${SLOTNAME} | grep "Device Serial Number" `
echo daq/${DSNCAP##*" "}