* Reworked userland buffer access functionality into two functions in the Transfer class to not duplicate code in each HCD
* Use these functions in UHCI and EHCI which greatly reduces overhead on fragmented transfers as the vector conversion and area cloning is only done once * Use the PrepareKernelAccess() function before submitting fragments which fixes the crash when trying to access the next fragments buffer in user memory from the kernel thread This should fix bug #1167 which could be encountered when sending data to a mass storage device with an application that uses a large enough transfer buffer size. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21059 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
562328e70a
commit
34cbbba798
@ -17,6 +17,8 @@ Transfer::Transfer(Pipe *pipe)
|
|||||||
fBaseAddress(NULL),
|
fBaseAddress(NULL),
|
||||||
fFragmented(false),
|
fFragmented(false),
|
||||||
fActualLength(0),
|
fActualLength(0),
|
||||||
|
fUserArea(-1),
|
||||||
|
fClonedArea(-1),
|
||||||
fCallback(NULL),
|
fCallback(NULL),
|
||||||
fCallbackCookie(NULL),
|
fCallbackCookie(NULL),
|
||||||
fRequestData(NULL)
|
fRequestData(NULL)
|
||||||
@ -32,6 +34,9 @@ Transfer::~Transfer()
|
|||||||
|
|
||||||
if (fVector && fVector != &fData)
|
if (fVector && fVector != &fData)
|
||||||
delete[] fVector;
|
delete[] fVector;
|
||||||
|
|
||||||
|
if (fClonedArea >= B_OK)
|
||||||
|
delete_area(fClonedArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,6 +106,72 @@ Transfer::AdvanceByFragment(size_t actualLength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Transfer::InitKernelAccess()
|
||||||
|
{
|
||||||
|
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
||||||
|
// we might need to access a buffer in userspace. this will not
|
||||||
|
// be possible in the kernel space finisher thread unless we
|
||||||
|
// get the proper area id for the space we need and then clone it
|
||||||
|
// before reading from or writing to it.
|
||||||
|
iovec *vector = fVector;
|
||||||
|
for (size_t i = 0; i < fVectorCount; i++) {
|
||||||
|
if (IS_USER_ADDRESS(vector[i].iov_base)) {
|
||||||
|
fUserArea = area_for(vector[i].iov_base);
|
||||||
|
if (fUserArea < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no user area required for access
|
||||||
|
if (fUserArea < B_OK)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
area_info areaInfo;
|
||||||
|
if (fUserArea < B_OK || get_area_info(fUserArea, &areaInfo) < B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fVectorCount; i++) {
|
||||||
|
(uint8 *)vector[i].iov_base -= (uint8 *)areaInfo.address;
|
||||||
|
|
||||||
|
if ((size_t)vector[i].iov_base > areaInfo.size
|
||||||
|
|| (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) {
|
||||||
|
TRACE_ERROR(("USB Transfer: data buffer spans across multiple areas!\n"));
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Transfer::PrepareKernelAccess()
|
||||||
|
{
|
||||||
|
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
||||||
|
// done if there is no userspace buffer or if we already cloned its area
|
||||||
|
if (fUserArea < B_OK || fClonedArea >= B_OK)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
void *clonedMemory = NULL;
|
||||||
|
// we got a userspace buffer, need to clone the area for that
|
||||||
|
// space first and map the iovecs to this cloned area.
|
||||||
|
fClonedArea = clone_area("userspace accessor", &clonedMemory,
|
||||||
|
B_ANY_ADDRESS, B_WRITE_AREA | B_KERNEL_WRITE_AREA, fUserArea);
|
||||||
|
|
||||||
|
if (fClonedArea < B_OK)
|
||||||
|
return fClonedArea;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < fVectorCount; i++)
|
||||||
|
(uint8 *)fVector[i].iov_base += (addr_t)clonedMemory;
|
||||||
|
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Transfer::SetCallback(usb_callback_func callback, void *cookie)
|
Transfer::SetCallback(usb_callback_func callback, void *cookie)
|
||||||
{
|
{
|
||||||
|
@ -533,6 +533,9 @@ public:
|
|||||||
bool IsFragmented() { return fFragmented; };
|
bool IsFragmented() { return fFragmented; };
|
||||||
void AdvanceByFragment(size_t actualLength);
|
void AdvanceByFragment(size_t actualLength);
|
||||||
|
|
||||||
|
status_t InitKernelAccess();
|
||||||
|
status_t PrepareKernelAccess();
|
||||||
|
|
||||||
void SetCallback(usb_callback_func callback,
|
void SetCallback(usb_callback_func callback,
|
||||||
void *cookie);
|
void *cookie);
|
||||||
|
|
||||||
@ -547,6 +550,8 @@ private:
|
|||||||
void *fBaseAddress;
|
void *fBaseAddress;
|
||||||
bool fFragmented;
|
bool fFragmented;
|
||||||
size_t fActualLength;
|
size_t fActualLength;
|
||||||
|
area_id fUserArea;
|
||||||
|
area_id fClonedArea;
|
||||||
|
|
||||||
usb_callback_func fCallback;
|
usb_callback_func fCallback;
|
||||||
void *fCallbackCookie;
|
void *fCallbackCookie;
|
||||||
|
@ -835,6 +835,10 @@ EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead,
|
|||||||
if (!data)
|
if (!data)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
status_t result = transfer->InitKernelAccess();
|
||||||
|
if (result < B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
data->transfer = transfer;
|
data->transfer = transfer;
|
||||||
data->queue_head = queueHead;
|
data->queue_head = queueHead;
|
||||||
data->data_descriptor = dataDescriptor;
|
data->data_descriptor = dataDescriptor;
|
||||||
@ -842,49 +846,6 @@ EHCI::AddPendingTransfer(Transfer *transfer, ehci_qh *queueHead,
|
|||||||
data->incoming = directionIn;
|
data->incoming = directionIn;
|
||||||
data->link = NULL;
|
data->link = NULL;
|
||||||
|
|
||||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
if (directionIn) {
|
|
||||||
// we might need to access a buffer in userspace. this will not
|
|
||||||
// be possible in the kernel space finisher thread unless we
|
|
||||||
// get the proper area id for the space we need and then clone it
|
|
||||||
// before writing to it. this is of course terribly inefficient...
|
|
||||||
iovec *vector = transfer->Vector();
|
|
||||||
size_t vectorCount = transfer->VectorCount();
|
|
||||||
for (size_t i = 0; i < vectorCount; i++) {
|
|
||||||
if (IS_USER_ADDRESS(vector[i].iov_base)) {
|
|
||||||
data->user_area = area_for(vector[i].iov_base);
|
|
||||||
if (data->user_area < B_OK) {
|
|
||||||
TRACE_ERROR(("usb_ehci: failed to get area of userspace buffer\n"));
|
|
||||||
delete data;
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->user_area >= B_OK) {
|
|
||||||
area_info areaInfo;
|
|
||||||
if (get_area_info(data->user_area, &areaInfo) < B_OK) {
|
|
||||||
TRACE_ERROR(("usb_ehci: failed to get info about user area\n"));
|
|
||||||
delete data;
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < vectorCount; i++) {
|
|
||||||
(uint8 *)vector[i].iov_base -= (uint8 *)areaInfo.address;
|
|
||||||
|
|
||||||
if ((size_t)vector[i].iov_base > areaInfo.size
|
|
||||||
|| (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) {
|
|
||||||
TRACE_ERROR(("usb_ehci: output data buffer spans across multiple areas!\n"));
|
|
||||||
delete data;
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
|
|
||||||
if (!Lock()) {
|
if (!Lock()) {
|
||||||
delete data;
|
delete data;
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@ -1053,36 +1014,11 @@ EHCI::FinishTransfers()
|
|||||||
// data to read out
|
// data to read out
|
||||||
iovec *vector = transfer->transfer->Vector();
|
iovec *vector = transfer->transfer->Vector();
|
||||||
size_t vectorCount = transfer->transfer->VectorCount();
|
size_t vectorCount = transfer->transfer->VectorCount();
|
||||||
|
transfer->transfer->PrepareKernelAccess();
|
||||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
area_id clonedArea = -1;
|
|
||||||
void *clonedMemory = NULL;
|
|
||||||
if (transfer->user_area >= B_OK) {
|
|
||||||
// we got a userspace output buffer, need to clone
|
|
||||||
// the area for that space first and map the iovecs
|
|
||||||
// to this cloned area.
|
|
||||||
clonedArea = clone_area("userspace accessor",
|
|
||||||
&clonedMemory, B_ANY_ADDRESS,
|
|
||||||
B_WRITE_AREA | B_KERNEL_WRITE_AREA,
|
|
||||||
transfer->user_area);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < vectorCount; i++)
|
|
||||||
(uint8 *)vector[i].iov_base += (addr_t)clonedMemory;
|
|
||||||
}
|
|
||||||
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
|
|
||||||
actualLength = ReadDescriptorChain(
|
actualLength = ReadDescriptorChain(
|
||||||
transfer->data_descriptor,
|
transfer->data_descriptor,
|
||||||
vector, vectorCount,
|
vector, vectorCount,
|
||||||
&nextDataToggle);
|
&nextDataToggle);
|
||||||
|
|
||||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
if (clonedArea >= B_OK) {
|
|
||||||
for (size_t i = 0; i < vectorCount; i++)
|
|
||||||
(uint8 *)vector[i].iov_base -= (addr_t)clonedMemory;
|
|
||||||
delete_area(clonedArea);
|
|
||||||
}
|
|
||||||
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
} else {
|
} else {
|
||||||
// calculate transfered length
|
// calculate transfered length
|
||||||
actualLength = ReadActualLength(
|
actualLength = ReadActualLength(
|
||||||
@ -1096,6 +1032,7 @@ EHCI::FinishTransfers()
|
|||||||
transfer->transfer->AdvanceByFragment(actualLength);
|
transfer->transfer->AdvanceByFragment(actualLength);
|
||||||
if (transfer->transfer->VectorLength() > 0) {
|
if (transfer->transfer->VectorLength() > 0) {
|
||||||
FreeDescriptorChain(transfer->data_descriptor);
|
FreeDescriptorChain(transfer->data_descriptor);
|
||||||
|
transfer->transfer->PrepareKernelAccess();
|
||||||
FillQueueWithData(transfer->transfer,
|
FillQueueWithData(transfer->transfer,
|
||||||
transfer->queue_head,
|
transfer->queue_head,
|
||||||
&transfer->data_descriptor, NULL);
|
&transfer->data_descriptor, NULL);
|
||||||
|
@ -668,6 +668,10 @@ UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
|
|||||||
if (!data)
|
if (!data)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
status_t result = transfer->InitKernelAccess();
|
||||||
|
if (result < B_OK)
|
||||||
|
return result;
|
||||||
|
|
||||||
data->transfer = transfer;
|
data->transfer = transfer;
|
||||||
data->queue = queue;
|
data->queue = queue;
|
||||||
data->transfer_queue = transferQueue;
|
data->transfer_queue = transferQueue;
|
||||||
@ -677,49 +681,6 @@ UHCI::AddPendingTransfer(Transfer *transfer, Queue *queue,
|
|||||||
data->incoming = directionIn;
|
data->incoming = directionIn;
|
||||||
data->link = NULL;
|
data->link = NULL;
|
||||||
|
|
||||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
if (directionIn) {
|
|
||||||
// we might need to access a buffer in userspace. this will not
|
|
||||||
// be possible in the kernel space finisher thread unless we
|
|
||||||
// get the proper area id for the space we need and then clone it
|
|
||||||
// before writing to it. this is of course terribly inefficient...
|
|
||||||
iovec *vector = transfer->Vector();
|
|
||||||
size_t vectorCount = transfer->VectorCount();
|
|
||||||
for (size_t i = 0; i < vectorCount; i++) {
|
|
||||||
if (IS_USER_ADDRESS(vector[i].iov_base)) {
|
|
||||||
data->user_area = area_for(vector[i].iov_base);
|
|
||||||
if (data->user_area < B_OK) {
|
|
||||||
TRACE_ERROR(("usb_uhci: failed to get area of userspace buffer\n"));
|
|
||||||
delete data;
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->user_area >= B_OK) {
|
|
||||||
area_info areaInfo;
|
|
||||||
if (get_area_info(data->user_area, &areaInfo) < B_OK) {
|
|
||||||
TRACE_ERROR(("usb_uhci: failed to get info about user area\n"));
|
|
||||||
delete data;
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < vectorCount; i++) {
|
|
||||||
(uint8 *)vector[i].iov_base -= (uint8 *)areaInfo.address;
|
|
||||||
|
|
||||||
if ((size_t)vector[i].iov_base > areaInfo.size
|
|
||||||
|| (size_t)vector[i].iov_base + vector[i].iov_len > areaInfo.size) {
|
|
||||||
TRACE_ERROR(("usb_uhci: output data buffer spans across multiple areas!\n"));
|
|
||||||
delete data;
|
|
||||||
return B_BAD_ADDRESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
|
|
||||||
if (!Lock()) {
|
if (!Lock()) {
|
||||||
delete data;
|
delete data;
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@ -840,35 +801,11 @@ UHCI::FinishTransfers()
|
|||||||
iovec *vector = transfer->transfer->Vector();
|
iovec *vector = transfer->transfer->Vector();
|
||||||
size_t vectorCount = transfer->transfer->VectorCount();
|
size_t vectorCount = transfer->transfer->VectorCount();
|
||||||
|
|
||||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
transfer->transfer->PrepareKernelAccess();
|
||||||
area_id clonedArea = -1;
|
|
||||||
void *clonedMemory = NULL;
|
|
||||||
if (transfer->user_area >= B_OK) {
|
|
||||||
// we got a userspace output buffer, need to clone
|
|
||||||
// the area for that space first and map the iovecs
|
|
||||||
// to this cloned area.
|
|
||||||
clonedArea = clone_area("userspace accessor",
|
|
||||||
&clonedMemory, B_ANY_ADDRESS,
|
|
||||||
B_WRITE_AREA | B_KERNEL_WRITE_AREA,
|
|
||||||
transfer->user_area);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < vectorCount; i++)
|
|
||||||
(uint8 *)vector[i].iov_base += (addr_t)clonedMemory;
|
|
||||||
}
|
|
||||||
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
|
|
||||||
actualLength = ReadDescriptorChain(
|
actualLength = ReadDescriptorChain(
|
||||||
transfer->data_descriptor,
|
transfer->data_descriptor,
|
||||||
vector, vectorCount,
|
vector, vectorCount,
|
||||||
&lastDataToggle);
|
&lastDataToggle);
|
||||||
|
|
||||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
if (clonedArea >= B_OK) {
|
|
||||||
for (size_t i = 0; i < vectorCount; i++)
|
|
||||||
(uint8 *)vector[i].iov_base -= (addr_t)clonedMemory;
|
|
||||||
delete_area(clonedArea);
|
|
||||||
}
|
|
||||||
#endif // !HAIKU_TARGET_PLATFORM_HAIKU
|
|
||||||
} else {
|
} else {
|
||||||
// read the actual length that was sent
|
// read the actual length that was sent
|
||||||
actualLength = ReadActualLength(
|
actualLength = ReadActualLength(
|
||||||
@ -886,6 +823,7 @@ UHCI::FinishTransfers()
|
|||||||
TRACE(("usb_uhci: still %ld bytes left on transfer\n", transfer->transfer->VectorLength()));
|
TRACE(("usb_uhci: still %ld bytes left on transfer\n", transfer->transfer->VectorLength()));
|
||||||
// resubmit the advanced transfer so the rest
|
// resubmit the advanced transfer so the rest
|
||||||
// of the buffers are transmitted over the bus
|
// of the buffers are transmitted over the bus
|
||||||
|
transfer->transfer->PrepareKernelAccess();
|
||||||
status_t result = CreateFilledTransfer(transfer->transfer,
|
status_t result = CreateFilledTransfer(transfer->transfer,
|
||||||
&transfer->first_descriptor,
|
&transfer->first_descriptor,
|
||||||
&transfer->transfer_queue);
|
&transfer->transfer_queue);
|
||||||
|
Loading…
Reference in New Issue
Block a user