On 03/24/2018 05:16 PM, Heinrich Schuchardt wrote: > As noted in the comments, UEFI manages to combines the all of the > worst aspects of both a polling design (inefficiency and inability to > sleep until something interesting happens) and of an interrupt-driven > design (the complexity of code that could be preempted at any time, > thanks to UEFI timers). > > This causes problems in particular for UEFI USB keyboards: the > keyboard driver calls UsbAsyncInterruptTransfer() to set up a periodic > timer which is used to poll the USB bus. This poll may interrupt a > critical section within iPXE, typically resulting in list corruption > and either a hang or reboot. > > Work around this problem by mirroring the BIOS design, in which we run > with interrupts disabled almost all of the time. > > Signed-off-by: Michael Brown <mc...@ipxe.org>
Unfortunately with this patch you broke booting from iSCSI with U-Boot. Up to now U-Boot was using a timer event running at level TPL_CALLBACK to poll the network card. Of cause this timer can be changed to run at TPL_NOTIFY. But, please, do not raise the TPL level for the iPXE application any further. Best regards Heinrich Schuchardt > --- > src/interface/efi/efi_snp.c | 12 +++++++++++ > src/interface/efi/efi_timer.c | 41 ++++++++++++++++++++++++++++++------ > src/interface/efi/efi_usb.c | 48 > ++----------------------------------------- > 3 files changed, 49 insertions(+), 52 deletions(-) > > diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c > index 263a25ac..88d999bf 100644 > --- a/src/interface/efi/efi_snp.c > +++ b/src/interface/efi/efi_snp.c > @@ -45,6 +45,9 @@ static LIST_HEAD ( efi_snp_devices ); > /** Network devices are currently claimed for use by iPXE */ > static int efi_snp_claimed; > > +/** TPL prior to network devices being claimed */ > +static EFI_TPL efi_snp_old_tpl; > + > /* Downgrade user experience if configured to do so > * > * The default UEFI user experience for network boot is somewhat > @@ -1895,8 +1898,13 @@ struct efi_snp_device * last_opened_snpdev ( void ) { > * @v delta Claim count change > */ > void efi_snp_add_claim ( int delta ) { > + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; > struct efi_snp_device *snpdev; > > + /* Raise TPL if we are about to claim devices */ > + if ( ! efi_snp_claimed ) > + efi_snp_old_tpl = bs->RaiseTPL ( TPL_CALLBACK ); > + > /* Claim SNP devices */ > efi_snp_claimed += delta; > assert ( efi_snp_claimed >= 0 ); > @@ -1904,4 +1912,8 @@ void efi_snp_add_claim ( int delta ) { > /* Update SNP mode state for each interface */ > list_for_each_entry ( snpdev, &efi_snp_devices, list ) > efi_snp_set_state ( snpdev ); > + > + /* Restore TPL if we have released devices */ > + if ( ! efi_snp_claimed ) > + bs->RestoreTPL ( efi_snp_old_tpl ); > } > diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c > index 0ffe2a1a..8fe307ce 100644 > --- a/src/interface/efi/efi_timer.c > +++ b/src/interface/efi/efi_timer.c > @@ -76,11 +76,36 @@ static void efi_udelay ( unsigned long usecs ) { > * @ret ticks Current time, in ticks > */ > static unsigned long efi_currticks ( void ) { > + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; > > - /* EFI provides no clean way for device drivers to shut down > - * in preparation for handover to a booted operating system. > - * The platform firmware simply doesn't bother to call the > - * drivers' Stop() methods. Instead, drivers must register an > + /* UEFI manages to ingeniously combine the worst aspects of > + * both polling and interrupt-driven designs. There is no way > + * to support proper interrupt-driven operation, since there > + * is no way to hook in an interrupt service routine. A > + * mockery of interrupts is provided by UEFI timers, which > + * trigger at a preset rate and can fire at any time. > + * > + * We therefore have all of the downsides of a polling design > + * (inefficiency and inability to sleep until something > + * interesting happens) combined with all of the downsides of > + * an interrupt-driven design (the complexity of code that > + * could be preempted at any time). > + * > + * The UEFI specification expects us to litter the entire > + * codebase with calls to RaiseTPL() as needed for sections of > + * code that are not reentrant. Since this doesn't actually > + * gain us any substantive benefits (since even with such > + * calls we would still be suffering from the limitations of a > + * polling design), we instead choose to run at TPL_CALLBACK > + * almost all of the time, dropping to TPL_APPLICATION to > + * allow timer ticks to occur. > + * > + * > + * For added excitement, UEFI provides no clean way for device > + * drivers to shut down in preparation for handover to a > + * booted operating system. The platform firmware simply > + * doesn't bother to call the drivers' Stop() methods. > + * Instead, all non-trivial drivers must register an > * EVT_SIGNAL_EXIT_BOOT_SERVICES event to be signalled when > * ExitBootServices() is called, and clean up without any > * reference to the EFI driver model. > @@ -97,10 +122,14 @@ static unsigned long efi_currticks ( void ) { > * the API lazily assumes that the host system continues to > * travel through time in the usual direction. Work around > * EFI's violation of this assumption by falling back to a > - * simple free-running monotonic counter. > + * simple free-running monotonic counter during shutdown. > */ > - if ( efi_shutdown_in_progress ) > + if ( efi_shutdown_in_progress ) { > efi_jiffies++; > + } else { > + bs->RestoreTPL ( TPL_APPLICATION ); > + bs->RaiseTPL ( TPL_CALLBACK ); > + } > > return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) ); > } > diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c > index db8c3d34..f9c91d09 100644 > --- a/src/interface/efi/efi_usb.c > +++ b/src/interface/efi/efi_usb.c > @@ -64,50 +64,6 @@ static const char * efi_usb_direction_name ( > EFI_USB_DATA_DIRECTION direction ){ > > ****************************************************************************** > */ > > -/** > - * Poll USB bus > - * > - * @v usbdev EFI USB device > - */ > -static void efi_usb_poll ( struct efi_usb_device *usbdev ) { > - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; > - struct usb_bus *bus = usbdev->usb->port->hub->bus; > - EFI_TPL tpl; > - > - /* UEFI manages to ingeniously combine the worst aspects of > - * both polling and interrupt-driven designs. There is no way > - * to support proper interrupt-driven operation, since there > - * is no way to hook in an interrupt service routine. A > - * mockery of interrupts is provided by UEFI timers, which > - * trigger at a preset rate and can fire at any time. > - * > - * We therefore have all of the downsides of a polling design > - * (inefficiency and inability to sleep until something > - * interesting happens) combined with all of the downsides of > - * an interrupt-driven design (the complexity of code that > - * could be preempted at any time). > - * > - * The UEFI specification expects us to litter the entire > - * codebase with calls to RaiseTPL() as needed for sections of > - * code that are not reentrant. Since this doesn't actually > - * gain us any substantive benefits (since even with such > - * calls we would still be suffering from the limitations of a > - * polling design), we instead choose to wrap only calls to > - * usb_poll(). This should be sufficient for most practical > - * purposes. > - * > - * A "proper" solution would involve rearchitecting the whole > - * codebase to support interrupt-driven operation. > - */ > - tpl = bs->RaiseTPL ( TPL_NOTIFY ); > - > - /* Poll bus */ > - usb_poll ( bus ); > - > - /* Restore task priority level */ > - bs->RestoreTPL ( tpl ); > -} > - > /** > * Poll USB bus (from endpoint event timer) > * > @@ -216,7 +172,7 @@ static int efi_usb_open ( struct efi_usb_interface > *usbintf, > > /* Create event */ > if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ), > - TPL_NOTIFY, efi_usb_timer, usbep, > + TPL_CALLBACK, efi_usb_timer, usbep, > &usbep->event ) ) != 0 ) { > rc = -EEFI ( efirc ); > DBGC ( usbdev, "USBDEV %s %s could not create event: %s\n", > @@ -363,7 +319,7 @@ static int efi_usb_sync_transfer ( struct > efi_usb_interface *usbintf, > for ( i = 0 ; ( ( timeout == 0 ) || ( i < timeout ) ) ; i++ ) { > > /* Poll bus */ > - efi_usb_poll ( usbdev ); > + usb_poll ( usbdev->usb->port->hub->bus ); > > /* Check for completion */ > if ( usbep->rc != -EINPROGRESS ) { > _______________________________________________ ipxe-devel mailing list ipxe-devel@lists.ipxe.org https://lists.ipxe.org/mailman/listinfo.cgi/ipxe-devel