// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2023 Red Hat */ #include "physical-zone.h" #include <linux/list.h> #include "logger.h" #include "memory-alloc.h" #include "permassert.h" #include "block-map.h" #include "completion.h" #include "constants.h" #include "data-vio.h" #include "dedupe.h" #include "encodings.h" #include "flush.h" #include "int-map.h" #include "slab-depot.h" #include "status-codes.h" #include "vdo.h" /* Each user data_vio needs a PBN read lock and write lock. */ #define LOCK_POOL_CAPACITY … struct pbn_lock_implementation { … }; /* This array must have an entry for every pbn_lock_type value. */ static const struct pbn_lock_implementation LOCK_IMPLEMENTATIONS[] = …; static inline bool has_lock_type(const struct pbn_lock *lock, enum pbn_lock_type type) { … } /** * vdo_is_pbn_read_lock() - Check whether a pbn_lock is a read lock. * @lock: The lock to check. * * Return: true if the lock is a read lock. */ bool vdo_is_pbn_read_lock(const struct pbn_lock *lock) { … } static inline void set_pbn_lock_type(struct pbn_lock *lock, enum pbn_lock_type type) { … } /** * vdo_downgrade_pbn_write_lock() - Downgrade a PBN write lock to a PBN read lock. * @lock: The PBN write lock to downgrade. * * The lock holder count is cleared and the caller is responsible for setting the new count. */ void vdo_downgrade_pbn_write_lock(struct pbn_lock *lock, bool compressed_write) { … } /** * vdo_claim_pbn_lock_increment() - Try to claim one of the available reference count increments on * a read lock. * @lock: The PBN read lock from which to claim an increment. * * Claims may be attempted from any thread. A claim is only valid until the PBN lock is released. * * Return: true if the claim succeeded, guaranteeing one increment can be made without overflowing * the PBN's reference count. */ bool vdo_claim_pbn_lock_increment(struct pbn_lock *lock) { … } /** * vdo_assign_pbn_lock_provisional_reference() - Inform a PBN lock that it is responsible for a * provisional reference. * @lock: The PBN lock. */ void vdo_assign_pbn_lock_provisional_reference(struct pbn_lock *lock) { … } /** * vdo_unassign_pbn_lock_provisional_reference() - Inform a PBN lock that it is no longer * responsible for a provisional reference. * @lock: The PBN lock. */ void vdo_unassign_pbn_lock_provisional_reference(struct pbn_lock *lock) { … } /** * release_pbn_lock_provisional_reference() - If the lock is responsible for a provisional * reference, release that reference. * @lock: The lock. * @locked_pbn: The PBN covered by the lock. * @allocator: The block allocator from which to release the reference. * * This method is called when the lock is released. */ static void release_pbn_lock_provisional_reference(struct pbn_lock *lock, physical_block_number_t locked_pbn, struct block_allocator *allocator) { … } /** * union idle_pbn_lock - PBN lock list entries. * * Unused (idle) PBN locks are kept in a list. Just like in a malloc implementation, the lock * structure is unused memory, so we can save a bit of space (and not pollute the lock structure * proper) by using a union to overlay the lock structure with the free list. */ idle_pbn_lock; /** * struct pbn_lock_pool - list of PBN locks. * * The lock pool is little more than the memory allocated for the locks. */ struct pbn_lock_pool { … }; /** * return_pbn_lock_to_pool() - Return a pbn lock to its pool. * @pool: The pool from which the lock was borrowed. * @lock: The last reference to the lock being returned. * * It must be the last live reference, as if the memory were being freed (the lock memory will * re-initialized or zeroed). */ static void return_pbn_lock_to_pool(struct pbn_lock_pool *pool, struct pbn_lock *lock) { … } /** * make_pbn_lock_pool() - Create a new PBN lock pool and all the lock instances it can loan out. * * @capacity: The number of PBN locks to allocate for the pool. * @pool_ptr: A pointer to receive the new pool. * * Return: VDO_SUCCESS or an error code. */ static int make_pbn_lock_pool(size_t capacity, struct pbn_lock_pool **pool_ptr) { … } /** * free_pbn_lock_pool() - Free a PBN lock pool. * @pool: The lock pool to free. * * This also frees all the PBN locks it allocated, so the caller must ensure that all locks have * been returned to the pool. */ static void free_pbn_lock_pool(struct pbn_lock_pool *pool) { … } /** * borrow_pbn_lock_from_pool() - Borrow a PBN lock from the pool and initialize it with the * provided type. * @pool: The pool from which to borrow. * @type: The type with which to initialize the lock. * @lock_ptr: A pointer to receive the borrowed lock. * * Pools do not grow on demand or allocate memory, so this will fail if the pool is empty. Borrowed * locks are still associated with this pool and must be returned to only this pool. * * Return: VDO_SUCCESS, or VDO_LOCK_ERROR if the pool is empty. */ static int __must_check borrow_pbn_lock_from_pool(struct pbn_lock_pool *pool, enum pbn_lock_type type, struct pbn_lock **lock_ptr) { … } /** * initialize_zone() - Initialize a physical zone. * @vdo: The vdo to which the zone will belong. * @zones: The physical_zones to which the zone being initialized belongs * * Return: VDO_SUCCESS or an error code. */ static int initialize_zone(struct vdo *vdo, struct physical_zones *zones) { … } /** * vdo_make_physical_zones() - Make the physical zones for a vdo. * @vdo: The vdo being constructed * @zones_ptr: A pointer to hold the zones * * Return: VDO_SUCCESS or an error code. */ int vdo_make_physical_zones(struct vdo *vdo, struct physical_zones **zones_ptr) { … } /** * vdo_free_physical_zones() - Destroy the physical zones. * @zones: The zones to free. */ void vdo_free_physical_zones(struct physical_zones *zones) { … } /** * vdo_get_physical_zone_pbn_lock() - Get the lock on a PBN if one exists. * @zone: The physical zone responsible for the PBN. * @pbn: The physical block number whose lock is desired. * * Return: The lock or NULL if the PBN is not locked. */ struct pbn_lock *vdo_get_physical_zone_pbn_lock(struct physical_zone *zone, physical_block_number_t pbn) { … } /** * vdo_attempt_physical_zone_pbn_lock() - Attempt to lock a physical block in the zone responsible * for it. * @zone: The physical zone responsible for the PBN. * @pbn: The physical block number to lock. * @type: The type with which to initialize a new lock. * @lock_ptr: A pointer to receive the lock, existing or new. * * If the PBN is already locked, the existing lock will be returned. Otherwise, a new lock instance * will be borrowed from the pool, initialized, and returned. The lock owner will be NULL for a new * lock acquired by the caller, who is responsible for setting that field promptly. The lock owner * will be non-NULL when there is already an existing lock on the PBN. * * Return: VDO_SUCCESS or an error. */ int vdo_attempt_physical_zone_pbn_lock(struct physical_zone *zone, physical_block_number_t pbn, enum pbn_lock_type type, struct pbn_lock **lock_ptr) { … } /** * allocate_and_lock_block() - Attempt to allocate a block from this zone. * @allocation: The struct allocation of the data_vio attempting to allocate. * * If a block is allocated, the recipient will also hold a lock on it. * * Return: VDO_SUCCESS if a block was allocated, or an error code. */ static int allocate_and_lock_block(struct allocation *allocation) { … } /** * retry_allocation() - Retry allocating a block now that we're done waiting for scrubbing. * @waiter: The allocating_vio that was waiting to allocate. * @context: The context (unused). */ static void retry_allocation(struct vdo_waiter *waiter, void *context __always_unused) { … } /** * continue_allocating() - Continue searching for an allocation by enqueuing to wait for scrubbing * or switching to the next zone. * @data_vio: The data_vio attempting to get an allocation. * * This method should only be called from the error handler set in data_vio_allocate_data_block. * * Return: true if the allocation process has continued in another zone. */ static bool continue_allocating(struct data_vio *data_vio) { … } /** * vdo_allocate_block_in_zone() - Attempt to allocate a block in the current physical zone, and if * that fails try the next if possible. * @data_vio: The data_vio needing an allocation. * * Return: true if a block was allocated, if not the data_vio will have been dispatched so the * caller must not touch it. */ bool vdo_allocate_block_in_zone(struct data_vio *data_vio) { … } /** * vdo_release_physical_zone_pbn_lock() - Release a physical block lock if it is held and return it * to the lock pool. * @zone: The physical zone in which the lock was obtained. * @locked_pbn: The physical block number to unlock. * @lock: The lock being released. * * It must be the last live reference, as if the memory were being freed (the * lock memory will re-initialized or zeroed). */ void vdo_release_physical_zone_pbn_lock(struct physical_zone *zone, physical_block_number_t locked_pbn, struct pbn_lock *lock) { … } /** * vdo_dump_physical_zone() - Dump information about a physical zone to the log for debugging. * @zone: The zone to dump. */ void vdo_dump_physical_zone(const struct physical_zone *zone) { … }