/* SPDX-License-Identifier: GPL-2.0 */ /* * zfcp device driver * * Data structure and helper functions for tracking pending FSF * requests. * * Copyright IBM Corp. 2009, 2023 */ #ifndef ZFCP_REQLIST_H #define ZFCP_REQLIST_H #include <linux/types.h> /* number of hash buckets */ #define ZFCP_REQ_LIST_BUCKETS 128u /** * struct zfcp_reqlist - Container for request list (reqlist) * @lock: Spinlock for protecting the hash list * @buckets: Array of hashbuckets, each is a list of requests in this bucket */ struct zfcp_reqlist { spinlock_t lock; struct list_head buckets[ZFCP_REQ_LIST_BUCKETS]; }; static inline size_t zfcp_reqlist_hash(u64 req_id) { return req_id % ZFCP_REQ_LIST_BUCKETS; } /** * zfcp_reqlist_alloc - Allocate and initialize reqlist * * Returns pointer to allocated reqlist on success, or NULL on * allocation failure. */ static inline struct zfcp_reqlist *zfcp_reqlist_alloc(void) { size_t i; struct zfcp_reqlist *rl; rl = kzalloc(sizeof(struct zfcp_reqlist), GFP_KERNEL); if (!rl) return NULL; spin_lock_init(&rl->lock); for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) INIT_LIST_HEAD(&rl->buckets[i]); return rl; } /** * zfcp_reqlist_isempty - Check whether the request list empty * @rl: pointer to reqlist * * Returns: 1 if list is empty, 0 if not */ static inline int zfcp_reqlist_isempty(struct zfcp_reqlist *rl) { size_t i; for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) if (!list_empty(&rl->buckets[i])) return 0; return 1; } /** * zfcp_reqlist_free - Free allocated memory for reqlist * @rl: The reqlist where to free memory */ static inline void zfcp_reqlist_free(struct zfcp_reqlist *rl) { /* sanity check */ BUG_ON(!zfcp_reqlist_isempty(rl)); kfree(rl); } static inline struct zfcp_fsf_req * _zfcp_reqlist_find(struct zfcp_reqlist *rl, u64 req_id) { struct zfcp_fsf_req *req; size_t i; i = zfcp_reqlist_hash(req_id); list_for_each_entry(req, &rl->buckets[i], list) if (req->req_id == req_id) return req; return NULL; } /** * zfcp_reqlist_find - Lookup FSF request by its request id * @rl: The reqlist where to lookup the FSF request * @req_id: The request id to look for * * Returns a pointer to the FSF request with the specified request id * or NULL if there is no known FSF request with this id. */ static inline struct zfcp_fsf_req * zfcp_reqlist_find(struct zfcp_reqlist *rl, u64 req_id) { unsigned long flags; struct zfcp_fsf_req *req; spin_lock_irqsave(&rl->lock, flags); req = _zfcp_reqlist_find(rl, req_id); spin_unlock_irqrestore(&rl->lock, flags); return req; } /** * zfcp_reqlist_find_rm - Lookup request by id and remove it from reqlist * @rl: reqlist where to search and remove entry * @req_id: The request id of the request to look for * * This functions tries to find the FSF request with the specified * id and then removes it from the reqlist. The reqlist lock is held * during both steps of the operation. * * Returns: Pointer to the FSF request if the request has been found, * NULL if it has not been found. */ static inline struct zfcp_fsf_req * zfcp_reqlist_find_rm(struct zfcp_reqlist *rl, u64 req_id) { unsigned long flags; struct zfcp_fsf_req *req; spin_lock_irqsave(&rl->lock, flags); req = _zfcp_reqlist_find(rl, req_id); if (req) list_del(&req->list); spin_unlock_irqrestore(&rl->lock, flags); return req; } /** * zfcp_reqlist_add - Add entry to reqlist * @rl: reqlist where to add the entry * @req: The entry to add * * The request id always increases. As an optimization new requests * are added here with list_add_tail at the end of the bucket lists * while old requests are looked up starting at the beginning of the * lists. */ static inline void zfcp_reqlist_add(struct zfcp_reqlist *rl, struct zfcp_fsf_req *req) { size_t i; unsigned long flags; i = zfcp_reqlist_hash(req->req_id); spin_lock_irqsave(&rl->lock, flags); list_add_tail(&req->list, &rl->buckets[i]); spin_unlock_irqrestore(&rl->lock, flags); } /** * zfcp_reqlist_move - Move all entries from reqlist to simple list * @rl: The zfcp_reqlist where to remove all entries * @list: The list where to move all entries */ static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl, struct list_head *list) { size_t i; unsigned long flags; spin_lock_irqsave(&rl->lock, flags); for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) list_splice_init(&rl->buckets[i], list); spin_unlock_irqrestore(&rl->lock, flags); } /** * zfcp_reqlist_apply_for_all() - apply a function to every request. * @rl: the requestlist that contains the target requests. * @f: the function to apply to each request; the first parameter of the * function will be the target-request; the second parameter is the same * pointer as given with the argument @data. * @data: freely chosen argument; passed through to @f as second parameter. * * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash- * table (not a 'safe' variant, so don't modify the list). * * Holds @rl->lock over the entire request-iteration. */ static inline void zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl, void (*f)(struct zfcp_fsf_req *, void *), void *data) { struct zfcp_fsf_req *req; unsigned long flags; size_t i; spin_lock_irqsave(&rl->lock, flags); for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++) list_for_each_entry(req, &rl->buckets[i], list) f(req, data); spin_unlock_irqrestore(&rl->lock, flags); } #endif /* ZFCP_REQLIST_H */