私は複数の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
これはソリューションの2番目の部分です。
#!/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
2番目の質問に対する答えも見つかりました。 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"