virtio_scsi: now handles events like capacity data changed.

* push event requests on the event queue, handles them.
* activate feature VIRTIO_SCSI_F_CHANGE.
* when a capacity data changed event is received, schedule a rescan of the scsi device node
on the scsi dpc queue. we find out unique child node and call the rescan_node() hook.
* Haiku only handles media changed events on removable devices, so the feature
only actually works when the device is defined as such, aka for QEMU the following option:
-device scsi-hd,drive=hd,removable=true
This commit is contained in:
Jérôme Duval 2013-07-17 17:48:33 +02:00
parent ed4a8e4d11
commit e598dee752
2 changed files with 98 additions and 2 deletions

View File

@ -32,13 +32,17 @@ VirtioSCSIController::VirtioSCSIController(device_node *node)
fVirtio(NULL),
fVirtioDevice(NULL),
fStatus(B_NO_INIT),
fRequest(NULL)
fRequest(NULL),
fEventDPC(NULL)
{
CALLED();
B_INITIALIZE_SPINLOCK(&fInterruptLock);
fInterruptCondition.Init(this, "virtio scsi transfer");
if (gSCSI->alloc_dpc(&fEventDPC) != B_OK)
return;
// get the Virtio device from our parent's parent
device_node *parent = gDeviceManager->get_parent_node(node);
device_node *virtioParent = gDeviceManager->get_parent_node(parent);
@ -49,7 +53,7 @@ VirtioSCSIController::VirtioSCSIController(device_node *node)
gDeviceManager->put_node(virtioParent);
fVirtio->negociate_features(fVirtioDevice,
0 /*VIRTIO_SCSI_F_HOTPLUG*/,
VIRTIO_SCSI_F_CHANGE /*VIRTIO_SCSI_F_HOTPLUG*/,
&fFeatures, &get_feature_name);
fStatus = fVirtio->read_device_config(fVirtioDevice, 0, &fConfig,
@ -84,6 +88,9 @@ VirtioSCSIController::VirtioSCSIController(device_node *node)
fEventVirtioQueue = virtioQueues[1];
fRequestVirtioQueue = virtioQueues[2];
for (uint32 i = 0; i < VIRTIO_SCSI_NUM_EVENTS; i++)
_SubmitEvent(i);
fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
if (fStatus != B_OK) {
ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
@ -98,6 +105,8 @@ VirtioSCSIController::~VirtioSCSIController()
{
CALLED();
delete fRequest;
gSCSI->free_dpc(fEventDPC);
}
@ -262,3 +271,81 @@ VirtioSCSIController::_RequestInterrupt()
fInterruptCondition.NotifyAll();
}
void
VirtioSCSIController::_EventCallback(void* driverCookie, void* cookie)
{
CALLED();
VirtioSCSIController* controller = (VirtioSCSIController*)driverCookie;
struct virtio_scsi_event* event = (struct virtio_scsi_event*)cookie;
controller->_EventInterrupt(event);
}
void
VirtioSCSIController::_EventInterrupt(struct virtio_scsi_event* event)
{
CALLED();
TRACE("events %#x\n", event->event);
if ((event->event & VIRTIO_SCSI_T_EVENTS_MISSED) != 0) {
ERROR("events missed\n");
} else switch (event->event) {
case VIRTIO_SCSI_T_TRANSPORT_RESET:
ERROR("transport reset\n");
break;
case VIRTIO_SCSI_T_ASYNC_NOTIFY:
ERROR("async notify\n");
break;
case VIRTIO_SCSI_T_PARAM_CHANGE:
{
uint16 sense = (event->reason >> 8)
| ((event->reason & 0xff) << 8);
if (sense == SCSIS_ASC_CAPACITY_DATA_HAS_CHANGED) {
ERROR("capacity data has changed for %x:%x\n", event->lun[1],
event->lun[2] << 8 | event->lun[3]);
gSCSI->schedule_dpc(fBus, fEventDPC, _RescanChildBus, this);
} else
ERROR("param change, unknown reason\n");
break;
}
default:
ERROR("unknown event %#x\n", event->event);
break;
}
}
void
VirtioSCSIController::_SubmitEvent(uint32 eventNumber)
{
CALLED();
struct virtio_scsi_event* event = &fEventBuffers[eventNumber];
bzero(event, sizeof(struct virtio_scsi_event));
physical_entry entry;
get_memory_map(event, sizeof(struct virtio_scsi_event), &entry, 1);
fVirtio->queue_request_v(fEventVirtioQueue, &entry,
0, 1, VirtioSCSIController::_EventCallback, event);
}
void
VirtioSCSIController::_RescanChildBus(void *cookie)
{
CALLED();
VirtioSCSIController* controller = (VirtioSCSIController*)cookie;
device_node *childNode = NULL;
const device_attr attrs[] = { { NULL } };
if (gDeviceManager->get_next_child_node(controller->fNode, attrs,
&childNode) != B_OK) {
ERROR("couldn't find the child node for %p\n", controller->fNode);
return;
}
gDeviceManager->rescan_node(childNode);
TRACE("rescan done %p\n", childNode);
gDeviceManager->put_node(childNode);
}

View File

@ -35,6 +35,8 @@ void swap_words(void *data, size_t size);
#define VIRTIO_SCSI_STANDARD_TIMEOUT 10 * 1000 * 1000
#define VIRTIO_SCSI_INITIATOR_ID 7
#define VIRTIO_SCSI_NUM_EVENTS 4
class VirtioSCSIRequest;
@ -63,6 +65,11 @@ private:
static void _RequestCallback(void* driverCookie,
void *cookie);
void _RequestInterrupt();
static void _EventCallback(void *driverCookie, void *cookie);
void _EventInterrupt(struct virtio_scsi_event* event);
static void _RescanChildBus(void *cookie);
void _SubmitEvent(uint32 event);
device_node* fNode;
scsi_bus fBus;
@ -87,6 +94,8 @@ private:
ConditionVariableEntry fInterruptConditionEntry;
bool fExpectsInterrupt;
scsi_dpc_cookie fEventDPC;
struct virtio_scsi_event fEventBuffers[VIRTIO_SCSI_NUM_EVENTS];
};