1/*-
2 * Copyright (c) 2017 Broadcom. All rights reserved.
3 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 *    this list of conditions and the following disclaimer in the documentation
13 *    and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $FreeBSD$
32 */
33
34/**
35 * @file
36 * Provide IO object allocation.
37 */
38
39/*!
40 * @defgroup io_alloc IO allocation
41 */
42
43#include "ocs.h"
44#include "ocs_scsi.h"
45#include "ocs_els.h"
46#include "ocs_utils.h"
47
48void ocs_mgmt_io_list(ocs_textbuf_t *textbuf, void *io);
49void ocs_mgmt_io_get_all(ocs_textbuf_t *textbuf, void *io);
50int ocs_mgmt_io_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *io);
51
52static ocs_mgmt_functions_t io_mgmt_functions = {
53	.get_list_handler	=	ocs_mgmt_io_list,
54	.get_handler		=	ocs_mgmt_io_get,
55	.get_all_handler	=	ocs_mgmt_io_get_all,
56};
57
58/**
59 * @brief IO pool.
60 *
61 * Structure encapsulating a pool of IO objects.
62 *
63 */
64
65struct ocs_io_pool_s {
66	ocs_t *ocs;			/* Pointer to device object */
67	ocs_lock_t lock;		/* IO pool lock */
68	uint32_t io_num_ios;		/* Total IOs allocated */
69	ocs_pool_t *pool;
70};
71
72/**
73 * @brief Create a pool of IO objects.
74 *
75 * @par Description
76 * This function allocates memory in larger chucks called
77 * "slabs" which are a fixed size. It calculates the number of IO objects that
78 * fit within each "slab" and determines the number of "slabs" required to
79 * allocate the number of IOs requested. Each of the slabs is allocated and
80 * then it grabs each IO object within the slab and adds it to the free list.
81 * Individual command, response and SGL DMA buffers are allocated for each IO.
82 *
83 *           "Slabs"
84 *      +----------------+
85 *      |                |
86 *   +----------------+  |
87 *   |    IO          |  |
88 *   +----------------+  |
89 *   |    ...         |  |
90 *   +----------------+__+
91 *   |    IO          |
92 *   +----------------+
93 *
94 * @param ocs Driver instance's software context.
95 * @param num_io Number of IO contexts to allocate.
96 * @param num_sgl Number of SGL entries to allocate for each IO.
97 *
98 * @return Returns a pointer to a new ocs_io_pool_t on success,
99 *         or NULL on failure.
100 */
101
102ocs_io_pool_t *
103ocs_io_pool_create(ocs_t *ocs, uint32_t num_io, uint32_t num_sgl)
104{
105	uint32_t i = 0;
106	int32_t	rc = -1;
107	ocs_io_pool_t *io_pool;
108
109	/* Allocate the IO pool */
110	io_pool = ocs_malloc(ocs, sizeof(*io_pool), OCS_M_ZERO | OCS_M_NOWAIT);
111	if (io_pool == NULL) {
112		ocs_log_err(ocs, "allocate of IO pool failed\n");
113		return NULL;;
114	}
115
116	io_pool->ocs = ocs;
117	io_pool->io_num_ios = num_io;
118
119	/* initialize IO pool lock */
120	ocs_lock_init(ocs, &io_pool->lock, "io_pool lock[%d]", ocs->instance_index);
121
122	io_pool->pool = ocs_pool_alloc(ocs, sizeof(ocs_io_t), io_pool->io_num_ios, FALSE);
123
124	for (i = 0; i < io_pool->io_num_ios; i++) {
125		ocs_io_t *io = ocs_pool_get_instance(io_pool->pool, i);
126
127		io->tag = i;
128		io->instance_index = i;
129		io->ocs = ocs;
130
131		/* allocate a command/response dma buffer */
132		if (ocs->enable_ini) {
133			rc = ocs_dma_alloc(ocs, &io->cmdbuf, SCSI_CMD_BUF_LENGTH, OCS_MIN_DMA_ALIGNMENT);
134			if (rc) {
135				ocs_log_err(ocs, "ocs_dma_alloc cmdbuf failed\n");
136				ocs_io_pool_free(io_pool);
137				return NULL;
138			}
139		}
140
141		/* Allocate a response buffer */
142		rc = ocs_dma_alloc(ocs, &io->rspbuf, SCSI_RSP_BUF_LENGTH, OCS_MIN_DMA_ALIGNMENT);
143		if (rc) {
144			ocs_log_err(ocs, "ocs_dma_alloc cmdbuf failed\n");
145			ocs_io_pool_free(io_pool);
146			return NULL;
147		}
148
149		/* Allocate SGL */
150		io->sgl = ocs_malloc(ocs, sizeof(*io->sgl) * num_sgl, OCS_M_NOWAIT | OCS_M_ZERO);
151		if (io->sgl == NULL) {
152			ocs_log_err(ocs, "malloc sgl's failed\n");
153			ocs_io_pool_free(io_pool);
154			return NULL;
155		}
156		io->sgl_allocated = num_sgl;
157		io->sgl_count = 0;
158
159		/* Make IO backend call to initialize IO */
160		ocs_scsi_tgt_io_init(io);
161		ocs_scsi_ini_io_init(io);
162
163		rc = ocs_dma_alloc(ocs, &io->els_req, OCS_ELS_REQ_LEN, OCS_MIN_DMA_ALIGNMENT);
164		if (rc) {
165			ocs_log_err(ocs, "ocs_dma_alloc els_req failed\n");
166			ocs_io_pool_free(io_pool);
167			return NULL;
168 		}
169
170		rc = ocs_dma_alloc(ocs, &io->els_rsp, OCS_ELS_GID_PT_RSP_LEN, OCS_MIN_DMA_ALIGNMENT);
171		if (rc) {
172			ocs_log_err(ocs, "ocs_dma_alloc els_rsp failed\n");
173			ocs_io_pool_free(io_pool);
174			return NULL;
175 		}
176	}
177
178	return io_pool;
179}
180
181/**
182 * @brief Free IO objects pool
183 *
184 * @par Description
185 * The pool of IO objects are freed.
186 *
187 * @param io_pool Pointer to IO pool object.
188 *
189 * @return Returns 0 on success, or a negative error code value on failure.
190 */
191int32_t
192ocs_io_pool_free(ocs_io_pool_t *io_pool)
193{
194	ocs_t *ocs;
195	uint32_t i;
196	ocs_io_t *io;
197
198	if (io_pool != NULL) {
199		ocs = io_pool->ocs;
200		for (i = 0; i < io_pool->io_num_ios; i++) {
201			io = ocs_pool_get_instance(io_pool->pool, i);
202			if (!io)
203				continue;
204			ocs_scsi_tgt_io_exit(io);
205			ocs_scsi_ini_io_exit(io);
206			if (io->sgl) {
207				ocs_free(ocs, io->sgl, sizeof(*io->sgl) * io->sgl_allocated);
208			}
209			ocs_dma_free(ocs, &io->cmdbuf);
210			ocs_dma_free(ocs, &io->rspbuf);
211			ocs_dma_free(ocs, &io->els_req);
212			ocs_dma_free(ocs, &io->els_rsp);
213		}
214
215		if (io_pool->pool != NULL) {
216			ocs_pool_free(io_pool->pool);
217		}
218		ocs_lock_free(&io_pool->lock);
219		ocs_free(ocs, io_pool, sizeof(*io_pool));
220		ocs->xport->io_pool = NULL;
221	}
222
223	return 0;
224}
225
226uint32_t ocs_io_pool_allocated(ocs_io_pool_t *io_pool)
227{
228	return io_pool->io_num_ios;
229}
230
231/**
232 * @ingroup io_alloc
233 * @brief Allocate an object used to track an IO.
234 *
235 * @param io_pool Pointer to the IO pool.
236 *
237 * @return Returns the pointer to a new object, or NULL if none available.
238 */
239ocs_io_t *
240ocs_io_pool_io_alloc(ocs_io_pool_t *io_pool)
241{
242	ocs_io_t *io = NULL;
243	ocs_t *ocs;
244
245	ocs_assert(io_pool, NULL);
246
247	ocs = io_pool->ocs;
248
249	ocs_lock(&io_pool->lock);
250	if ((io = ocs_pool_get(io_pool->pool)) != NULL) {
251		ocs_unlock(&io_pool->lock);
252
253		io->io_type = OCS_IO_TYPE_MAX;
254		io->hio_type = OCS_HW_IO_MAX;
255		io->hio = NULL;
256		io->transferred = 0;
257		io->ocs = ocs;
258		io->timeout = 0;
259		io->sgl_count = 0;
260		io->tgt_task_tag = 0;
261		io->init_task_tag = 0;
262		io->hw_tag = 0;
263		io->display_name = "pending";
264		io->seq_init = 0;
265		io->els_req_free = 0;
266		io->mgmt_functions = &io_mgmt_functions;
267		io->io_free = 0;
268		ocs_atomic_add_return(&ocs->xport->io_active_count, 1);
269		ocs_atomic_add_return(&ocs->xport->io_total_alloc, 1);
270	} else {
271		ocs_unlock(&io_pool->lock);
272	}
273	return io;
274}
275
276/**
277 * @ingroup io_alloc
278 * @brief Free an object used to track an IO.
279 *
280 * @param io_pool Pointer to IO pool object.
281 * @param io Pointer to the IO object.
282 */
283void
284ocs_io_pool_io_free(ocs_io_pool_t *io_pool, ocs_io_t *io)
285{
286	ocs_t *ocs;
287	ocs_hw_io_t *hio = NULL;
288
289	ocs_assert(io_pool);
290
291	ocs = io_pool->ocs;
292
293	ocs_lock(&io_pool->lock);
294		hio = io->hio;
295		io->hio = NULL;
296		ocs_pool_put(io_pool->pool, io);
297	ocs_unlock(&io_pool->lock);
298
299	if (hio) {
300		ocs_hw_io_free(&ocs->hw, hio);
301	}
302	io->io_free = 1;
303	ocs_atomic_sub_return(&ocs->xport->io_active_count, 1);
304	ocs_atomic_add_return(&ocs->xport->io_total_free, 1);
305}
306
307/**
308 * @ingroup io_alloc
309 * @brief Find an I/O given it's node and ox_id.
310 *
311 * @param ocs Driver instance's software context.
312 * @param node Pointer to node.
313 * @param ox_id OX_ID to find.
314 * @param rx_id RX_ID to find (0xffff for unassigned).
315 */
316ocs_io_t *
317ocs_io_find_tgt_io(ocs_t *ocs, ocs_node_t *node, uint16_t ox_id, uint16_t rx_id)
318{
319	ocs_io_t	*io = NULL;
320
321	ocs_lock(&node->active_ios_lock);
322		ocs_list_foreach(&node->active_ios, io)
323			if ((io->cmd_tgt && (io->init_task_tag == ox_id)) &&
324			    ((rx_id == 0xffff) || (io->tgt_task_tag == rx_id))) {
325				break;
326			}
327	ocs_unlock(&node->active_ios_lock);
328	return io;
329}
330
331/**
332 * @ingroup io_alloc
333 * @brief Return IO context given the instance index.
334 *
335 * @par Description
336 * Returns a pointer to the IO context given by the instance index.
337 *
338 * @param ocs Pointer to driver structure.
339 * @param index IO instance index to return.
340 *
341 * @return Returns a pointer to the IO context, or NULL if not found.
342 */
343ocs_io_t *
344ocs_io_get_instance(ocs_t *ocs, uint32_t index)
345{
346	ocs_xport_t *xport = ocs->xport;
347	ocs_io_pool_t *io_pool = xport->io_pool;
348	return ocs_pool_get_instance(io_pool->pool, index);
349}
350
351/**
352 * @brief Generate IO context ddump data.
353 *
354 * The ddump data for an IO context is generated.
355 *
356 * @param textbuf Pointer to text buffer.
357 * @param io Pointer to IO context.
358 *
359 * @return None.
360 */
361
362void
363ocs_ddump_io(ocs_textbuf_t *textbuf, ocs_io_t *io)
364{
365	ocs_ddump_section(textbuf, "io", io->instance_index);
366	ocs_ddump_value(textbuf, "display_name", "%s", io->display_name);
367	ocs_ddump_value(textbuf, "node_name", "%s", io->node->display_name);
368
369	ocs_ddump_value(textbuf, "ref_count", "%d", ocs_ref_read_count(&io->ref));
370	ocs_ddump_value(textbuf, "io_type", "%d", io->io_type);
371	ocs_ddump_value(textbuf, "hio_type", "%d", io->hio_type);
372	ocs_ddump_value(textbuf, "cmd_tgt", "%d", io->cmd_tgt);
373	ocs_ddump_value(textbuf, "cmd_ini", "%d", io->cmd_ini);
374	ocs_ddump_value(textbuf, "send_abts", "%d", io->send_abts);
375	ocs_ddump_value(textbuf, "init_task_tag", "0x%x", io->init_task_tag);
376	ocs_ddump_value(textbuf, "tgt_task_tag", "0x%x", io->tgt_task_tag);
377	ocs_ddump_value(textbuf, "hw_tag", "0x%x", io->hw_tag);
378	ocs_ddump_value(textbuf, "tag", "0x%x", io->tag);
379	ocs_ddump_value(textbuf, "timeout", "%d", io->timeout);
380	ocs_ddump_value(textbuf, "tmf_cmd", "%d", io->tmf_cmd);
381	ocs_ddump_value(textbuf, "abort_rx_id", "0x%x", io->abort_rx_id);
382
383	ocs_ddump_value(textbuf, "busy", "%d", ocs_io_busy(io));
384	ocs_ddump_value(textbuf, "transferred", "%zu", io->transferred);
385	ocs_ddump_value(textbuf, "auto_resp", "%d", io->auto_resp);
386	ocs_ddump_value(textbuf, "exp_xfer_len", "%d", io->exp_xfer_len);
387	ocs_ddump_value(textbuf, "xfer_req", "%d", io->xfer_req);
388	ocs_ddump_value(textbuf, "seq_init", "%d", io->seq_init);
389
390	ocs_ddump_value(textbuf, "alloc_link", "%d", ocs_list_on_list(&io->io_alloc_link));
391	ocs_ddump_value(textbuf, "pending_link", "%d", ocs_list_on_list(&io->io_pending_link));
392	ocs_ddump_value(textbuf, "backend_link", "%d", ocs_list_on_list(&io->link));
393
394	if (io->hio) {
395		ocs_ddump_value(textbuf, "hw_tag", "%#x", io->hio->reqtag);
396		ocs_ddump_value(textbuf, "hw_xri", "%#x", io->hio->indicator);
397		ocs_ddump_value(textbuf, "hw_type", "%#x", io->hio->type);
398	} else {
399		ocs_ddump_value(textbuf, "hw_tag", "%s", "pending");
400		ocs_ddump_value(textbuf, "hw_xri", "%s", "pending");
401		ocs_ddump_value(textbuf, "hw_type", "%s", "pending");
402	}
403
404	ocs_scsi_ini_ddump(textbuf, OCS_SCSI_DDUMP_IO, io);
405	ocs_scsi_tgt_ddump(textbuf, OCS_SCSI_DDUMP_IO, io);
406
407	ocs_ddump_endsection(textbuf, "io", io->instance_index);
408}
409
410
411void
412ocs_mgmt_io_list(ocs_textbuf_t *textbuf, void *object)
413{
414
415	/* Readonly values */
416	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "display_name");
417	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "init_task_tag");
418	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "tag");
419	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "transferred");
420	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "auto_resp");
421	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "exp_xfer_len");
422	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "xfer_req");
423}
424
425int
426ocs_mgmt_io_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *object)
427{
428	char qualifier[80];
429	int retval = -1;
430	ocs_io_t *io = (ocs_io_t *) object;
431
432	snprintf(qualifier, sizeof(qualifier), "%s/io[%d]", parent, io->instance_index);
433
434	/* If it doesn't start with my qualifier I don't know what to do with it */
435	if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
436		char *unqualified_name = name + strlen(qualifier) +1;
437
438		/* See if it's a value I can supply */
439		if (ocs_strcmp(unqualified_name, "display_name") == 0) {
440			ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", io->display_name);
441			retval = 0;
442		} else if (ocs_strcmp(unqualified_name, "init_task_tag") == 0) {
443			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "init_task_tag", "0x%x", io->init_task_tag);
444			retval = 0;
445		} else if (ocs_strcmp(unqualified_name, "tgt_task_tag") == 0) {
446			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tgt_task_tag", "0x%x", io->tgt_task_tag);
447			retval = 0;
448		} else if (ocs_strcmp(unqualified_name, "hw_tag") == 0) {
449			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "hw_tag", "0x%x", io->hw_tag);
450			retval = 0;
451		} else if (ocs_strcmp(unqualified_name, "tag") == 0) {
452			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tag", "0x%x", io->tag);
453			retval = 0;
454		} else if (ocs_strcmp(unqualified_name, "transferred") == 0) {
455			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "transferred", "%zu", io->transferred);
456			retval = 0;
457		} else if (ocs_strcmp(unqualified_name, "auto_resp") == 0) {
458			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "auto_resp", io->auto_resp);
459			retval = 0;
460		} else if (ocs_strcmp(unqualified_name, "exp_xfer_len") == 0) {
461			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "exp_xfer_len", "%d", io->exp_xfer_len);
462			retval = 0;
463		} else if (ocs_strcmp(unqualified_name, "xfer_req") == 0) {
464			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "xfer_req", "%d", io->xfer_req);
465			retval = 0;
466		}
467	}
468
469	return retval;
470}
471
472void
473ocs_mgmt_io_get_all(ocs_textbuf_t *textbuf, void *object)
474{
475	ocs_io_t *io = (ocs_io_t *) object;
476
477	ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", io->display_name);
478	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "init_task_tag", "0x%x", io->init_task_tag);
479	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tgt_task_tag", "0x%x", io->tgt_task_tag);
480	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "hw_tag", "0x%x", io->hw_tag);
481	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tag", "0x%x", io->tag);
482	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "transferred", "%zu", io->transferred);
483	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "auto_resp", io->auto_resp);
484	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "exp_xfer_len", "%d", io->exp_xfer_len);
485	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "xfer_req", "%d", io->xfer_req);
486
487}
488
489
490
491
492