/* 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 */