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