1f9c005a1SMarcelo Araujo /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3f9c005a1SMarcelo Araujo *
4f9c005a1SMarcelo Araujo * Copyright (c) 2016 Jakub Klama <jceel@FreeBSD.org>.
5f9c005a1SMarcelo Araujo * Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>.
6f9c005a1SMarcelo Araujo * All rights reserved.
7f9c005a1SMarcelo Araujo *
8f9c005a1SMarcelo Araujo * Redistribution and use in source and binary forms, with or without
9f9c005a1SMarcelo Araujo * modification, are permitted provided that the following conditions
10f9c005a1SMarcelo Araujo * are met:
11f9c005a1SMarcelo Araujo * 1. Redistributions of source code must retain the above copyright
12f9c005a1SMarcelo Araujo * notice, this list of conditions and the following disclaimer
13f9c005a1SMarcelo Araujo * in this position and unchanged.
14f9c005a1SMarcelo Araujo * 2. Redistributions in binary form must reproduce the above copyright
15f9c005a1SMarcelo Araujo * notice, this list of conditions and the following disclaimer in the
16f9c005a1SMarcelo Araujo * documentation and/or other materials provided with the distribution.
17f9c005a1SMarcelo Araujo *
18f9c005a1SMarcelo Araujo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19f9c005a1SMarcelo Araujo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20f9c005a1SMarcelo Araujo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21f9c005a1SMarcelo Araujo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22f9c005a1SMarcelo Araujo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23f9c005a1SMarcelo Araujo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24f9c005a1SMarcelo Araujo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25f9c005a1SMarcelo Araujo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26f9c005a1SMarcelo Araujo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27f9c005a1SMarcelo Araujo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28f9c005a1SMarcelo Araujo * SUCH DAMAGE.
29f9c005a1SMarcelo Araujo */
30f9c005a1SMarcelo Araujo
31f9c005a1SMarcelo Araujo #include <sys/param.h>
32f9c005a1SMarcelo Araujo #include <sys/linker_set.h>
33f9c005a1SMarcelo Araujo #include <sys/types.h>
34f9c005a1SMarcelo Araujo #include <sys/uio.h>
35f9c005a1SMarcelo Araujo #include <sys/time.h>
36f9c005a1SMarcelo Araujo #include <sys/queue.h>
37f9c005a1SMarcelo Araujo #include <sys/sbuf.h>
38f9c005a1SMarcelo Araujo
39f9c005a1SMarcelo Araujo #include <errno.h>
40f9c005a1SMarcelo Araujo #include <fcntl.h>
41f9c005a1SMarcelo Araujo #include <stdio.h>
42f9c005a1SMarcelo Araujo #include <stdlib.h>
43f9c005a1SMarcelo Araujo #include <stdbool.h>
44f9c005a1SMarcelo Araujo #include <string.h>
45f9c005a1SMarcelo Araujo #include <unistd.h>
46f9c005a1SMarcelo Araujo #include <assert.h>
47f9c005a1SMarcelo Araujo #include <pthread.h>
48f9c005a1SMarcelo Araujo #include <pthread_np.h>
49f9c005a1SMarcelo Araujo
50f9c005a1SMarcelo Araujo #include <cam/scsi/scsi_all.h>
51f9c005a1SMarcelo Araujo #include <cam/scsi/scsi_message.h>
52f9c005a1SMarcelo Araujo #include <cam/ctl/ctl.h>
53f9c005a1SMarcelo Araujo #include <cam/ctl/ctl_io.h>
54f9c005a1SMarcelo Araujo #include <cam/ctl/ctl_backend.h>
55f9c005a1SMarcelo Araujo #include <cam/ctl/ctl_ioctl.h>
56f9c005a1SMarcelo Araujo #include <cam/ctl/ctl_util.h>
57f9c005a1SMarcelo Araujo #include <cam/ctl/ctl_scsi_all.h>
58f9c005a1SMarcelo Araujo #include <camlib.h>
59f9c005a1SMarcelo Araujo
60f9c005a1SMarcelo Araujo #include "bhyverun.h"
61621b5090SJohn Baldwin #include "config.h"
62332eff95SVincenzo Maffione #include "debug.h"
63f9c005a1SMarcelo Araujo #include "pci_emul.h"
64f9c005a1SMarcelo Araujo #include "virtio.h"
65f9c005a1SMarcelo Araujo #include "iov.h"
66f9c005a1SMarcelo Araujo
67f9c005a1SMarcelo Araujo #define VTSCSI_RINGSZ 64
68f9c005a1SMarcelo Araujo #define VTSCSI_REQUESTQ 1
69f9c005a1SMarcelo Araujo #define VTSCSI_THR_PER_Q 16
70f9c005a1SMarcelo Araujo #define VTSCSI_MAXQ (VTSCSI_REQUESTQ + 2)
71f9c005a1SMarcelo Araujo #define VTSCSI_MAXSEG 64
72f9c005a1SMarcelo Araujo
73f9c005a1SMarcelo Araujo #define VTSCSI_IN_HEADER_LEN(_sc) \
74f9c005a1SMarcelo Araujo (sizeof(struct pci_vtscsi_req_cmd_rd) + _sc->vss_config.cdb_size)
75f9c005a1SMarcelo Araujo
76f9c005a1SMarcelo Araujo #define VTSCSI_OUT_HEADER_LEN(_sc) \
77f9c005a1SMarcelo Araujo (sizeof(struct pci_vtscsi_req_cmd_wr) + _sc->vss_config.sense_size)
78f9c005a1SMarcelo Araujo
79f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_MAX_CHANNEL 0
80f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_MAX_TARGET 0
81f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_MAX_LUN 16383
82f9c005a1SMarcelo Araujo
83f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_F_INOUT (1 << 0)
84f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_F_HOTPLUG (1 << 1)
85f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_F_CHANGE (1 << 2)
86f9c005a1SMarcelo Araujo
87f9c005a1SMarcelo Araujo static int pci_vtscsi_debug = 0;
8862806a7fSJohn Baldwin #define WPRINTF(msg, params...) PRINTLN("virtio-scsi: " msg, ##params)
8962806a7fSJohn Baldwin #define DPRINTF(msg, params...) if (pci_vtscsi_debug) WPRINTF(msg, ##params)
90f9c005a1SMarcelo Araujo
91f9c005a1SMarcelo Araujo struct pci_vtscsi_config {
92f9c005a1SMarcelo Araujo uint32_t num_queues;
93f9c005a1SMarcelo Araujo uint32_t seg_max;
94f9c005a1SMarcelo Araujo uint32_t max_sectors;
95f9c005a1SMarcelo Araujo uint32_t cmd_per_lun;
96f9c005a1SMarcelo Araujo uint32_t event_info_size;
97f9c005a1SMarcelo Araujo uint32_t sense_size;
98f9c005a1SMarcelo Araujo uint32_t cdb_size;
99f9c005a1SMarcelo Araujo uint16_t max_channel;
100f9c005a1SMarcelo Araujo uint16_t max_target;
101f9c005a1SMarcelo Araujo uint32_t max_lun;
102f9c005a1SMarcelo Araujo } __attribute__((packed));
103f9c005a1SMarcelo Araujo
104f9c005a1SMarcelo Araujo struct pci_vtscsi_queue {
105f9c005a1SMarcelo Araujo struct pci_vtscsi_softc * vsq_sc;
106f9c005a1SMarcelo Araujo struct vqueue_info * vsq_vq;
107f9c005a1SMarcelo Araujo pthread_mutex_t vsq_mtx;
108f9c005a1SMarcelo Araujo pthread_mutex_t vsq_qmtx;
109f9c005a1SMarcelo Araujo pthread_cond_t vsq_cv;
110f9c005a1SMarcelo Araujo STAILQ_HEAD(, pci_vtscsi_request) vsq_requests;
111f9c005a1SMarcelo Araujo LIST_HEAD(, pci_vtscsi_worker) vsq_workers;
112f9c005a1SMarcelo Araujo };
113f9c005a1SMarcelo Araujo
114f9c005a1SMarcelo Araujo struct pci_vtscsi_worker {
115f9c005a1SMarcelo Araujo struct pci_vtscsi_queue * vsw_queue;
116f9c005a1SMarcelo Araujo pthread_t vsw_thread;
117f9c005a1SMarcelo Araujo bool vsw_exiting;
118f9c005a1SMarcelo Araujo LIST_ENTRY(pci_vtscsi_worker) vsw_link;
119f9c005a1SMarcelo Araujo };
120f9c005a1SMarcelo Araujo
121f9c005a1SMarcelo Araujo struct pci_vtscsi_request {
122f9c005a1SMarcelo Araujo struct pci_vtscsi_queue * vsr_queue;
123f9c005a1SMarcelo Araujo struct iovec vsr_iov_in[VTSCSI_MAXSEG];
124f9c005a1SMarcelo Araujo int vsr_niov_in;
125f9c005a1SMarcelo Araujo struct iovec vsr_iov_out[VTSCSI_MAXSEG];
126f9c005a1SMarcelo Araujo int vsr_niov_out;
127f9c005a1SMarcelo Araujo uint32_t vsr_idx;
128f9c005a1SMarcelo Araujo STAILQ_ENTRY(pci_vtscsi_request) vsr_link;
129f9c005a1SMarcelo Araujo };
130f9c005a1SMarcelo Araujo
131f9c005a1SMarcelo Araujo /*
132f9c005a1SMarcelo Araujo * Per-device softc
133f9c005a1SMarcelo Araujo */
134f9c005a1SMarcelo Araujo struct pci_vtscsi_softc {
135f9c005a1SMarcelo Araujo struct virtio_softc vss_vs;
136f9c005a1SMarcelo Araujo struct vqueue_info vss_vq[VTSCSI_MAXQ];
137f9c005a1SMarcelo Araujo struct pci_vtscsi_queue vss_queues[VTSCSI_REQUESTQ];
138f9c005a1SMarcelo Araujo pthread_mutex_t vss_mtx;
139f9c005a1SMarcelo Araujo int vss_iid;
140f9c005a1SMarcelo Araujo int vss_ctl_fd;
141f9c005a1SMarcelo Araujo uint32_t vss_features;
142f9c005a1SMarcelo Araujo struct pci_vtscsi_config vss_config;
143f9c005a1SMarcelo Araujo };
144f9c005a1SMarcelo Araujo
145f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF 0
146f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
147f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1
148f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2
149f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3
150f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4
151f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5
152f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_QUERY_TASK 6
153f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7
154f9c005a1SMarcelo Araujo
155f9c005a1SMarcelo Araujo /* command-specific response values */
156f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_FUNCTION_COMPLETE 0
157f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10
158f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_FUNCTION_REJECTED 11
159f9c005a1SMarcelo Araujo
160f9c005a1SMarcelo Araujo struct pci_vtscsi_ctrl_tmf {
161f9c005a1SMarcelo Araujo uint32_t type;
162f9c005a1SMarcelo Araujo uint32_t subtype;
163f9c005a1SMarcelo Araujo uint8_t lun[8];
164f9c005a1SMarcelo Araujo uint64_t id;
165f9c005a1SMarcelo Araujo uint8_t response;
166f9c005a1SMarcelo Araujo } __attribute__((packed));
167f9c005a1SMarcelo Araujo
168f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_T_AN_QUERY 1
169f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2
170f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_EVT_ASYNC_POWER_MGMT 4
171f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_EVT_ASYNC_EXTERNAL_REQUEST 8
172f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_EVT_ASYNC_MEDIA_CHANGE 16
173f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_EVT_ASYNC_MULTI_HOST 32
174f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_EVT_ASYNC_DEVICE_BUSY 64
175f9c005a1SMarcelo Araujo
176f9c005a1SMarcelo Araujo struct pci_vtscsi_ctrl_an {
177f9c005a1SMarcelo Araujo uint32_t type;
178f9c005a1SMarcelo Araujo uint8_t lun[8];
179f9c005a1SMarcelo Araujo uint32_t event_requested;
180f9c005a1SMarcelo Araujo uint32_t event_actual;
181f9c005a1SMarcelo Araujo uint8_t response;
182f9c005a1SMarcelo Araujo } __attribute__((packed));
183f9c005a1SMarcelo Araujo
184f9c005a1SMarcelo Araujo /* command-specific response values */
185f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_OK 0
186f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_OVERRUN 1
187f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_ABORTED 2
188f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_BAD_TARGET 3
189f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_RESET 4
190f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_BUSY 5
191f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
192f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_TARGET_FAILURE 7
193f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_NEXUS_FAILURE 8
194f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_FAILURE 9
195f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_INCORRECT_LUN 12
196f9c005a1SMarcelo Araujo
197f9c005a1SMarcelo Araujo /* task_attr */
198f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_SIMPLE 0
199f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_ORDERED 1
200f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_HEAD 2
201f9c005a1SMarcelo Araujo #define VIRTIO_SCSI_S_ACA 3
202f9c005a1SMarcelo Araujo
203f9c005a1SMarcelo Araujo struct pci_vtscsi_event {
204f9c005a1SMarcelo Araujo uint32_t event;
205f9c005a1SMarcelo Araujo uint8_t lun[8];
206f9c005a1SMarcelo Araujo uint32_t reason;
207f9c005a1SMarcelo Araujo } __attribute__((packed));
208f9c005a1SMarcelo Araujo
209f9c005a1SMarcelo Araujo struct pci_vtscsi_req_cmd_rd {
210f9c005a1SMarcelo Araujo uint8_t lun[8];
211f9c005a1SMarcelo Araujo uint64_t id;
212f9c005a1SMarcelo Araujo uint8_t task_attr;
213f9c005a1SMarcelo Araujo uint8_t prio;
214f9c005a1SMarcelo Araujo uint8_t crn;
215f9c005a1SMarcelo Araujo uint8_t cdb[];
216f9c005a1SMarcelo Araujo } __attribute__((packed));
217f9c005a1SMarcelo Araujo
218f9c005a1SMarcelo Araujo struct pci_vtscsi_req_cmd_wr {
219f9c005a1SMarcelo Araujo uint32_t sense_len;
220f9c005a1SMarcelo Araujo uint32_t residual;
221f9c005a1SMarcelo Araujo uint16_t status_qualifier;
222f9c005a1SMarcelo Araujo uint8_t status;
223f9c005a1SMarcelo Araujo uint8_t response;
224f9c005a1SMarcelo Araujo uint8_t sense[];
225f9c005a1SMarcelo Araujo } __attribute__((packed));
226f9c005a1SMarcelo Araujo
227f9c005a1SMarcelo Araujo static void *pci_vtscsi_proc(void *);
228f9c005a1SMarcelo Araujo static void pci_vtscsi_reset(void *);
229f9c005a1SMarcelo Araujo static void pci_vtscsi_neg_features(void *, uint64_t);
230f9c005a1SMarcelo Araujo static int pci_vtscsi_cfgread(void *, int, int, uint32_t *);
231f9c005a1SMarcelo Araujo static int pci_vtscsi_cfgwrite(void *, int, int, uint32_t);
232f9c005a1SMarcelo Araujo static inline int pci_vtscsi_get_lun(uint8_t *);
233f9c005a1SMarcelo Araujo static int pci_vtscsi_control_handle(struct pci_vtscsi_softc *, void *, size_t);
234f9c005a1SMarcelo Araujo static int pci_vtscsi_tmf_handle(struct pci_vtscsi_softc *,
235f9c005a1SMarcelo Araujo struct pci_vtscsi_ctrl_tmf *);
236f9c005a1SMarcelo Araujo static int pci_vtscsi_an_handle(struct pci_vtscsi_softc *,
237f9c005a1SMarcelo Araujo struct pci_vtscsi_ctrl_an *);
238f9c005a1SMarcelo Araujo static int pci_vtscsi_request_handle(struct pci_vtscsi_queue *, struct iovec *,
239f9c005a1SMarcelo Araujo int, struct iovec *, int);
240f9c005a1SMarcelo Araujo static void pci_vtscsi_controlq_notify(void *, struct vqueue_info *);
241f9c005a1SMarcelo Araujo static void pci_vtscsi_eventq_notify(void *, struct vqueue_info *);
242f9c005a1SMarcelo Araujo static void pci_vtscsi_requestq_notify(void *, struct vqueue_info *);
243f9c005a1SMarcelo Araujo static int pci_vtscsi_init_queue(struct pci_vtscsi_softc *,
244f9c005a1SMarcelo Araujo struct pci_vtscsi_queue *, int);
2456a284cacSJohn Baldwin static int pci_vtscsi_init(struct pci_devinst *, nvlist_t *);
246f9c005a1SMarcelo Araujo
247f9c005a1SMarcelo Araujo static struct virtio_consts vtscsi_vi_consts = {
2486cb26162SMark Johnston .vc_name = "vtscsi",
2496cb26162SMark Johnston .vc_nvq = VTSCSI_MAXQ,
2506cb26162SMark Johnston .vc_cfgsize = sizeof(struct pci_vtscsi_config),
2516cb26162SMark Johnston .vc_reset = pci_vtscsi_reset,
2526cb26162SMark Johnston .vc_cfgread = pci_vtscsi_cfgread,
2536cb26162SMark Johnston .vc_cfgwrite = pci_vtscsi_cfgwrite,
2546cb26162SMark Johnston .vc_apply_features = pci_vtscsi_neg_features,
2556cb26162SMark Johnston .vc_hv_caps = 0,
256f9c005a1SMarcelo Araujo };
257f9c005a1SMarcelo Araujo
258f9c005a1SMarcelo Araujo static void *
pci_vtscsi_proc(void * arg)259f9c005a1SMarcelo Araujo pci_vtscsi_proc(void *arg)
260f9c005a1SMarcelo Araujo {
261f9c005a1SMarcelo Araujo struct pci_vtscsi_worker *worker = (struct pci_vtscsi_worker *)arg;
262f9c005a1SMarcelo Araujo struct pci_vtscsi_queue *q = worker->vsw_queue;
263f9c005a1SMarcelo Araujo struct pci_vtscsi_request *req;
264f9c005a1SMarcelo Araujo int iolen;
265f9c005a1SMarcelo Araujo
266f9c005a1SMarcelo Araujo for (;;) {
267f9c005a1SMarcelo Araujo pthread_mutex_lock(&q->vsq_mtx);
268f9c005a1SMarcelo Araujo
269f9c005a1SMarcelo Araujo while (STAILQ_EMPTY(&q->vsq_requests)
270f9c005a1SMarcelo Araujo && !worker->vsw_exiting)
271f9c005a1SMarcelo Araujo pthread_cond_wait(&q->vsq_cv, &q->vsq_mtx);
272f9c005a1SMarcelo Araujo
273f9c005a1SMarcelo Araujo if (worker->vsw_exiting)
274f9c005a1SMarcelo Araujo break;
275f9c005a1SMarcelo Araujo
276f9c005a1SMarcelo Araujo req = STAILQ_FIRST(&q->vsq_requests);
277f9c005a1SMarcelo Araujo STAILQ_REMOVE_HEAD(&q->vsq_requests, vsr_link);
278f9c005a1SMarcelo Araujo
279f9c005a1SMarcelo Araujo pthread_mutex_unlock(&q->vsq_mtx);
280f9c005a1SMarcelo Araujo iolen = pci_vtscsi_request_handle(q, req->vsr_iov_in,
281f9c005a1SMarcelo Araujo req->vsr_niov_in, req->vsr_iov_out, req->vsr_niov_out);
282f9c005a1SMarcelo Araujo
283f9c005a1SMarcelo Araujo pthread_mutex_lock(&q->vsq_qmtx);
284f9c005a1SMarcelo Araujo vq_relchain(q->vsq_vq, req->vsr_idx, iolen);
285f9c005a1SMarcelo Araujo vq_endchains(q->vsq_vq, 0);
286f9c005a1SMarcelo Araujo pthread_mutex_unlock(&q->vsq_qmtx);
287f9c005a1SMarcelo Araujo
28862806a7fSJohn Baldwin DPRINTF("request <idx=%d> completed", req->vsr_idx);
289f9c005a1SMarcelo Araujo free(req);
290f9c005a1SMarcelo Araujo }
291f9c005a1SMarcelo Araujo
292f9c005a1SMarcelo Araujo pthread_mutex_unlock(&q->vsq_mtx);
293f9c005a1SMarcelo Araujo return (NULL);
294f9c005a1SMarcelo Araujo }
295f9c005a1SMarcelo Araujo
296f9c005a1SMarcelo Araujo static void
pci_vtscsi_reset(void * vsc)297f9c005a1SMarcelo Araujo pci_vtscsi_reset(void *vsc)
298f9c005a1SMarcelo Araujo {
299f9c005a1SMarcelo Araujo struct pci_vtscsi_softc *sc;
300f9c005a1SMarcelo Araujo
301f9c005a1SMarcelo Araujo sc = vsc;
302f9c005a1SMarcelo Araujo
30362806a7fSJohn Baldwin DPRINTF("device reset requested");
304f9c005a1SMarcelo Araujo vi_reset_dev(&sc->vss_vs);
305f9c005a1SMarcelo Araujo
306f9c005a1SMarcelo Araujo /* initialize config structure */
307f9c005a1SMarcelo Araujo sc->vss_config = (struct pci_vtscsi_config){
308f9c005a1SMarcelo Araujo .num_queues = VTSCSI_REQUESTQ,
309db2114b4SEric van Gyzen /* Leave room for the request and the response. */
310db2114b4SEric van Gyzen .seg_max = VTSCSI_MAXSEG - 2,
311f9c005a1SMarcelo Araujo .max_sectors = 2,
312f9c005a1SMarcelo Araujo .cmd_per_lun = 1,
313f9c005a1SMarcelo Araujo .event_info_size = sizeof(struct pci_vtscsi_event),
314f9c005a1SMarcelo Araujo .sense_size = 96,
315f9c005a1SMarcelo Araujo .cdb_size = 32,
316f9c005a1SMarcelo Araujo .max_channel = VIRTIO_SCSI_MAX_CHANNEL,
317f9c005a1SMarcelo Araujo .max_target = VIRTIO_SCSI_MAX_TARGET,
318f9c005a1SMarcelo Araujo .max_lun = VIRTIO_SCSI_MAX_LUN
319f9c005a1SMarcelo Araujo };
320f9c005a1SMarcelo Araujo }
321f9c005a1SMarcelo Araujo
322f9c005a1SMarcelo Araujo static void
pci_vtscsi_neg_features(void * vsc,uint64_t negotiated_features)323f9c005a1SMarcelo Araujo pci_vtscsi_neg_features(void *vsc, uint64_t negotiated_features)
324f9c005a1SMarcelo Araujo {
325f9c005a1SMarcelo Araujo struct pci_vtscsi_softc *sc = vsc;
326f9c005a1SMarcelo Araujo
327f9c005a1SMarcelo Araujo sc->vss_features = negotiated_features;
328f9c005a1SMarcelo Araujo }
329f9c005a1SMarcelo Araujo
330f9c005a1SMarcelo Araujo static int
pci_vtscsi_cfgread(void * vsc,int offset,int size,uint32_t * retval)331f9c005a1SMarcelo Araujo pci_vtscsi_cfgread(void *vsc, int offset, int size, uint32_t *retval)
332f9c005a1SMarcelo Araujo {
333f9c005a1SMarcelo Araujo struct pci_vtscsi_softc *sc = vsc;
334f9c005a1SMarcelo Araujo void *ptr;
335f9c005a1SMarcelo Araujo
336f9c005a1SMarcelo Araujo ptr = (uint8_t *)&sc->vss_config + offset;
337f9c005a1SMarcelo Araujo memcpy(retval, ptr, size);
338f9c005a1SMarcelo Araujo return (0);
339f9c005a1SMarcelo Araujo }
340f9c005a1SMarcelo Araujo
341f9c005a1SMarcelo Araujo static int
pci_vtscsi_cfgwrite(void * vsc __unused,int offset __unused,int size __unused,uint32_t val __unused)34298d920d9SMark Johnston pci_vtscsi_cfgwrite(void *vsc __unused, int offset __unused, int size __unused,
34398d920d9SMark Johnston uint32_t val __unused)
344f9c005a1SMarcelo Araujo {
345f9c005a1SMarcelo Araujo return (0);
346f9c005a1SMarcelo Araujo }
347f9c005a1SMarcelo Araujo
348f9c005a1SMarcelo Araujo static inline int
pci_vtscsi_get_lun(uint8_t * lun)349f9c005a1SMarcelo Araujo pci_vtscsi_get_lun(uint8_t *lun)
350f9c005a1SMarcelo Araujo {
351f9c005a1SMarcelo Araujo
352f9c005a1SMarcelo Araujo return (((lun[2] << 8) | lun[3]) & 0x3fff);
353f9c005a1SMarcelo Araujo }
354f9c005a1SMarcelo Araujo
355f9c005a1SMarcelo Araujo static int
pci_vtscsi_control_handle(struct pci_vtscsi_softc * sc,void * buf,size_t bufsize)356f9c005a1SMarcelo Araujo pci_vtscsi_control_handle(struct pci_vtscsi_softc *sc, void *buf,
357f9c005a1SMarcelo Araujo size_t bufsize)
358f9c005a1SMarcelo Araujo {
359f9c005a1SMarcelo Araujo struct pci_vtscsi_ctrl_tmf *tmf;
360f9c005a1SMarcelo Araujo struct pci_vtscsi_ctrl_an *an;
361f9c005a1SMarcelo Araujo uint32_t type;
362f9c005a1SMarcelo Araujo
363bb31aee2SJohn Baldwin if (bufsize < sizeof(uint32_t)) {
364bb31aee2SJohn Baldwin WPRINTF("ignoring truncated control request");
365bb31aee2SJohn Baldwin return (0);
366bb31aee2SJohn Baldwin }
367bb31aee2SJohn Baldwin
368f9c005a1SMarcelo Araujo type = *(uint32_t *)buf;
369f9c005a1SMarcelo Araujo
370f9c005a1SMarcelo Araujo if (type == VIRTIO_SCSI_T_TMF) {
371bb31aee2SJohn Baldwin if (bufsize != sizeof(*tmf)) {
372bb31aee2SJohn Baldwin WPRINTF("ignoring tmf request with size %zu", bufsize);
373bb31aee2SJohn Baldwin return (0);
374bb31aee2SJohn Baldwin }
375f9c005a1SMarcelo Araujo tmf = (struct pci_vtscsi_ctrl_tmf *)buf;
376f9c005a1SMarcelo Araujo return (pci_vtscsi_tmf_handle(sc, tmf));
377f9c005a1SMarcelo Araujo }
378f9c005a1SMarcelo Araujo
379f9c005a1SMarcelo Araujo if (type == VIRTIO_SCSI_T_AN_QUERY) {
380bb31aee2SJohn Baldwin if (bufsize != sizeof(*an)) {
381bb31aee2SJohn Baldwin WPRINTF("ignoring AN request with size %zu", bufsize);
382bb31aee2SJohn Baldwin return (0);
383bb31aee2SJohn Baldwin }
384f9c005a1SMarcelo Araujo an = (struct pci_vtscsi_ctrl_an *)buf;
385f9c005a1SMarcelo Araujo return (pci_vtscsi_an_handle(sc, an));
386f9c005a1SMarcelo Araujo }
387f9c005a1SMarcelo Araujo
388f9c005a1SMarcelo Araujo return (0);
389f9c005a1SMarcelo Araujo }
390f9c005a1SMarcelo Araujo
391f9c005a1SMarcelo Araujo static int
pci_vtscsi_tmf_handle(struct pci_vtscsi_softc * sc,struct pci_vtscsi_ctrl_tmf * tmf)392f9c005a1SMarcelo Araujo pci_vtscsi_tmf_handle(struct pci_vtscsi_softc *sc,
393f9c005a1SMarcelo Araujo struct pci_vtscsi_ctrl_tmf *tmf)
394f9c005a1SMarcelo Araujo {
395f9c005a1SMarcelo Araujo union ctl_io *io;
396f9c005a1SMarcelo Araujo int err;
397f9c005a1SMarcelo Araujo
398f9c005a1SMarcelo Araujo io = ctl_scsi_alloc_io(sc->vss_iid);
399f9c005a1SMarcelo Araujo ctl_scsi_zero_io(io);
400f9c005a1SMarcelo Araujo
401f9c005a1SMarcelo Araujo io->io_hdr.io_type = CTL_IO_TASK;
4029ce46e81SAlexander Motin io->io_hdr.nexus.initid = sc->vss_iid;
403f9c005a1SMarcelo Araujo io->io_hdr.nexus.targ_lun = pci_vtscsi_get_lun(tmf->lun);
404f9c005a1SMarcelo Araujo io->taskio.tag_type = CTL_TAG_SIMPLE;
4050acc026dSAlexander Motin io->taskio.tag_num = tmf->id;
4067467a695SAlexander Motin io->io_hdr.flags |= CTL_FLAG_USER_TAG;
407f9c005a1SMarcelo Araujo
408f9c005a1SMarcelo Araujo switch (tmf->subtype) {
409f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_ABORT_TASK:
410f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_ABORT_TASK;
411f9c005a1SMarcelo Araujo break;
412f9c005a1SMarcelo Araujo
413f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET:
414f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_ABORT_TASK_SET;
415f9c005a1SMarcelo Araujo break;
416f9c005a1SMarcelo Araujo
417f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_CLEAR_ACA:
418f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_CLEAR_ACA;
419f9c005a1SMarcelo Araujo break;
420f9c005a1SMarcelo Araujo
421f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET:
422f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_CLEAR_TASK_SET;
423f9c005a1SMarcelo Araujo break;
424f9c005a1SMarcelo Araujo
425f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
426f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_I_T_NEXUS_RESET;
427f9c005a1SMarcelo Araujo break;
428f9c005a1SMarcelo Araujo
429f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
430f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_LUN_RESET;
431f9c005a1SMarcelo Araujo break;
432f9c005a1SMarcelo Araujo
433f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_QUERY_TASK:
434f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_QUERY_TASK;
435f9c005a1SMarcelo Araujo break;
436f9c005a1SMarcelo Araujo
437f9c005a1SMarcelo Araujo case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET:
438f9c005a1SMarcelo Araujo io->taskio.task_action = CTL_TASK_QUERY_TASK_SET;
439f9c005a1SMarcelo Araujo break;
440f9c005a1SMarcelo Araujo }
441f9c005a1SMarcelo Araujo
442f9c005a1SMarcelo Araujo if (pci_vtscsi_debug) {
443f9c005a1SMarcelo Araujo struct sbuf *sb = sbuf_new_auto();
444f9c005a1SMarcelo Araujo ctl_io_sbuf(io, sb);
445f9c005a1SMarcelo Araujo sbuf_finish(sb);
44662806a7fSJohn Baldwin DPRINTF("%s", sbuf_data(sb));
447f9c005a1SMarcelo Araujo sbuf_delete(sb);
448f9c005a1SMarcelo Araujo }
449f9c005a1SMarcelo Araujo
450f9c005a1SMarcelo Araujo err = ioctl(sc->vss_ctl_fd, CTL_IO, io);
451f9c005a1SMarcelo Araujo if (err != 0)
45262806a7fSJohn Baldwin WPRINTF("CTL_IO: err=%d (%s)", errno, strerror(errno));
453f9c005a1SMarcelo Araujo
454f9c005a1SMarcelo Araujo tmf->response = io->taskio.task_status;
455f9c005a1SMarcelo Araujo ctl_scsi_free_io(io);
456f9c005a1SMarcelo Araujo return (1);
457f9c005a1SMarcelo Araujo }
458f9c005a1SMarcelo Araujo
459f9c005a1SMarcelo Araujo static int
pci_vtscsi_an_handle(struct pci_vtscsi_softc * sc __unused,struct pci_vtscsi_ctrl_an * an __unused)46098d920d9SMark Johnston pci_vtscsi_an_handle(struct pci_vtscsi_softc *sc __unused,
46198d920d9SMark Johnston struct pci_vtscsi_ctrl_an *an __unused)
462f9c005a1SMarcelo Araujo {
463f9c005a1SMarcelo Araujo return (0);
464f9c005a1SMarcelo Araujo }
465f9c005a1SMarcelo Araujo
466f9c005a1SMarcelo Araujo static int
pci_vtscsi_request_handle(struct pci_vtscsi_queue * q,struct iovec * iov_in,int niov_in,struct iovec * iov_out,int niov_out)467f9c005a1SMarcelo Araujo pci_vtscsi_request_handle(struct pci_vtscsi_queue *q, struct iovec *iov_in,
468f9c005a1SMarcelo Araujo int niov_in, struct iovec *iov_out, int niov_out)
469f9c005a1SMarcelo Araujo {
470f9c005a1SMarcelo Araujo struct pci_vtscsi_softc *sc = q->vsq_sc;
471f9c005a1SMarcelo Araujo struct pci_vtscsi_req_cmd_rd *cmd_rd = NULL;
472f9c005a1SMarcelo Araujo struct pci_vtscsi_req_cmd_wr *cmd_wr;
473f9c005a1SMarcelo Araujo struct iovec data_iov_in[VTSCSI_MAXSEG], data_iov_out[VTSCSI_MAXSEG];
474f9c005a1SMarcelo Araujo union ctl_io *io;
47599fa47deSAlexander Motin int data_niov_in, data_niov_out;
476f9c005a1SMarcelo Araujo void *ext_data_ptr = NULL;
477f9c005a1SMarcelo Araujo uint32_t ext_data_len = 0, ext_sg_entries = 0;
478cb84aedaSSean Chittenden int err, nxferred;
479f9c005a1SMarcelo Araujo
480bb31aee2SJohn Baldwin if (count_iov(iov_out, niov_out) < VTSCSI_OUT_HEADER_LEN(sc)) {
481bb31aee2SJohn Baldwin WPRINTF("ignoring request with insufficient output");
482bb31aee2SJohn Baldwin return (0);
483bb31aee2SJohn Baldwin }
484bb31aee2SJohn Baldwin if (count_iov(iov_in, niov_in) < VTSCSI_IN_HEADER_LEN(sc)) {
485bb31aee2SJohn Baldwin WPRINTF("ignoring request with incomplete header");
486bb31aee2SJohn Baldwin return (0);
487bb31aee2SJohn Baldwin }
488bb31aee2SJohn Baldwin
489f9c005a1SMarcelo Araujo seek_iov(iov_in, niov_in, data_iov_in, &data_niov_in,
490f9c005a1SMarcelo Araujo VTSCSI_IN_HEADER_LEN(sc));
491f9c005a1SMarcelo Araujo seek_iov(iov_out, niov_out, data_iov_out, &data_niov_out,
492f9c005a1SMarcelo Araujo VTSCSI_OUT_HEADER_LEN(sc));
493f9c005a1SMarcelo Araujo
49499fa47deSAlexander Motin truncate_iov(iov_in, &niov_in, VTSCSI_IN_HEADER_LEN(sc));
49599fa47deSAlexander Motin truncate_iov(iov_out, &niov_out, VTSCSI_OUT_HEADER_LEN(sc));
496f9c005a1SMarcelo Araujo iov_to_buf(iov_in, niov_in, (void **)&cmd_rd);
497f9c005a1SMarcelo Araujo
498bb31aee2SJohn Baldwin cmd_wr = calloc(1, VTSCSI_OUT_HEADER_LEN(sc));
499f9c005a1SMarcelo Araujo io = ctl_scsi_alloc_io(sc->vss_iid);
500f9c005a1SMarcelo Araujo ctl_scsi_zero_io(io);
501f9c005a1SMarcelo Araujo
5029ce46e81SAlexander Motin io->io_hdr.nexus.initid = sc->vss_iid;
503f9c005a1SMarcelo Araujo io->io_hdr.nexus.targ_lun = pci_vtscsi_get_lun(cmd_rd->lun);
504f9c005a1SMarcelo Araujo
505f9c005a1SMarcelo Araujo io->io_hdr.io_type = CTL_IO_SCSI;
506f9c005a1SMarcelo Araujo
507f9c005a1SMarcelo Araujo if (data_niov_in > 0) {
508f9c005a1SMarcelo Araujo ext_data_ptr = (void *)data_iov_in;
509f9c005a1SMarcelo Araujo ext_sg_entries = data_niov_in;
510f9c005a1SMarcelo Araujo ext_data_len = count_iov(data_iov_in, data_niov_in);
511f9c005a1SMarcelo Araujo io->io_hdr.flags |= CTL_FLAG_DATA_OUT;
512f9c005a1SMarcelo Araujo } else if (data_niov_out > 0) {
513f9c005a1SMarcelo Araujo ext_data_ptr = (void *)data_iov_out;
514f9c005a1SMarcelo Araujo ext_sg_entries = data_niov_out;
515f9c005a1SMarcelo Araujo ext_data_len = count_iov(data_iov_out, data_niov_out);
516f9c005a1SMarcelo Araujo io->io_hdr.flags |= CTL_FLAG_DATA_IN;
517f9c005a1SMarcelo Araujo }
518f9c005a1SMarcelo Araujo
519f9c005a1SMarcelo Araujo io->scsiio.sense_len = sc->vss_config.sense_size;
5200acc026dSAlexander Motin io->scsiio.tag_num = cmd_rd->id;
5217467a695SAlexander Motin io->io_hdr.flags |= CTL_FLAG_USER_TAG;
5226810fd0aSAlexander Motin switch (cmd_rd->task_attr) {
5236810fd0aSAlexander Motin case VIRTIO_SCSI_S_ORDERED:
5246810fd0aSAlexander Motin io->scsiio.tag_type = CTL_TAG_ORDERED;
5256810fd0aSAlexander Motin break;
5266810fd0aSAlexander Motin case VIRTIO_SCSI_S_HEAD:
5276810fd0aSAlexander Motin io->scsiio.tag_type = CTL_TAG_HEAD_OF_QUEUE;
5286810fd0aSAlexander Motin break;
5296810fd0aSAlexander Motin case VIRTIO_SCSI_S_ACA:
5306810fd0aSAlexander Motin io->scsiio.tag_type = CTL_TAG_ACA;
5316810fd0aSAlexander Motin break;
5326810fd0aSAlexander Motin case VIRTIO_SCSI_S_SIMPLE:
5336810fd0aSAlexander Motin default:
534f9c005a1SMarcelo Araujo io->scsiio.tag_type = CTL_TAG_SIMPLE;
5356810fd0aSAlexander Motin break;
5366810fd0aSAlexander Motin }
537f9c005a1SMarcelo Araujo io->scsiio.ext_sg_entries = ext_sg_entries;
538f9c005a1SMarcelo Araujo io->scsiio.ext_data_ptr = ext_data_ptr;
539f9c005a1SMarcelo Araujo io->scsiio.ext_data_len = ext_data_len;
540f9c005a1SMarcelo Araujo io->scsiio.ext_data_filled = 0;
541f9c005a1SMarcelo Araujo io->scsiio.cdb_len = sc->vss_config.cdb_size;
542f9c005a1SMarcelo Araujo memcpy(io->scsiio.cdb, cmd_rd->cdb, sc->vss_config.cdb_size);
543f9c005a1SMarcelo Araujo
544f9c005a1SMarcelo Araujo if (pci_vtscsi_debug) {
545f9c005a1SMarcelo Araujo struct sbuf *sb = sbuf_new_auto();
546f9c005a1SMarcelo Araujo ctl_io_sbuf(io, sb);
547f9c005a1SMarcelo Araujo sbuf_finish(sb);
54862806a7fSJohn Baldwin DPRINTF("%s", sbuf_data(sb));
549f9c005a1SMarcelo Araujo sbuf_delete(sb);
550f9c005a1SMarcelo Araujo }
551f9c005a1SMarcelo Araujo
55249f87822SAlexander Motin err = ioctl(sc->vss_ctl_fd, CTL_IO, io);
553f9c005a1SMarcelo Araujo if (err != 0) {
55462806a7fSJohn Baldwin WPRINTF("CTL_IO: err=%d (%s)", errno, strerror(errno));
555f9c005a1SMarcelo Araujo cmd_wr->response = VIRTIO_SCSI_S_FAILURE;
556f9c005a1SMarcelo Araujo } else {
557f9c005a1SMarcelo Araujo cmd_wr->sense_len = MIN(io->scsiio.sense_len,
558f9c005a1SMarcelo Araujo sc->vss_config.sense_size);
559b81ac5cdSAlexander Motin cmd_wr->residual = ext_data_len - io->scsiio.ext_data_filled;
560f9c005a1SMarcelo Araujo cmd_wr->status = io->scsiio.scsi_status;
561f9c005a1SMarcelo Araujo cmd_wr->response = VIRTIO_SCSI_S_OK;
562f9c005a1SMarcelo Araujo memcpy(&cmd_wr->sense, &io->scsiio.sense_data,
563f9c005a1SMarcelo Araujo cmd_wr->sense_len);
564f9c005a1SMarcelo Araujo }
565f9c005a1SMarcelo Araujo
566f9c005a1SMarcelo Araujo buf_to_iov(cmd_wr, VTSCSI_OUT_HEADER_LEN(sc), iov_out, niov_out, 0);
567cb84aedaSSean Chittenden nxferred = VTSCSI_OUT_HEADER_LEN(sc) + io->scsiio.ext_data_filled;
568f9c005a1SMarcelo Araujo free(cmd_rd);
569f9c005a1SMarcelo Araujo free(cmd_wr);
570f9c005a1SMarcelo Araujo ctl_scsi_free_io(io);
571cb84aedaSSean Chittenden return (nxferred);
572f9c005a1SMarcelo Araujo }
573f9c005a1SMarcelo Araujo
574f9c005a1SMarcelo Araujo static void
pci_vtscsi_controlq_notify(void * vsc,struct vqueue_info * vq)575f9c005a1SMarcelo Araujo pci_vtscsi_controlq_notify(void *vsc, struct vqueue_info *vq)
576f9c005a1SMarcelo Araujo {
577f9c005a1SMarcelo Araujo struct pci_vtscsi_softc *sc;
578f9c005a1SMarcelo Araujo struct iovec iov[VTSCSI_MAXSEG];
579b0139127SKa Ho Ng struct vi_req req;
580f9c005a1SMarcelo Araujo void *buf = NULL;
581f9c005a1SMarcelo Araujo size_t bufsize;
58271fbc6faSMark Johnston int iolen, n;
583f9c005a1SMarcelo Araujo
584f9c005a1SMarcelo Araujo sc = vsc;
585f9c005a1SMarcelo Araujo
586f9c005a1SMarcelo Araujo while (vq_has_descs(vq)) {
587b0139127SKa Ho Ng n = vq_getchain(vq, iov, VTSCSI_MAXSEG, &req);
58871fbc6faSMark Johnston assert(n >= 1 && n <= VTSCSI_MAXSEG);
58971fbc6faSMark Johnston
590f9c005a1SMarcelo Araujo bufsize = iov_to_buf(iov, n, &buf);
591f9c005a1SMarcelo Araujo iolen = pci_vtscsi_control_handle(sc, buf, bufsize);
59263898728SMark Johnston buf_to_iov((uint8_t *)buf + bufsize - iolen, iolen, iov, n,
59399fa47deSAlexander Motin bufsize - iolen);
594f9c005a1SMarcelo Araujo
595f9c005a1SMarcelo Araujo /*
596f9c005a1SMarcelo Araujo * Release this chain and handle more
597f9c005a1SMarcelo Araujo */
598b0139127SKa Ho Ng vq_relchain(vq, req.idx, iolen);
599f9c005a1SMarcelo Araujo }
600f9c005a1SMarcelo Araujo vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
60199fa47deSAlexander Motin free(buf);
602f9c005a1SMarcelo Araujo }
603f9c005a1SMarcelo Araujo
604f9c005a1SMarcelo Araujo static void
pci_vtscsi_eventq_notify(void * vsc __unused,struct vqueue_info * vq)60598d920d9SMark Johnston pci_vtscsi_eventq_notify(void *vsc __unused, struct vqueue_info *vq)
606f9c005a1SMarcelo Araujo {
60717e9052cSVincenzo Maffione vq_kick_disable(vq);
608f9c005a1SMarcelo Araujo }
609f9c005a1SMarcelo Araujo
610f9c005a1SMarcelo Araujo static void
pci_vtscsi_requestq_notify(void * vsc,struct vqueue_info * vq)611f9c005a1SMarcelo Araujo pci_vtscsi_requestq_notify(void *vsc, struct vqueue_info *vq)
612f9c005a1SMarcelo Araujo {
613f9c005a1SMarcelo Araujo struct pci_vtscsi_softc *sc;
614f9c005a1SMarcelo Araujo struct pci_vtscsi_queue *q;
615f9c005a1SMarcelo Araujo struct pci_vtscsi_request *req;
616f9c005a1SMarcelo Araujo struct iovec iov[VTSCSI_MAXSEG];
617b0139127SKa Ho Ng struct vi_req vireq;
61871fbc6faSMark Johnston int n;
619f9c005a1SMarcelo Araujo
620f9c005a1SMarcelo Araujo sc = vsc;
621f9c005a1SMarcelo Araujo q = &sc->vss_queues[vq->vq_num - 2];
622f9c005a1SMarcelo Araujo
623f9c005a1SMarcelo Araujo while (vq_has_descs(vq)) {
624b0139127SKa Ho Ng n = vq_getchain(vq, iov, VTSCSI_MAXSEG, &vireq);
62571fbc6faSMark Johnston assert(n >= 1 && n <= VTSCSI_MAXSEG);
626f9c005a1SMarcelo Araujo
627f9c005a1SMarcelo Araujo req = calloc(1, sizeof(struct pci_vtscsi_request));
628b0139127SKa Ho Ng req->vsr_idx = vireq.idx;
629f9c005a1SMarcelo Araujo req->vsr_queue = q;
630b0139127SKa Ho Ng req->vsr_niov_in = vireq.readable;
631b0139127SKa Ho Ng req->vsr_niov_out = vireq.writable;
632f9c005a1SMarcelo Araujo memcpy(req->vsr_iov_in, iov,
633f9c005a1SMarcelo Araujo req->vsr_niov_in * sizeof(struct iovec));
634b0139127SKa Ho Ng memcpy(req->vsr_iov_out, iov + vireq.readable,
635f9c005a1SMarcelo Araujo req->vsr_niov_out * sizeof(struct iovec));
636f9c005a1SMarcelo Araujo
637f9c005a1SMarcelo Araujo pthread_mutex_lock(&q->vsq_mtx);
638f9c005a1SMarcelo Araujo STAILQ_INSERT_TAIL(&q->vsq_requests, req, vsr_link);
639f9c005a1SMarcelo Araujo pthread_cond_signal(&q->vsq_cv);
640f9c005a1SMarcelo Araujo pthread_mutex_unlock(&q->vsq_mtx);
641f9c005a1SMarcelo Araujo
64262806a7fSJohn Baldwin DPRINTF("request <idx=%d> enqueued", vireq.idx);
643f9c005a1SMarcelo Araujo }
644f9c005a1SMarcelo Araujo }
645f9c005a1SMarcelo Araujo
646f9c005a1SMarcelo Araujo static int
pci_vtscsi_init_queue(struct pci_vtscsi_softc * sc,struct pci_vtscsi_queue * queue,int num)647f9c005a1SMarcelo Araujo pci_vtscsi_init_queue(struct pci_vtscsi_softc *sc,
648f9c005a1SMarcelo Araujo struct pci_vtscsi_queue *queue, int num)
649f9c005a1SMarcelo Araujo {
650f9c005a1SMarcelo Araujo struct pci_vtscsi_worker *worker;
651cfe60d67SAlexander Motin char tname[MAXCOMLEN + 1];
652f9c005a1SMarcelo Araujo int i;
653f9c005a1SMarcelo Araujo
654f9c005a1SMarcelo Araujo queue->vsq_sc = sc;
655f9c005a1SMarcelo Araujo queue->vsq_vq = &sc->vss_vq[num + 2];
656f9c005a1SMarcelo Araujo
657f9c005a1SMarcelo Araujo pthread_mutex_init(&queue->vsq_mtx, NULL);
658f9c005a1SMarcelo Araujo pthread_mutex_init(&queue->vsq_qmtx, NULL);
659f9c005a1SMarcelo Araujo pthread_cond_init(&queue->vsq_cv, NULL);
660f9c005a1SMarcelo Araujo STAILQ_INIT(&queue->vsq_requests);
661f9c005a1SMarcelo Araujo LIST_INIT(&queue->vsq_workers);
662f9c005a1SMarcelo Araujo
663f9c005a1SMarcelo Araujo for (i = 0; i < VTSCSI_THR_PER_Q; i++) {
664f9c005a1SMarcelo Araujo worker = calloc(1, sizeof(struct pci_vtscsi_worker));
665f9c005a1SMarcelo Araujo worker->vsw_queue = queue;
666f9c005a1SMarcelo Araujo
667f9c005a1SMarcelo Araujo pthread_create(&worker->vsw_thread, NULL, &pci_vtscsi_proc,
668f9c005a1SMarcelo Araujo (void *)worker);
669f9c005a1SMarcelo Araujo
670cfe60d67SAlexander Motin snprintf(tname, sizeof(tname), "vtscsi:%d-%d", num, i);
671cfe60d67SAlexander Motin pthread_set_name_np(worker->vsw_thread, tname);
672f9c005a1SMarcelo Araujo LIST_INSERT_HEAD(&queue->vsq_workers, worker, vsw_link);
673f9c005a1SMarcelo Araujo }
674f9c005a1SMarcelo Araujo
675f9c005a1SMarcelo Araujo return (0);
676f9c005a1SMarcelo Araujo }
677f9c005a1SMarcelo Araujo
678f9c005a1SMarcelo Araujo static int
pci_vtscsi_legacy_config(nvlist_t * nvl,const char * opts)679621b5090SJohn Baldwin pci_vtscsi_legacy_config(nvlist_t *nvl, const char *opts)
680621b5090SJohn Baldwin {
681621b5090SJohn Baldwin char *cp, *devname;
682621b5090SJohn Baldwin
68389c3c326SRyan Moeller if (opts == NULL)
68489c3c326SRyan Moeller return (0);
68589c3c326SRyan Moeller
686621b5090SJohn Baldwin cp = strchr(opts, ',');
687621b5090SJohn Baldwin if (cp == NULL) {
688621b5090SJohn Baldwin set_config_value_node(nvl, "dev", opts);
689621b5090SJohn Baldwin return (0);
690621b5090SJohn Baldwin }
691621b5090SJohn Baldwin devname = strndup(opts, cp - opts);
692621b5090SJohn Baldwin set_config_value_node(nvl, "dev", devname);
693621b5090SJohn Baldwin free(devname);
694621b5090SJohn Baldwin return (pci_parse_legacy_config(nvl, cp + 1));
695621b5090SJohn Baldwin }
696621b5090SJohn Baldwin
697621b5090SJohn Baldwin static int
pci_vtscsi_init(struct pci_devinst * pi,nvlist_t * nvl)6986a284cacSJohn Baldwin pci_vtscsi_init(struct pci_devinst *pi, nvlist_t *nvl)
699f9c005a1SMarcelo Araujo {
700f9c005a1SMarcelo Araujo struct pci_vtscsi_softc *sc;
701e76c0e4fSElliott Mitchell const char *devname, *value;
702621b5090SJohn Baldwin int i;
703f9c005a1SMarcelo Araujo
704f9c005a1SMarcelo Araujo sc = calloc(1, sizeof(struct pci_vtscsi_softc));
705621b5090SJohn Baldwin value = get_config_value_node(nvl, "iid");
706621b5090SJohn Baldwin if (value != NULL)
707621b5090SJohn Baldwin sc->vss_iid = strtoul(value, NULL, 10);
708f9c005a1SMarcelo Araujo
709*480bef94SCorvin Köhne value = get_config_value_node(nvl, "bootindex");
710*480bef94SCorvin Köhne if (value != NULL) {
711*480bef94SCorvin Köhne if (pci_emul_add_boot_device(pi, atoi(value))) {
712*480bef94SCorvin Köhne EPRINTLN("Invalid bootindex %d", atoi(value));
713*480bef94SCorvin Köhne free(sc);
714*480bef94SCorvin Köhne return (-1);
715*480bef94SCorvin Köhne }
716*480bef94SCorvin Köhne }
717*480bef94SCorvin Köhne
718621b5090SJohn Baldwin devname = get_config_value_node(nvl, "dev");
719621b5090SJohn Baldwin if (devname == NULL)
720621b5090SJohn Baldwin devname = "/dev/cam/ctl";
72149f87822SAlexander Motin sc->vss_ctl_fd = open(devname, O_RDWR);
72249f87822SAlexander Motin if (sc->vss_ctl_fd < 0) {
72362806a7fSJohn Baldwin WPRINTF("cannot open %s: %s", devname, strerror(errno));
72449f87822SAlexander Motin free(sc);
72549f87822SAlexander Motin return (1);
726f9c005a1SMarcelo Araujo }
727f9c005a1SMarcelo Araujo
728f6f357efSAndy Fiddaman pthread_mutex_init(&sc->vss_mtx, NULL);
729f6f357efSAndy Fiddaman
730f9c005a1SMarcelo Araujo vi_softc_linkup(&sc->vss_vs, &vtscsi_vi_consts, sc, pi, sc->vss_vq);
731f9c005a1SMarcelo Araujo sc->vss_vs.vs_mtx = &sc->vss_mtx;
732f9c005a1SMarcelo Araujo
733f9c005a1SMarcelo Araujo /* controlq */
734f9c005a1SMarcelo Araujo sc->vss_vq[0].vq_qsize = VTSCSI_RINGSZ;
735f9c005a1SMarcelo Araujo sc->vss_vq[0].vq_notify = pci_vtscsi_controlq_notify;
736f9c005a1SMarcelo Araujo
737f9c005a1SMarcelo Araujo /* eventq */
738f9c005a1SMarcelo Araujo sc->vss_vq[1].vq_qsize = VTSCSI_RINGSZ;
739f9c005a1SMarcelo Araujo sc->vss_vq[1].vq_notify = pci_vtscsi_eventq_notify;
740f9c005a1SMarcelo Araujo
741f9c005a1SMarcelo Araujo /* request queues */
742f9c005a1SMarcelo Araujo for (i = 2; i < VTSCSI_MAXQ; i++) {
743f9c005a1SMarcelo Araujo sc->vss_vq[i].vq_qsize = VTSCSI_RINGSZ;
744f9c005a1SMarcelo Araujo sc->vss_vq[i].vq_notify = pci_vtscsi_requestq_notify;
745f9c005a1SMarcelo Araujo pci_vtscsi_init_queue(sc, &sc->vss_queues[i - 2], i - 2);
746f9c005a1SMarcelo Araujo }
747f9c005a1SMarcelo Araujo
748f9c005a1SMarcelo Araujo /* initialize config space */
749f9c005a1SMarcelo Araujo pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_SCSI);
750f9c005a1SMarcelo Araujo pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
751f9c005a1SMarcelo Araujo pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE);
75254ac6f72SKa Ho Ng pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_SCSI);
753f9c005a1SMarcelo Araujo pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
754f9c005a1SMarcelo Araujo
755f9c005a1SMarcelo Araujo if (vi_intr_init(&sc->vss_vs, 1, fbsdrun_virtio_msix()))
756f9c005a1SMarcelo Araujo return (1);
757f9c005a1SMarcelo Araujo vi_set_io_bar(&sc->vss_vs, 0);
758f9c005a1SMarcelo Araujo
759f9c005a1SMarcelo Araujo return (0);
760f9c005a1SMarcelo Araujo }
761f9c005a1SMarcelo Araujo
762f9c005a1SMarcelo Araujo
76337045dfaSMark Johnston static const struct pci_devemu pci_de_vscsi = {
764f9c005a1SMarcelo Araujo .pe_emu = "virtio-scsi",
765f9c005a1SMarcelo Araujo .pe_init = pci_vtscsi_init,
7664d5460a7SJohn Baldwin .pe_legacy_config = pci_vtscsi_legacy_config,
767f9c005a1SMarcelo Araujo .pe_barwrite = vi_pci_write,
768f9c005a1SMarcelo Araujo .pe_barread = vi_pci_read
769f9c005a1SMarcelo Araujo };
770f9c005a1SMarcelo Araujo PCI_EMUL_SET(pci_de_vscsi);
771