From: Bruce Richardson <bruce.richardson@intel.com>
To: dev@dpdk.org
Cc: Bruce Richardson <bruce.richardson@intel.com>
Subject: [PATCH] usertools/dpdk-devbind: improve query performance on BSD
Date: Mon, 29 Jun 2026 13:59:29 +0100 [thread overview]
Message-ID: <20260629125929.2331943-1-bruce.richardson@intel.com> (raw)
The time taken on FreeBSD to run the status command from dpdk-devbind
was exceptionally long. When run on a real system with lots of
devices, the original code took over 80 seconds in some cases to produce
the output of "dpdk-devbind.py -s". (Time on BSD VMs is quicker as they
tend not to have so many hardware devices, which is why this may not
have been noticed before.)
Rework the processing to avoid doing so many subprocess calls to
pciconf; use one call to get info on all devices rather than an
individual call per-device. On the same HW as before, it now takes a
fraction of a second, rather than >80 seconds.
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
usertools/dpdk-devbind.py | 111 ++++++++++++++++++++++++++++----------
1 file changed, 84 insertions(+), 27 deletions(-)
diff --git a/usertools/dpdk-devbind.py b/usertools/dpdk-devbind.py
index 93f2383dff..e72f238aba 100755
--- a/usertools/dpdk-devbind.py
+++ b/usertools/dpdk-devbind.py
@@ -111,6 +111,9 @@
# global dict ethernet devices present. Dictionary indexed by PCI address.
# Each device within this is itself a dictionary of device properties
devices = {}
+# for bsd, cache of all devices with ifconfig info merged in. populated once on first call.
+devices_cache = None
+
# list of supported DPDK drivers
dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"] if is_linux else ["nic_uio"]
# list of currently loaded kernel modules
@@ -220,7 +223,10 @@ def get_pci_device_details(dev_id, probe_lspci):
def clear_data():
'''This function clears any old data'''
global devices
+ global devices_cache
devices = {}
+ if is_bsd:
+ devices_cache = None
def get_basic_devinfo_linux(devices_type):
@@ -253,38 +259,89 @@ def get_basic_devinfo_linux(devices_type):
dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
.rstrip("]").lstrip("[")
+def init_devices_cache_bsd():
+ '''Initialize the devices cache by parsing pciconf -lv and ifconfig -a once.
+ Returns a list of device dictionaries with ifconfig data merged in.'''
+ global devices_cache
+
+ if devices_cache is not None:
+ return devices_cache
+
+ # Get all PCI device info in one call
+ pciconf_output = subprocess.check_output(["pciconf", "-lv"]).decode("utf8")
+ # Get all network interface info in one call
+ ifconfig_output = subprocess.check_output(["ifconfig", "-a"]).decode("utf8").splitlines()
+
+ devices_cache = []
+ current_dev = None
+
+ # Parse pciconf -lv output
+ for line in pciconf_output.splitlines():
+ # Main device line: none0@pci0:0:0:0: class=0x088000 rev=0x04 hdr=0x00 vendor=0x8086 device=0x09a2
+ if line and '@pci' in line:
+ # Save previous device if any
+ if current_dev:
+ devices_cache.append(current_dev)
+
+ # Parse slot/address from first part
+ name_addr, fields = line.split(maxsplit=1)
+ devname, addr = name_addr.split('@')
+ # Remove trailing colon from address
+ addr = addr.rstrip(':')
+
+ current_dev = {}
+ current_dev["Slot"] = addr
+ current_dev["Slot_str"] = addr
+
+ # Extract driver base name from device name (e.g., "nic_uio2" -> "nic_uio", "ixl0" -> "ixl")
+ # Device name format is like: "nic_uio2", "ioat0", "em0", "ixl1", "none0", etc.
+ current_dev["Driver_str"] = devname.rstrip('0123456789')
+
+ # Parse class/vendor/device from rest of line
+ fields_list = fields.split()
+ for field in fields_list:
+ if '=' in field:
+ name, value = field.split("=")
+ name = name.title()
+ if name.startswith("Sub"):
+ name = "S" + name[3:].title()
+ # For class code, extract only first 2 hex digits (class part)
+ if name == "Class" and value.startswith("0x") and len(value) > 4:
+ current_dev[name + "_str"] = value
+ current_dev[name] = value[2:4] # Extract just class code (e.g., "02" from "0x020000")
+ else:
+ current_dev[name + "_str"] = value
+ current_dev[name] = value[2:] if value.startswith("0x") else value
+
+ # Check if this is a network device and attach interface info
+ if "Class" in current_dev:
+ class_code = current_dev.get("Class", "")
+ if class_code == "02": # Network controller
+ iflines = [ln for ln in ifconfig_output if ln.startswith(f"{devname}:")]
+ if iflines:
+ current_dev["Interface"] = devname
+
+ # Device description line: device = 'Intel 82576 Gigabit Network Connection'
+ elif current_dev and "device" in line.lower() and "=" in line:
+ current_dev["Device_str"] = line.split("=")[1].strip().strip("'")
+
+ # Add the last device
+ if current_dev:
+ devices_cache.append(current_dev)
+
+ return devices_cache
def get_basic_devinfo_bsd(devices_type):
+ '''Filter the pre-cached device list by device type and populate devices dict.'''
global devices
- dev_lines = subprocess.check_output(["pciconf", "-l"]).decode("utf8").splitlines()
- netifs = subprocess.check_output(["ifconfig", "-a"]).decode("utf8").splitlines()
- for dev_line in dev_lines:
- dev = {}
- name_addr, fields = dev_line.split(maxsplit=1)
- devname, addr = name_addr.split('@')
- dev["Slot"] = addr
- dev["Slot_str"] = addr
- dev["Driver_str"] = devname[:-1]
- if devices_type == network_devices:
- iflines = [ln for ln in netifs if ln.startswith(f"{devname}:")]
- if iflines:
- dev["Interface"] = devname
- fields = fields.split()
- for field in fields:
- name, value = field.split("=")
- name = name.title()
- if name.startswith("Sub"):
- name = "S" + name[3:].title()
- dev[name + "_str"] = value
- dev[name] = value[2:] if value.startswith("0x") else value
- # explicitly query the device name string
- devdetails = subprocess.check_output(["pciconf", "-lv", addr]).decode("utf8")
- for devline in devdetails.splitlines():
- if devline.lstrip().startswith("device") and "=" in devline:
- dev["Device_str"] = devline.split("=")[1].strip().strip("'")
+ # Initialize cache on first call (subsequent calls return cached data)
+ all_devices = init_devices_cache_bsd()
+
+ # Filter by device type and add to devices dict
+ for dev in all_devices:
if device_type_match(dev, devices_type):
- devices[addr] = dict(dev)
+ devices[dev["Slot"]] = dict(dev)
def get_device_details(devices_type):
--
2.53.0
reply other threads:[~2026-06-29 12:59 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260629125929.2331943-1-bruce.richardson@intel.com \
--to=bruce.richardson@intel.com \
--cc=dev@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox