From d6115c91cfa4e3de72b87d3035b418d13b81e197 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Jun 2011 10:19:23 +0100 Subject: [PATCH 01/38] [netdevice] Allow non-completion TX errors to be recorded Allow TX errors to be recorded against a network device even when the packet didn't make it as far as netdev_tx(). Inspired-by: Dominik Russenberger Signed-off-by: Michael Brown --- src/include/ipxe/netdevice.h | 2 ++ src/net/netdevice.c | 32 +++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index e49191f4..64285984 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -586,6 +586,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 ); diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 9a8a3aaf..54b41b3f 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; } From cc7c2a9dcd3d501561bcc7ef67c67601404248bf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Jun 2011 10:21:30 +0100 Subject: [PATCH 02/38] [ipv4] Record ARP resolution errors At the time of attempting ARP resolution, we already know the transmitting network device. We can therefore record ARP errors using netdev_tx_err() so that they show up in the output of "ifstat". Inspired-by: Dominik Russenberger Signed-off-by: Michael Brown --- src/net/ipv4.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 465df45e..5bb48f61 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -347,6 +347,8 @@ static int ipv4_tx ( struct io_buffer *iobuf, ll_dest ) ) != 0 ) { DBG ( "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; } From be600ed9967d93f5012d5277d7ce8ce89d135918 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Jun 2011 11:29:28 +0100 Subject: [PATCH 03/38] [prefix] Cope with BOOT_IMAGE= anywhere within command line Some bootloaders seem to add "BOOT_IMAGE=..." at the end of the command line; some at the start. Cope with either variation. Reported-by: Dave Hansen Signed-off-by: Michael Brown --- src/arch/i386/core/cmdline.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) 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 ); From 5763472b34724d5f320da7ecd90ed32f14c0855b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 12:51:45 +0100 Subject: [PATCH 04/38] [ftp] Remove redundant ftp_data_deliver() method ftp_data_deliver() does nothing except pass through the received data to the xfer interface, and so can be eliminated by using a pass-through interface. Signed-off-by: Michael Brown --- src/net/tcp/ftp.c | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) 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 ); /***************************************************************************** * From bf8bfa23e2364793ccdfc32627d8094a74ae87aa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 22:49:10 +0100 Subject: [PATCH 05/38] [fc] Maintain a list of Fibre Channel upper-layer protocol users Signed-off-by: Michael Brown --- src/include/ipxe/fc.h | 33 +++++++++++++++++++++++++++----- src/net/fc.c | 44 +++++++++++++++++++++++++++++-------------- src/net/fcp.c | 32 ++++++++++++++----------------- src/usr/fcmgmt.c | 3 +-- 4 files changed, 73 insertions(+), 39 deletions(-) diff --git a/src/include/ipxe/fc.h b/src/include/ipxe/fc.h index ab6b5bae..06d38baf 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,14 @@ 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; +}; + /** * Get reference to Fibre Channel upper-layer protocol * @@ -462,8 +485,8 @@ 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/net/fc.c b/src/net/fc.c index 1934fab3..a94456c8 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 ); } /** @@ -1712,7 +1727,7 @@ void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) { fc_link_err ( &ulp->link, rc ); /* Close ULP if there are no clients attached */ - if ( ulp->usage == 0 ) + if ( list_empty ( &ulp->users ) ) fc_ulp_close ( ulp, rc ); } @@ -1795,6 +1810,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/fcp.c b/src/net/fcp.c index 40cd057e..28d2095d 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 */ @@ -734,13 +734,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 +748,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 ); @@ -772,8 +772,8 @@ static int fcpdev_scsi_command ( struct fcp_device *fcpdev, /* 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 +822,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 +832,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 +894,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 */ @@ -953,8 +950,7 @@ 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_attach ( ulp, &fcpdev->user ); DBGC ( fcpdev, "FCP %p opened for %s\n", fcpdev, fc_ntoa ( wwn ) ); 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; From 5f608a44a5574ec53c791e55894179dccb85f1c6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 23:16:17 +0100 Subject: [PATCH 06/38] [fc] Send xfer_window_changed() when FCP link is established Signed-off-by: Michael Brown --- src/include/ipxe/fc.h | 44 +++++++++++++++++++++++++++++++++++++++++++ src/net/fc.c | 44 +++++++++++++++++++++++++++++++------------ src/net/fcp.c | 25 +++++++++++++++++++++++- 3 files changed, 100 insertions(+), 13 deletions(-) diff --git a/src/include/ipxe/fc.h b/src/include/ipxe/fc.h index 06d38baf..6689f394 100644 --- a/src/include/ipxe/fc.h +++ b/src/include/ipxe/fc.h @@ -455,6 +455,13 @@ struct fc_ulp_user { 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 ); }; /** @@ -479,6 +486,43 @@ 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 * diff --git a/src/net/fc.c b/src/net/fc.c index a94456c8..977ad07c 100644 --- a/src/net/fc.c +++ b/src/net/fc.c @@ -1651,6 +1651,8 @@ void fc_ulp_detach ( struct fc_ulp_user *user ) { */ 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 ) && @@ -1659,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 ) ) { @@ -1685,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; @@ -1709,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 ) ); @@ -1726,6 +1739,13 @@ 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 ( list_empty ( &ulp->users ) ) fc_ulp_close ( ulp, rc ); diff --git a/src/net/fcp.c b/src/net/fcp.c index 28d2095d..419fea3e 100644 --- a/src/net/fcp.c +++ b/src/net/fcp.c @@ -920,6 +920,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 * @@ -950,10 +970,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 ); - fc_ulp_attach ( ulp, &fcpdev->user ); + 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 ) ); From 0cc03ac76a6c636d77cecca21d045ffb87f4945b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 18:11:12 +0100 Subject: [PATCH 07/38] [tls] Send xfer_window_changed() when TLS session is established Signed-off-by: Michael Brown --- src/net/tls.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/net/tls.c b/src/net/tls.c index 5e22221d..fba5160d 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -949,6 +949,10 @@ static int tls_new_finished ( struct tls_session *tls, tls->tx_state = TLS_TX_DATA; ( void ) data; ( void ) len; + + /* Send notification of a window change */ + xfer_window_changed ( &tls->plainstream ); + return 0; } From 1e90ff0eb73b105c964f65cce9b5619a07eaf3f3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 19:25:53 +0100 Subject: [PATCH 08/38] [infiniband] Send xfer_window_changed() when CMRC connection is established Signed-off-by: Michael Brown --- src/net/infiniband/ib_cmrc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index ed388b2f..4b15314e 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -180,6 +180,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 ); From c68bf14559d57170493e4eed21fd3c05309c351e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 23 Jun 2011 16:25:48 +0100 Subject: [PATCH 09/38] [tcp] Send xfer_window_changed() when window opens Signed-off-by: Michael Brown --- src/net/tcp.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/net/tcp.c b/src/net/tcp.c index fbcf279e..4df1aed5 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 */ @@ -1145,6 +1165,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 ) { @@ -1191,6 +1214,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: @@ -1256,25 +1283,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 * From ba3633782bd36831ca5471d792e626b7e8344e0c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 22 Jun 2011 16:40:13 +0100 Subject: [PATCH 10/38] [xfer] Send xfer_window_changed() after xfer_vredirect() Modify the default action for xfer_vredirect() to automatically send xfer_window_changed() messages to both the new child and the parent interfaces. This will allow the elimination of processes that simply poll on xfer_window() to determine when a redirection has completed successfully. Signed-off-by: Michael Brown --- src/core/xfer.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) 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 ) { From e01ec74601b58f54a5e2ae7b9fd1196972034114 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 14:14:41 +0100 Subject: [PATCH 11/38] [process] Pass containing object pointer to process step() methods Give the step() method a pointer to the containing object, rather than a pointer to the process. This is consistent with the operation of interface methods, and allows a single function to serve as both an interface method and a process step() method. Signed-off-by: Michael Brown --- src/core/hw.c | 22 ++++--- src/core/process.c | 42 ++++++++---- src/core/resolv.c | 12 ++-- src/drivers/block/scsi.c | 13 ++-- src/include/ipxe/process.h | 122 +++++++++++++++++++++++++++++++---- src/net/80211/net80211.c | 19 +++--- src/net/fcels.c | 13 ++-- src/net/fcns.c | 13 ++-- src/net/fcp.c | 13 ++-- src/net/infiniband.c | 5 +- src/net/infiniband/ib_cmrc.c | 12 ++-- src/net/netdevice.c | 5 +- src/net/retry.c | 5 +- src/net/tcp/http.c | 12 ++-- src/net/tcp/iscsi.c | 10 +-- src/net/tls.c | 12 ++-- 16 files changed, 229 insertions(+), 101 deletions(-) diff --git a/src/core/hw.c b/src/core/hw.c index aca55809..e9c9ffc1 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,16 @@ static void hw_step ( struct process *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 struct process_descriptor hw_process_desc = + PROC_DESC ( struct hw, process, hw_step ); + static int hw_open ( struct interface *xfer, struct uri *uri __unused ) { struct hw *hw; @@ -52,7 +54,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..c6660f22 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,21 @@ 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 ) ) ) { + ref_get ( process->refcnt ); /* Inhibit destruction mid-step */ + desc = process->desc; + object = process_object ( process ); 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 ); + 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..9edfcead 100644 --- a/src/core/resolv.c +++ b/src/core/resolv.c @@ -86,16 +86,17 @@ 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 ); + process_del ( &numeric->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 ( 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 +108,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/drivers/block/scsi.c b/src/drivers/block/scsi.c index d1416513..016a6c55 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -892,11 +892,9 @@ 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; /* Wait until underlying SCSI device is ready */ @@ -926,6 +924,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 ( struct scsi_device, process, scsidev_step ); + /** * Open SCSI device * @@ -946,7 +948,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/include/ipxe/process.h b/src/include/ipxe/process.h index 45c2af63..f8b10a8a 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,63 @@ 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 ); }; +/** + * 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 ), \ + } + +/** + * 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 ), \ + } + +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 +103,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 +119,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 +152,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/net/80211/net80211.c b/src/net/80211/net80211.c index f5ab65f0..466d1243 100644 --- a/src/net/80211/net80211.c +++ b/src/net/80211/net80211.c @@ -159,7 +159,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, @@ -729,6 +729,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 +765,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 +1635,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 +1839,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 +1864,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/fcels.c b/src/net/fcels.c index f8bcb865..656b4f67 100644 --- a/src/net/fcels.c +++ b/src/net/fcels.c @@ -244,11 +244,9 @@ 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; @@ -278,6 +276,10 @@ static void fc_els_step ( struct process *process ) { } } +/** Fibre Channel ELS process descriptor */ +static struct process_descriptor fc_els_process_desc = + PROC_DESC ( struct fc_els, process, fc_els_step ); + /** * Create ELS transaction * @@ -298,7 +300,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..afb57cfe 100644 --- a/src/net/fcns.c +++ b/src/net/fcns.c @@ -153,11 +153,9 @@ 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; @@ -208,6 +206,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 ( struct fc_ns_query, process, fc_ns_query_step ); + /** * Issue Fibre Channel name server query * @@ -226,7 +228,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/fcp.c b/src/net/fcp.c index 419fea3e..bd1a0900 100644 --- a/src/net/fcp.c +++ b/src/net/fcp.c @@ -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 * @@ -765,7 +767,8 @@ 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 ) ); 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 4b15314e..972a60cf 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 ); @@ -363,6 +361,10 @@ 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 ( struct ib_cmrc_connection, shutdown, ib_cmrc_shutdown ); + /** * Open CMRC connection * @@ -388,7 +390,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/netdevice.c b/src/net/netdevice.c index 54b41b3f..52ad8292 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -769,7 +769,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/retry.c b/src/net/retry.c index 082be39b..0aa165ab 100644 --- a/src/net/retry.c +++ b/src/net/retry.c @@ -198,7 +198,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/http.c b/src/net/tcp/http.c index 598f2893..15f88b31 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -483,11 +483,9 @@ static int http_socket_deliver ( struct http_request *http, /** * 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 = @@ -561,6 +559,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 ( struct http_request, process, http_step ); + /** * Initiate an HTTP connection, with optional filter * @@ -591,7 +593,7 @@ int http_open_filter ( struct interface *xfer, struct uri *uri, intf_init ( &http->xfer, &http_xfer_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 ); /* Open socket */ memset ( &server, 0, sizeof ( server ) ); diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index cde3ed6d..34d06ce3 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -1427,9 +1427,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; @@ -1488,6 +1486,10 @@ static void iscsi_tx_step ( struct process *process ) { } } +/** 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 * @@ -2034,7 +2036,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 fba5160d..d80648cc 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -1645,11 +1645,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_step ( struct tls_session *tls ) { int rc; /* Wait for cipherstream to become ready */ @@ -1717,6 +1715,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 ( struct tls_session, process, tls_step ); + /****************************************************************************** * * Instantiator @@ -1748,7 +1750,7 @@ int add_tls ( struct interface *xfer, struct interface **next ) { 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 ( &tls->process, &tls_process_desc, &tls->refcnt ); /* Attach to parent interface, mortalise self, and return */ intf_plug_plug ( &tls->plainstream, xfer ); From ccc2655540f01cc4da018fc31fa16ce8cdb1f4b5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 14:18:48 +0100 Subject: [PATCH 12/38] [process] Add support for one-shot processes Some processes execute only once, and exist solely in order to defer execution until after the relevant instantiator method has returned. Such processes do not need to be automatically rescheduled when executing. Signed-off-by: Michael Brown --- src/core/process.c | 8 ++++++-- src/include/ipxe/process.h | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/core/process.c b/src/core/process.c index c6660f22..4a705ef6 100644 --- a/src/core/process.c +++ b/src/core/process.c @@ -100,8 +100,12 @@ void step ( void ) { ref_get ( process->refcnt ); /* Inhibit destruction mid-step */ desc = process->desc; object = process_object ( process ); - list_del ( &process->list ); - list_add_tail ( &process->list, &run_queue ); + 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 ); diff --git a/src/include/ipxe/process.h b/src/include/ipxe/process.h index f8b10a8a..9b757981 100644 --- a/src/include/ipxe/process.h +++ b/src/include/ipxe/process.h @@ -39,6 +39,8 @@ struct process_descriptor { * CPU to another process. */ void ( * step ) ( void *object ); + /** Automatically reschedule the process */ + int reschedule; }; /** @@ -78,6 +80,21 @@ struct process_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, \ } /** @@ -91,6 +108,7 @@ struct process_descriptor { #define PROC_DESC_PURE( _step ) { \ .offset = 0, \ .step = PROC_STEP ( struct process, _step ), \ + .reschedule = 1, \ } extern void * __attribute__ (( pure )) From 5694b71b1184fafd21dea962c369abce77bdc608 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 16:45:28 +0100 Subject: [PATCH 13/38] [resolv] Use a one-shot process for the numeric resolver Signed-off-by: Michael Brown --- src/core/resolv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/resolv.c b/src/core/resolv.c index 9edfcead..31e80aab 100644 --- a/src/core/resolv.c +++ b/src/core/resolv.c @@ -88,14 +88,13 @@ struct numeric_resolv { static void numeric_step ( struct numeric_resolv *numeric ) { - process_del ( &numeric->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 ( struct numeric_resolv, process, numeric_step ); + PROC_DESC_ONCE ( struct numeric_resolv, process, numeric_step ); static int numeric_resolv ( struct interface *resolv, const char *name, struct sockaddr *sa ) { From 08ac74b70871c27ee702abebb097fe0baba4f7ce Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 17:16:38 +0100 Subject: [PATCH 14/38] [fc] Use a one-shot process for Fibre Channel ELS requests Signed-off-by: Michael Brown --- src/net/fcels.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/net/fcels.c b/src/net/fcels.c index 656b4f67..2106cbd5 100644 --- a/src/net/fcels.c +++ b/src/net/fcels.c @@ -253,9 +253,6 @@ static void fc_els_step ( struct fc_els *els ) { /* 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,7 +275,7 @@ static void fc_els_step ( struct fc_els *els ) { /** Fibre Channel ELS process descriptor */ static struct process_descriptor fc_els_process_desc = - PROC_DESC ( struct fc_els, process, fc_els_step ); + PROC_DESC_ONCE ( struct fc_els, process, fc_els_step ); /** * Create ELS transaction From ce3bc9e88b0045f0748cac0fc4332096bdc8c1cf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 17:17:49 +0100 Subject: [PATCH 15/38] [fc] Use a one-shot process for Fibre Channel name server queries Signed-off-by: Michael Brown --- src/net/fcns.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/net/fcns.c b/src/net/fcns.c index afb57cfe..55bd29e5 100644 --- a/src/net/fcns.c +++ b/src/net/fcns.c @@ -161,9 +161,6 @@ static void fc_ns_query_step ( struct fc_ns_query *query ) { 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,7 +205,7 @@ static struct interface_descriptor fc_ns_query_xchg_desc = /** Name server process descriptor */ static struct process_descriptor fc_ns_query_process_desc = - PROC_DESC ( struct fc_ns_query, process, fc_ns_query_step ); + PROC_DESC_ONCE ( struct fc_ns_query, process, fc_ns_query_step ); /** * Issue Fibre Channel name server query From 019d4c1c1895a672ced583b972b256cd978a6b9f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 17:21:52 +0100 Subject: [PATCH 16/38] [infiniband] Use a one-shot process for CMRC shutdown Signed-off-by: Michael Brown --- src/net/infiniband/ib_cmrc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/net/infiniband/ib_cmrc.c b/src/net/infiniband/ib_cmrc.c index 972a60cf..369e2e90 100644 --- a/src/net/infiniband/ib_cmrc.c +++ b/src/net/infiniband/ib_cmrc.c @@ -117,9 +117,6 @@ static void ib_cmrc_shutdown ( struct ib_cmrc_connection *cmrc ) { 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 ); } @@ -363,7 +360,8 @@ static struct interface_descriptor ib_cmrc_xfer_desc = /** CMRC shutdown process descriptor */ static struct process_descriptor ib_cmrc_shutdown_desc = - PROC_DESC ( struct ib_cmrc_connection, shutdown, ib_cmrc_shutdown ); + PROC_DESC_ONCE ( struct ib_cmrc_connection, shutdown, + ib_cmrc_shutdown ); /** * Open CMRC connection From 3915b660fda1c670bf1d5a5def4f68cd1cfb3967 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 16:51:31 +0100 Subject: [PATCH 17/38] [hw] Eliminate polling while waiting for window to open Polling for the data-transfer window to become open is wasteful. We can eliminate the polling loop by using hw_step() as the handler for an xfer_window_changed() event. If the window is already open at the time of instantiation, then xfer_window_changed() may never be called. We can cover this case by using hw_step() as the step() method of a one-shot process. Since the signature for an xfer_window_changed() method is identical to the signature for a process step() method, the same function can be used for both. Signed-off-by: Michael Brown --- src/core/hw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hw.c b/src/core/hw.c index e9c9ffc1..91736a65 100644 --- a/src/core/hw.c +++ b/src/core/hw.c @@ -36,6 +36,7 @@ static void hw_step ( struct hw *hw ) { } static struct interface_operation hw_xfer_operations[] = { + INTF_OP ( xfer_window_changed, struct hw *, hw_step ), INTF_OP ( intf_close, struct hw *, hw_finished ), }; @@ -43,7 +44,7 @@ static struct interface_descriptor hw_xfer_desc = INTF_DESC ( struct hw, xfer, hw_xfer_operations ); static struct process_descriptor hw_process_desc = - PROC_DESC ( struct hw, process, hw_step ); + PROC_DESC_ONCE ( struct hw, process, hw_step ); static int hw_open ( struct interface *xfer, struct uri *uri __unused ) { struct hw *hw; From 5c9c39e29949f89b4e4b96d6f15e90f5f54ac46c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 17:14:46 +0100 Subject: [PATCH 18/38] [scsi] Eliminate polling while waiting for window to open Signed-off-by: Michael Brown --- src/drivers/block/scsi.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index 016a6c55..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 */ @@ -897,16 +899,20 @@ static struct interface_descriptor scsidev_ready_desc = 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 ); @@ -916,6 +922,7 @@ static void scsidev_step ( struct scsi_device *scsidev ) { /** 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,7 +933,7 @@ static struct interface_descriptor scsidev_scsi_desc = /** SCSI device process descriptor */ static struct process_descriptor scsidev_process_desc = - PROC_DESC ( struct scsi_device, process, scsidev_step ); + PROC_DESC_ONCE ( struct scsi_device, process, scsidev_step ); /** * Open SCSI device From 3ad1a1a60a01e28ae4c02bcd5dfd8f4bf83e7aea Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 17:59:43 +0100 Subject: [PATCH 19/38] [http] Eliminate polling while waiting for window to open Signed-off-by: Michael Brown --- src/net/tcp/http.c | 78 +++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index 15f88b31..432e5cd9 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -48,6 +48,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); +/** HTTP transmission state */ +enum http_tx_state { + HTTP_TX_REQUEST = 0, + HTTP_TX_DONE, +}; + /** HTTP receive state */ enum http_rx_state { HTTP_RX_RESPONSE = 0, @@ -75,6 +81,8 @@ struct http_request { /** TX process */ struct process process; + /** TX state */ + enum http_tx_state tx_state; /** HTTP response code */ unsigned int response; @@ -498,49 +506,55 @@ static void http_step ( struct http_request *http ) { int rc; int request_len = unparse_uri ( NULL, 0, http->uri, URI_PATH_BIT | URI_QUERY_BIT ); + char request[ request_len + 1 /* NUL */ ]; - if ( xfer_window ( &http->socket ) ) { - char request[request_len + 1]; + /* Do nothing if we have already transmitted the request */ + if ( http->tx_state != HTTP_TX_REQUEST ) + 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 ); - } + /* 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 ); - } + /* Mark request as transmitted */ + http->tx_state = HTTP_TX_DONE; + + /* 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 ); } } /** HTTP socket interface operations */ static struct interface_operation http_socket_operations[] = { INTF_OP ( xfer_deliver, struct http_request *, http_socket_deliver ), + INTF_OP ( xfer_window_changed, struct http_request *, http_step ), INTF_OP ( intf_close, struct http_request *, http_done ), }; @@ -561,7 +575,7 @@ static struct interface_descriptor http_xfer_desc = /** HTTP process descriptor */ static struct process_descriptor http_process_desc = - PROC_DESC ( struct http_request, process, http_step ); + PROC_DESC_ONCE ( struct http_request, process, http_step ); /** * Initiate an HTTP connection, with optional filter From bce34e87df4469e99f91da2de5949ddc48fb17c9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 18:07:41 +0100 Subject: [PATCH 20/38] [iscsi] Eliminate polling while waiting for window to open Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 34d06ce3..87815599 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 * @@ -1324,7 +1342,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 +1423,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: @@ -1468,7 +1486,10 @@ static void iscsi_tx_step ( struct iscsi_session *iscsi ) { /* 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; } @@ -1696,6 +1717,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 ), }; From 5eb60f48839dc054d719fc484aa54f497e5daaf4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 18:35:56 +0100 Subject: [PATCH 21/38] [tls] Eliminate polling while TX state machine is idle Signed-off-by: Michael Brown --- src/net/tls.c | 80 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/src/net/tls.c b/src/net/tls.c index d80648cc..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,13 +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; - /* Send notification of a window change */ - xfer_window_changed ( &tls->plainstream ); - return 0; } @@ -1627,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 ), }; @@ -1647,7 +1701,7 @@ static struct interface_descriptor tls_cipherstream_desc = * * @v tls TLS session */ -static void tls_step ( struct tls_session *tls ) { +static void tls_tx_step ( struct tls_session *tls ) { int rc; /* Wait for cipherstream to become ready */ @@ -1665,7 +1719,7 @@ static void tls_step ( struct tls_session *tls ) { 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 */ @@ -1674,7 +1728,7 @@ static void tls_step ( struct tls_session *tls ) { "%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 */ @@ -1691,7 +1745,7 @@ static void tls_step ( struct tls_session *tls ) { 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 */ @@ -1700,7 +1754,7 @@ static void tls_step ( struct tls_session *tls ) { tls, strerror ( rc ) ); goto err; } - tls->tx_state = TLS_TX_NONE; + tls_tx_none ( tls ); break; case TLS_TX_DATA: /* Nothing to do */ @@ -1717,7 +1771,7 @@ static void tls_step ( struct tls_session *tls ) { /** TLS TX process descriptor */ static struct process_descriptor tls_process_desc = - PROC_DESC ( struct tls_session, process, tls_step ); + PROC_DESC_ONCE ( struct tls_session, process, tls_tx_step ); /****************************************************************************** * @@ -1749,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_process_desc, &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 ); From 69f5b2e8dc2bd756cc0aac21969b2bf683e94302 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 24 Jun 2011 19:14:13 +0100 Subject: [PATCH 22/38] [int13] Provide a permanently closed window via the control interface Allow objects to support both streaming and block device protocols, by starting streaming data only when the data transfer window opens. Signed-off-by: Michael Brown --- src/arch/i386/interface/pcbios/int13.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 ), }; From 2988b2665366700b406a027c0c322cc252e5cc39 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 27 Jun 2011 18:27:28 +0100 Subject: [PATCH 23/38] [http] Support read-only HTTP block devices Provide support for HTTP range requests, and expose this functionality via the iPXE block device API. This allows SAN booting from a root path such as: sanboot http://boot.ipxe.org/freedos/fdfullcd.iso Signed-off-by: Michael Brown --- src/net/tcp/http.c | 369 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 309 insertions(+), 60 deletions(-) diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index 432e5cd9..c4c1b628 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -44,14 +44,23 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include +#include #include FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); -/** HTTP transmission state */ -enum http_tx_state { - HTTP_TX_REQUEST = 0, - HTTP_TX_DONE, +/** 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 */ @@ -61,6 +70,7 @@ enum http_rx_state { HTTP_RX_CHUNK_LEN, HTTP_RX_DATA, HTTP_RX_TRAILER, + HTTP_RX_IDLE, HTTP_RX_DEAD, }; @@ -73,31 +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; - /** TX state */ - enum http_tx_state tx_state; - /** 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; }; /** @@ -115,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; @@ -128,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 */ @@ -140,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 * @@ -152,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; @@ -175,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 ); @@ -187,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 */ @@ -227,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; } @@ -309,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; } } @@ -358,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 ); @@ -366,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; @@ -375,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 */ @@ -422,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: @@ -483,11 +581,25 @@ 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 * @@ -507,9 +619,11 @@ static void http_step ( struct http_request *http ) { 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; /* Do nothing if we have already transmitted the request */ - if ( http->tx_state != HTTP_TX_REQUEST ) + if ( ! ( http->flags & HTTP_TX_PENDING ) ) return; /* Do nothing until socket is ready */ @@ -530,32 +644,151 @@ static void http_step ( struct http_request *http ) { 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 ), "%d-%d", http->partial_start, + ( http->partial_start + http->partial_len - 1 ) ); + /* Mark request as transmitted */ - http->tx_state = HTTP_TX_DONE; + http->flags &= ~HTTP_TX_PENDING; /* Send GET request */ if ( ( rc = xfer_printf ( &http->socket, - "GET %s%s HTTP/1.1\r\n" + "%s %s%s HTTP/1.1\r\n" "User-Agent: iPXE/" VERSION "\r\n" - "%s%s%s" "Host: %s\r\n" + "%s%s%s%s%s%s%s" "\r\n", - http->uri->path ? "" : "/", - request, + ( ( http->flags & HTTP_HEAD_ONLY ) ? + "HEAD" : "GET" ), + ( http->uri->path ? "" : "/" ), + request, host, + ( ( 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" : "" ), - host ) ) != 0 ) { - http_done ( http, rc ); + ( 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 ( xfer_window_changed, struct http_request *, http_step ), - INTF_OP ( intf_close, struct http_request *, http_done ), + INTF_OP ( intf_close, struct http_request *, http_close ), }; /** HTTP socket interface descriptor */ @@ -563,9 +796,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 */ @@ -605,9 +852,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_process_desc, &http->refcnt ); + http->flags = HTTP_TX_PENDING; /* Open socket */ memset ( &server, 0, sizeof ( server ) ); @@ -630,7 +879,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; } From 00afad81229fcf24512e5fe9f139d03c587d6e72 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Jun 2011 11:47:16 +0100 Subject: [PATCH 24/38] [http] Fix size_t format specifiers Signed-off-by: Michael Brown --- src/net/tcp/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index c4c1b628..012b2268 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -652,7 +652,7 @@ static void http_step ( struct http_request *http ) { /* Determine type of request */ partial = ( http->partial_len != 0 ); - snprintf ( range, sizeof ( range ), "%d-%d", http->partial_start, + snprintf ( range, sizeof ( range ), "%zd-%zd", http->partial_start, ( http->partial_start + http->partial_len - 1 ) ); /* Mark request as transmitted */ From 95d82bb2a23bf645d995aed0d643cc34d5d8a3af Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Jun 2011 11:42:07 +0100 Subject: [PATCH 25/38] [build] Avoid using -ffunction-sections on some older versions of gcc Some older versions of gcc issue a warning if -ffunction-sections is used in combination with -g (gcc bug #18553). Inhibit -ffunction-sections when building with such a version of gcc. Reported-by: zhengwei Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 From 1b8984eb5da23e58f275478474dd6ab8d7a785ea Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Jun 2011 14:49:18 +0100 Subject: [PATCH 26/38] [iscsi] Avoid duplicate calls to iscsi_tx_done() The iSCSI TX process can now be woken up by the TCP socket via xfer_window_changed(), so it is no longer valid to assume that iscsi_tx_step() can be called in state ISCSI_TX_IDLE only immediately after completing a transmission. Fix by calling iscsi_tx_done() only upon a transition into state ISCSI_TX_IDLE. Signed-off-by: Michael Brown --- src/net/tcp/iscsi.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index 87815599..8beeb877 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -1333,8 +1333,7 @@ static void iscsi_tx_resume ( 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 ) ); @@ -1476,8 +1475,8 @@ static void iscsi_tx_step ( struct iscsi_session *iscsi ) { 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 ); @@ -1504,6 +1503,12 @@ static void iscsi_tx_step ( struct iscsi_session *iscsi ) { /* 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 ); } } From 66cbae73bd6c7a7c87647cb0fe9fa761f9a51aaf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Jul 2011 14:52:53 +0100 Subject: [PATCH 27/38] [libc] Allow for zero-padded decimals in printf() Signed-off-by: Michael Brown --- src/core/vsprintf.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) 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; } From 9cf2f9dc2b773b6544c86a7a347e143d7c101ef7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Jul 2011 15:26:01 +0100 Subject: [PATCH 28/38] [dhcp] Add symbolic definitions for DHCP client architecture values Signed-off-by: Michael Brown --- src/arch/i386/include/efi/ipxe/dhcp_arch.h | 3 +- src/arch/i386/include/pcbios/ipxe/dhcp_arch.h | 3 +- src/arch/x86_64/include/efi/ipxe/dhcp_arch.h | 5 +-- src/include/ipxe/dhcp.h | 33 +++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) 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/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/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 From 5d23fb1bb4adffdf3b497136246c5c924416d32c Mon Sep 17 00:00:00 2001 From: Thomas Miletich Date: Sun, 10 Jul 2011 17:47:09 +0200 Subject: [PATCH 29/38] [igb] Remove __BIG_ENDIAN conditional Reported-by: Stefan Hajnoczi Signed-off-by: Thomas Miletich Signed-off-by: Michael Brown --- src/drivers/net/igb/igb_osdep.h | 5 ----- 1 file changed, 5 deletions(-) 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); } From 69b7d57265679d76e26581d034e8f8ab5168bb27 Mon Sep 17 00:00:00 2001 From: Malte Starostik Date: Fri, 15 Jul 2011 16:31:56 +0200 Subject: [PATCH 30/38] [http] Include port in HTTP Host header as needed According to section 14.23 of RFC2616, an HTTP Host header without port implies the default port is used. Thus, when fetching from anywhere but port 80 for HTTP or 443 for HTTPS, the port ought to be explicitly given in that header. Otherwise, some servers might fail to associate the request with the correct virtual host or generate incorrect self-referencing URLs. Signed-off-by: Michael Brown --- src/net/tcp/http.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/net/tcp/http.c b/src/net/tcp/http.c index 012b2268..cee75133 100644 --- a/src/net/tcp/http.c +++ b/src/net/tcp/http.c @@ -662,13 +662,17 @@ static void http_step ( struct http_request *http ) { if ( ( rc = xfer_printf ( &http->socket, "%s %s%s HTTP/1.1\r\n" "User-Agent: iPXE/" VERSION "\r\n" - "Host: %s\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=" : "" ), From a667bf044a37f9e96830f1f35627829860f7019f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 15 Jul 2011 18:48:46 +0100 Subject: [PATCH 31/38] [netdevice] Allow link layer to report broadcast/multicast packets via pull() Allow the link layer to directly report whether or not a packet is multicast or broadcast at the time of calling pull(), rather than relying on heuristics to determine this at a later stage. Signed-off-by: Michael Brown --- src/arch/i386/interface/pxe/pxe_undi.c | 14 ++++++------- src/drivers/net/ipoib.c | 5 ++++- src/include/ipxe/netdevice.h | 28 +++++++++++++++++--------- src/interface/efi/efi_snp.c | 4 +++- src/net/80211/net80211.c | 10 +++++++-- src/net/aoe.c | 5 +++-- src/net/arp.c | 4 +++- src/net/eapol.c | 4 +++- src/net/eth_slow.c | 4 +++- src/net/ethernet.c | 8 +++++++- src/net/fcoe.c | 8 ++++++-- src/net/ipv4.c | 13 +++++++----- src/net/ipv6.c | 4 +++- src/net/netdevice.c | 12 +++++++---- src/net/rarp.c | 4 +++- src/net/vlan.c | 7 +++++-- src/usr/lotest.c | 8 ++++++-- 17 files changed, 99 insertions(+), 43 deletions(-) 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/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/netdevice.h b/src/include/ipxe/netdevice.h index 64285984..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 * @@ -611,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/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 466d1243..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 ); /** @} */ /** @@ -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; } 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 9b5fd220..ef30d5ec 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/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/ipv4.c b/src/net/ipv4.c index 5bb48f61..01eca09d 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -381,10 +381,12 @@ static int ipv4_tx ( struct io_buffer *iobuf, /** * 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. @@ -392,7 +394,8 @@ static int ipv4_tx ( struct io_buffer *iobuf, static int ipv4_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 ) { struct iphdr *iphdr = iobuf->data; size_t hdrlen; size_t len; 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 52ad8292..f5ec4191 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -678,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", @@ -710,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 */ @@ -743,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; } @@ -751,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 ); } 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/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/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; From dfbb3bd1849852854fac5630a7461481d0d3e535 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 15 Jul 2011 18:58:44 +0100 Subject: [PATCH 32/38] [ipv4] Discard unwanted unicast packets Explicitly discard any unicast packets for addresses that we do not control, to avoid unexpected behaviour when operating in promiscuous mode (which is now the default, thanks to FCoE). Signed-off-by: Michael Brown --- src/net/ipv4.c | 60 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 01eca09d..aee3bc35 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -378,6 +378,42 @@ 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 * @@ -392,10 +428,10 @@ static int ipv4_tx ( struct io_buffer *iobuf, * 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, - unsigned int flags __unused ) { + unsigned int flags ) { struct iphdr *iphdr = iobuf->data; size_t hdrlen; size_t len; @@ -451,6 +487,15 @@ static int ipv4_rx ( struct io_buffer *iobuf, 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 ) ) ) { + DBG ( "IPv4 discarding non-local unicast packet for %s\n", + inet_ntoa ( iphdr->dest ) ); + goto err; + } + /* Truncate packet to correct length, calculate pseudo-header * checksum and then strip off the IPv4 header. */ @@ -499,15 +544,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; } From 5b41381f3386ac70de30edbdcd291cda112d9f64 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 15 Jul 2011 19:21:07 +0100 Subject: [PATCH 33/38] [ipv4] Use broadcast link-layer address for all broadcast IPv4 addresses When transmitting, use the broadcast link-layer address for any broadcast address (e.g. 192.168.0.255), not just INADDR_BROADCAST (255.255.255.255). Signed-off-by: Michael Brown --- src/net/ipv4.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/net/ipv4.c b/src/net/ipv4.c index aee3bc35..5bb01a1c 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -110,10 +110,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 ) ) @@ -260,15 +256,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 +304,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,6 +325,7 @@ 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 ) { @@ -343,7 +343,7 @@ 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 ) ); From 17f09dfe03a8c6b46d30844d3cee28266b6971fe Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 16 Jul 2011 01:46:12 +0100 Subject: [PATCH 34/38] [retry] Fix potential use-after-free in timer_expired() timer->refcnt is allowed to be NULL, in which case the timer's expired() method may end up freeing the timer object. Discovered using valgrind. Signed-off-by: Michael Brown --- src/net/retry.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/net/retry.c b/src/net/retry.c index 0aa165ab..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 ); } /** From 13186b64b6c3d5cbe9ed13bda1532e79b1afe81d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 16 Jul 2011 01:15:53 +0100 Subject: [PATCH 35/38] [ipv4] Fix fragment reassembly Signed-off-by: Michael Brown --- src/include/ipxe/ip.h | 23 ++--- src/net/ipv4.c | 208 ++++++++++++++++++++++++------------------ 2 files changed, 126 insertions(+), 105 deletions(-) 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/net/ipv4.c b/src/net/ipv4.c index 5bb01a1c..e31e4e2d 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 @@ -128,103 +132,126 @@ 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; + + DBG ( "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 ) { + DBG ( "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 ) { + DBG ( "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; } @@ -481,6 +508,9 @@ static int ipv4_rx ( struct io_buffer *iobuf, goto err; } + /* Truncate packet to correct length */ + iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); + /* Print IPv4 header for debugging */ DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) ); DBG ( "%s len %d proto %d id %04x csum %04x\n", @@ -496,31 +526,29 @@ static int ipv4_rx ( struct io_buffer *iobuf, goto err; } - /* Truncate packet to correct length, calculate pseudo-header - * checksum and then strip off the IPv4 header. - */ - 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. + /* 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", From 149b502306f7b5f7cc9d90cf2095f8dc6576375f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 16 Jul 2011 01:29:20 +0100 Subject: [PATCH 36/38] [ipv4] Improve debugging Use autocolourisation to improve legibility, and move per-packet messages to DBG2(). Signed-off-by: Michael Brown --- src/net/ipv4.c | 96 +++++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/src/net/ipv4.c b/src/net/ipv4.c index e31e4e2d..99c2580e 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -50,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; } @@ -87,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 ); @@ -143,7 +144,8 @@ static void ipv4_fragment_expired ( struct retry_timer *timer, container_of ( timer, struct ipv4_fragment, timer ); struct iphdr *iphdr = frag->iobuf->data; - DBG ( "IPv4 fragment %04x expired\n", ntohs ( iphdr->ident ) ); + DBGC ( iphdr->src, "IPv4 fragment %04x expired\n", + ntohs ( iphdr->ident ) ); free_iob ( frag->iobuf ); list_del ( &frag->list ); free ( frag ); @@ -192,8 +194,9 @@ static struct io_buffer * ipv4_reassemble ( struct io_buffer *iobuf ) { /* Drop out-of-order fragments */ expected_offset = ( frag ? frag->offset : 0 ); if ( offset != expected_offset ) { - DBG ( "IPv4 dropping out-of-sequence fragment %04x (%zd+%zd, " - "expected %zd)\n", ntohs ( iphdr->ident ), 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; } @@ -217,9 +220,9 @@ static struct io_buffer * ipv4_reassemble ( struct io_buffer *iobuf ) { new_iobuf = alloc_iob ( iob_len ( frag->iobuf ) + iob_len ( iobuf ) ); if ( ! new_iobuf ) { - DBG ( "IPv4 could not extend reassembly buffer to " - "%zd bytes\n", - ( iob_len ( frag->iobuf ) + iob_len ( 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 ) ), @@ -356,7 +359,8 @@ static int ipv4_tx ( struct io_buffer *iobuf, 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; } @@ -372,8 +376,8 @@ static int ipv4_tx ( struct io_buffer *iobuf, /* Determine link-layer destination address */ 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; @@ -385,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; } @@ -472,39 +477,40 @@ 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; } @@ -512,17 +518,17 @@ static int ipv4_rx ( struct io_buffer *iobuf, iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); /* 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 ) ); + 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 ) ) ) { - DBG ( "IPv4 discarding non-local unicast packet for %s\n", - inet_ntoa ( iphdr->dest ) ); + DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet " + "for %s\n", inet_ntoa ( iphdr->dest ) ); goto err; } @@ -551,8 +557,8 @@ static int ipv4_rx ( struct io_buffer *iobuf, 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; } From 24b62e0e1e42395d81396be6abc55b8bc7877df6 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Sat, 6 Aug 2011 23:40:04 +0400 Subject: [PATCH 37/38] [romprefix] Fix romprefix build with certain versions of binutils Signed-off-by: Valentine Barshak Signed-off-by: Michael Brown --- src/arch/i386/prefix/romprefix.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 8a86a848dc324221c39efd5853846e52a70046ed Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Sat, 6 Aug 2011 23:43:17 +0400 Subject: [PATCH 38/38] [lkrnprefix] Fix lost command line passed by grub iPXE specifies a value of 0 for cmdline_size, causing GRUB to not pass in a command line. Fix by setting cmdline_size to the maximum value of 2047. Signed-off-by: Valentine Barshak Signed-off-by: Michael Brown --- src/arch/i386/prefix/lkrnprefix.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: