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(¶ms->ioctl_mtx);
48767ceb24bSAlexander Motin params->state = CTL_IOCTL_DATAMOVE;
48867ceb24bSAlexander Motin cv_broadcast(¶ms->sem);
48967ceb24bSAlexander Motin mtx_unlock(¶ms->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(¶ms->ioctl_mtx);
50167ceb24bSAlexander Motin params->state = CTL_IOCTL_DONE;
50267ceb24bSAlexander Motin cv_broadcast(¶ms->sem);
50367ceb24bSAlexander Motin mtx_unlock(¶ms->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(¶ms, sizeof(params));
51467ceb24bSAlexander Motin mtx_init(¶ms.ioctl_mtx, "ctliocmtx", NULL, MTX_DEF);
51567ceb24bSAlexander Motin cv_init(¶ms.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 = ¶ms;
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(¶ms.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(¶ms.sem, ¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.ioctl_mtx);
57567ceb24bSAlexander Motin /* XXX KDM error here? */
57667ceb24bSAlexander Motin break;
57767ceb24bSAlexander Motin }
57867ceb24bSAlexander Motin } while (done == 0);
57967ceb24bSAlexander Motin
58067ceb24bSAlexander Motin mtx_destroy(¶ms.ioctl_mtx);
58167ceb24bSAlexander Motin cv_destroy(¶ms.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