Source string Read only

(itstool) path: sect2/para
Context English State
The filesystem converts the request into a struct bio instance and passes it to the GEOM subsystem. It knows what geom instance should handle it because filesystems are hosted directly on a geom instance.
The request ends up as a call to the <function>.start</function>() function made on the g_down thread and reaches the top-level geom instance.
This top-level geom instance (for example the partition slicer) determines that the request should be routed to a lower-level instance (for example the disk driver). It makes a copy of the bio request (bio requests <emphasis>ALWAYS</emphasis> need to be copied between instances, with <function>g_clone_bio</function>()!), modifies the data offset and target provider fields and executes the copy with <function>g_io_request</function>()
The disk driver gets the bio request also as a call to <function>.start</function>() on the <literal>g_down</literal> thread. It talks to hardware, gets the data back, and calls <function>g_io_deliver</function>() on the bio.
Now, the notification of bio completion <quote>bubbles up</quote> in the <literal>g_up</literal> thread. First the partition slicer gets <function>.done</function>() called in the <literal>g_up</literal> thread, it uses information stored in the bio to free the cloned <varname remap="structname">bio</varname> structure (with <function>g_destroy_bio</function>()) and calls <function>g_io_deliver</function>() on the original request.
The filesystem gets the data and transfers it to userland.
See <citerefentry><refentrytitle>g_bio</refentrytitle><manvolnum>9</manvolnum></citerefentry> man page for information how the data is passed back and forth in the <varname remap="structname">bio</varname> structure (note in particular the <varname>bio_parent</varname> and <varname>bio_children</varname> fields and how they are handled).
One important feature is: <emphasis>THERE CAN BE NO SLEEPING IN G_UP AND G_DOWN THREADS</emphasis>. This means that none of the following things can be done in those threads (the list is of course not complete, but only informative):
Calls to <function>msleep</function>() and <function>tsleep</function>(), obviously.
Calls to <function>g_write_data</function>() and <function>g_read_data</function>(), because these sleep between passing the data to consumers and returning.
Waiting for I/O.
Calls to <citerefentry><refentrytitle>malloc</refentrytitle><manvolnum>9</manvolnum></citerefentry> and <function>uma_zalloc</function>() with <varname>M_WAITOK</varname> flag set
sx and other sleepable locks
This restriction is here to stop GEOM code clogging the I/O request path, since sleeping is usually not time-bound and there can be no guarantees on how long will it take (there are some other, more technical reasons also). It also means that there is not much that can be done in those threads; for example, almost any complex thing requires memory allocation. Fortunately, there is a way out: creating additional kernel threads.
Kernel Threads for Use in GEOM Code
Kernel threads are created with <citerefentry><refentrytitle>kthread_create</refentrytitle><manvolnum>9</manvolnum></citerefentry> function, and they are sort of similar to userland threads in behavior, only they cannot return to caller to signify termination, but must call <citerefentry><refentrytitle>kthread_exit</refentrytitle><manvolnum>9</manvolnum></citerefentry>.
In GEOM code, the usual use of threads is to offload processing of requests from <literal>g_down</literal> thread (the <function>.start</function>() function). These threads look like <quote>event handlers</quote>: they have a linked list of event associated with them (on which events can be posted by various functions in various threads so it must be protected by a mutex), take the events from the list one by one and process them in a big <literal>switch</literal>() statement.
The main benefit of using a thread to handle I/O requests is that it can sleep when needed. Now, this sounds good, but should be carefully thought out. Sleeping is well and very convenient but can very effectively destroy performance of the geom transformation. Extremely performance-sensitive classes probably should do all the work in <function>.start</function>() function call, taking great care to handle out-of-memory and similar errors.
The other benefit of having a event-handler thread like that is to serialize all the requests and responses coming from different geom threads into one thread. This is also very convenient but can be slow. In most cases, handling of <function>.done</function>() requests can be left to the <literal>g_up</literal> thread.
Mutexes in FreeBSD kernel (see <citerefentry><refentrytitle>mutex</refentrytitle><manvolnum>9</manvolnum></citerefentry>) have one distinction from their more common userland cousins — the code cannot sleep while holding a mutex). If the code needs to sleep a lot, <citerefentry><refentrytitle>sx</refentrytitle><manvolnum>9</manvolnum></citerefentry> locks may be more appropriate. On the other hand, if you do almost everything in a single thread, you may get away with no mutexes at all.


No matching activity found.

Browse all component changes


English English
No related strings found in the glossary.

Source information

Source string comment
(itstool) path: sect2/para
Source string location
String age
a year ago
Source string age
a year ago
Translation file
articles/geom-class.pot, string 145