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 */
35void
36pqisrc_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 */
58uint32_t
59pqisrc_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 */
84int
85pqisrc_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
123err_lock:
124	os_mem_free(softs, (char *)taglist->elem_array,
125		(taglist->max_elem * sizeof(uint32_t)));
126	taglist->elem_array = NULL;
127err_out:
128	DBG_FUNC("OUT failed\n");
129	return ret;
130}
131
132/*
133 * Destroy circular queue implementation of tag list.
134 */
135void
136pqisrc_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 */
156int
157pqisrc_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;
187err_out:
188	DBG_FUNC("Failed OUT\n");
189	return ret;
190}
191
192/*
193 * Destroy circular queue implementation of tag list.
194 */
195void
196pqisrc_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 */
213void
214pqisrc_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 */
250uint32_t
251pqisrc_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