Home | History | Annotate | Download | only in network_WiFi_RateControl
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging
      6 
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.common_lib import utils
      9 from autotest_lib.client.common_lib.cros.network import iw_runner
     10 from autotest_lib.client.common_lib.cros.network import tcpdump_analyzer
     11 from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
     12 from autotest_lib.server.cros.network import hostap_config
     13 from autotest_lib.server.cros.network import netperf_runner
     14 from autotest_lib.server.cros.network import wifi_cell_test_base
     15 
     16 
     17 class network_WiFi_RateControl(wifi_cell_test_base.WiFiCellTestBase):
     18     """
     19     Test maximal achievable bandwidth on several channels per band.
     20 
     21     Conducts a performance test for a set of specified router configurations
     22     and reports results as keyval pairs.
     23 
     24     """
     25 
     26     version = 1
     27 
     28     # In case of aggregated frames, we need to capture whole packet to be able
     29     # to parse the A-MSDU subframe and extract the IP/UDP content.
     30     # Per spec the max AMSDU size for 11n is 7935bytes, let use it here.
     31     TEST_SNAPLEN = 7935
     32 
     33 
     34     def parse_additional_arguments(self, commandline_args, additional_params):
     35         """
     36         Hook into super class to take control files parameters.
     37 
     38         @param commandline_args: dict of parsed parameters from the autotest.
     39         @param additional_params: list of HostapConfig objects.
     40 
     41         """
     42         self._ap_configs = additional_params
     43 
     44 
     45     def get_highest_mcs_rate(self, frequency):
     46         """
     47         Get the highest MCS index supported by the DUT on |frequency|.
     48 
     49         @param frequency: int frequency to look for supported MCS rates.
     50         @return int highest rate supported.
     51 
     52         """
     53         # Figure out the highest MCS index supported by this hardware.
     54         phys = iw_runner.IwRunner(
     55                 remote_host=self.context.client.host).list_phys()
     56         if len(phys) != 1:
     57             raise error.TestFail('Test expects a single PHY, but we got %d' %
     58                                  len(phys))
     59 
     60         phy = phys[0]
     61         bands = [band for band in phy.bands if frequency in band.frequencies]
     62         if len(bands) != 1:
     63             raise error.TestFail('Test expects a single possible band for a '
     64                                  'given frequency, but this device has %d '
     65                                  'such bands.' % len(bands))
     66 
     67         # 32 is a special low throughput, high resilience mode.  Ignore it.
     68         possible_indices = filter(lambda x: x != 32, bands[0].mcs_indices)
     69 
     70         if not possible_indices:
     71             raise error.TestFail('No possible MCS indices on frequency %d' %
     72                                  frequency)
     73 
     74         return max(possible_indices)
     75 
     76 
     77     def check_bitrates_in_capture(self, pcap_result, max_mcs_index):
     78         """
     79         Check that frames in a packet capture have expected MCS indices.
     80 
     81         @param pcap_result: RemoteCaptureResult tuple.
     82         @param max_mcs_index: int MCS index representing the highest possible
     83                 bitrate on this device.
     84 
     85         """
     86         logging.info('Analyzing packet capture...')
     87         display_filter = 'udp and ip.src==%s' % self.context.client.wifi_ip
     88         frames = tcpdump_analyzer.get_frames(
     89                 pcap_result.local_pcap_path,
     90                 display_filter,
     91                 bad_fcs='include')
     92 
     93         logging.info('Grouping frames by MCS index')
     94         counts = {}
     95         for frame in frames:
     96             counts[frame.mcs_index] = counts.get(frame.mcs_index, 0) + 1
     97         logging.info('Saw WiFi frames with MCS indices: %r', counts)
     98 
     99         # Now figure out the index which the device sent the most packets with.
    100         dominant_index = None
    101         num_packets_sent = -1
    102         for index, num_packets in counts.iteritems():
    103             if num_packets > num_packets_sent:
    104                 dominant_index = index
    105                 num_packets_sent = num_packets
    106 
    107         # We should see that the device sent more frames with the maximal index
    108         # than anything else.  This checks that the rate controller is fairly
    109         # aggressive and using all of the device's capabilities.
    110         if dominant_index != max_mcs_index:
    111             raise error.TestFail('Failed to use best possible MCS '
    112                                  'index %d in a clean RF environment: %r' %
    113                                  (max_mcs_index, counts))
    114 
    115 
    116     def run_once(self):
    117         """Test body."""
    118         if utils.host_could_be_in_afe(self.context.client.host.hostname):
    119             # Just abort the test if we're in the lab and not on a
    120             # machine known to be conducted. The performance
    121             # requirements of this test are hard to meet, without
    122             # strong multi-path effects. (Our conducted setups are
    123             # designed to provide strong multi-path.)
    124             if not self.context.client.conductive:
    125                 raise error.TestNAError(
    126                     'This test requires a great RF environment.')
    127         else:
    128             logging.error('Unable to determine if DUT has conducted '
    129                           'connection to AP. Treat any TestFail with '
    130                           'skepticism.')
    131 
    132         caps = [hostap_config.HostapConfig.N_CAPABILITY_GREENFIELD,
    133                 hostap_config.HostapConfig.N_CAPABILITY_HT40]
    134         mode_11n = hostap_config.HostapConfig.MODE_11N_PURE
    135         get_config = lambda channel: hostap_config.HostapConfig(
    136                 channel=channel, mode=mode_11n, n_capabilities=caps)
    137         netperf_config = netperf_runner.NetperfConfig(
    138                 netperf_runner.NetperfConfig.TEST_TYPE_UDP_STREAM)
    139         for i, ap_config in enumerate([get_config(1), get_config(157)]):
    140             # Set up the router and associate the client with it.
    141             self.context.configure(ap_config)
    142             self.context.capture_host.start_capture(
    143                     ap_config.frequency,
    144                     ht_type=ap_config.ht_packet_capture_mode,
    145                     snaplen=self.TEST_SNAPLEN)
    146             assoc_params = xmlrpc_datatypes.AssociationParameters(
    147                     ssid=self.context.router.get_ssid())
    148             self.context.assert_connect_wifi(assoc_params)
    149             with netperf_runner.NetperfRunner(self.context.client,
    150                                               self.context.router,
    151                                               netperf_config) as runner:
    152                 runner.run()
    153             results = self.context.capture_host.stop_capture()
    154             if len(results) != 1:
    155                 raise error.TestError('Expected to generate one packet '
    156                                       'capture but got %d instead.' %
    157                                       len(results))
    158 
    159             # The device should sense that it is in a clean RF environment and
    160             # use the highest index to achieve maximal throughput.
    161             max_mcs_index = self.get_highest_mcs_rate(ap_config.frequency)
    162             self.check_bitrates_in_capture(results[0], max_mcs_index)
    163             # Clean up router and client state for the next run.
    164             self.context.client.shill.disconnect(self.context.router.get_ssid())
    165             self.context.router.deconfig()
    166