iPXE
ifmgmt.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <string.h>
00027 #include <stdio.h>
00028 #include <unistd.h>
00029 #include <errno.h>
00030 #include <ipxe/console.h>
00031 #include <ipxe/netdevice.h>
00032 #include <ipxe/device.h>
00033 #include <ipxe/job.h>
00034 #include <ipxe/monojob.h>
00035 #include <ipxe/timer.h>
00036 #include <ipxe/errortab.h>
00037 #include <usr/ifmgmt.h>
00038 
00039 /** @file
00040  *
00041  * Network interface management
00042  *
00043  */
00044 
00045 /** Default time to wait for link-up */
00046 #define LINK_WAIT_TIMEOUT ( 15 * TICKS_PER_SEC )
00047 
00048 /** Default unsuccessful configuration status code */
00049 #define EADDRNOTAVAIL_CONFIG __einfo_error ( EINFO_EADDRNOTAVAIL_CONFIG )
00050 #define EINFO_EADDRNOTAVAIL_CONFIG                                      \
00051         __einfo_uniqify ( EINFO_EADDRNOTAVAIL, 0x01,                    \
00052                           "No configuration methods succeeded" )
00053 
00054 /** Human-readable error message */
00055 struct errortab ifmgmt_errors[] __errortab = {
00056         __einfo_errortab ( EINFO_EADDRNOTAVAIL_CONFIG ),
00057 };
00058 
00059 /**
00060  * Open network device
00061  *
00062  * @v netdev            Network device
00063  * @ret rc              Return status code
00064  */
00065 int ifopen ( struct net_device *netdev ) {
00066         int rc;
00067 
00068         if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
00069                 printf ( "Could not open %s: %s\n",
00070                          netdev->name, strerror ( rc ) );
00071                 return rc;
00072         }
00073 
00074         return 0;
00075 }
00076 
00077 /**
00078  * Close network device
00079  *
00080  * @v netdev            Network device
00081  */
00082 void ifclose ( struct net_device *netdev ) {
00083         netdev_close ( netdev );
00084 }
00085 
00086 /**
00087  * Print network device error breakdown
00088  *
00089  * @v stats             Network device statistics
00090  * @v prefix            Message prefix
00091  */
00092 static void ifstat_errors ( struct net_device_stats *stats,
00093                             const char *prefix ) {
00094         unsigned int i;
00095 
00096         for ( i = 0 ; i < ( sizeof ( stats->errors ) /
00097                             sizeof ( stats->errors[0] ) ) ; i++ ) {
00098                 if ( stats->errors[i].count )
00099                         printf ( "  [%s: %d x \"%s\"]\n", prefix,
00100                                  stats->errors[i].count,
00101                                  strerror ( stats->errors[i].rc ) );
00102         }
00103 }
00104 
00105 /**
00106  * Print status of network device
00107  *
00108  * @v netdev            Network device
00109  */
00110 void ifstat ( struct net_device *netdev ) {
00111         printf ( "%s: %s using %s on %s (%s)\n"
00112                  "  [Link:%s%s, TX:%d TXE:%d RX:%d RXE:%d]\n",
00113                  netdev->name, netdev_addr ( netdev ),
00114                  netdev->dev->driver_name, netdev->dev->name,
00115                  ( netdev_is_open ( netdev ) ? "open" : "closed" ),
00116                  ( netdev_link_ok ( netdev ) ? "up" : "down" ),
00117                  ( netdev_link_blocked ( netdev ) ? " (blocked)" : "" ),
00118                  netdev->tx_stats.good, netdev->tx_stats.bad,
00119                  netdev->rx_stats.good, netdev->rx_stats.bad );
00120         if ( ! netdev_link_ok ( netdev ) ) {
00121                 printf ( "  [Link status: %s]\n",
00122                          strerror ( netdev->link_rc ) );
00123         }
00124         ifstat_errors ( &netdev->tx_stats, "TXE" );
00125         ifstat_errors ( &netdev->rx_stats, "RXE" );
00126 }
00127 
00128 /** Network device poller */
00129 struct ifpoller {
00130         /** Job control interface */
00131         struct interface job;
00132         /** Network device */
00133         struct net_device *netdev;
00134         /** Network device configurator (if applicable) */
00135         struct net_device_configurator *configurator;
00136         /**
00137          * Check progress
00138          *
00139          * @v ifpoller          Network device poller
00140          * @ret ongoing_rc      Ongoing job status code (if known)
00141          */
00142         int ( * progress ) ( struct ifpoller *ifpoller );
00143 };
00144 
00145 /**
00146  * Report network device poller progress
00147  *
00148  * @v ifpoller          Network device poller
00149  * @v progress          Progress report to fill in
00150  * @ret ongoing_rc      Ongoing job status code (if known)
00151  */
00152 static int ifpoller_progress ( struct ifpoller *ifpoller,
00153                                struct job_progress *progress __unused ) {
00154 
00155         /* Hand off to current progress checker */
00156         return ifpoller->progress ( ifpoller );
00157 }
00158 
00159 /** Network device poller operations */
00160 static struct interface_operation ifpoller_job_op[] = {
00161         INTF_OP ( job_progress, struct ifpoller *, ifpoller_progress ),
00162 };
00163 
00164 /** Network device poller descriptor */
00165 static struct interface_descriptor ifpoller_job_desc =
00166         INTF_DESC ( struct ifpoller, job, ifpoller_job_op );
00167 
00168 /**
00169  * Poll network device until completion
00170  *
00171  * @v netdev            Network device
00172  * @v configurator      Network device configurator (if applicable)
00173  * @v timeout           Timeout period, in ticks
00174  * @v progress          Method to check progress
00175  * @ret rc              Return status code
00176  */
00177 static int ifpoller_wait ( struct net_device *netdev,
00178                            struct net_device_configurator *configurator,
00179                            unsigned long timeout,
00180                            int ( * progress ) ( struct ifpoller *ifpoller ) ) {
00181         static struct ifpoller ifpoller = {
00182                 .job = INTF_INIT ( ifpoller_job_desc ),
00183         };
00184 
00185         ifpoller.netdev = netdev;
00186         ifpoller.configurator = configurator;
00187         ifpoller.progress = progress;
00188         intf_plug_plug ( &monojob, &ifpoller.job );
00189         return monojob_wait ( "", timeout );
00190 }
00191 
00192 /**
00193  * Check link-up progress
00194  *
00195  * @v ifpoller          Network device poller
00196  * @ret ongoing_rc      Ongoing job status code (if known)
00197  */
00198 static int iflinkwait_progress ( struct ifpoller *ifpoller ) {
00199         struct net_device *netdev = ifpoller->netdev;
00200         int ongoing_rc = netdev->link_rc;
00201 
00202         /* Terminate successfully if link is up */
00203         if ( ongoing_rc == 0 )
00204                 intf_close ( &ifpoller->job, 0 );
00205 
00206         /* Otherwise, report link status as ongoing job status */
00207         return ongoing_rc;
00208 }
00209 
00210 /**
00211  * Wait for link-up, with status indication
00212  *
00213  * @v netdev            Network device
00214  * @v timeout           Timeout period, in ticks
00215  */
00216 int iflinkwait ( struct net_device *netdev, unsigned long timeout ) {
00217         int rc;
00218 
00219         /* Ensure device is open */
00220         if ( ( rc = ifopen ( netdev ) ) != 0 )
00221                 return rc;
00222 
00223         /* Return immediately if link is already up */
00224         netdev_poll ( netdev );
00225         if ( netdev_link_ok ( netdev ) )
00226                 return 0;
00227 
00228         /* Wait for link-up */
00229         printf ( "Waiting for link-up on %s", netdev->name );
00230         return ifpoller_wait ( netdev, NULL, timeout, iflinkwait_progress );
00231 }
00232 
00233 /**
00234  * Check configuration progress
00235  *
00236  * @v ifpoller          Network device poller
00237  * @ret ongoing_rc      Ongoing job status code (if known)
00238  */
00239 static int ifconf_progress ( struct ifpoller *ifpoller ) {
00240         struct net_device *netdev = ifpoller->netdev;
00241         struct net_device_configurator *configurator = ifpoller->configurator;
00242         struct net_device_configuration *config;
00243         int rc;
00244 
00245         /* Do nothing unless configuration has completed */
00246         if ( netdev_configuration_in_progress ( netdev ) )
00247                 return 0;
00248 
00249         /* Terminate with appropriate overall return status code */
00250         if ( configurator ) {
00251                 config = netdev_configuration ( netdev, configurator );
00252                 rc = config->rc;
00253         } else {
00254                 rc = ( netdev_configuration_ok ( netdev ) ?
00255                        0 : -EADDRNOTAVAIL_CONFIG );
00256         }
00257         intf_close ( &ifpoller->job, rc );
00258 
00259         return rc;
00260 }
00261 
00262 /**
00263  * Perform network device configuration
00264  *
00265  * @v netdev            Network device
00266  * @v configurator      Network device configurator, or NULL to use all
00267  * @ret rc              Return status code
00268  */
00269 int ifconf ( struct net_device *netdev,
00270              struct net_device_configurator *configurator ) {
00271         int rc;
00272 
00273         /* Ensure device is open and link is up */
00274         if ( ( rc = iflinkwait ( netdev, LINK_WAIT_TIMEOUT ) ) != 0 )
00275                 return rc;
00276 
00277         /* Start configuration */
00278         if ( configurator ) {
00279                 if ( ( rc = netdev_configure ( netdev, configurator ) ) != 0 ) {
00280                         printf ( "Could not configure %s via %s: %s\n",
00281                                  netdev->name, configurator->name,
00282                                  strerror ( rc ) );
00283                         return rc;
00284                 }
00285         } else {
00286                 if ( ( rc = netdev_configure_all ( netdev ) ) != 0 ) {
00287                         printf ( "Could not configure %s: %s\n",
00288                                  netdev->name, strerror ( rc ) );
00289                         return rc;
00290                 }
00291         }
00292 
00293         /* Wait for configuration to complete */
00294         printf ( "Configuring %s%s%s(%s %s)",
00295                  ( configurator ? "[" : "" ),
00296                  ( configurator ? configurator->name : "" ),
00297                  ( configurator ? "] " : "" ),
00298                  netdev->name, netdev->ll_protocol->ntoa ( netdev->ll_addr ) );
00299         return ifpoller_wait ( netdev, configurator, 0, ifconf_progress );
00300 }