diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 06f9e7ae..41c59562 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -450,14 +450,20 @@ endif # Enable per-item sections and section garbage collection. Note that # some older versions of gcc support -fdata-sections but treat it as -# implying -fno-common, which would break our build. +# implying -fno-common, which would break our build. Some other older +# versions issue a spurious and uninhibitable warning if +# -ffunction-sections is used with -g, which would also break our +# build since we use -Werror. # ifeq ($(CCTYPE),gcc) DS_TEST = $(ECHO) 'char x;' | \ $(CC) -fdata-sections -S -x c - -o - 2>/dev/null | \ grep -E '\.comm' > /dev/null DS_FLAGS := $(shell $(DS_TEST) && $(ECHO) '-fdata-sections') -CFLAGS += -ffunction-sections $(DS_FLAGS) +FS_TEST = $(CC) -ffunction-sections -g -c -x c /dev/null \ + -o /dev/null 2>/dev/null +FS_FLAGS := $(shell $(FS_TEST) && $(ECHO) '-ffunction-sections') +CFLAGS += $(FS_FLAGS) $(DS_FLAGS) endif LDFLAGS += --gc-sections diff --git a/src/arch/i386/core/cmdline.c b/src/arch/i386/core/cmdline.c index fa5adb8c..595fdada 100644 --- a/src/arch/i386/core/cmdline.c +++ b/src/arch/i386/core/cmdline.c @@ -66,7 +66,8 @@ static void cmdline_init ( void ) { struct image *image = &cmdline_image; userptr_t cmdline_user; char *cmdline; - char *tmp; + char *boot_image; + char *boot_image_end; size_t len; /* Do nothing if no command line was specified */ @@ -91,9 +92,18 @@ static void cmdline_init ( void ) { /* Check for unwanted cruft in the command line */ while ( isspace ( *cmdline ) ) cmdline++; - if ( ( tmp = strstr ( cmdline, "BOOT_IMAGE=" ) ) != NULL ) { - DBGC ( image, "CMDLINE stripping \"%s\"\n", tmp ); - *tmp = '\0'; + if ( ( boot_image = strstr ( cmdline, "BOOT_IMAGE=" ) ) != NULL ) { + boot_image_end = strchr ( boot_image, ' ' ); + if ( boot_image_end ) { + *boot_image_end = '\0'; + DBGC ( image, "CMDLINE stripping \"%s\"\n", + boot_image ); + strcpy ( boot_image, ( boot_image_end + 1 ) ); + } else { + DBGC ( image, "CMDLINE stripping \"%s\"\n", + boot_image ); + *boot_image = '\0'; + } } DBGC ( image, "CMDLINE using \"%s\"\n", cmdline ); diff --git a/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/src/arch/i386/include/efi/ipxe/dhcp_arch.h index 902caff9..18417721 100644 --- a/src/arch/i386/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/efi/ipxe/dhcp_arch.h @@ -33,7 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '6', ':', \ 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_WORD ( 6 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_IA32 ) #define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) diff --git a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h index 822b3eba..a36d9cfa 100644 --- a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h +++ b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h @@ -33,7 +33,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', \ 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ) -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_WORD ( 0 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86 ) #define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ) diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index b099958c..8ce77ada 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -1333,6 +1333,20 @@ static void int13_unhook_vector ( void ) { &int13_vector ); } +/** + * Check INT13 emulated drive flow control window + * + * @v int13 Emulated drive + */ +static size_t int13_block_window ( struct int13_drive *int13 __unused ) { + + /* We are never ready to receive data via this interface. + * This prevents objects that support both block and stream + * interfaces from attempting to send us stream data. + */ + return 0; +} + /** * Handle INT 13 emulated drive underlying block device closing * @@ -1357,6 +1371,7 @@ static void int13_block_close ( struct int13_drive *int13, int rc ) { /** INT 13 drive interface operations */ static struct interface_operation int13_block_op[] = { + INTF_OP ( xfer_window, struct int13_drive *, int13_block_window ), INTF_OP ( intf_close, struct int13_drive *, int13_block_close ), }; diff --git a/src/arch/i386/interface/pxe/pxe_undi.c b/src/arch/i386/interface/pxe/pxe_undi.c index 3938207f..cf8820a0 100644 --- a/src/arch/i386/interface/pxe/pxe_undi.c +++ b/src/arch/i386/interface/pxe/pxe_undi.c @@ -652,6 +652,7 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { const void *ll_dest; const void *ll_source; uint16_t net_proto; + unsigned int flags; size_t ll_hlen; struct net_protocol *net_protocol; unsigned int prottype; @@ -753,7 +754,8 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { /* Strip link-layer header */ ll_protocol = pxe_netdev->ll_protocol; if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest, - &ll_source, &net_proto )) !=0){ + &ll_source, &net_proto, + &flags ) ) != 0 ) { /* Assume unknown net_proto and no ll_source */ net_proto = 0; ll_source = NULL; @@ -788,14 +790,12 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { undi_isr->Frame.segment = rm_ds; undi_isr->Frame.offset = __from_data16 ( basemem_packet ); undi_isr->ProtType = prottype; - if ( memcmp ( ll_dest, pxe_netdev->ll_addr, - ll_protocol->ll_addr_len ) == 0 ) { - undi_isr->PktType = P_DIRECTED; - } else if ( memcmp ( ll_dest, pxe_netdev->ll_broadcast, - ll_protocol->ll_addr_len ) == 0 ) { + if ( flags & LL_BROADCAST ) { undi_isr->PktType = P_BROADCAST; - } else { + } else if ( flags & LL_MULTICAST ) { undi_isr->PktType = P_MULTICAST; + } else { + undi_isr->PktType = P_DIRECTED; } DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d", undi_isr->Frame.segment, undi_isr->Frame.offset, diff --git a/src/arch/i386/prefix/lkrnprefix.S b/src/arch/i386/prefix/lkrnprefix.S index f87ef85a..338ffa3d 100644 --- a/src/arch/i386/prefix/lkrnprefix.S +++ b/src/arch/i386/prefix/lkrnprefix.S @@ -160,7 +160,7 @@ relocatable_kernel: pad2: .byte 0, 0, 0 cmdline_size: - .long 0 + .long 0x7ff hardware_subarch: .long 0 hardware_subarch_data: diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S index aa3465b2..c85e563d 100644 --- a/src/arch/i386/prefix/romprefix.S +++ b/src/arch/i386/prefix/romprefix.S @@ -445,7 +445,7 @@ get_pmm: /* Preserve registers */ pushl %eax pushw %di - movw $' ', %di + movw $( ' ' ), %di get_pmm_find: /* Try to find existing block */ pushl %ebx /* PMM handle */ @@ -474,7 +474,7 @@ get_pmm_allocate: pushw %dx pushw %ax popl %esi - movw $'+', %di /* Indicate allocation attempt */ + movw $( '+' ), %di /* Indicate allocation attempt */ testl %esi, %esi jnz get_pmm_done stc diff --git a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h index af41b195..9a4790fd 100644 --- a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h +++ b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h @@ -30,10 +30,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define DHCP_ARCH_VENDOR_CLASS_ID \ DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', \ - 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '7', ':', \ + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '9', ':', \ 'U', 'N', 'D', 'I', ':', '0', '0', '3', '0', '1', '0' ) -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_WORD ( 7 ) +#define DHCP_ARCH_CLIENT_ARCHITECTURE \ + DHCP_WORD ( DHCP_CLIENT_ARCHITECTURE_X86_64 ) #define DHCP_ARCH_CLIENT_NDI DHCP_OPTION ( 1 /* UNDI */ , 3, 10 /* v3.10 */ ) diff --git a/src/core/hw.c b/src/core/hw.c index aca55809..91736a65 100644 --- a/src/core/hw.c +++ b/src/core/hw.c @@ -26,15 +26,7 @@ static void hw_finished ( struct hw *hw, int rc ) { process_del ( &hw->process ); } -static struct interface_operation hw_xfer_operations[] = { - INTF_OP ( intf_close, struct hw *, hw_finished ), -}; - -static struct interface_descriptor hw_xfer_desc = - INTF_DESC ( struct hw, xfer, hw_xfer_operations ); - -static void hw_step ( struct process *process ) { - struct hw *hw = container_of ( process, struct hw, process ); +static void hw_step ( struct hw *hw ) { int rc; if ( xfer_window ( &hw->xfer ) ) { @@ -43,6 +35,17 @@ static void hw_step ( struct process *process ) { } } +static struct interface_operation hw_xfer_operations[] = { + INTF_OP ( xfer_window_changed, struct hw *, hw_step ), + INTF_OP ( intf_close, struct hw *, hw_finished ), +}; + +static struct interface_descriptor hw_xfer_desc = + INTF_DESC ( struct hw, xfer, hw_xfer_operations ); + +static struct process_descriptor hw_process_desc = + PROC_DESC_ONCE ( struct hw, process, hw_step ); + static int hw_open ( struct interface *xfer, struct uri *uri __unused ) { struct hw *hw; @@ -52,7 +55,7 @@ static int hw_open ( struct interface *xfer, struct uri *uri __unused ) { return -ENOMEM; ref_init ( &hw->refcnt, NULL ); intf_init ( &hw->xfer, &hw_xfer_desc, &hw->refcnt ); - process_init ( &hw->process, hw_step, &hw->refcnt ); + process_init ( &hw->process, &hw_process_desc, &hw->refcnt ); /* Attach parent interface, mortalise self, and return */ intf_plug_plug ( &hw->xfer, xfer ); diff --git a/src/core/process.c b/src/core/process.c index a3297856..4a705ef6 100644 --- a/src/core/process.c +++ b/src/core/process.c @@ -33,6 +33,16 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** Process run queue */ static LIST_HEAD ( run_queue ); +/** + * Get pointer to object containing process + * + * @v process Process + * @ret object Containing object + */ +void * process_object ( struct process *process ) { + return ( ( ( void * ) process ) - process->desc->offset ); +} + /** * Add process to process list * @@ -43,13 +53,13 @@ static LIST_HEAD ( run_queue ); */ void process_add ( struct process *process ) { if ( ! process_running ( process ) ) { - DBGC ( process, "PROCESS %p (%p) starting\n", - process, process->step ); + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " starting\n", PROC_DBG ( process ) ); ref_get ( process->refcnt ); list_add_tail ( &process->list, &run_queue ); } else { - DBGC ( process, "PROCESS %p (%p) already started\n", - process, process->step ); + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " already started\n", PROC_DBG ( process ) ); } } @@ -63,14 +73,14 @@ void process_add ( struct process *process ) { */ void process_del ( struct process *process ) { if ( process_running ( process ) ) { - DBGC ( process, "PROCESS %p (%p) stopping\n", - process, process->step ); + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " stopping\n", PROC_DBG ( process ) ); list_del ( &process->list ); INIT_LIST_HEAD ( &process->list ); ref_put ( process->refcnt ); } else { - DBGC ( process, "PROCESS %p (%p) already stopped\n", - process, process->step ); + DBGC ( PROC_COL ( process ), "PROCESS " PROC_FMT + " already stopped\n", PROC_DBG ( process ) ); } } @@ -82,17 +92,25 @@ void process_del ( struct process *process ) { */ void step ( void ) { struct process *process; + struct process_descriptor *desc; + void *object; if ( ( process = list_first_entry ( &run_queue, struct process, list ) ) ) { - list_del ( &process->list ); - list_add_tail ( &process->list, &run_queue ); ref_get ( process->refcnt ); /* Inhibit destruction mid-step */ - DBGC2 ( process, "PROCESS %p (%p) executing\n", - process, process->step ); - process->step ( process ); - DBGC2 ( process, "PROCESS %p (%p) finished executing\n", - process, process->step ); + desc = process->desc; + object = process_object ( process ); + if ( desc->reschedule ) { + list_del ( &process->list ); + list_add_tail ( &process->list, &run_queue ); + } else { + process_del ( process ); + } + DBGC2 ( PROC_COL ( process ), "PROCESS " PROC_FMT + " executing\n", PROC_DBG ( process ) ); + desc->step ( object ); + DBGC2 ( PROC_COL ( process ), "PROCESS " PROC_FMT + " finished executing\n", PROC_DBG ( process ) ); ref_put ( process->refcnt ); /* Allow destruction */ } } diff --git a/src/core/resolv.c b/src/core/resolv.c index 91f0c15c..31e80aab 100644 --- a/src/core/resolv.c +++ b/src/core/resolv.c @@ -86,16 +86,16 @@ struct numeric_resolv { int rc; }; -static void numeric_step ( struct process *process ) { - struct numeric_resolv *numeric = - container_of ( process, struct numeric_resolv, process ); +static void numeric_step ( struct numeric_resolv *numeric ) { - process_del ( process ); if ( numeric->rc == 0 ) resolv_done ( &numeric->resolv, &numeric->sa ); intf_shutdown ( &numeric->resolv, numeric->rc ); } +static struct process_descriptor numeric_process_desc = + PROC_DESC_ONCE ( struct numeric_resolv, process, numeric_step ); + static int numeric_resolv ( struct interface *resolv, const char *name, struct sockaddr *sa ) { struct numeric_resolv *numeric; @@ -107,7 +107,8 @@ static int numeric_resolv ( struct interface *resolv, return -ENOMEM; ref_init ( &numeric->refcnt, NULL ); intf_init ( &numeric->resolv, &null_intf_desc, &numeric->refcnt ); - process_init ( &numeric->process, numeric_step, &numeric->refcnt ); + process_init ( &numeric->process, &numeric_process_desc, + &numeric->refcnt ); memcpy ( &numeric->sa, sa, sizeof ( numeric->sa ) ); DBGC ( numeric, "NUMERIC %p attempting to resolve \"%s\"\n", diff --git a/src/core/vsprintf.c b/src/core/vsprintf.c index b46d9c41..b838b89e 100644 --- a/src/core/vsprintf.c +++ b/src/core/vsprintf.c @@ -60,12 +60,21 @@ static uint8_t type_sizes[] = { */ #define ALT_FORM 0x02 +/** + * Use zero padding + * + * Note that this value is set to 0x10 since that allows the pad + * character to be calculated as @c 0x20|(flags&ZPAD) + */ +#define ZPAD 0x10 + /** * Format a hexadecimal number * * @v end End of buffer to contain number * @v num Number to format * @v width Minimum field width + * @v flags Format flags * @ret ptr End of buffer * * Fills a buffer in reverse order with a formatted hexadecimal @@ -79,18 +88,18 @@ static uint8_t type_sizes[] = { static char * format_hex ( char *end, unsigned long long num, int width, int flags ) { char *ptr = end; - int case_mod; + int case_mod = ( flags & LCASE ); + int pad = ( ( flags & ZPAD ) | ' ' ); /* Generate the number */ - case_mod = flags & LCASE; do { *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod; num >>= 4; } while ( num ); - /* Zero-pad to width */ + /* Pad to width */ while ( ( end - ptr ) < width ) - *(--ptr) = '0'; + *(--ptr) = pad; /* Add "0x" or "0X" if alternate form specified */ if ( flags & ALT_FORM ) { @@ -107,6 +116,7 @@ static char * format_hex ( char *end, unsigned long long num, int width, * @v end End of buffer to contain number * @v num Number to format * @v width Minimum field width + * @v flags Format flags * @ret ptr End of buffer * * Fills a buffer in reverse order with a formatted decimal number. @@ -115,9 +125,12 @@ static char * format_hex ( char *end, unsigned long long num, int width, * There must be enough space in the buffer to contain the largest * number that this function can format. */ -static char * format_decimal ( char *end, signed long num, int width ) { +static char * format_decimal ( char *end, signed long num, int width, + int flags ) { char *ptr = end; int negative = 0; + int zpad = ( flags & ZPAD ); + int pad = ( zpad | ' ' ); /* Generate the number */ if ( num < 0 ) { @@ -130,12 +143,16 @@ static char * format_decimal ( char *end, signed long num, int width ) { } while ( num ); /* Add "-" if necessary */ - if ( negative ) + if ( negative && ( ! zpad ) ) *(--ptr) = '-'; - /* Space-pad to width */ + /* Pad to width */ while ( ( end - ptr ) < width ) - *(--ptr) = ' '; + *(--ptr) = pad; + + /* Add "-" if necessary */ + if ( negative && zpad ) + *ptr = '-'; return ptr; } @@ -186,7 +203,7 @@ size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) { if ( *fmt == '#' ) { flags |= ALT_FORM; } else if ( *fmt == '0' ) { - /* We always 0-pad hex and space-pad decimal */ + flags |= ZPAD; } else { /* End of flag characters */ break; @@ -250,7 +267,7 @@ size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) { } else { decimal = va_arg ( args, signed int ); } - ptr = format_decimal ( ptr, decimal, width ); + ptr = format_decimal ( ptr, decimal, width, flags ); } else { *(--ptr) = *fmt; } diff --git a/src/core/xfer.c b/src/core/xfer.c index a755d438..4d7d6b43 100644 --- a/src/core/xfer.c +++ b/src/core/xfer.c @@ -54,6 +54,7 @@ static struct xfer_metadata dummy_metadata; * @ret rc Return status code */ int xfer_vredirect ( struct interface *intf, int type, va_list args ) { + struct interface tmp = INTF_INIT ( null_intf_desc ); struct interface *dest; xfer_vredirect_TYPE ( void * ) *op = intf_get_dest_op_no_passthru ( intf, xfer_vredirect, &dest ); @@ -66,8 +67,22 @@ int xfer_vredirect ( struct interface *intf, int type, va_list args ) { if ( op ) { rc = op ( object, type, args ); } else { - /* Default is to reopen the interface as instructed */ + /* Default is to reopen the interface as instructed, + * then send xfer_window_changed() messages to both + * new child and parent interfaces. Since our + * original child interface is likely to be closed and + * unplugged as a result of the call to + * xfer_vreopen(), we create a temporary interface in + * order to be able to send xfer_window_changed() to + * the parent. + */ + intf_plug ( &tmp, dest ); rc = xfer_vreopen ( dest, type, args ); + if ( rc == 0 ) { + xfer_window_changed ( dest ); + xfer_window_changed ( &tmp ); + } + intf_unplug ( &tmp ); } if ( rc != 0 ) { diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index d1416513..fb90fbce 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -218,8 +218,10 @@ struct scsi_device { /** SCSI device flags */ enum scsi_device_flags { - /** Unit is ready */ - SCSIDEV_UNIT_READY = 0x0001, + /** TEST UNIT READY has been issued */ + SCSIDEV_UNIT_TESTED = 0x0001, + /** TEST UNIT READY has completed successfully */ + SCSIDEV_UNIT_READY = 0x0002, }; /** A SCSI command */ @@ -892,23 +894,25 @@ static struct interface_descriptor scsidev_ready_desc = /** * SCSI TEST UNIT READY process * - * @v process Process + * @v scsidev SCSI device */ -static void scsidev_step ( struct process *process ) { - struct scsi_device *scsidev = - container_of ( process, struct scsi_device, process ); +static void scsidev_step ( struct scsi_device *scsidev ) { int rc; + /* Do nothing if we have already issued TEST UNIT READY */ + if ( scsidev->flags & SCSIDEV_UNIT_TESTED ) + return; + /* Wait until underlying SCSI device is ready */ if ( xfer_window ( &scsidev->scsi ) == 0 ) return; - /* Stop process */ - process_del ( &scsidev->process ); - DBGC ( scsidev, "SCSI %p waiting for unit to become ready\n", scsidev ); + /* Mark TEST UNIT READY as sent */ + scsidev->flags |= SCSIDEV_UNIT_TESTED; + /* Issue TEST UNIT READY command */ if ( ( rc = scsidev_test_unit_ready ( scsidev, &scsidev->ready )) !=0){ scsidev_close ( scsidev, rc ); @@ -918,6 +922,7 @@ static void scsidev_step ( struct process *process ) { /** SCSI device SCSI interface operations */ static struct interface_operation scsidev_scsi_op[] = { + INTF_OP ( xfer_window_changed, struct scsi_device *, scsidev_step ), INTF_OP ( intf_close, struct scsi_device *, scsidev_close ), }; @@ -926,6 +931,10 @@ static struct interface_descriptor scsidev_scsi_desc = INTF_DESC_PASSTHRU ( struct scsi_device, scsi, scsidev_scsi_op, block ); +/** SCSI device process descriptor */ +static struct process_descriptor scsidev_process_desc = + PROC_DESC_ONCE ( struct scsi_device, process, scsidev_step ); + /** * Open SCSI device * @@ -946,7 +955,8 @@ int scsi_open ( struct interface *block, struct interface *scsi, intf_init ( &scsidev->block, &scsidev_block_desc, &scsidev->refcnt ); intf_init ( &scsidev->scsi, &scsidev_scsi_desc, &scsidev->refcnt ); intf_init ( &scsidev->ready, &scsidev_ready_desc, &scsidev->refcnt ); - process_init ( &scsidev->process, scsidev_step, &scsidev->refcnt ); + process_init ( &scsidev->process, &scsidev_process_desc, + &scsidev->refcnt ); INIT_LIST_HEAD ( &scsidev->cmds ); memcpy ( &scsidev->lun, lun, sizeof ( scsidev->lun ) ); DBGC ( scsidev, "SCSI %p created for LUN " SCSI_LUN_FORMAT "\n", diff --git a/src/drivers/net/igb/igb_osdep.h b/src/drivers/net/igb/igb_osdep.h index 167b0f85..84f490f5 100644 --- a/src/drivers/net/igb/igb_osdep.h +++ b/src/drivers/net/igb/igb_osdep.h @@ -69,11 +69,6 @@ typedef enum { #define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE #define ETH_ADDR_LEN ETH_ALEN -#ifdef __BIG_ENDIAN -#define E1000_BIG_ENDIAN __BIG_ENDIAN -#endif - - #define DEBUGOUT(S) if (0) { printf(S); } #define DEBUGOUT1(S, A...) if (0) { printf(S, A); } diff --git a/src/drivers/net/ipoib.c b/src/drivers/net/ipoib.c index 4917b58e..bc71a456 100644 --- a/src/drivers/net/ipoib.c +++ b/src/drivers/net/ipoib.c @@ -224,11 +224,13 @@ static int ipoib_push ( struct net_device *netdev __unused, * @ret ll_dest Link-layer destination address * @ret ll_source Source link-layer address * @ret net_proto Network-layer protocol, in network-byte order + * @ret flags Packet flags * @ret rc Return status code */ static int ipoib_pull ( struct net_device *netdev, struct io_buffer *iobuf, const void **ll_dest, - const void **ll_source, uint16_t *net_proto ) { + const void **ll_source, uint16_t *net_proto, + unsigned int *flags ) { struct ipoib_device *ipoib = netdev->priv; struct ipoib_hdr *ipoib_hdr = iobuf->data; struct ipoib_peer *dest; @@ -255,6 +257,7 @@ static int ipoib_pull ( struct net_device *netdev, *ll_dest = ( dest ? &dest->mac : &ipoib->broadcast ); *ll_source = ( source ? &source->mac : &ipoib->broadcast ); *net_proto = ipoib_hdr->proto; + *flags = ( ( *ll_dest == &ipoib->broadcast ) ? LL_BROADCAST : 0 ); return 0; } diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index 148e3d66..dbca8e63 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -240,6 +240,39 @@ struct dhcp_client_id { /** Client system architecture */ #define DHCP_CLIENT_ARCHITECTURE 93 +/** DHCP client architecture */ +struct dhcp_client_architecture { + uint16_t arch; +} __attribute__ (( packed )); + +/** DHCP client architecture values + * + * These are defined by the PXE specification and redefined by + * RFC4578. + */ +enum dhcp_client_architecture_values { + /** Intel x86 PC */ + DHCP_CLIENT_ARCHITECTURE_X86 = 0x0000, + /** NEC/PC98 */ + DHCP_CLIENT_ARCHITECTURE_PC98 = 0x0001, + /** EFI Itanium */ + DHCP_CLIENT_ARCHITECTURE_IA64 = 0x0002, + /** DEC Alpha */ + DHCP_CLIENT_ARCHITECTURE_ALPHA = 0x0003, + /** Arc x86 */ + DHCP_CLIENT_ARCHITECTURE_ARCX86 = 0x0004, + /** Intel Lean Client */ + DHCP_CLIENT_ARCHITECTURE_LC = 0x0005, + /** EFI IA32 */ + DHCP_CLIENT_ARCHITECTURE_IA32 = 0x0006, + /** EFI BC */ + DHCP_CLIENT_ARCHITECTURE_EFI = 0x0007, + /** EFI Xscale */ + DHCP_CLIENT_ARCHITECTURE_XSCALE = 0x0008, + /** EFI x86-64 */ + DHCP_CLIENT_ARCHITECTURE_X86_64 = 0x0009, +}; + /** Client network device interface */ #define DHCP_CLIENT_NDI 94 diff --git a/src/include/ipxe/fc.h b/src/include/ipxe/fc.h index ab6b5bae..6689f394 100644 --- a/src/include/ipxe/fc.h +++ b/src/include/ipxe/fc.h @@ -357,7 +357,15 @@ struct fc_peer { /** List of upper-layer protocols */ struct list_head ulps; - /** Active usage count */ + /** Active usage count + * + * A peer (and attached ULPs) may be created in response to + * unsolicited login requests received via the fabric. We + * track our own active usage count independently of the + * existence of the peer, so that if the peer becomes logged + * out (e.g. due to a link failure) then we know whether or + * not we should attempt to relogin. + */ unsigned int usage; }; @@ -424,8 +432,15 @@ struct fc_ulp { /** Service parameter length */ size_t param_len; - /** Active usage count */ - unsigned int usage; + /** Active users of this upper-layer protocol + * + * As with peers, an upper-layer protocol may be created in + * response to an unsolicited login request received via the + * fabric. This list records the number of active users of + * the ULP; the number of entries in the list is equivalent to + * the peer usage count. + */ + struct list_head users; }; /** Fibre Channel upper-layer protocol flags */ @@ -434,6 +449,21 @@ enum fc_ulp_flags { FC_ULP_ORIGINATED_LOGIN_OK = 0x0001, }; +/** A Fibre Channel upper-layer protocol user */ +struct fc_ulp_user { + /** Fibre Channel upper layer protocol */ + struct fc_ulp *ulp; + /** List of users */ + struct list_head list; + /** Containing object reference count, or NULL */ + struct refcnt *refcnt; + /** Examine link state + * + * @v user Fibre Channel upper-layer-protocol user + */ + void ( * examine ) ( struct fc_ulp_user *user ); +}; + /** * Get reference to Fibre Channel upper-layer protocol * @@ -456,14 +486,51 @@ fc_ulp_put ( struct fc_ulp *ulp ) { ref_put ( &ulp->refcnt ); } +/** + * Get reference to Fibre Channel upper-layer protocol user + * + * @v user Fibre Channel upper-layer protocol user + * @ret user Fibre Channel upper-layer protocol user + */ +static inline __attribute__ (( always_inline )) struct fc_ulp_user * +fc_ulp_user_get ( struct fc_ulp_user *user ) { + ref_get ( user->refcnt ); + return user; +} + +/** + * Drop reference to Fibre Channel upper-layer protocol user + * + * @v user Fibre Channel upper-layer protocol user + */ +static inline __attribute__ (( always_inline )) void +fc_ulp_user_put ( struct fc_ulp_user *user ) { + ref_put ( user->refcnt ); +} + +/** + * Initialise Fibre Channel upper-layer protocol user + * + * @v user Fibre Channel upper-layer protocol user + * @v examine Examine link state method + * @v refcnt Containing object reference count, or NULL + */ +static inline __attribute__ (( always_inline )) void +fc_ulp_user_init ( struct fc_ulp_user *user, + void ( * examine ) ( struct fc_ulp_user *user ), + struct refcnt *refcnt ) { + user->examine = examine; + user->refcnt = refcnt; +} + extern struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn, unsigned int type ); extern struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port, const struct fc_port_id *peer_port_id, unsigned int type ); -extern void fc_ulp_increment ( struct fc_ulp *ulp ); -extern void fc_ulp_decrement ( struct fc_ulp *ulp ); +extern void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ); +extern void fc_ulp_detach ( struct fc_ulp_user *user ); extern int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len, int originated ); extern void fc_ulp_logout ( struct fc_ulp *ulp, int rc ); diff --git a/src/include/ipxe/ip.h b/src/include/ipxe/ip.h index 3f3dc1f7..4366d9ab 100644 --- a/src/include/ipxe/ip.h +++ b/src/include/ipxe/ip.h @@ -31,9 +31,6 @@ struct io_buffer; #define IP_TOS 0 #define IP_TTL 64 -#define IP_FRAG_IOB_SIZE 1500 -#define IP_FRAG_TIMEOUT 50 - /** An IPv4 packet header */ struct iphdr { uint8_t verhdrlen; @@ -73,20 +70,16 @@ struct ipv4_miniroute { struct in_addr gateway; }; -/* Fragment reassembly buffer */ -struct frag_buffer { - /* Identification number */ - uint16_t ident; - /* Source network address */ - struct in_addr src; - /* Destination network address */ - struct in_addr dest; - /* Reassembled I/O buffer */ - struct io_buffer *frag_iob; - /* Reassembly timer */ - struct retry_timer frag_timer; +/* IPv4 fragment reassembly buffer */ +struct ipv4_fragment { /* List of fragment reassembly buffers */ struct list_head list; + /** Reassembled packet */ + struct io_buffer *iobuf; + /** Current offset */ + size_t offset; + /** Reassembly timer */ + struct retry_timer timer; }; extern struct list_head ipv4_miniroutes; diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index e49191f4..3633a165 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -66,20 +66,23 @@ struct net_protocol { /** * Process received packet * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_dest Link-layer destination address + * @v ll_source Link-layer source address + * @v flags Packet flags + * @ret rc Return status code * * This method takes ownership of the I/O buffer. */ int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev, - const void *ll_dest, const void *ll_source ); + const void *ll_dest, const void *ll_source, + unsigned int flags ); /** * Transcribe network-layer address * - * @v net_addr Network-layer address - * @ret string Human-readable transcription of address + * @v net_addr Network-layer address + * @ret string Human-readable transcription of address * * This method should convert the network-layer address into a * human-readable format (e.g. dotted quad notation for IPv4). @@ -97,6 +100,12 @@ struct net_protocol { uint8_t net_addr_len; }; +/** Packet is a multicast (including broadcast) packet */ +#define LL_MULTICAST 0x0001 + +/** Packet is a broadcast packet */ +#define LL_BROADCAST 0x0002 + /** * A link-layer protocol * @@ -125,11 +134,12 @@ struct ll_protocol { * @ret ll_dest Link-layer destination address * @ret ll_source Source link-layer address * @ret net_proto Network-layer protocol, in network-byte order + * @ret flags Packet flags * @ret rc Return status code */ int ( * pull ) ( struct net_device *netdev, struct io_buffer *iobuf, const void **ll_dest, const void **ll_source, - uint16_t *net_proto ); + uint16_t *net_proto, unsigned int *flags ); /** * Initialise link-layer address * @@ -586,6 +596,8 @@ netdev_rx_frozen ( struct net_device *netdev ) { extern void netdev_link_err ( struct net_device *netdev, int rc ); extern void netdev_link_down ( struct net_device *netdev ); extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ); +extern void netdev_tx_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ); extern void netdev_tx_complete_err ( struct net_device *netdev, struct io_buffer *iobuf, int rc ); extern void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ); @@ -609,7 +621,7 @@ extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, const void *ll_source ); extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, uint16_t net_proto, const void *ll_dest, - const void *ll_source ); + const void *ll_source, unsigned int flags ); extern void net_poll ( void ); /** diff --git a/src/include/ipxe/process.h b/src/include/ipxe/process.h index 45c2af63..9b757981 100644 --- a/src/include/ipxe/process.h +++ b/src/include/ipxe/process.h @@ -17,6 +17,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct process { /** List of processes */ struct list_head list; + /** Process descriptor */ + struct process_descriptor *desc; + /** Reference counter + * + * If this process is not part of a reference-counted object, + * this field may be NULL. + */ + struct refcnt *refcnt; +}; + +/** A process descriptor */ +struct process_descriptor { + /** Offset of process within containing object */ + size_t offset; /** * Single-step the process * @@ -24,15 +38,81 @@ struct process { * Returning from this method is isomorphic to yielding the * CPU to another process. */ - void ( * step ) ( struct process *process ); - /** Reference counter - * - * If this interface is not part of a reference-counted - * object, this field may be NULL. - */ - struct refcnt *refcnt; + void ( * step ) ( void *object ); + /** Automatically reschedule the process */ + int reschedule; }; +/** + * Define a process step() method + * + * @v object_type Implementing method's expected object type + * @v step Implementing method + * @ret step Process step method + */ +#define PROC_STEP( object_type, step ) \ + ( ( ( ( typeof ( step ) * ) NULL ) == \ + ( ( void ( * ) ( object_type *object ) ) NULL ) ) ? \ + ( void ( * ) ( void *object ) ) step : \ + ( void ( * ) ( void *object ) ) step ) + +/** + * Calculate offset of process within containing object + * + * @v object_type Containing object data type + * @v name Process name (i.e. field within object data type) + * @ret offset Offset of process within containing object + */ +#define process_offset( object_type, name ) \ + ( ( ( ( typeof ( ( ( object_type * ) NULL )->name ) * ) NULL ) \ + == ( ( struct process * ) NULL ) ) \ + ? offsetof ( object_type, name ) \ + : offsetof ( object_type, name ) ) + +/** + * Define a process descriptor + * + * @v object_type Containing object data type + * @v process Process name (i.e. field within object data type) + * @v step Process' step() method + * @ret desc Object interface descriptor + */ +#define PROC_DESC( object_type, process, _step ) { \ + .offset = process_offset ( object_type, process ), \ + .step = PROC_STEP ( object_type, _step ), \ + .reschedule = 1, \ + } + +/** + * Define a process descriptor for a process that runs only once + * + * @v object_type Containing object data type + * @v process Process name (i.e. field within object data type) + * @v step Process' step() method + * @ret desc Object interface descriptor + */ +#define PROC_DESC_ONCE( object_type, process, _step ) { \ + .offset = process_offset ( object_type, process ), \ + .step = PROC_STEP ( object_type, _step ), \ + .reschedule = 0, \ + } + +/** + * Define a process descriptor for a pure process + * + * A pure process is a process that does not have a containing object. + * + * @v step Process' step() method + * @ret desc Object interface descriptor + */ +#define PROC_DESC_PURE( _step ) { \ + .offset = 0, \ + .step = PROC_STEP ( struct process, _step ), \ + .reschedule = 1, \ + } + +extern void * __attribute__ (( pure )) +process_object ( struct process *process ); extern void process_add ( struct process *process ); extern void process_del ( struct process *process ); extern void step ( void ); @@ -41,14 +121,15 @@ extern void step ( void ); * Initialise process without adding to process list * * @v process Process - * @v step Process' step() method + * @v desc Process descriptor + * @v refcnt Containing object reference count, or NULL */ static inline __attribute__ (( always_inline )) void process_init_stopped ( struct process *process, - void ( * step ) ( struct process *process ), + struct process_descriptor *desc, struct refcnt *refcnt ) { INIT_LIST_HEAD ( &process->list ); - process->step = step; + process->desc = desc; process->refcnt = refcnt; } @@ -56,13 +137,14 @@ process_init_stopped ( struct process *process, * Initialise process and add to process list * * @v process Process - * @v step Process' step() method + * @v desc Process descriptor + * @v refcnt Containing object reference count, or NULL */ static inline __attribute__ (( always_inline )) void process_init ( struct process *process, - void ( * step ) ( struct process *process ), + struct process_descriptor *desc, struct refcnt *refcnt ) { - process_init_stopped ( process, step, refcnt ); + process_init_stopped ( process, desc, refcnt ); process_add ( process ); } @@ -88,4 +170,36 @@ process_running ( struct process *process ) { */ #define __permanent_process __table_entry ( PERMANENT_PROCESSES, 01 ) +/** Define a permanent process + * + */ +#define PERMANENT_PROCESS( name, step ) \ +struct process_descriptor name ## _desc = PROC_DESC_PURE ( step ); \ +struct process name __permanent_process = { \ + .list = LIST_HEAD_INIT ( name.list ), \ + .desc = & name ## _desc, \ + .refcnt = NULL, \ +}; + +/** + * Find debugging colourisation for a process + * + * @v process Process + * @ret col Debugging colourisation + * + * Use as the first argument to DBGC() or equivalent macro. + */ +#define PROC_COL( process ) process_object ( process ) + +/** printf() format string for PROC_DBG() */ +#define PROC_FMT "%p+%zx" + +/** + * printf() arguments for representing a process + * + * @v process Process + * @ret args printf() argument list corresponding to PROC_FMT + */ +#define PROC_DBG( process ) process_object ( process ), (process)->desc->offset + #endif /* _IPXE_PROCESS_H */ diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index bc6c0919..4c499826 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -658,6 +658,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, const void *iob_ll_dest; const void *iob_ll_src; uint16_t iob_net_proto; + unsigned int iob_flags; int rc; EFI_STATUS efirc; @@ -682,7 +683,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, /* Attempt to decode link-layer header */ if ( ( rc = ll_protocol->pull ( snpdev->netdev, iobuf, &iob_ll_dest, - &iob_ll_src, &iob_net_proto ) ) != 0 ){ + &iob_ll_src, &iob_net_proto, + &iob_flags ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not parse header: %s\n", snpdev, strerror ( rc ) ); efirc = RC_TO_EFIRC ( rc ); diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c index f5ab65f0..c00363cd 100644 --- a/src/net/80211/net80211.c +++ b/src/net/80211/net80211.c @@ -135,7 +135,8 @@ static int net80211_ll_push ( struct net_device *netdev, const void *ll_source, uint16_t net_proto ); static int net80211_ll_pull ( struct net_device *netdev, struct io_buffer *iobuf, const void **ll_dest, - const void **ll_source, uint16_t * net_proto ); + const void **ll_source, uint16_t * net_proto, + unsigned int *flags ); /** @} */ /** @@ -159,7 +160,7 @@ net80211_marshal_request_info ( struct net80211_device *dev, * @defgroup net80211_assoc_ll 802.11 association handling functions * @{ */ -static void net80211_step_associate ( struct process *proc ); +static void net80211_step_associate ( struct net80211_device *dev ); static void net80211_handle_auth ( struct net80211_device *dev, struct io_buffer *iob ); static void net80211_handle_assoc_reply ( struct net80211_device *dev, @@ -529,6 +530,7 @@ static int net80211_ll_push ( struct net_device *netdev, * @ret ll_dest Link-layer destination address * @ret ll_source Link-layer source * @ret net_proto Network-layer protocol, in network byte order + * @ret flags Packet flags * @ret rc Return status code * * This expects and removes both the 802.11 frame header and the 802.2 @@ -537,7 +539,7 @@ static int net80211_ll_push ( struct net_device *netdev, static int net80211_ll_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, const void **ll_dest, const void **ll_source, - uint16_t * net_proto ) + uint16_t * net_proto, unsigned int *flags ) { struct ieee80211_frame *hdr = iobuf->data; struct ieee80211_llc_snap_header *lhdr = @@ -586,6 +588,10 @@ static int net80211_ll_pull ( struct net_device *netdev __unused, *ll_dest = hdr->addr1; *ll_source = hdr->addr3; *net_proto = lhdr->ethertype; + *flags = ( ( is_multicast_ether_addr ( hdr->addr1 ) ? + LL_MULTICAST : 0 ) | + ( is_broadcast_ether_addr ( hdr->addr1 ) ? + LL_BROADCAST : 0 ) ); return 0; } @@ -729,6 +735,11 @@ int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 dest[6], /* ---------- Driver API ---------- */ +/** 802.11 association process descriptor */ +static struct process_descriptor net80211_process_desc = + PROC_DESC ( struct net80211_device, proc_assoc, + net80211_step_associate ); + /** * Allocate 802.11 device * @@ -760,7 +771,7 @@ struct net80211_device * net80211_alloc ( size_t priv_size ) dev->priv = ( u8 * ) dev + sizeof ( *dev ); dev->op = &net80211_null_ops; - process_init_stopped ( &dev->proc_assoc, net80211_step_associate, + process_init_stopped ( &dev->proc_assoc, &net80211_process_desc, &netdev->refcnt ); INIT_LIST_HEAD ( &dev->mgmt_queue ); INIT_LIST_HEAD ( &dev->mgmt_info_queue ); @@ -1630,12 +1641,10 @@ void net80211_free_wlanlist ( struct list_head *list ) /** * Step 802.11 association process * - * @v proc Association process + * @v dev 802.11 device */ -static void net80211_step_associate ( struct process *proc ) +static void net80211_step_associate ( struct net80211_device *dev ) { - struct net80211_device *dev = - container_of ( proc, struct net80211_device, proc_assoc ); int rc = 0; int status = dev->state & NET80211_STATUS_MASK; @@ -1836,7 +1845,7 @@ static void net80211_step_associate ( struct process *proc ) dev->rctl = rc80211_init ( dev ); - process_del ( proc ); + process_del ( &dev->proc_assoc ); DBGC ( dev, "802.11 %p associated with %s (%s)\n", dev, dev->essid, eth_ntoa ( dev->bssid ) ); @@ -1861,7 +1870,7 @@ static void net80211_step_associate ( struct process *proc ) net80211_free_wlan ( dev->associating ); dev->associating = NULL; - process_del ( proc ); + process_del ( &dev->proc_assoc ); DBGC ( dev, "802.11 %p association failed (state=%04x): " "%s\n", dev, dev->state, strerror ( dev->assoc_rc ) ); diff --git a/src/net/aoe.c b/src/net/aoe.c index 3b1953a2..1016b250 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -906,13 +906,14 @@ static int aoedev_open ( struct interface *parent, struct net_device *netdev, * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code - * */ static int aoe_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, const void *ll_dest __unused, - const void *ll_source ) { + const void *ll_source, + unsigned int flags __unused ) { struct aoehdr *aoehdr = iobuf->data; struct aoe_command *aoecmd; int rc; diff --git a/src/net/arp.c b/src/net/arp.c index 565eaf01..fd1ce02d 100644 --- a/src/net/arp.c +++ b/src/net/arp.c @@ -186,6 +186,7 @@ static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) { * @v iobuf I/O buffer * @v netdev Network device * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code * * This handles ARP requests and responses as detailed in RFC826. The @@ -196,7 +197,8 @@ static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) { */ static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, const void *ll_dest __unused, - const void *ll_source __unused ) { + const void *ll_source __unused, + unsigned int flags __unused ) { struct arphdr *arphdr = iobuf->data; struct arp_net_protocol *arp_net_protocol; struct net_protocol *net_protocol; diff --git a/src/net/eapol.c b/src/net/eapol.c index 9e5f2640..dd042083 100644 --- a/src/net/eapol.c +++ b/src/net/eapol.c @@ -38,11 +38,13 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * * This function takes ownership of the I/O buffer passed to it. */ static int eapol_rx ( struct io_buffer *iob, struct net_device *netdev, - const void *ll_dest, const void *ll_source ) { + const void *ll_dest, const void *ll_source, + unsigned int flags __unused ) { struct eapol_frame *eapol = iob->data; struct eapol_handler *handler; diff --git a/src/net/eth_slow.c b/src/net/eth_slow.c index 9e68939c..593e45bc 100644 --- a/src/net/eth_slow.c +++ b/src/net/eth_slow.c @@ -234,12 +234,14 @@ static int eth_slow_marker_rx ( struct io_buffer *iobuf, * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code */ static int eth_slow_rx ( struct io_buffer *iobuf, struct net_device *netdev, const void *ll_dest __unused, - const void *ll_source __unused ) { + const void *ll_source __unused, + unsigned int flags __unused ) { union eth_slow_packet *eth_slow = iobuf->data; /* Sanity checks */ diff --git a/src/net/ethernet.c b/src/net/ethernet.c index d14cfefc..c63fd9bc 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -71,11 +71,13 @@ static int eth_push ( struct net_device *netdev __unused, * @ret ll_dest Link-layer destination address * @ret ll_source Source link-layer address * @ret net_proto Network-layer protocol, in network-byte order + * @ret flags Packet flags * @ret rc Return status code */ static int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, const void **ll_dest, - const void **ll_source, uint16_t *net_proto ) { + const void **ll_source, uint16_t *net_proto, + unsigned int *flags ) { struct ethhdr *ethhdr = iobuf->data; /* Sanity check */ @@ -92,6 +94,10 @@ static int eth_pull ( struct net_device *netdev __unused, *ll_dest = ethhdr->h_dest; *ll_source = ethhdr->h_source; *net_proto = ethhdr->h_protocol; + *flags = ( ( is_multicast_ether_addr ( ethhdr->h_dest ) ? + LL_MULTICAST : 0 ) | + ( is_broadcast_ether_addr ( ethhdr->h_dest ) ? + LL_BROADCAST : 0 ) ); return 0; } diff --git a/src/net/fc.c b/src/net/fc.c index 1934fab3..977ad07c 100644 --- a/src/net/fc.c +++ b/src/net/fc.c @@ -1580,7 +1580,7 @@ static void fc_ulp_close ( struct fc_ulp *ulp, int rc ) { fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) ); /* Sanity check */ - assert ( ulp->usage == 0 ); + assert ( list_empty ( &ulp->users ) ); /* Stop link monitor */ fc_link_stop ( &ulp->link ); @@ -1594,35 +1594,50 @@ static void fc_ulp_close ( struct fc_ulp *ulp, int rc ) { } /** - * Increment Fibre Channel upper-layer protocol active usage count + * Attach Fibre Channel upper-layer protocol user * - * @v ulp Fibre Channel ulp + * @v ulp Fibre Channel upper-layer protocol + * @v user Fibre Channel upper-layer protocol user */ -void fc_ulp_increment ( struct fc_ulp *ulp ) { +void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ) { + + /* Sanity check */ + assert ( user->ulp == NULL ); /* Increment peer's usage count */ fc_peer_increment ( ulp->peer ); - /* Increment our usage count */ - ulp->usage++; + /* Attach user */ + user->ulp = fc_ulp_get ( ulp ); + list_add ( &user->list, &ulp->users ); } /** - * Decrement Fibre Channel upper-layer protocol active usage count + * Detach Fibre Channel upper-layer protocol user * - * @v ulp Fibre Channel ulp + * @v user Fibre Channel upper-layer protocol user */ -void fc_ulp_decrement ( struct fc_ulp *ulp ) { +void fc_ulp_detach ( struct fc_ulp_user *user ) { + struct fc_ulp *ulp = user->ulp; - /* Sanity check */ - assert ( ulp->usage > 0 ); + /* Do nothing if not attached */ + if ( ! ulp ) + return; - /* Decrement our usage count and log out if we reach zero */ - if ( --(ulp->usage) == 0 ) + /* Sanity checks */ + list_check_contains ( user, &ulp->users, list ); + + /* Detach user and log out if no users remain */ + list_del ( &user->list ); + if ( list_empty ( &ulp->users ) ) fc_ulp_logout ( ulp, 0 ); /* Decrement our peer's usage count */ fc_peer_decrement ( ulp->peer ); + + /* Drop reference */ + user->ulp = NULL; + fc_ulp_put ( ulp ); } /** @@ -1636,6 +1651,8 @@ void fc_ulp_decrement ( struct fc_ulp *ulp ) { */ int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len, int originated ) { + struct fc_ulp_user *user; + struct fc_ulp_user *tmp; /* Perform implicit logout if logged in and service parameters differ */ if ( fc_link_ok ( &ulp->link ) && @@ -1644,6 +1661,22 @@ int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len, fc_ulp_logout ( ulp, 0 ); } + /* Work around a bug in some versions of the Linux Fibre + * Channel stack, which fail to fully initialise image pairs + * established via a PRLI originated by the Linux stack + * itself. + */ + if ( originated ) + ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK; + if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) { + DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around " + "Linux bug\n", + fc_ntoa ( &ulp->peer->port_wwn ), ulp->type ); + fc_link_stop ( &ulp->link ); + fc_link_start ( &ulp->link ); + return 0; + } + /* Log in, if applicable */ if ( ! fc_link_ok ( &ulp->link ) ) { @@ -1670,18 +1703,11 @@ int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len, /* Record login */ fc_link_up ( &ulp->link ); - /* Work around a bug in some versions of the Linux Fibre - * Channel stack, which fail to fully initialise image pairs - * established via a PRLI originated by the Linux stack - * itself. - */ - if ( originated ) - ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK; - if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) { - DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around " - "Linux bug\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type ); - fc_link_start ( &ulp->link ); + /* Notify users of link state change */ + list_for_each_entry_safe ( user, tmp, &ulp->users, list ) { + fc_ulp_user_get ( user ); + user->examine ( user ); + fc_ulp_user_put ( user ); } return 0; @@ -1694,6 +1720,8 @@ int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len, * @v rc Reason for logout */ void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) { + struct fc_ulp_user *user; + struct fc_ulp_user *tmp; DBGC ( ulp, "FCULP %s/%02x logged out: %s\n", fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) ); @@ -1711,8 +1739,15 @@ void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) { /* Record logout */ fc_link_err ( &ulp->link, rc ); + /* Notify users of link state change */ + list_for_each_entry_safe ( user, tmp, &ulp->users, list ) { + fc_ulp_user_get ( user ); + user->examine ( user ); + fc_ulp_user_put ( user ); + } + /* Close ULP if there are no clients attached */ - if ( ulp->usage == 0 ) + if ( list_empty ( &ulp->users ) ) fc_ulp_close ( ulp, rc ); } @@ -1795,6 +1830,7 @@ static struct fc_ulp * fc_ulp_create ( struct fc_peer *peer, ulp->peer = fc_peer_get ( peer ); list_add_tail ( &ulp->list, &peer->ulps ); ulp->type = type; + INIT_LIST_HEAD ( &ulp->users ); /* Start link state monitor */ fc_link_start ( &ulp->link ); diff --git a/src/net/fcels.c b/src/net/fcels.c index f8bcb865..2106cbd5 100644 --- a/src/net/fcels.c +++ b/src/net/fcels.c @@ -244,20 +244,15 @@ static struct interface_descriptor fc_els_job_desc = /** * Fibre Channel ELS process * - * @v process Process + * @v els Fibre Channel ELS transaction */ -static void fc_els_step ( struct process *process ) { - struct fc_els *els = - container_of ( process, struct fc_els, process ); +static void fc_els_step ( struct fc_els *els ) { int xchg_id; int rc; /* Sanity check */ assert ( fc_els_is_request ( els ) ); - /* Stop process */ - process_del ( &els->process ); - /* Create exchange */ if ( ( xchg_id = fc_xchg_originate ( &els->xchg, els->port, &els->peer_port_id, @@ -278,6 +273,10 @@ static void fc_els_step ( struct process *process ) { } } +/** Fibre Channel ELS process descriptor */ +static struct process_descriptor fc_els_process_desc = + PROC_DESC_ONCE ( struct fc_els, process, fc_els_step ); + /** * Create ELS transaction * @@ -298,7 +297,8 @@ static struct fc_els * fc_els_create ( struct fc_port *port, ref_init ( &els->refcnt, fc_els_free ); intf_init ( &els->job, &fc_els_job_desc, &els->refcnt ); intf_init ( &els->xchg, &fc_els_xchg_desc, &els->refcnt ); - process_init_stopped ( &els->process, fc_els_step, &els->refcnt ); + process_init_stopped ( &els->process, &fc_els_process_desc, + &els->refcnt ); els->port = fc_port_get ( port ); memcpy ( &els->port_id, port_id, sizeof ( els->port_id ) ); memcpy ( &els->peer_port_id, peer_port_id, diff --git a/src/net/fcns.c b/src/net/fcns.c index 7769e255..55bd29e5 100644 --- a/src/net/fcns.c +++ b/src/net/fcns.c @@ -153,19 +153,14 @@ static int fc_ns_query_deliver ( struct fc_ns_query *query, /** * Name server query process * - * @v process Process + * @v query Name server query */ -static void fc_ns_query_step ( struct process *process ) { - struct fc_ns_query *query = - container_of ( process, struct fc_ns_query, process ); +static void fc_ns_query_step ( struct fc_ns_query *query ) { struct xfer_metadata meta; struct fc_ns_gid_pn_request gid_pn; int xchg_id; int rc; - /* Stop process */ - process_del ( &query->process ); - /* Create exchange */ if ( ( xchg_id = fc_xchg_originate ( &query->xchg, query->port, &fc_gs_port_id, @@ -208,6 +203,10 @@ static struct interface_operation fc_ns_query_xchg_op[] = { static struct interface_descriptor fc_ns_query_xchg_desc = INTF_DESC ( struct fc_ns_query, xchg, fc_ns_query_xchg_op ); +/** Name server process descriptor */ +static struct process_descriptor fc_ns_query_process_desc = + PROC_DESC_ONCE ( struct fc_ns_query, process, fc_ns_query_step ); + /** * Issue Fibre Channel name server query * @@ -226,7 +225,8 @@ int fc_ns_query ( struct fc_peer *peer, struct fc_port *port, return -ENOMEM; ref_init ( &query->refcnt, fc_ns_query_free ); intf_init ( &query->xchg, &fc_ns_query_xchg_desc, &query->refcnt ); - process_init ( &query->process, fc_ns_query_step, &query->refcnt ); + process_init ( &query->process, &fc_ns_query_process_desc, + &query->refcnt ); query->peer = fc_peer_get ( peer ); query->port = fc_port_get ( port ); query->done = done; diff --git a/src/net/fcoe.c b/src/net/fcoe.c index db2fc980..c54d1b47 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -331,10 +331,12 @@ static struct io_buffer * fcoe_alloc_iob ( struct fcoe_port *fcoe __unused, * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code */ static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev, - const void *ll_dest, const void *ll_source ) { + const void *ll_dest, const void *ll_source, + unsigned int flags __unused ) { struct fcoe_header *fcoehdr; struct fcoe_footer *fcoeftr; struct fcoe_port *fcoe; @@ -924,12 +926,14 @@ static struct fip_handler fip_handlers[] = { * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code */ static int fcoe_fip_rx ( struct io_buffer *iobuf, struct net_device *netdev, const void *ll_dest, - const void *ll_source __unused ) { + const void *ll_source __unused, + unsigned int flags __unused ) { struct fip_header *fiphdr = iobuf->data; struct fip_descriptors descs; struct fip_handler *handler; diff --git a/src/net/fcp.c b/src/net/fcp.c index 40cd057e..bd1a0900 100644 --- a/src/net/fcp.c +++ b/src/net/fcp.c @@ -146,8 +146,8 @@ struct fc_els_prli_descriptor fcp_prli_descriptor __fc_els_prli_descriptor = { struct fcp_device { /** Reference count */ struct refcnt refcnt; - /** Fibre Channel upper-layer protocol */ - struct fc_ulp *ulp; + /** Fibre Channel upper-layer protocol user */ + struct fc_ulp_user user; /** SCSI command issuing interface */ struct interface scsi; /** List of active commands */ @@ -649,11 +649,9 @@ static int fcpcmd_recv_unknown ( struct fcp_command *fcpcmd, /** * Transmit FCP frame * - * @v process FCP command process + * @v fcpcmd FCP command */ -static void fcpcmd_step ( struct process *process ) { - struct fcp_command *fcpcmd = - container_of ( process, struct fcp_command, process ); +static void fcpcmd_step ( struct fcp_command *fcpcmd ) { int rc; /* Send the current IU */ @@ -723,6 +721,10 @@ static struct interface_operation fcpcmd_xchg_op[] = { static struct interface_descriptor fcpcmd_xchg_desc = INTF_DESC_PASSTHRU ( struct fcp_command, xchg, fcpcmd_xchg_op, scsi ); +/** FCP command process descriptor */ +static struct process_descriptor fcpcmd_process_desc = + PROC_DESC ( struct fcp_command, process, fcpcmd_step ); + /** * Issue FCP SCSI command * @@ -734,13 +736,13 @@ static struct interface_descriptor fcpcmd_xchg_desc = static int fcpdev_scsi_command ( struct fcp_device *fcpdev, struct interface *parent, struct scsi_cmd *command ) { - struct fcp_prli_service_parameters *param = fcpdev->ulp->param; + struct fcp_prli_service_parameters *param = fcpdev->user.ulp->param; struct fcp_command *fcpcmd; int xchg_id; int rc; /* Check link */ - if ( ( rc = fcpdev->ulp->link.rc ) != 0 ) { + if ( ( rc = fcpdev->user.ulp->link.rc ) != 0 ) { DBGC ( fcpdev, "FCP %p could not issue command while link is " "down: %s\n", fcpdev, strerror ( rc ) ); goto err_link; @@ -748,7 +750,7 @@ static int fcpdev_scsi_command ( struct fcp_device *fcpdev, /* Check target capability */ assert ( param != NULL ); - assert ( fcpdev->ulp->param_len >= sizeof ( *param ) ); + assert ( fcpdev->user.ulp->param_len >= sizeof ( *param ) ); if ( ! ( param->flags & htonl ( FCP_PRLI_TARGET ) ) ) { DBGC ( fcpdev, "FCP %p could not issue command: not a target\n", fcpdev ); @@ -765,15 +767,16 @@ static int fcpdev_scsi_command ( struct fcp_device *fcpdev, ref_init ( &fcpcmd->refcnt, fcpcmd_free ); intf_init ( &fcpcmd->scsi, &fcpcmd_scsi_desc, &fcpcmd->refcnt ); intf_init ( &fcpcmd->xchg, &fcpcmd_xchg_desc, &fcpcmd->refcnt ); - process_init_stopped ( &fcpcmd->process, fcpcmd_step, &fcpcmd->refcnt ); + process_init_stopped ( &fcpcmd->process, &fcpcmd_process_desc, + &fcpcmd->refcnt ); fcpcmd->fcpdev = fcpdev_get ( fcpdev ); list_add ( &fcpcmd->list, &fcpdev->fcpcmds ); memcpy ( &fcpcmd->command, command, sizeof ( fcpcmd->command ) ); /* Create new exchange */ if ( ( xchg_id = fc_xchg_originate ( &fcpcmd->xchg, - fcpdev->ulp->peer->port, - &fcpdev->ulp->peer->port_id, + fcpdev->user.ulp->peer->port, + &fcpdev->user.ulp->peer->port_id, FC_TYPE_FCP ) ) < 0 ) { rc = xchg_id; DBGC ( fcpdev, "FCP %p could not create exchange: %s\n", @@ -822,11 +825,7 @@ static void fcpdev_close ( struct fcp_device *fcpdev, int rc ) { } /* Drop reference to ULP */ - if ( fcpdev->ulp ) { - fc_ulp_decrement ( fcpdev->ulp ); - fc_ulp_put ( fcpdev->ulp ); - fcpdev->ulp = NULL; - } + fc_ulp_detach ( &fcpdev->user ); } /** @@ -836,7 +835,8 @@ static void fcpdev_close ( struct fcp_device *fcpdev, int rc ) { * @ret len Length of window */ static size_t fcpdev_window ( struct fcp_device *fcpdev ) { - return ( fc_link_ok ( &fcpdev->ulp->link ) ? ~( ( size_t ) 0 ) : 0 ); + return ( fc_link_ok ( &fcpdev->user.ulp->link ) ? + ~( ( size_t ) 0 ) : 0 ); } /** @@ -897,15 +897,15 @@ static struct device * fcpdev_identify_device ( struct fcp_device *fcpdev ) { /* We know the underlying device only if the link is up; * otherwise we don't have a port to examine. */ - if ( ! fc_link_ok ( &fcpdev->ulp->link ) ) { + if ( ! fc_link_ok ( &fcpdev->user.ulp->link ) ) { DBGC ( fcpdev, "FCP %p doesn't know underlying device " "until link is up\n", fcpdev ); return NULL; } /* Hand off to port's transport interface */ - assert ( fcpdev->ulp->peer->port != NULL ); - return identify_device ( &fcpdev->ulp->peer->port->transport ); + assert ( fcpdev->user.ulp->peer->port != NULL ); + return identify_device ( &fcpdev->user.ulp->peer->port->transport ); } /** FCP device SCSI interface operations */ @@ -923,6 +923,26 @@ static struct interface_operation fcpdev_scsi_op[] = { static struct interface_descriptor fcpdev_scsi_desc = INTF_DESC ( struct fcp_device, scsi, fcpdev_scsi_op ); +/** + * Examine FCP ULP link state + * + * @v user Fibre Channel upper-layer protocol user + */ +static void fcpdev_examine ( struct fc_ulp_user *user ) { + struct fcp_device *fcpdev = + container_of ( user, struct fcp_device, user ); + + if ( fc_link_ok ( &fcpdev->user.ulp->link ) ) { + DBGC ( fcpdev, "FCP %p link is up\n", fcpdev ); + } else { + DBGC ( fcpdev, "FCP %p link is down: %s\n", + fcpdev, strerror ( fcpdev->user.ulp->link.rc ) ); + } + + /* Notify SCSI layer of window change */ + xfer_window_changed ( &fcpdev->scsi ); +} + /** * Open FCP device * @@ -953,11 +973,13 @@ static int fcpdev_open ( struct interface *parent, struct fc_name *wwn, ref_init ( &fcpdev->refcnt, NULL ); intf_init ( &fcpdev->scsi, &fcpdev_scsi_desc, &fcpdev->refcnt ); INIT_LIST_HEAD ( &fcpdev->fcpcmds ); - fcpdev->ulp = fc_ulp_get ( ulp ); - fc_ulp_increment ( fcpdev->ulp ); + fc_ulp_user_init ( &fcpdev->user, fcpdev_examine, &fcpdev->refcnt ); DBGC ( fcpdev, "FCP %p opened for %s\n", fcpdev, fc_ntoa ( wwn ) ); + /* Attach to Fibre Channel ULP */ + fc_ulp_attach ( ulp, &fcpdev->user ); + /* Preserve parameters required for boot firmware table */ memcpy ( &fcpdev->wwn, wwn, sizeof ( fcpdev->wwn ) ); memcpy ( &fcpdev->lun, lun, sizeof ( fcpdev->lun ) ); diff --git a/src/net/infiniband.c b/src/net/infiniband.c index c2cb834d..c88da73e 100644 --- a/src/net/infiniband.c +++ b/src/net/infiniband.c @@ -866,10 +866,7 @@ static void ib_step ( struct process *process __unused ) { } /** Infiniband event queue process */ -struct process ib_process __permanent_process = { - .list = LIST_HEAD_INIT ( ib_process.list ), - .step = ib_step, -}; +PERMANENT_PROCESS ( ib_process, ib_step ); /*************************************************************************** * diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index ed388b2f..369e2e90 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -92,7 +92,7 @@ struct ib_cmrc_connection { /** * Shut down CMRC connection gracefully * - * @v process Process + * @v cmrc Communication-Managed Reliable Connection * * The Infiniband data structures are not reference-counted or * guarded. It is therefore unsafe to shut them down while we may be @@ -107,9 +107,7 @@ struct ib_cmrc_connection { * connection, ensuring that the structure is not freed before the * shutdown process has run. */ -static void ib_cmrc_shutdown ( struct process *process ) { - struct ib_cmrc_connection *cmrc = - container_of ( process, struct ib_cmrc_connection, shutdown ); +static void ib_cmrc_shutdown ( struct ib_cmrc_connection *cmrc ) { DBGC ( cmrc, "CMRC %p shutting down\n", cmrc ); @@ -119,9 +117,6 @@ static void ib_cmrc_shutdown ( struct process *process ) { ib_destroy_cq ( cmrc->ibdev, cmrc->cq ); ib_close ( cmrc->ibdev ); - /* Remove process from run queue */ - process_del ( &cmrc->shutdown ); - /* Drop the remaining reference */ ref_put ( &cmrc->refcnt ); } @@ -180,6 +175,9 @@ static void ib_cmrc_changed ( struct ib_device *ibdev __unused, return; } + /* Notify upper connection of window change */ + xfer_window_changed ( &cmrc->xfer ); + /* If we are disconnected, close the upper connection */ if ( rc_cm != 0 ) { ib_cmrc_close ( cmrc, rc_cm ); @@ -360,6 +358,11 @@ static struct interface_operation ib_cmrc_xfer_operations[] = { static struct interface_descriptor ib_cmrc_xfer_desc = INTF_DESC ( struct ib_cmrc_connection, xfer, ib_cmrc_xfer_operations ); +/** CMRC shutdown process descriptor */ +static struct process_descriptor ib_cmrc_shutdown_desc = + PROC_DESC_ONCE ( struct ib_cmrc_connection, shutdown, + ib_cmrc_shutdown ); + /** * Open CMRC connection * @@ -385,7 +388,7 @@ int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, cmrc->ibdev = ibdev; memcpy ( &cmrc->dgid, dgid, sizeof ( cmrc->dgid ) ); memcpy ( &cmrc->service_id, service_id, sizeof ( cmrc->service_id ) ); - process_init_stopped ( &cmrc->shutdown, ib_cmrc_shutdown, + process_init_stopped ( &cmrc->shutdown, &ib_cmrc_shutdown_desc, &cmrc->refcnt ); /* Open Infiniband device */ diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 465df45e..99c2580e 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -14,6 +14,7 @@ #include #include #include +#include /** @file * @@ -30,7 +31,10 @@ static uint8_t next_ident_high = 0; struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes ); /** List of fragment reassembly buffers */ -static LIST_HEAD ( frag_buffers ); +static LIST_HEAD ( ipv4_fragments ); + +/** Fragment reassembly timeout */ +#define IP_FRAG_TIMEOUT ( TICKS_PER_SEC / 2 ) /** * Add IPv4 minirouting table entry @@ -46,16 +50,16 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, struct in_addr netmask, struct in_addr gateway ) { struct ipv4_miniroute *miniroute; - DBG ( "IPv4 add %s", inet_ntoa ( address ) ); - DBG ( "/%s ", inet_ntoa ( netmask ) ); + DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) ); + DBGC ( netdev, "/%s ", inet_ntoa ( netmask ) ); if ( gateway.s_addr ) - DBG ( "gw %s ", inet_ntoa ( gateway ) ); - DBG ( "via %s\n", netdev->name ); + DBGC ( netdev, "gw %s ", inet_ntoa ( gateway ) ); + DBGC ( netdev, "via %s\n", netdev->name ); /* Allocate and populate miniroute structure */ miniroute = malloc ( sizeof ( *miniroute ) ); if ( ! miniroute ) { - DBG ( "IPv4 could not add miniroute\n" ); + DBGC ( netdev, "IPv4 could not add miniroute\n" ); return NULL; } @@ -83,12 +87,13 @@ add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, * @v miniroute Routing table entry */ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { + struct net_device *netdev = miniroute->netdev; - DBG ( "IPv4 del %s", inet_ntoa ( miniroute->address ) ); - DBG ( "/%s ", inet_ntoa ( miniroute->netmask ) ); + DBGC ( netdev, "IPv4 del %s", inet_ntoa ( miniroute->address ) ); + DBGC ( netdev, "/%s ", inet_ntoa ( miniroute->netmask ) ); if ( miniroute->gateway.s_addr ) - DBG ( "gw %s ", inet_ntoa ( miniroute->gateway ) ); - DBG ( "via %s\n", miniroute->netdev->name ); + DBGC ( netdev, "gw %s ", inet_ntoa ( miniroute->gateway ) ); + DBGC ( netdev, "via %s\n", miniroute->netdev->name ); netdev_put ( miniroute->netdev ); list_del ( &miniroute->list ); @@ -110,10 +115,6 @@ static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) { int local; int has_gw; - /* Never attempt to route the broadcast address */ - if ( dest->s_addr == INADDR_BROADCAST ) - return NULL; - /* Find first usable route in routing table */ list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { if ( ! netdev_is_open ( miniroute->netdev ) ) @@ -132,103 +133,128 @@ static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) { } /** - * Fragment reassembly counter timeout + * Expire fragment reassembly buffer * - * @v timer Retry timer - * @v over If asserted, the timer is greater than @c MAX_TIMEOUT + * @v timer Retry timer + * @v fail Failure indicator */ -static void ipv4_frag_expired ( struct retry_timer *timer __unused, - int over ) { - if ( over ) { - DBG ( "Fragment reassembly timeout" ); - /* Free the fragment buffer */ - } +static void ipv4_fragment_expired ( struct retry_timer *timer, + int fail __unused ) { + struct ipv4_fragment *frag = + container_of ( timer, struct ipv4_fragment, timer ); + struct iphdr *iphdr = frag->iobuf->data; + + DBGC ( iphdr->src, "IPv4 fragment %04x expired\n", + ntohs ( iphdr->ident ) ); + free_iob ( frag->iobuf ); + list_del ( &frag->list ); + free ( frag ); } /** - * Free fragment buffer + * Find matching fragment reassembly buffer * - * @v fragbug Fragment buffer + * @v iphdr IPv4 header + * @ret frag Fragment reassembly buffer, or NULL */ -static void free_fragbuf ( struct frag_buffer *fragbuf ) { - free ( fragbuf ); +static struct ipv4_fragment * ipv4_fragment ( struct iphdr *iphdr ) { + struct ipv4_fragment *frag; + struct iphdr *frag_iphdr; + + list_for_each_entry ( frag, &ipv4_fragments, list ) { + frag_iphdr = frag->iobuf->data; + + if ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) && + ( iphdr->ident == frag_iphdr->ident ) ) { + return frag; + } + } + + return NULL; } /** * Fragment reassembler * - * @v iobuf I/O buffer, fragment of the datagram - * @ret frag_iob Reassembled packet, or NULL + * @v iobuf I/O buffer + * @ret iobuf Reassembled packet, or NULL */ -static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) { +static struct io_buffer * ipv4_reassemble ( struct io_buffer *iobuf ) { struct iphdr *iphdr = iobuf->data; - struct frag_buffer *fragbuf; - - /** - * Check if the fragment belongs to any fragment series - */ - list_for_each_entry ( fragbuf, &frag_buffers, list ) { - if ( fragbuf->ident == iphdr->ident && - fragbuf->src.s_addr == iphdr->src.s_addr ) { - /** - * Check if the packet is the expected fragment - * - * The offset of the new packet must be equal to the - * length of the data accumulated so far (the length of - * the reassembled I/O buffer - */ - if ( iob_len ( fragbuf->frag_iob ) == - ( iphdr->frags & IP_MASK_OFFSET ) ) { - /** - * Append the contents of the fragment to the - * reassembled I/O buffer - */ - iob_pull ( iobuf, sizeof ( *iphdr ) ); - memcpy ( iob_put ( fragbuf->frag_iob, - iob_len ( iobuf ) ), - iobuf->data, iob_len ( iobuf ) ); - free_iob ( iobuf ); + size_t offset = ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 ); + unsigned int more_frags = ( iphdr->frags & htons ( IP_MASK_MOREFRAGS )); + size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); + struct ipv4_fragment *frag; + size_t expected_offset; + struct io_buffer *new_iobuf; - /** Check if the fragment series is over */ - if ( ! ( iphdr->frags & IP_MASK_MOREFRAGS ) ) { - iobuf = fragbuf->frag_iob; - free_fragbuf ( fragbuf ); - return iobuf; - } + /* Find matching fragment reassembly buffer, if any */ + frag = ipv4_fragment ( iphdr ); - } else { - /* Discard the fragment series */ - free_fragbuf ( fragbuf ); - free_iob ( iobuf ); - } - return NULL; + /* Drop out-of-order fragments */ + expected_offset = ( frag ? frag->offset : 0 ); + if ( offset != expected_offset ) { + DBGC ( iphdr->src, "IPv4 dropping out-of-sequence fragment " + "%04x (%zd+%zd, expected %zd)\n", + ntohs ( iphdr->ident ), offset, + ( iob_len ( iobuf ) - hdrlen ), expected_offset ); + goto drop; + } + + /* Create or extend fragment reassembly buffer as applicable */ + if ( frag == NULL ) { + + /* Create new fragment reassembly buffer */ + frag = zalloc ( sizeof ( *frag ) ); + if ( ! frag ) + goto drop; + list_add ( &frag->list, &ipv4_fragments ); + frag->iobuf = iobuf; + frag->offset = ( iob_len ( iobuf ) - hdrlen ); + timer_init ( &frag->timer, ipv4_fragment_expired, NULL ); + + } else { + + /* Extend reassembly buffer */ + iob_pull ( iobuf, hdrlen ); + new_iobuf = alloc_iob ( iob_len ( frag->iobuf ) + + iob_len ( iobuf ) ); + if ( ! new_iobuf ) { + DBGC ( iphdr->src, "IPv4 could not extend reassembly " + "buffer to %zd bytes\n", + iob_len ( frag->iobuf ) + iob_len ( iobuf ) ); + goto drop; + } + memcpy ( iob_put ( new_iobuf, iob_len ( frag->iobuf ) ), + frag->iobuf->data, iob_len ( frag->iobuf ) ); + memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ), + iobuf->data, iob_len ( iobuf ) ); + free_iob ( frag->iobuf ); + frag->iobuf = new_iobuf; + frag->offset += iob_len ( iobuf ); + free_iob ( iobuf ); + iphdr = frag->iobuf->data; + iphdr->len = ntohs ( iob_len ( frag->iobuf ) ); + + /* Stop fragment reassembly timer */ + stop_timer ( &frag->timer ); + + /* If this is the final fragment, return it */ + if ( ! more_frags ) { + iobuf = frag->iobuf; + list_del ( &frag->list ); + free ( frag ); + return iobuf; } } - - /** Check if the fragment is the first in the fragment series */ - if ( iphdr->frags & IP_MASK_MOREFRAGS && - ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) { - - /** Create a new fragment buffer */ - fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) ); - fragbuf->ident = iphdr->ident; - fragbuf->src = iphdr->src; - /* Set up the reassembly I/O buffer */ - fragbuf->frag_iob = alloc_iob ( IP_FRAG_IOB_SIZE ); - iob_pull ( iobuf, sizeof ( *iphdr ) ); - memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ), - iobuf->data, iob_len ( iobuf ) ); - free_iob ( iobuf ); + /* (Re)start fragment reassembly timer */ + start_timer_fixed ( &frag->timer, IP_FRAG_TIMEOUT ); - /* Set the reassembly timer */ - timer_init ( &fragbuf->frag_timer, ipv4_frag_expired, NULL ); - start_timer_fixed ( &fragbuf->frag_timer, IP_FRAG_TIMEOUT ); + return NULL; - /* Add the fragment buffer to the list of fragment buffers */ - list_add ( &fragbuf->list, &frag_buffers ); - } - + drop: + free_iob ( iobuf ); return NULL; } @@ -260,15 +286,17 @@ static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) { * * @v dest IPv4 destination address * @v src IPv4 source address + * @v netmask IPv4 subnet mask * @v netdev Network device * @v ll_dest Link-layer destination address buffer * @ret rc Return status code */ static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src, - struct net_device *netdev, uint8_t *ll_dest ) { + struct in_addr netmask, struct net_device *netdev, + uint8_t *ll_dest ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; - if ( dest.s_addr == INADDR_BROADCAST ) { + if ( ( ( dest.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0 ) { /* Broadcast address */ memcpy ( ll_dest, netdev->ll_broadcast, ll_protocol->ll_addr_len ); @@ -306,6 +334,7 @@ static int ipv4_tx ( struct io_buffer *iobuf, struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest ); struct ipv4_miniroute *miniroute; struct in_addr next_hop; + struct in_addr netmask = { .s_addr = 0 }; uint8_t ll_dest[MAX_LL_ADDR_LEN]; int rc; @@ -326,10 +355,12 @@ static int ipv4_tx ( struct io_buffer *iobuf, ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) && ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) { iphdr->src = miniroute->address; + netmask = miniroute->netmask; netdev = miniroute->netdev; } if ( ! netdev ) { - DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) ); + DBGC ( sin_dest->sin_addr, "IPv4 has no route to %s\n", + inet_ntoa ( iphdr->dest ) ); rc = -ENETUNREACH; goto err; } @@ -343,10 +374,12 @@ static int ipv4_tx ( struct io_buffer *iobuf, ( ( netdev->rx_stats.good & 0xf ) << 0 ) ); /* Determine link-layer destination address */ - if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, netdev, + if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, netmask, netdev, ll_dest ) ) != 0 ) { - DBG ( "IPv4 has no link-layer address for %s: %s\n", - inet_ntoa ( next_hop ), strerror ( rc ) ); + DBGC ( sin_dest->sin_addr, "IPv4 has no link-layer address for " + "%s: %s\n", inet_ntoa ( next_hop ), strerror ( rc ) ); + /* Record error for diagnosis */ + netdev_tx_err ( netdev, iob_disown ( iobuf ), rc ); goto err; } @@ -356,16 +389,17 @@ static int ipv4_tx ( struct io_buffer *iobuf, iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); /* Print IP4 header for debugging */ - DBG ( "IPv4 TX %s->", inet_ntoa ( iphdr->src ) ); - DBG ( "%s len %d proto %d id %04x csum %04x\n", - inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol, - ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); + DBGC2 ( sin_dest->sin_addr, "IPv4 TX %s->", inet_ntoa ( iphdr->src ) ); + DBGC2 ( sin_dest->sin_addr, "%s len %d proto %d id %04x csum %04x\n", + inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), + iphdr->protocol, ntohs ( iphdr->ident ), + ntohs ( iphdr->chksum ) ); /* Hand off to link layer */ if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest, netdev->ll_addr ) ) != 0 ) { - DBG ( "IPv4 could not transmit packet via %s: %s\n", - netdev->name, strerror ( rc ) ); + DBGC ( sin_dest->sin_addr, "IPv4 could not transmit packet " + "via %s: %s\n", netdev->name, strerror ( rc ) ); return rc; } @@ -376,21 +410,60 @@ static int ipv4_tx ( struct io_buffer *iobuf, return rc; } +/** + * Check if network device has any IPv4 address + * + * @v netdev Network device + * @ret has_any_addr Network device has any IPv4 address + */ +static int ipv4_has_any_addr ( struct net_device *netdev ) { + struct ipv4_miniroute *miniroute; + + list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { + if ( miniroute->netdev == netdev ) + return 1; + } + return 0; +} + +/** + * Check if network device has a specific IPv4 address + * + * @v netdev Network device + * @v addr IPv4 address + * @ret has_addr Network device has this IPv4 address + */ +static int ipv4_has_addr ( struct net_device *netdev, struct in_addr addr ) { + struct ipv4_miniroute *miniroute; + + list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { + if ( ( miniroute->netdev == netdev ) && + ( miniroute->address.s_addr == addr.s_addr ) ) { + /* Found matching address */ + return 1; + } + } + return 0; +} + /** * Process incoming packets * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer destination source + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_dest Link-layer destination address + * @v ll_source Link-layer destination source + * @v flags Packet flags + * @ret rc Return status code * * This function expects an IP4 network datagram. It processes the headers * and sends it to the transport layer. */ static int ipv4_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, + struct net_device *netdev, const void *ll_dest __unused, - const void *ll_source __unused ) { + const void *ll_source __unused, + unsigned int flags ) { struct iphdr *iphdr = iobuf->data; size_t hdrlen; size_t len; @@ -404,77 +477,88 @@ static int ipv4_rx ( struct io_buffer *iobuf, /* Sanity check the IPv4 header */ if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) { - DBG ( "IPv4 packet too short at %zd bytes (min %zd bytes)\n", - iob_len ( iobuf ), sizeof ( *iphdr ) ); + DBGC ( iphdr->src, "IPv4 packet too short at %zd bytes (min " + "%zd bytes)\n", iob_len ( iobuf ), sizeof ( *iphdr ) ); goto err; } if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) { - DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen ); + DBGC ( iphdr->src, "IPv4 version %#02x not supported\n", + iphdr->verhdrlen ); goto err; } hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); if ( hdrlen < sizeof ( *iphdr ) ) { - DBG ( "IPv4 header too short at %zd bytes (min %zd bytes)\n", - hdrlen, sizeof ( *iphdr ) ); + DBGC ( iphdr->src, "IPv4 header too short at %zd bytes (min " + "%zd bytes)\n", hdrlen, sizeof ( *iphdr ) ); goto err; } if ( hdrlen > iob_len ( iobuf ) ) { - DBG ( "IPv4 header too long at %zd bytes " - "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) ); + DBGC ( iphdr->src, "IPv4 header too long at %zd bytes " + "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) ); goto err; } if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) { - DBG ( "IPv4 checksum incorrect (is %04x including checksum " - "field, should be 0000)\n", csum ); + DBGC ( iphdr->src, "IPv4 checksum incorrect (is %04x " + "including checksum field, should be 0000)\n", csum ); goto err; } len = ntohs ( iphdr->len ); if ( len < hdrlen ) { - DBG ( "IPv4 length too short at %zd bytes " - "(header is %zd bytes)\n", len, hdrlen ); + DBGC ( iphdr->src, "IPv4 length too short at %zd bytes " + "(header is %zd bytes)\n", len, hdrlen ); goto err; } if ( len > iob_len ( iobuf ) ) { - DBG ( "IPv4 length too long at %zd bytes " - "(packet is %zd bytes)\n", len, iob_len ( iobuf ) ); + DBGC ( iphdr->src, "IPv4 length too long at %zd bytes " + "(packet is %zd bytes)\n", len, iob_len ( iobuf ) ); goto err; } - /* Print IPv4 header for debugging */ - DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) ); - DBG ( "%s len %d proto %d id %04x csum %04x\n", - inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol, - ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); - - /* Truncate packet to correct length, calculate pseudo-header - * checksum and then strip off the IPv4 header. - */ + /* Truncate packet to correct length */ iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); - pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM ); - iob_pull ( iobuf, hdrlen ); - /* Fragment reassembly */ - if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) || - ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) { - /* Pass the fragment to ipv4_reassemble() which either - * returns a fully reassembled I/O buffer or NULL. + /* Print IPv4 header for debugging */ + DBGC2 ( iphdr->src, "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) ); + DBGC2 ( iphdr->src, "%s len %d proto %d id %04x csum %04x\n", + inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol, + ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); + + /* Discard unicast packets not destined for us */ + if ( ( ! ( flags & LL_MULTICAST ) ) && + ipv4_has_any_addr ( netdev ) && + ( ! ipv4_has_addr ( netdev, iphdr->dest ) ) ) { + DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet " + "for %s\n", inet_ntoa ( iphdr->dest ) ); + goto err; + } + + /* Perform fragment reassembly if applicable */ + if ( iphdr->frags & htons ( IP_MASK_OFFSET | IP_MASK_MOREFRAGS ) ) { + /* Pass the fragment to ipv4_reassemble() which returns + * either a fully reassembled I/O buffer or NULL. */ iobuf = ipv4_reassemble ( iobuf ); if ( ! iobuf ) return 0; + iphdr = iobuf->data; + hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); } - /* Construct socket addresses and hand off to transport layer */ + /* Construct socket addresses, calculate pseudo-header + * checksum, and hand off to transport layer + */ memset ( &src, 0, sizeof ( src ) ); src.sin.sin_family = AF_INET; src.sin.sin_addr = iphdr->src; memset ( &dest, 0, sizeof ( dest ) ); dest.sin.sin_family = AF_INET; dest.sin.sin_addr = iphdr->dest; + pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM ); + iob_pull ( iobuf, hdrlen ); if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st, &dest.st, pshdr_csum ) ) != 0 ) { - DBG ( "IPv4 received packet rejected by stack: %s\n", - strerror ( rc ) ); + DBGC ( src.sin.sin_addr, "IPv4 received packet rejected by " + "stack: %s\n", strerror ( rc ) ); return rc; } @@ -494,15 +578,10 @@ static int ipv4_rx ( struct io_buffer *iobuf, */ static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) { const struct in_addr *address = net_addr; - struct ipv4_miniroute *miniroute; - list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { - if ( ( miniroute->netdev == netdev ) && - ( miniroute->address.s_addr == address->s_addr ) ) { - /* Found matching address */ - return 0; - } - } + if ( ipv4_has_addr ( netdev, *address ) ) + return 0; + return -ENOENT; } diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 712aa49e..57bf94d8 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -288,13 +288,15 @@ static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr, * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * * This function processes a IPv6 packet */ static int ipv6_rx ( struct io_buffer *iobuf, __unused struct net_device *netdev, __unused const void *ll_dest, - __unused const void *ll_source ) { + __unused const void *ll_source, + __unused unsigned int flags ) { struct ip6_header *ip6hdr = iobuf->data; union { diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 9a8a3aaf..f5ec4191 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -194,16 +194,17 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { } /** - * Complete network transmission + * Discard transmitted packet * * @v netdev Network device - * @v iobuf I/O buffer + * @v iobuf I/O buffer, or NULL * @v rc Packet status code * - * The packet must currently be in the network device's TX queue. + * The packet is discarded and a TX error is recorded. This function + * takes ownership of the I/O buffer. */ -void netdev_tx_complete_err ( struct net_device *netdev, - struct io_buffer *iobuf, int rc ) { +void netdev_tx_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ) { /* Update statistics counter */ netdev_record_stat ( &netdev->tx_stats, rc ); @@ -215,12 +216,28 @@ void netdev_tx_complete_err ( struct net_device *netdev, netdev->name, iobuf, strerror ( rc ) ); } + /* Discard packet */ + free_iob ( iobuf ); +} + +/** + * Complete network transmission + * + * @v netdev Network device + * @v iobuf I/O buffer + * @v rc Packet status code + * + * The packet must currently be in the network device's TX queue. + */ +void netdev_tx_complete_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ) { + /* Catch data corruption as early as possible */ list_check_contains ( iobuf, &netdev->tx_queue, list ); /* Dequeue and free I/O buffer */ list_del ( &iobuf->list ); - free_iob ( iobuf ); + netdev_tx_err ( netdev, iobuf, rc ); } /** @@ -644,7 +661,8 @@ int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, /* Add link-layer header */ if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest, ll_source, net_protocol->net_proto ) ) != 0 ) { - free_iob ( iobuf ); + /* Record error for diagnosis */ + netdev_tx_err ( netdev, iobuf, rc ); return rc; } @@ -660,17 +678,19 @@ int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, * @v net_proto Network-layer protocol, in network-byte order * @v ll_dest Destination link-layer address * @v ll_source Source link-layer address + * @v flags Packet flags * @ret rc Return status code */ int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, - uint16_t net_proto, const void *ll_dest, const void *ll_source ) { + uint16_t net_proto, const void *ll_dest, const void *ll_source, + unsigned int flags ) { struct net_protocol *net_protocol; /* Hand off to network-layer protocol, if any */ for_each_table_entry ( net_protocol, NET_PROTOCOLS ) { if ( net_protocol->net_proto == net_proto ) return net_protocol->rx ( iobuf, netdev, ll_dest, - ll_source ); + ll_source, flags ); } DBGC ( netdev, "NETDEV %s unknown network protocol %04x\n", @@ -692,6 +712,7 @@ void net_poll ( void ) { const void *ll_dest; const void *ll_source; uint16_t net_proto; + unsigned int flags; int rc; /* Poll and process each network device */ @@ -725,7 +746,8 @@ void net_poll ( void ) { ll_protocol = netdev->ll_protocol; if ( ( rc = ll_protocol->pull ( netdev, iobuf, &ll_dest, &ll_source, - &net_proto ) ) != 0 ) { + &net_proto, + &flags ) ) != 0 ) { free_iob ( iobuf ); continue; } @@ -733,7 +755,7 @@ void net_poll ( void ) { /* Hand packet to network layer */ if ( ( rc = net_rx ( iob_disown ( iobuf ), netdev, net_proto, ll_dest, - ll_source ) ) != 0 ) { + ll_source, flags ) ) != 0 ) { /* Record error for diagnosis */ netdev_rx_err ( netdev, NULL, rc ); } @@ -751,7 +773,4 @@ static void net_step ( struct process *process __unused ) { } /** Networking stack process */ -struct process net_process __permanent_process = { - .list = LIST_HEAD_INIT ( net_process.list ), - .step = net_step, -}; +PERMANENT_PROCESS ( net_process, net_step ); diff --git a/src/net/rarp.c b/src/net/rarp.c index da67c459..59cb1d07 100644 --- a/src/net/rarp.c +++ b/src/net/rarp.c @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code * * This is a dummy method which simply discards RARP packets. @@ -45,7 +46,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); static int rarp_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, const void *ll_dest __unused, - const void *ll_source __unused ) { + const void *ll_source __unused, + unsigned int flags __unused ) { free_iob ( iobuf ); return 0; } diff --git a/src/net/retry.c b/src/net/retry.c index 082be39b..7e20f0c8 100644 --- a/src/net/retry.c +++ b/src/net/retry.c @@ -148,6 +148,7 @@ void stop_timer ( struct retry_timer *timer ) { * @v timer Retry timer */ static void timer_expired ( struct retry_timer *timer ) { + struct refcnt *refcnt = timer->refcnt; int fail; /* Stop timer without performing RTT calculations */ @@ -169,8 +170,9 @@ static void timer_expired ( struct retry_timer *timer ) { /* Call expiry callback */ timer->expired ( timer, fail ); + /* If refcnt is NULL, then timer may already have been freed */ - ref_put ( timer->refcnt ); + ref_put ( refcnt ); } /** @@ -198,7 +200,4 @@ static void retry_step ( struct process *process __unused ) { } /** Retry timer process */ -struct process retry_process __permanent_process = { - .list = LIST_HEAD_INIT ( retry_process.list ), - .step = retry_step, -}; +PERMANENT_PROCESS ( retry_process, retry_step ); diff --git a/src/net/tcp.c b/src/net/tcp.c index a6e0dbdb..1d93d06f 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -391,6 +391,25 @@ static size_t tcp_xmit_win ( struct tcp_connection *tcp ) { return len; } +/** + * Check data-transfer flow control window + * + * @v tcp TCP connection + * @ret len Length of window + */ +static size_t tcp_xfer_window ( struct tcp_connection *tcp ) { + + /* Not ready if data queue is non-empty. This imposes a limit + * of only one unACKed packet in the TX queue at any time; we + * do this to conserve memory usage. + */ + if ( ! list_empty ( &tcp->tx_queue ) ) + return 0; + + /* Return TCP window length */ + return tcp_xmit_win ( tcp ); +} + /** * Process TCP transmit queue * @@ -1084,6 +1103,7 @@ static int tcp_rx ( struct io_buffer *iobuf, unsigned int flags; size_t len; uint32_t seq_len; + size_t old_xfer_window; int rc; /* Sanity check packet */ @@ -1146,6 +1166,9 @@ static int tcp_rx ( struct io_buffer *iobuf, goto discard; } + /* Record old data-transfer window */ + old_xfer_window = tcp_xfer_window ( tcp ); + /* Handle ACK, if present */ if ( flags & TCP_ACK ) { if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) { @@ -1192,6 +1215,10 @@ static int tcp_rx ( struct io_buffer *iobuf, start_timer_fixed ( &tcp->wait, ( 2 * TCP_MSL ) ); } + /* Notify application if window has changed */ + if ( tcp_xfer_window ( tcp ) != old_xfer_window ) + xfer_window_changed ( &tcp->xfer ); + return 0; discard: @@ -1257,25 +1284,6 @@ static void tcp_xfer_close ( struct tcp_connection *tcp, int rc ) { tcp_xmit ( tcp ); } -/** - * Check flow control window - * - * @v tcp TCP connection - * @ret len Length of window - */ -static size_t tcp_xfer_window ( struct tcp_connection *tcp ) { - - /* Not ready if data queue is non-empty. This imposes a limit - * of only one unACKed packet in the TX queue at any time; we - * do this to conserve memory usage. - */ - if ( ! list_empty ( &tcp->tx_queue ) ) - return 0; - - /* Return TCP window length */ - return tcp_xmit_win ( tcp ); -} - /** * Deliver datagram as I/O buffer * diff --git a/src/net/tcp/ftp.c b/src/net/tcp/ftp.c index 957f05cc..e5bc8e9f 100644 --- a/src/net/tcp/ftp.c +++ b/src/net/tcp/ftp.c @@ -378,37 +378,15 @@ static void ftp_data_closed ( struct ftp_request *ftp, int rc ) { } } -/** - * Handle data delivery via FTP data channel - * - * @v ftp FTP request - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int ftp_data_deliver ( struct ftp_request *ftp, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - int rc; - - if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) { - DBGC ( ftp, "FTP %p failed to deliver data: %s\n", - ftp, strerror ( rc ) ); - return rc; - } - - return 0; -} - /** FTP data channel interface operations */ static struct interface_operation ftp_data_operations[] = { - INTF_OP ( xfer_deliver, struct ftp_request *, ftp_data_deliver ), INTF_OP ( intf_close, struct ftp_request *, ftp_data_closed ), }; /** FTP data channel interface descriptor */ static struct interface_descriptor ftp_data_desc = - INTF_DESC ( struct ftp_request, data, ftp_data_operations ); + INTF_DESC_PASSTHRU ( struct ftp_request, data, ftp_data_operations, + xfer ); /***************************************************************************** * @@ -423,7 +401,8 @@ static struct interface_operation ftp_xfer_operations[] = { /** FTP data transfer interface descriptor */ static struct interface_descriptor ftp_xfer_desc = - INTF_DESC ( struct ftp_request, xfer, ftp_xfer_operations ); + INTF_DESC_PASSTHRU ( struct ftp_request, xfer, ftp_xfer_operations, + data ); /***************************************************************************** * diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index 598f2893..cee75133 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -44,10 +44,25 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include +#include #include FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); +/** Block size used for HTTP block device request */ +#define HTTP_BLKSIZE 512 + +/** HTTP flags */ +enum http_flags { + /** Request is waiting to be transmitted */ + HTTP_TX_PENDING = 0x0001, + /** Fetch header only */ + HTTP_HEAD_ONLY = 0x0002, + /** Keep connection alive */ + HTTP_KEEPALIVE = 0x0004, +}; + /** HTTP receive state */ enum http_rx_state { HTTP_RX_RESPONSE = 0, @@ -55,6 +70,7 @@ enum http_rx_state { HTTP_RX_CHUNK_LEN, HTTP_RX_DATA, HTTP_RX_TRAILER, + HTTP_RX_IDLE, HTTP_RX_DEAD, }; @@ -67,29 +83,38 @@ struct http_request { struct refcnt refcnt; /** Data transfer interface */ struct interface xfer; + /** Partial transfer interface */ + struct interface partial; /** URI being fetched */ struct uri *uri; /** Transport layer interface */ struct interface socket; + /** Flags */ + unsigned int flags; + /** Starting offset of partial transfer (if applicable) */ + size_t partial_start; + /** Length of partial transfer (if applicable) */ + size_t partial_len; + /** TX process */ struct process process; - /** HTTP response code */ - unsigned int response; - /** HTTP Content-Length */ - size_t content_length; - /** HTTP is using Transfer-Encoding: chunked */ - int chunked; - /** Current chunk length */ - size_t chunk_len; - /** Received length */ - size_t rx_len; /** RX state */ enum http_rx_state rx_state; + /** Received length */ + size_t rx_len; + /** Length remaining (or 0 if unknown) */ + size_t remaining; + /** HTTP is using Transfer-Encoding: chunked */ + int chunked; + /** Current chunk length remaining (if applicable) */ + size_t chunk_remaining; /** Line buffer for received header lines */ struct line_buffer linebuf; + /** Receive data buffer (if applicable) */ + userptr_t rx_buffer; }; /** @@ -107,12 +132,12 @@ static void http_free ( struct refcnt *refcnt ) { }; /** - * Mark HTTP request as complete + * Close HTTP request * * @v http HTTP request * @v rc Return status code */ -static void http_done ( struct http_request *http, int rc ) { +static void http_close ( struct http_request *http, int rc ) { /* Prevent further processing of any current packet */ http->rx_state = HTTP_RX_DEAD; @@ -120,11 +145,11 @@ static void http_done ( struct http_request *http, int rc ) { /* If we had a Content-Length, and the received content length * isn't correct, flag an error */ - if ( http->content_length && - ( http->content_length != http->rx_len ) ) { + if ( http->remaining != 0 ) { DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n", - http, http->rx_len, http->content_length ); - rc = -EIO; + http, http->rx_len, ( http->rx_len + http->remaining ) ); + if ( rc == 0 ) + rc = -EIO; } /* Remove process */ @@ -132,9 +157,40 @@ static void http_done ( struct http_request *http, int rc ) { /* Close all data transfer interfaces */ intf_shutdown ( &http->socket, rc ); + intf_shutdown ( &http->partial, rc ); intf_shutdown ( &http->xfer, rc ); } +/** + * Mark HTTP request as completed successfully + * + * @v http HTTP request + */ +static void http_done ( struct http_request *http ) { + + /* If we had a Content-Length, and the received content length + * isn't correct, force an error + */ + if ( http->remaining != 0 ) { + http_close ( http, -EIO ); + return; + } + + /* Enter idle state */ + http->rx_state = HTTP_RX_IDLE; + http->rx_len = 0; + assert ( http->remaining == 0 ); + assert ( http->chunked == 0 ); + assert ( http->chunk_remaining == 0 ); + + /* Close partial transfer interface */ + intf_restart ( &http->partial, 0 ); + + /* Close everything unless we are keeping the connection alive */ + if ( ! ( http->flags & HTTP_KEEPALIVE ) ) + http_close ( http, 0 ); +} + /** * Convert HTTP response code to return status code * @@ -144,6 +200,7 @@ static void http_done ( struct http_request *http, int rc ) { static int http_response_to_rc ( unsigned int response ) { switch ( response ) { case 200: + case 206: case 301: case 302: return 0; @@ -167,6 +224,7 @@ static int http_response_to_rc ( unsigned int response ) { */ static int http_rx_response ( struct http_request *http, char *response ) { char *spc; + unsigned int code; int rc; DBGC ( http, "HTTP %p response \"%s\"\n", http, response ); @@ -179,8 +237,8 @@ static int http_rx_response ( struct http_request *http, char *response ) { spc = strchr ( response, ' ' ); if ( ! spc ) return -EIO; - http->response = strtoul ( spc, NULL, 10 ); - if ( ( rc = http_response_to_rc ( http->response ) ) != 0 ) + code = strtoul ( spc, NULL, 10 ); + if ( ( rc = http_response_to_rc ( code ) ) != 0 ) return rc; /* Move to received headers */ @@ -219,19 +277,40 @@ static int http_rx_location ( struct http_request *http, const char *value ) { */ static int http_rx_content_length ( struct http_request *http, const char *value ) { + struct block_device_capacity capacity; + size_t content_len; char *endp; - http->content_length = strtoul ( value, &endp, 10 ); + /* Parse content length */ + content_len = strtoul ( value, &endp, 10 ); if ( *endp != '\0' ) { DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", http, value ); return -EIO; } + /* If we already have an expected content length, and this + * isn't it, then complain + */ + if ( http->remaining && ( http->remaining != content_len ) ) { + DBGC ( http, "HTTP %p incorrect Content-Length %zd (expected " + "%zd)\n", http, content_len, http->remaining ); + return -EIO; + } + if ( ! ( http->flags & HTTP_HEAD_ONLY ) ) + http->remaining = content_len; + /* Use seek() to notify recipient of filesize */ - xfer_seek ( &http->xfer, http->content_length ); + xfer_seek ( &http->xfer, http->remaining ); xfer_seek ( &http->xfer, 0 ); + /* Report block device capacity if applicable */ + if ( http->flags & HTTP_HEAD_ONLY ) { + capacity.blocks = ( content_len / HTTP_BLKSIZE ); + capacity.blksize = HTTP_BLKSIZE; + capacity.max_count = -1U; + block_capacity ( &http->partial, &capacity ); + } return 0; } @@ -301,14 +380,15 @@ static int http_rx_header ( struct http_request *http, char *header ) { /* An empty header line marks the end of this phase */ if ( ! header[0] ) { empty_line_buffer ( &http->linebuf ); - if ( http->rx_state == HTTP_RX_HEADER ) { + if ( ( http->rx_state == HTTP_RX_HEADER ) && + ( ! ( http->flags & HTTP_HEAD_ONLY ) ) ) { DBGC ( http, "HTTP %p start of data\n", http ); http->rx_state = ( http->chunked ? HTTP_RX_CHUNK_LEN : HTTP_RX_DATA ); return 0; } else { DBGC ( http, "HTTP %p end of trailer\n", http ); - http_done ( http, 0 ); + http_done ( http ); return 0; } } @@ -350,7 +430,7 @@ static int http_rx_chunk_len ( struct http_request *http, char *length ) { return 0; /* Parse chunk length */ - http->chunk_len = strtoul ( length, &endp, 16 ); + http->chunk_remaining = strtoul ( length, &endp, 16 ); if ( *endp != '\0' ) { DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n", http, length ); @@ -358,7 +438,7 @@ static int http_rx_chunk_len ( struct http_request *http, char *length ) { } /* Terminate chunked encoding if applicable */ - if ( http->chunk_len == 0 ) { + if ( http->chunk_remaining == 0 ) { DBGC ( http, "HTTP %p end of chunks\n", http ); http->chunked = 0; http->rx_state = HTTP_RX_TRAILER; @@ -367,8 +447,8 @@ static int http_rx_chunk_len ( struct http_request *http, char *length ) { /* Use seek() to notify recipient of new filesize */ DBGC ( http, "HTTP %p start of chunk of length %zd\n", - http, http->chunk_len ); - xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_len ) ); + http, http->chunk_remaining ); + xfer_seek ( &http->xfer, ( http->rx_len + http->chunk_remaining ) ); xfer_seek ( &http->xfer, http->rx_len ); /* Start receiving data */ @@ -414,34 +494,60 @@ static int http_socket_deliver ( struct http_request *http, int rc = 0; while ( iobuf && iob_len ( iobuf ) ) { + switch ( http->rx_state ) { + case HTTP_RX_IDLE: + /* Receiving any data in this state is an error */ + DBGC ( http, "HTTP %p received %zd bytes while %s\n", + http, iob_len ( iobuf ), + ( ( http->rx_state == HTTP_RX_IDLE ) ? + "idle" : "dead" ) ); + rc = -EPROTO; + goto done; case HTTP_RX_DEAD: /* Do no further processing */ goto done; case HTTP_RX_DATA: /* Pass received data to caller */ data_len = iob_len ( iobuf ); - if ( http->chunk_len && ( http->chunk_len < data_len )){ - data_len = http->chunk_len; + if ( http->chunk_remaining && + ( http->chunk_remaining < data_len ) ) { + data_len = http->chunk_remaining; + } + if ( http->remaining && + ( http->remaining < data_len ) ) { + data_len = http->remaining; + } + if ( http->rx_buffer != UNULL ) { + /* Copy to partial transfer buffer */ + copy_to_user ( http->rx_buffer, http->rx_len, + iobuf->data, data_len ); + iob_pull ( iobuf, data_len ); + } else if ( data_len < iob_len ( iobuf ) ) { + /* Deliver partial buffer as raw data */ rc = xfer_deliver_raw ( &http->xfer, iobuf->data, data_len ); iob_pull ( iobuf, data_len ); + if ( rc != 0 ) + goto done; } else { - rc = xfer_deliver_iob ( &http->xfer, - iob_disown ( iobuf ) ); - } - if ( rc != 0 ) - goto done; - if ( http->chunk_len ) { - http->chunk_len -= data_len; - if ( http->chunk_len == 0 ) - http->rx_state = HTTP_RX_CHUNK_LEN; + /* Deliver whole I/O buffer */ + if ( ( rc = xfer_deliver_iob ( &http->xfer, + iob_disown ( iobuf ) ) ) != 0 ) + goto done; } http->rx_len += data_len; - if ( http->content_length && - ( http->rx_len >= http->content_length ) ) { - http_done ( http, 0 ); - goto done; + if ( http->chunk_remaining ) { + http->chunk_remaining -= data_len; + if ( http->chunk_remaining == 0 ) + http->rx_state = HTTP_RX_CHUNK_LEN; + } + if ( http->remaining ) { + http->remaining -= data_len; + if ( ( http->remaining == 0 ) && + ( http->rx_state == HTTP_RX_DATA ) ) { + http_done ( http ); + } } break; case HTTP_RX_RESPONSE: @@ -475,19 +581,31 @@ static int http_socket_deliver ( struct http_request *http, done: if ( rc ) - http_done ( http, rc ); + http_close ( http, rc ); free_iob ( iobuf ); return rc; } +/** + * Check HTTP socket flow control window + * + * @v http HTTP request + * @ret len Length of window + */ +static size_t http_socket_window ( struct http_request *http __unused ) { + + /* Window is always open. This is to prevent TCP from + * stalling if our parent window is not currently open. + */ + return ( ~( ( size_t ) 0 ) ); +} + /** * HTTP process * - * @v process Process + * @v http HTTP request */ -static void http_step ( struct process *process ) { - struct http_request *http = - container_of ( process, struct http_request, process ); +static void http_step ( struct http_request *http ) { const char *host = http->uri->host; const char *user = http->uri->user; const char *password = @@ -500,50 +618,181 @@ static void http_step ( struct process *process ) { int rc; int request_len = unparse_uri ( NULL, 0, http->uri, URI_PATH_BIT | URI_QUERY_BIT ); + char request[ request_len + 1 /* NUL */ ]; + char range[48]; /* Enough for two 64-bit integers in decimal */ + int partial; - if ( xfer_window ( &http->socket ) ) { - char request[request_len + 1]; + /* Do nothing if we have already transmitted the request */ + if ( ! ( http->flags & HTTP_TX_PENDING ) ) + return; - /* Construct path?query request */ - unparse_uri ( request, sizeof ( request ), http->uri, - URI_PATH_BIT | URI_QUERY_BIT ); + /* Do nothing until socket is ready */ + if ( ! xfer_window ( &http->socket ) ) + return; - /* We want to execute only once */ - process_del ( &http->process ); + /* Construct path?query request */ + unparse_uri ( request, sizeof ( request ), http->uri, + URI_PATH_BIT | URI_QUERY_BIT ); - /* Construct authorisation, if applicable */ - if ( user ) { - /* Make "user:password" string from decoded fields */ - snprintf ( ( ( char * ) user_pw ), sizeof ( user_pw ), - "%s:%s", user, password ); + /* Construct authorisation, if applicable */ + if ( user ) { + /* Make "user:password" string from decoded fields */ + snprintf ( ( ( char * ) user_pw ), sizeof ( user_pw ), + "%s:%s", user, password ); - /* Base64-encode the "user:password" string */ - base64_encode ( user_pw, user_pw_len, user_pw_base64 ); - } - - /* Send GET request */ - if ( ( rc = xfer_printf ( &http->socket, - "GET %s%s HTTP/1.1\r\n" - "User-Agent: iPXE/" VERSION "\r\n" - "%s%s%s" - "Host: %s\r\n" - "\r\n", - http->uri->path ? "" : "/", - request, - ( user ? - "Authorization: Basic " : "" ), - ( user ? user_pw_base64 : "" ), - ( user ? "\r\n" : "" ), - host ) ) != 0 ) { - http_done ( http, rc ); - } + /* Base64-encode the "user:password" string */ + base64_encode ( user_pw, user_pw_len, user_pw_base64 ); } + + /* Force a HEAD request if we have nowhere to send any received data */ + if ( ( xfer_window ( &http->xfer ) == 0 ) && + ( http->rx_buffer == UNULL ) ) { + http->flags |= ( HTTP_HEAD_ONLY | HTTP_KEEPALIVE ); + } + + /* Determine type of request */ + partial = ( http->partial_len != 0 ); + snprintf ( range, sizeof ( range ), "%zd-%zd", http->partial_start, + ( http->partial_start + http->partial_len - 1 ) ); + + /* Mark request as transmitted */ + http->flags &= ~HTTP_TX_PENDING; + + /* Send GET request */ + if ( ( rc = xfer_printf ( &http->socket, + "%s %s%s HTTP/1.1\r\n" + "User-Agent: iPXE/" VERSION "\r\n" + "Host: %s%s%s\r\n" + "%s%s%s%s%s%s%s" + "\r\n", + ( ( http->flags & HTTP_HEAD_ONLY ) ? + "HEAD" : "GET" ), + ( http->uri->path ? "" : "/" ), + request, host, + ( http->uri->port ? + ":" : "" ), + ( http->uri->port ? + http->uri->port : "" ), + ( ( http->flags & HTTP_KEEPALIVE ) ? + "Connection: Keep-Alive\r\n" : "" ), + ( partial ? "Range: bytes=" : "" ), + ( partial ? range : "" ), + ( partial ? "\r\n" : "" ), + ( user ? + "Authorization: Basic " : "" ), + ( user ? user_pw_base64 : "" ), + ( user ? "\r\n" : "" ) ) ) != 0 ) { + http_close ( http, rc ); + } +} + +/** + * Check HTTP data transfer flow control window + * + * @v http HTTP request + * @ret len Length of window + */ +static size_t http_xfer_window ( struct http_request *http ) { + + /* New block commands may be issued only when we are idle */ + return ( ( http->rx_state == HTTP_RX_IDLE ) ? 1 : 0 ); +} + +/** + * Initiate HTTP partial read + * + * @v http HTTP request + * @v partial Partial transfer interface + * @v offset Starting offset + * @v buffer Data buffer + * @v len Length + * @ret rc Return status code + */ +static int http_partial_read ( struct http_request *http, + struct interface *partial, + size_t offset, userptr_t buffer, size_t len ) { + + /* Sanity check */ + if ( http_xfer_window ( http ) == 0 ) + return -EBUSY; + + /* Initialise partial transfer parameters */ + http->rx_buffer = buffer; + http->partial_start = offset; + http->partial_len = len; + http->remaining = len; + + /* Schedule request */ + http->rx_state = HTTP_RX_RESPONSE; + http->flags = ( HTTP_TX_PENDING | HTTP_KEEPALIVE ); + if ( ! len ) + http->flags |= HTTP_HEAD_ONLY; + process_add ( &http->process ); + + /* Attach to parent interface and return */ + intf_plug_plug ( &http->partial, partial ); + + return 0; +} + +/** + * Issue HTTP block device read + * + * @v http HTTP request + * @v block Block data interface + * @v lba Starting logical block address + * @v count Number of blocks to transfer + * @v buffer Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int http_block_read ( struct http_request *http, + struct interface *block, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len __unused ) { + + return http_partial_read ( http, block, ( lba * HTTP_BLKSIZE ), + buffer, ( count * HTTP_BLKSIZE ) ); +} + +/** + * Read HTTP block device capacity + * + * @v http HTTP request + * @v block Block data interface + * @ret rc Return status code + */ +static int http_block_read_capacity ( struct http_request *http, + struct interface *block ) { + + return http_partial_read ( http, block, 0, 0, 0 ); +} + +/** + * Describe HTTP device in an ACPI table + * + * @v http HTTP request + * @v acpi ACPI table + * @v len Length of ACPI table + * @ret rc Return status code + */ +static int http_acpi_describe ( struct http_request *http, + struct acpi_description_header *acpi, + size_t len ) { + + DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", + http ); + ( void ) acpi; + ( void ) len; + return 0; } /** HTTP socket interface operations */ static struct interface_operation http_socket_operations[] = { + INTF_OP ( xfer_window, struct http_request *, http_socket_window ), INTF_OP ( xfer_deliver, struct http_request *, http_socket_deliver ), - INTF_OP ( intf_close, struct http_request *, http_done ), + INTF_OP ( xfer_window_changed, struct http_request *, http_step ), + INTF_OP ( intf_close, struct http_request *, http_close ), }; /** HTTP socket interface descriptor */ @@ -551,9 +800,23 @@ static struct interface_descriptor http_socket_desc = INTF_DESC_PASSTHRU ( struct http_request, socket, http_socket_operations, xfer ); +/** HTTP partial transfer interface operations */ +static struct interface_operation http_partial_operations[] = { + INTF_OP ( intf_close, struct http_request *, http_close ), +}; + +/** HTTP partial transfer interface descriptor */ +static struct interface_descriptor http_partial_desc = + INTF_DESC ( struct http_request, partial, http_partial_operations ); + /** HTTP data transfer interface operations */ static struct interface_operation http_xfer_operations[] = { - INTF_OP ( intf_close, struct http_request *, http_done ), + INTF_OP ( xfer_window, struct http_request *, http_xfer_window ), + INTF_OP ( block_read, struct http_request *, http_block_read ), + INTF_OP ( block_read_capacity, struct http_request *, + http_block_read_capacity ), + INTF_OP ( intf_close, struct http_request *, http_close ), + INTF_OP ( acpi_describe, struct http_request *, http_acpi_describe ), }; /** HTTP data transfer interface descriptor */ @@ -561,6 +824,10 @@ static struct interface_descriptor http_xfer_desc = INTF_DESC_PASSTHRU ( struct http_request, xfer, http_xfer_operations, socket ); +/** HTTP process descriptor */ +static struct process_descriptor http_process_desc = + PROC_DESC_ONCE ( struct http_request, process, http_step ); + /** * Initiate an HTTP connection, with optional filter * @@ -589,9 +856,11 @@ int http_open_filter ( struct interface *xfer, struct uri *uri, return -ENOMEM; ref_init ( &http->refcnt, http_free ); intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt ); + intf_init ( &http->partial, &http_partial_desc, &http->refcnt ); http->uri = uri_get ( uri ); intf_init ( &http->socket, &http_socket_desc, &http->refcnt ); - process_init ( &http->process, http_step, &http->refcnt ); + process_init ( &http->process, &http_process_desc, &http->refcnt ); + http->flags = HTTP_TX_PENDING; /* Open socket */ memset ( &server, 0, sizeof ( server ) ); @@ -614,7 +883,7 @@ int http_open_filter ( struct interface *xfer, struct uri *uri, err: DBGC ( http, "HTTP %p could not create request: %s\n", http, strerror ( rc ) ); - http_done ( http, rc ); + http_close ( http, rc ); ref_put ( &http->refcnt ); return rc; } diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index cde3ed6d..8beeb877 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -1304,6 +1304,24 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi, * */ +/** + * Pause TX engine + * + * @v iscsi iSCSI session + */ +static void iscsi_tx_pause ( struct iscsi_session *iscsi ) { + process_del ( &iscsi->process ); +} + +/** + * Resume TX engine + * + * @v iscsi iSCSI session + */ +static void iscsi_tx_resume ( struct iscsi_session *iscsi ) { + process_add ( &iscsi->process ); +} + /** * Start up a new TX PDU * @@ -1315,8 +1333,7 @@ static int iscsi_rx_login_response ( struct iscsi_session *iscsi, static void iscsi_start_tx ( struct iscsi_session *iscsi ) { assert ( iscsi->tx_state == ISCSI_TX_IDLE ); - assert ( ! process_running ( &iscsi->process ) ); - + /* Initialise TX BHS */ memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) ); @@ -1324,7 +1341,7 @@ static void iscsi_start_tx ( struct iscsi_session *iscsi ) { iscsi->tx_state = ISCSI_TX_BHS; /* Start transmission process */ - process_add ( &iscsi->process ); + iscsi_tx_resume ( iscsi ); } /** @@ -1405,7 +1422,7 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) { struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; /* Stop transmission process */ - process_del ( &iscsi->process ); + iscsi_tx_pause ( iscsi ); switch ( common->opcode & ISCSI_OPCODE_MASK ) { case ISCSI_OPCODE_DATA_OUT: @@ -1427,9 +1444,7 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) { * * Constructs data to be sent for the current TX state */ -static void iscsi_tx_step ( struct process *process ) { - struct iscsi_session *iscsi = - container_of ( process, struct iscsi_session, process ); +static void iscsi_tx_step ( struct iscsi_session *iscsi ) { struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; int ( * tx ) ( struct iscsi_session *iscsi ); enum iscsi_tx_state next_state; @@ -1460,8 +1475,8 @@ static void iscsi_tx_step ( struct process *process ) { next_state = ISCSI_TX_IDLE; break; case ISCSI_TX_IDLE: - /* Stop processing */ - iscsi_tx_done ( iscsi ); + /* Nothing to do; pause processing */ + iscsi_tx_pause ( iscsi ); return; default: assert ( 0 ); @@ -1470,7 +1485,10 @@ static void iscsi_tx_step ( struct process *process ) { /* Check for window availability, if needed */ if ( tx_len && ( xfer_window ( &iscsi->socket ) == 0 ) ) { - /* Cannot transmit at this point; stop processing */ + /* Cannot transmit at this point; pause + * processing and wait for window to reopen + */ + iscsi_tx_pause ( iscsi ); return; } @@ -1485,9 +1503,19 @@ static void iscsi_tx_step ( struct process *process ) { /* Move to next state */ iscsi->tx_state = next_state; + + /* If we have moved to the idle state, mark + * transmission as complete + */ + if ( iscsi->tx_state == ISCSI_TX_IDLE ) + iscsi_tx_done ( iscsi ); } } +/** iSCSI TX process descriptor */ +static struct process_descriptor iscsi_process_desc = + PROC_DESC ( struct iscsi_session, process, iscsi_tx_step ); + /** * Receive basic header segment of an iSCSI PDU * @@ -1694,6 +1722,8 @@ static int iscsi_vredirect ( struct iscsi_session *iscsi, int type, /** iSCSI socket interface operations */ static struct interface_operation iscsi_socket_operations[] = { INTF_OP ( xfer_deliver, struct iscsi_session *, iscsi_socket_deliver ), + INTF_OP ( xfer_window_changed, struct iscsi_session *, + iscsi_tx_resume ), INTF_OP ( xfer_vredirect, struct iscsi_session *, iscsi_vredirect ), INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ), }; @@ -2034,7 +2064,7 @@ static int iscsi_open ( struct interface *parent, struct uri *uri ) { intf_init ( &iscsi->control, &iscsi_control_desc, &iscsi->refcnt ); intf_init ( &iscsi->data, &iscsi_data_desc, &iscsi->refcnt ); intf_init ( &iscsi->socket, &iscsi_socket_desc, &iscsi->refcnt ); - process_init_stopped ( &iscsi->process, iscsi_tx_step, + process_init_stopped ( &iscsi->process, &iscsi_process_desc, &iscsi->refcnt ); /* Parse root path */ diff --git a/src/net/tls.c b/src/net/tls.c index 5e22221d..ace719c1 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -580,6 +580,62 @@ static void tls_verify_handshake ( struct tls_session *tls, void *out ) { digest_final ( sha1, sha1_ctx, sha1_digest ); } +/****************************************************************************** + * + * TX state machine transitions + * + ****************************************************************************** + */ + +/** + * Resume TX state machine + * + * @v tls TLS session + */ +static void tls_tx_resume ( struct tls_session *tls ) { + process_add ( &tls->process ); +} + +/** + * Enter TX state machine active state + * + * @v tls TLS session + * @v state TX state + */ +static void tls_tx_start ( struct tls_session *tls, enum tls_tx_state state ) { + + /* Enter specified state */ + tls->tx_state = state; + + /* Resume state machine */ + tls_tx_resume ( tls ); +} + +/** + * Enter TX state machine idle state + * + * @v tls TLS session + */ +static void tls_tx_none ( struct tls_session *tls ) { + + /* Enter idle state */ + tls->tx_state = TLS_TX_NONE; +} + +/** + * Enter TX state machine data state + * + * @v tls TLS session + */ +static void tls_tx_data ( struct tls_session *tls ) { + + /* Enter data state */ + tls->tx_state = TLS_TX_DATA; + + /* Send notification of a window change */ + xfer_window_changed ( &tls->plainstream ); +} + /****************************************************************************** * * Record handling @@ -929,7 +985,7 @@ static int tls_new_server_hello_done ( struct tls_session *tls, } /* Start sending the Client Key Exchange */ - tls->tx_state = TLS_TX_CLIENT_KEY_EXCHANGE; + tls_tx_start ( tls, TLS_TX_CLIENT_KEY_EXCHANGE ); return 0; } @@ -946,9 +1002,10 @@ static int tls_new_finished ( struct tls_session *tls, void *data, size_t len ) { /* FIXME: Handle this properly */ - tls->tx_state = TLS_TX_DATA; + tls_tx_data ( tls ); ( void ) data; ( void ) len; + return 0; } @@ -1623,6 +1680,7 @@ static int tls_cipherstream_deliver ( struct tls_session *tls, static struct interface_operation tls_cipherstream_ops[] = { INTF_OP ( xfer_deliver, struct tls_session *, tls_cipherstream_deliver ), + INTF_OP ( xfer_window_changed, struct tls_session *, tls_tx_resume ), INTF_OP ( intf_close, struct tls_session *, tls_close ), }; @@ -1641,11 +1699,9 @@ static struct interface_descriptor tls_cipherstream_desc = /** * TLS TX state machine * - * @v process TLS process + * @v tls TLS session */ -static void tls_step ( struct process *process ) { - struct tls_session *tls = - container_of ( process, struct tls_session, process ); +static void tls_tx_step ( struct tls_session *tls ) { int rc; /* Wait for cipherstream to become ready */ @@ -1663,7 +1719,7 @@ static void tls_step ( struct process *process ) { tls, strerror ( rc ) ); goto err; } - tls->tx_state = TLS_TX_NONE; + tls_tx_none ( tls ); break; case TLS_TX_CLIENT_KEY_EXCHANGE: /* Send Client Key Exchange */ @@ -1672,7 +1728,7 @@ static void tls_step ( struct process *process ) { "%s\n", tls, strerror ( rc ) ); goto err; } - tls->tx_state = TLS_TX_CHANGE_CIPHER; + tls_tx_start ( tls, TLS_TX_CHANGE_CIPHER ); break; case TLS_TX_CHANGE_CIPHER: /* Send Change Cipher, and then change the cipher in use */ @@ -1689,7 +1745,7 @@ static void tls_step ( struct process *process ) { goto err; } tls->tx_seq = 0; - tls->tx_state = TLS_TX_FINISHED; + tls_tx_start ( tls, TLS_TX_FINISHED ); break; case TLS_TX_FINISHED: /* Send Finished */ @@ -1698,7 +1754,7 @@ static void tls_step ( struct process *process ) { tls, strerror ( rc ) ); goto err; } - tls->tx_state = TLS_TX_NONE; + tls_tx_none ( tls ); break; case TLS_TX_DATA: /* Nothing to do */ @@ -1713,6 +1769,10 @@ static void tls_step ( struct process *process ) { tls_close ( tls, rc ); } +/** TLS TX process descriptor */ +static struct process_descriptor tls_process_desc = + PROC_DESC_ONCE ( struct tls_session, process, tls_tx_step ); + /****************************************************************************** * * Instantiator @@ -1743,8 +1803,8 @@ int add_tls ( struct interface *xfer, struct interface **next ) { ( sizeof ( tls->pre_master_secret.random ) ) ); digest_init ( &md5_algorithm, tls->handshake_md5_ctx ); digest_init ( &sha1_algorithm, tls->handshake_sha1_ctx ); - tls->tx_state = TLS_TX_CLIENT_HELLO; - process_init ( &tls->process, tls_step, &tls->refcnt ); + process_init_stopped ( &tls->process, &tls_process_desc, &tls->refcnt ); + tls_tx_start ( tls, TLS_TX_CLIENT_HELLO ); /* Attach to parent interface, mortalise self, and return */ intf_plug_plug ( &tls->plainstream, xfer ); diff --git a/src/net/vlan.c b/src/net/vlan.c index 9ac560f1..2147f91c 100644 --- a/src/net/vlan.c +++ b/src/net/vlan.c @@ -91,12 +91,13 @@ static int vlan_transmit ( struct net_device *netdev, const void *ll_dest; const void *ll_source; uint16_t net_proto; + unsigned int flags; int rc; /* Strip link-layer header and preserve link-layer header fields */ ll_protocol = netdev->ll_protocol; if ( ( rc = ll_protocol->pull ( netdev, iobuf, &ll_dest, &ll_source, - &net_proto ) ) != 0 ) { + &net_proto, &flags ) ) != 0 ) { DBGC ( netdev, "VLAN %s could not parse link-layer header: " "%s\n", netdev->name, strerror ( rc ) ); return rc; @@ -214,10 +215,12 @@ struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) { * @v trunk Trunk network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code */ static int vlan_rx ( struct io_buffer *iobuf, struct net_device *trunk, - const void *ll_dest, const void *ll_source ) { + const void *ll_dest, const void *ll_source, + unsigned int flags __unused ) { struct vlan_header *vlanhdr = iobuf->data; struct net_device *netdev; struct ll_protocol *ll_protocol; diff --git a/src/usr/fcmgmt.c b/src/usr/fcmgmt.c index 1af723d1..f46c7d6b 100644 --- a/src/usr/fcmgmt.c +++ b/src/usr/fcmgmt.c @@ -74,8 +74,7 @@ void fcpeerstat ( struct fc_peer *peer ) { } list_for_each_entry ( ulp, &peer->ulps, list ) { - printf ( " [Type %02x usage %d link:", - ulp->type, ulp->usage ); + printf ( " [Type %02x link:", ulp->type ); if ( fc_link_ok ( &ulp->link ) ) { printf ( " up, params" ); param = ulp->param; diff --git a/src/usr/lotest.c b/src/usr/lotest.c index 7f9f2fd0..6ed31ea5 100644 --- a/src/usr/lotest.c +++ b/src/usr/lotest.c @@ -47,12 +47,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v netdev Network device * @v ll_dest Link-layer destination address * @v ll_source Link-layer source address + * @v flags Packet flags * @ret rc Return status code */ static int lotest_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, const void *ll_dest __unused, - const void *ll_source __unused ) { + const void *ll_source __unused, + unsigned int flags __unused ) { free_iob ( iobuf ); return -ENOTSUP; } @@ -97,6 +99,7 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, const void *ll_dest; const void *ll_source; uint16_t net_proto; + unsigned int flags; unsigned int i; unsigned int successes; int rc; @@ -166,7 +169,8 @@ int loopback_test ( struct net_device *sender, struct net_device *receiver, /* Check received packet */ if ( ( rc = receiver->ll_protocol->pull ( receiver, iobuf, &ll_dest, &ll_source, - &net_proto ) ) != 0 ){ + &net_proto, + &flags ) ) != 0 ) { printf ( "\nFailed to strip link-layer header: %s", strerror ( rc ) ); goto done;