PCIe UIO 장치에 대한 일련 번호 기반 심볼릭 링크를 생성하는 방법은 무엇입니까?

PCIe UIO 장치에 대한 일련 번호 기반 심볼릭 링크를 생성하는 방법은 무엇입니까?

저는 여러 개의 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에서 테스트되었습니다. 이것은 일이다. 그러나 나는 이것이 최적이 아니라고 생각하며 여기에 문제가 있습니다.

  1. udev 규칙에서 상위 장치의 속성에 액세스할 수 있는 방법이 있습니까?
  2. 내 솔루션은 이에 의존합니다 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##*" "}

관련 정보