1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
|
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2016 Jakub Klama <jceel@FreeBSD.org>.
* Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>.
* Copyright (c) 2026 Hans Rosenfeld
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _PCI_VIRTIO_SCSI_H_
#define _PCI_VIRTIO_SCSI_H_
#include "iov.h"
extern int pci_vtscsi_debug;
#define WPRINTF(msg, params...) PRINTLN("virtio-scsi: " msg, ##params)
#define DPRINTF(msg, params...) if (pci_vtscsi_debug) WPRINTF(msg, ##params)
/* Absolute limits given by the VirtIO SCSI spec */
#define VIRTIO_SCSI_MAX_CHANNEL 0
#define VIRTIO_SCSI_MAX_TARGET 255
#define VIRTIO_SCSI_MAX_LUN 16383
#define VIRTIO_SCSI_HDR_SEG 2
#define VIRTIO_SCSI_ADDL_Q 2
/* Features specific to VirtIO SCSI, none of which we currently support */
#define VIRTIO_SCSI_F_INOUT (1 << 0)
#define VIRTIO_SCSI_F_HOTPLUG (1 << 1)
#define VIRTIO_SCSI_F_CHANGE (1 << 2)
/* Default limits which we set. All of these are configurable. */
#define VTSCSI_DEF_RINGSZ 64
#define VTSCSI_MIN_RINGSZ 4
#define VTSCSI_MAX_RINGSZ 4096
#define VTSCSI_DEF_THR_PER_Q 16
#define VTSCSI_MIN_THR_PER_Q 1
#define VTSCSI_MAX_THR_PER_Q 256
#define VTSCSI_DEF_MAXSEG 64
#define VTSCSI_MIN_MAXSEG (VIRTIO_SCSI_HDR_SEG + 1)
#define VTSCSI_MAX_MAXSEG \
(4096 - VIRTIO_SCSI_HDR_SEG - SPLIT_IOV_ADDL_IOV)
#define VTSCSI_DEF_REQUESTQ 1
#define VTSCSI_MIN_REQUESTQ 1
#define VTSCSI_MAX_REQUESTQ (32 - VIRTIO_SCSI_ADDL_Q)
/*
* Device-specific config space registers
*
* The guest driver may try to modify cdb_size and sense_size by writing the
* respective config space registers. Since we currently ignore all writes to
* config space, these macros are essentially constant.
*/
#define VTSCSI_IN_HEADER_LEN(_sc) \
(sizeof(struct pci_vtscsi_req_cmd_rd) + _sc->vss_config.cdb_size)
#define VTSCSI_OUT_HEADER_LEN(_sc) \
(sizeof(struct pci_vtscsi_req_cmd_wr) + _sc->vss_config.sense_size)
struct pci_vtscsi_config {
uint32_t num_queues;
uint32_t seg_max;
uint32_t max_sectors;
uint32_t cmd_per_lun;
uint32_t event_info_size;
uint32_t sense_size;
uint32_t cdb_size;
uint16_t max_channel;
uint16_t max_target;
uint32_t max_lun;
} __attribute__((packed));
/*
* I/O request state
*
* Each pci_vtscsi_queue has configurable number of pci_vtscsi_request
* structures pre-allocated on vsq_free_requests. For each I/O request
* coming in on the I/O virtqueue, the request queue handler takes
* pci_vtscsi_request off vsq_free_requests, fills in the data from the
* I/O virtqueue, puts it on vsq_requests, and signals vsq_cv.
*
* Each pci_vtscsi_queue will have a configurable number of worker threads,
* which wait on vsq_cv. When signalled, they repeatedly take a single
* pci_vtscsi_request off vsq_requests and hand it to the backend, which
* processes it synchronously. After completion, the pci_vtscsi_request
* is re-initialized and put back onto vsq_free_requests.
*
* The worker threads exit when vsq_cv is signalled after vsw_exiting was set.
*
* The I/O vectors for each request are kept in the preallocated iovec array
* vsr_iov, and pointers to the respective header/data in/out portions are set
* up to point into the array when the request is queued for processing.
*
* The number of iovecs preallocated for vsr_iov is derived from the configured
* 'seg_max' parameter defined by the virtio spec:
* - 'seg_max' parameter specifies the maximum number of I/O data vectors
* we support in any request
* - we need 2 additional iovecs for the I/O headers (VIRTIO_SCSI_HDR_SEG)
* - we need another 2 additional iovecs for split_iov() (SPLIT_IOV_ADDL_IOV)
*
* The only time we explicitly need the full size of vsr_iov after preallocation
* is during re-initialization after completing a request, and implicitly in the
* calls to split_iov() the set up the pointers. In all other cases, we use only
* 'seg_max' + VIRTIO_SCSI_HDR_SEG, and we advertise only 'seg_max' to the guest
* in accordance to the virtio spec.
*/
STAILQ_HEAD(pci_vtscsi_req_queue, pci_vtscsi_request);
struct pci_vtscsi_queue {
struct pci_vtscsi_softc *vsq_sc;
struct vqueue_info *vsq_vq;
pthread_mutex_t vsq_rmtx;
pthread_mutex_t vsq_fmtx;
pthread_mutex_t vsq_qmtx;
pthread_cond_t vsq_cv;
struct pci_vtscsi_req_queue vsq_requests;
struct pci_vtscsi_req_queue vsq_free_requests;
LIST_HEAD(, pci_vtscsi_worker) vsq_workers;
};
struct pci_vtscsi_worker {
struct pci_vtscsi_queue *vsw_queue;
pthread_t vsw_thread;
bool vsw_exiting;
LIST_ENTRY(pci_vtscsi_worker) vsw_link;
};
struct pci_vtscsi_request {
struct pci_vtscsi_queue *vsr_queue;
struct iovec *vsr_iov;
struct iovec *vsr_iov_in;
struct iovec *vsr_iov_out;
struct iovec *vsr_data_iov_in;
struct iovec *vsr_data_iov_out;
struct pci_vtscsi_req_cmd_rd *vsr_cmd_rd;
struct pci_vtscsi_req_cmd_wr *vsr_cmd_wr;
void *vsr_backend;
size_t vsr_niov_in;
size_t vsr_niov_out;
size_t vsr_data_niov_in;
size_t vsr_data_niov_out;
uint32_t vsr_idx;
STAILQ_ENTRY(pci_vtscsi_request) vsr_link;
};
/*
* Per-target state.
*/
struct pci_vtscsi_target {
uint8_t vst_target;
int vst_fd;
int vst_max_sectors;
};
/*
* Per-device softc
*/
struct pci_vtscsi_softc {
struct virtio_softc vss_vs;
struct virtio_consts vss_vi_consts;
struct vqueue_info *vss_vq;
struct pci_vtscsi_queue *vss_queues;
pthread_mutex_t vss_mtx;
uint32_t vss_features;
size_t vss_num_target;
uint32_t vss_ctl_ringsz;
uint32_t vss_evt_ringsz;
uint32_t vss_req_ringsz;
uint32_t vss_thr_per_q;
struct pci_vtscsi_config vss_default_config;
struct pci_vtscsi_config vss_config;
struct pci_vtscsi_target *vss_targets;
struct pci_vtscsi_backend *vss_backend;
};
/*
* VirtIO-SCSI Task Management Function control requests
*/
#define VIRTIO_SCSI_T_TMF 0
#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1
#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2
#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3
#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4
#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5
#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6
#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7
#define VIRTIO_SCSI_T_TMF_MAX_FUNC VIRTIO_SCSI_T_TMF_QUERY_TASK_SET
/* command-specific response values */
#define VIRTIO_SCSI_S_FUNCTION_COMPLETE 0
#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10
#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11
struct pci_vtscsi_ctrl_tmf {
const uint32_t type;
const uint32_t subtype;
const uint8_t lun[8];
const uint64_t id;
uint8_t response;
} __attribute__((packed));
/*
* VirtIO-SCSI Asynchronous Notification control requests
*/
#define VIRTIO_SCSI_T_AN_QUERY 1
#define VIRTIO_SCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2
#define VIRTIO_SCSI_EVT_ASYNC_POWER_MGMT 4
#define VIRTIO_SCSI_EVT_ASYNC_EXTERNAL_REQUEST 8
#define VIRTIO_SCSI_EVT_ASYNC_MEDIA_CHANGE 16
#define VIRTIO_SCSI_EVT_ASYNC_MULTI_HOST 32
#define VIRTIO_SCSI_EVT_ASYNC_DEVICE_BUSY 64
struct pci_vtscsi_ctrl_an {
const uint32_t type;
const uint8_t lun[8];
const uint32_t event_requested;
uint32_t event_actual;
uint8_t response;
} __attribute__((packed));
/* command-specific response values */
#define VIRTIO_SCSI_S_OK 0
#define VIRTIO_SCSI_S_OVERRUN 1
#define VIRTIO_SCSI_S_ABORTED 2
#define VIRTIO_SCSI_S_BAD_TARGET 3
#define VIRTIO_SCSI_S_RESET 4
#define VIRTIO_SCSI_S_BUSY 5
#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
#define VIRTIO_SCSI_S_TARGET_FAILURE 7
#define VIRTIO_SCSI_S_NEXUS_FAILURE 8
#define VIRTIO_SCSI_S_FAILURE 9
#define VIRTIO_SCSI_S_INCORRECT_LUN 12
struct pci_vtscsi_event {
uint32_t event;
uint8_t lun[8];
uint32_t reason;
} __attribute__((packed));
/*
* VirtIO-SCSI I/O requests
*/
struct pci_vtscsi_req_cmd_rd {
const uint8_t lun[8];
const uint64_t id;
const uint8_t task_attr;
const uint8_t prio;
const uint8_t crn;
const uint8_t cdb[];
} __attribute__((packed));
/* task_attr */
#define VIRTIO_SCSI_S_SIMPLE 0
#define VIRTIO_SCSI_S_ORDERED 1
#define VIRTIO_SCSI_S_HEAD 2
#define VIRTIO_SCSI_S_ACA 3
struct pci_vtscsi_req_cmd_wr {
uint32_t sense_len;
uint32_t residual;
uint16_t status_qualifier;
uint8_t status;
uint8_t response;
uint8_t sense[];
} __attribute__((packed));
/*
* Backend interface
*/
struct pci_vtscsi_backend {
const char *vsb_name;
int (*vsb_init)(struct pci_vtscsi_softc *,
struct pci_vtscsi_backend *, nvlist_t *);
int (*vsb_open)(struct pci_vtscsi_softc *, const char *,
long);
void (*vsb_reset)(struct pci_vtscsi_softc *);
void* (*vsb_req_alloc)(struct pci_vtscsi_softc *);
void (*vsb_req_clear)(void *);
void (*vsb_req_free)(void *);
void (*vsb_tmf_hdl)(struct pci_vtscsi_softc *, int,
struct pci_vtscsi_ctrl_tmf *);
void (*vsb_an_hdl)(struct pci_vtscsi_softc *, int,
struct pci_vtscsi_ctrl_an *);
int (*vsb_req_hdl)(struct pci_vtscsi_softc *, int,
struct pci_vtscsi_request *);
};
#define PCI_VTSCSI_BACKEND_SET(x) DATA_SET(pci_vtscsi_backend_set, x)
/*
* LUN address parsing
*
* The LUN address consists of 8 bytes. While the spec describes this as 0x01,
* followed by the target byte, followed by a "single-level LUN structure",
* this is actually the same as a hierarchical LUN address as defined by SAM-5,
* consisting of four levels of addressing, where in each level the two MSB of
* byte 0 select the address mode used in the remaining bits and bytes.
*
*
* Only the first two levels are acutally used by virtio-scsi:
*
* Level 1: 0x01, 0xTT: Peripheral Device Addressing: Bus 1, Target 0-255
* Level 2: 0xLL, 0xLL: Peripheral Device Addressing: Bus MBZ, LUN 0-255
* or: Flat Space Addressing: LUN (0-16383)
* Level 3 and 4: not used, MBZ
*
*
* Alternatively, the first level may contain an extended LUN address to select
* the REPORT_LUNS well-known logical unit:
*
* Level 1: 0xC1, 0x01: Extended LUN Adressing, Well-Known LUN 1 (REPORT_LUNS)
* Level 2, 3, and 4: not used, MBZ
*
* The virtio spec says that we SHOULD implement the REPORT_LUNS well-known
* logical unit but we currently don't.
*
* According to the virtio spec, these are the only LUNS address formats to be
* used with virtio-scsi.
*/
/*
* Check that the given LUN address conforms to the virtio spec, does not
* address an unknown target, and especially does not address the REPORT_LUNS
* well-known logical unit.
*/
static inline bool
pci_vtscsi_check_lun(struct pci_vtscsi_softc *sc, const uint8_t *lun)
{
if (lun[0] == 0xC1)
return (false);
if (lun[0] != 0x01)
return (false);
if (lun[1] >= sc->vss_num_target)
return (false);
if (lun[1] != sc->vss_targets[lun[1]].vst_target)
return (false);
if (sc->vss_targets[lun[1]].vst_fd < 0)
return (false);
if (lun[2] != 0x00 && (lun[2] & 0xc0) != 0x40)
return (false);
if (lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0)
return (false);
return (true);
}
/*
* Get the target id from a LUN address.
*
* Every code path using this function must have called pci_vtscsi_check_lun()
* before to make sure the LUN address is valid.
*/
static inline uint8_t
pci_vtscsi_get_target(struct pci_vtscsi_softc *sc, const uint8_t *lun)
{
assert(lun[0] == 0x01);
assert(lun[1] < sc->vss_num_target);
assert(lun[1] == sc->vss_targets[lun[1]].vst_target);
assert(sc->vss_targets[lun[1]].vst_fd >= 0);
assert(lun[2] == 0x00 || (lun[2] & 0xc0) == 0x40);
return (lun[1]);
}
/*
* Get the LUN id from a LUN address.
*
* Every code path using this function must have called pci_vtscsi_check_lun()
* before to make sure the LUN address is valid.
*/
static inline uint16_t
pci_vtscsi_get_lun(struct pci_vtscsi_softc *sc, const uint8_t *lun)
{
assert(lun[0] == 0x01);
assert(lun[1] < sc->vss_num_target);
assert(lun[1] == sc->vss_targets[lun[1]].vst_target);
assert(sc->vss_targets[lun[1]].vst_fd >= 0);
assert(lun[2] == 0x00 || (lun[2] & 0xc0) == 0x40);
return (((lun[2] << 8) | lun[3]) & 0x3fff);
}
#endif /* _PCI_VIRTIO_SCSI_H_ */
|