Skip to content

provider: don't idle on single thread each device for multithread probe all devices#1459

Open
GermanAizek wants to merge 1 commit into
storaged-project:masterfrom
GermanAizek:mt-probe
Open

provider: don't idle on single thread each device for multithread probe all devices#1459
GermanAizek wants to merge 1 commit into
storaged-project:masterfrom
GermanAizek:mt-probe

Conversation

@GermanAizek
Copy link
Copy Markdown
Contributor

@tbzatek, I started contribute to your project first time, but probe is not very dependent and can be parallelized to go through process probing all block devices faster.

Copy link
Copy Markdown
Member

@tbzatek tbzatek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started contribute to your project first time

This is actually your fifth pull request, most of which I refused to merge due to excessive microoptimization. Anyway, this PR looks to be a good addition! For CDROM, ATA or NVMe devices where additional I/O is done as part of UDisksLinuxDevice probing this may significantly speedup multiple device uevents handling. For other uevents this brings additional overhead of creating new threads that perform very little to no tasks, but I think that should be okay.

I was also thinking about a compromise of a selective GTask submission for ATA and NVMe devices, however that would involve additional udev attr checks, bringing additional overhead for every uevent itself. So I think your approach is a good compromise for now.

That said, we have never done any real-world performance profiling other than scenarios like 5000 scsi_debug devices appearing at the same time or 10k LVs activated at once, but that doesn't really reflect reality.

Either way, the most important rule here is to always spawn the on_idle_with_probed_uevent() callback in the main thread since that's when UDisksObject creation starts and the code that follows is not thread safe. And that is unchanged and working fine with this proposal. So full paralelization like that is mostly impossible at the current state.

Comment thread src/udiskslinuxprovider.c
/* probe the device - this may take a while */
request->udisks_device = udisks_linux_device_new_sync (request->udev_device, provider->gudev_client);
{
probe_request_free (request);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

I make separate PR with this fix leak.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Included in #1472 keeping your credits

Comment thread src/udiskslinuxprovider.c

/* now that we've probed the device, post the request back to the main thread */
g_idle_add (on_idle_with_probed_uevent, request);
GTask *task = g_task_new (provider, NULL, on_probe_job_completed, NULL);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the variable declaration to the top of the function along to others.

Comment thread src/udiskslinuxprovider.c
probe_device_thread_func (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
{
ProbeRequest *request = task_data;
request->udisks_device = udisks_linux_device_new_sync (request->udev_device, udisks_linux_provider_get_udev_client(request->provider));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space before the opening parenthesis.

Comment thread src/udiskslinuxprovider.c
/* now that we've probed the device, post the request back to the main thread */
g_idle_add (on_idle_with_probed_uevent, request);
GTask *task = g_task_new (provider, NULL, on_probe_job_completed, NULL);
g_task_set_task_data (task, request, (GDestroyNotify) probe_request_free);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probe_request_free() is called within on_idle_with_probed_uevent(), this will actually result in double-free. I believe the destroy func is only called when replacing existing data, i.e. when this function is called twice and more, which is not the case here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probe_request_free() is called within on_idle_with_probed_uevent(), this will actually result in double-free. I believe the destroy func is only called when replacing existing data, i.e. when this function is called twice and more, which is not the case here.

Do you have any assumptions on how to get around double free, maybe do the check inside function?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pass NULL here as the thread func will always succeed.

@tbzatek
Copy link
Copy Markdown
Member

tbzatek commented Jan 30, 2026

Jenkins, ok to test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants