From f05490d0e254ab5e79e90b8bd7d98f16863f183d Mon Sep 17 00:00:00 2001 From: Tyson Whitehead Date: Sat, 23 Mar 2024 14:10:28 -0400 Subject: [PATCH] udev: Fix O(n^2) device walking issue (closes #9092) I believe there was a O(n^2) device walking issues on startup - MaybeAddDevice gets called for every device at startup - MaybeAddDevice calls IsJoystick - IsJoystick calls SDL_UDEV_GetProductInfo - SDL_UDEV_GetProductInfo calls udev_enumerate_scan_devices - udev_enumerate_scan_devices walks all the devices Prior to commit 3b1e0e1 this was mostly masked as IsJoystick only called SDL_UDEV_GetProductInfo when a JSIOCGNAME ioctl was successful. This fixes the O(n^2) behaviour by directly getting the device via udev_device_new_from_devnum (based on type, major, and minor number) instead of enumerating everything via udev_enumerate_scan_devices and matching on name. (cherry picked from commit 6e1611cc779b223765f80352653cdd95aabc4b18) --- src/core/linux/SDL_udev.c | 89 +++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/src/core/linux/SDL_udev.c b/src/core/linux/SDL_udev.c index 9cb5345ca7..5bdfc0db81 100644 --- a/src/core/linux/SDL_udev.c +++ b/src/core/linux/SDL_udev.c @@ -31,6 +31,7 @@ #ifdef SDL_USE_LIBUDEV #include +#include #include "SDL_assert.h" #include "SDL_evdev_capabilities.h" @@ -227,61 +228,59 @@ void SDL_UDEV_Scan(void) SDL_bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version, int *class) { - struct udev_enumerate *enumerate = NULL; - struct udev_list_entry *devs = NULL; - struct udev_list_entry *item = NULL; - SDL_bool found = SDL_FALSE; + struct stat statbuf; + char type; + struct udev_device *dev; + const char* val; + int class_temp; if (!_this) { return SDL_FALSE; } - enumerate = _this->syms.udev_enumerate_new(_this->udev); - if (!enumerate) { - SDL_SetError("udev_enumerate_new() failed"); + if (stat(device_path, &statbuf) == -1) { return SDL_FALSE; } - _this->syms.udev_enumerate_scan_devices(enumerate); - devs = _this->syms.udev_enumerate_get_list_entry(enumerate); - for (item = devs; item && !found; item = _this->syms.udev_list_entry_get_next(item)) { - const char *path = _this->syms.udev_list_entry_get_name(item); - struct udev_device *dev = _this->syms.udev_device_new_from_syspath(_this->udev, path); - if (dev) { - const char *val = NULL; - const char *existing_path; - - existing_path = _this->syms.udev_device_get_devnode(dev); - if (existing_path && SDL_strcmp(device_path, existing_path) == 0) { - int class_temp; - found = SDL_TRUE; - - val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID"); - if (val) { - *vendor = (Uint16)SDL_strtol(val, NULL, 16); - } - - val = _this->syms.udev_device_get_property_value(dev, "ID_MODEL_ID"); - if (val) { - *product = (Uint16)SDL_strtol(val, NULL, 16); - } - - val = _this->syms.udev_device_get_property_value(dev, "ID_REVISION"); - if (val) { - *version = (Uint16)SDL_strtol(val, NULL, 16); - } - - class_temp = device_class(dev); - if (class_temp) { - *class = class_temp; - } - } - _this->syms.udev_device_unref(dev); - } + if (S_ISBLK(statbuf.st_mode)) { + type = 'b'; + } + else if (S_ISCHR(statbuf.st_mode)) { + type = 'c'; + } + else { + return SDL_FALSE; } - _this->syms.udev_enumerate_unref(enumerate); - return found; + dev = _this->syms.udev_device_new_from_devnum(_this->udev, type, statbuf.st_rdev); + + if (!dev) { + return SDL_FALSE; + } + + val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID"); + if (val) { + *vendor = (Uint16)SDL_strtol(val, NULL, 16); + } + + val = _this->syms.udev_device_get_property_value(dev, "ID_MODEL_ID"); + if (val) { + *product = (Uint16)SDL_strtol(val, NULL, 16); + } + + val = _this->syms.udev_device_get_property_value(dev, "ID_REVISION"); + if (val) { + *version = (Uint16)SDL_strtol(val, NULL, 16); + } + + class_temp = device_class(dev); + if (class_temp) { + *class = class_temp; + } + + _this->syms.udev_device_unref(dev); + + return SDL_TRUE; } void SDL_UDEV_UnloadLibrary(void)