| .. SPDX-License-Identifier: GPL-2.0+ |
| |
| =========== |
| Folio Queue |
| =========== |
| |
| :Author: David Howells <dhowells@redhat.com> |
| |
| .. Contents: |
| |
| * Overview |
| * Initialisation |
| * Adding and removing folios |
| * Querying information about a folio |
| * Querying information about a folio_queue |
| * Folio queue iteration |
| * Folio marks |
| * Lockless simultaneous production/consumption issues |
| |
| |
| Overview |
| ======== |
| |
| The folio_queue struct forms a single segment in a segmented list of folios |
| that can be used to form an I/O buffer. As such, the list can be iterated over |
| using the ITER_FOLIOQ iov_iter type. |
| |
| The publicly accessible members of the structure are:: |
| |
| struct folio_queue { |
| struct folio_queue *next; |
| struct folio_queue *prev; |
| ... |
| }; |
| |
| A pair of pointers are provided, ``next`` and ``prev``, that point to the |
| segments on either side of the segment being accessed. Whilst this is a |
| doubly-linked list, it is intentionally not a circular list; the outward |
| sibling pointers in terminal segments should be NULL. |
| |
| Each segment in the list also stores: |
| |
| * an ordered sequence of folio pointers, |
| * the size of each folio and |
| * three 1-bit marks per folio, |
| |
| but hese should not be accessed directly as the underlying data structure may |
| change, but rather the access functions outlined below should be used. |
| |
| The facility can be made accessible by:: |
| |
| #include <linux/folio_queue.h> |
| |
| and to use the iterator:: |
| |
| #include <linux/uio.h> |
| |
| |
| Initialisation |
| ============== |
| |
| A segment should be initialised by calling:: |
| |
| void folioq_init(struct folio_queue *folioq); |
| |
| with a pointer to the segment to be initialised. Note that this will not |
| necessarily initialise all the folio pointers, so care must be taken to check |
| the number of folios added. |
| |
| |
| Adding and removing folios |
| ========================== |
| |
| Folios can be set in the next unused slot in a segment struct by calling one |
| of:: |
| |
| unsigned int folioq_append(struct folio_queue *folioq, |
| struct folio *folio); |
| |
| unsigned int folioq_append_mark(struct folio_queue *folioq, |
| struct folio *folio); |
| |
| Both functions update the stored folio count, store the folio and note its |
| size. The second function also sets the first mark for the folio added. Both |
| functions return the number of the slot used. [!] Note that no attempt is made |
| to check that the capacity wasn't overrun and the list will not be extended |
| automatically. |
| |
| A folio can be excised by calling:: |
| |
| void folioq_clear(struct folio_queue *folioq, unsigned int slot); |
| |
| This clears the slot in the array and also clears all the marks for that folio, |
| but doesn't change the folio count - so future accesses of that slot must check |
| if the slot is occupied. |
| |
| |
| Querying information about a folio |
| ================================== |
| |
| Information about the folio in a particular slot may be queried by the |
| following function:: |
| |
| struct folio *folioq_folio(const struct folio_queue *folioq, |
| unsigned int slot); |
| |
| If a folio has not yet been set in that slot, this may yield an undefined |
| pointer. The size of the folio in a slot may be queried with either of:: |
| |
| unsigned int folioq_folio_order(const struct folio_queue *folioq, |
| unsigned int slot); |
| |
| size_t folioq_folio_size(const struct folio_queue *folioq, |
| unsigned int slot); |
| |
| The first function returns the size as an order and the second as a number of |
| bytes. |
| |
| |
| Querying information about a folio_queue |
| ======================================== |
| |
| Information may be retrieved about a particular segment with the following |
| functions:: |
| |
| unsigned int folioq_nr_slots(const struct folio_queue *folioq); |
| |
| unsigned int folioq_count(struct folio_queue *folioq); |
| |
| bool folioq_full(struct folio_queue *folioq); |
| |
| The first function returns the maximum capacity of a segment. It must not be |
| assumed that this won't vary between segments. The second returns the number |
| of folios added to a segments and the third is a shorthand to indicate if the |
| segment has been filled to capacity. |
| |
| Not that the count and fullness are not affected by clearing folios from the |
| segment. These are more about indicating how many slots in the array have been |
| initialised, and it assumed that slots won't get reused, but rather the segment |
| will get discarded as the queue is consumed. |
| |
| |
| Folio marks |
| =========== |
| |
| Folios within a queue can also have marks assigned to them. These marks can be |
| used to note information such as if a folio needs folio_put() calling upon it. |
| There are three marks available to be set for each folio. |
| |
| The marks can be set by:: |
| |
| void folioq_mark(struct folio_queue *folioq, unsigned int slot); |
| void folioq_mark2(struct folio_queue *folioq, unsigned int slot); |
| void folioq_mark3(struct folio_queue *folioq, unsigned int slot); |
| |
| Cleared by:: |
| |
| void folioq_unmark(struct folio_queue *folioq, unsigned int slot); |
| void folioq_unmark2(struct folio_queue *folioq, unsigned int slot); |
| void folioq_unmark3(struct folio_queue *folioq, unsigned int slot); |
| |
| And the marks can be queried by:: |
| |
| bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot); |
| bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot); |
| bool folioq_is_marked3(const struct folio_queue *folioq, unsigned int slot); |
| |
| The marks can be used for any purpose and are not interpreted by this API. |
| |
| |
| Folio queue iteration |
| ===================== |
| |
| A list of segments may be iterated over using the I/O iterator facility using |
| an ``iov_iter`` iterator of ``ITER_FOLIOQ`` type. The iterator may be |
| initialised with:: |
| |
| void iov_iter_folio_queue(struct iov_iter *i, unsigned int direction, |
| const struct folio_queue *folioq, |
| unsigned int first_slot, unsigned int offset, |
| size_t count); |
| |
| This may be told to start at a particular segment, slot and offset within a |
| queue. The iov iterator functions will follow the next pointers when advancing |
| and prev pointers when reverting when needed. |
| |
| |
| Lockless simultaneous production/consumption issues |
| =================================================== |
| |
| If properly managed, the list can be extended by the producer at the head end |
| and shortened by the consumer at the tail end simultaneously without the need |
| to take locks. The ITER_FOLIOQ iterator inserts appropriate barriers to aid |
| with this. |
| |
| Care must be taken when simultaneously producing and consuming a list. If the |
| last segment is reached and the folios it refers to are entirely consumed by |
| the IOV iterators, an iov_iter struct will be left pointing to the last segment |
| with a slot number equal to the capacity of that segment. The iterator will |
| try to continue on from this if there's another segment available when it is |
| used again, but care must be taken lest the segment got removed and freed by |
| the consumer before the iterator was advanced. |
| |
| It is recommended that the queue always contain at least one segment, even if |
| that segment has never been filled or is entirely spent. This prevents the |
| head and tail pointers from collapsing. |
| |
| |
| API Function Reference |
| ====================== |
| |
| .. kernel-doc:: include/linux/folio_queue.h |