[RFC PATCH 6/6] lib: Remove unused assoc_array
Jarkko Sakkinen
jarkko at kernel.org
Fri Oct 16 21:53:04 UTC 2026
Drop the obsolete leaf-count reference from a dm-verity test comment.
Signed-off-by: Jarkko Sakkinen <jarkko at kernel.org>
---
Documentation/core-api/assoc_array.rst | 564 ------
Documentation/core-api/index.rst | 1 -
.../zh_CN/core-api/assoc_array.rst | 473 -----
.../translations/zh_CN/core-api/index.rst | 1 -
include/linux/assoc_array.h | 88 -
include/linux/assoc_array_priv.h | 178 --
lib/Kconfig | 14 -
lib/Makefile | 1 -
lib/assoc_array.c | 1724 -----------------
9 files changed, 3044 deletions(-)
delete mode 100644 Documentation/core-api/assoc_array.rst
delete mode 100644 Documentation/translations/zh_CN/core-api/assoc_array.rst
delete mode 100644 include/linux/assoc_array.h
delete mode 100644 include/linux/assoc_array_priv.h
delete mode 100644 lib/assoc_array.c
diff --git a/Documentation/core-api/assoc_array.rst b/Documentation/core-api/assoc_array.rst
deleted file mode 100644
index 19d89f92bf8d..000000000000
--- a/Documentation/core-api/assoc_array.rst
+++ /dev/null
@@ -1,564 +0,0 @@
-========================================
-Generic Associative Array Implementation
-========================================
-
-Overview
-========
-
-This associative array implementation is an object container with the following
-properties:
-
-1. Objects are opaque pointers. The implementation does not care where they
- point (if anywhere) or what they point to (if anything).
-
- .. note::
-
- Pointers to objects _must_ be zero in the least significant bit.
-
-2. Objects do not need to contain linkage blocks for use by the array. This
- permits an object to be located in multiple arrays simultaneously.
- Rather, the array is made up of metadata blocks that point to objects.
-
-3. Objects require index keys to locate them within the array.
-
-4. Index keys must be unique. Inserting an object with the same key as one
- already in the array will replace the old object.
-
-5. Index keys can be of any length and can be of different lengths.
-
-6. Index keys should encode the length early on, before any variation due to
- length is seen.
-
-7. Index keys can include a hash to scatter objects throughout the array.
-
-8. The array can iterated over. The objects will not necessarily come out in
- key order.
-
-9. The array can be iterated over while it is being modified, provided the
- RCU readlock is being held by the iterator. Note, however, under these
- circumstances, some objects may be seen more than once. If this is a
- problem, the iterator should lock against modification. Objects will not
- be missed, however, unless deleted.
-
-10. Objects in the array can be looked up by means of their index key.
-
-11. Objects can be looked up while the array is being modified, provided the
- RCU readlock is being held by the thread doing the look up.
-
-The implementation uses a tree of 16-pointer nodes internally that are indexed
-on each level by nibbles from the index key in the same manner as in a radix
-tree. To improve memory efficiency, shortcuts can be emplaced to skip over
-what would otherwise be a series of single-occupancy nodes. Further, nodes
-pack leaf object pointers into spare space in the node rather than making an
-extra branch until as such time an object needs to be added to a full node.
-
-
-The Public API
-==============
-
-The public API can be found in ``<linux/assoc_array.h>``. The associative
-array is rooted on the following structure::
-
- struct assoc_array {
- ...
- };
-
-The code is selected by enabling ``CONFIG_ASSOCIATIVE_ARRAY`` with::
-
- ./script/config -e ASSOCIATIVE_ARRAY
-
-
-Edit Script
------------
-
-The insertion and deletion functions produce an 'edit script' that can later be
-applied to effect the changes without risking ``ENOMEM``. This retains the
-preallocated metadata blocks that will be installed in the internal tree and
-keeps track of the metadata blocks that will be removed from the tree when the
-script is applied.
-
-This is also used to keep track of dead blocks and dead objects after the
-script has been applied so that they can be freed later. The freeing is done
-after an RCU grace period has passed - thus allowing access functions to
-proceed under the RCU read lock.
-
-The script appears as outside of the API as a pointer of the type::
-
- struct assoc_array_edit;
-
-There are two functions for dealing with the script:
-
-1. Apply an edit script::
-
- void assoc_array_apply_edit(struct assoc_array_edit *edit);
-
- This will perform the edit functions, interpolating various write barriers
- to permit accesses under the RCU read lock to continue. The edit script
- will then be passed to ``call_rcu()`` to free it and any dead stuff it
- points to.
-
-2. Cancel an edit script::
-
- void assoc_array_cancel_edit(struct assoc_array_edit *edit);
-
- This frees the edit script and all preallocated memory immediately. If
- this was for insertion, the new object is *not* released by this function,
- but must rather be released by the caller.
-
-These functions are guaranteed not to fail.
-
-
-Operations Table
-----------------
-
-Various functions take a table of operations::
-
- struct assoc_array_ops {
- ...
- };
-
-This points to a number of methods, all of which need to be provided:
-
-1. Get a chunk of index key from caller data::
-
- unsigned long (*get_key_chunk)(const void *index_key, int level);
-
- This should return a chunk of caller-supplied index key starting at the
- *bit* position given by the level argument. The level argument will be a
- multiple of ``ASSOC_ARRAY_KEY_CHUNK_SIZE`` and the function should return
- ``ASSOC_ARRAY_KEY_CHUNK_SIZE bits``. No error is possible.
-
-
-2. Get a chunk of an object's index key::
-
- unsigned long (*get_object_key_chunk)(const void *object, int level);
-
- As the previous function, but gets its data from an object in the array
- rather than from a caller-supplied index key.
-
-
-3. See if this is the object we're looking for::
-
- bool (*compare_object)(const void *object, const void *index_key);
-
- Compare the object against an index key and return ``true`` if it matches
- and ``false`` if it doesn't.
-
-
-4. Diff the index keys of two objects::
-
- int (*diff_objects)(const void *object, const void *index_key);
-
- Return the bit position at which the index key of the specified object
- differs from the given index key or -1 if they are the same.
-
-
-5. Free an object::
-
- void (*free_object)(void *object);
-
- Free the specified object. Note that this may be called an RCU grace period
- after ``assoc_array_apply_edit()`` was called, so ``synchronize_rcu()`` may
- be necessary on module unloading.
-
-
-Manipulation Functions
-----------------------
-
-There are a number of functions for manipulating an associative array:
-
-1. Initialise an associative array::
-
- void assoc_array_init(struct assoc_array *array);
-
- This initialises the base structure for an associative array. It can't fail.
-
-
-2. Insert/replace an object in an associative array::
-
- struct assoc_array_edit *
- assoc_array_insert(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key,
- void *object);
-
- This inserts the given object into the array. Note that the least
- significant bit of the pointer must be zero as it's used to type-mark
- pointers internally.
-
- If an object already exists for that key then it will be replaced with the
- new object and the old one will be freed automatically.
-
- The ``index_key`` argument should hold index key information and is
- passed to the methods in the ops table when they are called.
-
- This function makes no alteration to the array itself, but rather returns
- an edit script that must be applied. ``-ENOMEM`` is returned in the case of
- an out-of-memory error.
-
- The caller should lock exclusively against other modifiers of the array.
-
-
-3. Delete an object from an associative array::
-
- struct assoc_array_edit *
- assoc_array_delete(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key);
-
- This deletes an object that matches the specified data from the array.
-
- The ``index_key`` argument should hold index key information and is
- passed to the methods in the ops table when they are called.
-
- This function makes no alteration to the array itself, but rather returns
- an edit script that must be applied. ``-ENOMEM`` is returned in the case of
- an out-of-memory error. ``NULL`` will be returned if the specified object
- is not found within the array.
-
-The caller should lock exclusively against other modifiers of the array.
-
-
-4. Delete all objects from an associative array::
-
- struct assoc_array_edit *
- assoc_array_clear(struct assoc_array *array,
- const struct assoc_array_ops *ops);
-
- This deletes all the objects from an associative array and leaves it
- completely empty.
-
- This function makes no alteration to the array itself, but rather returns
- an edit script that must be applied. ``-ENOMEM`` is returned in the case of
- an out-of-memory error.
-
- The caller should lock exclusively against other modifiers of the array.
-
-
-5. Destroy an associative array, deleting all objects::
-
- void assoc_array_destroy(struct assoc_array *array,
- const struct assoc_array_ops *ops);
-
- This destroys the contents of the associative array and leaves it
- completely empty. It is not permitted for another thread to be traversing
- the array under the RCU read lock at the same time as this function is
- destroying it as no RCU deferral is performed on memory release -
- something that would require memory to be allocated.
-
- The caller should lock exclusively against other modifiers and accessors
- of the array.
-
-
-6. Garbage collect an associative array::
-
- int assoc_array_gc(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- bool (*iterator)(void *object, void *iterator_data),
- void *iterator_data);
-
- This iterates over the objects in an associative array and passes each one
- to ``iterator()``. If ``iterator()`` returns ``true``, the object is kept.
- If it returns ``false``, the object will be freed. If the ``iterator()``
- function returns ``true``, it must perform any appropriate refcount
- incrementing on the object before returning.
-
- The internal tree will be packed down if possible as part of the iteration
- to reduce the number of nodes in it.
-
- The ``iterator_data`` is passed directly to ``iterator()`` and is otherwise
- ignored by the function.
-
- The function will return ``0`` if successful and ``-ENOMEM`` if there wasn't
- enough memory.
-
- It is possible for other threads to iterate over or search the array under
- the RCU read lock while this function is in progress. The caller should
- lock exclusively against other modifiers of the array.
-
-
-Access Functions
-----------------
-
-There are two functions for accessing an associative array:
-
-1. Iterate over all the objects in an associative array::
-
- int assoc_array_iterate(const struct assoc_array *array,
- int (*iterator)(const void *object,
- void *iterator_data),
- void *iterator_data);
-
- This passes each object in the array to the iterator callback function.
- ``iterator_data`` is private data for that function.
-
- This may be used on an array at the same time as the array is being
- modified, provided the RCU read lock is held. Under such circumstances,
- it is possible for the iteration function to see some objects twice. If
- this is a problem, then modification should be locked against. The
- iteration algorithm should not, however, miss any objects.
-
- The function will return ``0`` if no objects were in the array or else it
- will return the result of the last iterator function called. Iteration
- stops immediately if any call to the iteration function results in a
- non-zero return.
-
-
-2. Find an object in an associative array::
-
- void *assoc_array_find(const struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key);
-
- This walks through the array's internal tree directly to the object
- specified by the index key.
-
- This may be used on an array at the same time as the array is being
- modified, provided the RCU read lock is held.
-
- The function will return the object if found (and set ``*_type`` to the
- object type) or will return ``NULL`` if the object was not found.
-
-
-Index Key Form
---------------
-
-The index key can be of any form, but since the algorithms aren't told how long
-the key is, it is strongly recommended that the index key includes its length
-very early on before any variation due to the length would have an effect on
-comparisons.
-
-This will cause leaves with different length keys to scatter away from each
-other - and those with the same length keys to cluster together.
-
-It is also recommended that the index key begin with a hash of the rest of the
-key to maximise scattering throughout keyspace.
-
-The better the scattering, the wider and lower the internal tree will be.
-
-Poor scattering isn't too much of a problem as there are shortcuts and nodes
-can contain mixtures of leaves and metadata pointers.
-
-The index key is read in chunks of machine word. Each chunk is subdivided into
-one nibble (4 bits) per level, so on a 32-bit CPU this is good for 8 levels and
-on a 64-bit CPU, 16 levels. Unless the scattering is really poor, it is
-unlikely that more than one word of any particular index key will have to be
-used.
-
-
-Internal Workings
-=================
-
-The associative array data structure has an internal tree. This tree is
-constructed of two types of metadata blocks: nodes and shortcuts.
-
-A node is an array of slots. Each slot can contain one of four things:
-
-* A NULL pointer, indicating that the slot is empty.
-* A pointer to an object (a leaf).
-* A pointer to a node at the next level.
-* A pointer to a shortcut.
-
-
-Basic Internal Tree Layout
---------------------------
-
-Ignoring shortcuts for the moment, the nodes form a multilevel tree. The index
-key space is strictly subdivided by the nodes in the tree and nodes occur on
-fixed levels. For example::
-
- Level: 0 1 2 3
- =============== =============== =============== ===============
- NODE D
- NODE B NODE C +------>+---+
- +------>+---+ +------>+---+ | | 0 |
- NODE A | | 0 | | | 0 | | +---+
- +---+ | +---+ | +---+ | : :
- | 0 | | : : | : : | +---+
- +---+ | +---+ | +---+ | | f |
- | 1 |---+ | 3 |---+ | 7 |---+ +---+
- +---+ +---+ +---+
- : : : : | 8 |---+
- +---+ +---+ +---+ | NODE E
- | e |---+ | f | : : +------>+---+
- +---+ | +---+ +---+ | 0 |
- | f | | | f | +---+
- +---+ | +---+ : :
- | NODE F +---+
- +------>+---+ | f |
- | 0 | NODE G +---+
- +---+ +------>+---+
- : : | | 0 |
- +---+ | +---+
- | 6 |---+ : :
- +---+ +---+
- : : | f |
- +---+ +---+
- | f |
- +---+
-
-In the above example, there are 7 nodes (A-G), each with 16 slots (0-f).
-Assuming no other meta data nodes in the tree, the key space is divided
-thusly:
-
- =========== ====
- KEY PREFIX NODE
- =========== ====
- 137* D
- 138* E
- 13[0-69-f]* C
- 1[0-24-f]* B
- e6* G
- e[0-57-f]* F
- [02-df]* A
- =========== ====
-
-So, for instance, keys with the following example index keys will be found in
-the appropriate nodes:
-
- =============== ======= ====
- INDEX KEY PREFIX NODE
- =============== ======= ====
- 13694892892489 13 C
- 13795289025897 137 D
- 13889dde88793 138 E
- 138bbb89003093 138 E
- 1394879524789 12 C
- 1458952489 1 B
- 9431809de993ba \- A
- b4542910809cd \- A
- e5284310def98 e F
- e68428974237 e6 G
- e7fffcbd443 e F
- f3842239082 \- A
- =============== ======= ====
-
-To save memory, if a node can hold all the leaves in its portion of keyspace,
-then the node will have all those leaves in it and will not have any metadata
-pointers - even if some of those leaves would like to be in the same slot.
-
-A node can contain a heterogeneous mix of leaves and metadata pointers.
-Metadata pointers must be in the slots that match their subdivisions of key
-space. The leaves can be in any slot not occupied by a metadata pointer. It
-is guaranteed that none of the leaves in a node will match a slot occupied by a
-metadata pointer. If the metadata pointer is there, any leaf whose key matches
-the metadata key prefix must be in the subtree that the metadata pointer points
-to.
-
-In the above example list of index keys, node A will contain:
-
- ==== =============== ==================
- SLOT CONTENT INDEX KEY (PREFIX)
- ==== =============== ==================
- 1 PTR TO NODE B 1*
- any LEAF 9431809de993ba
- any LEAF b4542910809cd
- e PTR TO NODE F e*
- any LEAF f3842239082
- ==== =============== ==================
-
-and node B:
-
- ==== =============== ==================
- SLOT CONTENT INDEX KEY (PREFIX)
- ==== =============== ==================
- 3 PTR TO NODE C 13*
- any LEAF 1458952489
- ==== =============== ==================
-
-
-Shortcuts
----------
-
-Shortcuts are metadata records that jump over a piece of keyspace. A shortcut
-is a replacement for a series of single-occupancy nodes ascending through the
-levels. Shortcuts exist to save memory and to speed up traversal.
-
-It is possible for the root of the tree to be a shortcut - say, for example,
-the tree contains at least 17 nodes all with key prefix ``1111``. The
-insertion algorithm will insert a shortcut to skip over the ``1111`` keyspace
-in a single bound and get to the fourth level where these actually become
-different.
-
-
-Splitting And Collapsing Nodes
-------------------------------
-
-Each node has a maximum capacity of 16 leaves and metadata pointers. If the
-insertion algorithm finds that it is trying to insert a 17th object into a
-node, that node will be split such that at least two leaves that have a common
-key segment at that level end up in a separate node rooted on that slot for
-that common key segment.
-
-If the leaves in a full node and the leaf that is being inserted are
-sufficiently similar, then a shortcut will be inserted into the tree.
-
-When the number of objects in the subtree rooted at a node falls to 16 or
-fewer, then the subtree will be collapsed down to a single node - and this will
-ripple towards the root if possible.
-
-
-Non-Recursive Iteration
------------------------
-
-Each node and shortcut contains a back pointer to its parent and the number of
-slot in that parent that points to it. None-recursive iteration uses these to
-proceed rootwards through the tree, going to the parent node, slot N + 1 to
-make sure progress is made without the need for a stack.
-
-The backpointers, however, make simultaneous alteration and iteration tricky.
-
-
-Simultaneous Alteration And Iteration
--------------------------------------
-
-There are a number of cases to consider:
-
-1. Simple insert/replace. This involves simply replacing a NULL or old
- matching leaf pointer with the pointer to the new leaf after a barrier.
- The metadata blocks don't change otherwise. An old leaf won't be freed
- until after the RCU grace period.
-
-2. Simple delete. This involves just clearing an old matching leaf. The
- metadata blocks don't change otherwise. The old leaf won't be freed until
- after the RCU grace period.
-
-3. Insertion replacing part of a subtree that we haven't yet entered. This
- may involve replacement of part of that subtree - but that won't affect
- the iteration as we won't have reached the pointer to it yet and the
- ancestry blocks are not replaced (the layout of those does not change).
-
-4. Insertion replacing nodes that we're actively processing. This isn't a
- problem as we've passed the anchoring pointer and won't switch onto the
- new layout until we follow the back pointers - at which point we've
- already examined the leaves in the replaced node (we iterate over all the
- leaves in a node before following any of its metadata pointers).
-
- We might, however, re-see some leaves that have been split out into a new
- branch that's in a slot further along than we were at.
-
-5. Insertion replacing nodes that we're processing a dependent branch of.
- This won't affect us until we follow the back pointers. Similar to (4).
-
-6. Deletion collapsing a branch under us. This doesn't affect us because the
- back pointers will get us back to the parent of the new node before we
- could see the new node. The entire collapsed subtree is thrown away
- unchanged - and will still be rooted on the same slot, so we shouldn't
- process it a second time as we'll go back to slot + 1.
-
-.. note::
-
- Under some circumstances, we need to simultaneously change the parent
- pointer and the parent slot pointer on a node (say, for example, we
- inserted another node before it and moved it up a level). We cannot do
- this without locking against a read - so we have to replace that node too.
-
- However, when we're changing a shortcut into a node this isn't a problem
- as shortcuts only have one slot and so the parent slot number isn't used
- when traversing backwards over one. This means that it's okay to change
- the slot number first - provided suitable barriers are used to make sure
- the parent slot number is read after the back pointer.
-
-Obsolete blocks and leaves are freed up after an RCU grace period has passed,
-so as long as anyone doing walking or iteration holds the RCU read lock, the
-old superstructure should not go away on them.
diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/index.rst
index 92f91c6a0d79..d56c60663b0c 100644
--- a/Documentation/core-api/index.rst
+++ b/Documentation/core-api/index.rst
@@ -38,7 +38,6 @@ Library functionality that is used throughout the kernel.
kobject
kref
cleanup
- assoc_array
folio_queue
xarray
maple_tree
diff --git a/Documentation/translations/zh_CN/core-api/assoc_array.rst b/Documentation/translations/zh_CN/core-api/assoc_array.rst
deleted file mode 100644
index 3649bf0d1488..000000000000
--- a/Documentation/translations/zh_CN/core-api/assoc_array.rst
+++ /dev/null
@@ -1,473 +0,0 @@
-.. include:: ../disclaimer-zh_CN.rst
-
-:Original: Documentation/core-api/assoc_array.rst
-
-:ç¿»è¯:
-
- å¸å»¶è
¾ Yanteng Si <siyanteng at loongson.cn>
-
-:æ ¡è¯:
-
-
-
-.. _cn_core-api_assoc_array:
-
-==================
-éç¨å
³èæ°ç»çå®ç°
-==================
-
-ç®ä»
-====
-
-è¿ä¸ªå
³èæ°ç»çå®ç°æ¯ä¸ä¸ªå
·æä»¥ä¸å±æ§ç对象容å¨:
-
-1. 对象æ¯ä¸éæçæéã该å®ç°ä¸å
³å¿å®ä»¬æååªéï¼å¦ææçè¯ï¼æå®ä»¬æåä»ä¹ï¼å¦ææç
- è¯ï¼ã
-
- .. note::
-
- æå对象çæé *å¿
é¡»* 卿尿æä½ä¸ºé¶ã
-
-2. 对象ä¸éè¦å
å«ä¾æ°ç»ä½¿ç¨ç龿¥åãè¿å
许ä¸ä¸ªå¯¹è±¡åæ¶ä½äºå¤ä¸ªæ°ç»ä¸ãç¸åï¼æ°ç»æ¯
- ç±æå对象çå
æ°æ®åç»æçã
-
-3. 对象éè¦ç´¢å¼é®æ¥å®ä½å®ä»¬å¨éµåä¸çä½ç½®ã
-
-4. ç´¢å¼é®å¿
é¡»æ¯å¯ä¸çãæå
¥ä¸ä¸ªä¸å·²ç»å¨æ°ç»ä¸çä¸å
·æç¸åé®å¼ç对象å°å代æ§ç对象ã
-
-5. ç´¢å¼é®å¯ä»¥æ¯ä»»ä½é¿åº¦ï¼ä¹å¯ä»¥æ¯ä¸åçé¿åº¦ã
-
-6. ç´¢å¼é®åºè¯¥å¨æ©æå°±å¯¹é¿åº¦è¿è¡ç¼ç ï¼å³å¨ä»»ä½ç±äºé¿åº¦å¼èµ·çåååºç°ä¹åã
-
-7. ç´¢å¼é®å¯ä»¥å
æ¬ä¸ä¸ªåå¸å¼ï¼ä»¥ä¾¿å°å¯¹è±¡åæ£å°æ´ä¸ªæ°ç»ä¸ã
-
-8. 该æ°ç»å¯ä»¥è¿ä»£ã对象ä¸ä¸å®ä¼æç´¢å¼é®ç顺åºåºç°ã
-
-9. æ°ç»å¯ä»¥å¨è¢«ä¿®æ¹çæ¶åè¿è¡è¿ä»£ï¼åªè¦RCUç读é被è¿ä»£å¨ææãç¶èï¼è¯·æ³¨æï¼å¨è¿ç§æ
- åµä¸ï¼ä¸äºå¯¹è±¡å¯è½ä¼è¢«çå°ä¸æ¢ä¸æ¬¡ãå¦æè¿æ¯ä¸ªé®é¢ï¼è¿ä»£å¨åºè¯¥éå®ä»¥é²æ¢ä¿®æ¹ãç¶
- èï¼é¤éå é¤ï¼å¦å对象ä¸ä¼è¢«éè¿ã
-
-10. æ°ç»ä¸ç对象å¯ä»¥éè¿å
¶ç´¢å¼é®è¿è¡æ¥è¯¢ã
-
-11. 彿°ç»è¢«ä¿®æ¹æ¶ï¼å¯¹è±¡å¯ä»¥è¢«æ¥è¯¢ï¼åææ¯è¿è¡æ¥è¯¢ççº¿ç¨ææRCUç读éã
-
-该å®ç°å¨å
é¨ä½¿ç¨äºä¸æ£µç±16个æéèç¹ç»æçæ ï¼è¿äºèç¹å¨æ¯ä¸å±é½ç±ç´¢å¼é®çå°æ°ç¹è¿è¡ç´¢
-å¼ï¼å
¶æ¹å¼ä¸åºæ°æ ç¸åãä¸ºäºæé«å
åæçï¼å¯ä»¥æ¾ç½®å¿«æ·é®ï¼ä»¥è·³è¿æ¬æ¥æ¯ä¸ç³»ååå èç¹çå°
-æ¹ãæ¤å¤ï¼èç¹å°å¶å对象æéæå
å°èç¹ç空é²ç©ºé´ä¸ï¼è䏿¯åä¸ä¸ªé¢å¤ç忝ï¼ç´å°æå¯¹è±¡
-éè¦è¢«æ·»å å°ä¸ä¸ªå®æ´çèç¹ä¸ã
-
-å
¬ç¨API
-=======
-
-å
¬ç¨APIå¯ä»¥å¨ ``<linux/assoc_array.h>`` 䏿¾å°ãå
³èæ°ç»çæ ¹æ¯ä»¥ä¸ç»æ::
-
- struct assoc_array {
- ...
- };
-
-è¯¥ä»£ç æ¯éè¿å¯ç¨ ``CONFIG_ASSOCIATIVE_ARRAY`` æ¥éæ©çï¼ä»¥::
-
- ./script/config -e ASSOCIATIVE_ARRAY
-
-
-ç¼è¾èæ¬
---------
-
-æå
¥åå é¤åè½ä¼äº§çä¸ä¸ªâç¼è¾èæ¬âï¼ä»¥åå¯ä»¥åºç¨è¿ä¸ªèæ¬æ¥å®ç°æ´æ¹ï¼èä¸ä¼é æ ``ENOMEM``
-é£é©ãè¿ä¿çäºå°è¢«å®è£
å¨å
鍿 ä¸çé¢åé
çå
æ°æ®åï¼å¹¶è·è¸ªåºç¨èæ¬æ¶å°ä»æ ä¸å é¤çå
æ°
-æ®åã
-
-å¨èæ¬åºç¨åï¼è¿ä¹è¢«ç¨æ¥è·è¸ªæ»ååæ»å¯¹è±¡ï¼ä»¥ä¾¿ä»¥åå¯ä»¥éæ¾å®ä»¬ãéæ¾æ¯å¨RCU宽éæè¿å
-è¿è¡ç--å æ¤å
许访é®åè½å¨RCU读éä¸è¿è¡ã
-
-èæ¬å¨APIä¹å¤æ¾ç¤ºä¸ºä¸ä¸ªç±»å为::
-
- struct assoc_array_edit;
-
-æä¸¤ä¸ªå¤çèæ¬çåè½:
-
-1. åºç¨ä¸ä¸ªç¼è¾èæ¬::
-
- void assoc_array_apply_edit(struct assoc_array_edit *edit);
-
-è¿å°æ§è¡ç¼è¾åè½ï¼æå¼åç§åå±éï¼ä»¥å
许å¨RCU读éä¸ç访é®ç»§ç»è¿è¡ãç¶åï¼ç¼è¾èæ¬å°è¢«
-ä¼ éç» ``call_rcu()`` ï¼ä»¥éæ¾å®å宿æåç任使»çä¸è¥¿ã
-
-2. Cancel an edit script::
-
- void assoc_array_cancel_edit(struct assoc_array_edit *edit);
-
-è¿å°ç«å³éæ¾ç¼è¾èæ¬åææé¢åé
çå
åãå¦æè¿æ¯ä¸ºäºæå
¥ï¼æ°ç对象ä¸ä¼è¢«è¿ä¸ªå½æ°éæ¾ï¼
-èæ¯å¿
é¡»ç±è°ç¨è
éæ¾ã
-
-è¿äºå½æ°ä¿è¯ä¸ä¼å¤±è´¥ã
-
-
-æä½è¡¨
-------
-
-åç§åè½éç¨äºä¸ä¸ªæä½è¡¨::
-
- struct assoc_array_ops {
- ...
- };
-
-è¿æåºäºä¸äºæ¹æ³ï¼ææè¿äºæ¹æ³é½éè¦æä¾:
-
-1. ä»è°ç¨è
æ°æ®ä¸è·åç´¢å¼é®çä¸ä¸ªå::
-
- unsigned long (*get_key_chunk)(const void *index_key, int level);
-
-è¿åºè¯¥è¿åä¸ä¸ªç±è°ç¨è
æä¾çç´¢å¼é®çåï¼ä»levelåæ°ç»åºç *æ¯ç¹* ä½ç½®å¼å§ãlevelåæ°å°
-æ¯ ``ASSOC_ARRAY_KEY_CHUNK_SIZE`` çåæ°ï¼è¯¥å½æ°åºè¿å ``ASSOC_ARRAY_KEY_CHUNK_SIZE``
-ä½ãä¸å¯è½åºç°é误ã
-
-
-2. è·åä¸ä¸ªå¯¹è±¡çç´¢å¼é®çä¸ä¸ªå::
-
- unsigned long (*get_object_key_chunk)(const void *object, int level);
-
-ååé¢ç彿°ä¸æ ·ï¼ä½æ¯ä»æ°ç»ä¸çä¸ä¸ªå¯¹è±¡è䏿¯ä»è°ç¨è
æä¾çç´¢å¼é®ä¸è·åæ°æ®ã
-
-
-3. ççè¿æ¯å¦æ¯æä»¬è¦æ¾ç对象::
-
- bool (*compare_object)(const void *object, const void *index_key);
-
-å°å¯¹è±¡ä¸ä¸ä¸ªç´¢å¼é®è¿è¡æ¯è¾ï¼å¦æå¹é
åè¿å ``true`` ï¼ä¸å¹é
åè¿å ``false`` ã
-
-
-4. 对两个对象çç´¢å¼é®è¿è¡æ¯è¾::
-
- int (*diff_objects)(const void *object, const void *index_key);
-
-è¿åæå®å¯¹è±¡çç´¢å¼é®ä¸ç»å®ç´¢å¼é®ä¸åçæ¯ç¹ä½ç½®ï¼å¦æå®ä»¬ç¸åï¼åè¿å-1ã
-
-
-5. éæ¾ä¸ä¸ªå¯¹è±¡::
-
- void (*free_object)(void *object);
-
-éæ¾æå®çå¯¹è±¡ãæ³¨æï¼è¿å¯è½æ¯å¨è°ç¨ ``assoc_array_apply_edit()`` åçä¸ä¸ªRCU宽éæå
-è°ç¨çï¼æä»¥å¨æ¨¡åå¸è½½æ¶å¯è½éè¦ ``synchronize_rcu()`` ã
-
-
-ææ§å½æ°
---------
-
-æä¸äºå½æ°ç¨äºææ§å
³èæ°ç»:
-
-1. åå§åä¸ä¸ªå
³èæ°ç»::
-
- void assoc_array_init(struct assoc_array *array);
-
-è¿å°åå§åä¸ä¸ªå
³èæ°ç»çåºç¡ç»æãå®ä¸ä¼å¤±è´¥ã
-
-
-2. å¨ä¸ä¸ªå
³èæ°ç»ä¸æå
¥/æ¿æ¢ä¸ä¸ªå¯¹è±¡::
-
- struct assoc_array_edit *
- assoc_array_insert(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key,
- void *object);
-
-è¿å°æç»å®ç对象æå
¥æ°ç»ä¸ã注æï¼æéçæå°ææä½å¿
é¡»æ¯0ï¼å 为å®è¢«ç¨æ¥å¨å
鍿 è®°æéçç±»
-åã
-
-å¦æè¯¥é®å·²ç»åå¨ä¸ä¸ªå¯¹è±¡ï¼é£ä¹å®å°è¢«æ°ç对象æåä»£ï¼æ§ç对象å°è¢«èªå¨éæ¾ã
-
-``index_key`` åæ°åºææç´¢å¼é®ä¿¡æ¯ï¼å¹¶å¨è°ç¨OPP表ä¸çæ¹æ³æ¶ä¼ éç»å®ä»¬ã
-
-è¿ä¸ªå½æ°ä¸å¯¹æ°ç»æ¬èº«å任使¹å¨ï¼èæ¯è¿åä¸ä¸ªå¿
é¡»åºç¨çç¼è¾èæ¬ã妿åºç°å
åä¸è¶³çé误ï¼ä¼
-è¿å ``-ENOMEM`` ã
-
-è°ç¨è
åºä¸é¨é宿°ç»çå
¶ä»ä¿®æ¹å¨ã
-
-
-3. ä»ä¸ä¸ªå
³èæ°ç»ä¸å é¤ä¸ä¸ªå¯¹è±¡::
-
- struct assoc_array_edit *
- assoc_array_delete(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key);
-
-è¿å°ä»æ°ç»ä¸å é¤ä¸ä¸ªç¬¦åæå®æ°æ®ç对象ã
-
-``index_key`` åæ°åºææç´¢å¼é®ä¿¡æ¯ï¼å¹¶å¨è°ç¨OPP表ä¸çæ¹æ³æ¶ä¼ éç»å®ä»¬ã
-
-è¿ä¸ªå½æ°ä¸å¯¹æ°ç»æ¬èº«å任使¹å¨ï¼èæ¯è¿åä¸ä¸ªå¿
é¡»åºç¨çç¼è¾èæ¬ã ``-ENOMEM`` å¨åºç°å
åä¸è¶³
-çé误æ¶è¿åã妿卿°ç»ä¸æ²¡ææ¾å°æå®ç对象ï¼å°è¿å ``NULL`` ã
-
-è°ç¨è
åºè¯¥å¯¹æ°ç»çå
¶ä»ä¿®æ¹è
è¿è¡ä¸é¨éå®ã
-
-
-4. ä»ä¸ä¸ªå
³èæ°ç»ä¸å 餿æå¯¹è±¡::
-
- struct assoc_array_edit *
- assoc_array_clear(struct assoc_array *array,
- const struct assoc_array_ops *ops);
-
-è¿ä¸ªå½æ°å é¤äºä¸ä¸ªå
³èæ°ç»ä¸çææå¯¹è±¡ï¼ä½¿å
¶å®å
¨ä¸ºç©ºã
-
-è¿ä¸ªå½æ°æ²¡æå¯¹æ°ç»æ¬èº«å任使¹å¨ï¼èæ¯è¿åä¸ä¸ªå¿
é¡»åºç¨çç¼è¾èæ¬ã妿åºç°å
åä¸è¶³
-çé误ï¼åè¿å ``-ENOMEM`` ã
-
-è°ç¨è
åºä¸é¨é宿°ç»çå
¶ä»ä¿®æ¹è
ã
-
-
-5. 鿝ä¸ä¸ªå
³èæ°ç»ï¼å 餿æå¯¹è±¡::
-
- void assoc_array_destroy(struct assoc_array *array,
- const struct assoc_array_ops *ops);
-
-è¿å°ç ´åå
³èæ°ç»çå
容ï¼ä½¿å
¶å®å
¨ä¸ºç©ºãå¨è¿ä¸ªå½æ°éæ¯æ°ç»çåæ¶ï¼ä¸å
许å¦ä¸ä¸ªçº¿ç¨å¨RCU读é
-ä¸éåæ°ç»ï¼å 为å¨å
åéæ¾æ¶ä¸æ§è¡RCUå»¶è¿ï¼è¿éè¦åé
å
åã
-
-è°ç¨è
åºè¯¥ä¸é¨é对æ°ç»çå
¶ä»ä¿®æ¹è
å访é®è
è¿è¡éå®ã
-
-
-6. åå¾åæ¶ä¸ä¸ªå
³èæ°ç»::
-
- int assoc_array_gc(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- bool (*iterator)(void *object, void *iterator_data),
- void *iterator_data);
-
-è¿æ¯å¯¹ä¸ä¸ªå
³èæ°ç»ä¸ç对象è¿è¡è¿ä»£ï¼å¹¶å°æ¯ä¸ªå¯¹è±¡ä¼ éç» ``iterator()`` ã妿 ``iterator()`` è¿å
-trueï¼è¯¥å¯¹è±¡è¢«ä¿çã妿å®è¿å ``false`` ï¼è¯¥å¯¹è±¡å°è¢«éæ¾ã妿 ``iterator()`` 彿°è¿å ``true`` ï¼å®å¿
é¡»
-å¨è¿åä¹å对该对象è¿è¡éå½ç ``refcount`` éå¢ã
-
-妿å¯è½çè¯ï¼å
鍿 å°è¢«æå
䏿¥ï¼ä½ä¸ºè¿ä»£çä¸é¨åï¼ä»¥åå°å
¶ä¸çèç¹æ°éã
-
-``iterator_data`` è¢«ç´æ¥ä¼ éç» ``iterator()`` ï¼å¦åä¼è¢«å½æ°å¿½ç¥ã
-
-妿æåï¼è¯¥å½æ°å°è¿å ``0`` ï¼å¦ææ²¡æè¶³å¤çå
åï¼åè¿å ``-ENOMEM`` ã
-
-å¨è¿ä¸ªå½æ°æ§è¡è¿ç¨ä¸ï¼å
¶ä»çº¿ç¨æå¯è½å¨RCU读éä¸è¿ä»£ææç´¢éµåãè°ç¨è
åºè¯¥ä¸é¨é对æ°ç»çå
¶ä»
-ä¿®æ¹è
è¿è¡éå®ã
-
-
-访é®å½æ°
---------
-
-æä¸¤ä¸ªå½æ°ç¨äºè®¿é®ä¸ä¸ªå
³èæ°ç»:
-
-1. éåä¸ä¸ªå
³èæ°ç»ä¸çææå¯¹è±¡::
-
- int assoc_array_iterate(const struct assoc_array *array,
- int (*iterator)(const void *object,
- void *iterator_data),
- void *iterator_data);
-
-è¿å°æ°ç»ä¸çæ¯ä¸ªå¯¹è±¡ä¼ éç»è¿ä»£å¨åè°å½æ°ã ``iterator_data`` æ¯è¯¥å½æ°çç§ææ°æ®ã
-
-卿°ç»è¢«ä¿®æ¹çåæ¶ï¼å¯ä»¥å¨æ°ç»ä¸ä½¿ç¨è¿ä¸ªæ¹æ³ï¼åææ¯RCU读é被ææãå¨è¿ç§æ
åµä¸ï¼è¿ä»£å½æ°æ
-å¯è½ä¸¤æ¬¡çå°æäºå¯¹è±¡ãå¦æè¿æ¯ä¸ªé®é¢ï¼é£ä¹ä¿®æ¹åºè¯¥è¢«éå®ãç¶èï¼è¿ä»£ç®æ³ä¸åºè¯¥éè¿ä»»ä½å¯¹è±¡ã
-
-妿æ°ç»ä¸æ²¡æå¯¹è±¡ï¼è¯¥å½æ°å°è¿å ``0`` ï¼å¦åå°è¿åæå䏿¬¡è°ç¨çè¿ä»£å¨å½æ°çç»æã妿坹è¿ä»£å½æ°
-çä»»ä½è°ç¨å¯¼è´éé¶è¿åï¼è¿ä»£ç«å³åæ¢ã
-
-
-2. å¨ä¸ä¸ªå
³èæ°ç»ä¸å¯»æ¾ä¸ä¸ªå¯¹è±¡::
-
- void *assoc_array_find(const struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key);
-
-è¿å°ç´æ¥ç©¿è¿æ°ç»çå
鍿 ï¼å°è¾¾ç´¢å¼é®ææå®ç对象ã
-
-è¿ä¸ªå½æ°å¯ä»¥å¨ä¿®æ¹æ°ç»çåæ¶ç¨å¨æ°ç»ä¸ï¼åææ¯RCU读é被ææã
-
-妿æ¾å°å¯¹è±¡ï¼è¯¥å½æ°å°è¿å对象ï¼å¹¶å° ``*_type`` 设置为对象çç±»åï¼ï¼å¦ææ²¡ææ¾å°å¯¹è±¡ï¼å°è¿å ``NULL`` ã
-
-
-ç´¢å¼é®å½¢å¼
-----------
-
-ç´¢å¼é®å¯ä»¥æ¯ä»»ä½å½¢å¼çï¼ä½æ¯ç±äºç®æ³æ²¡æè¢«åç¥é®æå¤é¿ï¼æä»¥å¼ºç建议å¨ä»»ä½ç±äºé¿åº¦è产ççåå
-对æ¯è¾äº§çå½±åä¹åï¼ç´¢å¼é®åºè¯¥å¾æ©å°±å
æ¬å
¶é¿åº¦ã
-
-è¿å°å¯¼è´å
·æä¸åé¿åº¦é®çå¶åç¸äºåæ£ï¼èå
·æç¸åé¿åº¦é®çå¶ååèéå¨ä¸èµ·ã
-
-æä»¬è¿å»ºè®®ç´¢å¼é®ä»¥é®çå
¶ä½é¨åçåå¸å¼å¼å§ï¼ä»¥æå¤§éåº¦å°æé«æ´ä¸ªé®ç©ºé´çæ£å¸æ
åµã
-
-忣æ§è¶å¥½ï¼å
鍿 å°±è¶å®½ï¼è¶ä½ã
-
-忣æ§å·®å¹¶ä¸æ¯ä¸ä¸ªå¤ªå¤§çé®é¢ï¼å 为æå¿«æ·é®ï¼èç¹å¯ä»¥å
å«å¶ååå
æ°æ®æéçæ··åç©ã
-
-ç´¢å¼é®æ¯ä»¥æºå¨åçåç¶æ¥è¯»åçãæ¯ä¸ªå被ç»å为æ¯å±ä¸ä¸ªnibbleï¼4æ¯ç¹ï¼ï¼æä»¥å¨32ä½CPUä¸è¿éå8å±ï¼
-å¨64ä½CPUä¸éå16å±ãé¤éæ£å¸æ
åµççå¾å·®ï¼å¦åä¸å¤ªå¯è½æè¶
è¿ä¸ä¸ªåçä»»ä½ç¹å®ç´¢å¼é®éè¦è¢«ä½¿ç¨ã
-
-
-å
é¨å·¥ä½æºå¶
-============
-
-å
³èæ°ç»æ°æ®ç»ææä¸ä¸ªå
鍿 ãè¿ä¸ªæ ç±ä¸¤ç§ç±»åçå
æ°æ®åææï¼èç¹åå¿«æ·é®ã
-
-ä¸ä¸ªèç¹æ¯ä¸ä¸ªæ§½çæ°ç»ãæ¯ä¸ªæ§½å¯ä»¥å
å«ä»¥ä¸åç§ä¸è¥¿ä¹ä¸:
-
-* ä¸ä¸ªNULLçæéï¼è¡¨ç¤ºæ§½æ¯ç©ºçã
-* ä¸ä¸ªæå对象ï¼å¶åï¼çæéã
-* ä¸ä¸ªæåä¸ä¸çº§èç¹çæéã
-* ä¸ä¸ªæåå¿«æ·é®çæéã
-
-
-åºæ¬çå
鍿 å½¢å¸å±
-------------------
-
-ææ¶ä¸èèå¿«æ·é®ï¼èç¹å½¢æä¸ä¸ªå¤çº§æ ãç´¢å¼é®ç©ºé´è¢«æ ä¸çèç¹ä¸¥æ ¼ç»åï¼èç¹åºç°å¨åºå®ç屿¬¡ä¸ãä¾å¦::
-
- Level: 0 1 2 3
- =============== =============== =============== ===============
- NODE D
- NODE B NODE C +------>+---+
- +------>+---+ +------>+---+ | | 0 |
- NODE A | | 0 | | | 0 | | +---+
- +---+ | +---+ | +---+ | : :
- | 0 | | : : | : : | +---+
- +---+ | +---+ | +---+ | | f |
- | 1 |---+ | 3 |---+ | 7 |---+ +---+
- +---+ +---+ +---+
- : : : : | 8 |---+
- +---+ +---+ +---+ | NODE E
- | e |---+ | f | : : +------>+---+
- +---+ | +---+ +---+ | 0 |
- | f | | | f | +---+
- +---+ | +---+ : :
- | NODE F +---+
- +------>+---+ | f |
- | 0 | NODE G +---+
- +---+ +------>+---+
- : : | | 0 |
- +---+ | +---+
- | 6 |---+ : :
- +---+ +---+
- : : | f |
- +---+ +---+
- | f |
- +---+
-
-å¨ä¸è¿°ä¾åä¸ï¼æ7个èç¹ï¼A-Gï¼ï¼æ¯ä¸ªèç¹æ16个槽ï¼0-fï¼ãå设æ 䏿²¡æå
¶ä»å
æ°æ®èç¹ï¼é£ä¹å¯é¥ç©ºé´
-æ¯è¿æ ·ååç::
-
- KEY PREFIX NODE
- ========== ====
- 137* D
- 138* E
- 13[0-69-f]* C
- 1[0-24-f]* B
- e6* G
- e[0-57-f]* F
- [02-df]* A
-
-å æ¤ï¼ä¾å¦ï¼å
·æä»¥ä¸ç¤ºä¾ç´¢å¼é®çé®å°å¨éå½çèç¹ä¸è¢«æ¾å°::
-
- INDEX KEY PREFIX NODE
- =============== ======= ====
- 13694892892489 13 C
- 13795289025897 137 D
- 13889dde88793 138 E
- 138bbb89003093 138 E
- 1394879524789 12 C
- 1458952489 1 B
- 9431809de993ba - A
- b4542910809cd - A
- e5284310def98 e F
- e68428974237 e6 G
- e7fffcbd443 e F
- f3842239082 - A
-
-为äºèçå
åï¼å¦æä¸ä¸ªèç¹å¯ä»¥å®¹çº³å®çé£é¨åé®ç©ºé´ä¸çææå¶åï¼é£ä¹è¿ä¸ªèç¹å°æææè¿äºå¶åï¼èä¸
-伿任ä½å
æ°æ®æéââå³ä½¿å
¶ä¸ä¸äºå¶åæ³å¨åä¸ä¸ªæ§½ä¸ã
-
-ä¸ä¸ªèç¹å¯ä»¥å
å«å¶ååå
æ°æ®æéçå¼è´¨æ§æ··åãå
æ°æ®æéå¿
é¡»å¨ä¸å®ä»¬çå
³é®ç©ºé´çç»åç¸å¹é
çæ§½ä¸ã
-å¶åå¯ä»¥å¨ä»»ä½æ²¡æè¢«å
æ°æ®æéå æ®çæ§½ä¸ãä¿è¯ä¸ä¸ªèç¹ä¸æ²¡æä¸ä¸ªå¶åä¼ä¸å
æ°æ®æéå æ®çæ§½ç¸å¹é
ã
-妿å
æ°æ®æéå¨é£éï¼ä»»ä½é®ä¸å
æ°æ®é®åç¼ç¸å¹é
çå¶å¿
é¡»å¨å
æ°æ®æéæåçåæ ä¸ã
-
-å¨ä¸é¢çç´¢å¼é®çä¾åå表ä¸ï¼èç¹Aå°å
å«::
-
- SLOT CONTENT INDEX KEY (PREFIX)
- ==== =============== ==================
- 1 PTR TO NODE B 1*
- any LEAF 9431809de993ba
- any LEAF b4542910809cd
- e PTR TO NODE F e*
- any LEAF f3842239082
-
-åèç¹B::
-
- 3 PTR TO NODE C 13*
- any LEAF 1458952489
-
-
-å¿«æ·é®
----------
-
-å¿«æ·é®æ¯è·³è¿ä¸åé®ç©ºé´çå
æ°æ®è®°å½ãå¿«æ·é®æ¯ä¸ç³»åéè¿å±æ¬¡ä¸åçåå èç¹çæ¿ä»£ç©ãå¿«æ·é®çå卿¯
-为äºèçå
ååå å¿«éåé度ã
-
-æ çæ ¹é¨æå¯è½æ¯ä¸ä¸ªå¿«æ·é®ââæ¯å¦è¯´ï¼æ è³å°å
å«17个èç¹ï¼é½æé®åç¼ ``1111`` ãæå
¥ç®æ³å°æå
¥ä¸ä¸ªå¿«æ·é®ï¼
-以忬¡è·³è¿ ``1111`` çé®ä½ï¼å¹¶å°è¾¾ç¬¬åå±ï¼å¨è¿éï¼è¿äºé®ä½å®é
ä¸åå¾ä¸åã
-
-
-æåååå¹¶èç¹
-------------------------------
-
-æ¯ä¸ªèç¹çæå¤§å®¹é为16个å¶ååå
æ°æ®æéã妿æå
¥ç®æ³åç°å®æ£è¯å¾å°ä¸ä¸ªç¬¬17个对象æå
¥å°ä¸ä¸ªèç¹ä¸ï¼
-该èç¹å°è¢«æåï¼ä½¿å¾è³å°ä¸¤ä¸ªå¨è¯¥å±æä¸ä¸ªå
񆇆
³é®æ®µçå¶åæç»å¨ä¸ä¸ªåç¬çèç¹ä¸ï¼è¯¥å
񆇆
³é®æ®µçæ ¹
-å¨è¯¥æ§½ä¸ã
-
-妿ä¸ä¸ªå®æ´çèç¹ä¸çå¶åå被æå
¥çå¶åè¶³å¤ç¸ä¼¼ï¼é£ä¹å°±ä¼å¨æ 䏿å
¥ä¸ä¸ªå¿«æ·é®ã
-
-彿 ¹æ¤äºæä¸ªèç¹çåæ ä¸ç对象æ°éä¸éå°16ä¸ªææ´å°æ¶ï¼é£ä¹è¯¥åæ å°è¢«åå¹¶æä¸ä¸ªåç¬çèç¹ââ妿å¯è½ç
-è¯ï¼è¿å°åæ ¹é¨æ©æ£ã
-
-
-ééå½å¼è¿ä»£
-------------
-
-æ¯ä¸ªèç¹åå¿«æ·é®é½å
å«ä¸ä¸ªæåå
¶ç¶èç¹çåç½®æéï¼ä»¥å该ç¶èç¹ä¸æåå®çæ§½æ°ãééå½è¿ä»£ä½¿ç¨è¿äºæ¥
-éè¿æ çæ ¹é¨è¿è¡ï¼åå¾ç¶èç¹ï¼æ§½N+1ï¼ä»¥ç¡®ä¿å¨æ²¡æå æ çæ
åµä¸åå¾è¿å±ã
-
-ç¶èï¼ååæé使å¾åæ¶æ¹ååè¿ä»£åå¾å¾æ£æã
-
-
-åæ¶æ¹ååè¿ä»£
---------------
-
-æä¸äºæ
åµéè¦èè:
-
-1. ç®åçæå
¥/æ¿æ¢ãè¿æ¶åå°ç®åå°å°ä¸ä¸ªNULLææ§çå¹é
å¶åçæéæ¿æ¢ä¸ºå±éåçæ°å¶åçæéãå¦åå
æ°
- æ®åä¸ä¼æ¹åãä¸ä¸ªæ§çå¶åç´å°RCU宽éæè¿åæä¼è¢«éæ¾ã
-
-2. ç®åå é¤ãè¿åªæ¯æ¶åå°æ¸
é¤ä¸ä¸ªæ§çå¹é
å¶åãå
æ°æ®åä¸ä¼æå
¶ä»ååãæ§çå¶åç´å°RCU宽éæä¹åæä¼
- è¢«éæ¾ã
-
-3. æå
¥ï¼æ¿æ¢æä»¬è¿æ²¡æè¿å
¥çåæ çä¸é¨åãè¿å¯è½æ¶åå°æ¿æ¢è¯¥åæ çä¸é¨åââä½è¿ä¸ä¼å½±åè¿ä»£ï¼å 为æä»¬
- è¿æ²¡æå°è¾¾å®çæéï¼èä¸ç¥å
åä¹ä¸ä¼è¢«æ¿æ¢ï¼è¿äºåçå¸å±ä¸ä¼æ¹åï¼ã
-
-4. æå
¥æ¿æ¢äºæä»¬æ£å¨å¤ççèç¹ãè¿ä¸æ¯ä¸ä¸ªé®é¢ï¼å 为æä»¬å·²ç»éè¿äºé宿éï¼ç´å°æä»¬è·éåé¢çæéæ
- ä¼åæ¢å°æ°çå¸å±ä¸ââè¿æ¶æä»¬å·²ç»æ£æ¥äºè¢«æ¿æ¢èç¹çå¶åï¼å¨è·éä»»ä½å
æ°æ®æéä¹åï¼æä»¬ä¼è¿ä»£ä¸ä¸ªè
- ç¹çææå¶åï¼ã
-
- ç¶èï¼æä»¬å¯è½ä¼éæ°çå°ä¸äºå¶åï¼è¿äºå¶åå·²ç»è¢«å岿ä¸ä¸ªæ°ç忝ï¼èè¿ä¸ªåæ¯çä½ç½®æ¯æä»¬ä¹åçä½
- ç½®æ´è¿ã
-
-5. æå
¥æ¿æ¢äºæä»¬æ£å¨å¤ççä¾èµåæ¯çèç¹ãè¿ä¸ä¼å½±å尿们ï¼ç´å°æä»¬è·éåé¢çæéãä¸ï¼4ï¼ç±»ä¼¼ã
-
-6. å ææä»¬ä¸é¢çä¸ä¸ªåæ¯ãè¿ä¸ä¼å½±åæä»¬ï¼å ä¸ºå¨æä»¬çå°æ°èç¹ä¹åï¼å溯æéä¼è®©æä»¬åå°æ°èç¹çç¶è
- ç¹ãæ´ä¸ªå´©æºçåæ è¢«ææäºï¼æ²¡æä»»ä½ååââèä¸ä»ç¶ä¼å¨åä¸ä¸ªæ§½ä¸çæ ¹ï¼æä»¥æä»¬ä¸åºè¯¥ç¬¬äºæ¬¡å¤çå®ï¼
- å 为æä»¬ä¼åå°æ§½+1ã
-
-.. note::
-
- å¨æäºæ
åµä¸ï¼æä»¬éè¦åæ¶æ¹åä¸ä¸ªèç¹çç¶æéåç¶æ§½æéï¼æ¯å¦è¯´ï¼æä»¬å¨å®ä¹åæå
¥äºå¦ä¸ä¸ªèç¹ï¼
- å¹¶æå®å¾ä¸ç§»äºä¸å±ï¼ãæä»¬ä¸è½å¨ä¸éå®è¯»åçæ
åµä¸è¿æ ·åââæä»¥æä»¬å¿
é¡»åæ¶æ¿æ¢è¯¥èç¹ã
-
- ç¶èï¼å½æä»¬æä¸ä¸ªå¿«æ·é®æ¹æä¸ä¸ªèç¹æ¶ï¼è¿ä¸æ¯ä¸ä¸ªé®é¢ï¼å 为快æ·é®åªæä¸ä¸ªæ§½ï¼æä»¥å½ååé
- åä¸ä¸ªæ§½æ¶ï¼ä¸ä¼ä½¿ç¨ç¶æ§½å·ãè¿æå³çå
æ¹åæ§½ä½å·æ¯å¯ä»¥çââåªè¦ä½¿ç¨éå½çå±éæ¥ç¡®ä¿ç¶æ§½ä½å·å¨å
- éæéä¹å被读åã
-
-è¿æ¶çååå¶åå¨RCU宽éæè¿åä¼è¢«éæ¾ï¼æä»¥åªè¦ä»»ä½è¿è¡éåæè¿ä»£ç人ææRCU读éï¼æ§çä¸å±å»ºçå°±ä¸
-åºè¯¥å¨ä»ä»¬èº«ä¸æ¶å¤±ã
diff --git a/Documentation/translations/zh_CN/core-api/index.rst b/Documentation/translations/zh_CN/core-api/index.rst
index 453a02cd1f44..ec4b03b2a0a6 100644
--- a/Documentation/translations/zh_CN/core-api/index.rst
+++ b/Documentation/translations/zh_CN/core-api/index.rst
@@ -41,7 +41,6 @@
kobject
kref
- assoc_array
xarray
rbtree
idr
diff --git a/include/linux/assoc_array.h b/include/linux/assoc_array.h
deleted file mode 100644
index 8b3f230ce894..000000000000
--- a/include/linux/assoc_array.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Generic associative array implementation.
- *
- * See Documentation/core-api/assoc_array.rst for information.
- *
- * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells at redhat.com)
- */
-
-#ifndef _LINUX_ASSOC_ARRAY_H
-#define _LINUX_ASSOC_ARRAY_H
-
-#ifdef CONFIG_ASSOCIATIVE_ARRAY
-
-#include <linux/types.h>
-
-#define ASSOC_ARRAY_KEY_CHUNK_SIZE BITS_PER_LONG /* Key data retrieved in chunks of this size */
-
-/*
- * Generic associative array.
- */
-struct assoc_array {
- struct assoc_array_ptr *root; /* The node at the root of the tree */
- unsigned long nr_leaves_on_tree;
-};
-
-/*
- * Operations on objects and index keys for use by array manipulation routines.
- */
-struct assoc_array_ops {
- /* Method to get a chunk of an index key from caller-supplied data */
- unsigned long (*get_key_chunk)(const void *index_key, int level);
-
- /* Method to get a piece of an object's index key */
- unsigned long (*get_object_key_chunk)(const void *object, int level);
-
- /* Is this the object we're looking for? */
- bool (*compare_object)(const void *object, const void *index_key);
-
- /* How different is an object from an index key, to a bit position in
- * their keys? (or -1 if they're the same)
- */
- int (*diff_objects)(const void *object, const void *index_key);
-
- /* Method to free an object. */
- void (*free_object)(void *object);
-};
-
-/*
- * Access and manipulation functions.
- */
-struct assoc_array_edit;
-
-static inline void assoc_array_init(struct assoc_array *array)
-{
- array->root = NULL;
- array->nr_leaves_on_tree = 0;
-}
-
-extern int assoc_array_iterate(const struct assoc_array *array,
- int (*iterator)(const void *object,
- void *iterator_data),
- void *iterator_data);
-extern void *assoc_array_find(const struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key);
-extern void assoc_array_destroy(struct assoc_array *array,
- const struct assoc_array_ops *ops);
-extern struct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key,
- void *object);
-extern void assoc_array_insert_set_object(struct assoc_array_edit *edit,
- void *object);
-extern struct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key);
-extern struct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
- const struct assoc_array_ops *ops);
-extern void assoc_array_apply_edit(struct assoc_array_edit *edit);
-extern void assoc_array_cancel_edit(struct assoc_array_edit *edit);
-extern int assoc_array_gc(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- bool (*iterator)(void *object, void *iterator_data),
- void *iterator_data);
-
-#endif /* CONFIG_ASSOCIATIVE_ARRAY */
-#endif /* _LINUX_ASSOC_ARRAY_H */
diff --git a/include/linux/assoc_array_priv.h b/include/linux/assoc_array_priv.h
deleted file mode 100644
index dca733ef6750..000000000000
--- a/include/linux/assoc_array_priv.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* Private definitions for the generic associative array implementation.
- *
- * See Documentation/core-api/assoc_array.rst for information.
- *
- * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells at redhat.com)
- */
-
-#ifndef _LINUX_ASSOC_ARRAY_PRIV_H
-#define _LINUX_ASSOC_ARRAY_PRIV_H
-
-#ifdef CONFIG_ASSOCIATIVE_ARRAY
-
-#include <linux/assoc_array.h>
-
-#define ASSOC_ARRAY_FAN_OUT 16 /* Number of slots per node */
-#define ASSOC_ARRAY_FAN_MASK (ASSOC_ARRAY_FAN_OUT - 1)
-#define ASSOC_ARRAY_LEVEL_STEP (ilog2(ASSOC_ARRAY_FAN_OUT))
-#define ASSOC_ARRAY_LEVEL_STEP_MASK (ASSOC_ARRAY_LEVEL_STEP - 1)
-#define ASSOC_ARRAY_KEY_CHUNK_MASK (ASSOC_ARRAY_KEY_CHUNK_SIZE - 1)
-#define ASSOC_ARRAY_KEY_CHUNK_SHIFT (ilog2(BITS_PER_LONG))
-
-/*
- * Undefined type representing a pointer with type information in the bottom
- * two bits.
- */
-struct assoc_array_ptr;
-
-/*
- * An N-way node in the tree.
- *
- * Each slot contains one of four things:
- *
- * (1) Nothing (NULL).
- *
- * (2) A leaf object (pointer types 0).
- *
- * (3) A next-level node (pointer type 1, subtype 0).
- *
- * (4) A shortcut (pointer type 1, subtype 1).
- *
- * The tree is optimised for search-by-ID, but permits reasonable iteration
- * also.
- *
- * The tree is navigated by constructing an index key consisting of an array of
- * segments, where each segment is ilog2(ASSOC_ARRAY_FAN_OUT) bits in size.
- *
- * The segments correspond to levels of the tree (the first segment is used at
- * level 0, the second at level 1, etc.).
- */
-struct assoc_array_node {
- struct assoc_array_ptr *back_pointer;
- u8 parent_slot;
- struct assoc_array_ptr *slots[ASSOC_ARRAY_FAN_OUT];
- unsigned long nr_leaves_on_branch;
-};
-
-/*
- * A shortcut through the index space out to where a collection of nodes/leaves
- * with the same IDs live.
- */
-struct assoc_array_shortcut {
- struct assoc_array_ptr *back_pointer;
- int parent_slot;
- int skip_to_level;
- struct assoc_array_ptr *next_node;
- unsigned long index_key[];
-};
-
-/*
- * Preallocation cache.
- */
-struct assoc_array_edit {
- struct rcu_head rcu;
- struct assoc_array *array;
- const struct assoc_array_ops *ops;
- const struct assoc_array_ops *ops_for_excised_subtree;
- struct assoc_array_ptr *leaf;
- struct assoc_array_ptr **leaf_p;
- struct assoc_array_ptr *dead_leaf;
- struct assoc_array_ptr *new_meta[3];
- struct assoc_array_ptr *excised_meta[1];
- struct assoc_array_ptr *excised_subtree;
- struct assoc_array_ptr **set_backpointers[ASSOC_ARRAY_FAN_OUT];
- struct assoc_array_ptr *set_backpointers_to;
- struct assoc_array_node *adjust_count_on;
- long adjust_count_by;
- struct {
- struct assoc_array_ptr **ptr;
- struct assoc_array_ptr *to;
- } set[2];
- struct {
- u8 *p;
- u8 to;
- } set_parent_slot[1];
- u8 segment_cache[ASSOC_ARRAY_FAN_OUT + 1];
-};
-
-/*
- * Internal tree member pointers are marked in the bottom one or two bits to
- * indicate what type they are so that we don't have to look behind every
- * pointer to see what it points to.
- *
- * We provide functions to test type annotations and to create and translate
- * the annotated pointers.
- */
-#define ASSOC_ARRAY_PTR_TYPE_MASK 0x1UL
-#define ASSOC_ARRAY_PTR_LEAF_TYPE 0x0UL /* Points to leaf (or nowhere) */
-#define ASSOC_ARRAY_PTR_META_TYPE 0x1UL /* Points to node or shortcut */
-#define ASSOC_ARRAY_PTR_SUBTYPE_MASK 0x2UL
-#define ASSOC_ARRAY_PTR_NODE_SUBTYPE 0x0UL
-#define ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE 0x2UL
-
-static inline bool assoc_array_ptr_is_meta(const struct assoc_array_ptr *x)
-{
- return (unsigned long)x & ASSOC_ARRAY_PTR_TYPE_MASK;
-}
-static inline bool assoc_array_ptr_is_leaf(const struct assoc_array_ptr *x)
-{
- return !assoc_array_ptr_is_meta(x);
-}
-static inline bool assoc_array_ptr_is_shortcut(const struct assoc_array_ptr *x)
-{
- return (unsigned long)x & ASSOC_ARRAY_PTR_SUBTYPE_MASK;
-}
-static inline bool assoc_array_ptr_is_node(const struct assoc_array_ptr *x)
-{
- return !assoc_array_ptr_is_shortcut(x);
-}
-
-static inline void *assoc_array_ptr_to_leaf(const struct assoc_array_ptr *x)
-{
- return (void *)((unsigned long)x & ~ASSOC_ARRAY_PTR_TYPE_MASK);
-}
-
-static inline
-unsigned long __assoc_array_ptr_to_meta(const struct assoc_array_ptr *x)
-{
- return (unsigned long)x &
- ~(ASSOC_ARRAY_PTR_SUBTYPE_MASK | ASSOC_ARRAY_PTR_TYPE_MASK);
-}
-static inline
-struct assoc_array_node *assoc_array_ptr_to_node(const struct assoc_array_ptr *x)
-{
- return (struct assoc_array_node *)__assoc_array_ptr_to_meta(x);
-}
-static inline
-struct assoc_array_shortcut *assoc_array_ptr_to_shortcut(const struct assoc_array_ptr *x)
-{
- return (struct assoc_array_shortcut *)__assoc_array_ptr_to_meta(x);
-}
-
-static inline
-struct assoc_array_ptr *__assoc_array_x_to_ptr(const void *p, unsigned long t)
-{
- return (struct assoc_array_ptr *)((unsigned long)p | t);
-}
-static inline
-struct assoc_array_ptr *assoc_array_leaf_to_ptr(const void *p)
-{
- return __assoc_array_x_to_ptr(p, ASSOC_ARRAY_PTR_LEAF_TYPE);
-}
-static inline
-struct assoc_array_ptr *assoc_array_node_to_ptr(const struct assoc_array_node *p)
-{
- return __assoc_array_x_to_ptr(
- p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_NODE_SUBTYPE);
-}
-static inline
-struct assoc_array_ptr *assoc_array_shortcut_to_ptr(const struct assoc_array_shortcut *p)
-{
- return __assoc_array_x_to_ptr(
- p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE);
-}
-
-#endif /* CONFIG_ASSOCIATIVE_ARRAY */
-#endif /* _LINUX_ASSOC_ARRAY_PRIV_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 4e6b34c3346d..9b3dd46c6799 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -349,20 +349,6 @@ config XARRAY_MULTI
Support entries which occupy multiple consecutive indices in the
XArray.
-config ASSOCIATIVE_ARRAY
- bool
- help
- Generic associative array. Can be searched and iterated over whilst
- it is being modified. It is also reasonably quick to search and
- modify. The algorithms are non-recursive, and the trees are highly
- capacious.
-
- See:
-
- Documentation/core-api/assoc_array.rst
-
- for more information.
-
config CLOSURES
bool
diff --git a/lib/Makefile b/lib/Makefile
index dfab958327c5..a2cdff9a60ea 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -141,7 +141,6 @@ obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
obj-$(CONFIG_BTREE) += btree.o
obj-$(CONFIG_INTERVAL_TREE) += interval_tree.o
-obj-$(CONFIG_ASSOCIATIVE_ARRAY) += assoc_array.o
obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
obj-$(CONFIG_LIST_HARDENED) += list_debug.o
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
diff --git a/lib/assoc_array.c b/lib/assoc_array.c
deleted file mode 100644
index b6c9723e12ce..000000000000
--- a/lib/assoc_array.c
+++ /dev/null
@@ -1,1724 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* Generic associative array implementation.
- *
- * See Documentation/core-api/assoc_array.rst for information.
- *
- * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells at redhat.com)
- */
-//#define DEBUG
-#include <linux/rcupdate.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/assoc_array_priv.h>
-
-/*
- * Iterate over an associative array. The caller must hold the RCU read lock
- * or better.
- */
-static int assoc_array_subtree_iterate(const struct assoc_array_ptr *root,
- const struct assoc_array_ptr *stop,
- int (*iterator)(const void *leaf,
- void *iterator_data),
- void *iterator_data)
-{
- const struct assoc_array_shortcut *shortcut;
- const struct assoc_array_node *node;
- const struct assoc_array_ptr *cursor, *ptr, *parent;
- unsigned long has_meta;
- int slot, ret;
-
- cursor = root;
-
-begin_node:
- if (assoc_array_ptr_is_shortcut(cursor)) {
- /* Descend through a shortcut */
- shortcut = assoc_array_ptr_to_shortcut(cursor);
- cursor = READ_ONCE(shortcut->next_node); /* Address dependency. */
- }
-
- node = assoc_array_ptr_to_node(cursor);
- slot = 0;
-
- /* We perform two passes of each node.
- *
- * The first pass does all the leaves in this node. This means we
- * don't miss any leaves if the node is split up by insertion whilst
- * we're iterating over the branches rooted here (we may, however, see
- * some leaves twice).
- */
- has_meta = 0;
- for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
- has_meta |= (unsigned long)ptr;
- if (ptr && assoc_array_ptr_is_leaf(ptr)) {
- /* We need a barrier between the read of the pointer,
- * which is supplied by the above READ_ONCE().
- */
- /* Invoke the callback */
- ret = iterator(assoc_array_ptr_to_leaf(ptr),
- iterator_data);
- if (ret)
- return ret;
- }
- }
-
- /* The second pass attends to all the metadata pointers. If we follow
- * one of these we may find that we don't come back here, but rather go
- * back to a replacement node with the leaves in a different layout.
- *
- * We are guaranteed to make progress, however, as the slot number for
- * a particular portion of the key space cannot change - and we
- * continue at the back pointer + 1.
- */
- if (!(has_meta & ASSOC_ARRAY_PTR_META_TYPE))
- goto finished_node;
- slot = 0;
-
-continue_node:
- node = assoc_array_ptr_to_node(cursor);
- for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
- if (assoc_array_ptr_is_meta(ptr)) {
- cursor = ptr;
- goto begin_node;
- }
- }
-
-finished_node:
- /* Move up to the parent (may need to skip back over a shortcut) */
- parent = READ_ONCE(node->back_pointer); /* Address dependency. */
- slot = node->parent_slot;
- if (parent == stop)
- return 0;
-
- if (assoc_array_ptr_is_shortcut(parent)) {
- shortcut = assoc_array_ptr_to_shortcut(parent);
- cursor = parent;
- parent = READ_ONCE(shortcut->back_pointer); /* Address dependency. */
- slot = shortcut->parent_slot;
- if (parent == stop)
- return 0;
- }
-
- /* Ascend to next slot in parent node */
- cursor = parent;
- slot++;
- goto continue_node;
-}
-
-/**
- * assoc_array_iterate - Pass all objects in the array to a callback
- * @array: The array to iterate over.
- * @iterator: The callback function.
- * @iterator_data: Private data for the callback function.
- *
- * Iterate over all the objects in an associative array. Each one will be
- * presented to the iterator function.
- *
- * If the array is being modified concurrently with the iteration then it is
- * possible that some objects in the array will be passed to the iterator
- * callback more than once - though every object should be passed at least
- * once. If this is undesirable then the caller must lock against modification
- * for the duration of this function.
- *
- * The function will return 0 if no objects were in the array or else it will
- * return the result of the last iterator function called. Iteration stops
- * immediately if any call to the iteration function results in a non-zero
- * return.
- *
- * The caller should hold the RCU read lock or better if concurrent
- * modification is possible.
- */
-int assoc_array_iterate(const struct assoc_array *array,
- int (*iterator)(const void *object,
- void *iterator_data),
- void *iterator_data)
-{
- struct assoc_array_ptr *root = READ_ONCE(array->root); /* Address dependency. */
-
- if (!root)
- return 0;
- return assoc_array_subtree_iterate(root, NULL, iterator, iterator_data);
-}
-
-enum assoc_array_walk_status {
- assoc_array_walk_tree_empty,
- assoc_array_walk_found_terminal_node,
- assoc_array_walk_found_wrong_shortcut,
-};
-
-struct assoc_array_walk_result {
- struct {
- struct assoc_array_node *node; /* Node in which leaf might be found */
- int level;
- int slot;
- } terminal_node;
- struct {
- struct assoc_array_shortcut *shortcut;
- int level;
- int sc_level;
- unsigned long sc_segments;
- unsigned long dissimilarity;
- } wrong_shortcut;
-};
-
-/*
- * Navigate through the internal tree looking for the closest node to the key.
- */
-static enum assoc_array_walk_status
-assoc_array_walk(const struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key,
- struct assoc_array_walk_result *result)
-{
- struct assoc_array_shortcut *shortcut;
- struct assoc_array_node *node;
- struct assoc_array_ptr *cursor, *ptr;
- unsigned long sc_segments, dissimilarity;
- unsigned long segments;
- int level, sc_level, next_sc_level;
- int slot;
-
- pr_devel("-->%s()\n", __func__);
-
- cursor = READ_ONCE(array->root); /* Address dependency. */
- if (!cursor)
- return assoc_array_walk_tree_empty;
-
- level = 0;
-
- /* Use segments from the key for the new leaf to navigate through the
- * internal tree, skipping through nodes and shortcuts that are on
- * route to the destination. Eventually we'll come to a slot that is
- * either empty or contains a leaf at which point we've found a node in
- * which the leaf we're looking for might be found or into which it
- * should be inserted.
- */
-jumped:
- segments = ops->get_key_chunk(index_key, level);
- pr_devel("segments[%d]: %lx\n", level, segments);
-
- if (assoc_array_ptr_is_shortcut(cursor))
- goto follow_shortcut;
-
-consider_node:
- node = assoc_array_ptr_to_node(cursor);
- slot = segments >> (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
- slot &= ASSOC_ARRAY_FAN_MASK;
- ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
-
- pr_devel("consider slot %x [ix=%d type=%lu]\n",
- slot, level, (unsigned long)ptr & 3);
-
- if (!assoc_array_ptr_is_meta(ptr)) {
- /* The node doesn't have a node/shortcut pointer in the slot
- * corresponding to the index key that we have to follow.
- */
- result->terminal_node.node = node;
- result->terminal_node.level = level;
- result->terminal_node.slot = slot;
- pr_devel("<--%s() = terminal_node\n", __func__);
- return assoc_array_walk_found_terminal_node;
- }
-
- if (assoc_array_ptr_is_node(ptr)) {
- /* There is a pointer to a node in the slot corresponding to
- * this index key segment, so we need to follow it.
- */
- cursor = ptr;
- level += ASSOC_ARRAY_LEVEL_STEP;
- if ((level & ASSOC_ARRAY_KEY_CHUNK_MASK) != 0)
- goto consider_node;
- goto jumped;
- }
-
- /* There is a shortcut in the slot corresponding to the index key
- * segment. We follow the shortcut if its partial index key matches
- * this leaf's. Otherwise we need to split the shortcut.
- */
- cursor = ptr;
-follow_shortcut:
- shortcut = assoc_array_ptr_to_shortcut(cursor);
- pr_devel("shortcut to %d\n", shortcut->skip_to_level);
- sc_level = level + ASSOC_ARRAY_LEVEL_STEP;
- BUG_ON(sc_level > shortcut->skip_to_level);
-
- do {
- /* Check the leaf against the shortcut's index key a word at a
- * time, trimming the final word (the shortcut stores the index
- * key completely from the root to the shortcut's target).
- */
- if ((sc_level & ASSOC_ARRAY_KEY_CHUNK_MASK) == 0)
- segments = ops->get_key_chunk(index_key, sc_level);
-
- sc_segments = shortcut->index_key[sc_level >> ASSOC_ARRAY_KEY_CHUNK_SHIFT];
- dissimilarity = segments ^ sc_segments;
-
- if (shortcut->skip_to_level < round_down(sc_level,
- ASSOC_ARRAY_KEY_CHUNK_SIZE) + ASSOC_ARRAY_KEY_CHUNK_SIZE) {
- /* Trim segments that are beyond the shortcut */
- int shift = shortcut->skip_to_level & ASSOC_ARRAY_KEY_CHUNK_MASK;
- dissimilarity &= ~(ULONG_MAX << shift);
- next_sc_level = shortcut->skip_to_level;
- } else {
- next_sc_level = sc_level + ASSOC_ARRAY_KEY_CHUNK_SIZE;
- next_sc_level = round_down(next_sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
- }
-
- if (dissimilarity != 0) {
- /* This shortcut points elsewhere */
- result->wrong_shortcut.shortcut = shortcut;
- result->wrong_shortcut.level = level;
- result->wrong_shortcut.sc_level = sc_level;
- result->wrong_shortcut.sc_segments = sc_segments;
- result->wrong_shortcut.dissimilarity = dissimilarity;
- return assoc_array_walk_found_wrong_shortcut;
- }
-
- sc_level = next_sc_level;
- } while (sc_level < shortcut->skip_to_level);
-
- /* The shortcut matches the leaf's index to this point. */
- cursor = READ_ONCE(shortcut->next_node); /* Address dependency. */
- if (((level ^ sc_level) & ~ASSOC_ARRAY_KEY_CHUNK_MASK) != 0) {
- level = sc_level;
- goto jumped;
- } else {
- level = sc_level;
- goto consider_node;
- }
-}
-
-/**
- * assoc_array_find - Find an object by index key
- * @array: The associative array to search.
- * @ops: The operations to use.
- * @index_key: The key to the object.
- *
- * Find an object in an associative array by walking through the internal tree
- * to the node that should contain the object and then searching the leaves
- * there. NULL is returned if the requested object was not found in the array.
- *
- * The caller must hold the RCU read lock or better.
- */
-void *assoc_array_find(const struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key)
-{
- struct assoc_array_walk_result result;
- const struct assoc_array_node *node;
- const struct assoc_array_ptr *ptr;
- const void *leaf;
- int slot;
-
- if (assoc_array_walk(array, ops, index_key, &result) !=
- assoc_array_walk_found_terminal_node)
- return NULL;
-
- node = result.terminal_node.node;
-
- /* If the target key is available to us, it's has to be pointed to by
- * the terminal node.
- */
- for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- ptr = READ_ONCE(node->slots[slot]); /* Address dependency. */
- if (ptr && assoc_array_ptr_is_leaf(ptr)) {
- /* We need a barrier between the read of the pointer
- * and dereferencing the pointer - but only if we are
- * actually going to dereference it.
- */
- leaf = assoc_array_ptr_to_leaf(ptr);
- if (ops->compare_object(leaf, index_key))
- return (void *)leaf;
- }
- }
-
- return NULL;
-}
-
-/*
- * Destructively iterate over an associative array. The caller must prevent
- * other simultaneous accesses.
- */
-static void assoc_array_destroy_subtree(struct assoc_array_ptr *root,
- const struct assoc_array_ops *ops)
-{
- struct assoc_array_shortcut *shortcut;
- struct assoc_array_node *node;
- struct assoc_array_ptr *cursor, *parent = NULL;
- int slot = -1;
-
- pr_devel("-->%s()\n", __func__);
-
- cursor = root;
- if (!cursor) {
- pr_devel("empty\n");
- return;
- }
-
-move_to_meta:
- if (assoc_array_ptr_is_shortcut(cursor)) {
- /* Descend through a shortcut */
- pr_devel("[%d] shortcut\n", slot);
- BUG_ON(!assoc_array_ptr_is_shortcut(cursor));
- shortcut = assoc_array_ptr_to_shortcut(cursor);
- BUG_ON(shortcut->back_pointer != parent);
- BUG_ON(slot != -1 && shortcut->parent_slot != slot);
- parent = cursor;
- cursor = shortcut->next_node;
- slot = -1;
- BUG_ON(!assoc_array_ptr_is_node(cursor));
- }
-
- pr_devel("[%d] node\n", slot);
- node = assoc_array_ptr_to_node(cursor);
- BUG_ON(node->back_pointer != parent);
- BUG_ON(slot != -1 && node->parent_slot != slot);
- slot = 0;
-
-continue_node:
- pr_devel("Node %p [back=%p]\n", node, node->back_pointer);
- for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- struct assoc_array_ptr *ptr = node->slots[slot];
- if (!ptr)
- continue;
- if (assoc_array_ptr_is_meta(ptr)) {
- parent = cursor;
- cursor = ptr;
- goto move_to_meta;
- }
-
- if (ops) {
- pr_devel("[%d] free leaf\n", slot);
- ops->free_object(assoc_array_ptr_to_leaf(ptr));
- }
- }
-
- parent = node->back_pointer;
- slot = node->parent_slot;
- pr_devel("free node\n");
- kfree(node);
- if (!parent)
- return; /* Done */
-
- /* Move back up to the parent (may need to free a shortcut on
- * the way up) */
- if (assoc_array_ptr_is_shortcut(parent)) {
- shortcut = assoc_array_ptr_to_shortcut(parent);
- BUG_ON(shortcut->next_node != cursor);
- cursor = parent;
- parent = shortcut->back_pointer;
- slot = shortcut->parent_slot;
- pr_devel("free shortcut\n");
- kfree(shortcut);
- if (!parent)
- return;
-
- BUG_ON(!assoc_array_ptr_is_node(parent));
- }
-
- /* Ascend to next slot in parent node */
- pr_devel("ascend to %p[%d]\n", parent, slot);
- cursor = parent;
- node = assoc_array_ptr_to_node(cursor);
- slot++;
- goto continue_node;
-}
-
-/**
- * assoc_array_destroy - Destroy an associative array
- * @array: The array to destroy.
- * @ops: The operations to use.
- *
- * Discard all metadata and free all objects in an associative array. The
- * array will be empty and ready to use again upon completion. This function
- * cannot fail.
- *
- * The caller must prevent all other accesses whilst this takes place as no
- * attempt is made to adjust pointers gracefully to permit RCU readlock-holding
- * accesses to continue. On the other hand, no memory allocation is required.
- */
-void assoc_array_destroy(struct assoc_array *array,
- const struct assoc_array_ops *ops)
-{
- assoc_array_destroy_subtree(array->root, ops);
- array->root = NULL;
-}
-
-/*
- * Handle insertion into an empty tree.
- */
-static bool assoc_array_insert_in_empty_tree(struct assoc_array_edit *edit)
-{
- struct assoc_array_node *new_n0;
-
- pr_devel("-->%s()\n", __func__);
-
- new_n0 = kzalloc_obj(struct assoc_array_node);
- if (!new_n0)
- return false;
-
- edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
- edit->leaf_p = &new_n0->slots[0];
- edit->adjust_count_on = new_n0;
- edit->set[0].ptr = &edit->array->root;
- edit->set[0].to = assoc_array_node_to_ptr(new_n0);
-
- pr_devel("<--%s() = ok [no root]\n", __func__);
- return true;
-}
-
-/*
- * Handle insertion into a terminal node.
- */
-static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit,
- const struct assoc_array_ops *ops,
- const void *index_key,
- struct assoc_array_walk_result *result)
-{
- struct assoc_array_shortcut *shortcut, *new_s0;
- struct assoc_array_node *node, *new_n0, *new_n1, *side;
- struct assoc_array_ptr *ptr;
- unsigned long dissimilarity, base_seg, blank;
- size_t keylen;
- bool have_meta;
- int level, diff;
- int slot, next_slot, free_slot, i, j;
-
- node = result->terminal_node.node;
- level = result->terminal_node.level;
- edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = result->terminal_node.slot;
-
- pr_devel("-->%s()\n", __func__);
-
- /* We arrived at a node which doesn't have an onward node or shortcut
- * pointer that we have to follow. This means that (a) the leaf we
- * want must go here (either by insertion or replacement) or (b) we
- * need to split this node and insert in one of the fragments.
- */
- free_slot = -1;
-
- /* Firstly, we have to check the leaves in this node to see if there's
- * a matching one we should replace in place.
- */
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- ptr = node->slots[i];
- if (!ptr) {
- free_slot = i;
- continue;
- }
- if (assoc_array_ptr_is_leaf(ptr) &&
- ops->compare_object(assoc_array_ptr_to_leaf(ptr),
- index_key)) {
- pr_devel("replace in slot %d\n", i);
- edit->leaf_p = &node->slots[i];
- edit->dead_leaf = node->slots[i];
- pr_devel("<--%s() = ok [replace]\n", __func__);
- return true;
- }
- }
-
- /* If there is a free slot in this node then we can just insert the
- * leaf here.
- */
- if (free_slot >= 0) {
- pr_devel("insert in free slot %d\n", free_slot);
- edit->leaf_p = &node->slots[free_slot];
- edit->adjust_count_on = node;
- pr_devel("<--%s() = ok [insert]\n", __func__);
- return true;
- }
-
- /* The node has no spare slots - so we're either going to have to split
- * it or insert another node before it.
- *
- * Whatever, we're going to need at least two new nodes - so allocate
- * those now. We may also need a new shortcut, but we deal with that
- * when we need it.
- */
- new_n0 = kzalloc_obj(struct assoc_array_node);
- if (!new_n0)
- return false;
- edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
- new_n1 = kzalloc_obj(struct assoc_array_node);
- if (!new_n1)
- return false;
- edit->new_meta[1] = assoc_array_node_to_ptr(new_n1);
-
- /* We need to find out how similar the leaves are. */
- pr_devel("no spare slots\n");
- have_meta = false;
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- ptr = node->slots[i];
- if (assoc_array_ptr_is_meta(ptr)) {
- edit->segment_cache[i] = 0xff;
- have_meta = true;
- continue;
- }
- base_seg = ops->get_object_key_chunk(
- assoc_array_ptr_to_leaf(ptr), level);
- base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
- edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
- }
-
- if (have_meta) {
- pr_devel("have meta\n");
- goto split_node;
- }
-
- /* The node contains only leaves */
- dissimilarity = 0;
- base_seg = edit->segment_cache[0];
- for (i = 1; i < ASSOC_ARRAY_FAN_OUT; i++)
- dissimilarity |= edit->segment_cache[i] ^ base_seg;
-
- pr_devel("only leaves; dissimilarity=%lx\n", dissimilarity);
-
- if ((dissimilarity & ASSOC_ARRAY_FAN_MASK) == 0) {
- /* The old leaves all cluster in the same slot. We will need
- * to insert a shortcut if the new node wants to cluster with them.
- */
- if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0)
- goto all_leaves_cluster_together;
-
- /* Otherwise all the old leaves cluster in the same slot, but
- * the new leaf wants to go into a different slot - so we
- * create a new node (n0) to hold the new leaf and a pointer to
- * a new node (n1) holding all the old leaves.
- *
- * This can be done by falling through to the node splitting
- * path.
- */
- pr_devel("present leaves cluster but not new leaf\n");
- }
-
-split_node:
- pr_devel("split node\n");
-
- /* We need to split the current node. The node must contain anything
- * from a single leaf (in the one leaf case, this leaf will cluster
- * with the new leaf) and the rest meta-pointers, to all leaves, some
- * of which may cluster.
- *
- * It won't contain the case in which all the current leaves plus the
- * new leaves want to cluster in the same slot.
- *
- * We need to expel at least two leaves out of a set consisting of the
- * leaves in the node and the new leaf. The current meta pointers can
- * just be copied as they shouldn't cluster with any of the leaves.
- *
- * We need a new node (n0) to replace the current one and a new node to
- * take the expelled nodes (n1).
- */
- edit->set[0].to = assoc_array_node_to_ptr(new_n0);
- new_n0->back_pointer = node->back_pointer;
- new_n0->parent_slot = node->parent_slot;
- new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
- new_n1->parent_slot = -1; /* Need to calculate this */
-
-do_split_node:
- pr_devel("do_split_node\n");
-
- new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
- new_n1->nr_leaves_on_branch = 0;
-
- /* Begin by finding two matching leaves. There have to be at least two
- * that match - even if there are meta pointers - because any leaf that
- * would match a slot with a meta pointer in it must be somewhere
- * behind that meta pointer and cannot be here. Further, given N
- * remaining leaf slots, we now have N+1 leaves to go in them.
- */
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- slot = edit->segment_cache[i];
- if (slot != 0xff)
- for (j = i + 1; j < ASSOC_ARRAY_FAN_OUT + 1; j++)
- if (edit->segment_cache[j] == slot)
- goto found_slot_for_multiple_occupancy;
- }
-found_slot_for_multiple_occupancy:
- pr_devel("same slot: %x %x [%02x]\n", i, j, slot);
- BUG_ON(i >= ASSOC_ARRAY_FAN_OUT);
- BUG_ON(j >= ASSOC_ARRAY_FAN_OUT + 1);
- BUG_ON(slot >= ASSOC_ARRAY_FAN_OUT);
-
- new_n1->parent_slot = slot;
-
- /* Metadata pointers cannot change slot */
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++)
- if (assoc_array_ptr_is_meta(node->slots[i]))
- new_n0->slots[i] = node->slots[i];
- else
- new_n0->slots[i] = NULL;
- BUG_ON(new_n0->slots[slot] != NULL);
- new_n0->slots[slot] = assoc_array_node_to_ptr(new_n1);
-
- /* Filter the leaf pointers between the new nodes */
- free_slot = -1;
- next_slot = 0;
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- if (assoc_array_ptr_is_meta(node->slots[i]))
- continue;
- if (edit->segment_cache[i] == slot) {
- new_n1->slots[next_slot++] = node->slots[i];
- new_n1->nr_leaves_on_branch++;
- } else {
- do {
- free_slot++;
- } while (new_n0->slots[free_slot] != NULL);
- new_n0->slots[free_slot] = node->slots[i];
- }
- }
-
- pr_devel("filtered: f=%x n=%x\n", free_slot, next_slot);
-
- if (edit->segment_cache[ASSOC_ARRAY_FAN_OUT] != slot) {
- do {
- free_slot++;
- } while (new_n0->slots[free_slot] != NULL);
- edit->leaf_p = &new_n0->slots[free_slot];
- edit->adjust_count_on = new_n0;
- } else {
- edit->leaf_p = &new_n1->slots[next_slot++];
- edit->adjust_count_on = new_n1;
- }
-
- BUG_ON(next_slot <= 1);
-
- edit->set_backpointers_to = assoc_array_node_to_ptr(new_n0);
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- if (edit->segment_cache[i] == 0xff) {
- ptr = node->slots[i];
- BUG_ON(assoc_array_ptr_is_leaf(ptr));
- if (assoc_array_ptr_is_node(ptr)) {
- side = assoc_array_ptr_to_node(ptr);
- edit->set_backpointers[i] = &side->back_pointer;
- } else {
- shortcut = assoc_array_ptr_to_shortcut(ptr);
- edit->set_backpointers[i] = &shortcut->back_pointer;
- }
- }
- }
-
- ptr = node->back_pointer;
- if (!ptr)
- edit->set[0].ptr = &edit->array->root;
- else if (assoc_array_ptr_is_node(ptr))
- edit->set[0].ptr = &assoc_array_ptr_to_node(ptr)->slots[node->parent_slot];
- else
- edit->set[0].ptr = &assoc_array_ptr_to_shortcut(ptr)->next_node;
- edit->excised_meta[0] = assoc_array_node_to_ptr(node);
- pr_devel("<--%s() = ok [split node]\n", __func__);
- return true;
-
-all_leaves_cluster_together:
- /* All the leaves, new and old, want to cluster together in this node
- * in the same slot, so we have to replace this node with a shortcut to
- * skip over the identical parts of the key and then place a pair of
- * nodes, one inside the other, at the end of the shortcut and
- * distribute the keys between them.
- *
- * Firstly we need to work out where the leaves start diverging as a
- * bit position into their keys so that we know how big the shortcut
- * needs to be.
- *
- * We only need to make a single pass of N of the N+1 leaves because if
- * any keys differ between themselves at bit X then at least one of
- * them must also differ with the base key at bit X or before.
- */
- pr_devel("all leaves cluster together\n");
- diff = INT_MAX;
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- int x = ops->diff_objects(assoc_array_ptr_to_leaf(node->slots[i]),
- index_key);
- if (x < diff) {
- BUG_ON(x < 0);
- diff = x;
- }
- }
- BUG_ON(diff == INT_MAX);
- BUG_ON(diff < level + ASSOC_ARRAY_LEVEL_STEP);
-
- keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
- keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
-
- new_s0 = kzalloc_flex(*new_s0, index_key, keylen);
- if (!new_s0)
- return false;
- edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s0);
-
- edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
- new_s0->back_pointer = node->back_pointer;
- new_s0->parent_slot = node->parent_slot;
- new_s0->next_node = assoc_array_node_to_ptr(new_n0);
- new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
- new_n0->parent_slot = 0;
- new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
- new_n1->parent_slot = -1; /* Need to calculate this */
-
- new_s0->skip_to_level = level = diff & ~ASSOC_ARRAY_LEVEL_STEP_MASK;
- pr_devel("skip_to_level = %d [diff %d]\n", level, diff);
- BUG_ON(level <= 0);
-
- for (i = 0; i < keylen; i++)
- new_s0->index_key[i] =
- ops->get_key_chunk(index_key, i * ASSOC_ARRAY_KEY_CHUNK_SIZE);
-
- if (level & ASSOC_ARRAY_KEY_CHUNK_MASK) {
- blank = ULONG_MAX << (level & ASSOC_ARRAY_KEY_CHUNK_MASK);
- pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, level, blank);
- new_s0->index_key[keylen - 1] &= ~blank;
- }
-
- /* This now reduces to a node splitting exercise for which we'll need
- * to regenerate the disparity table.
- */
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- ptr = node->slots[i];
- base_seg = ops->get_object_key_chunk(assoc_array_ptr_to_leaf(ptr),
- level);
- base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
- edit->segment_cache[i] = base_seg & ASSOC_ARRAY_FAN_MASK;
- }
-
- base_seg = ops->get_key_chunk(index_key, level);
- base_seg >>= level & ASSOC_ARRAY_KEY_CHUNK_MASK;
- edit->segment_cache[ASSOC_ARRAY_FAN_OUT] = base_seg & ASSOC_ARRAY_FAN_MASK;
- goto do_split_node;
-}
-
-/*
- * Handle insertion into the middle of a shortcut.
- */
-static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit,
- const struct assoc_array_ops *ops,
- struct assoc_array_walk_result *result)
-{
- struct assoc_array_shortcut *shortcut, *new_s0, *new_s1;
- struct assoc_array_node *node, *new_n0, *side;
- unsigned long sc_segments, dissimilarity, blank;
- size_t keylen;
- int level, sc_level, diff;
- int sc_slot;
-
- shortcut = result->wrong_shortcut.shortcut;
- level = result->wrong_shortcut.level;
- sc_level = result->wrong_shortcut.sc_level;
- sc_segments = result->wrong_shortcut.sc_segments;
- dissimilarity = result->wrong_shortcut.dissimilarity;
-
- pr_devel("-->%s(ix=%d dis=%lx scix=%d)\n",
- __func__, level, dissimilarity, sc_level);
-
- /* We need to split a shortcut and insert a node between the two
- * pieces. Zero-length pieces will be dispensed with entirely.
- *
- * First of all, we need to find out in which level the first
- * difference was.
- */
- diff = __ffs(dissimilarity);
- diff &= ~ASSOC_ARRAY_LEVEL_STEP_MASK;
- diff += sc_level & ~ASSOC_ARRAY_KEY_CHUNK_MASK;
- pr_devel("diff=%d\n", diff);
-
- if (!shortcut->back_pointer) {
- edit->set[0].ptr = &edit->array->root;
- } else if (assoc_array_ptr_is_node(shortcut->back_pointer)) {
- node = assoc_array_ptr_to_node(shortcut->back_pointer);
- edit->set[0].ptr = &node->slots[shortcut->parent_slot];
- } else {
- BUG();
- }
-
- edit->excised_meta[0] = assoc_array_shortcut_to_ptr(shortcut);
-
- /* Create a new node now since we're going to need it anyway */
- new_n0 = kzalloc_obj(struct assoc_array_node);
- if (!new_n0)
- return false;
- edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
- edit->adjust_count_on = new_n0;
-
- /* Insert a new shortcut before the new node if this segment isn't of
- * zero length - otherwise we just connect the new node directly to the
- * parent.
- */
- level += ASSOC_ARRAY_LEVEL_STEP;
- if (diff > level) {
- pr_devel("pre-shortcut %d...%d\n", level, diff);
- keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE);
- keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
-
- new_s0 = kzalloc_flex(*new_s0, index_key, keylen);
- if (!new_s0)
- return false;
- edit->new_meta[1] = assoc_array_shortcut_to_ptr(new_s0);
- edit->set[0].to = assoc_array_shortcut_to_ptr(new_s0);
- new_s0->back_pointer = shortcut->back_pointer;
- new_s0->parent_slot = shortcut->parent_slot;
- new_s0->next_node = assoc_array_node_to_ptr(new_n0);
- new_s0->skip_to_level = diff;
-
- new_n0->back_pointer = assoc_array_shortcut_to_ptr(new_s0);
- new_n0->parent_slot = 0;
-
- memcpy(new_s0->index_key, shortcut->index_key,
- flex_array_size(new_s0, index_key, keylen));
-
- blank = ULONG_MAX << (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
- pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, diff, blank);
- new_s0->index_key[keylen - 1] &= ~blank;
- } else {
- pr_devel("no pre-shortcut\n");
- edit->set[0].to = assoc_array_node_to_ptr(new_n0);
- new_n0->back_pointer = shortcut->back_pointer;
- new_n0->parent_slot = shortcut->parent_slot;
- }
-
- side = assoc_array_ptr_to_node(shortcut->next_node);
- new_n0->nr_leaves_on_branch = side->nr_leaves_on_branch;
-
- /* We need to know which slot in the new node is going to take a
- * metadata pointer.
- */
- sc_slot = sc_segments >> (diff & ASSOC_ARRAY_KEY_CHUNK_MASK);
- sc_slot &= ASSOC_ARRAY_FAN_MASK;
-
- pr_devel("new slot %lx >> %d -> %d\n",
- sc_segments, diff & ASSOC_ARRAY_KEY_CHUNK_MASK, sc_slot);
-
- /* Determine whether we need to follow the new node with a replacement
- * for the current shortcut. We could in theory reuse the current
- * shortcut if its parent slot number doesn't change - but that's a
- * 1-in-16 chance so not worth expending the code upon.
- */
- level = diff + ASSOC_ARRAY_LEVEL_STEP;
- if (level < shortcut->skip_to_level) {
- pr_devel("post-shortcut %d...%d\n", level, shortcut->skip_to_level);
- keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
- keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
-
- new_s1 = kzalloc_flex(*new_s1, index_key, keylen);
- if (!new_s1)
- return false;
- edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s1);
-
- new_s1->back_pointer = assoc_array_node_to_ptr(new_n0);
- new_s1->parent_slot = sc_slot;
- new_s1->next_node = shortcut->next_node;
- new_s1->skip_to_level = shortcut->skip_to_level;
-
- new_n0->slots[sc_slot] = assoc_array_shortcut_to_ptr(new_s1);
-
- memcpy(new_s1->index_key, shortcut->index_key,
- flex_array_size(new_s1, index_key, keylen));
-
- edit->set[1].ptr = &side->back_pointer;
- edit->set[1].to = assoc_array_shortcut_to_ptr(new_s1);
- } else {
- pr_devel("no post-shortcut\n");
-
- /* We don't have to replace the pointed-to node as long as we
- * use memory barriers to make sure the parent slot number is
- * changed before the back pointer (the parent slot number is
- * irrelevant to the old parent shortcut).
- */
- new_n0->slots[sc_slot] = shortcut->next_node;
- edit->set_parent_slot[0].p = &side->parent_slot;
- edit->set_parent_slot[0].to = sc_slot;
- edit->set[1].ptr = &side->back_pointer;
- edit->set[1].to = assoc_array_node_to_ptr(new_n0);
- }
-
- /* Install the new leaf in a spare slot in the new node. */
- if (sc_slot == 0)
- edit->leaf_p = &new_n0->slots[1];
- else
- edit->leaf_p = &new_n0->slots[0];
-
- pr_devel("<--%s() = ok [split shortcut]\n", __func__);
- return true;
-}
-
-/**
- * assoc_array_insert - Script insertion of an object into an associative array
- * @array: The array to insert into.
- * @ops: The operations to use.
- * @index_key: The key to insert at.
- * @object: The object to insert.
- *
- * Precalculate and preallocate a script for the insertion or replacement of an
- * object in an associative array. This results in an edit script that can
- * either be applied or cancelled.
- *
- * The function returns a pointer to an edit script or -ENOMEM.
- *
- * The caller should lock against other modifications and must continue to hold
- * the lock until assoc_array_apply_edit() has been called.
- *
- * Accesses to the tree may take place concurrently with this function,
- * provided they hold the RCU read lock.
- */
-struct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key,
- void *object)
-{
- struct assoc_array_walk_result result;
- struct assoc_array_edit *edit;
-
- pr_devel("-->%s()\n", __func__);
-
- /* The leaf pointer we're given must not have the bottom bit set as we
- * use those for type-marking the pointer. NULL pointers are also not
- * allowed as they indicate an empty slot but we have to allow them
- * here as they can be updated later.
- */
- BUG_ON(assoc_array_ptr_is_meta(object));
-
- edit = kzalloc_obj(struct assoc_array_edit);
- if (!edit)
- return ERR_PTR(-ENOMEM);
- edit->array = array;
- edit->ops = ops;
- edit->leaf = assoc_array_leaf_to_ptr(object);
- edit->adjust_count_by = 1;
-
- switch (assoc_array_walk(array, ops, index_key, &result)) {
- case assoc_array_walk_tree_empty:
- /* Allocate a root node if there isn't one yet */
- if (!assoc_array_insert_in_empty_tree(edit))
- goto enomem;
- return edit;
-
- case assoc_array_walk_found_terminal_node:
- /* We found a node that doesn't have a node/shortcut pointer in
- * the slot corresponding to the index key that we have to
- * follow.
- */
- if (!assoc_array_insert_into_terminal_node(edit, ops, index_key,
- &result))
- goto enomem;
- return edit;
-
- case assoc_array_walk_found_wrong_shortcut:
- /* We found a shortcut that didn't match our key in a slot we
- * needed to follow.
- */
- if (!assoc_array_insert_mid_shortcut(edit, ops, &result))
- goto enomem;
- return edit;
- }
-
-enomem:
- /* Clean up after an out of memory error */
- pr_devel("enomem\n");
- assoc_array_cancel_edit(edit);
- return ERR_PTR(-ENOMEM);
-}
-
-/**
- * assoc_array_insert_set_object - Set the new object pointer in an edit script
- * @edit: The edit script to modify.
- * @object: The object pointer to set.
- *
- * Change the object to be inserted in an edit script. The object pointed to
- * by the old object is not freed. This must be done prior to applying the
- * script.
- */
-void assoc_array_insert_set_object(struct assoc_array_edit *edit, void *object)
-{
- BUG_ON(!object);
- edit->leaf = assoc_array_leaf_to_ptr(object);
-}
-
-struct assoc_array_delete_collapse_context {
- struct assoc_array_node *node;
- const void *skip_leaf;
- int slot;
-};
-
-/*
- * Subtree collapse to node iterator.
- */
-static int assoc_array_delete_collapse_iterator(const void *leaf,
- void *iterator_data)
-{
- struct assoc_array_delete_collapse_context *collapse = iterator_data;
-
- if (leaf == collapse->skip_leaf)
- return 0;
-
- BUG_ON(collapse->slot >= ASSOC_ARRAY_FAN_OUT);
-
- collapse->node->slots[collapse->slot++] = assoc_array_leaf_to_ptr(leaf);
- return 0;
-}
-
-/**
- * assoc_array_delete - Script deletion of an object from an associative array
- * @array: The array to search.
- * @ops: The operations to use.
- * @index_key: The key to the object.
- *
- * Precalculate and preallocate a script for the deletion of an object from an
- * associative array. This results in an edit script that can either be
- * applied or cancelled.
- *
- * The function returns a pointer to an edit script if the object was found,
- * NULL if the object was not found or -ENOMEM.
- *
- * The caller should lock against other modifications and must continue to hold
- * the lock until assoc_array_apply_edit() has been called.
- *
- * Accesses to the tree may take place concurrently with this function,
- * provided they hold the RCU read lock.
- */
-struct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- const void *index_key)
-{
- struct assoc_array_delete_collapse_context collapse;
- struct assoc_array_walk_result result;
- struct assoc_array_node *node, *new_n0;
- struct assoc_array_edit *edit;
- struct assoc_array_ptr *ptr;
- bool has_meta;
- int slot, i;
-
- pr_devel("-->%s()\n", __func__);
-
- edit = kzalloc_obj(struct assoc_array_edit);
- if (!edit)
- return ERR_PTR(-ENOMEM);
- edit->array = array;
- edit->ops = ops;
- edit->adjust_count_by = -1;
-
- switch (assoc_array_walk(array, ops, index_key, &result)) {
- case assoc_array_walk_found_terminal_node:
- /* We found a node that should contain the leaf we've been
- * asked to remove - *if* it's in the tree.
- */
- pr_devel("terminal_node\n");
- node = result.terminal_node.node;
-
- for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- ptr = node->slots[slot];
- if (ptr &&
- assoc_array_ptr_is_leaf(ptr) &&
- ops->compare_object(assoc_array_ptr_to_leaf(ptr),
- index_key))
- goto found_leaf;
- }
- fallthrough;
- case assoc_array_walk_tree_empty:
- case assoc_array_walk_found_wrong_shortcut:
- default:
- assoc_array_cancel_edit(edit);
- pr_devel("not found\n");
- return NULL;
- }
-
-found_leaf:
- BUG_ON(array->nr_leaves_on_tree <= 0);
-
- /* In the simplest form of deletion we just clear the slot and release
- * the leaf after a suitable interval.
- */
- edit->dead_leaf = node->slots[slot];
- edit->set[0].ptr = &node->slots[slot];
- edit->set[0].to = NULL;
- edit->adjust_count_on = node;
-
- /* If that concludes erasure of the last leaf, then delete the entire
- * internal array.
- */
- if (array->nr_leaves_on_tree == 1) {
- edit->set[1].ptr = &array->root;
- edit->set[1].to = NULL;
- edit->adjust_count_on = NULL;
- edit->excised_subtree = array->root;
- pr_devel("all gone\n");
- return edit;
- }
-
- /* However, we'd also like to clear up some metadata blocks if we
- * possibly can.
- *
- * We go for a simple algorithm of: if this node has FAN_OUT or fewer
- * leaves in it, then attempt to collapse it - and attempt to
- * recursively collapse up the tree.
- *
- * We could also try and collapse in partially filled subtrees to take
- * up space in this node.
- */
- if (node->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
- struct assoc_array_node *parent, *grandparent;
- struct assoc_array_ptr *ptr;
-
- /* First of all, we need to know if this node has metadata so
- * that we don't try collapsing if all the leaves are already
- * here.
- */
- has_meta = false;
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- ptr = node->slots[i];
- if (assoc_array_ptr_is_meta(ptr)) {
- has_meta = true;
- break;
- }
- }
-
- pr_devel("leaves: %ld [m=%d]\n",
- node->nr_leaves_on_branch - 1, has_meta);
-
- /* Look further up the tree to see if we can collapse this node
- * into a more proximal node too.
- */
- parent = node;
- collapse_up:
- pr_devel("collapse subtree: %ld\n", parent->nr_leaves_on_branch);
-
- ptr = parent->back_pointer;
- if (!ptr)
- goto do_collapse;
- if (assoc_array_ptr_is_shortcut(ptr)) {
- struct assoc_array_shortcut *s = assoc_array_ptr_to_shortcut(ptr);
- ptr = s->back_pointer;
- if (!ptr)
- goto do_collapse;
- }
-
- grandparent = assoc_array_ptr_to_node(ptr);
- if (grandparent->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT + 1) {
- parent = grandparent;
- goto collapse_up;
- }
-
- do_collapse:
- /* There's no point collapsing if the original node has no meta
- * pointers to discard and if we didn't merge into one of that
- * node's ancestry.
- */
- if (has_meta || parent != node) {
- node = parent;
-
- /* Create a new node to collapse into */
- new_n0 = kzalloc_obj(struct assoc_array_node);
- if (!new_n0)
- goto enomem;
- edit->new_meta[0] = assoc_array_node_to_ptr(new_n0);
-
- new_n0->back_pointer = node->back_pointer;
- new_n0->parent_slot = node->parent_slot;
- new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
- edit->adjust_count_on = new_n0;
-
- collapse.node = new_n0;
- collapse.skip_leaf = assoc_array_ptr_to_leaf(edit->dead_leaf);
- collapse.slot = 0;
- assoc_array_subtree_iterate(assoc_array_node_to_ptr(node),
- node->back_pointer,
- assoc_array_delete_collapse_iterator,
- &collapse);
- pr_devel("collapsed %d,%lu\n", collapse.slot, new_n0->nr_leaves_on_branch);
- BUG_ON(collapse.slot != new_n0->nr_leaves_on_branch - 1);
-
- if (!node->back_pointer) {
- edit->set[1].ptr = &array->root;
- } else if (assoc_array_ptr_is_leaf(node->back_pointer)) {
- BUG();
- } else if (assoc_array_ptr_is_node(node->back_pointer)) {
- struct assoc_array_node *p =
- assoc_array_ptr_to_node(node->back_pointer);
- edit->set[1].ptr = &p->slots[node->parent_slot];
- } else if (assoc_array_ptr_is_shortcut(node->back_pointer)) {
- struct assoc_array_shortcut *s =
- assoc_array_ptr_to_shortcut(node->back_pointer);
- edit->set[1].ptr = &s->next_node;
- }
- edit->set[1].to = assoc_array_node_to_ptr(new_n0);
- edit->excised_subtree = assoc_array_node_to_ptr(node);
- }
- }
-
- return edit;
-
-enomem:
- /* Clean up after an out of memory error */
- pr_devel("enomem\n");
- assoc_array_cancel_edit(edit);
- return ERR_PTR(-ENOMEM);
-}
-
-/**
- * assoc_array_clear - Script deletion of all objects from an associative array
- * @array: The array to clear.
- * @ops: The operations to use.
- *
- * Precalculate and preallocate a script for the deletion of all the objects
- * from an associative array. This results in an edit script that can either
- * be applied or cancelled.
- *
- * The function returns a pointer to an edit script if there are objects to be
- * deleted, NULL if there are no objects in the array or -ENOMEM.
- *
- * The caller should lock against other modifications and must continue to hold
- * the lock until assoc_array_apply_edit() has been called.
- *
- * Accesses to the tree may take place concurrently with this function,
- * provided they hold the RCU read lock.
- */
-struct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
- const struct assoc_array_ops *ops)
-{
- struct assoc_array_edit *edit;
-
- pr_devel("-->%s()\n", __func__);
-
- if (!array->root)
- return NULL;
-
- edit = kzalloc_obj(struct assoc_array_edit);
- if (!edit)
- return ERR_PTR(-ENOMEM);
- edit->array = array;
- edit->ops = ops;
- edit->set[1].ptr = &array->root;
- edit->set[1].to = NULL;
- edit->excised_subtree = array->root;
- edit->ops_for_excised_subtree = ops;
- pr_devel("all gone\n");
- return edit;
-}
-
-/*
- * Handle the deferred destruction after an applied edit.
- */
-static void assoc_array_rcu_cleanup(struct rcu_head *head)
-{
- struct assoc_array_edit *edit =
- container_of(head, struct assoc_array_edit, rcu);
- int i;
-
- pr_devel("-->%s()\n", __func__);
-
- if (edit->dead_leaf)
- edit->ops->free_object(assoc_array_ptr_to_leaf(edit->dead_leaf));
- for (i = 0; i < ARRAY_SIZE(edit->excised_meta); i++)
- if (edit->excised_meta[i])
- kfree(assoc_array_ptr_to_node(edit->excised_meta[i]));
-
- if (edit->excised_subtree) {
- BUG_ON(assoc_array_ptr_is_leaf(edit->excised_subtree));
- if (assoc_array_ptr_is_node(edit->excised_subtree)) {
- struct assoc_array_node *n =
- assoc_array_ptr_to_node(edit->excised_subtree);
- n->back_pointer = NULL;
- } else {
- struct assoc_array_shortcut *s =
- assoc_array_ptr_to_shortcut(edit->excised_subtree);
- s->back_pointer = NULL;
- }
- assoc_array_destroy_subtree(edit->excised_subtree,
- edit->ops_for_excised_subtree);
- }
-
- kfree(edit);
-}
-
-/**
- * assoc_array_apply_edit - Apply an edit script to an associative array
- * @edit: The script to apply.
- *
- * Apply an edit script to an associative array to effect an insertion,
- * deletion or clearance. As the edit script includes preallocated memory,
- * this is guaranteed not to fail.
- *
- * The edit script, dead objects and dead metadata will be scheduled for
- * destruction after an RCU grace period to permit those doing read-only
- * accesses on the array to continue to do so under the RCU read lock whilst
- * the edit is taking place.
- */
-void assoc_array_apply_edit(struct assoc_array_edit *edit)
-{
- struct assoc_array_shortcut *shortcut;
- struct assoc_array_node *node;
- struct assoc_array_ptr *ptr;
- int i;
-
- pr_devel("-->%s()\n", __func__);
-
- smp_wmb();
- if (edit->leaf_p)
- *edit->leaf_p = edit->leaf;
-
- smp_wmb();
- for (i = 0; i < ARRAY_SIZE(edit->set_parent_slot); i++)
- if (edit->set_parent_slot[i].p)
- *edit->set_parent_slot[i].p = edit->set_parent_slot[i].to;
-
- smp_wmb();
- for (i = 0; i < ARRAY_SIZE(edit->set_backpointers); i++)
- if (edit->set_backpointers[i])
- *edit->set_backpointers[i] = edit->set_backpointers_to;
-
- smp_wmb();
- for (i = 0; i < ARRAY_SIZE(edit->set); i++)
- if (edit->set[i].ptr)
- *edit->set[i].ptr = edit->set[i].to;
-
- if (edit->array->root == NULL) {
- edit->array->nr_leaves_on_tree = 0;
- } else if (edit->adjust_count_on) {
- node = edit->adjust_count_on;
- for (;;) {
- node->nr_leaves_on_branch += edit->adjust_count_by;
-
- ptr = node->back_pointer;
- if (!ptr)
- break;
- if (assoc_array_ptr_is_shortcut(ptr)) {
- shortcut = assoc_array_ptr_to_shortcut(ptr);
- ptr = shortcut->back_pointer;
- if (!ptr)
- break;
- }
- BUG_ON(!assoc_array_ptr_is_node(ptr));
- node = assoc_array_ptr_to_node(ptr);
- }
-
- edit->array->nr_leaves_on_tree += edit->adjust_count_by;
- }
-
- call_rcu(&edit->rcu, assoc_array_rcu_cleanup);
-}
-
-/**
- * assoc_array_cancel_edit - Discard an edit script.
- * @edit: The script to discard.
- *
- * Free an edit script and all the preallocated data it holds without making
- * any changes to the associative array it was intended for.
- *
- * NOTE! In the case of an insertion script, this does _not_ release the leaf
- * that was to be inserted. That is left to the caller.
- */
-void assoc_array_cancel_edit(struct assoc_array_edit *edit)
-{
- struct assoc_array_ptr *ptr;
- int i;
-
- pr_devel("-->%s()\n", __func__);
-
- /* Clean up after an out of memory error */
- for (i = 0; i < ARRAY_SIZE(edit->new_meta); i++) {
- ptr = edit->new_meta[i];
- if (ptr) {
- if (assoc_array_ptr_is_node(ptr))
- kfree(assoc_array_ptr_to_node(ptr));
- else
- kfree(assoc_array_ptr_to_shortcut(ptr));
- }
- }
- kfree(edit);
-}
-
-/**
- * assoc_array_gc - Garbage collect an associative array.
- * @array: The array to clean.
- * @ops: The operations to use.
- * @iterator: A callback function to pass judgement on each object.
- * @iterator_data: Private data for the callback function.
- *
- * Collect garbage from an associative array and pack down the internal tree to
- * save memory.
- *
- * The iterator function is asked to pass judgement upon each object in the
- * array. If it returns false, the object is discard and if it returns true,
- * the object is kept. If it returns true, it must increment the object's
- * usage count (or whatever it needs to do to retain it) before returning.
- *
- * This function returns 0 if successful or -ENOMEM if out of memory. In the
- * latter case, the array is not changed.
- *
- * The caller should lock against other modifications and must continue to hold
- * the lock until assoc_array_apply_edit() has been called.
- *
- * Accesses to the tree may take place concurrently with this function,
- * provided they hold the RCU read lock.
- */
-int assoc_array_gc(struct assoc_array *array,
- const struct assoc_array_ops *ops,
- bool (*iterator)(void *object, void *iterator_data),
- void *iterator_data)
-{
- struct assoc_array_shortcut *shortcut, *new_s;
- struct assoc_array_node *node, *new_n;
- struct assoc_array_edit *edit;
- struct assoc_array_ptr *cursor, *ptr;
- struct assoc_array_ptr *new_root, *new_parent, **new_ptr_pp;
- unsigned long nr_leaves_on_tree;
- bool retained;
- int keylen, slot, nr_free, next_slot, i;
-
- pr_devel("-->%s()\n", __func__);
-
- if (!array->root)
- return 0;
-
- edit = kzalloc_obj(struct assoc_array_edit);
- if (!edit)
- return -ENOMEM;
- edit->array = array;
- edit->ops = ops;
- edit->ops_for_excised_subtree = ops;
- edit->set[0].ptr = &array->root;
- edit->excised_subtree = array->root;
-
- new_root = new_parent = NULL;
- new_ptr_pp = &new_root;
- cursor = array->root;
-
-descend:
- /* If this point is a shortcut, then we need to duplicate it and
- * advance the target cursor.
- */
- if (assoc_array_ptr_is_shortcut(cursor)) {
- shortcut = assoc_array_ptr_to_shortcut(cursor);
- keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE);
- keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT;
- new_s = kmalloc_flex(*new_s, index_key, keylen);
- if (!new_s)
- goto enomem;
- pr_devel("dup shortcut %p -> %p\n", shortcut, new_s);
- memcpy(new_s, shortcut, struct_size(new_s, index_key, keylen));
- new_s->back_pointer = new_parent;
- new_s->parent_slot = shortcut->parent_slot;
- *new_ptr_pp = new_parent = assoc_array_shortcut_to_ptr(new_s);
- new_ptr_pp = &new_s->next_node;
- cursor = shortcut->next_node;
- }
-
- /* Duplicate the node at this position */
- node = assoc_array_ptr_to_node(cursor);
- new_n = kzalloc_obj(struct assoc_array_node);
- if (!new_n)
- goto enomem;
- pr_devel("dup node %p -> %p\n", node, new_n);
- new_n->back_pointer = new_parent;
- new_n->parent_slot = node->parent_slot;
- *new_ptr_pp = new_parent = assoc_array_node_to_ptr(new_n);
- new_ptr_pp = NULL;
- slot = 0;
-
-continue_node:
- /* Filter across any leaves and gc any subtrees */
- for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- ptr = node->slots[slot];
- if (!ptr)
- continue;
-
- if (assoc_array_ptr_is_leaf(ptr)) {
- if (iterator(assoc_array_ptr_to_leaf(ptr),
- iterator_data))
- /* The iterator will have done any reference
- * counting on the object for us.
- */
- new_n->slots[slot] = ptr;
- continue;
- }
-
- new_ptr_pp = &new_n->slots[slot];
- cursor = ptr;
- goto descend;
- }
-
-retry_compress:
- pr_devel("-- compress node %p --\n", new_n);
-
- /* Count up the number of empty slots in this node and work out the
- * subtree leaf count.
- */
- new_n->nr_leaves_on_branch = 0;
- nr_free = 0;
- for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- ptr = new_n->slots[slot];
- if (!ptr)
- nr_free++;
- else if (assoc_array_ptr_is_leaf(ptr))
- new_n->nr_leaves_on_branch++;
- }
- pr_devel("free=%d, leaves=%lu\n", nr_free, new_n->nr_leaves_on_branch);
-
- /* See what we can fold in */
- retained = false;
- next_slot = 0;
- for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
- struct assoc_array_shortcut *s;
- struct assoc_array_node *child;
-
- ptr = new_n->slots[slot];
- if (!ptr || assoc_array_ptr_is_leaf(ptr))
- continue;
-
- s = NULL;
- if (assoc_array_ptr_is_shortcut(ptr)) {
- s = assoc_array_ptr_to_shortcut(ptr);
- ptr = s->next_node;
- }
-
- child = assoc_array_ptr_to_node(ptr);
- new_n->nr_leaves_on_branch += child->nr_leaves_on_branch;
-
- if (child->nr_leaves_on_branch <= nr_free + 1) {
- /* Fold the child node into this one */
- pr_devel("[%d] fold node %lu/%d [nx %d]\n",
- slot, child->nr_leaves_on_branch, nr_free + 1,
- next_slot);
-
- /* We would already have reaped an intervening shortcut
- * on the way back up the tree.
- */
- BUG_ON(s);
-
- new_n->slots[slot] = NULL;
- nr_free++;
- if (slot < next_slot)
- next_slot = slot;
- for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++) {
- struct assoc_array_ptr *p = child->slots[i];
- if (!p)
- continue;
- BUG_ON(assoc_array_ptr_is_meta(p));
- while (new_n->slots[next_slot])
- next_slot++;
- BUG_ON(next_slot >= ASSOC_ARRAY_FAN_OUT);
- new_n->slots[next_slot++] = p;
- nr_free--;
- }
- kfree(child);
- } else {
- pr_devel("[%d] retain node %lu/%d [nx %d]\n",
- slot, child->nr_leaves_on_branch, nr_free + 1,
- next_slot);
- retained = true;
- }
- }
-
- if (retained && new_n->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT) {
- pr_devel("internal nodes remain despite enough space, retrying\n");
- goto retry_compress;
- }
- pr_devel("after: %lu\n", new_n->nr_leaves_on_branch);
-
- nr_leaves_on_tree = new_n->nr_leaves_on_branch;
-
- /* Excise this node if it is singly occupied by a shortcut */
- if (nr_free == ASSOC_ARRAY_FAN_OUT - 1) {
- for (slot = 0; slot < ASSOC_ARRAY_FAN_OUT; slot++)
- if ((ptr = new_n->slots[slot]))
- break;
-
- if (assoc_array_ptr_is_meta(ptr) &&
- assoc_array_ptr_is_shortcut(ptr)) {
- pr_devel("excise node %p with 1 shortcut\n", new_n);
- new_s = assoc_array_ptr_to_shortcut(ptr);
- new_parent = new_n->back_pointer;
- slot = new_n->parent_slot;
- kfree(new_n);
- if (!new_parent) {
- new_s->back_pointer = NULL;
- new_s->parent_slot = 0;
- new_root = ptr;
- goto gc_complete;
- }
-
- if (assoc_array_ptr_is_shortcut(new_parent)) {
- /* We can discard any preceding shortcut also */
- struct assoc_array_shortcut *s =
- assoc_array_ptr_to_shortcut(new_parent);
-
- pr_devel("excise preceding shortcut\n");
-
- new_parent = new_s->back_pointer = s->back_pointer;
- slot = new_s->parent_slot = s->parent_slot;
- kfree(s);
- if (!new_parent) {
- new_s->back_pointer = NULL;
- new_s->parent_slot = 0;
- new_root = ptr;
- goto gc_complete;
- }
- }
-
- new_s->back_pointer = new_parent;
- new_s->parent_slot = slot;
- new_n = assoc_array_ptr_to_node(new_parent);
- new_n->slots[slot] = ptr;
- goto ascend_old_tree;
- }
- }
-
- /* Excise any shortcuts we might encounter that point to nodes that
- * only contain leaves.
- */
- ptr = new_n->back_pointer;
- if (!ptr)
- goto gc_complete;
-
- if (assoc_array_ptr_is_shortcut(ptr)) {
- new_s = assoc_array_ptr_to_shortcut(ptr);
- new_parent = new_s->back_pointer;
- slot = new_s->parent_slot;
-
- if (new_n->nr_leaves_on_branch <= ASSOC_ARRAY_FAN_OUT) {
- struct assoc_array_node *n;
-
- pr_devel("excise shortcut\n");
- new_n->back_pointer = new_parent;
- new_n->parent_slot = slot;
- kfree(new_s);
- if (!new_parent) {
- new_root = assoc_array_node_to_ptr(new_n);
- goto gc_complete;
- }
-
- n = assoc_array_ptr_to_node(new_parent);
- n->slots[slot] = assoc_array_node_to_ptr(new_n);
- }
- } else {
- new_parent = ptr;
- }
- new_n = assoc_array_ptr_to_node(new_parent);
-
-ascend_old_tree:
- ptr = node->back_pointer;
- if (assoc_array_ptr_is_shortcut(ptr)) {
- shortcut = assoc_array_ptr_to_shortcut(ptr);
- slot = shortcut->parent_slot;
- cursor = shortcut->back_pointer;
- if (!cursor)
- goto gc_complete;
- } else {
- slot = node->parent_slot;
- cursor = ptr;
- }
- BUG_ON(!cursor);
- node = assoc_array_ptr_to_node(cursor);
- slot++;
- goto continue_node;
-
-gc_complete:
- edit->set[0].to = new_root;
- assoc_array_apply_edit(edit);
- array->nr_leaves_on_tree = nr_leaves_on_tree;
- return 0;
-
-enomem:
- pr_devel("enomem\n");
- assoc_array_destroy_subtree(new_root, edit->ops);
- kfree(edit);
- return -ENOMEM;
-}
--
2.47.3
More information about the Linux-security-module-archive
mailing list