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 
zfcp_reqlist_hash(u64 req_id)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  */
zfcp_reqlist_alloc(void)40 static inline struct zfcp_reqlist *zfcp_reqlist_alloc(void)
41 {
42 	size_t i;
43 	struct zfcp_reqlist *rl;
44 
45 	rl = kzalloc(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(&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  */
zfcp_reqlist_isempty(struct zfcp_reqlist * rl)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(&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  */
zfcp_reqlist_free(struct zfcp_reqlist * rl)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(rl);
83 }
84 
85 static inline struct zfcp_fsf_req *
_zfcp_reqlist_find(struct zfcp_reqlist * rl,u64 req_id)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 *
zfcp_reqlist_find(struct zfcp_reqlist * rl,u64 req_id)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(&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 *
zfcp_reqlist_find_rm(struct zfcp_reqlist * rl,u64 req_id)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(&req->list);
141 	spin_unlock_irqrestore(&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  */
zfcp_reqlist_add(struct zfcp_reqlist * rl,struct zfcp_fsf_req * req)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->req_id);
163 
164 	spin_lock_irqsave(&rl->lock, flags);
165 	list_add_tail(&req->list, &rl->buckets[i]);
166 	spin_unlock_irqrestore(&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  */
zfcp_reqlist_move(struct zfcp_reqlist * rl,struct list_head * list)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(&rl->buckets[i], list);
183 	spin_unlock_irqrestore(&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
zfcp_reqlist_apply_for_all(struct zfcp_reqlist * rl,void (* f)(struct zfcp_fsf_req *,void *),void * data)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(&rl->lock, flags);
212 }
213 
214 #endif /* ZFCP_REQLIST_H */
215