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