usb: avoid pagefault during session destruction

due to pointer to object allocated in context of the session object.

Fixes #2565
This commit is contained in:
Alexander Boettcher 2017-11-05 16:36:14 +01:00 committed by Christian Helmuth
parent 03ae78173b
commit c1493b2ed2
3 changed files with 55 additions and 17 deletions

View File

@ -114,7 +114,7 @@ struct Device : List<Device>::Element
* Handle packet stream request, this way the entrypoint always returns to it's * Handle packet stream request, this way the entrypoint always returns to it's
* server loop * server loop
*/ */
class Usb::Worker class Usb::Worker : public Genode::Weak_object<Usb::Worker>
{ {
private: private:
@ -205,10 +205,27 @@ class Usb::Worker
*/ */
struct Complete_data struct Complete_data
{ {
Worker *worker; Weak_ptr<Worker> worker;
Packet_descriptor packet; Packet_descriptor packet;
Complete_data(Weak_ptr<Worker> &w, Packet_descriptor &p)
: worker(w), packet(p) { }
}; };
Complete_data * alloc_complete_data(Packet_descriptor &p)
{
void * data = kmalloc(sizeof(Complete_data), GFP_KERNEL);
construct_at<Complete_data>(data, this->weak_ptr(), p);
return reinterpret_cast<Complete_data *>(data);
}
static void free_complete_data(Complete_data *data)
{
data->packet.~Packet_descriptor();
data->worker.~Weak_ptr<Worker>();
kfree (data);
}
void _async_finish(Packet_descriptor &p, urb *urb, bool read) void _async_finish(Packet_descriptor &p, urb *urb, bool read)
{ {
if (urb->status == 0) { if (urb->status == 0) {
@ -231,9 +248,15 @@ class Usb::Worker
{ {
Complete_data *data = (Complete_data *)urb->context; Complete_data *data = (Complete_data *)urb->context;
data->worker->_async_finish(data->packet, urb, {
!!(data->packet.transfer.ep & USB_DIR_IN)); Locked_ptr<Worker> worker(data->worker);
kfree (data);
if (worker.valid())
worker->_async_finish(data->packet, urb,
!!(data->packet.transfer.ep & USB_DIR_IN));
}
free_complete_data(data);
dma_free(urb->transfer_buffer); dma_free(urb->transfer_buffer);
usb_free_urb(urb); usb_free_urb(urb);
} }
@ -261,9 +284,7 @@ class Usb::Worker
return false; return false;
} }
Complete_data *data = (Complete_data *)kmalloc(sizeof(Complete_data), GFP_KERNEL); Complete_data *data = alloc_complete_data(p);
data->packet = p;
data->worker = this;
usb_fill_bulk_urb(bulk_urb, _device->udev, pipe, buf, p.size(), usb_fill_bulk_urb(bulk_urb, _device->udev, pipe, buf, p.size(),
_async_complete, data); _async_complete, data);
@ -272,7 +293,8 @@ class Usb::Worker
if (ret != 0) { if (ret != 0) {
error("Failed to submit URB, error: ", ret); error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR; p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
kfree(data);
free_complete_data(data);
usb_free_urb(bulk_urb); usb_free_urb(bulk_urb);
dma_free(buf); dma_free(buf);
return false; return false;
@ -304,9 +326,7 @@ class Usb::Worker
return false; return false;
} }
Complete_data *data = (Complete_data *)kmalloc(sizeof(Complete_data), GFP_KERNEL); Complete_data *data = alloc_complete_data(p);
data->packet = p;
data->worker = this;
int polling_interval; int polling_interval;
@ -326,7 +346,8 @@ class Usb::Worker
if (ret != 0) { if (ret != 0) {
error("Failed to submit URB, error: ", ret); error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR; p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
kfree(data);
free_complete_data(data);
usb_free_urb(irq_urb); usb_free_urb(irq_urb);
dma_free(buf); dma_free(buf);
return false; return false;
@ -485,6 +506,11 @@ class Usb::Worker
: _sink(sink) : _sink(sink)
{ } { }
~Worker()
{
Weak_object<Worker>::lock_for_destruction();
}
void start() void start()
{ {
if (!_task) { if (!_task) {

View File

@ -45,7 +45,9 @@ set config {
</parent-provides> </parent-provides>
<default-route> <default-route>
<any-service> <parent/> <any-child/> </any-service> <any-service> <parent/> <any-child/> </any-service>
</default-route>} </default-route>
<default caps="100"/>}
append_platform_drv_config append_platform_drv_config

View File

@ -187,7 +187,7 @@ struct Usb::Block_driver : Usb::Completion,
} }
if (!p.succeded) { if (!p.succeded) {
Genode::error("init complete error: packet not succeded"); Genode::error("init complete error: packet not succeeded");
iface.release(p); iface.release(p);
return; return;
} }
@ -720,6 +720,12 @@ struct Usb::Block_driver : Usb::Completion,
/* USB device gets initialized by handle_state_change() */ /* USB device gets initialized by handle_state_change() */
} }
~Block_driver()
{
Interface &iface = device.interface(active_interface);
iface.release();
}
/** /**
* Send CBW * Send CBW
*/ */
@ -812,9 +818,13 @@ struct Usb::Main
driver = new (&alloc) Usb::Block_driver(env, alloc, sigh); driver = new (&alloc) Usb::Block_driver(env, alloc, sigh);
} }
Block::Driver *create() { return driver; } Block::Driver *create() override { return driver; }
void destroy(Block::Driver *driver) { } void destroy(Block::Driver *driver) override
{
Genode::destroy(alloc, driver);
driver = nullptr;
}
}; };
Factory factory { env, heap, announce_dispatcher }; Factory factory { env, heap, announce_dispatcher };