usb_disk: Granularize locking.
There is now an "io_lock" which must be held when performing any USB operations. All ioctls that read basic status, size, icon, etc. information do not need to acquire it, of course, which should improve userland lockup occurrences on congested USB disks.
This commit is contained in:
@@ -87,7 +87,6 @@ struct {
|
|||||||
static void usb_disk_callback(void *cookie, status_t status, void *data,
|
static void usb_disk_callback(void *cookie, status_t status, void *data,
|
||||||
size_t actualLength);
|
size_t actualLength);
|
||||||
|
|
||||||
status_t usb_disk_mass_storage_reset(disk_device *device);
|
|
||||||
uint8 usb_disk_get_max_lun(disk_device *device);
|
uint8 usb_disk_get_max_lun(disk_device *device);
|
||||||
void usb_disk_reset_recovery(disk_device *device);
|
void usb_disk_reset_recovery(disk_device *device);
|
||||||
status_t usb_disk_receive_csw(disk_device *device,
|
status_t usb_disk_receive_csw(disk_device *device,
|
||||||
@@ -111,12 +110,14 @@ disk_device_s::disk_device_s()
|
|||||||
notify(-1),
|
notify(-1),
|
||||||
interruptLock(-1)
|
interruptLock(-1)
|
||||||
{
|
{
|
||||||
|
recursive_lock_init(&io_lock, "usb_disk i/o lock");
|
||||||
mutex_init(&lock, "usb_disk device lock");
|
mutex_init(&lock, "usb_disk device lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
disk_device_s::~disk_device_s()
|
disk_device_s::~disk_device_s()
|
||||||
{
|
{
|
||||||
|
recursive_lock_destroy(&io_lock);
|
||||||
mutex_destroy(&lock);
|
mutex_destroy(&lock);
|
||||||
|
|
||||||
if (notify >= 0)
|
if (notify >= 0)
|
||||||
@@ -156,7 +157,7 @@ usb_disk_free_device_and_luns(disk_device *device)
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
status_t
|
static status_t
|
||||||
usb_disk_mass_storage_reset(disk_device *device)
|
usb_disk_mass_storage_reset(disk_device *device)
|
||||||
{
|
{
|
||||||
return gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_OUT
|
return gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_OUT
|
||||||
@@ -168,6 +169,8 @@ usb_disk_mass_storage_reset(disk_device *device)
|
|||||||
uint8
|
uint8
|
||||||
usb_disk_get_max_lun(disk_device *device)
|
usb_disk_get_max_lun(disk_device *device)
|
||||||
{
|
{
|
||||||
|
ASSERT_LOCKED_RECURSIVE(&device->io_lock);
|
||||||
|
|
||||||
uint8 result = 0;
|
uint8 result = 0;
|
||||||
size_t actualLength = 0;
|
size_t actualLength = 0;
|
||||||
|
|
||||||
@@ -200,6 +203,7 @@ void
|
|||||||
usb_disk_reset_recovery(disk_device *device, err_act *_action)
|
usb_disk_reset_recovery(disk_device *device, err_act *_action)
|
||||||
{
|
{
|
||||||
TRACE("reset recovery\n");
|
TRACE("reset recovery\n");
|
||||||
|
ASSERT_LOCKED_RECURSIVE(&device->io_lock);
|
||||||
|
|
||||||
usb_disk_mass_storage_reset(device);
|
usb_disk_mass_storage_reset(device);
|
||||||
usb_disk_clear_halt(device->bulk_in);
|
usb_disk_clear_halt(device->bulk_in);
|
||||||
@@ -240,6 +244,7 @@ usb_disk_transfer_data(disk_device *device, bool directionIn, const transfer_dat
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&device->lock);
|
||||||
do {
|
do {
|
||||||
result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT,
|
result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT,
|
||||||
10 * 1000 * 1000);
|
10 * 1000 * 1000);
|
||||||
@@ -253,6 +258,7 @@ usb_disk_transfer_data(disk_device *device, bool directionIn, const transfer_dat
|
|||||||
acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0);
|
acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0);
|
||||||
}
|
}
|
||||||
} while (result == B_INTERRUPTED);
|
} while (result == B_INTERRUPTED);
|
||||||
|
mutex_lock(&device->lock);
|
||||||
|
|
||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
TRACE_ALWAYS("acquire_sem failed while waiting for data transfer: %s\n",
|
TRACE_ALWAYS("acquire_sem failed while waiting for data transfer: %s\n",
|
||||||
@@ -280,8 +286,8 @@ usb_disk_transfer_data(disk_device *device, bool directionIn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
static void
|
||||||
usb_disk_interrupt(void* cookie, int32 status, void* data, size_t length)
|
callback_interrupt(void* cookie, int32 status, void* data, size_t length)
|
||||||
{
|
{
|
||||||
disk_device* device = (disk_device*)cookie;
|
disk_device* device = (disk_device*)cookie;
|
||||||
// We release the lock even if the interrupt is invalid. This way there
|
// We release the lock even if the interrupt is invalid. This way there
|
||||||
@@ -300,17 +306,18 @@ usb_disk_interrupt(void* cookie, int32 status, void* data, size_t length)
|
|||||||
|
|
||||||
// Reschedule the interrupt for next time
|
// Reschedule the interrupt for next time
|
||||||
gUSBModule->queue_interrupt(device->interrupt, device->interruptBuffer, 2,
|
gUSBModule->queue_interrupt(device->interrupt, device->interruptBuffer, 2,
|
||||||
usb_disk_interrupt, cookie);
|
callback_interrupt, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
static status_t
|
||||||
usb_disk_receive_csw_interrupt(disk_device *device,
|
receive_csw_interrupt(disk_device *device,
|
||||||
interrupt_status_wrapper *status)
|
interrupt_status_wrapper *status)
|
||||||
{
|
{
|
||||||
TRACE("Waiting for result...\n");
|
TRACE("Waiting for result...\n");
|
||||||
|
|
||||||
gUSBModule->queue_interrupt(device->interrupt,
|
gUSBModule->queue_interrupt(device->interrupt,
|
||||||
device->interruptBuffer, 2, usb_disk_interrupt, device);
|
device->interruptBuffer, 2, callback_interrupt, device);
|
||||||
|
|
||||||
acquire_sem(device->interruptLock);
|
acquire_sem(device->interruptLock);
|
||||||
|
|
||||||
@@ -320,8 +327,9 @@ usb_disk_receive_csw_interrupt(disk_device *device,
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t
|
|
||||||
usb_disk_receive_csw_bulk(disk_device *device,
|
static status_t
|
||||||
|
receive_csw_bulk(disk_device *device,
|
||||||
usb_massbulk_command_status_wrapper *status)
|
usb_massbulk_command_status_wrapper *status)
|
||||||
{
|
{
|
||||||
status_t result = usb_disk_transfer_data(device, true, status,
|
status_t result = usb_disk_transfer_data(device, true, status,
|
||||||
@@ -348,6 +356,7 @@ usb_disk_operation_interrupt(device_lun *lun, uint8* operation,
|
|||||||
TRACE("operation: lun: %u; op: 0x%x; data: %p; dlen: %p (%lu); in: %c\n",
|
TRACE("operation: lun: %u; op: 0x%x; data: %p; dlen: %p (%lu); in: %c\n",
|
||||||
lun->logical_unit_number, operation[0], data.vecs, dataLength,
|
lun->logical_unit_number, operation[0], data.vecs, dataLength,
|
||||||
dataLength ? *dataLength : 0, directionIn ? 'y' : 'n');
|
dataLength ? *dataLength : 0, directionIn ? 'y' : 'n');
|
||||||
|
ASSERT_LOCKED_RECURSIVE(&lun->device->io_lock);
|
||||||
|
|
||||||
disk_device* device = lun->device;
|
disk_device* device = lun->device;
|
||||||
|
|
||||||
@@ -395,13 +404,13 @@ usb_disk_operation_interrupt(device_lun *lun, uint8* operation,
|
|||||||
// step 3 : wait for the device to send the interrupt ACK
|
// step 3 : wait for the device to send the interrupt ACK
|
||||||
if (operation[0] != SCSI_REQUEST_SENSE_6) {
|
if (operation[0] != SCSI_REQUEST_SENSE_6) {
|
||||||
interrupt_status_wrapper status;
|
interrupt_status_wrapper status;
|
||||||
result = usb_disk_receive_csw_interrupt(device, &status);
|
result = receive_csw_interrupt(device, &status);
|
||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
// in case of a stall or error clear the stall and try again
|
// in case of a stall or error clear the stall and try again
|
||||||
TRACE("Error receiving interrupt: %s. Retrying...\n",
|
TRACE("Error receiving interrupt: %s. Retrying...\n",
|
||||||
strerror(result));
|
strerror(result));
|
||||||
usb_disk_clear_halt(device->bulk_in);
|
usb_disk_clear_halt(device->bulk_in);
|
||||||
result = usb_disk_receive_csw_interrupt(device, &status);
|
result = receive_csw_interrupt(device, &status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
@@ -426,6 +435,7 @@ usb_disk_operation_bulk(device_lun *lun, uint8 *operation, size_t operationLengt
|
|||||||
lun->logical_unit_number, operation[0],
|
lun->logical_unit_number, operation[0],
|
||||||
data.vecs, dataLength, dataLength ? *dataLength : 0,
|
data.vecs, dataLength, dataLength ? *dataLength : 0,
|
||||||
directionIn ? 'y' : 'n');
|
directionIn ? 'y' : 'n');
|
||||||
|
ASSERT_LOCKED_RECURSIVE(&lun->device->io_lock);
|
||||||
|
|
||||||
disk_device *device = lun->device;
|
disk_device *device = lun->device;
|
||||||
usb_massbulk_command_block_wrapper command;
|
usb_massbulk_command_block_wrapper command;
|
||||||
@@ -477,11 +487,11 @@ usb_disk_operation_bulk(device_lun *lun, uint8 *operation, size_t operationLengt
|
|||||||
}
|
}
|
||||||
|
|
||||||
usb_massbulk_command_status_wrapper status;
|
usb_massbulk_command_status_wrapper status;
|
||||||
result = usb_disk_receive_csw_bulk(device, &status);
|
result = receive_csw_bulk(device, &status);
|
||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
// in case of a stall or error clear the stall and try again
|
// in case of a stall or error clear the stall and try again
|
||||||
usb_disk_clear_halt(device->bulk_in);
|
usb_disk_clear_halt(device->bulk_in);
|
||||||
result = usb_disk_receive_csw_bulk(device, &status);
|
result = receive_csw_bulk(device, &status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
@@ -1020,6 +1030,9 @@ usb_disk_attach(device_node *node, usb_device newDevice, void **cookie)
|
|||||||
{
|
{
|
||||||
TRACE("device_added(0x%08" B_PRIx32 ")\n", newDevice);
|
TRACE("device_added(0x%08" B_PRIx32 ")\n", newDevice);
|
||||||
disk_device *device = new(std::nothrow) disk_device;
|
disk_device *device = new(std::nothrow) disk_device;
|
||||||
|
recursive_lock_lock(&device->io_lock);
|
||||||
|
mutex_lock(&device->lock);
|
||||||
|
|
||||||
device->node = node;
|
device->node = node;
|
||||||
device->device = newDevice;
|
device->device = newDevice;
|
||||||
device->removed = false;
|
device->removed = false;
|
||||||
@@ -1204,6 +1217,9 @@ usb_disk_attach(device_node *node, usb_device newDevice, void **cookie)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&device->lock);
|
||||||
|
recursive_lock_unlock(&device->io_lock);
|
||||||
|
|
||||||
TRACE("new device: 0x%p\n", device);
|
TRACE("new device: 0x%p\n", device);
|
||||||
*cookie = (void *)device;
|
*cookie = (void *)device;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -1443,10 +1459,11 @@ usb_disk_close(void *cookie)
|
|||||||
device_lun *lun = (device_lun *)cookie;
|
device_lun *lun = (device_lun *)cookie;
|
||||||
disk_device *device = lun->device;
|
disk_device *device = lun->device;
|
||||||
|
|
||||||
mutex_lock(&device->lock);
|
RecursiveLocker ioLocker(device->io_lock);
|
||||||
|
MutexLocker deviceLocker(device->lock);
|
||||||
|
|
||||||
if (!device->removed)
|
if (!device->removed)
|
||||||
usb_disk_synchronize(lun, false);
|
usb_disk_synchronize(lun, false);
|
||||||
mutex_unlock(&device->lock);
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -1493,6 +1510,42 @@ normalize_name(char *name, size_t nameLength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
acquire_io_lock(disk_device *device, MutexLocker& locker, RecursiveLocker& ioLocker)
|
||||||
|
{
|
||||||
|
locker.Unlock();
|
||||||
|
ioLocker.SetTo(device->io_lock, false, true);
|
||||||
|
locker.Lock();
|
||||||
|
|
||||||
|
if (!locker.IsLocked() || !ioLocker.IsLocked())
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
if (device->removed)
|
||||||
|
return B_DEV_NOT_READY;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
handle_media_change(device_lun *lun, MutexLocker& locker)
|
||||||
|
{
|
||||||
|
RecursiveLocker ioLocker;
|
||||||
|
status_t result = acquire_io_lock(lun->device, locker, ioLocker);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// It may have been handled while we were waiting for locks.
|
||||||
|
if (lun->media_changed) {
|
||||||
|
result = usb_disk_update_capacity(lun);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
||||||
{
|
{
|
||||||
@@ -1501,12 +1554,13 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
|||||||
MutexLocker locker(&device->lock);
|
MutexLocker locker(&device->lock);
|
||||||
if (device->removed)
|
if (device->removed)
|
||||||
return B_DEV_NOT_READY;
|
return B_DEV_NOT_READY;
|
||||||
|
RecursiveLocker ioLocker;
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case B_GET_DEVICE_SIZE:
|
case B_GET_DEVICE_SIZE:
|
||||||
{
|
{
|
||||||
if (lun->media_changed) {
|
if (lun->media_changed) {
|
||||||
status_t result = usb_disk_update_capacity(lun);
|
status_t result = handle_media_change(lun, locker);
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1517,6 +1571,10 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
|||||||
|
|
||||||
case B_GET_MEDIA_STATUS:
|
case B_GET_MEDIA_STATUS:
|
||||||
{
|
{
|
||||||
|
status_t result = acquire_io_lock(lun->device, locker, ioLocker);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
err_act action = err_act_ok;
|
err_act action = err_act_ok;
|
||||||
status_t ready;
|
status_t ready;
|
||||||
for (uint32 tries = 0; tries < 3; tries++) {
|
for (uint32 tries = 0; tries < 3; tries++) {
|
||||||
@@ -1544,7 +1602,7 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
|||||||
if (buffer == NULL || length > sizeof(device_geometry))
|
if (buffer == NULL || length > sizeof(device_geometry))
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
if (lun->media_changed) {
|
if (lun->media_changed) {
|
||||||
status_t result = usb_disk_update_capacity(lun);
|
status_t result = handle_media_change(lun, locker);
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1565,11 +1623,22 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case B_FLUSH_DRIVE_CACHE:
|
case B_FLUSH_DRIVE_CACHE:
|
||||||
|
{
|
||||||
TRACE("B_FLUSH_DRIVE_CACHE\n");
|
TRACE("B_FLUSH_DRIVE_CACHE\n");
|
||||||
|
|
||||||
|
status_t result = acquire_io_lock(lun->device, locker, ioLocker);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
return usb_disk_synchronize(lun, true);
|
return usb_disk_synchronize(lun, true);
|
||||||
|
}
|
||||||
|
|
||||||
case B_EJECT_DEVICE:
|
case B_EJECT_DEVICE:
|
||||||
{
|
{
|
||||||
|
status_t result = acquire_io_lock(lun->device, locker, ioLocker);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
uint8 commandBlock[12];
|
uint8 commandBlock[12];
|
||||||
memset(commandBlock, 0, sizeof(commandBlock));
|
memset(commandBlock, 0, sizeof(commandBlock));
|
||||||
|
|
||||||
@@ -1583,6 +1652,10 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
|||||||
|
|
||||||
case B_LOAD_MEDIA:
|
case B_LOAD_MEDIA:
|
||||||
{
|
{
|
||||||
|
status_t result = acquire_io_lock(lun->device, locker, ioLocker);
|
||||||
|
if (result != B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
uint8 commandBlock[12];
|
uint8 commandBlock[12];
|
||||||
memset(commandBlock, 0, sizeof(commandBlock));
|
memset(commandBlock, 0, sizeof(commandBlock));
|
||||||
|
|
||||||
@@ -1818,11 +1891,12 @@ usb_disk_io(void *cookie, io_request *request)
|
|||||||
|
|
||||||
device_lun *lun = (device_lun *)cookie;
|
device_lun *lun = (device_lun *)cookie;
|
||||||
disk_device *device = lun->device;
|
disk_device *device = lun->device;
|
||||||
mutex_lock(&device->lock);
|
|
||||||
if (device->removed) {
|
RecursiveLocker ioLocker(device->io_lock);
|
||||||
mutex_unlock(&device->lock);
|
MutexLocker deviceLocker(device->lock);
|
||||||
|
|
||||||
|
if (device->removed)
|
||||||
return B_DEV_NOT_READY;
|
return B_DEV_NOT_READY;
|
||||||
}
|
|
||||||
|
|
||||||
status_t status;
|
status_t status;
|
||||||
if (!usb_disk_needs_bounce(lun, request)) {
|
if (!usb_disk_needs_bounce(lun, request)) {
|
||||||
@@ -1831,7 +1905,8 @@ usb_disk_io(void *cookie, io_request *request)
|
|||||||
status = usb_disk_bounced_io(lun, request);
|
status = usb_disk_bounced_io(lun, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&device->lock);
|
deviceLocker.Unlock();
|
||||||
|
ioLocker.Unlock();
|
||||||
|
|
||||||
if (request->Status() > 0)
|
if (request->Status() > 0)
|
||||||
request->SetStatusAndNotify(status);
|
request->SetStatusAndNotify(status);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ typedef struct disk_device_s {
|
|||||||
usb_device device;
|
usb_device device;
|
||||||
bool removed;
|
bool removed;
|
||||||
uint32 open_count;
|
uint32 open_count;
|
||||||
|
recursive_lock io_lock;
|
||||||
mutex lock;
|
mutex lock;
|
||||||
|
|
||||||
// IO operations
|
// IO operations
|
||||||
|
|||||||
Reference in New Issue
Block a user