xref: /freebsd/sys/dev/smartpqi/smartpqi_tag.c (revision 9768746b)
1 /*-
2  * Copyright 2016-2021 Microchip Technology, Inc. and/or its subsidiaries.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 /* $FreeBSD$ */
27 
28 #include "smartpqi_includes.h"
29 
30 #ifndef LOCKFREE_STACK
31 
32 /*
33  * Function used to release the tag from taglist.
34  */
35 void
36 pqisrc_put_tag(pqi_taglist_t *taglist, uint32_t elem)
37 {
38 
39 	OS_ACQUIRE_SPINLOCK(&(taglist->lock));
40 	DBG_FUNC("IN\n");
41 
42 	ASSERT(taglist->num_elem < taglist->max_elem);
43 
44 	if (taglist->num_elem < taglist->max_elem) {
45 		taglist->elem_array[taglist->tail] = elem;
46 		taglist->num_elem++;
47 		taglist->tail = (taglist->tail + 1) % taglist->max_elem;
48 	}
49 
50 	OS_RELEASE_SPINLOCK(&taglist->lock);
51 
52 	DBG_FUNC("OUT\n");
53 }
54 
55 /*
56  * Function used to get an unoccupied tag from the tag list.
57  */
58 uint32_t
59 pqisrc_get_tag(pqi_taglist_t *taglist)
60 {
61 	uint32_t elem = INVALID_ELEM;
62 
63 /*	DBG_FUNC("IN\n");*/
64 
65 	OS_ACQUIRE_SPINLOCK(&taglist->lock);
66 
67 	ASSERT(taglist->num_elem > 0);
68 
69 	if (taglist->num_elem > 0) {
70 		elem = taglist->elem_array[taglist->head];
71 		taglist->num_elem--;
72 		taglist->head = (taglist->head + 1) % taglist->max_elem;
73 	}
74 
75 	OS_RELEASE_SPINLOCK(&taglist->lock);
76 
77 /*	DBG_FUNC("OUT got %d\n", elem);*/
78 	return elem;
79 }
80 
81 /*
82  * Initialize circular queue implementation of tag list.
83  */
84 int
85 pqisrc_init_taglist(pqisrc_softstate_t *softs, pqi_taglist_t *taglist,
86 				uint32_t max_elem)
87 {
88 	int ret = PQI_STATUS_SUCCESS;
89 	int i = 0;
90 
91 	DBG_FUNC("IN\n");
92 
93 	taglist->max_elem = max_elem;
94 	taglist->num_elem = 0;
95 	taglist->head = 0;
96 	taglist->tail = 0;
97 	taglist->elem_array = os_mem_alloc(softs,
98 			(max_elem * sizeof(uint32_t)));
99 	if (!(taglist->elem_array)) {
100 		DBG_FUNC("Unable to allocate memory for taglist\n");
101 		ret = PQI_STATUS_FAILURE;
102 		goto err_out;
103 	}
104 
105 	os_strlcpy(taglist->lockname, "tag_lock",  LOCKNAME_SIZE);
106 	ret = os_init_spinlock(softs, &taglist->lock, taglist->lockname);
107 	if(ret){
108 		DBG_ERR("tag lock initialization failed\n");
109 		taglist->lockcreated=false;
110 		goto err_lock;
111 	}
112 	taglist->lockcreated = true;
113 
114 	/* indices 1 to max_elem are considered as valid tags */
115 	for (i=1; i <= max_elem; i++) {
116 		softs->rcb[i].tag = INVALID_ELEM;
117 		pqisrc_put_tag(taglist, i);
118 	}
119 
120 	DBG_FUNC("OUT\n");
121 	return ret;
122 
123 err_lock:
124 	os_mem_free(softs, (char *)taglist->elem_array,
125 		(taglist->max_elem * sizeof(uint32_t)));
126 	taglist->elem_array = NULL;
127 err_out:
128 	DBG_FUNC("OUT failed\n");
129 	return ret;
130 }
131 
132 /*
133  * Destroy circular queue implementation of tag list.
134  */
135 void
136 pqisrc_destroy_taglist(pqisrc_softstate_t *softs, pqi_taglist_t *taglist)
137 {
138 	DBG_FUNC("IN\n");
139 	os_mem_free(softs, (char *)taglist->elem_array,
140 		(taglist->max_elem * sizeof(uint32_t)));
141 	taglist->elem_array = NULL;
142 
143 	if(taglist->lockcreated==true){
144 		os_uninit_spinlock(&taglist->lock);
145 		taglist->lockcreated = false;
146 	}
147 
148 	DBG_FUNC("OUT\n");
149 }
150 
151 #else	 /* LOCKFREE_STACK */
152 
153 /*
154  * Initialize circular queue implementation of tag list.
155  */
156 int
157 pqisrc_init_taglist(pqisrc_softstate_t *softs, lockless_stack_t *stack,
158 				uint32_t max_elem)
159 {
160 	int ret = PQI_STATUS_SUCCESS;
161 	int index = 0;
162 
163 	DBG_FUNC("IN\n");
164 
165 	/* indices 1 to max_elem are considered as valid tags */
166 	stack->max_elem = max_elem + 1;
167 	stack->head.data = 0;
168 	DBG_INFO("Stack head address :%p\n",&stack->head);
169 
170 	/*Allocate memory for stack*/
171 	stack->next_index_array = (uint32_t*)os_mem_alloc(softs,
172 		(stack->max_elem * sizeof(uint32_t)));
173 	if (!(stack->next_index_array)) {
174 		DBG_ERR("Unable to allocate memory for stack\n");
175 		ret = PQI_STATUS_FAILURE;
176 		goto err_out;
177 	}
178 
179 	/* push all the entries to the stack */
180 	for (index = 1; index < stack->max_elem ; index++) {
181 		softs->rcb[index].tag = INVALID_ELEM;
182 		pqisrc_put_tag(stack, index);
183 	}
184 
185 	DBG_FUNC("OUT\n");
186 	return ret;
187 err_out:
188 	DBG_FUNC("Failed OUT\n");
189 	return ret;
190 }
191 
192 /*
193  * Destroy circular queue implementation of tag list.
194  */
195 void
196 pqisrc_destroy_taglist(pqisrc_softstate_t *softs, lockless_stack_t *stack)
197 {
198 	DBG_FUNC("IN\n");
199 
200 	/* de-allocate stack memory */
201 	if (stack->next_index_array) {
202 		os_mem_free(softs,(char*)stack->next_index_array,
203 			(stack->max_elem * sizeof(uint32_t)));
204 		stack->next_index_array = NULL;
205 	}
206 
207 	DBG_FUNC("OUT\n");
208 }
209 
210 /*
211  * Function used to release the tag from taglist.
212  */
213 void
214 pqisrc_put_tag(lockless_stack_t *stack, uint32_t index)
215 {
216    union head_list cur_head, new_head;
217 
218 	DBG_FUNC("IN\n");
219  	DBG_INFO("push tag :%d\n",index);
220 
221 	if (index >= stack->max_elem) {
222 		ASSERT(false);
223 		DBG_INFO("Pushed Invalid index\n"); /* stack full */
224 		return;
225 	}
226 
227 	if (stack->next_index_array[index] != 0) {
228  		ASSERT(false);
229 		DBG_INFO("Index already present as tag in the stack\n");
230 		return;
231 	}
232 
233 	do {
234 		cur_head = stack->head;
235 		/* increment seq_no */
236  		new_head.top.seq_no = cur_head.top.seq_no + 1;
237 		/* update the index at the top of the stack with the new index */
238 		new_head.top.index = index;
239 		/* Create a link to the previous index */
240 		stack->next_index_array[index] = cur_head.top.index;
241 	}while(!os_atomic64_cas(&stack->head.data,cur_head.data,new_head.data));
242 	stack->num_elem++;
243  	DBG_FUNC("OUT\n");
244  	return;
245 }
246 
247 /*
248  * Function used to get an unoccupied tag from the tag list.
249  */
250 uint32_t
251 pqisrc_get_tag(lockless_stack_t *stack)
252 {
253 	union head_list cur_head, new_head;
254 
255 	DBG_FUNC("IN\n");
256 	do {
257 		cur_head = stack->head;
258 		if (cur_head.top.index == 0)    /* stack empty */
259 			return INVALID_ELEM;
260 		/* increment seq_no field */
261 		new_head.top.seq_no = cur_head.top.seq_no + 1;
262 		/* update the index at the top of the stack with the next index */
263 		new_head.top.index = stack->next_index_array[cur_head.top.index];
264 	}while(!os_atomic64_cas(&stack->head.data,cur_head.data,new_head.data));
265  	stack->next_index_array[cur_head.top.index] = 0;
266 	stack->num_elem--;
267 
268 	DBG_INFO("pop tag: %d\n",cur_head.top.index);
269  	DBG_FUNC("OUT\n");
270 	return cur_head.top.index; /*tag*/
271 }
272 #endif /* LOCKFREE_STACK */
273