Skip to content

Commit

Permalink
udev-node: optimize device node symlink creation
Browse files Browse the repository at this point in the history
If multiple devices requested the same device node symlink with the same
priority, then previously we read O(N^2) of files saved in
/run/udev/links.

This makes if the requested symlink already exists with equal or higher
priority, then the symlink is kept, and skip to read all existing files,
except for one related to the current device node, in /run/udev/links.
Hence, the total amount of file read becomes O(N).

This improves performance of testcase_simultaneous_events_2 added by the
previous commit about 30%.
Before (32.8 sec):
```
 ## 3 iterations start: 11:13:44.690953163
 ## 3 iterations end: 11:14:17.493974927
```
After (23.8 sec):
```
 ## 3 iterations start: 11:17:53.869938387
 ## 3 iterations end: 11:18:17.624268345
```

This is based on the idea and analysis by Franck Bui.

Replaces #25839.

Co-authored-by: Franck Bui <[email protected]>
(cherry picked from commit 331aa7a)

Resolves: RHEL-33815
  • Loading branch information
yuwata authored and jamacku committed Dec 20, 2024
1 parent cf23250 commit 623d77b
Showing 1 changed file with 88 additions and 2 deletions.
90 changes: 88 additions & 2 deletions src/udev/udev-node.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,42 @@ static int stack_directory_open(sd_device *dev, const char *slink, int *ret_dirf
return 0;
}

static int node_get_current(const char *slink, int dirfd, char **ret_id, int *ret_prio) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
_cleanup_free_ char *id_dup = NULL;
const char *id;
int r;

assert(slink);
assert(dirfd >= 0);
assert(ret_id);

r = sd_device_new_from_devname(&dev, slink);
if (r < 0)
return r;

r = device_get_device_id(dev, &id);
if (r < 0)
return r;

id_dup = strdup(id);
if (!id_dup)
return -ENOMEM;

if (ret_prio) {
r = stack_directory_read_one(dirfd, id, NULL, ret_prio);
if (r < 0)
return r;
}

*ret_id = TAKE_PTR(id_dup);
return 0;
}

static int link_update(sd_device *dev, const char *slink, bool add) {
_cleanup_free_ char *current_id = NULL, *devnode = NULL;
_cleanup_close_ int dirfd = -EBADF, lockfd = -EBADF;
_cleanup_free_ char *devnode = NULL;
int r;
int r, current_prio;

assert(dev);
assert(slink);
Expand All @@ -395,10 +427,64 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
if (r < 0)
return r;

r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL);
if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);

r = stack_directory_update(dev, dirfd, add);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to update stack directory for '%s': %m", slink);

if (current_id) {
const char *id;

r = device_get_device_id(dev, &id);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get device id: %m");

if (add) {
int prio;

r = device_get_devlink_priority(dev, &prio);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get devlink priority: %m");

if (streq(current_id, id)) {
if (current_prio <= prio)
/* The devlink is ours and already exists, and the new priority is
* equal or higher than the previous. Hence, it is not necessary to
* recreate it. */
return 0;

/* The devlink priority is downgraded. Another device may have a higher
* priority now. Let's find the device node with the highest priority. */
} else {
if (current_prio >= prio)
/* The devlink with equal or higher priority already exists and is
* owned by another device. Hence, it is not necessary to recreate it. */
return 0;

/* This device has a higher priority than the current. Let's create the
* devlink to our device node. */
return node_symlink(dev, NULL, slink);
}

} else {
if (!streq(current_id, id))
/* The devlink already exists and is owned by another device. Hence, it is
* not necessary to recreate it. */
return 0;

/* The current devlink is ours, and the target device will be removed. Hence, we need
* to search the device that has the highest priority. and update the devlink. */
}
} else {
/* The requested devlink does not exist, or the target device does not exist and the devlink
* points to a non-existing device. Let's search the deivce that has the highest priority,
* and update the devlink. */
;
}

r = stack_directory_find_prioritized_devnode(dev, dirfd, add, &devnode);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to determine device node with the highest priority for '%s': %m", slink);
Expand Down

0 comments on commit 623d77b

Please sign in to comment.