xref: /linux-6.15/include/linux/folio_queue.h (revision 58abac76)
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>
18*58abac76SLiu Ye #include <linux/mm.h>
19db0aa2e9SDavid Howells 
20db0aa2e9SDavid Howells /*
21db0aa2e9SDavid Howells  * Segment in a queue of running buffers.  Each segment can hold a number of
22db0aa2e9SDavid Howells  * folios and a portion of the queue can be referenced with the ITER_FOLIOQ
23db0aa2e9SDavid Howells  * iterator.  The possibility exists of inserting non-folio elements into the
24db0aa2e9SDavid Howells  * queue (such as gaps).
25db0aa2e9SDavid Howells  *
26db0aa2e9SDavid Howells  * Explicit prev and next pointers are used instead of a list_head to make it
27db0aa2e9SDavid Howells  * easier to add segments to tail and remove them from the head without the
28db0aa2e9SDavid Howells  * need for a lock.
29db0aa2e9SDavid Howells  */
30db0aa2e9SDavid Howells struct folio_queue {
31db0aa2e9SDavid Howells 	struct folio_batch	vec;		/* Folios in the queue segment */
32db0aa2e9SDavid Howells 	u8			orders[PAGEVEC_SIZE]; /* Order of each folio */
33db0aa2e9SDavid Howells 	struct folio_queue	*next;		/* Next queue segment or NULL */
34db0aa2e9SDavid Howells 	struct folio_queue	*prev;		/* Previous queue segment of NULL */
35db0aa2e9SDavid Howells 	unsigned long		marks;		/* 1-bit mark per folio */
36db0aa2e9SDavid Howells 	unsigned long		marks2;		/* Second 1-bit mark per folio */
37ee4cdf7bSDavid Howells 	unsigned long		marks3;		/* Third 1-bit mark per folio */
38db0aa2e9SDavid Howells #if PAGEVEC_SIZE > BITS_PER_LONG
39db0aa2e9SDavid Howells #error marks is not big enough
40db0aa2e9SDavid Howells #endif
41aabcabf2SDavid Howells 	unsigned int		rreq_id;
42aabcabf2SDavid Howells 	unsigned int		debug_id;
43db0aa2e9SDavid Howells };
44db0aa2e9SDavid Howells 
4528e8c5c0SDavid Howells /**
4628e8c5c0SDavid Howells  * folioq_init - Initialise a folio queue segment
4728e8c5c0SDavid Howells  * @folioq: The segment to initialise
48aabcabf2SDavid Howells  * @rreq_id: The request identifier to use in tracelines.
4928e8c5c0SDavid Howells  *
50aabcabf2SDavid Howells  * Initialise a folio queue segment and set an identifier to be used in traces.
51aabcabf2SDavid Howells  *
52aabcabf2SDavid Howells  * Note that the folio pointers are left uninitialised.
5328e8c5c0SDavid Howells  */
folioq_init(struct folio_queue * folioq,unsigned int rreq_id)54aabcabf2SDavid Howells static inline void folioq_init(struct folio_queue *folioq, unsigned int rreq_id)
55db0aa2e9SDavid Howells {
56db0aa2e9SDavid Howells 	folio_batch_init(&folioq->vec);
57db0aa2e9SDavid Howells 	folioq->next = NULL;
58db0aa2e9SDavid Howells 	folioq->prev = NULL;
59db0aa2e9SDavid Howells 	folioq->marks = 0;
60db0aa2e9SDavid Howells 	folioq->marks2 = 0;
61ee4cdf7bSDavid Howells 	folioq->marks3 = 0;
62aabcabf2SDavid Howells 	folioq->rreq_id = rreq_id;
63aabcabf2SDavid Howells 	folioq->debug_id = 0;
64db0aa2e9SDavid Howells }
65db0aa2e9SDavid Howells 
6628e8c5c0SDavid Howells /**
6728e8c5c0SDavid Howells  * folioq_nr_slots: Query the capacity of a folio queue segment
6828e8c5c0SDavid Howells  * @folioq: The segment to query
6928e8c5c0SDavid Howells  *
7028e8c5c0SDavid Howells  * Query the number of folios that a particular folio queue segment might hold.
7128e8c5c0SDavid Howells  * [!] NOTE: This must not be assumed to be the same for every segment!
7228e8c5c0SDavid Howells  */
folioq_nr_slots(const struct folio_queue * folioq)73db0aa2e9SDavid Howells static inline unsigned int folioq_nr_slots(const struct folio_queue *folioq)
74db0aa2e9SDavid Howells {
75db0aa2e9SDavid Howells 	return PAGEVEC_SIZE;
76db0aa2e9SDavid Howells }
77db0aa2e9SDavid Howells 
7828e8c5c0SDavid Howells /**
7928e8c5c0SDavid Howells  * folioq_count: Query the occupancy of a folio queue segment
8028e8c5c0SDavid Howells  * @folioq: The segment to query
8128e8c5c0SDavid Howells  *
8228e8c5c0SDavid Howells  * Query the number of folios that have been added to a folio queue segment.
8328e8c5c0SDavid Howells  * Note that this is not decreased as folios are removed from a segment.
8428e8c5c0SDavid Howells  */
folioq_count(struct folio_queue * folioq)85db0aa2e9SDavid Howells static inline unsigned int folioq_count(struct folio_queue *folioq)
86db0aa2e9SDavid Howells {
87db0aa2e9SDavid Howells 	return folio_batch_count(&folioq->vec);
88db0aa2e9SDavid Howells }
89db0aa2e9SDavid Howells 
9028e8c5c0SDavid Howells /**
91f5c82730SChristian Brauner  * folioq_full: Query if a folio queue segment is full
9228e8c5c0SDavid Howells  * @folioq: The segment to query
9328e8c5c0SDavid Howells  *
9428e8c5c0SDavid Howells  * Query if a folio queue segment is fully occupied.  Note that this does not
9528e8c5c0SDavid Howells  * change if folios are removed from a segment.
9628e8c5c0SDavid Howells  */
folioq_full(struct folio_queue * folioq)97db0aa2e9SDavid Howells static inline bool folioq_full(struct folio_queue *folioq)
98db0aa2e9SDavid Howells {
99db0aa2e9SDavid Howells 	//return !folio_batch_space(&folioq->vec);
100db0aa2e9SDavid Howells 	return folioq_count(folioq) >= folioq_nr_slots(folioq);
101db0aa2e9SDavid Howells }
102db0aa2e9SDavid Howells 
10328e8c5c0SDavid Howells /**
10428e8c5c0SDavid Howells  * folioq_is_marked: Check first folio mark in a folio queue segment
10528e8c5c0SDavid Howells  * @folioq: The segment to query
10628e8c5c0SDavid Howells  * @slot: The slot number of the folio to query
10728e8c5c0SDavid Howells  *
10828e8c5c0SDavid Howells  * Determine if the first mark is set for the folio in the specified slot in a
10928e8c5c0SDavid Howells  * folio queue segment.
11028e8c5c0SDavid Howells  */
folioq_is_marked(const struct folio_queue * folioq,unsigned int slot)111db0aa2e9SDavid Howells static inline bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot)
112db0aa2e9SDavid Howells {
113db0aa2e9SDavid Howells 	return test_bit(slot, &folioq->marks);
114db0aa2e9SDavid Howells }
115db0aa2e9SDavid Howells 
11628e8c5c0SDavid Howells /**
11728e8c5c0SDavid Howells  * folioq_mark: Set the first mark on a folio in a folio queue segment
11828e8c5c0SDavid Howells  * @folioq: The segment to modify
11928e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
12028e8c5c0SDavid Howells  *
12128e8c5c0SDavid Howells  * Set the first mark for the folio in the specified slot in a folio queue
12228e8c5c0SDavid Howells  * segment.
12328e8c5c0SDavid Howells  */
folioq_mark(struct folio_queue * folioq,unsigned int slot)124db0aa2e9SDavid Howells static inline void folioq_mark(struct folio_queue *folioq, unsigned int slot)
125db0aa2e9SDavid Howells {
126db0aa2e9SDavid Howells 	set_bit(slot, &folioq->marks);
127db0aa2e9SDavid Howells }
128db0aa2e9SDavid Howells 
12928e8c5c0SDavid Howells /**
13028e8c5c0SDavid Howells  * folioq_unmark: Clear the first mark on a folio in a folio queue segment
13128e8c5c0SDavid Howells  * @folioq: The segment to modify
13228e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
13328e8c5c0SDavid Howells  *
13428e8c5c0SDavid Howells  * Clear the first mark for the folio in the specified slot in a folio queue
13528e8c5c0SDavid Howells  * segment.
13628e8c5c0SDavid Howells  */
folioq_unmark(struct folio_queue * folioq,unsigned int slot)137db0aa2e9SDavid Howells static inline void folioq_unmark(struct folio_queue *folioq, unsigned int slot)
138db0aa2e9SDavid Howells {
139db0aa2e9SDavid Howells 	clear_bit(slot, &folioq->marks);
140db0aa2e9SDavid Howells }
141db0aa2e9SDavid Howells 
14228e8c5c0SDavid Howells /**
14328e8c5c0SDavid Howells  * folioq_is_marked2: Check second folio mark in a folio queue segment
14428e8c5c0SDavid Howells  * @folioq: The segment to query
14528e8c5c0SDavid Howells  * @slot: The slot number of the folio to query
14628e8c5c0SDavid Howells  *
14728e8c5c0SDavid Howells  * Determine if the second mark is set for the folio in the specified slot in a
14828e8c5c0SDavid Howells  * folio queue segment.
14928e8c5c0SDavid Howells  */
folioq_is_marked2(const struct folio_queue * folioq,unsigned int slot)150db0aa2e9SDavid Howells static inline bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot)
151db0aa2e9SDavid Howells {
152db0aa2e9SDavid Howells 	return test_bit(slot, &folioq->marks2);
153db0aa2e9SDavid Howells }
154db0aa2e9SDavid Howells 
15528e8c5c0SDavid Howells /**
15628e8c5c0SDavid Howells  * folioq_mark2: Set the second mark on a folio in a folio queue segment
15728e8c5c0SDavid Howells  * @folioq: The segment to modify
15828e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
15928e8c5c0SDavid Howells  *
16028e8c5c0SDavid Howells  * Set the second mark for the folio in the specified slot in a folio queue
16128e8c5c0SDavid Howells  * segment.
16228e8c5c0SDavid Howells  */
folioq_mark2(struct folio_queue * folioq,unsigned int slot)163db0aa2e9SDavid Howells static inline void folioq_mark2(struct folio_queue *folioq, unsigned int slot)
164db0aa2e9SDavid Howells {
165db0aa2e9SDavid Howells 	set_bit(slot, &folioq->marks2);
166db0aa2e9SDavid Howells }
167db0aa2e9SDavid Howells 
16828e8c5c0SDavid Howells /**
16928e8c5c0SDavid Howells  * folioq_unmark2: Clear the second mark on a folio in a folio queue segment
17028e8c5c0SDavid Howells  * @folioq: The segment to modify
17128e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
17228e8c5c0SDavid Howells  *
17328e8c5c0SDavid Howells  * Clear the second mark for the folio in the specified slot in a folio queue
17428e8c5c0SDavid Howells  * segment.
17528e8c5c0SDavid Howells  */
folioq_unmark2(struct folio_queue * folioq,unsigned int slot)176db0aa2e9SDavid Howells static inline void folioq_unmark2(struct folio_queue *folioq, unsigned int slot)
177db0aa2e9SDavid Howells {
178db0aa2e9SDavid Howells 	clear_bit(slot, &folioq->marks2);
179db0aa2e9SDavid Howells }
180db0aa2e9SDavid Howells 
18128e8c5c0SDavid Howells /**
18228e8c5c0SDavid Howells  * folioq_is_marked3: Check third folio mark in a folio queue segment
18328e8c5c0SDavid Howells  * @folioq: The segment to query
18428e8c5c0SDavid Howells  * @slot: The slot number of the folio to query
18528e8c5c0SDavid Howells  *
18628e8c5c0SDavid Howells  * Determine if the third mark is set for the folio in the specified slot in a
18728e8c5c0SDavid Howells  * folio queue segment.
18828e8c5c0SDavid Howells  */
folioq_is_marked3(const struct folio_queue * folioq,unsigned int slot)189ee4cdf7bSDavid Howells static inline bool folioq_is_marked3(const struct folio_queue *folioq, unsigned int slot)
190ee4cdf7bSDavid Howells {
191ee4cdf7bSDavid Howells 	return test_bit(slot, &folioq->marks3);
192ee4cdf7bSDavid Howells }
193ee4cdf7bSDavid Howells 
19428e8c5c0SDavid Howells /**
19528e8c5c0SDavid Howells  * folioq_mark3: Set the third mark on a folio in a folio queue segment
19628e8c5c0SDavid Howells  * @folioq: The segment to modify
19728e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
19828e8c5c0SDavid Howells  *
19928e8c5c0SDavid Howells  * Set the third mark for the folio in the specified slot in a folio queue
20028e8c5c0SDavid Howells  * segment.
20128e8c5c0SDavid Howells  */
folioq_mark3(struct folio_queue * folioq,unsigned int slot)202ee4cdf7bSDavid Howells static inline void folioq_mark3(struct folio_queue *folioq, unsigned int slot)
203ee4cdf7bSDavid Howells {
204ee4cdf7bSDavid Howells 	set_bit(slot, &folioq->marks3);
205ee4cdf7bSDavid Howells }
206ee4cdf7bSDavid Howells 
20728e8c5c0SDavid Howells /**
20828e8c5c0SDavid Howells  * folioq_unmark3: Clear the third mark on a folio in a folio queue segment
20928e8c5c0SDavid Howells  * @folioq: The segment to modify
21028e8c5c0SDavid Howells  * @slot: The slot number of the folio to modify
21128e8c5c0SDavid Howells  *
21228e8c5c0SDavid Howells  * Clear the third mark for the folio in the specified slot in a folio queue
21328e8c5c0SDavid Howells  * segment.
21428e8c5c0SDavid Howells  */
folioq_unmark3(struct folio_queue * folioq,unsigned int slot)215ee4cdf7bSDavid Howells static inline void folioq_unmark3(struct folio_queue *folioq, unsigned int slot)
216ee4cdf7bSDavid Howells {
217ee4cdf7bSDavid Howells 	clear_bit(slot, &folioq->marks3);
218ee4cdf7bSDavid Howells }
219ee4cdf7bSDavid Howells 
22028e8c5c0SDavid Howells /**
22128e8c5c0SDavid Howells  * folioq_append: Add a folio to a folio queue segment
22228e8c5c0SDavid Howells  * @folioq: The segment to add to
22328e8c5c0SDavid Howells  * @folio: The folio to add
22428e8c5c0SDavid Howells  *
22528e8c5c0SDavid Howells  * Add a folio to the tail of the sequence in a folio queue segment, increasing
22628e8c5c0SDavid Howells  * the occupancy count and returning the slot number for the folio just added.
22728e8c5c0SDavid Howells  * The folio size is extracted and stored in the queue and the marks are left
22828e8c5c0SDavid Howells  * unmodified.
22928e8c5c0SDavid Howells  *
23028e8c5c0SDavid Howells  * Note that it's left up to the caller to check that the segment capacity will
23128e8c5c0SDavid Howells  * not be exceeded and to extend the queue.
23228e8c5c0SDavid Howells  */
folioq_append(struct folio_queue * folioq,struct folio * folio)233db0aa2e9SDavid Howells static inline unsigned int folioq_append(struct folio_queue *folioq, struct folio *folio)
234db0aa2e9SDavid Howells {
235db0aa2e9SDavid Howells 	unsigned int slot = folioq->vec.nr++;
236db0aa2e9SDavid Howells 
237db0aa2e9SDavid Howells 	folioq->vec.folios[slot] = folio;
238*58abac76SLiu Ye 	folioq->orders[slot] = folio_order(folio);
239db0aa2e9SDavid Howells 	return slot;
240db0aa2e9SDavid Howells }
241db0aa2e9SDavid Howells 
24228e8c5c0SDavid Howells /**
24328e8c5c0SDavid Howells  * folioq_append_mark: Add a folio to a folio queue segment
24428e8c5c0SDavid Howells  * @folioq: The segment to add to
24528e8c5c0SDavid Howells  * @folio: The folio to add
24628e8c5c0SDavid Howells  *
24728e8c5c0SDavid Howells  * Add a folio to the tail of the sequence in a folio queue segment, increasing
24828e8c5c0SDavid Howells  * the occupancy count and returning the slot number for the folio just added.
24928e8c5c0SDavid Howells  * The folio size is extracted and stored in the queue, the first mark is set
25028e8c5c0SDavid Howells  * and and the second and third marks are left unmodified.
25128e8c5c0SDavid Howells  *
25228e8c5c0SDavid Howells  * Note that it's left up to the caller to check that the segment capacity will
25328e8c5c0SDavid Howells  * not be exceeded and to extend the queue.
25428e8c5c0SDavid Howells  */
folioq_append_mark(struct folio_queue * folioq,struct folio * folio)255db0aa2e9SDavid Howells static inline unsigned int folioq_append_mark(struct folio_queue *folioq, struct folio *folio)
256db0aa2e9SDavid Howells {
257db0aa2e9SDavid Howells 	unsigned int slot = folioq->vec.nr++;
258db0aa2e9SDavid Howells 
259db0aa2e9SDavid Howells 	folioq->vec.folios[slot] = folio;
260*58abac76SLiu Ye 	folioq->orders[slot] = folio_order(folio);
261db0aa2e9SDavid Howells 	folioq_mark(folioq, slot);
262db0aa2e9SDavid Howells 	return slot;
263db0aa2e9SDavid Howells }
264db0aa2e9SDavid Howells 
26528e8c5c0SDavid Howells /**
26628e8c5c0SDavid Howells  * folioq_folio: Get a folio from a folio queue segment
26728e8c5c0SDavid Howells  * @folioq: The segment to access
26828e8c5c0SDavid Howells  * @slot: The folio slot to access
26928e8c5c0SDavid Howells  *
27028e8c5c0SDavid Howells  * Retrieve the folio in the specified slot from a folio queue segment.  Note
27128e8c5c0SDavid Howells  * that no bounds check is made and if the slot hasn't been added into yet, the
27228e8c5c0SDavid Howells  * pointer will be undefined.  If the slot has been cleared, NULL will be
27328e8c5c0SDavid Howells  * returned.
27428e8c5c0SDavid Howells  */
folioq_folio(const struct folio_queue * folioq,unsigned int slot)275db0aa2e9SDavid Howells static inline struct folio *folioq_folio(const struct folio_queue *folioq, unsigned int slot)
276db0aa2e9SDavid Howells {
277db0aa2e9SDavid Howells 	return folioq->vec.folios[slot];
278db0aa2e9SDavid Howells }
279db0aa2e9SDavid Howells 
28028e8c5c0SDavid Howells /**
28128e8c5c0SDavid Howells  * folioq_folio_order: Get the order of a folio from a folio queue segment
28228e8c5c0SDavid Howells  * @folioq: The segment to access
28328e8c5c0SDavid Howells  * @slot: The folio slot to access
28428e8c5c0SDavid Howells  *
28528e8c5c0SDavid Howells  * Retrieve the order of the folio in the specified slot from a folio queue
28628e8c5c0SDavid Howells  * segment.  Note that no bounds check is made and if the slot hasn't been
28728e8c5c0SDavid Howells  * added into yet, the order returned will be 0.
28828e8c5c0SDavid Howells  */
folioq_folio_order(const struct folio_queue * folioq,unsigned int slot)289db0aa2e9SDavid Howells static inline unsigned int folioq_folio_order(const struct folio_queue *folioq, unsigned int slot)
290db0aa2e9SDavid Howells {
291db0aa2e9SDavid Howells 	return folioq->orders[slot];
292db0aa2e9SDavid Howells }
293db0aa2e9SDavid Howells 
29428e8c5c0SDavid Howells /**
29528e8c5c0SDavid Howells  * folioq_folio_size: Get the size of a folio from a folio queue segment
29628e8c5c0SDavid Howells  * @folioq: The segment to access
29728e8c5c0SDavid Howells  * @slot: The folio slot to access
29828e8c5c0SDavid Howells  *
29928e8c5c0SDavid Howells  * Retrieve the size of the folio in the specified slot from a folio queue
30028e8c5c0SDavid Howells  * segment.  Note that no bounds check is made and if the slot hasn't been
30128e8c5c0SDavid Howells  * added into yet, the size returned will be PAGE_SIZE.
30228e8c5c0SDavid Howells  */
folioq_folio_size(const struct folio_queue * folioq,unsigned int slot)303db0aa2e9SDavid Howells static inline size_t folioq_folio_size(const struct folio_queue *folioq, unsigned int slot)
304db0aa2e9SDavid Howells {
305db0aa2e9SDavid Howells 	return PAGE_SIZE << folioq_folio_order(folioq, slot);
306db0aa2e9SDavid Howells }
307db0aa2e9SDavid Howells 
30828e8c5c0SDavid Howells /**
30928e8c5c0SDavid Howells  * folioq_clear: Clear a folio from a folio queue segment
31028e8c5c0SDavid Howells  * @folioq: The segment to clear
31128e8c5c0SDavid Howells  * @slot: The folio slot to clear
31228e8c5c0SDavid Howells  *
31328e8c5c0SDavid Howells  * Clear a folio from a sequence in a folio queue segment and clear its marks.
31428e8c5c0SDavid Howells  * The occupancy count is left unchanged.
31528e8c5c0SDavid Howells  */
folioq_clear(struct folio_queue * folioq,unsigned int slot)316db0aa2e9SDavid Howells static inline void folioq_clear(struct folio_queue *folioq, unsigned int slot)
317db0aa2e9SDavid Howells {
318db0aa2e9SDavid Howells 	folioq->vec.folios[slot] = NULL;
319db0aa2e9SDavid Howells 	folioq_unmark(folioq, slot);
320db0aa2e9SDavid Howells 	folioq_unmark2(folioq, slot);
321ee4cdf7bSDavid Howells 	folioq_unmark3(folioq, slot);
322db0aa2e9SDavid Howells }
323db0aa2e9SDavid Howells 
324db0aa2e9SDavid Howells #endif /* _LINUX_FOLIO_QUEUE_H */
325