summaryrefslogtreecommitdiff
path: root/drivers/s390/scsi/zfcp_reqlist.h
blob: 703fce59befef0be884449e00addad4a49981d61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/*
 * zfcp device driver
 *
 * Data structure and helper functions for tracking pending FSF
 * requests.
 *
 * Copyright IBM Corp. 2009, 2016
 */

#ifndef ZFCP_REQLIST_H
#define ZFCP_REQLIST_H

/* number of hash buckets */
#define ZFCP_REQ_LIST_BUCKETS 128

/**
 * struct zfcp_reqlist - Container for request list (reqlist)
 * @lock: Spinlock for protecting the hash list
 * @list: 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 int zfcp_reqlist_hash(unsigned long 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)
{
	unsigned int 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)
{
	unsigned int 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, unsigned long req_id)
{
	struct zfcp_fsf_req *req;
	unsigned int 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, unsigned long 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, unsigned long 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)
{
	unsigned int 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)
{
	unsigned int 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;
	unsigned int 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 */