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 | */ |
24 | struct zfcp_reqlist { |
25 | spinlock_t lock; |
26 | struct list_head buckets[ZFCP_REQ_LIST_BUCKETS]; |
27 | }; |
28 | |
29 | static 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 | */ |
40 | static 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 | */ |
63 | static 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 | */ |
77 | static 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 | |
85 | static 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 | */ |
106 | static inline struct zfcp_fsf_req * |
107 | zfcp_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 | */ |
131 | static inline struct zfcp_fsf_req * |
132 | zfcp_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 | */ |
156 | static 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 | */ |
174 | static 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 | */ |
199 | static inline void |
200 | zfcp_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 | |