xref: /freebsd/sys/cam/ctl/ctl_frontend_ioctl.c (revision edbd489d09babebdc6c03924a912013be584c409)
167ceb24bSAlexander Motin /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
38951f055SMarcelo Araujo  *
467ceb24bSAlexander Motin  * Copyright (c) 2003-2009 Silicon Graphics International Corp.
567ceb24bSAlexander Motin  * Copyright (c) 2012 The FreeBSD Foundation
667ceb24bSAlexander Motin  * Copyright (c) 2015 Alexander Motin <mav@FreeBSD.org>
78951f055SMarcelo Araujo  * Copyright (c) 2017 Jakub Wojciech Klama <jceel@FreeBSD.org>
867ceb24bSAlexander Motin  * All rights reserved.
967ceb24bSAlexander Motin  *
1067ceb24bSAlexander Motin  * Redistribution and use in source and binary forms, with or without
1167ceb24bSAlexander Motin  * modification, are permitted provided that the following conditions
1267ceb24bSAlexander Motin  * are met:
1367ceb24bSAlexander Motin  * 1. Redistributions of source code must retain the above copyright
1467ceb24bSAlexander Motin  *    notice, this list of conditions and the following disclaimer,
1567ceb24bSAlexander Motin  *    without modification, immediately at the beginning of the file.
1667ceb24bSAlexander Motin  * 2. Redistributions in binary form must reproduce the above copyright
1767ceb24bSAlexander Motin  *    notice, this list of conditions and the following disclaimer in the
1867ceb24bSAlexander Motin  *    documentation and/or other materials provided with the distribution.
1967ceb24bSAlexander Motin  *
2067ceb24bSAlexander Motin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2167ceb24bSAlexander Motin  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2267ceb24bSAlexander Motin  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2367ceb24bSAlexander Motin  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2467ceb24bSAlexander Motin  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2567ceb24bSAlexander Motin  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2667ceb24bSAlexander Motin  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2767ceb24bSAlexander Motin  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2867ceb24bSAlexander Motin  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2967ceb24bSAlexander Motin  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3067ceb24bSAlexander Motin  */
3167ceb24bSAlexander Motin 
3267ceb24bSAlexander Motin #include <sys/param.h>
3367ceb24bSAlexander Motin #include <sys/systm.h>
3467ceb24bSAlexander Motin #include <sys/kernel.h>
3567ceb24bSAlexander Motin #include <sys/types.h>
3667ceb24bSAlexander Motin #include <sys/lock.h>
3767ceb24bSAlexander Motin #include <sys/module.h>
3867ceb24bSAlexander Motin #include <sys/mutex.h>
3967ceb24bSAlexander Motin #include <sys/condvar.h>
4067ceb24bSAlexander Motin #include <sys/malloc.h>
4167ceb24bSAlexander Motin #include <sys/conf.h>
4267ceb24bSAlexander Motin #include <sys/queue.h>
4367ceb24bSAlexander Motin #include <sys/sysctl.h>
448951f055SMarcelo Araujo #include <sys/nv.h>
458951f055SMarcelo Araujo #include <sys/dnv.h>
4667ceb24bSAlexander Motin 
4767ceb24bSAlexander Motin #include <cam/cam.h>
4867ceb24bSAlexander Motin #include <cam/scsi/scsi_all.h>
4967ceb24bSAlexander Motin #include <cam/scsi/scsi_da.h>
5067ceb24bSAlexander Motin #include <cam/ctl/ctl_io.h>
5167ceb24bSAlexander Motin #include <cam/ctl/ctl.h>
5267ceb24bSAlexander Motin #include <cam/ctl/ctl_frontend.h>
5367ceb24bSAlexander Motin #include <cam/ctl/ctl_util.h>
5467ceb24bSAlexander Motin #include <cam/ctl/ctl_backend.h>
5567ceb24bSAlexander Motin #include <cam/ctl/ctl_ioctl.h>
5667ceb24bSAlexander Motin #include <cam/ctl/ctl_ha.h>
5767ceb24bSAlexander Motin #include <cam/ctl/ctl_private.h>
5867ceb24bSAlexander Motin #include <cam/ctl/ctl_debug.h>
5967ceb24bSAlexander Motin #include <cam/ctl/ctl_error.h>
6067ceb24bSAlexander Motin 
616bff2b5bSAlexander Motin typedef enum {
626bff2b5bSAlexander Motin 	CTL_IOCTL_INPROG,
636bff2b5bSAlexander Motin 	CTL_IOCTL_DATAMOVE,
646bff2b5bSAlexander Motin 	CTL_IOCTL_DONE
656bff2b5bSAlexander Motin } ctl_fe_ioctl_state;
666bff2b5bSAlexander Motin 
676bff2b5bSAlexander Motin struct ctl_fe_ioctl_params {
686bff2b5bSAlexander Motin 	struct cv		sem;
696bff2b5bSAlexander Motin 	struct mtx		ioctl_mtx;
706bff2b5bSAlexander Motin 	ctl_fe_ioctl_state	state;
716bff2b5bSAlexander Motin };
726bff2b5bSAlexander Motin 
738951f055SMarcelo Araujo struct cfi_port {
748951f055SMarcelo Araujo 	TAILQ_ENTRY(cfi_port)	link;
750acc026dSAlexander Motin 	u_int			cur_tag_num;
768951f055SMarcelo Araujo 	struct cdev *		dev;
7767ceb24bSAlexander Motin 	struct ctl_port		port;
7867ceb24bSAlexander Motin };
7967ceb24bSAlexander Motin 
808951f055SMarcelo Araujo struct cfi_softc {
818951f055SMarcelo Araujo 	TAILQ_HEAD(, cfi_port)	ports;
828951f055SMarcelo Araujo };
838951f055SMarcelo Araujo 
8467ceb24bSAlexander Motin static struct cfi_softc cfi_softc;
8567ceb24bSAlexander Motin 
8667ceb24bSAlexander Motin static int cfi_init(void);
870c629e28SAlexander Motin static int cfi_shutdown(void);
8867ceb24bSAlexander Motin static void cfi_datamove(union ctl_io *io);
8967ceb24bSAlexander Motin static void cfi_done(union ctl_io *io);
908951f055SMarcelo Araujo static int cfi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
918951f055SMarcelo Araujo     struct thread *td);
928951f055SMarcelo Araujo static void cfi_ioctl_port_create(struct ctl_req *req);
938951f055SMarcelo Araujo static void cfi_ioctl_port_remove(struct ctl_req *req);
948951f055SMarcelo Araujo 
958951f055SMarcelo Araujo static struct cdevsw cfi_cdevsw = {
968951f055SMarcelo Araujo 	.d_version = D_VERSION,
978951f055SMarcelo Araujo 	.d_flags = 0,
988951f055SMarcelo Araujo 	.d_ioctl = ctl_ioctl_io
998951f055SMarcelo Araujo };
10067ceb24bSAlexander Motin 
10167ceb24bSAlexander Motin static struct ctl_frontend cfi_frontend =
10267ceb24bSAlexander Motin {
10367ceb24bSAlexander Motin 	.name = "ioctl",
10467ceb24bSAlexander Motin 	.init = cfi_init,
1058951f055SMarcelo Araujo 	.ioctl = cfi_ioctl,
10667ceb24bSAlexander Motin 	.shutdown = cfi_shutdown,
10767ceb24bSAlexander Motin };
10867ceb24bSAlexander Motin CTL_FRONTEND_DECLARE(ctlioctl, cfi_frontend);
10967ceb24bSAlexander Motin 
11067ceb24bSAlexander Motin static int
cfi_init(void)11167ceb24bSAlexander Motin cfi_init(void)
11267ceb24bSAlexander Motin {
11367ceb24bSAlexander Motin 	struct cfi_softc *isoftc = &cfi_softc;
1148951f055SMarcelo Araujo 	struct cfi_port *cfi;
11567ceb24bSAlexander Motin 	struct ctl_port *port;
1160c629e28SAlexander Motin 	int error = 0;
11767ceb24bSAlexander Motin 
11867ceb24bSAlexander Motin 	memset(isoftc, 0, sizeof(*isoftc));
1198951f055SMarcelo Araujo 	TAILQ_INIT(&isoftc->ports);
12067ceb24bSAlexander Motin 
1218951f055SMarcelo Araujo 	cfi = malloc(sizeof(*cfi), M_CTL, M_WAITOK | M_ZERO);
1228951f055SMarcelo Araujo 	port = &cfi->port;
12367ceb24bSAlexander Motin 	port->frontend = &cfi_frontend;
12467ceb24bSAlexander Motin 	port->port_type = CTL_PORT_IOCTL;
12567ceb24bSAlexander Motin 	port->num_requested_ctl_io = 100;
12667ceb24bSAlexander Motin 	port->port_name = "ioctl";
12767ceb24bSAlexander Motin 	port->fe_datamove = cfi_datamove;
12867ceb24bSAlexander Motin 	port->fe_done = cfi_done;
1298951f055SMarcelo Araujo 	port->physical_port = 0;
1307ac58230SAlexander Motin 	port->targ_port = -1;
13167ceb24bSAlexander Motin 
1320c629e28SAlexander Motin 	if ((error = ctl_port_register(port)) != 0) {
13367ceb24bSAlexander Motin 		printf("%s: ioctl port registration failed\n", __func__);
1340c629e28SAlexander Motin 		return (error);
13567ceb24bSAlexander Motin 	}
1368951f055SMarcelo Araujo 
13767ceb24bSAlexander Motin 	ctl_port_online(port);
1388951f055SMarcelo Araujo 	TAILQ_INSERT_TAIL(&isoftc->ports, cfi, link);
13967ceb24bSAlexander Motin 	return (0);
14067ceb24bSAlexander Motin }
14167ceb24bSAlexander Motin 
1420c629e28SAlexander Motin static int
cfi_shutdown(void)14367ceb24bSAlexander Motin cfi_shutdown(void)
14467ceb24bSAlexander Motin {
14567ceb24bSAlexander Motin 	struct cfi_softc *isoftc = &cfi_softc;
1468951f055SMarcelo Araujo 	struct cfi_port *cfi, *temp;
1478951f055SMarcelo Araujo 	struct ctl_port *port;
1488951f055SMarcelo Araujo 	int error;
14967ceb24bSAlexander Motin 
1508951f055SMarcelo Araujo 	TAILQ_FOREACH_SAFE(cfi, &isoftc->ports, link, temp) {
1518951f055SMarcelo Araujo 		port = &cfi->port;
15267ceb24bSAlexander Motin 		ctl_port_offline(port);
1538951f055SMarcelo Araujo 		error = ctl_port_deregister(port);
1548951f055SMarcelo Araujo 		if (error != 0) {
1558951f055SMarcelo Araujo 			printf("%s: ctl_frontend_deregister() failed\n",
1568951f055SMarcelo Araujo 			   __func__);
1570c629e28SAlexander Motin 			return (error);
15867ceb24bSAlexander Motin 		}
15967ceb24bSAlexander Motin 
1608951f055SMarcelo Araujo 		TAILQ_REMOVE(&isoftc->ports, cfi, link);
1618951f055SMarcelo Araujo 		free(cfi, M_CTL);
1628951f055SMarcelo Araujo 	}
1638951f055SMarcelo Araujo 
1648951f055SMarcelo Araujo 	return (0);
1658951f055SMarcelo Araujo }
1668951f055SMarcelo Araujo 
1678951f055SMarcelo Araujo static void
cfi_ioctl_port_create(struct ctl_req * req)1688951f055SMarcelo Araujo cfi_ioctl_port_create(struct ctl_req *req)
1698951f055SMarcelo Araujo {
1708951f055SMarcelo Araujo 	struct cfi_softc *isoftc = &cfi_softc;
1718951f055SMarcelo Araujo 	struct cfi_port *cfi;
1728951f055SMarcelo Araujo 	struct ctl_port *port;
1738951f055SMarcelo Araujo 	struct make_dev_args args;
1748951f055SMarcelo Araujo 	const char *val;
1758951f055SMarcelo Araujo 	int retval;
1768951f055SMarcelo Araujo 	int pp = -1, vp = 0;
1778951f055SMarcelo Araujo 
1788951f055SMarcelo Araujo 	val = dnvlist_get_string(req->args_nvl, "pp", NULL);
1798951f055SMarcelo Araujo 	if (val != NULL)
1808951f055SMarcelo Araujo 		pp = strtol(val, NULL, 10);
1818951f055SMarcelo Araujo 
1828951f055SMarcelo Araujo 	val = dnvlist_get_string(req->args_nvl, "vp", NULL);
1838951f055SMarcelo Araujo 	if (val != NULL)
1848951f055SMarcelo Araujo 		vp = strtol(val, NULL, 10);
1858951f055SMarcelo Araujo 
1868951f055SMarcelo Araujo 	if (pp != -1) {
1878951f055SMarcelo Araujo 		/* Check for duplicates */
1888951f055SMarcelo Araujo 		TAILQ_FOREACH(cfi, &isoftc->ports, link) {
1898951f055SMarcelo Araujo 			if (pp == cfi->port.physical_port &&
1908951f055SMarcelo Araujo 			    vp == cfi->port.virtual_port) {
1918951f055SMarcelo Araujo 				req->status = CTL_LUN_ERROR;
1928951f055SMarcelo Araujo 				snprintf(req->error_str, sizeof(req->error_str),
1938951f055SMarcelo Araujo 				    "port %d already exists", pp);
1948951f055SMarcelo Araujo 
1958951f055SMarcelo Araujo 				return;
1968951f055SMarcelo Araujo 			}
1978951f055SMarcelo Araujo 		}
1988951f055SMarcelo Araujo 	} else {
1998951f055SMarcelo Araujo 		/* Find free port number */
2008951f055SMarcelo Araujo 		TAILQ_FOREACH(cfi, &isoftc->ports, link) {
2018951f055SMarcelo Araujo 			pp = MAX(pp, cfi->port.physical_port);
2028951f055SMarcelo Araujo 		}
2038951f055SMarcelo Araujo 
2048951f055SMarcelo Araujo 		pp++;
2058951f055SMarcelo Araujo 	}
2068951f055SMarcelo Araujo 
2078951f055SMarcelo Araujo 	cfi = malloc(sizeof(*cfi), M_CTL, M_WAITOK | M_ZERO);
2088951f055SMarcelo Araujo 	port = &cfi->port;
2098951f055SMarcelo Araujo 	port->frontend = &cfi_frontend;
2108951f055SMarcelo Araujo 	port->port_type = CTL_PORT_IOCTL;
2118951f055SMarcelo Araujo 	port->num_requested_ctl_io = 100;
2128951f055SMarcelo Araujo 	port->port_name = "ioctl";
2138951f055SMarcelo Araujo 	port->fe_datamove = cfi_datamove;
2148951f055SMarcelo Araujo 	port->fe_done = cfi_done;
2158951f055SMarcelo Araujo 	port->physical_port = pp;
2168951f055SMarcelo Araujo 	port->virtual_port = vp;
2178951f055SMarcelo Araujo 	port->targ_port = -1;
2188951f055SMarcelo Araujo 
2198951f055SMarcelo Araujo 	retval = ctl_port_register(port);
2208951f055SMarcelo Araujo 	if (retval != 0) {
2218951f055SMarcelo Araujo 		req->status = CTL_LUN_ERROR;
2228951f055SMarcelo Araujo 		snprintf(req->error_str, sizeof(req->error_str),
2238951f055SMarcelo Araujo 		    "ctl_port_register() failed with error %d", retval);
2247758c80fSAlexander Motin 		free(cfi, M_CTL);
2258951f055SMarcelo Araujo 		return;
2268951f055SMarcelo Araujo 	}
2278951f055SMarcelo Araujo 
2288951f055SMarcelo Araujo 	req->result_nvl = nvlist_create(0);
2298951f055SMarcelo Araujo 	nvlist_add_number(req->result_nvl, "port_id", port->targ_port);
2308951f055SMarcelo Araujo 	ctl_port_online(port);
2318951f055SMarcelo Araujo 
2328951f055SMarcelo Araujo 	make_dev_args_init(&args);
2338951f055SMarcelo Araujo 	args.mda_devsw = &cfi_cdevsw;
2348951f055SMarcelo Araujo 	args.mda_uid = UID_ROOT;
2358951f055SMarcelo Araujo 	args.mda_gid = GID_OPERATOR;
2368951f055SMarcelo Araujo 	args.mda_mode = 0600;
2378951f055SMarcelo Araujo 	args.mda_si_drv1 = NULL;
2388951f055SMarcelo Araujo 	args.mda_si_drv2 = cfi;
2398951f055SMarcelo Araujo 
2408951f055SMarcelo Araujo 	retval = make_dev_s(&args, &cfi->dev, "cam/ctl%d.%d", pp, vp);
2418951f055SMarcelo Araujo 	if (retval != 0) {
2428951f055SMarcelo Araujo 		req->status = CTL_LUN_ERROR;
2438951f055SMarcelo Araujo 		snprintf(req->error_str, sizeof(req->error_str),
2448951f055SMarcelo Araujo 		    "make_dev_s() failed with error %d", retval);
2457758c80fSAlexander Motin 		ctl_port_offline(port);
2467758c80fSAlexander Motin 		ctl_port_deregister(port);
2477758c80fSAlexander Motin 		free(cfi, M_CTL);
2488951f055SMarcelo Araujo 		return;
2498951f055SMarcelo Araujo 	}
2508951f055SMarcelo Araujo 
2518951f055SMarcelo Araujo 	req->status = CTL_LUN_OK;
2528951f055SMarcelo Araujo 	TAILQ_INSERT_TAIL(&isoftc->ports, cfi, link);
2538951f055SMarcelo Araujo }
2548951f055SMarcelo Araujo 
2558951f055SMarcelo Araujo static void
cfi_ioctl_port_remove(struct ctl_req * req)2568951f055SMarcelo Araujo cfi_ioctl_port_remove(struct ctl_req *req)
2578951f055SMarcelo Araujo {
2588951f055SMarcelo Araujo 	struct cfi_softc *isoftc = &cfi_softc;
2598951f055SMarcelo Araujo 	struct cfi_port *cfi = NULL;
2608951f055SMarcelo Araujo 	const char *val;
2618951f055SMarcelo Araujo 	int port_id = -1;
2628951f055SMarcelo Araujo 
2638951f055SMarcelo Araujo 	val = dnvlist_get_string(req->args_nvl, "port_id", NULL);
2648951f055SMarcelo Araujo 	if (val != NULL)
2658951f055SMarcelo Araujo 		port_id = strtol(val, NULL, 10);
2668951f055SMarcelo Araujo 
2678951f055SMarcelo Araujo 	if (port_id == -1) {
2688951f055SMarcelo Araujo 		req->status = CTL_LUN_ERROR;
2698951f055SMarcelo Araujo 		snprintf(req->error_str, sizeof(req->error_str),
270*edbd489dSAlan Somers 		    "Missing required argument: port_id");
2718951f055SMarcelo Araujo 		return;
2728951f055SMarcelo Araujo 	}
2738951f055SMarcelo Araujo 
2748951f055SMarcelo Araujo 	TAILQ_FOREACH(cfi, &isoftc->ports, link) {
2758951f055SMarcelo Araujo 		if (cfi->port.targ_port == port_id)
2768951f055SMarcelo Araujo 			break;
2778951f055SMarcelo Araujo 	}
2788951f055SMarcelo Araujo 
2798951f055SMarcelo Araujo 	if (cfi == NULL) {
2808951f055SMarcelo Araujo 		req->status = CTL_LUN_ERROR;
2818951f055SMarcelo Araujo 		snprintf(req->error_str, sizeof(req->error_str),
2828951f055SMarcelo Araujo 		    "cannot find port %d", port_id);
2838951f055SMarcelo Araujo 
2848951f055SMarcelo Araujo 		return;
2858951f055SMarcelo Araujo 	}
2868951f055SMarcelo Araujo 
2878951f055SMarcelo Araujo 	if (cfi->port.physical_port == 0 && cfi->port.virtual_port == 0) {
2888951f055SMarcelo Araujo 		req->status = CTL_LUN_ERROR;
2898951f055SMarcelo Araujo 		snprintf(req->error_str, sizeof(req->error_str),
2908951f055SMarcelo Araujo 		    "cannot destroy default ioctl port");
2918951f055SMarcelo Araujo 
2928951f055SMarcelo Araujo 		return;
2938951f055SMarcelo Araujo 	}
2948951f055SMarcelo Araujo 
2958951f055SMarcelo Araujo 	ctl_port_offline(&cfi->port);
2968951f055SMarcelo Araujo 	ctl_port_deregister(&cfi->port);
2978951f055SMarcelo Araujo 	TAILQ_REMOVE(&isoftc->ports, cfi, link);
2988951f055SMarcelo Araujo 	destroy_dev(cfi->dev);
2998951f055SMarcelo Araujo 	free(cfi, M_CTL);
3008951f055SMarcelo Araujo 	req->status = CTL_LUN_OK;
3018951f055SMarcelo Araujo }
3028951f055SMarcelo Araujo 
3038951f055SMarcelo Araujo static int
cfi_ioctl(struct cdev * dev,u_long cmd,caddr_t addr,int flag,struct thread * td)3048951f055SMarcelo Araujo cfi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
3058951f055SMarcelo Araujo     struct thread *td)
3068951f055SMarcelo Araujo {
3078951f055SMarcelo Araujo 	struct ctl_req *req;
3088951f055SMarcelo Araujo 
3098951f055SMarcelo Araujo 	if (cmd == CTL_PORT_REQ) {
3108951f055SMarcelo Araujo 		req = (struct ctl_req *)addr;
3118951f055SMarcelo Araujo 		switch (req->reqtype) {
3128951f055SMarcelo Araujo 		case CTL_REQ_CREATE:
3138951f055SMarcelo Araujo 			cfi_ioctl_port_create(req);
3148951f055SMarcelo Araujo 			break;
3158951f055SMarcelo Araujo 		case CTL_REQ_REMOVE:
3168951f055SMarcelo Araujo 			cfi_ioctl_port_remove(req);
3178951f055SMarcelo Araujo 			break;
3188951f055SMarcelo Araujo 		default:
3198951f055SMarcelo Araujo 			req->status = CTL_LUN_ERROR;
3208951f055SMarcelo Araujo 			snprintf(req->error_str, sizeof(req->error_str),
3218951f055SMarcelo Araujo 			    "Unsupported request type %d", req->reqtype);
3228951f055SMarcelo Araujo 		}
3238951f055SMarcelo Araujo 		return (0);
3248951f055SMarcelo Araujo 	}
3258951f055SMarcelo Araujo 
3268951f055SMarcelo Araujo 	return (ENOTTY);
3278951f055SMarcelo Araujo }
3288951f055SMarcelo Araujo 
32967ceb24bSAlexander Motin /*
33067ceb24bSAlexander Motin  * Data movement routine for the CTL ioctl frontend port.
33167ceb24bSAlexander Motin  */
33267ceb24bSAlexander Motin static int
ctl_ioctl_do_datamove(struct ctl_scsiio * ctsio)33367ceb24bSAlexander Motin ctl_ioctl_do_datamove(struct ctl_scsiio *ctsio)
33467ceb24bSAlexander Motin {
33567ceb24bSAlexander Motin 	struct ctl_sg_entry *ext_sglist, *kern_sglist;
33667ceb24bSAlexander Motin 	struct ctl_sg_entry ext_entry, kern_entry;
33767ceb24bSAlexander Motin 	int ext_sglen, ext_sg_entries, kern_sg_entries;
33867ceb24bSAlexander Motin 	int ext_sg_start, ext_offset;
339eb6ac6f9SAlexander Motin 	int len_to_copy;
34067ceb24bSAlexander Motin 	int kern_watermark, ext_watermark;
34167ceb24bSAlexander Motin 	int ext_sglist_malloced;
34267ceb24bSAlexander Motin 	int i, j;
34367ceb24bSAlexander Motin 
34467ceb24bSAlexander Motin 	CTL_DEBUG_PRINT(("ctl_ioctl_do_datamove\n"));
34567ceb24bSAlexander Motin 
34667ceb24bSAlexander Motin 	/*
34767ceb24bSAlexander Motin 	 * If this flag is set, fake the data transfer.
34867ceb24bSAlexander Motin 	 */
34967ceb24bSAlexander Motin 	if (ctsio->io_hdr.flags & CTL_FLAG_NO_DATAMOVE) {
350953f538eSAlexander Motin 		ext_sglist_malloced = 0;
351eb6ac6f9SAlexander Motin 		ctsio->ext_data_filled += ctsio->kern_data_len;
352eb6ac6f9SAlexander Motin 		ctsio->kern_data_resid = 0;
35367ceb24bSAlexander Motin 		goto bailout;
35467ceb24bSAlexander Motin 	}
35567ceb24bSAlexander Motin 
35667ceb24bSAlexander Motin 	/*
35767ceb24bSAlexander Motin 	 * To simplify things here, if we have a single buffer, stick it in
35867ceb24bSAlexander Motin 	 * a S/G entry and just make it a single entry S/G list.
35967ceb24bSAlexander Motin 	 */
360b2221369SAlexander Motin 	if (ctsio->ext_sg_entries > 0) {
36167ceb24bSAlexander Motin 		int len_seen;
36267ceb24bSAlexander Motin 
36367ceb24bSAlexander Motin 		ext_sglen = ctsio->ext_sg_entries * sizeof(*ext_sglist);
36467ceb24bSAlexander Motin 		ext_sglist = (struct ctl_sg_entry *)malloc(ext_sglen, M_CTL,
36567ceb24bSAlexander Motin 							   M_WAITOK);
36667ceb24bSAlexander Motin 		ext_sglist_malloced = 1;
3675c5fb901SAlexander Motin 		if (copyin(ctsio->ext_data_ptr, ext_sglist, ext_sglen) != 0) {
3685c5fb901SAlexander Motin 			ctsio->io_hdr.port_status = 31343;
36967ceb24bSAlexander Motin 			goto bailout;
37067ceb24bSAlexander Motin 		}
37167ceb24bSAlexander Motin 		ext_sg_entries = ctsio->ext_sg_entries;
372953f538eSAlexander Motin 		ext_sg_start = ext_sg_entries;
373953f538eSAlexander Motin 		ext_offset = 0;
37467ceb24bSAlexander Motin 		len_seen = 0;
37567ceb24bSAlexander Motin 		for (i = 0; i < ext_sg_entries; i++) {
37667ceb24bSAlexander Motin 			if ((len_seen + ext_sglist[i].len) >=
37767ceb24bSAlexander Motin 			     ctsio->ext_data_filled) {
37867ceb24bSAlexander Motin 				ext_sg_start = i;
37967ceb24bSAlexander Motin 				ext_offset = ctsio->ext_data_filled - len_seen;
38067ceb24bSAlexander Motin 				break;
38167ceb24bSAlexander Motin 			}
38267ceb24bSAlexander Motin 			len_seen += ext_sglist[i].len;
38367ceb24bSAlexander Motin 		}
38467ceb24bSAlexander Motin 	} else {
38567ceb24bSAlexander Motin 		ext_sglist = &ext_entry;
386953f538eSAlexander Motin 		ext_sglist_malloced = 0;
38767ceb24bSAlexander Motin 		ext_sglist->addr = ctsio->ext_data_ptr;
38867ceb24bSAlexander Motin 		ext_sglist->len = ctsio->ext_data_len;
38967ceb24bSAlexander Motin 		ext_sg_entries = 1;
39067ceb24bSAlexander Motin 		ext_sg_start = 0;
39167ceb24bSAlexander Motin 		ext_offset = ctsio->ext_data_filled;
39267ceb24bSAlexander Motin 	}
39367ceb24bSAlexander Motin 
39467ceb24bSAlexander Motin 	if (ctsio->kern_sg_entries > 0) {
39567ceb24bSAlexander Motin 		kern_sglist = (struct ctl_sg_entry *)ctsio->kern_data_ptr;
39667ceb24bSAlexander Motin 		kern_sg_entries = ctsio->kern_sg_entries;
39767ceb24bSAlexander Motin 	} else {
39867ceb24bSAlexander Motin 		kern_sglist = &kern_entry;
39967ceb24bSAlexander Motin 		kern_sglist->addr = ctsio->kern_data_ptr;
40067ceb24bSAlexander Motin 		kern_sglist->len = ctsio->kern_data_len;
40167ceb24bSAlexander Motin 		kern_sg_entries = 1;
40267ceb24bSAlexander Motin 	}
40367ceb24bSAlexander Motin 
40467ceb24bSAlexander Motin 	kern_watermark = 0;
40567ceb24bSAlexander Motin 	ext_watermark = ext_offset;
40667ceb24bSAlexander Motin 	for (i = ext_sg_start, j = 0;
40767ceb24bSAlexander Motin 	     i < ext_sg_entries && j < kern_sg_entries;) {
40867ceb24bSAlexander Motin 		uint8_t *ext_ptr, *kern_ptr;
40967ceb24bSAlexander Motin 
41067ceb24bSAlexander Motin 		len_to_copy = MIN(ext_sglist[i].len - ext_watermark,
41167ceb24bSAlexander Motin 				  kern_sglist[j].len - kern_watermark);
41267ceb24bSAlexander Motin 
41367ceb24bSAlexander Motin 		ext_ptr = (uint8_t *)ext_sglist[i].addr;
41467ceb24bSAlexander Motin 		ext_ptr = ext_ptr + ext_watermark;
41567ceb24bSAlexander Motin 		if (ctsio->io_hdr.flags & CTL_FLAG_BUS_ADDR) {
41667ceb24bSAlexander Motin 			/*
41767ceb24bSAlexander Motin 			 * XXX KDM fix this!
41867ceb24bSAlexander Motin 			 */
41967ceb24bSAlexander Motin 			panic("need to implement bus address support");
42067ceb24bSAlexander Motin #if 0
42167ceb24bSAlexander Motin 			kern_ptr = bus_to_virt(kern_sglist[j].addr);
42267ceb24bSAlexander Motin #endif
42367ceb24bSAlexander Motin 		} else
42467ceb24bSAlexander Motin 			kern_ptr = (uint8_t *)kern_sglist[j].addr;
42567ceb24bSAlexander Motin 		kern_ptr = kern_ptr + kern_watermark;
42667ceb24bSAlexander Motin 
42767ceb24bSAlexander Motin 		if ((ctsio->io_hdr.flags & CTL_FLAG_DATA_MASK) ==
42867ceb24bSAlexander Motin 		     CTL_FLAG_DATA_IN) {
42967ceb24bSAlexander Motin 			CTL_DEBUG_PRINT(("ctl_ioctl_do_datamove: copying %d "
43067ceb24bSAlexander Motin 					 "bytes to user\n", len_to_copy));
43167ceb24bSAlexander Motin 			CTL_DEBUG_PRINT(("ctl_ioctl_do_datamove: from %p "
43267ceb24bSAlexander Motin 					 "to %p\n", kern_ptr, ext_ptr));
43367ceb24bSAlexander Motin 			if (copyout(kern_ptr, ext_ptr, len_to_copy) != 0) {
4345c5fb901SAlexander Motin 				ctsio->io_hdr.port_status = 31344;
43567ceb24bSAlexander Motin 				goto bailout;
43667ceb24bSAlexander Motin 			}
43767ceb24bSAlexander Motin 		} else {
43867ceb24bSAlexander Motin 			CTL_DEBUG_PRINT(("ctl_ioctl_do_datamove: copying %d "
43967ceb24bSAlexander Motin 					 "bytes from user\n", len_to_copy));
44067ceb24bSAlexander Motin 			CTL_DEBUG_PRINT(("ctl_ioctl_do_datamove: from %p "
44167ceb24bSAlexander Motin 					 "to %p\n", ext_ptr, kern_ptr));
44267ceb24bSAlexander Motin 			if (copyin(ext_ptr, kern_ptr, len_to_copy)!= 0){
4435c5fb901SAlexander Motin 				ctsio->io_hdr.port_status = 31345;
44467ceb24bSAlexander Motin 				goto bailout;
44567ceb24bSAlexander Motin 			}
44667ceb24bSAlexander Motin 		}
44767ceb24bSAlexander Motin 
448eb6ac6f9SAlexander Motin 		ctsio->ext_data_filled += len_to_copy;
449eb6ac6f9SAlexander Motin 		ctsio->kern_data_resid -= len_to_copy;
45067ceb24bSAlexander Motin 
451eb6ac6f9SAlexander Motin 		ext_watermark += len_to_copy;
45267ceb24bSAlexander Motin 		if (ext_sglist[i].len == ext_watermark) {
45367ceb24bSAlexander Motin 			i++;
45467ceb24bSAlexander Motin 			ext_watermark = 0;
45567ceb24bSAlexander Motin 		}
45667ceb24bSAlexander Motin 
457eb6ac6f9SAlexander Motin 		kern_watermark += len_to_copy;
45867ceb24bSAlexander Motin 		if (kern_sglist[j].len == kern_watermark) {
45967ceb24bSAlexander Motin 			j++;
46067ceb24bSAlexander Motin 			kern_watermark = 0;
46167ceb24bSAlexander Motin 		}
46267ceb24bSAlexander Motin 	}
46367ceb24bSAlexander Motin 
46467ceb24bSAlexander Motin 	CTL_DEBUG_PRINT(("ctl_ioctl_do_datamove: ext_sg_entries: %d, "
46567ceb24bSAlexander Motin 			 "kern_sg_entries: %d\n", ext_sg_entries,
46667ceb24bSAlexander Motin 			 kern_sg_entries));
46767ceb24bSAlexander Motin 	CTL_DEBUG_PRINT(("ctl_ioctl_do_datamove: ext_data_len = %d, "
46867ceb24bSAlexander Motin 			 "kern_data_len = %d\n", ctsio->ext_data_len,
46967ceb24bSAlexander Motin 			 ctsio->kern_data_len));
47067ceb24bSAlexander Motin 
47167ceb24bSAlexander Motin bailout:
47267ceb24bSAlexander Motin 	if (ext_sglist_malloced != 0)
47367ceb24bSAlexander Motin 		free(ext_sglist, M_CTL);
47467ceb24bSAlexander Motin 
47567ceb24bSAlexander Motin 	return (CTL_RETVAL_COMPLETE);
47667ceb24bSAlexander Motin }
47767ceb24bSAlexander Motin 
47867ceb24bSAlexander Motin static void
cfi_datamove(union ctl_io * io)47967ceb24bSAlexander Motin cfi_datamove(union ctl_io *io)
48067ceb24bSAlexander Motin {
48167ceb24bSAlexander Motin 	struct ctl_fe_ioctl_params *params;
48267ceb24bSAlexander Motin 
48367ceb24bSAlexander Motin 	params = (struct ctl_fe_ioctl_params *)
48467ceb24bSAlexander Motin 		io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
48567ceb24bSAlexander Motin 
48667ceb24bSAlexander Motin 	mtx_lock(&params->ioctl_mtx);
48767ceb24bSAlexander Motin 	params->state = CTL_IOCTL_DATAMOVE;
48867ceb24bSAlexander Motin 	cv_broadcast(&params->sem);
48967ceb24bSAlexander Motin 	mtx_unlock(&params->ioctl_mtx);
49067ceb24bSAlexander Motin }
49167ceb24bSAlexander Motin 
49267ceb24bSAlexander Motin static void
cfi_done(union ctl_io * io)49367ceb24bSAlexander Motin cfi_done(union ctl_io *io)
49467ceb24bSAlexander Motin {
49567ceb24bSAlexander Motin 	struct ctl_fe_ioctl_params *params;
49667ceb24bSAlexander Motin 
49767ceb24bSAlexander Motin 	params = (struct ctl_fe_ioctl_params *)
49867ceb24bSAlexander Motin 		io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr;
49967ceb24bSAlexander Motin 
50067ceb24bSAlexander Motin 	mtx_lock(&params->ioctl_mtx);
50167ceb24bSAlexander Motin 	params->state = CTL_IOCTL_DONE;
50267ceb24bSAlexander Motin 	cv_broadcast(&params->sem);
50367ceb24bSAlexander Motin 	mtx_unlock(&params->ioctl_mtx);
50467ceb24bSAlexander Motin }
50567ceb24bSAlexander Motin 
50667ceb24bSAlexander Motin static int
cfi_submit_wait(union ctl_io * io)50767ceb24bSAlexander Motin cfi_submit_wait(union ctl_io *io)
50867ceb24bSAlexander Motin {
50967ceb24bSAlexander Motin 	struct ctl_fe_ioctl_params params;
51067ceb24bSAlexander Motin 	ctl_fe_ioctl_state last_state;
51167ceb24bSAlexander Motin 	int done, retval;
51267ceb24bSAlexander Motin 
51367ceb24bSAlexander Motin 	bzero(&params, sizeof(params));
51467ceb24bSAlexander Motin 	mtx_init(&params.ioctl_mtx, "ctliocmtx", NULL, MTX_DEF);
51567ceb24bSAlexander Motin 	cv_init(&params.sem, "ctlioccv");
51667ceb24bSAlexander Motin 	params.state = CTL_IOCTL_INPROG;
51767ceb24bSAlexander Motin 	last_state = params.state;
51867ceb24bSAlexander Motin 
51967ceb24bSAlexander Motin 	io->io_hdr.ctl_private[CTL_PRIV_FRONTEND].ptr = &params;
52067ceb24bSAlexander Motin 
52167ceb24bSAlexander Motin 	CTL_DEBUG_PRINT(("cfi_submit_wait\n"));
52267ceb24bSAlexander Motin 
52367ceb24bSAlexander Motin 	/* This shouldn't happen */
524812c9f48SAlexander Motin 	if ((retval = ctl_run(io)) != CTL_RETVAL_COMPLETE)
52567ceb24bSAlexander Motin 		return (retval);
52667ceb24bSAlexander Motin 
52767ceb24bSAlexander Motin 	done = 0;
52867ceb24bSAlexander Motin 
52967ceb24bSAlexander Motin 	do {
53067ceb24bSAlexander Motin 		mtx_lock(&params.ioctl_mtx);
53167ceb24bSAlexander Motin 		/*
53267ceb24bSAlexander Motin 		 * Check the state here, and don't sleep if the state has
5331ffe5851SPedro F. Giffuni 		 * already changed (i.e. wakeup has already occurred, but we
53467ceb24bSAlexander Motin 		 * weren't waiting yet).
53567ceb24bSAlexander Motin 		 */
53667ceb24bSAlexander Motin 		if (params.state == last_state) {
53767ceb24bSAlexander Motin 			/* XXX KDM cv_wait_sig instead? */
53867ceb24bSAlexander Motin 			cv_wait(&params.sem, &params.ioctl_mtx);
53967ceb24bSAlexander Motin 		}
54067ceb24bSAlexander Motin 		last_state = params.state;
54167ceb24bSAlexander Motin 
54267ceb24bSAlexander Motin 		switch (params.state) {
54367ceb24bSAlexander Motin 		case CTL_IOCTL_INPROG:
54467ceb24bSAlexander Motin 			/* Why did we wake up? */
54567ceb24bSAlexander Motin 			/* XXX KDM error here? */
54667ceb24bSAlexander Motin 			mtx_unlock(&params.ioctl_mtx);
54767ceb24bSAlexander Motin 			break;
54867ceb24bSAlexander Motin 		case CTL_IOCTL_DATAMOVE:
54967ceb24bSAlexander Motin 			CTL_DEBUG_PRINT(("got CTL_IOCTL_DATAMOVE\n"));
55067ceb24bSAlexander Motin 
55167ceb24bSAlexander Motin 			/*
55267ceb24bSAlexander Motin 			 * change last_state back to INPROG to avoid
55367ceb24bSAlexander Motin 			 * deadlock on subsequent data moves.
55467ceb24bSAlexander Motin 			 */
55567ceb24bSAlexander Motin 			params.state = last_state = CTL_IOCTL_INPROG;
55667ceb24bSAlexander Motin 
55767ceb24bSAlexander Motin 			mtx_unlock(&params.ioctl_mtx);
55867ceb24bSAlexander Motin 			ctl_ioctl_do_datamove(&io->scsiio);
55967ceb24bSAlexander Motin 			/*
56067ceb24bSAlexander Motin 			 * Note that in some cases, most notably writes,
56167ceb24bSAlexander Motin 			 * this will queue the I/O and call us back later.
56267ceb24bSAlexander Motin 			 * In other cases, generally reads, this routine
56367ceb24bSAlexander Motin 			 * will immediately call back and wake us up,
56467ceb24bSAlexander Motin 			 * probably using our own context.
56567ceb24bSAlexander Motin 			 */
5662c7dc6baSAlexander Motin 			ctl_datamove_done(io, false);
56767ceb24bSAlexander Motin 			break;
56867ceb24bSAlexander Motin 		case CTL_IOCTL_DONE:
56967ceb24bSAlexander Motin 			mtx_unlock(&params.ioctl_mtx);
57067ceb24bSAlexander Motin 			CTL_DEBUG_PRINT(("got CTL_IOCTL_DONE\n"));
57167ceb24bSAlexander Motin 			done = 1;
57267ceb24bSAlexander Motin 			break;
57367ceb24bSAlexander Motin 		default:
57467ceb24bSAlexander Motin 			mtx_unlock(&params.ioctl_mtx);
57567ceb24bSAlexander Motin 			/* XXX KDM error here? */
57667ceb24bSAlexander Motin 			break;
57767ceb24bSAlexander Motin 		}
57867ceb24bSAlexander Motin 	} while (done == 0);
57967ceb24bSAlexander Motin 
58067ceb24bSAlexander Motin 	mtx_destroy(&params.ioctl_mtx);
58167ceb24bSAlexander Motin 	cv_destroy(&params.sem);
58267ceb24bSAlexander Motin 
58367ceb24bSAlexander Motin 	return (CTL_RETVAL_COMPLETE);
58467ceb24bSAlexander Motin }
58567ceb24bSAlexander Motin 
58667ceb24bSAlexander Motin int
ctl_ioctl_io(struct cdev * dev,u_long cmd,caddr_t addr,int flag,struct thread * td)58767ceb24bSAlexander Motin ctl_ioctl_io(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
58867ceb24bSAlexander Motin     struct thread *td)
58967ceb24bSAlexander Motin {
5908951f055SMarcelo Araujo 	struct cfi_port *cfi;
59167ceb24bSAlexander Motin 	union ctl_io *io;
592953f538eSAlexander Motin 	void *pool_tmp, *sc_tmp;
59367ceb24bSAlexander Motin 	int retval = 0;
59467ceb24bSAlexander Motin 
5958951f055SMarcelo Araujo 	if (cmd != CTL_IO)
5968951f055SMarcelo Araujo 		return (ENOTTY);
5978951f055SMarcelo Araujo 
5988951f055SMarcelo Araujo 	cfi = dev->si_drv2 == NULL
5998951f055SMarcelo Araujo 	    ? TAILQ_FIRST(&cfi_softc.ports)
6008951f055SMarcelo Araujo 	    : dev->si_drv2;
6018951f055SMarcelo Araujo 
60267ceb24bSAlexander Motin 	/*
60367ceb24bSAlexander Motin 	 * If we haven't been "enabled", don't allow any SCSI I/O
60467ceb24bSAlexander Motin 	 * to this FETD.
60567ceb24bSAlexander Motin 	 */
6068951f055SMarcelo Araujo 	if ((cfi->port.status & CTL_PORT_STATUS_ONLINE) == 0)
60767ceb24bSAlexander Motin 		return (EPERM);
60867ceb24bSAlexander Motin 
6098951f055SMarcelo Araujo 	io = ctl_alloc_io(cfi->port.ctl_pool_ref);
61067ceb24bSAlexander Motin 
61167ceb24bSAlexander Motin 	/*
61267ceb24bSAlexander Motin 	 * Need to save the pool reference so it doesn't get
61367ceb24bSAlexander Motin 	 * spammed by the user's ctl_io.
61467ceb24bSAlexander Motin 	 */
61567ceb24bSAlexander Motin 	pool_tmp = io->io_hdr.pool;
616953f538eSAlexander Motin 	sc_tmp = CTL_SOFTC(io);
61767ceb24bSAlexander Motin 	memcpy(io, (void *)addr, sizeof(*io));
61867ceb24bSAlexander Motin 	io->io_hdr.pool = pool_tmp;
619953f538eSAlexander Motin 	CTL_SOFTC(io) = sc_tmp;
620321f819bSAlexander Motin 	TAILQ_INIT(&io->io_hdr.blocked_queue);
62167ceb24bSAlexander Motin 
62267ceb24bSAlexander Motin 	/*
62367ceb24bSAlexander Motin 	 * No status yet, so make sure the status is set properly.
62467ceb24bSAlexander Motin 	 */
62567ceb24bSAlexander Motin 	io->io_hdr.status = CTL_STATUS_NONE;
62667ceb24bSAlexander Motin 
62767ceb24bSAlexander Motin 	/*
62867ceb24bSAlexander Motin 	 * The user sets the initiator ID, target and LUN IDs.
62967ceb24bSAlexander Motin 	 */
6308951f055SMarcelo Araujo 	io->io_hdr.nexus.targ_port = cfi->port.targ_port;
63167ceb24bSAlexander Motin 	io->io_hdr.flags |= CTL_FLAG_USER_REQ;
6327467a695SAlexander Motin 	if ((io->io_hdr.flags & CTL_FLAG_USER_TAG) == 0 &&
6337467a695SAlexander Motin 	    io->io_hdr.io_type == CTL_IO_SCSI &&
6347467a695SAlexander Motin 	    io->scsiio.tag_type != CTL_TAG_UNTAGGED)
6350acc026dSAlexander Motin 		io->scsiio.tag_num = atomic_fetchadd_int(&cfi->cur_tag_num, 1);
63667ceb24bSAlexander Motin 
63767ceb24bSAlexander Motin 	retval = cfi_submit_wait(io);
63867ceb24bSAlexander Motin 	if (retval == 0)
63967ceb24bSAlexander Motin 		memcpy((void *)addr, io, sizeof(*io));
6408951f055SMarcelo Araujo 
64167ceb24bSAlexander Motin 	ctl_free_io(io);
64267ceb24bSAlexander Motin 	return (retval);
64367ceb24bSAlexander Motin }
644