xref: /linux-6.15/include/linux/folio_queue.h (revision aabcabf2)
1db0aa2e9SDavid Howells /* SPDX-License-Identifier: GPL-2.0-or-later */
2db0aa2e9SDavid Howells /* Queue of folios definitions
3db0aa2e9SDavid Howells  *
4db0aa2e9SDavid Howells  * Copyright (C) 2024 Red Hat, Inc. All Rights Reserved.
5db0aa2e9SDavid Howells  * Written by David Howells ([email protected])
628e8c5c0SDavid Howells  *
728e8c5c0SDavid Howells  * See:
828e8c5c0SDavid Howells  *
928e8c5c0SDavid Howells  *	Documentation/core-api/folio_queue.rst
1028e8c5c0SDavid Howells  *
1128e8c5c0SDavid Howells  * for a description of the API.
12db0aa2e9SDavid Howells  */
13db0aa2e9SDavid Howells 
14db0aa2e9SDavid Howells #ifndef _LINUX_FOLIO_QUEUE_H
15db0aa2e9SDavid Howells #define _LINUX_FOLIO_QUEUE_H
16db0aa2e9SDavid Howells 
17db0aa2e9SDavid Howells #include <linux/pagevec.h>
18db0aa2e9SDavid Howells 
19db0aa2e9SDavid Howells /*
20db0aa2e9SDavid Howells  * Segment in a queue of running buffers.  Each segment can hold a number of
21db0aa2e9SDavid Howells  * folios and a portion of the queue can be referenced with the ITER_FOLIOQ
22db0aa2e9SDavid Howells  * iterator.  The possibility exists of inserting non-folio elements into the
23db0aa2e9SDavid Howells  * queue (such as gaps).
24db0aa2e9SDavid Howells  *
25db0aa2e9SDavid Howells  * Explicit prev and next pointers are used instead of a list_head to make it
26db0aa2e9SDavid Howells  * easier to add segments to tail and remove them from the head without the
27db0aa2e9SDavid Howells  * need for a lock.
28db0aa2e9SDavid Howells  */
29db0aa2e9SDavid Howells struct folio_queue {
30db0aa2e9SDavid Howells 	struct folio_batch	vec;		/* Folios in the queue segment */
31db0aa2e9SDavid Howells 	u8			orders[PAGEVEC_SIZE]; /* Order of each folio */
32db0aa2e9SDavid Howells 	struct folio_queue	*next;		/* Next queue segment or NULL */
33db0aa2e9SDavid Howells 	struct folio_queue	*prev;		/* Previous queue segment of NULL */
34db0aa2e9SDavid Howells 	unsigned long		marks;		/* 1-bit mark per folio */
35db0aa2e9SDavid Howells 	unsigned long		marks2;		/* Second 1-bit mark per folio */
36ee4cdf7bSDavid Howells 	unsigned long		marks3;		/* Third 1-bit mark per folio */
37db0aa2e9SDavid Howells #if PAGEVEC_SIZE > BITS_PER_LONG
38db0aa2e9SDavid Howells #error marks is not big enough
39db0aa2e9SDavid Howells #endif
40*aabcabf2SDavid Howells 	unsigned int		rreq_id;
41*aabcabf2SDavid Howells 	unsigned int		debug_id;
42db0aa2e9SDavid Howells };
43db0aa2e9SDavid Howells 
4428e8c5c0SDavid Howells /**
4528e8c5c0SDavid Howells  * folioq_init - Initialise a folio queue segment
4628e8c5c0SDavid Howells  * @folioq: The segment to initialise
47*aabcabf2SDavid Howells  * @rreq_id: The request identifier to use in tracelines.
4828e8c5c0SDavid Howells  *
49*aabcabf2SDavid Howells  * Initialise a folio queue segment and set an identifier to be used in traces.
50*aabcabf2SDavid Howells  *
51*aabcabf2SDavid Howells  * Note that the folio pointers are left uninitialised.
5228e8c5c0SDavid Howells  */
53*aabcabf2SDavid Howells static inline void folioq_init(struct folio_queue *folioq, unsigned int rreq_id)
54db0aa2e9SDavid Howells {
55db0aa2e9SDavid Howells 	folio_batch_init(&folioq->vec);
56db0aa2e9SDavid Howells 	folioq->next = NULL;
57db0aa2e9SDavid Howells 	folioq->prev = NULL;
58db0aa2e9SDavid Howells 	folioq->marks = 0;
59db0aa2e9SDavid Howells 	folioq->marks2 = 0;
60ee4cdf7bSDavid Howells 	folioq->marks3 = 0;
61*aabcabf2SDavid Howells 	folioq->rreq_id = rreq_id;
62*aabcabf2SDavid Howells 	folioq->debug_id = 0;
63db0aa2e9SDavid Howells }
64db0aa2e9SDavid Howells 
6528e8c5c0SDavid Howells /**
6628e8c5c0SDavid Howells  * folioq_nr_slots: Query the capacity of a folio queue segment
6728e8c5c0SDavid Howells  * @folioq: The segment to query
6828e8c5c0SDavid Howells  *
6928e8c5c0SDavid Howells  * Query the number of folios that a particular folio queue segment might hold.
7028e8c5c0SDavid Howells  * [!] NOTE: This must not be assumed to be the same for every segment!
7128e8c5c0SDavid Howells  */
72db0aa2e9SDavid Howells static inline unsigned int folioq_nr_slots(const struct folio_queue *folioq)
73db0aa2e9SDavid Howells {
74db0aa2e9SDavid Howells 	return PAGEVEC_SIZE;
75db0aa2e9SDavid Howells }
76db0aa2e9SDavid Howells 
7728e8c5c0SDavid Howells /**
7828e8c5c0SDavid Howells  * folioq_count: Query the occupancy of a folio queue segment
7928e8c5c0SDavid Howells  * @folioq: The segment to query
8028e8c5c0SDavid Howells  *
8128e8c5c0SDavid Howells  * Query the number of folios that have been added to a folio queue segment.
8228e8c5c0SDavid Howells  * Note that this is not decreased as folios are removed from a segment.
8328e8c5c0SDavid Howells  */
84db0aa2e9SDavid Howells static inline unsigned int folioq_count(struct folio_queue *folioq)
85db0aa2e9SDavid Howells {
86db0aa2e9SDavid Howells 	return folio_batch_count(&folioq->vec);
87db0aa2e9SDavid Howells }
88db0aa2e9SDavid Howells 
8928e8c5c0SDavid Howells /**
90f5c82730SChristian Brauner  * folioq_full: Query if a folio queue segment is full
9128e8c5c0SDavid Howells  * @folioq: The segment to query
9228e8c5c0SDavid Howells  *
9328e8c5c0SDavid Howells  * Query if a folio queue segment is fully occupied.  Note that this does not
9428e8c5c0SDavid Howells  * change if folios are removed from a segment.
9528e8c5c0SDavid Howells  */
96db0aa2e9SDavid Howells static inline bool folioq_full(struct folio_queue *folioq)
97db0aa2e9SDavid Howells {
98db0aa2e9SDavid Howells 	//return !folio_batch_space(&folioq->vec);
99db0aa2e9SDavid Howells 	return folioq_count(folioq) >= folioq_nr_slots(folioq);
100db0aa2e9SDavid Howells }
101db0aa2e9SDavid Howells 
10228e8c5c0SDavid Howells /**
10328e8c5c0SDavid Howells  * folioq_is_marked: Check first folio mark in a folio queue segment
10428e8c5c0SDavid Howells  * @folioq: The segment to query
10528e8c5c0SDavid Howells  * @slot: The slot number of the folio to query
10628e8c5c0SDavid Howells  *
10728e8c5c0SDavid Howells  * Determine if the first mark is set for the folio in the specified slot in a
10828e8c5c0SDavid Howells  * folio queue segment.
10928e8c5c0SDavid Howells  */
110db0aa2e9SDavid Howells static inline bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot)
111db0aa2e9SDavid Howells {
112db0aa2e9SDavid Howells 	return test_bit(slot, &folioq->marks);
113db0aa2e9SDavid Howells }
114db0aa2e9SDavid Howells 
11528e8c5c0SDavid Howells /**
11628e8c5c0SDavid Howells  * folioq_mark: Set the first mark on a folio in a folio queue segment
11728e8c5c0SDavid Howells  * @folioq: The segment to modify
11828e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
11928e8c5c0SDavid Howells  *
12028e8c5c0SDavid Howells  * Set the first mark for the folio in the specified slot in a folio queue
12128e8c5c0SDavid Howells  * segment.
12228e8c5c0SDavid Howells  */
123db0aa2e9SDavid Howells static inline void folioq_mark(struct folio_queue *folioq, unsigned int slot)
124db0aa2e9SDavid Howells {
125db0aa2e9SDavid Howells 	set_bit(slot, &folioq->marks);
126db0aa2e9SDavid Howells }
127db0aa2e9SDavid Howells 
12828e8c5c0SDavid Howells /**
12928e8c5c0SDavid Howells  * folioq_unmark: Clear the first mark on a folio in a folio queue segment
13028e8c5c0SDavid Howells  * @folioq: The segment to modify
13128e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
13228e8c5c0SDavid Howells  *
13328e8c5c0SDavid Howells  * Clear the first mark for the folio in the specified slot in a folio queue
13428e8c5c0SDavid Howells  * segment.
13528e8c5c0SDavid Howells  */
136db0aa2e9SDavid Howells static inline void folioq_unmark(struct folio_queue *folioq, unsigned int slot)
137db0aa2e9SDavid Howells {
138db0aa2e9SDavid Howells 	clear_bit(slot, &folioq->marks);
139db0aa2e9SDavid Howells }
140db0aa2e9SDavid Howells 
14128e8c5c0SDavid Howells /**
14228e8c5c0SDavid Howells  * folioq_is_marked2: Check second folio mark in a folio queue segment
14328e8c5c0SDavid Howells  * @folioq: The segment to query
14428e8c5c0SDavid Howells  * @slot: The slot number of the folio to query
14528e8c5c0SDavid Howells  *
14628e8c5c0SDavid Howells  * Determine if the second mark is set for the folio in the specified slot in a
14728e8c5c0SDavid Howells  * folio queue segment.
14828e8c5c0SDavid Howells  */
149db0aa2e9SDavid Howells static inline bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot)
150db0aa2e9SDavid Howells {
151db0aa2e9SDavid Howells 	return test_bit(slot, &folioq->marks2);
152db0aa2e9SDavid Howells }
153db0aa2e9SDavid Howells 
15428e8c5c0SDavid Howells /**
15528e8c5c0SDavid Howells  * folioq_mark2: Set the second mark on a folio in a folio queue segment
15628e8c5c0SDavid Howells  * @folioq: The segment to modify
15728e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
15828e8c5c0SDavid Howells  *
15928e8c5c0SDavid Howells  * Set the second mark for the folio in the specified slot in a folio queue
16028e8c5c0SDavid Howells  * segment.
16128e8c5c0SDavid Howells  */
162db0aa2e9SDavid Howells static inline void folioq_mark2(struct folio_queue *folioq, unsigned int slot)
163db0aa2e9SDavid Howells {
164db0aa2e9SDavid Howells 	set_bit(slot, &folioq->marks2);
165db0aa2e9SDavid Howells }
166db0aa2e9SDavid Howells 
16728e8c5c0SDavid Howells /**
16828e8c5c0SDavid Howells  * folioq_unmark2: Clear the second mark on a folio in a folio queue segment
16928e8c5c0SDavid Howells  * @folioq: The segment to modify
17028e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
17128e8c5c0SDavid Howells  *
17228e8c5c0SDavid Howells  * Clear the second mark for the folio in the specified slot in a folio queue
17328e8c5c0SDavid Howells  * segment.
17428e8c5c0SDavid Howells  */
175db0aa2e9SDavid Howells static inline void folioq_unmark2(struct folio_queue *folioq, unsigned int slot)
176db0aa2e9SDavid Howells {
177db0aa2e9SDavid Howells 	clear_bit(slot, &folioq->marks2);
178db0aa2e9SDavid Howells }
179db0aa2e9SDavid Howells 
18028e8c5c0SDavid Howells /**
18128e8c5c0SDavid Howells  * folioq_is_marked3: Check third folio mark in a folio queue segment
18228e8c5c0SDavid Howells  * @folioq: The segment to query
18328e8c5c0SDavid Howells  * @slot: The slot number of the folio to query
18428e8c5c0SDavid Howells  *
18528e8c5c0SDavid Howells  * Determine if the third mark is set for the folio in the specified slot in a
18628e8c5c0SDavid Howells  * folio queue segment.
18728e8c5c0SDavid Howells  */
188ee4cdf7bSDavid Howells static inline bool folioq_is_marked3(const struct folio_queue *folioq, unsigned int slot)
189ee4cdf7bSDavid Howells {
190ee4cdf7bSDavid Howells 	return test_bit(slot, &folioq->marks3);
191ee4cdf7bSDavid Howells }
192ee4cdf7bSDavid Howells 
19328e8c5c0SDavid Howells /**
19428e8c5c0SDavid Howells  * folioq_mark3: Set the third mark on a folio in a folio queue segment
19528e8c5c0SDavid Howells  * @folioq: The segment to modify
19628e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
19728e8c5c0SDavid Howells  *
19828e8c5c0SDavid Howells  * Set the third mark for the folio in the specified slot in a folio queue
19928e8c5c0SDavid Howells  * segment.
20028e8c5c0SDavid Howells  */
201ee4cdf7bSDavid Howells static inline void folioq_mark3(struct folio_queue *folioq, unsigned int slot)
202ee4cdf7bSDavid Howells {
203ee4cdf7bSDavid Howells 	set_bit(slot, &folioq->marks3);
204ee4cdf7bSDavid Howells }
205ee4cdf7bSDavid Howells 
20628e8c5c0SDavid Howells /**
20728e8c5c0SDavid Howells  * folioq_unmark3: Clear the third mark on a folio in a folio queue segment
20828e8c5c0SDavid Howells  * @folioq: The segment to modify
20928e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
21028e8c5c0SDavid Howells  *
21128e8c5c0SDavid Howells  * Clear the third mark for the folio in the specified slot in a folio queue
21228e8c5c0SDavid Howells  * segment.
21328e8c5c0SDavid Howells  */
214ee4cdf7bSDavid Howells static inline void folioq_unmark3(struct folio_queue *folioq, unsigned int slot)
215ee4cdf7bSDavid Howells {
216ee4cdf7bSDavid Howells 	clear_bit(slot, &folioq->marks3);
217ee4cdf7bSDavid Howells }
218ee4cdf7bSDavid Howells 
219db0aa2e9SDavid Howells static inline unsigned int __folio_order(struct folio *folio)
220db0aa2e9SDavid Howells {
221db0aa2e9SDavid Howells 	if (!folio_test_large(folio))
222db0aa2e9SDavid Howells 		return 0;
223db0aa2e9SDavid Howells 	return folio->_flags_1 & 0xff;
224db0aa2e9SDavid Howells }
225db0aa2e9SDavid Howells 
22628e8c5c0SDavid Howells /**
22728e8c5c0SDavid Howells  * folioq_append: Add a folio to a folio queue segment
22828e8c5c0SDavid Howells  * @folioq: The segment to add to
22928e8c5c0SDavid Howells  * @folio: The folio to add
23028e8c5c0SDavid Howells  *
23128e8c5c0SDavid Howells  * Add a folio to the tail of the sequence in a folio queue segment, increasing
23228e8c5c0SDavid Howells  * the occupancy count and returning the slot number for the folio just added.
23328e8c5c0SDavid Howells  * The folio size is extracted and stored in the queue and the marks are left
23428e8c5c0SDavid Howells  * unmodified.
23528e8c5c0SDavid Howells  *
23628e8c5c0SDavid Howells  * Note that it's left up to the caller to check that the segment capacity will
23728e8c5c0SDavid Howells  * not be exceeded and to extend the queue.
23828e8c5c0SDavid Howells  */
239db0aa2e9SDavid Howells static inline unsigned int folioq_append(struct folio_queue *folioq, struct folio *folio)
240db0aa2e9SDavid Howells {
241db0aa2e9SDavid Howells 	unsigned int slot = folioq->vec.nr++;
242db0aa2e9SDavid Howells 
243db0aa2e9SDavid Howells 	folioq->vec.folios[slot] = folio;
244db0aa2e9SDavid Howells 	folioq->orders[slot] = __folio_order(folio);
245db0aa2e9SDavid Howells 	return slot;
246db0aa2e9SDavid Howells }
247db0aa2e9SDavid Howells 
24828e8c5c0SDavid Howells /**
24928e8c5c0SDavid Howells  * folioq_append_mark: Add a folio to a folio queue segment
25028e8c5c0SDavid Howells  * @folioq: The segment to add to
25128e8c5c0SDavid Howells  * @folio: The folio to add
25228e8c5c0SDavid Howells  *
25328e8c5c0SDavid Howells  * Add a folio to the tail of the sequence in a folio queue segment, increasing
25428e8c5c0SDavid Howells  * the occupancy count and returning the slot number for the folio just added.
25528e8c5c0SDavid Howells  * The folio size is extracted and stored in the queue, the first mark is set
25628e8c5c0SDavid Howells  * and and the second and third marks are left unmodified.
25728e8c5c0SDavid Howells  *
25828e8c5c0SDavid Howells  * Note that it's left up to the caller to check that the segment capacity will
25928e8c5c0SDavid Howells  * not be exceeded and to extend the queue.
26028e8c5c0SDavid Howells  */
261db0aa2e9SDavid Howells static inline unsigned int folioq_append_mark(struct folio_queue *folioq, struct folio *folio)
262db0aa2e9SDavid Howells {
263db0aa2e9SDavid Howells 	unsigned int slot = folioq->vec.nr++;
264db0aa2e9SDavid Howells 
265db0aa2e9SDavid Howells 	folioq->vec.folios[slot] = folio;
266db0aa2e9SDavid Howells 	folioq->orders[slot] = __folio_order(folio);
267db0aa2e9SDavid Howells 	folioq_mark(folioq, slot);
268db0aa2e9SDavid Howells 	return slot;
269db0aa2e9SDavid Howells }
270db0aa2e9SDavid Howells 
27128e8c5c0SDavid Howells /**
27228e8c5c0SDavid Howells  * folioq_folio: Get a folio from a folio queue segment
27328e8c5c0SDavid Howells  * @folioq: The segment to access
27428e8c5c0SDavid Howells  * @slot: The folio slot to access
27528e8c5c0SDavid Howells  *
27628e8c5c0SDavid Howells  * Retrieve the folio in the specified slot from a folio queue segment.  Note
27728e8c5c0SDavid Howells  * that no bounds check is made and if the slot hasn't been added into yet, the
27828e8c5c0SDavid Howells  * pointer will be undefined.  If the slot has been cleared, NULL will be
27928e8c5c0SDavid Howells  * returned.
28028e8c5c0SDavid Howells  */
281db0aa2e9SDavid Howells static inline struct folio *folioq_folio(const struct folio_queue *folioq, unsigned int slot)
282db0aa2e9SDavid Howells {
283db0aa2e9SDavid Howells 	return folioq->vec.folios[slot];
284db0aa2e9SDavid Howells }
285db0aa2e9SDavid Howells 
28628e8c5c0SDavid Howells /**
28728e8c5c0SDavid Howells  * folioq_folio_order: Get the order of a folio from a folio queue segment
28828e8c5c0SDavid Howells  * @folioq: The segment to access
28928e8c5c0SDavid Howells  * @slot: The folio slot to access
29028e8c5c0SDavid Howells  *
29128e8c5c0SDavid Howells  * Retrieve the order of the folio in the specified slot from a folio queue
29228e8c5c0SDavid Howells  * segment.  Note that no bounds check is made and if the slot hasn't been
29328e8c5c0SDavid Howells  * added into yet, the order returned will be 0.
29428e8c5c0SDavid Howells  */
295db0aa2e9SDavid Howells static inline unsigned int folioq_folio_order(const struct folio_queue *folioq, unsigned int slot)
296db0aa2e9SDavid Howells {
297db0aa2e9SDavid Howells 	return folioq->orders[slot];
298db0aa2e9SDavid Howells }
299db0aa2e9SDavid Howells 
30028e8c5c0SDavid Howells /**
30128e8c5c0SDavid Howells  * folioq_folio_size: Get the size of a folio from a folio queue segment
30228e8c5c0SDavid Howells  * @folioq: The segment to access
30328e8c5c0SDavid Howells  * @slot: The folio slot to access
30428e8c5c0SDavid Howells  *
30528e8c5c0SDavid Howells  * Retrieve the size of the folio in the specified slot from a folio queue
30628e8c5c0SDavid Howells  * segment.  Note that no bounds check is made and if the slot hasn't been
30728e8c5c0SDavid Howells  * added into yet, the size returned will be PAGE_SIZE.
30828e8c5c0SDavid Howells  */
309db0aa2e9SDavid Howells static inline size_t folioq_folio_size(const struct folio_queue *folioq, unsigned int slot)
310db0aa2e9SDavid Howells {
311db0aa2e9SDavid Howells 	return PAGE_SIZE << folioq_folio_order(folioq, slot);
312db0aa2e9SDavid Howells }
313db0aa2e9SDavid Howells 
31428e8c5c0SDavid Howells /**
31528e8c5c0SDavid Howells  * folioq_clear: Clear a folio from a folio queue segment
31628e8c5c0SDavid Howells  * @folioq: The segment to clear
31728e8c5c0SDavid Howells  * @slot: The folio slot to clear
31828e8c5c0SDavid Howells  *
31928e8c5c0SDavid Howells  * Clear a folio from a sequence in a folio queue segment and clear its marks.
32028e8c5c0SDavid Howells  * The occupancy count is left unchanged.
32128e8c5c0SDavid Howells  */
322db0aa2e9SDavid Howells static inline void folioq_clear(struct folio_queue *folioq, unsigned int slot)
323db0aa2e9SDavid Howells {
324db0aa2e9SDavid Howells 	folioq->vec.folios[slot] = NULL;
325db0aa2e9SDavid Howells 	folioq_unmark(folioq, slot);
326db0aa2e9SDavid Howells 	folioq_unmark2(folioq, slot);
327ee4cdf7bSDavid Howells 	folioq_unmark3(folioq, slot);
328db0aa2e9SDavid Howells }
329db0aa2e9SDavid Howells 
330db0aa2e9SDavid Howells #endif /* _LINUX_FOLIO_QUEUE_H */
331