winedevice: Add support for loading/unloading drivers asynchronously.
Sebastian Lackner
sebastian at fds-team.de
Tue Aug 23 03:13:42 CDT 2016
Signed-off-by: Sebastian Lackner <sebastian at fds-team.de>
---
In preparation for the final patch, this adds support for loading/unloading drivers
asynchronously. With a single driver this doesn't make a big difference, but will
be required to avoid blocking the service control handler (especially for recursive
loads). During shutdown a threadpool cleanup group is used to synchronize with any
pending load/unloads.
This patch also modifies unload_driver to ensure that we never attempt to unload a
driver without DriverUnload entry point, which could have fatal consequences. On Windows
it is required to reboot the operating system in such a case to get rid of the driver.
(Unfortunately the diff is a bit bigger because I wanted to avoid forward declarations,
but I can change that back if preferred.)
programs/winedevice/device.c | 204 ++++++++++++++++++++++++++++++++-----------
1 file changed, 152 insertions(+), 52 deletions(-)
diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c
index ac368c8..e1a38a4 100644
--- a/programs/winedevice/device.c
+++ b/programs/winedevice/device.c
@@ -2,6 +2,7 @@
* Service process to load a kernel driver
*
* Copyright 2007 Alexandre Julliard
+ * Copyright 2016 Sebastian Lackner
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -43,7 +44,9 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
extern NTSTATUS CDECL wine_ntoskrnl_main_loop( HANDLE stop_event );
static SERVICE_STATUS_HANDLE service_handle;
+static PTP_CLEANUP_GROUP cleanup_group;
static SC_HANDLE manager_handle;
+static BOOL shutdown_in_progress;
static HANDLE stop_event;
struct wine_driver
@@ -57,6 +60,15 @@ struct wine_driver
static struct wine_rb_tree wine_drivers;
+static CRITICAL_SECTION drivers_cs;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+ 0, 0, &drivers_cs,
+ { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+ 0, 0, { (DWORD_PTR)(__FILE__ ": drivers_cs") }
+};
+static CRITICAL_SECTION drivers_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
+
static void *wine_drivers_rb_alloc( size_t size )
{
return HeapAlloc( GetProcessHeap(), 0, size );
@@ -307,32 +319,116 @@ static void set_service_status( SERVICE_STATUS_HANDLE handle, DWORD state, DWORD
SetServiceStatus( handle, &status );
}
+static void WINAPI async_unload_driver( PTP_CALLBACK_INSTANCE instance, void *context )
+{
+ struct wine_driver *driver = context;
+ DRIVER_OBJECT *driver_obj = driver->driver_obj;
+ LDR_MODULE *ldr;
+
+ if (WINE_TRACE_ON(relay))
+ WINE_DPRINTF( "%04x:Call driver unload %p (obj=%p)\n", GetCurrentThreadId(),
+ driver_obj->DriverUnload, driver_obj );
+
+ driver_obj->DriverUnload( driver_obj );
+
+ if (WINE_TRACE_ON(relay))
+ WINE_DPRINTF( "%04x:Ret driver unload %p (obj=%p)\n", GetCurrentThreadId(),
+ driver_obj->DriverUnload, driver_obj );
+
+ ldr = driver_obj->DriverSection;
+ FreeLibrary( ldr->BaseAddress );
+ IoDeleteDriver( driver_obj );
+ ObDereferenceObject( driver_obj );
+
+ set_service_status( driver->handle, SERVICE_STOPPED, 0 );
+ CloseServiceHandle( (void *)driver->handle );
+ HeapFree( GetProcessHeap(), 0, driver );
+}
+
/* call the driver unload function */
-static void unload_driver( struct wine_rb_entry *entry, void *context )
+static NTSTATUS unload_driver( struct wine_rb_entry *entry, BOOL destroy )
{
+ TP_CALLBACK_ENVIRON environment;
struct wine_driver *driver = WINE_RB_ENTRY_VALUE( entry, struct wine_driver, entry );
DRIVER_OBJECT *driver_obj = driver->driver_obj;
- LDR_MODULE *ldr = driver_obj->DriverSection;
+
+ if (!driver_obj)
+ {
+ TRACE( "driver %s has not finished loading yet\n", wine_dbgstr_w(driver->name) );
+ return STATUS_UNSUCCESSFUL;
+ }
+ if (!driver_obj->DriverUnload)
+ {
+ TRACE( "driver %s does not support unloading\n", wine_dbgstr_w(driver->name) );
+ return STATUS_UNSUCCESSFUL;
+ }
TRACE( "stopping driver %s\n", wine_dbgstr_w(driver->name) );
set_service_status( driver->handle, SERVICE_STOP_PENDING, 0 );
- if (driver_obj->DriverUnload)
+ if (destroy)
{
- if (WINE_TRACE_ON(relay))
- WINE_DPRINTF( "%04x:Call driver unload %p (obj=%p)\n", GetCurrentThreadId(),
- driver_obj->DriverUnload, driver_obj );
+ async_unload_driver( NULL, driver );
+ return STATUS_SUCCESS;
+ }
- driver_obj->DriverUnload( driver_obj );
+ wine_rb_remove( &wine_drivers, driver->name );
- if (WINE_TRACE_ON(relay))
- WINE_DPRINTF( "%04x:Ret driver unload %p (obj=%p)\n", GetCurrentThreadId(),
- driver_obj->DriverUnload, driver_obj );
+ memset( &environment, 0, sizeof(environment) );
+ environment.Version = 1;
+ environment.CleanupGroup = cleanup_group;
+
+ /* don't block the service control handler */
+ if (!TrySubmitThreadpoolCallback( async_unload_driver, driver, &environment ))
+ async_unload_driver( NULL, driver );
+
+ return STATUS_SUCCESS;
+}
+
+static void WINAPI async_create_driver( PTP_CALLBACK_INSTANCE instance, void *context )
+{
+ static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
+ struct wine_driver *driver = context;
+ DRIVER_OBJECT *driver_obj;
+ UNICODE_STRING drv_name;
+ NTSTATUS status;
+ WCHAR *str;
+
+ if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver->name)*sizeof(WCHAR) )))
+ goto error;
+
+ lstrcpyW( str, driverW);
+ lstrcatW( str, driver->name );
+ RtlInitUnicodeString( &drv_name, str );
+
+ status = IoCreateDriver( &drv_name, init_driver );
+ if (status != STATUS_SUCCESS)
+ {
+ ERR( "failed to create driver %s: %08x\n", debugstr_w(driver->name), status );
+ RtlFreeUnicodeString( &drv_name );
+ goto error;
}
- FreeLibrary( ldr->BaseAddress );
- IoDeleteDriver( driver_obj );
- ObDereferenceObject( driver_obj );
+ status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL,
+ 0, NULL, KernelMode, NULL, (void **)&driver_obj );
+ RtlFreeUnicodeString( &drv_name );
+ if (status != STATUS_SUCCESS)
+ {
+ ERR( "failed to locate driver %s: %08x\n", debugstr_w(driver->name), status );
+ goto error;
+ }
+
+ EnterCriticalSection( &drivers_cs );
+ driver->driver_obj = driver_obj;
+ set_service_status( driver->handle, SERVICE_RUNNING,
+ SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
+ LeaveCriticalSection( &drivers_cs );
+ return;
+
+error:
+ EnterCriticalSection( &drivers_cs );
+ wine_rb_remove( &wine_drivers, driver->name );
+ LeaveCriticalSection( &drivers_cs );
set_service_status( driver->handle, SERVICE_STOPPED, 0 );
CloseServiceHandle( (void *)driver->handle );
@@ -342,12 +438,9 @@ static void unload_driver( struct wine_rb_entry *entry, void *context )
/* load a driver and notify services.exe about the status change */
static NTSTATUS create_driver( const WCHAR *driver_name )
{
- static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
+ TP_CALLBACK_ENVIRON environment;
struct wine_driver *driver;
- UNICODE_STRING drv_name;
- NTSTATUS status;
DWORD length;
- WCHAR *str;
length = FIELD_OFFSET( struct wine_driver, name[strlenW(driver_name) + 1] );
if (!(driver = HeapAlloc( GetProcessHeap(), 0, length )))
@@ -369,45 +462,45 @@ static NTSTATUS create_driver( const WCHAR *driver_name )
return STATUS_UNSUCCESSFUL;
}
- if (!(str = HeapAlloc( GetProcessHeap(), 0, sizeof(driverW) + strlenW(driver_name)*sizeof(WCHAR) )))
- {
- status = STATUS_NO_MEMORY;
- goto error;
- }
-
TRACE( "starting driver %s\n", wine_dbgstr_w(driver_name) );
set_service_status( driver->handle, SERVICE_START_PENDING, 0 );
- lstrcpyW( str, driverW);
- lstrcatW( str, driver_name );
- RtlInitUnicodeString( &drv_name, str );
+ memset( &environment, 0, sizeof(environment) );
+ environment.Version = 1;
+ environment.CleanupGroup = cleanup_group;
- status = IoCreateDriver( &drv_name, init_driver );
- if (status == STATUS_SUCCESS)
- {
- status = ObReferenceObjectByName( &drv_name, OBJ_CASE_INSENSITIVE, NULL,
- 0, NULL, KernelMode, NULL, (void **)&driver->driver_obj );
- if (status != STATUS_SUCCESS)
- {
- ERR("Failed to locate loaded driver (%s)\n", wine_dbgstr_w(driver_name));
- }
- }
+ /* don't block the service control handler */
+ if (!TrySubmitThreadpoolCallback( async_create_driver, driver, &environment ))
+ async_create_driver( NULL, driver );
- RtlFreeUnicodeString( &drv_name );
+ return STATUS_SUCCESS;
+}
- if (status == STATUS_SUCCESS)
- {
- set_service_status( driver->handle, SERVICE_RUNNING,
- SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN );
- return STATUS_SUCCESS;
- }
+static void wine_drivers_rb_destroy( struct wine_rb_entry *entry, void *context )
+{
+ unload_driver( entry, TRUE );
+}
-error:
- set_service_status( driver->handle, SERVICE_STOPPED, 0 );
- CloseServiceHandle( (void *)driver->handle );
- wine_rb_remove( &wine_drivers, driver_name );
- HeapFree( GetProcessHeap(), 0, driver );
- return status;
+static void WINAPI async_shutdown_drivers( PTP_CALLBACK_INSTANCE instance, void *context )
+{
+ CloseThreadpoolCleanupGroupMembers( cleanup_group, FALSE, NULL );
+
+ EnterCriticalSection( &drivers_cs );
+ wine_rb_destroy( &wine_drivers, wine_drivers_rb_destroy, NULL );
+ LeaveCriticalSection( &drivers_cs );
+
+ SetEvent( stop_event );
+}
+
+static void shutdown_drivers( void )
+{
+ if (shutdown_in_progress) return;
+
+ /* don't block the service control handler */
+ if (!TrySubmitThreadpoolCallback( async_shutdown_drivers, NULL, NULL ))
+ async_shutdown_drivers( NULL, NULL );
+
+ shutdown_in_progress = TRUE;
}
static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_data, LPVOID context )
@@ -420,7 +513,7 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_
case SERVICE_CONTROL_SHUTDOWN:
WINE_TRACE( "shutting down %s\n", wine_dbgstr_w(driver_name) );
set_service_status( service_handle, SERVICE_STOP_PENDING, 0 );
- SetEvent( stop_event );
+ shutdown_drivers();
return NO_ERROR;
default:
WINE_FIXME( "got service ctrl %x for %s\n", ctrl, wine_dbgstr_w(driver_name) );
@@ -433,22 +526,29 @@ static DWORD WINAPI service_handler( DWORD ctrl, DWORD event_type, LPVOID event_
static void WINAPI ServiceMain( DWORD argc, LPWSTR *argv )
{
const WCHAR *driver_name = argv[0];
+ NTSTATUS status;
if (wine_rb_init( &wine_drivers, &wine_drivers_rb_functions ))
return;
if (!(stop_event = CreateEventW( NULL, TRUE, FALSE, NULL )))
return;
+ if (!(cleanup_group = CreateThreadpoolCleanupGroup()))
+ return;
if (!(manager_handle = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT )))
return;
if (!(service_handle = RegisterServiceCtrlHandlerExW( driver_name, service_handler, (void *)driver_name )))
return;
- if (create_driver( driver_name ) == STATUS_SUCCESS)
+ EnterCriticalSection( &drivers_cs );
+ status = create_driver( driver_name );
+ LeaveCriticalSection( &drivers_cs );
+
+ if (status == STATUS_SUCCESS)
wine_ntoskrnl_main_loop( stop_event );
- wine_rb_destroy( &wine_drivers, unload_driver, NULL );
set_service_status( service_handle, SERVICE_STOPPED, 0 );
CloseServiceHandle( manager_handle );
+ CloseThreadpoolCleanupGroup( cleanup_group );
CloseHandle( stop_event );
}
--
2.9.0
More information about the wine-patches
mailing list