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:
@@ -32,12 +32,16 @@ VirtioSCSIController::VirtioSCSIController(device_node *node)
|
|||||||
fVirtio(NULL),
|
fVirtio(NULL),
|
||||||
fVirtioDevice(NULL),
|
fVirtioDevice(NULL),
|
||||||
fStatus(B_NO_INIT),
|
fStatus(B_NO_INIT),
|
||||||
fRequest(NULL)
|
fRequest(NULL),
|
||||||
|
fEventDPC(NULL)
|
||||||
{
|
{
|
||||||
CALLED();
|
CALLED();
|
||||||
|
|
||||||
B_INITIALIZE_SPINLOCK(&fInterruptLock);
|
B_INITIALIZE_SPINLOCK(&fInterruptLock);
|
||||||
fInterruptCondition.Init(this, "virtio scsi transfer");
|
fInterruptCondition.Init(this, "virtio scsi transfer");
|
||||||
|
|
||||||
|
if (gSCSI->alloc_dpc(&fEventDPC) != B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
// get the Virtio device from our parent's parent
|
// get the Virtio device from our parent's parent
|
||||||
device_node *parent = gDeviceManager->get_parent_node(node);
|
device_node *parent = gDeviceManager->get_parent_node(node);
|
||||||
@@ -49,7 +53,7 @@ VirtioSCSIController::VirtioSCSIController(device_node *node)
|
|||||||
gDeviceManager->put_node(virtioParent);
|
gDeviceManager->put_node(virtioParent);
|
||||||
|
|
||||||
fVirtio->negociate_features(fVirtioDevice,
|
fVirtio->negociate_features(fVirtioDevice,
|
||||||
0 /*VIRTIO_SCSI_F_HOTPLUG*/,
|
VIRTIO_SCSI_F_CHANGE /*VIRTIO_SCSI_F_HOTPLUG*/,
|
||||||
&fFeatures, &get_feature_name);
|
&fFeatures, &get_feature_name);
|
||||||
|
|
||||||
fStatus = fVirtio->read_device_config(fVirtioDevice, 0, &fConfig,
|
fStatus = fVirtio->read_device_config(fVirtioDevice, 0, &fConfig,
|
||||||
@@ -83,6 +87,9 @@ VirtioSCSIController::VirtioSCSIController(device_node *node)
|
|||||||
fControlVirtioQueue = virtioQueues[0];
|
fControlVirtioQueue = virtioQueues[0];
|
||||||
fEventVirtioQueue = virtioQueues[1];
|
fEventVirtioQueue = virtioQueues[1];
|
||||||
fRequestVirtioQueue = virtioQueues[2];
|
fRequestVirtioQueue = virtioQueues[2];
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < VIRTIO_SCSI_NUM_EVENTS; i++)
|
||||||
|
_SubmitEvent(i);
|
||||||
|
|
||||||
fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
|
fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
|
||||||
if (fStatus != B_OK) {
|
if (fStatus != B_OK) {
|
||||||
@@ -98,6 +105,8 @@ VirtioSCSIController::~VirtioSCSIController()
|
|||||||
{
|
{
|
||||||
CALLED();
|
CALLED();
|
||||||
delete fRequest;
|
delete fRequest;
|
||||||
|
|
||||||
|
gSCSI->free_dpc(fEventDPC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -262,3 +271,81 @@ VirtioSCSIController::_RequestInterrupt()
|
|||||||
fInterruptCondition.NotifyAll();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ void swap_words(void *data, size_t size);
|
|||||||
#define VIRTIO_SCSI_STANDARD_TIMEOUT 10 * 1000 * 1000
|
#define VIRTIO_SCSI_STANDARD_TIMEOUT 10 * 1000 * 1000
|
||||||
#define VIRTIO_SCSI_INITIATOR_ID 7
|
#define VIRTIO_SCSI_INITIATOR_ID 7
|
||||||
|
|
||||||
|
#define VIRTIO_SCSI_NUM_EVENTS 4
|
||||||
|
|
||||||
|
|
||||||
class VirtioSCSIRequest;
|
class VirtioSCSIRequest;
|
||||||
|
|
||||||
@@ -63,6 +65,11 @@ private:
|
|||||||
static void _RequestCallback(void* driverCookie,
|
static void _RequestCallback(void* driverCookie,
|
||||||
void *cookie);
|
void *cookie);
|
||||||
void _RequestInterrupt();
|
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;
|
device_node* fNode;
|
||||||
scsi_bus fBus;
|
scsi_bus fBus;
|
||||||
@@ -87,6 +94,8 @@ private:
|
|||||||
ConditionVariableEntry fInterruptConditionEntry;
|
ConditionVariableEntry fInterruptConditionEntry;
|
||||||
bool fExpectsInterrupt;
|
bool fExpectsInterrupt;
|
||||||
|
|
||||||
|
scsi_dpc_cookie fEventDPC;
|
||||||
|
struct virtio_scsi_event fEventBuffers[VIRTIO_SCSI_NUM_EVENTS];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user