1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * zfcp device driver
4 *
5 * Data structure and helper functions for tracking pending FSF
6 * requests.
7 *
8 * Copyright IBM Corp. 2009, 2023
9 */
10
11#ifndef ZFCP_REQLIST_H
12#define ZFCP_REQLIST_H
13
14#include <linux/types.h>
15
16/* number of hash buckets */
17#define ZFCP_REQ_LIST_BUCKETS 128u
18
19/**
20 * struct zfcp_reqlist - Container for request list (reqlist)
21 * @lock: Spinlock for protecting the hash list
22 * @buckets: Array of hashbuckets, each is a list of requests in this bucket
23 */
24struct zfcp_reqlist {
25 spinlock_t lock;
26 struct list_head buckets[ZFCP_REQ_LIST_BUCKETS];
27};
28
29static inline size_t zfcp_reqlist_hash(u64 req_id)
30{
31 return req_id % ZFCP_REQ_LIST_BUCKETS;
32}
33
34/**
35 * zfcp_reqlist_alloc - Allocate and initialize reqlist
36 *
37 * Returns pointer to allocated reqlist on success, or NULL on
38 * allocation failure.
39 */
40static inline struct zfcp_reqlist *zfcp_reqlist_alloc(void)
41{
42 size_t i;
43 struct zfcp_reqlist *rl;
44
45 rl = kzalloc(size: sizeof(struct zfcp_reqlist), GFP_KERNEL);
46 if (!rl)
47 return NULL;
48
49 spin_lock_init(&rl->lock);
50
51 for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
52 INIT_LIST_HEAD(list: &rl->buckets[i]);
53
54 return rl;
55}
56
57/**
58 * zfcp_reqlist_isempty - Check whether the request list empty
59 * @rl: pointer to reqlist
60 *
61 * Returns: 1 if list is empty, 0 if not
62 */
63static inline int zfcp_reqlist_isempty(struct zfcp_reqlist *rl)
64{
65 size_t i;
66
67 for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
68 if (!list_empty(head: &rl->buckets[i]))
69 return 0;
70 return 1;
71}
72
73/**
74 * zfcp_reqlist_free - Free allocated memory for reqlist
75 * @rl: The reqlist where to free memory
76 */
77static inline void zfcp_reqlist_free(struct zfcp_reqlist *rl)
78{
79 /* sanity check */
80 BUG_ON(!zfcp_reqlist_isempty(rl));
81
82 kfree(objp: rl);
83}
84
85static inline struct zfcp_fsf_req *
86_zfcp_reqlist_find(struct zfcp_reqlist *rl, u64 req_id)
87{
88 struct zfcp_fsf_req *req;
89 size_t i;
90
91 i = zfcp_reqlist_hash(req_id);
92 list_for_each_entry(req, &rl->buckets[i], list)
93 if (req->req_id == req_id)
94 return req;
95 return NULL;
96}
97
98/**
99 * zfcp_reqlist_find - Lookup FSF request by its request id
100 * @rl: The reqlist where to lookup the FSF request
101 * @req_id: The request id to look for
102 *
103 * Returns a pointer to the FSF request with the specified request id
104 * or NULL if there is no known FSF request with this id.
105 */
106static inline struct zfcp_fsf_req *
107zfcp_reqlist_find(struct zfcp_reqlist *rl, u64 req_id)
108{
109 unsigned long flags;
110 struct zfcp_fsf_req *req;
111
112 spin_lock_irqsave(&rl->lock, flags);
113 req = _zfcp_reqlist_find(rl, req_id);
114 spin_unlock_irqrestore(lock: &rl->lock, flags);
115
116 return req;
117}
118
119/**
120 * zfcp_reqlist_find_rm - Lookup request by id and remove it from reqlist
121 * @rl: reqlist where to search and remove entry
122 * @req_id: The request id of the request to look for
123 *
124 * This functions tries to find the FSF request with the specified
125 * id and then removes it from the reqlist. The reqlist lock is held
126 * during both steps of the operation.
127 *
128 * Returns: Pointer to the FSF request if the request has been found,
129 * NULL if it has not been found.
130 */
131static inline struct zfcp_fsf_req *
132zfcp_reqlist_find_rm(struct zfcp_reqlist *rl, u64 req_id)
133{
134 unsigned long flags;
135 struct zfcp_fsf_req *req;
136
137 spin_lock_irqsave(&rl->lock, flags);
138 req = _zfcp_reqlist_find(rl, req_id);
139 if (req)
140 list_del(entry: &req->list);
141 spin_unlock_irqrestore(lock: &rl->lock, flags);
142
143 return req;
144}
145
146/**
147 * zfcp_reqlist_add - Add entry to reqlist
148 * @rl: reqlist where to add the entry
149 * @req: The entry to add
150 *
151 * The request id always increases. As an optimization new requests
152 * are added here with list_add_tail at the end of the bucket lists
153 * while old requests are looked up starting at the beginning of the
154 * lists.
155 */
156static inline void zfcp_reqlist_add(struct zfcp_reqlist *rl,
157 struct zfcp_fsf_req *req)
158{
159 size_t i;
160 unsigned long flags;
161
162 i = zfcp_reqlist_hash(req_id: req->req_id);
163
164 spin_lock_irqsave(&rl->lock, flags);
165 list_add_tail(new: &req->list, head: &rl->buckets[i]);
166 spin_unlock_irqrestore(lock: &rl->lock, flags);
167}
168
169/**
170 * zfcp_reqlist_move - Move all entries from reqlist to simple list
171 * @rl: The zfcp_reqlist where to remove all entries
172 * @list: The list where to move all entries
173 */
174static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl,
175 struct list_head *list)
176{
177 size_t i;
178 unsigned long flags;
179
180 spin_lock_irqsave(&rl->lock, flags);
181 for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
182 list_splice_init(list: &rl->buckets[i], head: list);
183 spin_unlock_irqrestore(lock: &rl->lock, flags);
184}
185
186/**
187 * zfcp_reqlist_apply_for_all() - apply a function to every request.
188 * @rl: the requestlist that contains the target requests.
189 * @f: the function to apply to each request; the first parameter of the
190 * function will be the target-request; the second parameter is the same
191 * pointer as given with the argument @data.
192 * @data: freely chosen argument; passed through to @f as second parameter.
193 *
194 * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash-
195 * table (not a 'safe' variant, so don't modify the list).
196 *
197 * Holds @rl->lock over the entire request-iteration.
198 */
199static inline void
200zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl,
201 void (*f)(struct zfcp_fsf_req *, void *), void *data)
202{
203 struct zfcp_fsf_req *req;
204 unsigned long flags;
205 size_t i;
206
207 spin_lock_irqsave(&rl->lock, flags);
208 for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
209 list_for_each_entry(req, &rl->buckets[i], list)
210 f(req, data);
211 spin_unlock_irqrestore(lock: &rl->lock, flags);
212}
213
214#endif /* ZFCP_REQLIST_H */
215

source code of linux/drivers/s390/scsi/zfcp_reqlist.h