Revision 06114d72 hw/virtio-scsi.c
b/hw/virtio-scsi.c | ||
---|---|---|
134 | 134 |
VirtQueue *cmd_vq; |
135 | 135 |
uint32_t sense_size; |
136 | 136 |
uint32_t cdb_size; |
137 |
int resetting; |
|
137 | 138 |
} VirtIOSCSI; |
138 | 139 |
|
139 | 140 |
typedef struct VirtIOSCSIReq { |
... | ... | |
236 | 237 |
return req; |
237 | 238 |
} |
238 | 239 |
|
239 |
static void virtio_scsi_fail_ctrl_req(VirtIOSCSIReq *req)
|
|
240 |
static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
|
240 | 241 |
{ |
241 |
if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) { |
|
242 |
req->resp.tmf->response = VIRTIO_SCSI_S_FAILURE; |
|
243 |
} else { |
|
244 |
req->resp.an->response = VIRTIO_SCSI_S_FAILURE; |
|
242 |
SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun); |
|
243 |
SCSIRequest *r, *next; |
|
244 |
DeviceState *qdev; |
|
245 |
int target; |
|
246 |
|
|
247 |
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ |
|
248 |
req->resp.tmf->response = VIRTIO_SCSI_S_OK; |
|
249 |
|
|
250 |
switch (req->req.tmf->subtype) { |
|
251 |
case VIRTIO_SCSI_T_TMF_ABORT_TASK: |
|
252 |
case VIRTIO_SCSI_T_TMF_QUERY_TASK: |
|
253 |
if (!d) { |
|
254 |
goto fail; |
|
255 |
} |
|
256 |
if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { |
|
257 |
goto incorrect_lun; |
|
258 |
} |
|
259 |
QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { |
|
260 |
if (r->tag == req->req.tmf->tag) { |
|
261 |
break; |
|
262 |
} |
|
263 |
} |
|
264 |
if (r && r->hba_private) { |
|
265 |
if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { |
|
266 |
/* "If the specified command is present in the task set, then |
|
267 |
* return a service response set to FUNCTION SUCCEEDED". |
|
268 |
*/ |
|
269 |
req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; |
|
270 |
} else { |
|
271 |
scsi_req_cancel(r); |
|
272 |
} |
|
273 |
} |
|
274 |
break; |
|
275 |
|
|
276 |
case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: |
|
277 |
if (!d) { |
|
278 |
goto fail; |
|
279 |
} |
|
280 |
if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { |
|
281 |
goto incorrect_lun; |
|
282 |
} |
|
283 |
s->resetting++; |
|
284 |
qdev_reset_all(&d->qdev); |
|
285 |
s->resetting--; |
|
286 |
break; |
|
287 |
|
|
288 |
case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: |
|
289 |
case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: |
|
290 |
case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: |
|
291 |
if (!d) { |
|
292 |
goto fail; |
|
293 |
} |
|
294 |
if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { |
|
295 |
goto incorrect_lun; |
|
296 |
} |
|
297 |
QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { |
|
298 |
if (r->hba_private) { |
|
299 |
if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { |
|
300 |
/* "If there is any command present in the task set, then |
|
301 |
* return a service response set to FUNCTION SUCCEEDED". |
|
302 |
*/ |
|
303 |
req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; |
|
304 |
break; |
|
305 |
} else { |
|
306 |
scsi_req_cancel(r); |
|
307 |
} |
|
308 |
} |
|
309 |
} |
|
310 |
break; |
|
311 |
|
|
312 |
case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: |
|
313 |
target = req->req.tmf->lun[1]; |
|
314 |
s->resetting++; |
|
315 |
QTAILQ_FOREACH(qdev, &s->bus.qbus.children, sibling) { |
|
316 |
d = DO_UPCAST(SCSIDevice, qdev, qdev); |
|
317 |
if (d->channel == 0 && d->id == target) { |
|
318 |
qdev_reset_all(&d->qdev); |
|
319 |
} |
|
320 |
} |
|
321 |
s->resetting--; |
|
322 |
break; |
|
323 |
|
|
324 |
case VIRTIO_SCSI_T_TMF_CLEAR_ACA: |
|
325 |
default: |
|
326 |
req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED; |
|
327 |
break; |
|
245 | 328 |
} |
246 | 329 |
|
247 |
virtio_scsi_complete_req(req); |
|
330 |
return; |
|
331 |
|
|
332 |
incorrect_lun: |
|
333 |
req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN; |
|
334 |
return; |
|
335 |
|
|
336 |
fail: |
|
337 |
req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET; |
|
248 | 338 |
} |
249 | 339 |
|
250 | 340 |
static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) |
... | ... | |
253 | 343 |
VirtIOSCSIReq *req; |
254 | 344 |
|
255 | 345 |
while ((req = virtio_scsi_pop_req(s, vq))) { |
256 |
virtio_scsi_fail_ctrl_req(req); |
|
346 |
int out_size, in_size; |
|
347 |
if (req->elem.out_num < 1 || req->elem.in_num < 1) { |
|
348 |
virtio_scsi_bad_req(); |
|
349 |
continue; |
|
350 |
} |
|
351 |
|
|
352 |
out_size = req->elem.out_sg[0].iov_len; |
|
353 |
in_size = req->elem.in_sg[0].iov_len; |
|
354 |
if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) { |
|
355 |
if (out_size < sizeof(VirtIOSCSICtrlTMFReq) || |
|
356 |
in_size < sizeof(VirtIOSCSICtrlTMFResp)) { |
|
357 |
virtio_scsi_bad_req(); |
|
358 |
} |
|
359 |
virtio_scsi_do_tmf(s, req); |
|
360 |
|
|
361 |
} else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY || |
|
362 |
req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { |
|
363 |
if (out_size < sizeof(VirtIOSCSICtrlANReq) || |
|
364 |
in_size < sizeof(VirtIOSCSICtrlANResp)) { |
|
365 |
virtio_scsi_bad_req(); |
|
366 |
} |
|
367 |
req->resp.an->event_actual = 0; |
|
368 |
req->resp.an->response = VIRTIO_SCSI_S_OK; |
|
369 |
} |
|
370 |
virtio_scsi_complete_req(req); |
|
257 | 371 |
} |
258 | 372 |
} |
259 | 373 |
|
... | ... | |
288 | 402 |
if (!req) { |
289 | 403 |
return; |
290 | 404 |
} |
291 |
req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED; |
|
405 |
if (req->dev->resetting) { |
|
406 |
req->resp.cmd->response = VIRTIO_SCSI_S_RESET; |
|
407 |
} else { |
|
408 |
req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED; |
|
409 |
} |
|
292 | 410 |
virtio_scsi_complete_req(req); |
293 | 411 |
} |
294 | 412 |
|
Also available in: Unified diff